Version Description
- Fixed SQL errors caused on some hosts.
- Fixed search filters & pagination.
- Fixed link table design in mobile devices.
- Fixed deprecated functions on PHP 7.4
- Fixed filter_var deprecated notices.
- Improved code with WPCS.
- Added a new API key for the YouTube API.
Download this release
Release Info
Developer | bplv |
Plugin | Broken Link Checker |
Version | 1.11.11 |
Comparing to | |
See all releases |
Code changes from version 1.11.10 to 1.11.11
- broken-link-checker.php +6 -6
- core/core.php +3383 -3215
- core/init.php +372 -399
- css/links-page.css +6 -0
- idn/idna_convert.class.php +863 -824
- idn/transcode_wrapper.php +134 -88
- idn/uctc.php +292 -279
- includes/activation.php +118 -114
- includes/admin/db-schema.php +106 -96
- includes/admin/db-upgrade.php +591 -585
- includes/admin/links-page-js.php +946 -946
- includes/admin/options-page-js.php +147 -147
- includes/admin/search-form.php +117 -117
- includes/admin/sidebar.php +14 -14
- includes/admin/table-printer.php +133 -108
- includes/any-post.php +810 -804
- includes/checkers.php +120 -120
- includes/config-manager.php +131 -127
- includes/containers.php +946 -911
- includes/extra-strings.php +19 -19
- includes/instances.php +633 -639
- includes/link-query.php +879 -870
- includes/links.php +946 -942
- includes/logger.php +188 -188
- includes/module-base.php +80 -80
- includes/module-manager.php +861 -860
- includes/modules.php +37 -35
- includes/parsers.php +367 -365
- includes/screen-meta-links.php +312 -312
- includes/screen-options/screen-options.php +296 -297
- includes/token-bucket.php +126 -121
- includes/transactions-manager.php +39 -41
- includes/utility-class.php +419 -417
- includes/wp-mutex.php +57 -57
- languages/broken-link-checker.pot +205 -204
broken-link-checker.php
CHANGED
@@ -1,10 +1,10 @@
|
|
1 |
<?php
|
2 |
|
3 |
-
|
4 |
* Plugin Name: Broken Link Checker
|
5 |
* Plugin URI: https://wordpress.org/plugins/broken-link-checker/
|
6 |
* Description: Checks your blog for broken links and missing images and notifies you on the dashboard if any are found.
|
7 |
-
* Version: 1.11.
|
8 |
* Author: ManageWP
|
9 |
* Author URI: https://managewp.com
|
10 |
* Text Domain: broken-link-checker
|
@@ -28,13 +28,13 @@ along with Broken Link Checker. If not, see https://www.gnu.org/licenses/gpl-2.0
|
|
28 |
*/
|
29 |
|
30 |
//Path to this file
|
31 |
-
if ( !defined('BLC_PLUGIN_FILE') ){
|
32 |
-
define('BLC_PLUGIN_FILE', __FILE__);
|
33 |
}
|
34 |
|
35 |
//Path to the plugin's directory
|
36 |
-
if ( !defined('BLC_DIRECTORY') ){
|
37 |
-
define('BLC_DIRECTORY', dirname(__FILE__));
|
38 |
}
|
39 |
|
40 |
//Load the actual plugin
|
1 |
<?php
|
2 |
|
3 |
+
/**
|
4 |
* Plugin Name: Broken Link Checker
|
5 |
* Plugin URI: https://wordpress.org/plugins/broken-link-checker/
|
6 |
* Description: Checks your blog for broken links and missing images and notifies you on the dashboard if any are found.
|
7 |
+
* Version: 1.11.11
|
8 |
* Author: ManageWP
|
9 |
* Author URI: https://managewp.com
|
10 |
* Text Domain: broken-link-checker
|
28 |
*/
|
29 |
|
30 |
//Path to this file
|
31 |
+
if ( ! defined( 'BLC_PLUGIN_FILE' ) ) {
|
32 |
+
define( 'BLC_PLUGIN_FILE', __FILE__ );
|
33 |
}
|
34 |
|
35 |
//Path to the plugin's directory
|
36 |
+
if ( ! defined( 'BLC_DIRECTORY' ) ) {
|
37 |
+
define( 'BLC_DIRECTORY', dirname( __FILE__ ) );
|
38 |
}
|
39 |
|
40 |
//Load the actual plugin
|
core/core.php
CHANGED
@@ -1,11 +1,12 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* Simple function to replicate PHP 5 behaviour
|
4 |
*/
|
5 |
if ( ! function_exists( 'microtime_float' ) ) {
|
6 |
function microtime_float() {
|
7 |
-
list($usec, $sec) = explode( ' ', microtime() );
|
8 |
-
return ( (float)$usec + (float)$sec);
|
9 |
}
|
10 |
}
|
11 |
|
@@ -14,1182 +15,1254 @@ require BLC_DIRECTORY . '/includes/screen-meta-links.php';
|
|
14 |
require BLC_DIRECTORY . '/includes/wp-mutex.php';
|
15 |
require BLC_DIRECTORY . '/includes/transactions-manager.php';
|
16 |
|
17 |
-
if (!class_exists('wsBrokenLinkChecker')) {
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
* @return void
|
105 |
-
*/
|
106 |
-
function admin_footer(){
|
107 |
-
if ( !$this->conf->options['run_in_dashboard'] ){
|
108 |
-
return;
|
109 |
-
}
|
110 |
-
$nonce = wp_create_nonce('blc_work');
|
111 |
-
?>
|
112 |
-
<!-- wsblc admin footer -->
|
113 |
-
<script type='text/javascript'>
|
114 |
-
(function($){
|
115 |
-
|
116 |
-
//(Re)starts the background worker thread
|
117 |
-
function blcDoWork(){
|
118 |
-
$.post(
|
119 |
-
"<?php echo admin_url('admin-ajax.php'); ?>",
|
120 |
-
{
|
121 |
-
'action' : 'blc_work',
|
122 |
-
'_ajax_nonce' : '<?php echo esc_js($nonce); ?>'
|
123 |
-
}
|
124 |
-
);
|
125 |
}
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
}
|
137 |
-
|
138 |
-
/**
|
139 |
-
* Check if an URL matches the exclusion list.
|
140 |
-
*
|
141 |
-
* @param string $url
|
142 |
-
* @return bool
|
143 |
-
*/
|
144 |
-
function is_excluded($url){
|
145 |
-
if (!is_array($this->conf->options['exclusion_list'])) return false;
|
146 |
-
foreach($this->conf->options['exclusion_list'] as $excluded_word){
|
147 |
-
if (stristr($url, $excluded_word)){
|
148 |
-
return true;
|
149 |
-
}
|
150 |
-
}
|
151 |
-
return false;
|
152 |
-
}
|
153 |
-
|
154 |
-
function dashboard_widget(){
|
155 |
-
?>
|
156 |
-
<p id='wsblc_activity_box'><?php _e( 'Loading...', 'broken-link-checker' ); ?></p>
|
157 |
-
<script type='text/javascript'>
|
158 |
-
jQuery( function($){
|
159 |
-
var blc_was_autoexpanded = false;
|
160 |
-
|
161 |
-
function blcDashboardStatus(){
|
162 |
-
$.getJSON(
|
163 |
-
"<?php echo admin_url('admin-ajax.php'); ?>",
|
164 |
{
|
165 |
-
'action' : '
|
166 |
-
'
|
167 |
-
},
|
168 |
-
function (data){
|
169 |
-
if ( data && ( typeof(data.text) != 'undefined' ) ) {
|
170 |
-
$('#wsblc_activity_box').html(data.text);
|
171 |
-
<?php if ( $this->conf->options['autoexpand_widget'] ) { ?>
|
172 |
-
//Expand the widget if there are broken links.
|
173 |
-
//Do this only once per pageload so as not to annoy the user.
|
174 |
-
if ( !blc_was_autoexpanded && ( data.status.broken_links > 0 ) ){
|
175 |
-
$('#blc_dashboard_widget.postbox').removeClass('closed');
|
176 |
-
blc_was_autoexpanded = true;
|
177 |
-
}
|
178 |
-
<?php } ?>
|
179 |
-
} else {
|
180 |
-
$('#wsblc_activity_box').html('<?php _e('[ Network error ]', 'broken-link-checker'); ?>');
|
181 |
-
}
|
182 |
-
|
183 |
-
setTimeout( blcDashboardStatus, 120*1000 ); //...update every two minutes
|
184 |
}
|
185 |
);
|
186 |
}
|
|
|
|
|
187 |
|
188 |
-
|
|
|
189 |
|
190 |
-
}
|
191 |
-
|
192 |
-
|
193 |
-
|
|
|
194 |
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
202 |
}
|
203 |
|
204 |
-
|
205 |
-
|
206 |
-
<
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
224 |
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
wp_enqueue_script('sprintf', plugins_url('js/sprintf.js', BLC_PLUGIN_FILE)); //Used in error messages
|
230 |
-
}
|
231 |
|
232 |
-
|
233 |
-
* Initiate a full recheck - reparse everything and check all links anew.
|
234 |
-
*
|
235 |
-
* @return void
|
236 |
-
*/
|
237 |
-
function initiate_recheck(){
|
238 |
-
global $wpdb; /** @var wpdb $wpdb */
|
239 |
|
240 |
-
|
241 |
-
|
|
|
|
|
242 |
|
243 |
-
|
244 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
245 |
|
246 |
-
|
247 |
-
|
248 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
249 |
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
270 |
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
blcUtility::optimize_database();
|
285 |
-
}
|
286 |
|
287 |
-
|
288 |
-
* Create the plugin's menu items and enqueue their scripts and CSS.
|
289 |
-
* Callback for the 'admin_menu' action.
|
290 |
-
*
|
291 |
-
* @return void
|
292 |
-
*/
|
293 |
-
function admin_menu(){
|
294 |
-
if (current_user_can('manage_options'))
|
295 |
-
add_filter('plugin_action_links', array($this, 'plugin_action_links'), 10, 2);
|
296 |
-
|
297 |
-
$options_page_hook = add_options_page(
|
298 |
-
__('Link Checker Settings', 'broken-link-checker'),
|
299 |
-
__('Link Checker', 'broken-link-checker'),
|
300 |
-
'manage_options',
|
301 |
-
'link-checker-settings',array($this, 'options_page')
|
302 |
-
);
|
303 |
-
|
304 |
-
$menu_title = __('Broken Links', 'broken-link-checker');
|
305 |
-
if ( $this->conf->options['show_link_count_bubble'] ){
|
306 |
-
//To make it easier to notice when broken links appear, display the current number of
|
307 |
-
//broken links in a little bubble notification in the "Broken Links" menu.
|
308 |
-
//(Similar to how the number of plugin updates and unmoderated comments is displayed).
|
309 |
-
$blc_link_query = blcLinkQuery::getInstance();
|
310 |
-
$broken_links = $blc_link_query->get_filter_links('broken', array('count_only' => true));
|
311 |
-
if ( $broken_links > 0 ){
|
312 |
-
//TODO: Appropriating existing CSS classes for my own purposes is hacky. Fix eventually.
|
313 |
-
$menu_title .= sprintf(
|
314 |
-
' <span class="update-plugins"><span class="update-count blc-menu-bubble">%d</span></span>',
|
315 |
-
$broken_links
|
316 |
-
);
|
317 |
-
}
|
318 |
}
|
319 |
-
$links_page_hook = add_management_page(
|
320 |
-
__('View Broken Links', 'broken-link-checker'),
|
321 |
-
$menu_title,
|
322 |
-
'edit_others_posts',
|
323 |
-
'view-broken-links',array($this, 'links_page')
|
324 |
-
);
|
325 |
-
|
326 |
-
//Add plugin-specific scripts and CSS only to the it's own pages
|
327 |
-
add_action( 'admin_print_styles-' . $options_page_hook, array($this, 'options_page_css') );
|
328 |
-
add_action( 'admin_print_styles-' . $links_page_hook, array($this, 'links_page_css') );
|
329 |
-
add_action( 'admin_print_scripts-' . $options_page_hook, array($this, 'enqueue_settings_scripts') );
|
330 |
-
add_action( 'admin_print_scripts-' . $links_page_hook, array($this, 'enqueue_link_page_scripts') );
|
331 |
-
|
332 |
-
//Make the Settings page link to the link list
|
333 |
-
add_screen_meta_link(
|
334 |
-
'blc-links-page-link',
|
335 |
-
__('Go to Broken Links', 'broken-link-checker'),
|
336 |
-
admin_url('tools.php?page=view-broken-links'),
|
337 |
-
$options_page_hook,
|
338 |
-
array('style' => 'font-weight: bold;')
|
339 |
-
);
|
340 |
-
}
|
341 |
-
|
342 |
-
/**
|
343 |
-
* plugin_action_links()
|
344 |
-
* Handler for the 'plugin_action_links' hook. Adds a "Settings" link to this plugin's entry
|
345 |
-
* on the plugin list.
|
346 |
-
*
|
347 |
-
* @param array $links
|
348 |
-
* @param string $file
|
349 |
-
* @return array
|
350 |
-
*/
|
351 |
-
function plugin_action_links($links, $file) {
|
352 |
-
if ($file == $this->my_basename)
|
353 |
-
$links[] = "<a href='options-general.php?page=link-checker-settings'>" . __('Settings') . "</a>";
|
354 |
-
return $links;
|
355 |
-
}
|
356 |
-
|
357 |
-
function options_page(){
|
358 |
-
$moduleManager = blcModuleManager::getInstance();
|
359 |
-
|
360 |
-
//Prior to 1.5.2 (released 2012-05-27), there was a bug that would cause the donation flag to be
|
361 |
-
//set incorrectly. So we'll unset the flag in that case.
|
362 |
-
$reset_donation_flag =
|
363 |
-
($this->conf->get('first_installation_timestamp', 0) < strtotime('2012-05-27 00:00')) &&
|
364 |
-
!$this->conf->get('donation_flag_fixed', false);
|
365 |
-
|
366 |
-
if ( $reset_donation_flag) {
|
367 |
-
$this->conf->set('user_has_donated', false);
|
368 |
-
$this->conf->set('donation_flag_fixed', true);
|
369 |
-
$this->conf->save_options();
|
370 |
-
}
|
371 |
-
|
372 |
-
if (isset($_POST['recheck']) && !empty($_POST['recheck']) ){
|
373 |
-
$this->initiate_recheck();
|
374 |
-
|
375 |
-
//Redirect back to the settings page
|
376 |
-
$base_url = remove_query_arg( array('_wpnonce', 'noheader', 'updated', 'error', 'action', 'message') );
|
377 |
-
wp_redirect( add_query_arg( array( 'recheck-initiated' => true), $base_url ) );
|
378 |
-
die();
|
379 |
-
}
|
380 |
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
|
|
|
|
389 |
|
390 |
-
|
391 |
-
|
|
|
|
|
|
|
|
|
|
|
392 |
|
393 |
-
$
|
394 |
-
if (
|
395 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
396 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
397 |
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
402 |
}
|
|
|
|
|
403 |
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
|
|
|
|
|
|
|
|
|
|
410 |
}
|
411 |
-
|
412 |
-
if ( empty($
|
413 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
414 |
}
|
415 |
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
|
|
|
|
|
|
|
|
420 |
|
421 |
-
$
|
|
|
422 |
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
}
|
428 |
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
|
435 |
-
|
436 |
-
|
437 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
438 |
|
439 |
-
|
440 |
-
|
441 |
-
|
|
|
442 |
|
443 |
-
|
444 |
|
445 |
-
|
|
|
|
|
|
|
|
|
446 |
|
447 |
-
|
448 |
-
|
449 |
-
'/[\s\r\n]+/', //split on newlines and whitespace
|
450 |
-
$cleanPost['exclusion_list'],
|
451 |
-
-1,
|
452 |
-
PREG_SPLIT_NO_EMPTY //skip empty values
|
453 |
-
)
|
454 |
-
);
|
455 |
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
);
|
460 |
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
$this->conf->options['custom_fields'] = $new_custom_fields;
|
465 |
-
|
466 |
-
//Parse the custom field list
|
467 |
-
$new_acf_fields = array_filter(preg_split('/[\r\n]+/', $cleanPost['blc_acf_fields'], -1, PREG_SPLIT_NO_EMPTY));
|
468 |
-
|
469 |
-
//Calculate the difference between the old custom field list and the new one (used later)
|
470 |
-
$acf_fields_diff1 = array_diff($new_acf_fields, $this->conf->options['acf_fields']);
|
471 |
-
$acf_fields_diff2 = array_diff($this->conf->options['acf_fields'], $new_acf_fields);
|
472 |
-
$this->conf->options['acf_fields'] = $new_acf_fields;
|
473 |
-
|
474 |
-
//Turning off warnings turns existing warnings into "broken" links.
|
475 |
-
$warnings_enabled = !empty($_POST['warnings_enabled']);
|
476 |
-
if ( $this->conf->get('warnings_enabled') && !$warnings_enabled ) {
|
477 |
-
$this->promote_warnings_to_broken();
|
478 |
-
}
|
479 |
-
$this->conf->options['warnings_enabled'] = $warnings_enabled;
|
480 |
-
|
481 |
-
//HTTP timeout
|
482 |
-
$new_timeout = intval($_POST['timeout']);
|
483 |
-
if( $new_timeout > 0 ){
|
484 |
-
$this->conf->options['timeout'] = $new_timeout ;
|
485 |
-
}
|
486 |
-
|
487 |
-
//Server load limit
|
488 |
-
if ( isset($_POST['server_load_limit']) ){
|
489 |
-
$this->conf->options['server_load_limit'] = floatval($_POST['server_load_limit']);
|
490 |
-
if ( $this->conf->options['server_load_limit'] < 0 ){
|
491 |
-
$this->conf->options['server_load_limit'] = 0;
|
492 |
-
}
|
493 |
-
|
494 |
-
$this->conf->options['enable_load_limit'] = $this->conf->options['server_load_limit'] > 0;
|
495 |
-
}
|
496 |
-
|
497 |
-
//Target resource usage (1% to 100%)
|
498 |
-
if ( isset($_POST['target_resource_usage']) ) {
|
499 |
-
$usage = floatval($_POST['target_resource_usage']);
|
500 |
-
$usage = max(min($usage / 100, 1), 0.01);
|
501 |
-
$this->conf->options['target_resource_usage'] = $usage;
|
502 |
-
}
|
503 |
-
|
504 |
-
//When to run the checker
|
505 |
-
$this->conf->options['run_in_dashboard'] = !empty($_POST['run_in_dashboard']);
|
506 |
-
$this->conf->options['run_via_cron'] = !empty($_POST['run_via_cron']);
|
507 |
-
|
508 |
-
//Email notifications on/off
|
509 |
-
$email_notifications = !empty($_POST['send_email_notifications']);
|
510 |
-
$send_authors_email_notifications = !empty($_POST['send_authors_email_notifications']);
|
511 |
-
|
512 |
-
if (
|
513 |
-
($email_notifications && !$this->conf->options['send_email_notifications'])
|
514 |
-
|| ($send_authors_email_notifications && !$this->conf->options['send_authors_email_notifications'])
|
515 |
-
){
|
516 |
-
/*
|
517 |
-
The plugin should only send notifications about links that have become broken
|
518 |
-
since the time when email notifications were turned on. If we don't do this,
|
519 |
-
the first email notification will be sent nigh-immediately and list *all* broken
|
520 |
-
links that the plugin currently knows about.
|
521 |
-
*/
|
522 |
-
$this->conf->options['last_notification_sent'] = time();
|
523 |
-
}
|
524 |
-
$this->conf->options['send_email_notifications'] = $email_notifications;
|
525 |
-
$this->conf->options['send_authors_email_notifications'] = $send_authors_email_notifications;
|
526 |
|
527 |
-
|
528 |
-
|
529 |
-
$this->conf->options['
|
530 |
-
}
|
531 |
|
532 |
-
|
533 |
-
if ( !empty($widget_cap) ) {
|
534 |
-
$this->conf->options['dashboard_widget_capability'] = $widget_cap;
|
535 |
-
}
|
536 |
|
537 |
-
|
538 |
-
$show_link_actions = array();
|
539 |
-
foreach(array_keys($available_link_actions) as $action) {
|
540 |
-
$show_link_actions[$action] = isset($_POST['show_link_actions']) &&
|
541 |
-
!empty($_POST['show_link_actions'][$action]);
|
542 |
-
}
|
543 |
-
$this->conf->set('show_link_actions', $show_link_actions);
|
544 |
|
545 |
-
|
546 |
-
|
547 |
-
|
|
|
|
|
|
|
|
|
|
|
548 |
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
563 |
}
|
|
|
564 |
}
|
565 |
|
566 |
-
|
|
|
|
|
|
|
|
|
|
|
567 |
|
568 |
-
//
|
569 |
-
|
570 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
571 |
}
|
|
|
|
|
|
|
572 |
|
573 |
-
|
574 |
-
|
575 |
-
$this->conf->options['logging_enabled'] = false;
|
576 |
}
|
577 |
-
}
|
578 |
|
579 |
-
|
580 |
-
|
|
|
|
|
581 |
|
582 |
-
|
|
|
|
|
|
|
|
|
|
|
583 |
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
594 |
}
|
595 |
-
}
|
596 |
|
597 |
-
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
606 |
blc_got_unsynched_items();
|
|
|
|
|
607 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
608 |
}
|
609 |
|
610 |
-
//
|
611 |
-
if ( $
|
612 |
-
|
613 |
-
$overlord->enabled_post_statuses = $this->conf->get('enabled_post_statuses', array());
|
614 |
-
$overlord->resynch('wsh_status_resynch_trigger');
|
615 |
|
616 |
-
blc_got_unsynched_items();
|
617 |
-
blc_cleanup_instances();
|
618 |
-
blc_cleanup_links();
|
619 |
}
|
620 |
|
621 |
-
//
|
622 |
-
|
623 |
-
|
624 |
-
|
625 |
-
|
626 |
-
|
627 |
-
|
628 |
-
|
629 |
-
|
630 |
-
|
631 |
-
|
632 |
-
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
|
638 |
-
|
639 |
-
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
|
646 |
-
|
647 |
-
|
648 |
-
|
649 |
-
|
650 |
-
|
651 |
-
|
652 |
-
|
653 |
-
|
654 |
-
|
655 |
-
|
656 |
-
|
657 |
-
|
658 |
-
|
659 |
-
|
660 |
-
|
661 |
-
|
662 |
-
|
663 |
-
|
664 |
-
|
665 |
-
|
666 |
-
|
667 |
-
|
668 |
-
|
669 |
-
|
670 |
-
|
671 |
-
|
672 |
-
|
673 |
-
|
674 |
-
|
675 |
-
|
676 |
-
|
677 |
-
|
678 |
-
|
679 |
-
</style>
|
680 |
-
<![endif]-->
|
681 |
-
|
682 |
-
<div class="wrap" id="blc-settings-wrap">
|
683 |
-
<h2><?php _e('Broken Link Checker Options', 'broken-link-checker'); ?></h2>
|
684 |
-
|
685 |
-
|
686 |
-
<div id="blc-sidebar">
|
687 |
-
<div class="metabox-holder">
|
688 |
-
<?php include BLC_DIRECTORY . '/includes/admin/sidebar.php'; ?>
|
689 |
</div>
|
690 |
-
</div>
|
691 |
|
692 |
|
693 |
-
|
694 |
|
695 |
-
|
696 |
-
|
697 |
-
|
698 |
-
|
699 |
-
|
700 |
-
|
|
|
|
|
701 |
|
702 |
-
|
703 |
|
704 |
-
|
705 |
-
|
706 |
-
foreach($section_names as $section_id => $section_name){
|
707 |
printf(
|
708 |
'<li id="tab-button-%s"><a href="#section-%s" title="%s">%s</a></li>',
|
709 |
-
esc_attr($section_id),
|
710 |
-
esc_attr($section_id),
|
711 |
-
esc_attr($section_name),
|
712 |
$section_name
|
713 |
);
|
714 |
}
|
715 |
-
|
716 |
-
|
717 |
-
|
718 |
-
<div id="section-general" class="blc-section">
|
719 |
-
<h3 class="hide-if-js"><?php echo $section_names['general']; ?></h3>
|
720 |
-
|
721 |
-
<table class="form-table">
|
722 |
-
|
723 |
-
<tr valign="top">
|
724 |
-
<th scope="row">
|
725 |
-
<?php _e('Status','broken-link-checker'); ?>
|
726 |
-
<br>
|
727 |
-
<a href="javascript:void(0)" id="blc-debug-info-toggle"><?php _e('Show debug info', 'broken-link-checker'); ?></a>
|
728 |
-
</th>
|
729 |
-
<td>
|
730 |
-
|
731 |
-
<div id='wsblc_full_status'>
|
732 |
-
<br/><br/><br/>
|
733 |
-
</div>
|
734 |
-
|
735 |
-
<table id="blc-debug-info">
|
736 |
-
<?php
|
737 |
-
|
738 |
-
//Output the debug info in a table
|
739 |
-
foreach( $debug as $key => $value ){
|
740 |
-
printf (
|
741 |
-
'<tr valign="top" class="blc-debug-item-%s"><th scope="row">%s</th><td>%s<div class="blc-debug-message">%s</div></td></tr>',
|
742 |
-
$value['state'],
|
743 |
-
$key,
|
744 |
-
$value['value'],
|
745 |
-
( array_key_exists('message', $value)?$value['message']:'')
|
746 |
-
);
|
747 |
-
}
|
748 |
-
?>
|
749 |
-
</table>
|
750 |
|
751 |
-
|
752 |
-
|
753 |
|
754 |
-
|
755 |
-
<th scope="row"><?php _e('Check each link','broken-link-checker'); ?></th>
|
756 |
-
<td>
|
757 |
|
758 |
-
|
759 |
-
|
760 |
-
|
761 |
-
|
762 |
-
|
763 |
-
|
764 |
-
)
|
765 |
-
);
|
766 |
-
?>
|
767 |
-
<br/>
|
768 |
-
<span class="description">
|
769 |
-
<?php _e('Existing links will be checked this often. New links will usually be checked ASAP.', 'broken-link-checker'); ?>
|
770 |
-
</span>
|
771 |
-
|
772 |
-
</td>
|
773 |
-
</tr>
|
774 |
-
|
775 |
-
<tr valign="top">
|
776 |
-
<th scope="row"><?php _e('E-mail notifications', 'broken-link-checker'); ?></th>
|
777 |
-
<td>
|
778 |
-
<p style="margin-top: 0;">
|
779 |
-
<label for='send_email_notifications'>
|
780 |
-
<input type="checkbox" name="send_email_notifications" id="send_email_notifications"
|
781 |
-
<?php if ($this->conf->options['send_email_notifications']) echo ' checked="checked"'; ?>/>
|
782 |
-
<?php _e('Send me e-mail notifications about newly detected broken links', 'broken-link-checker'); ?>
|
783 |
-
</label><br />
|
784 |
-
</p>
|
785 |
-
|
786 |
-
<p>
|
787 |
-
<label for='send_authors_email_notifications'>
|
788 |
-
<input type="checkbox" name="send_authors_email_notifications" id="send_authors_email_notifications"
|
789 |
-
<?php if ($this->conf->options['send_authors_email_notifications']) echo ' checked="checked"'; ?>/>
|
790 |
-
<?php _e('Send authors e-mail notifications about broken links in their posts', 'broken-link-checker'); ?>
|
791 |
-
</label><br />
|
792 |
-
</p>
|
793 |
-
</td>
|
794 |
-
</tr>
|
795 |
-
|
796 |
-
<tr valign="top">
|
797 |
-
<th scope="row"><?php echo __('Notification e-mail address', 'broken-link-checker'); ?></th>
|
798 |
<td>
|
799 |
-
<p>
|
800 |
-
<label>
|
801 |
-
<input
|
802 |
-
type="text"
|
803 |
-
name="notification_email_address"
|
804 |
-
id="notification_email_address"
|
805 |
-
value="<?php echo esc_attr($this->conf->get('notification_email_address', '')); ?>"
|
806 |
-
class="regular-text ltr">
|
807 |
-
</label><br>
|
808 |
-
<span class="description">
|
809 |
-
<?php echo __('Leave empty to use the e-mail address specified in Settings → General.', 'broken-link-checker'); ?>
|
810 |
-
</span>
|
811 |
-
</p>
|
812 |
-
</td>
|
813 |
-
</tr>
|
814 |
|
815 |
-
|
816 |
-
|
817 |
-
|
818 |
-
<p style="margin-top: 0; margin-bottom: 0.5em;">
|
819 |
-
<label for='mark_broken_links'>
|
820 |
-
<input type="checkbox" name="mark_broken_links" id="mark_broken_links"
|
821 |
-
<?php if ($this->conf->options['mark_broken_links']) echo ' checked="checked"'; ?>/>
|
822 |
-
<?php _e('Apply custom formatting to broken links', 'broken-link-checker'); ?>
|
823 |
-
</label>
|
824 |
-
|
|
825 |
-
<a id="toggle-broken-link-css-editor" href="#" class="blc-toggle-link"><?php
|
826 |
-
_e('Edit CSS', 'broken-link-checker');
|
827 |
-
?></a>
|
828 |
-
</p>
|
829 |
|
830 |
-
<
|
831 |
-
|
832 |
-
|
833 |
-
|
834 |
-
|
835 |
-
<textarea name="broken_link_css" id="broken_link_css" cols='45' rows='4'><?php
|
836 |
-
if( isset($this->conf->options['broken_link_css']) ) {
|
837 |
-
echo $this->conf->options['broken_link_css'];
|
838 |
-
}
|
839 |
-
?></textarea>
|
840 |
-
<p class="description"><?php
|
841 |
printf(
|
842 |
-
|
843 |
-
'
|
|
|
|
|
|
|
844 |
);
|
845 |
-
echo ' ', __('Click "Save Changes" to update example output.', 'broken-link-checker');
|
846 |
-
?></p>
|
847 |
-
</div>
|
848 |
-
|
849 |
-
<p style="margin-bottom: 0.5em;">
|
850 |
-
<label for='mark_removed_links'>
|
851 |
-
<input type="checkbox" name="mark_removed_links" id="mark_removed_links"
|
852 |
-
<?php if ($this->conf->options['mark_removed_links']) echo ' checked="checked"'; ?>/>
|
853 |
-
<?php _e('Apply custom formatting to removed links', 'broken-link-checker'); ?>
|
854 |
-
</label>
|
855 |
-
|
|
856 |
-
<a id="toggle-removed-link-css-editor" href="#" class="blc-toggle-link"><?php
|
857 |
-
_e('Edit CSS', 'broken-link-checker');
|
858 |
-
?></a>
|
859 |
-
</p>
|
860 |
-
|
861 |
-
<div id="removed-link-css-wrap" <?php
|
862 |
-
if ( !blcUtility::get_cookie('removed-link-css-wrap', false) ){
|
863 |
-
echo ' class="hidden"';
|
864 |
}
|
865 |
-
?>>
|
866 |
-
<textarea name="removed_link_css" id="removed_link_css" cols='45' rows='4'><?php
|
867 |
-
if( isset($this->conf->options['removed_link_css']) )
|
868 |
-
echo $this->conf->options['removed_link_css'];
|
869 |
-
?></textarea>
|
870 |
-
|
871 |
-
<p class="description"><?php
|
872 |
-
printf(
|
873 |
-
__('Example : Lorem ipsum <span %s>removed link</span>, dolor sit amet.', 'broken-link-checker'),
|
874 |
-
' class="removed_link"'
|
875 |
-
);
|
876 |
-
echo ' ', __('Click "Save Changes" to update example output.', 'broken-link-checker');
|
877 |
?>
|
|
|
878 |
|
879 |
-
|
880 |
-
|
881 |
|
882 |
-
|
883 |
-
|
884 |
-
|
885 |
-
<?php if ($this->conf->options['nofollow_broken_links']) echo ' checked="checked"'; ?>/>
|
886 |
-
<?php _e('Stop search engines from following broken links', 'broken-link-checker'); ?>
|
887 |
-
</label>
|
888 |
-
</p>
|
889 |
|
890 |
-
<p class="description">
|
891 |
<?php
|
892 |
-
|
893 |
-
'
|
894 |
-
|
895 |
-
|
|
|
|
|
896 |
);
|
897 |
?>
|
898 |
-
|
899 |
-
|
900 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
901 |
|
902 |
<tr valign="top">
|
903 |
-
<th scope="row"><?php echo
|
904 |
<td>
|
|
|
905 |
<label>
|
906 |
-
<input
|
907 |
-
|
908 |
-
|
909 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
910 |
</td>
|
911 |
</tr>
|
912 |
|
913 |
<tr valign="top">
|
914 |
-
|
915 |
-
|
916 |
-
|
917 |
-
|
918 |
-
|
919 |
-
|
920 |
-
|
921 |
-
|
922 |
-
|
923 |
-
|
924 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
925 |
</tr>
|
926 |
|
927 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
928 |
|
929 |
-
|
930 |
|
931 |
-
|
932 |
-
|
933 |
|
934 |
-
|
935 |
|
936 |
-
|
937 |
-
|
938 |
-
|
939 |
-
|
940 |
-
|
941 |
-
|
942 |
-
|
943 |
-
|
944 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
945 |
}
|
946 |
-
);
|
947 |
-
$this->print_module_list($modules['container'], $this->conf->options);
|
948 |
-
}
|
949 |
-
?>
|
950 |
-
</td></tr>
|
951 |
-
|
952 |
-
<tr valign="top">
|
953 |
-
<th scope="row"><?php _e('Post statuses', 'broken-link-checker'); ?></th>
|
954 |
-
<td>
|
955 |
-
<?php
|
956 |
-
$available_statuses = get_post_stati(array('internal' => false), 'objects');
|
957 |
-
|
958 |
-
if ( isset($this->conf->options['enabled_post_statuses']) ){
|
959 |
-
$enabled_post_statuses = $this->conf->options['enabled_post_statuses'];
|
960 |
-
} else {
|
961 |
-
$enabled_post_statuses = array();
|
962 |
-
}
|
963 |
-
|
964 |
-
foreach($available_statuses as $status => $status_object){
|
965 |
-
printf(
|
966 |
-
'<p><label><input type="checkbox" name="enabled_post_statuses[]" value="%s"%s> %s</label></p>',
|
967 |
-
esc_attr($status),
|
968 |
-
in_array($status, $enabled_post_statuses)?' checked="checked"':'',
|
969 |
-
$status_object->label
|
970 |
-
);
|
971 |
-
}
|
972 |
-
?>
|
973 |
-
</td></tr>
|
974 |
|
975 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
976 |
|
977 |
-
|
978 |
|
979 |
|
980 |
-
|
981 |
<h3 class="hide-if-js"><?php echo $section_names['which']; ?></h3>
|
982 |
|
983 |
-
|
984 |
-
|
985 |
-
|
986 |
-
|
987 |
-
|
988 |
-
|
989 |
-
|
990 |
-
|
991 |
-
|
992 |
-
|
993 |
-
|
994 |
-
|
995 |
-
|
996 |
</tr>
|
997 |
|
998 |
-
|
999 |
-
|
1000 |
-
|
1001 |
-
|
1002 |
-
|
1003 |
-
|
1004 |
-
|
|
|
|
|
|
|
1005 |
|
1006 |
-
|
1007 |
-
|
1008 |
|
1009 |
-
|
1010 |
-
|
1011 |
|
1012 |
-
|
1013 |
<h3 class="hide-if-js"><?php echo $section_names['how']; ?></h3>
|
1014 |
|
1015 |
-
|
1016 |
|
1017 |
-
|
1018 |
-
|
1019 |
-
|
1020 |
-
|
1021 |
-
|
1022 |
-
|
1023 |
-
|
1024 |
-
|
1025 |
-
|
1026 |
-
|
1027 |
|
1028 |
-
|
1029 |
-
|
1030 |
|
1031 |
-
|
1032 |
<h3 class="hide-if-js"><?php echo $section_names['advanced']; ?></h3>
|
1033 |
|
1034 |
-
|
1035 |
|
1036 |
-
|
1037 |
-
|
1038 |
-
|
1039 |
|
1040 |
-
|
1041 |
|
1042 |
-
|
1043 |
-
|
1044 |
-
|
1045 |
-
|
1046 |
-
|
1047 |
-
|
1048 |
-
|
1049 |
|
1050 |
-
|
1051 |
-
|
1052 |
-
|
1053 |
</span>
|
1054 |
|
1055 |
-
|
1056 |
-
|
1057 |
|
1058 |
-
|
1059 |
-
|
1060 |
-
|
1061 |
|
1062 |
-
|
1063 |
<label for='run_in_dashboard'>
|
1064 |
|
1065 |
-
|
1066 |
-
|
1067 |
-
|
|
|
|
|
|
|
|
|
1068 |
</label>
|
1069 |
</p>
|
1070 |
|
1071 |
<p>
|
1072 |
<label for='run_via_cron'>
|
1073 |
-
|
1074 |
-
|
1075 |
-
|
|
|
|
|
|
|
|
|
1076 |
</label>
|
1077 |
</p>
|
1078 |
|
1079 |
-
|
1080 |
-
|
1081 |
|
1082 |
-
|
1083 |
-
|
1084 |
-
|
1085 |
|
1086 |
-
|
1087 |
$widget_caps = array(
|
1088 |
-
_x('Administrator', 'dashboard widget visibility', 'broken-link-checker') => 'manage_options',
|
1089 |
-
_x('Editor and above', 'dashboard widget visibility', 'broken-link-checker') => 'edit_others_posts',
|
1090 |
-
_x('Nobody (disables the widget)', 'dashboard widget visibility', 'broken-link-checker') => 'do_not_allow',
|
1091 |
);
|
1092 |
|
1093 |
-
|
1094 |
-
|
1095 |
-
|
1096 |
-
|
1097 |
-
|
1098 |
-
|
1099 |
-
|
1100 |
-
|
1101 |
-
|
1102 |
-
|
1103 |
-
|
1104 |
|
1105 |
<tr valign="top">
|
1106 |
-
<th scope="row"><?php echo _x('Show link actions', 'settings page', 'broken-link-checker'); ?></th>
|
1107 |
<td>
|
1108 |
<?php
|
1109 |
-
$show_link_actions = $this->conf->get('show_link_actions', array());
|
1110 |
-
foreach($available_link_actions as $action => $text) {
|
1111 |
-
$enabled = isset($show_link_actions[$action]) ? (bool)($show_link_actions[$action]) : true;
|
1112 |
printf(
|
1113 |
'<p><label><input type="checkbox" name="show_link_actions[%1$s]" %3$s> %2$s</label></p>',
|
1114 |
$action,
|
1115 |
$text,
|
1116 |
-
checked($enabled, true, false)
|
1117 |
);
|
1118 |
}
|
1119 |
?>
|
1120 |
</td>
|
1121 |
</tr>
|
1122 |
|
1123 |
-
|
1124 |
-
|
1125 |
-
|
1126 |
|
1127 |
-
|
1128 |
|
1129 |
-
|
1130 |
-
|
1131 |
-
|
1132 |
-
|
1133 |
-
|
1134 |
-
|
1135 |
-
|
1136 |
|
1137 |
-
|
1138 |
-
|
1139 |
-
|
1140 |
|
1141 |
-
|
1142 |
|
1143 |
-
|
1144 |
</span>
|
1145 |
|
1146 |
-
|
1147 |
-
|
1148 |
|
1149 |
-
|
1150 |
-
|
1151 |
-
|
1152 |
-
|
1153 |
|
1154 |
-
|
1155 |
-
|
1156 |
|
1157 |
-
|
1158 |
-
|
1159 |
-
|
1160 |
-
|
1161 |
-
|
1162 |
-
|
1163 |
|
1164 |
-
|
1165 |
-
|
1166 |
-
|
1167 |
-
|
1168 |
-
|
1169 |
-
|
1170 |
-
|
1171 |
-
|
1172 |
-
|
1173 |
-
|
1174 |
-
|
1175 |
-
|
1176 |
-
|
1177 |
|
1178 |
-
|
1179 |
-
|
1180 |
-
|
1181 |
-
|
1182 |
-
|
1183 |
-
|
1184 |
-
|
1185 |
-
|
1186 |
-
|
1187 |
|
1188 |
<tr valign="top">
|
1189 |
-
<th scope="row"><?php _e('Target resource usage', 'broken-link-checker'); ?></th>
|
1190 |
<td>
|
1191 |
<?php
|
1192 |
-
$target_resource_usage = $this->conf->get('target_resource_usage', 0.25);
|
1193 |
printf(
|
1194 |
'<input name="target_resource_usage" value="%d"
|
1195 |
type="range" min="1" max="100" id="target_resource_usage">',
|
@@ -1197,27 +1270,29 @@ class wsBrokenLinkChecker {
|
|
1197 |
);
|
1198 |
?>
|
1199 |
|
1200 |
-
<span id="target_resource_usage_percent"
|
1201 |
-
|
1202 |
-
|
|
|
|
|
1203 |
</td>
|
1204 |
</tr>
|
1205 |
|
1206 |
<tr valign="top">
|
1207 |
-
<th scope="row"><?php _e('Logging', 'broken-link-checker'); ?></th>
|
1208 |
<td>
|
1209 |
<p>
|
1210 |
<label for='logging_enabled'>
|
1211 |
<input type="checkbox" name="logging_enabled" id="logging_enabled"
|
1212 |
-
<?php checked($this->conf->options['logging_enabled']); ?>/>
|
1213 |
-
<?php _e('Enable logging', 'broken-link-checker'); ?>
|
1214 |
</label>
|
1215 |
</p>
|
1216 |
</td>
|
1217 |
</tr>
|
1218 |
|
1219 |
<tr valign="top">
|
1220 |
-
<th scope="row"><?php _e('Log file location', 'broken-link-checker'); ?></th>
|
1221 |
<td>
|
1222 |
|
1223 |
<div id="blc-logging-options">
|
@@ -1225,25 +1300,27 @@ class wsBrokenLinkChecker {
|
|
1225 |
<p>
|
1226 |
<label>
|
1227 |
<input type="radio" name="custom_log_file_enabled" value=""
|
1228 |
-
<?php checked(
|
1229 |
-
<?php echo _x('Default', 'log file location', 'broken-link-checker'); ?>
|
1230 |
</label>
|
1231 |
<br>
|
1232 |
<span class="description">
|
1233 |
-
<code
|
|
|
1234 |
echo self::get_default_log_directory(), '/', self::get_default_log_basename();
|
1235 |
-
|
|
|
1236 |
</span>
|
1237 |
</p>
|
1238 |
|
1239 |
<p>
|
1240 |
<label>
|
1241 |
<input type="radio" name="custom_log_file_enabled" value="1"
|
1242 |
-
<?php checked($this->conf->options['custom_log_file_enabled']); ?>>
|
1243 |
-
<?php echo _x('Custom', 'log file location', 'broken-link-checker'); ?>
|
1244 |
</label>
|
1245 |
<br><input type="text" name="log_file" id="log_file" size="90"
|
1246 |
-
value="<?php echo esc_attr($this->conf->options['log_file']); ?>">
|
1247 |
</p>
|
1248 |
|
1249 |
</div>
|
@@ -1251,2591 +1328,2682 @@ class wsBrokenLinkChecker {
|
|
1251 |
</tr>
|
1252 |
|
1253 |
|
1254 |
-
|
1255 |
-
|
1256 |
-
|
1257 |
-
|
1258 |
-
value="<?php _e('Re-check all pages', 'broken-link-checker'); ?>" />
|
1259 |
-
|
1260 |
<br />
|
1261 |
-
|
1262 |
-
|
|
|
1263 |
|
1264 |
-
|
|
|
1265 |
</td>
|
1266 |
</tr>
|
1267 |
|
1268 |
-
|
1269 |
-
|
1270 |
-
|
1271 |
-
</div>
|
1272 |
-
|
1273 |
-
<p class="submit"><input type="submit" name="submit" class='button-primary' value="<?php _e('Save Changes') ?>" /></p>
|
1274 |
-
</form>
|
1275 |
-
|
1276 |
-
</div> <!-- First postbox-container -->
|
1277 |
|
|
|
1278 |
|
1279 |
-
|
|
|
1280 |
|
|
|
1281 |
|
1282 |
|
1283 |
-
|
1284 |
-
//The various JS for this page is stored in a separate file for the purposes readability.
|
1285 |
-
include dirname($this->loader) . '/includes/admin/options-page-js.php';
|
1286 |
-
}
|
1287 |
|
1288 |
-
|
1289 |
-
|
1290 |
-
|
1291 |
-
|
1292 |
-
* currently active.
|
1293 |
-
*
|
1294 |
-
* @param array $modules Array of modules to display
|
1295 |
-
* @param array $current_settings
|
1296 |
-
* @return void
|
1297 |
-
*/
|
1298 |
-
function print_module_list($modules, $current_settings){
|
1299 |
-
$moduleManager = blcModuleManager::getInstance();
|
1300 |
|
1301 |
-
foreach($modules as $module_id => $module_data){
|
1302 |
-
$module_id = $module_data['ModuleID'];
|
1303 |
|
1304 |
-
$style = $module_data['ModuleHidden']?' style="display:none;"':'';
|
1305 |
|
1306 |
-
|
1307 |
-
|
1308 |
-
|
1309 |
-
|
1310 |
-
|
1311 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
1312 |
|
1313 |
-
$
|
1314 |
-
'
|
1315 |
-
'',
|
1316 |
-
$current_settings
|
1317 |
-
);
|
1318 |
|
1319 |
-
|
1320 |
|
1321 |
printf(
|
1322 |
-
'
|
1323 |
-
|
1324 |
-
|
1325 |
);
|
|
|
1326 |
|
1327 |
-
|
1328 |
-
|
1329 |
-
|
1330 |
-
$
|
1331 |
-
$moduleManager->is_active($module_id)
|
1332 |
);
|
1333 |
|
1334 |
-
|
1335 |
-
|
1336 |
-
|
1337 |
-
|
1338 |
-
|
1339 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1340 |
}
|
1341 |
|
1342 |
-
echo '</div>';
|
1343 |
-
}
|
1344 |
-
}
|
1345 |
-
|
1346 |
-
/**
|
1347 |
-
* Output a checkbox for a module.
|
1348 |
-
*
|
1349 |
-
* Generates a simple checkbox that can be used to mark a module as active/inactive.
|
1350 |
-
* If the specified module can't be deactivated (ModuleAlwaysActive = true), the checkbox
|
1351 |
-
* will be displayed in a disabled state and a hidden field will be created to make
|
1352 |
-
* form submissions work correctly.
|
1353 |
-
*
|
1354 |
-
* @param string $module_id Module ID.
|
1355 |
-
* @param array $module_data Associative array of module data.
|
1356 |
-
* @param bool $active If true, the newly created checkbox will start out checked.
|
1357 |
-
* @return void
|
1358 |
-
*/
|
1359 |
-
function print_module_checkbox($module_id, $module_data, $active = false){
|
1360 |
-
$disabled = false;
|
1361 |
-
$name_prefix = 'module';
|
1362 |
-
$label_class = '';
|
1363 |
-
$active = $active || $module_data['ModuleAlwaysActive'];
|
1364 |
-
|
1365 |
-
if ( $module_data['ModuleAlwaysActive'] ){
|
1366 |
-
$disabled = true;
|
1367 |
-
$name_prefix = 'module-always-active';
|
1368 |
-
}
|
1369 |
-
|
1370 |
-
$checked = $active ? ' checked="checked"':'';
|
1371 |
-
if ( $disabled ){
|
1372 |
-
$checked .= ' disabled="disabled"';
|
1373 |
-
}
|
1374 |
-
|
1375 |
-
printf(
|
1376 |
-
'<label class="%s">
|
1377 |
-
<input type="checkbox" name="%s[%s]" id="module-checkbox-%s"%s /> %s
|
1378 |
-
</label>',
|
1379 |
-
esc_attr($label_class),
|
1380 |
-
$name_prefix,
|
1381 |
-
esc_attr($module_id),
|
1382 |
-
esc_attr($module_id),
|
1383 |
-
$checked,
|
1384 |
-
$module_data['Name']
|
1385 |
-
);
|
1386 |
-
|
1387 |
-
if ( $module_data['ModuleAlwaysActive'] ){
|
1388 |
printf(
|
1389 |
-
'<
|
1390 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1391 |
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1392 |
}
|
1393 |
-
}
|
1394 |
-
|
1395 |
-
/**
|
1396 |
-
* Add extra settings to the "Custom fields" entry on the plugin's config. page.
|
1397 |
-
*
|
1398 |
-
* Callback for the 'blc-module-settings-custom_field' filter.
|
1399 |
-
*
|
1400 |
-
* @param string $html Current extra HTML
|
1401 |
-
* @param array $current_settings The current plugin configuration.
|
1402 |
-
* @return string New extra HTML.
|
1403 |
-
*/
|
1404 |
-
function make_custom_field_input($html, $current_settings){
|
1405 |
-
$html .= '<span class="description">' .
|
1406 |
-
__(
|
1407 |
-
'Enter the names of custom fields you want to check (one per line). If a field contains HTML code, prefix its name with <code>html:</code>. For example, <code>html:field_name</code>.',
|
1408 |
-
'broken-link-checker'
|
1409 |
-
) .
|
1410 |
-
'</span>';
|
1411 |
-
$html .= '<br><textarea name="blc_custom_fields" id="blc_custom_fields" cols="45" rows="4">';
|
1412 |
-
if( isset($current_settings['custom_fields']) ){
|
1413 |
-
$html .= esc_textarea(implode("\n", $current_settings['custom_fields']));
|
1414 |
-
}
|
1415 |
-
$html .= '</textarea>';
|
1416 |
-
|
1417 |
-
return $html;
|
1418 |
-
}
|
1419 |
-
function make_acf_field_input($html, $current_settings) {
|
1420 |
-
$html .= '<span class="description">' . __('Enter the keys of acf fields you want to check (one per line). If a field contains HTML code, prefix its name with <code>html:</code>. For example, <code>html:field_586a3eaa4091b</code>.', 'broken-link-checker') . '</span>';
|
1421 |
-
$html .= '<br><textarea name="blc_acf_fields" id="blc_acf_fields" cols="45" rows="4">';
|
1422 |
-
if (isset($current_settings['acf_fields'])) {
|
1423 |
-
$html .= esc_textarea(implode("\n", $current_settings['acf_fields']));
|
1424 |
-
}
|
1425 |
-
$html .= '</textarea>';
|
1426 |
-
|
1427 |
-
return $html;
|
1428 |
-
}
|
1429 |
-
/**
|
1430 |
-
* Enqueue CSS file for the plugin's Settings page.
|
1431 |
-
*
|
1432 |
-
* @return void
|
1433 |
-
*/
|
1434 |
-
function options_page_css(){
|
1435 |
-
wp_enqueue_style('blc-options-page', plugins_url('css/options-page.css', BLC_PLUGIN_FILE), array(), '20141113');
|
1436 |
-
wp_enqueue_style('dashboard');
|
1437 |
-
}
|
1438 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1439 |
|
1440 |
-
|
1441 |
-
|
1442 |
-
|
1443 |
-
|
1444 |
-
|
1445 |
-
|
1446 |
-
|
|
|
|
|
|
|
|
|
1447 |
|
1448 |
-
$blc_link_query = blcLinkQuery::getInstance();
|
1449 |
|
1450 |
-
|
1451 |
-
|
1452 |
-
|
|
|
|
|
|
|
|
|
1453 |
|
1454 |
-
|
1455 |
-
//Make module headers translatable. They need to be formatted corrrectly and
|
1456 |
-
//placed in a .php file to be visible to the script(s) that generate .pot files.
|
1457 |
-
$code = $moduleManager->_build_header_translation_code();
|
1458 |
-
file_put_contents( dirname($this->loader) . '/includes/extra-strings.php', $code );
|
1459 |
-
}
|
1460 |
|
1461 |
-
|
1462 |
-
|
1463 |
-
|
1464 |
-
$action = !empty($_POST['action2'])?$_POST['action2']:'';
|
1465 |
-
}
|
1466 |
|
1467 |
-
|
1468 |
-
|
1469 |
-
|
1470 |
-
|
1471 |
-
|
1472 |
-
|
1473 |
-
|
1474 |
-
|
1475 |
-
|
1476 |
-
|
1477 |
-
|
1478 |
-
|
1479 |
-
|
1480 |
-
|
1481 |
-
|
1482 |
-
|
1483 |
-
|
1484 |
-
|
1485 |
-
|
1486 |
-
|
1487 |
-
|
1488 |
-
|
1489 |
-
|
1490 |
-
|
1491 |
-
|
1492 |
-
|
1493 |
-
|
1494 |
-
|
1495 |
-
|
1496 |
-
|
1497 |
-
|
1498 |
-
|
1499 |
-
|
1500 |
-
|
1501 |
-
|
1502 |
-
|
1503 |
-
|
1504 |
-
|
1505 |
-
|
1506 |
-
|
1507 |
-
|
1508 |
-
|
1509 |
-
|
1510 |
-
|
1511 |
-
|
1512 |
-
|
1513 |
-
|
1514 |
-
|
1515 |
-
|
1516 |
-
|
1517 |
-
|
1518 |
-
|
1519 |
-
|
1520 |
-
|
1521 |
-
|
1522 |
-
|
1523 |
-
|
1524 |
-
|
1525 |
-
|
1526 |
-
|
1527 |
-
|
1528 |
-
|
1529 |
-
|
1530 |
-
|
1531 |
-
|
1532 |
-
|
1533 |
-
|
1534 |
-
|
1535 |
-
|
1536 |
-
|
1537 |
-
|
1538 |
-
|
1539 |
-
|
1540 |
-
$
|
1541 |
-
'broken',
|
1542 |
-
isset($_GET['orderby']) ? $_GET['orderby'] : '',
|
1543 |
-
isset($_GET['order']) ? $_GET['order'] : ''
|
1544 |
-
);
|
1545 |
-
|
1546 |
-
//exec_filter() returns an array with filter data, including the actual filter ID that was used.
|
1547 |
-
$filter_id = $current_filter['filter_id'];
|
1548 |
-
|
1549 |
-
//Error?
|
1550 |
-
if ( empty($current_filter['links']) && !empty($wpdb->last_error) ){
|
1551 |
-
printf( __('Database error : %s', 'broken-link-checker'), $wpdb->last_error);
|
1552 |
-
}
|
1553 |
-
?>
|
1554 |
-
|
1555 |
-
<script type='text/javascript'>
|
1556 |
-
var blc_current_filter = '<?php echo $filter_id; ?>';
|
1557 |
-
var blc_is_broken_filter = <?php echo $current_filter['is_broken_filter'] ? 'true' : 'false'; ?>;
|
1558 |
-
var blc_current_base_filter = '<?php echo esc_js($current_filter['base_filter']); ?>';
|
1559 |
-
var blc_suggestions_enabled = <?php echo $this->conf->options['suggestions_enabled'] ? 'true' : 'false'; ?>;
|
1560 |
-
</script>
|
1561 |
-
|
1562 |
-
<div class="wrap">
|
1563 |
-
<?php
|
1564 |
-
$blc_link_query->print_filter_heading($current_filter);
|
1565 |
-
$blc_link_query->print_filter_menu($filter_id);
|
1566 |
-
|
1567 |
-
//Display the "Search" form and associated buttons.
|
1568 |
-
//The form requires the $filter_id and $current_filter variables to be set.
|
1569 |
-
include dirname($this->loader) . '/includes/admin/search-form.php';
|
1570 |
-
|
1571 |
-
//If the user has decided to switch the table to a different mode (compact/full),
|
1572 |
-
//save the new setting.
|
1573 |
-
if ( isset($_GET['compact']) ){
|
1574 |
-
$this->conf->options['table_compact'] = (bool)$_GET['compact'];
|
1575 |
-
$this->conf->save_options();
|
1576 |
-
}
|
1577 |
|
1578 |
-
|
1579 |
-
|
1580 |
|
1581 |
-
|
1582 |
-
$
|
1583 |
-
|
1584 |
-
|
1585 |
-
|
1586 |
-
|
1587 |
-
$
|
|
|
|
|
|
|
|
|
|
|
1588 |
);
|
1589 |
|
1590 |
-
|
1591 |
-
|
1592 |
-
|
1593 |
-
//Load assorted JS event handlers and other shinies
|
1594 |
-
include dirname($this->loader) . '/includes/admin/links-page-js.php';
|
1595 |
-
|
1596 |
-
?></div><?php
|
1597 |
-
}
|
1598 |
-
|
1599 |
-
/**
|
1600 |
-
* Create a custom link filter using params passed in $_POST.
|
1601 |
-
*
|
1602 |
-
* @uses $_POST
|
1603 |
-
* @uses $_GET to replace the current filter ID (if any) with that of the newly created filter.
|
1604 |
-
*
|
1605 |
-
* @return array Message and the CSS class to apply to the message.
|
1606 |
-
*/
|
1607 |
-
function do_create_custom_filter(){
|
1608 |
-
global $wpdb;
|
1609 |
-
|
1610 |
-
//Create a custom filter!
|
1611 |
-
check_admin_referer( 'create-custom-filter' );
|
1612 |
-
$msg_class = 'updated';
|
1613 |
-
|
1614 |
-
//Filter name must be set
|
1615 |
-
if ( empty($_POST['name']) ){
|
1616 |
-
$message = __("You must enter a filter name!", 'broken-link-checker');
|
1617 |
-
$msg_class = 'error';
|
1618 |
-
//Filter parameters (a search query) must also be set
|
1619 |
-
} elseif ( empty($_POST['params']) ){
|
1620 |
-
$message = __("Invalid search query.", 'broken-link-checker');
|
1621 |
-
$msg_class = 'error';
|
1622 |
-
} else {
|
1623 |
-
//Save the new filter
|
1624 |
-
$name = strip_tags(strval($_POST['name']));
|
1625 |
-
$blc_link_query = blcLinkQuery::getInstance();
|
1626 |
-
$filter_id = $blc_link_query->create_custom_filter($name, $_POST['params']);
|
1627 |
|
1628 |
-
|
1629 |
-
|
1630 |
-
|
1631 |
-
|
1632 |
-
|
1633 |
-
|
1634 |
-
|
1635 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1636 |
$msg_class = 'error';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1637 |
}
|
|
|
|
|
1638 |
}
|
1639 |
|
1640 |
-
|
1641 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1642 |
|
1643 |
-
|
1644 |
-
|
1645 |
-
|
1646 |
-
* @uses $_POST
|
1647 |
-
*
|
1648 |
-
* @return array Message and a CSS class to apply to the message.
|
1649 |
-
*/
|
1650 |
-
function do_delete_custom_filter(){
|
1651 |
-
//Delete an existing custom filter!
|
1652 |
-
check_admin_referer( 'delete-custom-filter' );
|
1653 |
-
$msg_class = 'updated';
|
1654 |
-
|
1655 |
-
//Filter ID must be set
|
1656 |
-
if ( empty($_POST['filter_id']) ){
|
1657 |
-
$message = __("Filter ID not specified.", 'broken-link-checker');
|
1658 |
-
$msg_class = 'error';
|
1659 |
-
} else {
|
1660 |
-
//Try to delete the filter
|
1661 |
-
$blc_link_query = blcLinkQuery::getInstance();
|
1662 |
-
if ( $blc_link_query->delete_custom_filter($_POST['filter_id']) ){
|
1663 |
-
//Success
|
1664 |
-
$message = __('Filter deleted', 'broken-link-checker');
|
1665 |
-
} else {
|
1666 |
-
//Either the ID is wrong or there was some other error
|
1667 |
-
$message = __('Database error : %s', 'broken-link-checker');
|
1668 |
$msg_class = 'error';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1669 |
}
|
|
|
|
|
1670 |
}
|
1671 |
|
1672 |
-
|
1673 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
1674 |
|
1675 |
-
|
1676 |
-
|
1677 |
-
|
1678 |
-
|
1679 |
-
|
1680 |
-
|
1681 |
-
|
1682 |
-
|
1683 |
-
|
1684 |
-
|
1685 |
-
|
1686 |
-
|
1687 |
-
|
1688 |
-
|
1689 |
-
|
1690 |
-
|
1691 |
-
|
1692 |
-
|
1693 |
-
|
1694 |
-
|
1695 |
-
|
1696 |
-
|
1697 |
-
|
1698 |
-
|
1699 |
-
|
1700 |
-
|
1701 |
-
|
1702 |
-
|
1703 |
-
|
1704 |
-
|
1705 |
-
|
1706 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1707 |
}
|
|
|
|
|
1708 |
}
|
|
|
1709 |
|
1710 |
-
|
1711 |
-
|
1712 |
-
|
1713 |
-
|
1714 |
-
|
1715 |
-
|
1716 |
-
|
1717 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1718 |
);
|
1719 |
|
1720 |
-
if ( $
|
1721 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1722 |
_n(
|
1723 |
-
'
|
1724 |
-
'
|
1725 |
-
$
|
1726 |
'broken-link-checker'
|
1727 |
),
|
1728 |
-
$
|
1729 |
);
|
1730 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1731 |
}
|
1732 |
-
} else {
|
1733 |
-
$message = __('None of the selected links are redirects!', 'broken-link-checker');
|
1734 |
}
|
|
|
|
|
1735 |
}
|
1736 |
|
1737 |
-
|
1738 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1739 |
|
1740 |
-
|
1741 |
-
|
1742 |
-
|
1743 |
-
|
1744 |
-
|
1745 |
-
|
1746 |
-
|
1747 |
-
|
1748 |
-
|
1749 |
-
|
1750 |
-
|
1751 |
-
|
1752 |
-
|
1753 |
-
|
1754 |
-
|
1755 |
-
|
1756 |
-
|
1757 |
-
|
1758 |
-
|
1759 |
-
|
1760 |
-
|
1761 |
-
|
1762 |
-
|
1763 |
-
|
1764 |
-
|
1765 |
-
|
1766 |
-
|
1767 |
-
|
1768 |
-
|
1769 |
-
|
1770 |
-
//
|
1771 |
-
$
|
1772 |
-
$
|
1773 |
-
|
1774 |
-
|
1775 |
-
|
1776 |
-
|
1777 |
-
|
1778 |
-
|
1779 |
-
|
1780 |
-
|
1781 |
-
|
1782 |
-
|
1783 |
-
|
1784 |
-
|
1785 |
-
|
1786 |
-
$
|
1787 |
-
|
1788 |
-
|
1789 |
-
|
1790 |
-
|
1791 |
-
|
1792 |
-
$
|
1793 |
-
|
1794 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1795 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1796 |
|
1797 |
-
|
1798 |
-
|
|
|
|
|
|
|
1799 |
continue;
|
1800 |
}
|
1801 |
|
1802 |
-
|
1803 |
-
|
1804 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1805 |
} else {
|
1806 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
1807 |
}
|
1808 |
}
|
1809 |
|
1810 |
-
|
1811 |
-
|
1812 |
-
|
1813 |
-
|
1814 |
-
|
1815 |
-
|
1816 |
-
|
1817 |
-
|
1818 |
-
);
|
1819 |
|
1820 |
-
|
1821 |
-
|
|
|
1822 |
_n(
|
1823 |
-
'
|
1824 |
-
'
|
1825 |
-
$
|
1826 |
-
'broken-link-checker'
|
1827 |
),
|
1828 |
-
$
|
1829 |
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1830 |
$msg_class = 'error';
|
1831 |
}
|
1832 |
}
|
|
|
|
|
1833 |
}
|
1834 |
|
1835 |
-
|
1836 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1837 |
|
1838 |
-
|
1839 |
-
|
1840 |
-
*
|
1841 |
-
* @param string $pattern
|
1842 |
-
* @param string $delimiter
|
1843 |
-
* @return string
|
1844 |
-
*/
|
1845 |
-
private function escape_regex_delimiter($pattern, $delimiter) {
|
1846 |
-
if ( empty($pattern) ) {
|
1847 |
-
return '';
|
1848 |
-
}
|
1849 |
|
1850 |
-
|
1851 |
-
$length = strlen($pattern);
|
1852 |
-
$escaped = false;
|
1853 |
|
1854 |
-
|
1855 |
-
$char = $pattern[$i];
|
1856 |
|
1857 |
-
|
1858 |
-
|
1859 |
-
|
1860 |
-
|
1861 |
-
|
1862 |
-
|
1863 |
-
|
1864 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1865 |
}
|
1866 |
|
1867 |
-
$
|
1868 |
}
|
1869 |
|
1870 |
-
return $output;
|
1871 |
-
}
|
1872 |
|
1873 |
-
|
1874 |
-
|
1875 |
-
|
1876 |
-
|
1877 |
-
|
1878 |
-
|
1879 |
-
|
1880 |
-
|
1881 |
-
|
1882 |
-
|
1883 |
-
|
1884 |
-
|
1885 |
-
|
1886 |
-
|
1887 |
-
|
1888 |
-
|
1889 |
-
|
1890 |
-
|
1891 |
-
|
1892 |
-
|
1893 |
-
|
1894 |
-
|
1895 |
-
|
1896 |
-
|
1897 |
-
|
1898 |
-
|
1899 |
-
|
1900 |
-
|
1901 |
-
|
1902 |
-
|
1903 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1904 |
$processed_links++;
|
|
|
|
|
|
|
|
|
|
|
|
|
1905 |
}
|
1906 |
}
|
|
|
1907 |
|
1908 |
-
|
1909 |
-
|
1910 |
-
$
|
1911 |
_n(
|
1912 |
-
'%d link
|
1913 |
-
'%d links
|
1914 |
$processed_links,
|
1915 |
'broken-link-checker'
|
1916 |
),
|
1917 |
$processed_links
|
1918 |
);
|
1919 |
-
|
1920 |
-
if ( $failed_links > 0 ) {
|
1921 |
-
$message .= '<br>' . sprintf(
|
1922 |
-
_n(
|
1923 |
-
'Failed to remove %d link',
|
1924 |
-
'Failed to remove %d links',
|
1925 |
-
$failed_links,
|
1926 |
-
'broken-link-checker'
|
1927 |
-
),
|
1928 |
-
$failed_links
|
1929 |
-
);
|
1930 |
-
$msg_class = 'error';
|
1931 |
-
}
|
1932 |
}
|
|
|
|
|
1933 |
}
|
1934 |
|
1935 |
-
|
1936 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
1937 |
|
1938 |
-
|
1939 |
-
|
1940 |
-
|
1941 |
-
|
1942 |
-
|
1943 |
-
|
1944 |
-
|
1945 |
-
|
1946 |
-
|
1947 |
-
|
1948 |
-
|
1949 |
-
|
1950 |
-
|
1951 |
-
|
1952 |
-
//Delete posts, blogroll entries and any other link containers that contain any of the selected links.
|
1953 |
-
//
|
1954 |
-
//Note that once all containers containing a particular link have been deleted,
|
1955 |
-
//there is no need to explicitly delete the link record itself. The hooks attached to
|
1956 |
-
//the actions that execute when something is deleted (e.g. "post_deleted") will
|
1957 |
-
//take care of that.
|
1958 |
-
|
1959 |
-
check_admin_referer( 'bulk-action' );
|
1960 |
-
|
1961 |
-
if ( count($selected_links) > 0 ) {
|
1962 |
-
$messages = array();
|
1963 |
-
|
1964 |
-
//Fetch all the selected links
|
1965 |
-
$links = blc_get_links(array(
|
1966 |
-
'link_ids' => $selected_links,
|
1967 |
-
'load_instances' => true,
|
1968 |
-
));
|
1969 |
-
|
1970 |
-
//Make a list of all containers associated with these links, with each container
|
1971 |
-
//listed only once.
|
1972 |
-
$containers = array();
|
1973 |
-
foreach($links as $link){ /* @var blcLink $link */
|
1974 |
-
$instances = $link->get_instances();
|
1975 |
-
foreach($instances as $instance){ /* @var blcLinkInstance $instance */
|
1976 |
-
$key = $instance->container_type . '|' . $instance->container_id;
|
1977 |
-
$containers[$key] = array($instance->container_type, $instance->container_id);
|
1978 |
-
}
|
1979 |
-
}
|
1980 |
-
|
1981 |
-
//Instantiate the containers
|
1982 |
-
$containers = blcContainerHelper::get_containers($containers);
|
1983 |
-
|
1984 |
-
//Delete/trash their associated entities
|
1985 |
-
$deleted = array();
|
1986 |
-
$skipped = array();
|
1987 |
-
foreach($containers as $container){ /* @var blcContainer $container */
|
1988 |
-
if ( !$container->current_user_can_delete() ){
|
1989 |
-
continue;
|
1990 |
-
}
|
1991 |
-
|
1992 |
-
if ( $force_delete ){
|
1993 |
-
$rez = $container->delete_wrapped_object();
|
1994 |
-
} else {
|
1995 |
-
if ( $container->can_be_trashed() ){
|
1996 |
-
$rez = $container->trash_wrapped_object();
|
1997 |
-
} else {
|
1998 |
-
$skipped[] = $container;
|
1999 |
continue;
|
2000 |
}
|
2001 |
-
}
|
2002 |
|
2003 |
-
|
2004 |
-
|
2005 |
-
|
2006 |
-
$msg_class = 'error';
|
2007 |
-
} else {
|
2008 |
-
//Keep track of how many of each type were deleted.
|
2009 |
-
$container_type = $container->container_type;
|
2010 |
-
if ( isset($deleted[$container_type]) ){
|
2011 |
-
$deleted[$container_type]++;
|
2012 |
-
} else {
|
2013 |
-
$deleted[$container_type] = 1;
|
2014 |
}
|
2015 |
-
}
|
2016 |
-
}
|
2017 |
|
2018 |
-
|
2019 |
-
|
2020 |
-
|
2021 |
-
|
2022 |
-
|
2023 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2024 |
}
|
2025 |
-
|
2026 |
}
|
2027 |
|
2028 |
-
|
2029 |
-
|
2030 |
-
$
|
2031 |
_n(
|
2032 |
-
|
2033 |
-
|
2034 |
-
|
|
|
2035 |
),
|
2036 |
-
|
2037 |
);
|
2038 |
-
$message .= '<br><ul>';
|
2039 |
-
foreach($skipped as $container){
|
2040 |
-
$message .= sprintf(
|
2041 |
-
'<li>%s</li>',
|
2042 |
-
$container->ui_get_source('')
|
2043 |
-
);
|
2044 |
-
}
|
2045 |
-
$message .= '</ul>';
|
2046 |
-
|
2047 |
-
$messages[] = $message;
|
2048 |
}
|
2049 |
|
2050 |
-
|
2051 |
-
$message = implode('<p>', $messages);
|
2052 |
-
} else {
|
2053 |
-
$message = __("Didn't find anything to delete!", 'broken-link-checker');
|
2054 |
-
$msg_class = 'error';
|
2055 |
-
}
|
2056 |
}
|
2057 |
|
2058 |
-
return array($message, $msg_class);
|
2059 |
-
}
|
2060 |
|
2061 |
-
|
2062 |
-
|
2063 |
-
|
2064 |
-
|
2065 |
-
|
2066 |
-
|
2067 |
-
|
2068 |
-
|
2069 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2070 |
|
2071 |
-
|
2072 |
-
|
|
|
2073 |
|
2074 |
-
|
|
|
|
|
2075 |
|
2076 |
-
|
2077 |
-
|
2078 |
-
|
2079 |
-
|
2080 |
-
|
|
|
|
|
2081 |
|
2082 |
-
|
2083 |
-
|
2084 |
-
|
2085 |
-
|
2086 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2087 |
'broken-link-checker'
|
2088 |
),
|
2089 |
-
|
|
|
|
|
|
|
|
|
|
|
2090 |
);
|
2091 |
}
|
2092 |
|
2093 |
-
|
2094 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2095 |
|
|
|
|
|
2096 |
|
2097 |
-
|
2098 |
-
|
2099 |
-
|
2100 |
-
* @param array $selected_links An array of link IDs
|
2101 |
-
* @return array Confirmation nessage and the CSS class to use with that message.
|
2102 |
-
*/
|
2103 |
-
function do_bulk_discard($selected_links){
|
2104 |
-
check_admin_referer( 'bulk-action' );
|
2105 |
|
2106 |
-
|
2107 |
-
$msg_class = 'updated';
|
2108 |
-
$processed_links = 0;
|
2109 |
|
2110 |
-
|
2111 |
-
|
2112 |
-
|
2113 |
-
|
2114 |
-
|
2115 |
-
|
|
|
|
|
2116 |
|
2117 |
-
|
2118 |
-
if ( !$link->valid() ){
|
2119 |
-
continue;
|
2120 |
-
}
|
2121 |
|
2122 |
-
|
2123 |
-
|
2124 |
-
|
2125 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2126 |
|
2127 |
-
|
2128 |
-
|
2129 |
-
|
2130 |
-
|
2131 |
-
|
2132 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2133 |
|
2134 |
-
|
2135 |
-
|
2136 |
-
|
2137 |
-
|
2138 |
-
|
2139 |
-
|
2140 |
-
|
2141 |
-
|
2142 |
-
|
2143 |
-
|
2144 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2145 |
}
|
|
|
|
|
|
|
2146 |
}
|
2147 |
|
2148 |
-
|
2149 |
-
|
2150 |
-
$messages[] = sprintf(
|
2151 |
-
_n(
|
2152 |
-
'%d link marked as not broken',
|
2153 |
-
'%d links marked as not broken',
|
2154 |
-
$processed_links,
|
2155 |
-
'broken-link-checker'
|
2156 |
-
),
|
2157 |
-
$processed_links
|
2158 |
-
);
|
2159 |
}
|
2160 |
|
2161 |
-
|
2162 |
-
|
|
|
2163 |
|
2164 |
-
|
2165 |
-
|
2166 |
-
|
2167 |
-
|
2168 |
-
|
2169 |
-
|
2170 |
-
|
2171 |
-
check_admin_referer( 'bulk-action' );
|
2172 |
-
|
2173 |
-
$messages = array();
|
2174 |
-
$msg_class = 'updated';
|
2175 |
-
$processed_links = 0;
|
2176 |
-
|
2177 |
-
if ( count($selected_links) > 0 ){
|
2178 |
-
$transactionManager = TransactionManager::getInstance();
|
2179 |
-
$transactionManager->start();
|
2180 |
-
foreach($selected_links as $link_id){
|
2181 |
-
//Load the link
|
2182 |
-
$link = new blcLink( intval($link_id) );
|
2183 |
|
2184 |
-
|
2185 |
-
|
2186 |
-
|
2187 |
-
|
|
|
|
|
|
|
|
|
2188 |
|
2189 |
-
|
2190 |
-
|
2191 |
-
|
2192 |
-
|
|
|
2193 |
|
2194 |
-
|
|
|
|
|
|
|
|
|
2195 |
|
2196 |
-
|
2197 |
-
|
2198 |
-
|
2199 |
-
|
2200 |
-
|
2201 |
-
|
2202 |
-
|
2203 |
-
|
2204 |
-
|
2205 |
-
|
|
|
|
|
|
|
|
|
2206 |
}
|
|
|
|
|
|
|
2207 |
}
|
2208 |
-
}
|
2209 |
|
2210 |
-
|
2211 |
-
|
2212 |
-
|
2213 |
-
|
2214 |
-
|
2215 |
-
|
2216 |
-
|
2217 |
-
|
2218 |
-
)
|
2219 |
-
|
2220 |
-
)
|
2221 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2222 |
|
2223 |
-
|
2224 |
-
|
|
|
2225 |
|
|
|
|
|
|
|
2226 |
|
2227 |
-
|
2228 |
-
|
2229 |
-
|
2230 |
-
* @return void
|
2231 |
-
*/
|
2232 |
-
function links_page_css(){
|
2233 |
-
wp_enqueue_style('blc-links-page', plugins_url('css/links-page.css', $this->loader), array(), '20141113-2');
|
2234 |
-
}
|
2235 |
|
2236 |
-
|
2237 |
-
|
2238 |
-
* The user can hide the notice.
|
2239 |
-
*/
|
2240 |
-
public function show_warnings_section_notice() {
|
2241 |
-
$is_warnings_section = isset($_GET['filter_id'])
|
2242 |
-
&& ($_GET['filter_id'] === 'warnings')
|
2243 |
-
&& isset($_GET['page'])
|
2244 |
-
&& ($_GET['page'] === 'view-broken-links');
|
2245 |
|
2246 |
-
|
2247 |
-
return;
|
2248 |
-
}
|
2249 |
|
2250 |
-
|
2251 |
-
|
2252 |
-
$notice_name = 'show_warnings_section_hint';
|
2253 |
|
2254 |
-
|
2255 |
-
|
2256 |
-
|
2257 |
-
}
|
2258 |
-
if ( !$conf->get($notice_name, true) ) {
|
2259 |
-
return;
|
2260 |
-
}
|
2261 |
|
2262 |
-
|
2263 |
-
|
2264 |
-
|
2265 |
-
|
2266 |
-
|
2267 |
-
|
2268 |
-
|
2269 |
-
|
2270 |
-
|
2271 |
-
'The "Warnings" page lists problems that are probably temporary or suspected to be false positives.<br> Warnings that persist for a long time will usually be reclassified as broken links.',
|
2272 |
-
'broken-link-checker'
|
2273 |
-
),
|
2274 |
-
esc_attr(add_query_arg($notice_name, '0')),
|
2275 |
-
_x(
|
2276 |
-
'Hide notice',
|
2277 |
-
'admin notice under Tools - Broken links - Warnings',
|
2278 |
-
'broken-link-checker'
|
2279 |
-
),
|
2280 |
-
esc_attr(admin_url('options-general.php?page=link-checker-settings#blc_warning_settings')),
|
2281 |
-
_x(
|
2282 |
-
'Change warning settings',
|
2283 |
-
'a link from the admin notice under Tools - Broken links - Warnings',
|
2284 |
-
'broken-link-checker'
|
2285 |
-
)
|
2286 |
-
);
|
2287 |
-
}
|
2288 |
|
2289 |
-
|
2290 |
-
|
2291 |
-
|
2292 |
-
|
2293 |
-
|
2294 |
-
|
2295 |
-
|
2296 |
-
|
2297 |
-
|
2298 |
-
|
2299 |
-
|
2300 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2301 |
$this->conf->save_options();
|
2302 |
}
|
2303 |
-
}
|
2304 |
|
2305 |
-
|
2306 |
-
|
|
|
2307 |
|
2308 |
-
|
2309 |
-
|
2310 |
-
$available_columns = $table->get_layout_columns($this->conf->options['table_layout']);
|
2311 |
|
2312 |
-
|
|
|
2313 |
|
2314 |
-
|
2315 |
-
|
2316 |
-
|
2317 |
-
esc_attr($column_id),
|
2318 |
-
in_array($column_id, $this->conf->options['table_visible_columns']) ? ' checked="checked"' : '',
|
2319 |
-
$data['heading']
|
2320 |
-
);
|
2321 |
-
}
|
2322 |
|
2323 |
-
|
2324 |
-
|
2325 |
-
|
2326 |
-
|
2327 |
-
|
2328 |
-
|
2329 |
-
|
2330 |
-
<input type="button" class="button" value="%s" id="blc-per-page-apply-button" /><br />',
|
2331 |
-
$this->conf->options['table_links_per_page'],
|
2332 |
-
__('links', 'broken-link-checker'),
|
2333 |
-
__('Apply')
|
2334 |
-
);
|
2335 |
-
$html .= '</div>';
|
2336 |
-
|
2337 |
-
$html .= '<h5>' . __('Misc', 'broken-link-checker') . '</h5>';
|
2338 |
-
$html .= '<div class="screen-options">';
|
2339 |
-
/*
|
2340 |
-
Display a checkbox in "Screen Options" that lets the user highlight links that
|
2341 |
-
have been broken for at least X days.
|
2342 |
-
*/
|
2343 |
-
$html .= sprintf(
|
2344 |
-
'<label><input type="checkbox" id="highlight_permanent_failures" name="highlight_permanent_failures"%s> ',
|
2345 |
-
$this->conf->options['highlight_permanent_failures'] ? ' checked="checked"' : ''
|
2346 |
-
);
|
2347 |
-
$input_box = sprintf(
|
2348 |
-
'</label><input type="text" name="failure_duration_threshold" id="failure_duration_threshold" value="%d" size="2"><label for="highlight_permanent_failures">',
|
2349 |
-
$this->conf->options['failure_duration_threshold']
|
2350 |
-
);
|
2351 |
-
$html .= sprintf(
|
2352 |
-
__('Highlight links broken for at least %s days', 'broken-link-checker'),
|
2353 |
-
$input_box
|
2354 |
-
);
|
2355 |
-
$html .= '</label>';
|
2356 |
-
|
2357 |
-
//Display a checkbox for turning colourful link status messages on/off
|
2358 |
-
$html .= sprintf(
|
2359 |
-
'<br/><label><input type="checkbox" id="table_color_code_status" name="table_color_code_status"%s> %s</label>',
|
2360 |
-
$this->conf->options['table_color_code_status'] ? ' checked="checked"' : '',
|
2361 |
-
__('Color-code status codes', 'broken-link-checker')
|
2362 |
-
);
|
2363 |
-
|
2364 |
-
$html .= '</div>';
|
2365 |
-
|
2366 |
-
return $html;
|
2367 |
-
}
|
2368 |
|
2369 |
-
|
2370 |
-
|
2371 |
-
|
2372 |
-
|
2373 |
-
|
2374 |
-
|
2375 |
-
function ajax_save_screen_options($form){
|
2376 |
-
if ( !current_user_can('edit_others_posts') ){
|
2377 |
-
die( json_encode( array(
|
2378 |
-
'error' => __("You're not allowed to do that!", 'broken-link-checker')
|
2379 |
-
)));
|
2380 |
-
}
|
2381 |
|
2382 |
-
|
2383 |
-
|
|
|
|
|
2384 |
|
2385 |
-
|
2386 |
-
|
2387 |
-
$
|
2388 |
-
}
|
2389 |
|
2390 |
-
|
2391 |
-
|
2392 |
-
}
|
2393 |
|
2394 |
-
|
2395 |
-
|
2396 |
-
|
2397 |
|
2398 |
-
|
2399 |
-
|
2400 |
-
}
|
2401 |
|
2402 |
-
|
2403 |
-
|
2404 |
-
}
|
2405 |
|
2406 |
-
|
2407 |
-
|
2408 |
-
|
2409 |
-
|
2410 |
-
|
2411 |
-
|
2412 |
-
|
2413 |
-
|
2414 |
-
|
2415 |
-
|
2416 |
-
|
2417 |
-
|
2418 |
-
//plugins do, so we should explicitly close the session (if any) before starting the worker.
|
2419 |
-
if ( session_id() != '' ) {
|
2420 |
-
session_write_close();
|
2421 |
-
}
|
2422 |
-
|
2423 |
-
if ( !$this->acquire_lock() ){
|
2424 |
-
//FB::warn("Another instance of BLC is already working. Stop.");
|
2425 |
-
$blclog->info('Another instance of BLC is already working. Stop.');
|
2426 |
-
return;
|
2427 |
-
}
|
2428 |
-
|
2429 |
-
if ( $this->server_too_busy() ){
|
2430 |
-
//FB::warn("Server is too busy. Stop.");
|
2431 |
-
$blclog->warn('Server load is too high, stopping.');
|
2432 |
-
return;
|
2433 |
-
}
|
2434 |
-
|
2435 |
-
$this->start_timer();
|
2436 |
-
$blclog->info('work() starts');
|
2437 |
-
|
2438 |
-
$max_execution_time = $this->conf->options['max_execution_time'];
|
2439 |
-
|
2440 |
-
/*****************************************
|
2441 |
-
Preparation
|
2442 |
-
******************************************/
|
2443 |
-
// Check for safe mode
|
2444 |
-
if( blcUtility::is_safe_mode() ){
|
2445 |
-
// Do it the safe mode way - obey the existing max_execution_time setting
|
2446 |
-
$t = ini_get('max_execution_time');
|
2447 |
-
if ($t && ($t < $max_execution_time))
|
2448 |
-
$max_execution_time = $t-1;
|
2449 |
-
} else {
|
2450 |
-
// Do it the regular way
|
2451 |
-
@set_time_limit( $max_execution_time * 2 ); //x2 should be plenty, running any longer would mean a glitch.
|
2452 |
-
}
|
2453 |
-
|
2454 |
-
//Don't stop the script when the connection is closed
|
2455 |
-
ignore_user_abort( true );
|
2456 |
-
|
2457 |
-
//Close the connection as per http://www.php.net/manual/en/features.connection-handling.php#71172
|
2458 |
-
//This reduces resource usage.
|
2459 |
-
//(Disable when debugging or you won't get the FirePHP output)
|
2460 |
-
if (
|
2461 |
-
!headers_sent()
|
2462 |
-
&& (defined('DOING_AJAX') && constant('DOING_AJAX'))
|
2463 |
-
&& (!defined('BLC_DEBUG') || !constant('BLC_DEBUG'))
|
2464 |
-
){
|
2465 |
-
@ob_end_clean(); //Discard the existing buffer, if any
|
2466 |
-
header("Connection: close");
|
2467 |
-
ob_start();
|
2468 |
-
echo ('Connection closed'); //This could be anything
|
2469 |
-
$size = ob_get_length();
|
2470 |
-
header("Content-Length: $size");
|
2471 |
-
ob_end_flush(); // Strange behaviour, will not work
|
2472 |
-
flush(); // Unless both are called !
|
2473 |
-
}
|
2474 |
-
|
2475 |
-
//Load modules for this context
|
2476 |
-
$moduleManager = blcModuleManager::getInstance();
|
2477 |
-
$moduleManager->load_modules('work');
|
2478 |
-
|
2479 |
-
$target_usage_fraction = $this->conf->get('target_resource_usage', 0.25);
|
2480 |
-
//Target usage must be between 1% and 100%.
|
2481 |
-
$target_usage_fraction = max(min($target_usage_fraction, 1), 0.01);
|
2482 |
-
|
2483 |
-
|
2484 |
-
/*****************************************
|
2485 |
-
Parse posts and bookmarks
|
2486 |
-
******************************************/
|
2487 |
-
|
2488 |
-
$orphans_possible = false;
|
2489 |
-
$still_need_resynch = $this->conf->options['need_resynch'];
|
2490 |
-
|
2491 |
-
if ( $still_need_resynch ) {
|
2492 |
-
|
2493 |
-
//FB::log("Looking for containers that need parsing...");
|
2494 |
-
$max_containers_per_query = 50;
|
2495 |
-
|
2496 |
-
$start = microtime(true);
|
2497 |
-
$containers = blcContainerHelper::get_unsynched_containers($max_containers_per_query);
|
2498 |
-
$get_containers_time = microtime(true) - $start;
|
2499 |
-
|
2500 |
-
while( !empty($containers) ){
|
2501 |
-
//FB::log($containers, 'Found containers');
|
2502 |
-
$this->sleep_to_maintain_ratio($get_containers_time, $target_usage_fraction);
|
2503 |
-
|
2504 |
-
foreach($containers as $container){
|
2505 |
-
$synch_start_time = microtime(true);
|
2506 |
-
|
2507 |
-
//FB::log($container, "Parsing container");
|
2508 |
-
$container->synch();
|
2509 |
-
|
2510 |
-
$synch_elapsed_time = microtime(true) - $synch_start_time;
|
2511 |
-
$blclog->info(sprintf(
|
2512 |
-
'Parsed container %s[%s] in %.2f ms',
|
2513 |
-
$container->container_type,
|
2514 |
-
$container->container_id,
|
2515 |
-
$synch_elapsed_time * 1000
|
2516 |
-
));
|
2517 |
|
2518 |
//Check if we still have some execution time left
|
2519 |
-
if( $this->execution_time() > $max_execution_time ){
|
|
|
2520 |
//FB::log('The allotted execution time has run out');
|
2521 |
-
|
2522 |
$this->release_lock();
|
2523 |
return;
|
2524 |
}
|
2525 |
|
2526 |
//Check if the server isn't overloaded
|
2527 |
-
if ( $this->server_too_busy() ){
|
|
|
2528 |
//FB::log('Server overloaded, bailing out.');
|
2529 |
-
|
2530 |
$this->release_lock();
|
2531 |
return;
|
2532 |
}
|
2533 |
-
|
2534 |
-
//Intentionally slow down parsing to reduce the load on the server. Basically,
|
2535 |
-
//we work $target_usage_fraction of the time and sleep the rest of the time.
|
2536 |
-
$this->sleep_to_maintain_ratio($synch_elapsed_time, $target_usage_fraction);
|
2537 |
}
|
2538 |
-
$
|
2539 |
|
2540 |
-
$start
|
2541 |
-
$
|
2542 |
-
$
|
2543 |
}
|
|
|
2544 |
|
2545 |
-
|
2546 |
-
$
|
2547 |
-
|
2548 |
-
}
|
2549 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2550 |
}
|
2551 |
|
2552 |
-
|
2553 |
-
|
2554 |
-
|
2555 |
-
|
2556 |
-
|
2557 |
-
|
2558 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2559 |
|
2560 |
-
|
2561 |
-
|
2562 |
-
|
|
|
|
|
|
|
2563 |
|
2564 |
-
|
2565 |
-
|
2566 |
|
2567 |
-
|
2568 |
-
|
|
|
|
|
2569 |
|
2570 |
-
|
2571 |
-
$
|
2572 |
-
|
|
|
|
|
2573 |
|
2574 |
-
|
2575 |
-
|
2576 |
-
|
2577 |
-
|
2578 |
-
|
2579 |
-
return;
|
2580 |
-
}
|
2581 |
|
2582 |
-
|
2583 |
-
//FB::log('Server overloaded, bailing out.');
|
2584 |
-
$blclog->info('Server load too high, stopping.');
|
2585 |
-
$this->release_lock();
|
2586 |
-
return;
|
2587 |
}
|
2588 |
|
2589 |
-
|
2590 |
-
|
2591 |
-
|
2592 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2593 |
|
2594 |
-
|
2595 |
-
|
2596 |
-
$get_links_time = microtime(true) - $start;
|
2597 |
|
2598 |
-
|
2599 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
2600 |
|
2601 |
-
|
2602 |
-
|
2603 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2604 |
|
2605 |
-
|
2606 |
-
shuffle($links);
|
2607 |
|
2608 |
-
$
|
2609 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2610 |
|
2611 |
-
|
2612 |
-
|
2613 |
-
|
2614 |
-
|
2615 |
-
|
2616 |
-
|
2617 |
-
$link->check( true );
|
2618 |
} else {
|
2619 |
-
|
2620 |
-
|
2621 |
-
|
|
|
|
|
2622 |
}
|
2623 |
-
|
2624 |
-
|
2625 |
-
|
2626 |
-
|
2627 |
-
|
2628 |
-
$blclog->info('The allotted execution time has run out.');
|
2629 |
-
$this->release_lock();
|
2630 |
-
return;
|
2631 |
}
|
|
|
2632 |
|
2633 |
-
|
2634 |
-
|
2635 |
-
|
2636 |
-
|
2637 |
-
|
2638 |
-
|
2639 |
-
|
2640 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2641 |
}
|
2642 |
-
$transactionManager->commit();
|
2643 |
|
2644 |
-
$
|
2645 |
-
|
2646 |
-
|
2647 |
}
|
2648 |
-
//FB::log('No links need to be checked right now.');
|
2649 |
|
2650 |
-
|
2651 |
-
|
2652 |
-
|
2653 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2654 |
|
2655 |
-
|
2656 |
-
|
2657 |
-
*
|
2658 |
-
* For example, if $ratio is 0.25 and $elapsed_time is 1 second, this method will sleep for 3 seconds.
|
2659 |
-
* Total runtime = 1 + 3 = 4, ratio = 1 / 4 = 0.25.
|
2660 |
-
*
|
2661 |
-
* @param float $elapsed_time
|
2662 |
-
* @param float $ratio
|
2663 |
-
*/
|
2664 |
-
private function sleep_to_maintain_ratio($elapsed_time, $ratio) {
|
2665 |
-
if ( ($ratio <= 0) || ($ratio > 1) ) {
|
2666 |
-
return;
|
2667 |
-
}
|
2668 |
-
$sleep_time = $elapsed_time * ((1 / $ratio) - 1);
|
2669 |
-
if ($sleep_time > 0.0001) {
|
2670 |
-
/*global $blclog;
|
2671 |
-
$blclog->debug(sprintf(
|
2672 |
-
'Task took %.2f ms, sleeping for %.2f ms',
|
2673 |
-
$elapsed_time * 1000,
|
2674 |
-
$sleep_time * 1000
|
2675 |
-
));*/
|
2676 |
-
usleep($sleep_time * 1000000);
|
2677 |
-
}
|
2678 |
-
}
|
2679 |
|
2680 |
-
|
2681 |
-
|
2682 |
-
* Its only purpose is to invoke the worker function.
|
2683 |
-
*
|
2684 |
-
* @uses wsBrokenLinkChecker::work()
|
2685 |
-
*
|
2686 |
-
* @return void
|
2687 |
-
*/
|
2688 |
-
function cron_check_links(){
|
2689 |
-
$this->work();
|
2690 |
-
}
|
2691 |
|
2692 |
-
|
2693 |
-
* Retrieve links that need to be checked or re-checked.
|
2694 |
-
*
|
2695 |
-
* @param integer $max_results The maximum number of links to return. Defaults to 0 = no limit.
|
2696 |
-
* @param bool $count_only If true, only the number of found links will be returned, not the links themselves.
|
2697 |
-
* @return int|blcLink[]
|
2698 |
-
*/
|
2699 |
-
function get_links_to_check($max_results = 0, $count_only = false){
|
2700 |
-
global $wpdb; /* @var wpdb $wpdb */
|
2701 |
-
|
2702 |
-
$check_threshold = date('Y-m-d H:i:s', strtotime('-'.$this->conf->options['check_threshold'].' hours'));
|
2703 |
-
$recheck_threshold = date('Y-m-d H:i:s', time() - $this->conf->options['recheck_threshold']);
|
2704 |
-
|
2705 |
-
//FB::log('Looking for links to check (threshold : '.$check_threshold.', recheck_threshold : '.$recheck_threshold.')...');
|
2706 |
-
|
2707 |
-
//Select some links that haven't been checked for a long time or
|
2708 |
-
//that are broken and need to be re-checked again. Links that are
|
2709 |
-
//marked as "being checked" and have been that way for several minutes
|
2710 |
-
//can also be considered broken/buggy, so those will be selected
|
2711 |
-
//as well.
|
2712 |
-
|
2713 |
-
//Only check links that have at least one valid instance (i.e. an instance exists and
|
2714 |
-
//it corresponds to one of the currently loaded container/parser types).
|
2715 |
-
$manager = blcModuleManager::getInstance();
|
2716 |
-
$loaded_containers = $manager->get_escaped_ids('container');
|
2717 |
-
$loaded_parsers = $manager->get_escaped_ids('parser');
|
2718 |
-
|
2719 |
-
//Note : This is a slow query, but AFAIK there is no way to speed it up.
|
2720 |
-
//I could put an index on last_check_attempt, but that value is almost
|
2721 |
-
//certainly unique for each row so it wouldn't be much better than a full table scan.
|
2722 |
-
if ( $count_only ){
|
2723 |
-
$q = "SELECT COUNT(DISTINCT links.link_id)\n";
|
2724 |
-
} else {
|
2725 |
-
$q = "SELECT DISTINCT links.*\n";
|
2726 |
-
}
|
2727 |
-
$q .= "FROM {$wpdb->prefix}blc_links AS links
|
2728 |
-
INNER JOIN {$wpdb->prefix}blc_instances AS instances USING(link_id)
|
2729 |
-
WHERE
|
2730 |
-
(
|
2731 |
-
( last_check_attempt < %s )
|
2732 |
-
OR
|
2733 |
-
(
|
2734 |
-
(broken = 1 OR being_checked = 1)
|
2735 |
-
AND may_recheck = 1
|
2736 |
-
AND check_count < %d
|
2737 |
-
AND last_check_attempt < %s
|
2738 |
-
)
|
2739 |
-
)
|
2740 |
-
|
2741 |
-
AND
|
2742 |
-
( instances.container_type IN ({$loaded_containers}) )
|
2743 |
-
AND ( instances.parser_type IN ({$loaded_parsers}) )
|
2744 |
-
";
|
2745 |
-
|
2746 |
-
if ( !$count_only ){
|
2747 |
-
$q .= "\nORDER BY last_check_attempt ASC\n";
|
2748 |
-
if ( !empty($max_results) ){
|
2749 |
-
$q .= "LIMIT " . intval($max_results);
|
2750 |
-
}
|
2751 |
-
}
|
2752 |
-
|
2753 |
-
$link_q = $wpdb->prepare(
|
2754 |
-
$q,
|
2755 |
-
$check_threshold,
|
2756 |
-
$this->conf->options['recheck_count'],
|
2757 |
-
$recheck_threshold
|
2758 |
-
);
|
2759 |
-
//FB::log($link_q, "Find links to check");
|
2760 |
-
//$blclog->debug("Find links to check: \n" . $link_q);
|
2761 |
-
|
2762 |
-
//If we just need the number of links, retrieve it and return
|
2763 |
-
if ( $count_only ){
|
2764 |
-
return $wpdb->get_var($link_q);
|
2765 |
-
}
|
2766 |
-
|
2767 |
-
//Fetch the link data
|
2768 |
-
$link_data = $wpdb->get_results($link_q, ARRAY_A);
|
2769 |
-
if ( empty($link_data) ){
|
2770 |
-
return array();
|
2771 |
-
}
|
2772 |
-
|
2773 |
-
//Instantiate blcLink objects for all fetched links
|
2774 |
-
$links = array();
|
2775 |
-
foreach($link_data as $data){
|
2776 |
-
$links[] = new blcLink($data);
|
2777 |
-
}
|
2778 |
-
|
2779 |
-
return $links;
|
2780 |
-
}
|
2781 |
|
2782 |
-
|
2783 |
-
* Output the current link checker status in JSON format.
|
2784 |
-
* Ajax hook for the 'blc_full_status' action.
|
2785 |
-
*
|
2786 |
-
* @return void
|
2787 |
-
*/
|
2788 |
-
function ajax_full_status( ){
|
2789 |
-
$status = $this->get_status();
|
2790 |
-
$text = $this->status_text( $status );
|
2791 |
-
|
2792 |
-
echo json_encode( array(
|
2793 |
-
'text' => $text,
|
2794 |
-
'status' => $status,
|
2795 |
-
) );
|
2796 |
-
|
2797 |
-
die();
|
2798 |
-
}
|
2799 |
|
2800 |
-
|
2801 |
-
|
2802 |
-
|
2803 |
-
|
2804 |
-
|
2805 |
-
|
2806 |
-
|
2807 |
-
$text = '';
|
2808 |
-
|
2809 |
-
if( $status['broken_links'] > 0 ){
|
2810 |
-
$text .= sprintf(
|
2811 |
-
"<a href='%s' title='" . __('View broken links', 'broken-link-checker') . "'><strong>".
|
2812 |
-
_n('Found %d broken link', 'Found %d broken links', $status['broken_links'], 'broken-link-checker') .
|
2813 |
-
"</strong></a>",
|
2814 |
-
esc_attr(admin_url('tools.php?page=view-broken-links')),
|
2815 |
-
$status['broken_links']
|
2816 |
);
|
2817 |
-
} else {
|
2818 |
-
$text .= __("No broken links found.", 'broken-link-checker');
|
2819 |
}
|
2820 |
|
2821 |
-
|
|
|
2822 |
|
2823 |
-
|
2824 |
-
$
|
2825 |
-
|
2826 |
-
$status['unchecked_links'] );
|
2827 |
-
} else {
|
2828 |
-
$text .= __("No URLs in the work queue.", 'broken-link-checker');
|
2829 |
}
|
2830 |
|
2831 |
-
|
2832 |
-
|
2833 |
-
|
2834 |
-
|
2835 |
-
|
2836 |
-
|
2837 |
-
|
2838 |
-
|
2839 |
-
$status['known_instances']
|
2840 |
-
);
|
2841 |
-
|
2842 |
-
if ($this->conf->options['need_resynch']){
|
2843 |
-
$text .= sprintf(
|
2844 |
-
__('Detected %1$s in %2$s and still searching...', 'broken-link-checker'),
|
2845 |
-
$url_count,
|
2846 |
-
$link_count
|
2847 |
-
);
|
2848 |
-
} else {
|
2849 |
-
$text .= sprintf(
|
2850 |
-
__('Detected %1$s in %2$s.', 'broken-link-checker'),
|
2851 |
-
$url_count,
|
2852 |
-
$link_count
|
2853 |
-
);
|
2854 |
-
}
|
2855 |
-
} else {
|
2856 |
-
if ($this->conf->options['need_resynch']){
|
2857 |
-
$text .= __('Searching your blog for links...', 'broken-link-checker');
|
2858 |
-
} else {
|
2859 |
-
$text .= __('No links detected.', 'broken-link-checker');
|
2860 |
}
|
2861 |
-
}
|
2862 |
-
|
2863 |
-
return $text;
|
2864 |
-
}
|
2865 |
-
|
2866 |
-
/**
|
2867 |
-
* @uses wsBrokenLinkChecker::ajax_full_status()
|
2868 |
-
*
|
2869 |
-
* @return void
|
2870 |
-
*/
|
2871 |
-
function ajax_dashboard_status(){
|
2872 |
-
//Just display the full status.
|
2873 |
-
$this->ajax_full_status();
|
2874 |
-
}
|
2875 |
-
|
2876 |
-
/**
|
2877 |
-
* Output the current average server load (over the last one-minute period).
|
2878 |
-
* Called via AJAX.
|
2879 |
-
*
|
2880 |
-
* @return void
|
2881 |
-
*/
|
2882 |
-
function ajax_current_load(){
|
2883 |
-
$load = blcUtility::get_server_load();
|
2884 |
-
if ( empty($load) ){
|
2885 |
-
die( _x('Unknown', 'current load', 'broken-link-checker') );
|
2886 |
-
}
|
2887 |
-
|
2888 |
-
$one_minute = reset($load);
|
2889 |
-
printf('%.2f', $one_minute);
|
2890 |
-
die();
|
2891 |
-
}
|
2892 |
|
2893 |
-
|
2894 |
-
|
2895 |
-
|
2896 |
-
* recheck_threshold - date/time; broken links checked before this threshold should be re-checked.
|
2897 |
-
* known_links - the number of detected unique URLs (a misleading name, yes).
|
2898 |
-
* known_instances - the number of detected link instances, i.e. actual link elements in posts and other places.
|
2899 |
-
* broken_links - the number of detected broken links.
|
2900 |
-
* unchecked_links - the number of URLs that need to be checked ASAP; based on check_threshold and recheck_threshold.
|
2901 |
-
*
|
2902 |
-
* @return array
|
2903 |
-
*/
|
2904 |
-
function get_status(){
|
2905 |
-
$blc_link_query = blcLinkQuery::getInstance();
|
2906 |
-
|
2907 |
-
$check_threshold=date('Y-m-d H:i:s', strtotime('-'.$this->conf->options['check_threshold'].' hours'));
|
2908 |
-
$recheck_threshold=date('Y-m-d H:i:s', time() - $this->conf->options['recheck_threshold']);
|
2909 |
-
|
2910 |
-
$known_links = blc_get_links(array('count_only' => true));
|
2911 |
-
$known_instances = blc_get_usable_instance_count();
|
2912 |
-
|
2913 |
-
$broken_links = $blc_link_query->get_filter_links('broken', array('count_only' => true));
|
2914 |
-
|
2915 |
-
$unchecked_links = $this->get_links_to_check(0, true);
|
2916 |
-
|
2917 |
-
return array(
|
2918 |
-
'check_threshold' => $check_threshold,
|
2919 |
-
'recheck_threshold' => $recheck_threshold,
|
2920 |
-
'known_links' => $known_links,
|
2921 |
-
'known_instances' => $known_instances,
|
2922 |
-
'broken_links' => $broken_links,
|
2923 |
-
'unchecked_links' => $unchecked_links,
|
2924 |
-
);
|
2925 |
-
}
|
2926 |
-
|
2927 |
-
function ajax_work(){
|
2928 |
-
check_ajax_referer('blc_work');
|
2929 |
-
|
2930 |
-
//Run the worker function
|
2931 |
-
$this->work();
|
2932 |
-
die();
|
2933 |
-
}
|
2934 |
-
|
2935 |
-
/**
|
2936 |
-
* AJAX hook for the "Not broken" button. Marks a link as broken and as a likely false positive.
|
2937 |
-
*
|
2938 |
-
* @return void
|
2939 |
-
*/
|
2940 |
-
function ajax_discard(){
|
2941 |
-
if (!current_user_can('edit_others_posts') || !check_ajax_referer('blc_discard', false, false)){
|
2942 |
-
die( __("You're not allowed to do that!", 'broken-link-checker') );
|
2943 |
-
}
|
2944 |
-
|
2945 |
-
if ( isset($_POST['link_id']) ){
|
2946 |
-
//Load the link
|
2947 |
-
$link = new blcLink( intval($_POST['link_id']) );
|
2948 |
|
2949 |
-
|
2950 |
-
|
2951 |
-
|
2952 |
-
|
2953 |
-
|
2954 |
-
|
2955 |
-
|
2956 |
-
|
2957 |
-
|
2958 |
-
|
2959 |
|
2960 |
-
|
2961 |
|
2962 |
-
|
2963 |
-
|
2964 |
|
2965 |
-
|
2966 |
-
|
2967 |
-
|
2968 |
-
|
|
|
|
|
|
|
2969 |
} else {
|
2970 |
-
die( __(
|
2971 |
}
|
2972 |
-
} else {
|
2973 |
-
die( __("Error : link_id not specified", 'broken-link-checker') );
|
2974 |
}
|
2975 |
-
}
|
2976 |
-
|
2977 |
-
public function ajax_dismiss(){
|
2978 |
-
$this->ajax_set_link_dismissed(true);
|
2979 |
-
}
|
2980 |
-
|
2981 |
-
public function ajax_undismiss(){
|
2982 |
-
$this->ajax_set_link_dismissed(false);
|
2983 |
-
}
|
2984 |
|
2985 |
-
|
2986 |
-
|
|
|
2987 |
|
2988 |
-
|
2989 |
-
|
2990 |
}
|
2991 |
|
2992 |
-
|
2993 |
-
|
2994 |
-
$link = new blcLink( intval($_POST['link_id']) );
|
2995 |
|
2996 |
-
if (
|
2997 |
-
|
2998 |
-
die();
|
2999 |
}
|
3000 |
|
3001 |
-
$
|
|
|
|
|
3002 |
|
3003 |
-
|
3004 |
-
|
3005 |
-
|
3006 |
-
|
3007 |
-
if ( $link->save() ){
|
3008 |
-
$transactionManager->commit();
|
3009 |
-
die( "OK" );
|
3010 |
-
} else {
|
3011 |
-
die( __("Oops, couldn't modify the link!", 'broken-link-checker') ) ;
|
3012 |
-
}
|
3013 |
-
} else {
|
3014 |
-
die( __("Error : link_id not specified", 'broken-link-checker') );
|
3015 |
-
}
|
3016 |
-
}
|
3017 |
|
3018 |
-
|
3019 |
-
* AJAX hook for the inline link editor on Tools -> Broken Links.
|
3020 |
-
*
|
3021 |
-
* @return void
|
3022 |
-
*/
|
3023 |
-
function ajax_edit(){
|
3024 |
-
if (!current_user_can('edit_others_posts') || !check_ajax_referer('blc_edit', false, false)){
|
3025 |
-
die( json_encode( array(
|
3026 |
-
'error' => __("You're not allowed to do that!", 'broken-link-checker')
|
3027 |
-
)));
|
3028 |
-
}
|
3029 |
|
3030 |
-
|
3031 |
-
|
3032 |
-
|
3033 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3034 |
}
|
3035 |
|
3036 |
-
|
3037 |
-
|
3038 |
-
|
3039 |
-
|
3040 |
-
|
3041 |
-
|
3042 |
-
)))
|
3043 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3044 |
|
3045 |
-
|
3046 |
-
|
3047 |
-
|
3048 |
-
|
3049 |
-
|
3050 |
-
|
3051 |
-
|
3052 |
-
|
|
|
3053 |
|
3054 |
-
|
3055 |
-
|
3056 |
-
|
3057 |
-
|
3058 |
-
|
3059 |
-
|
3060 |
-
|
3061 |
-
|
|
|
|
|
|
|
3062 |
}
|
3063 |
-
}
|
3064 |
|
3065 |
-
|
3066 |
-
|
3067 |
-
$
|
3068 |
-
|
3069 |
-
|
3070 |
-
|
3071 |
-
|
|
|
|
|
|
|
|
|
|
|
3072 |
|
3073 |
-
|
3074 |
-
|
3075 |
-
|
3076 |
-
|
3077 |
-
|
3078 |
-
|
3079 |
-
|
3080 |
-
|
3081 |
-
|
3082 |
-
|
3083 |
-
|
3084 |
-
|
3085 |
-
$first_instance = reset($instances);
|
3086 |
-
$ui_link_text = $first_instance->ui_get_link_text();
|
3087 |
}
|
3088 |
}
|
3089 |
|
3090 |
-
$
|
3091 |
-
|
3092 |
-
|
3093 |
-
|
3094 |
-
|
3095 |
-
|
3096 |
-
|
3097 |
-
|
3098 |
-
|
3099 |
-
|
3100 |
-
|
3101 |
-
|
3102 |
-
|
3103 |
-
|
3104 |
-
|
3105 |
-
|
3106 |
-
|
3107 |
-
|
3108 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3109 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3110 |
|
3111 |
-
|
3112 |
-
|
|
|
|
|
3113 |
}
|
3114 |
-
die( json_encode($response) );
|
3115 |
}
|
3116 |
-
}
|
3117 |
|
3118 |
-
|
3119 |
-
|
3120 |
-
|
3121 |
-
|
3122 |
-
|
3123 |
-
|
3124 |
-
|
3125 |
-
|
3126 |
-
|
3127 |
-
|
3128 |
-
|
3129 |
-
|
|
|
|
|
|
|
|
|
3130 |
|
3131 |
-
|
3132 |
-
|
3133 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3134 |
|
3135 |
-
|
3136 |
-
|
3137 |
-
'error' => sprintf( __("Oops, I can't find the link %d", 'broken-link-checker'), intval($_POST['link_id']) )
|
3138 |
-
)));
|
3139 |
-
}
|
3140 |
|
3141 |
-
|
3142 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3143 |
|
3144 |
-
|
3145 |
-
|
3146 |
-
'error' => __("An unexpected error occured!", 'broken-link-checker')
|
3147 |
-
)));
|
3148 |
} else {
|
3149 |
-
|
3150 |
-
|
3151 |
-
|
3152 |
-
|
|
|
|
|
3153 |
);
|
3154 |
-
foreach($rez['errors'] as $error){ /** @var WP_Error $error */
|
3155 |
-
array_push( $response['errors'], implode(', ', $error->get_error_messages()) );
|
3156 |
-
}
|
3157 |
-
|
3158 |
-
die( json_encode($response) );
|
3159 |
}
|
3160 |
-
|
3161 |
-
} else {
|
3162 |
-
die( json_encode( array(
|
3163 |
-
'error' => __("Error : link_id not specified", 'broken-link-checker')
|
3164 |
-
)));
|
3165 |
}
|
3166 |
-
}
|
3167 |
|
3168 |
-
|
3169 |
-
|
3170 |
-
|
3171 |
-
|
3172 |
-
|
3173 |
-
|
|
|
|
|
|
|
|
|
3174 |
|
3175 |
-
|
3176 |
-
|
3177 |
-
|
3178 |
-
|
3179 |
-
|
|
|
|
|
|
|
|
|
3180 |
|
3181 |
-
|
3182 |
-
|
3183 |
|
3184 |
-
|
3185 |
-
|
3186 |
-
|
3187 |
-
|
3188 |
-
|
|
|
|
|
|
|
|
|
3189 |
|
3190 |
-
|
3191 |
-
|
3192 |
-
|
3193 |
-
|
3194 |
-
|
3195 |
-
|
3196 |
-
|
|
|
|
|
|
|
|
|
3197 |
|
3198 |
-
|
3199 |
|
3200 |
-
|
3201 |
-
|
3202 |
-
|
3203 |
-
|
3204 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3205 |
|
3206 |
-
'
|
3207 |
-
|
3208 |
-
|
3209 |
-
|
3210 |
-
|
|
|
3211 |
|
3212 |
-
|
3213 |
-
|
3214 |
-
'errors' => array(),
|
3215 |
-
);
|
3216 |
|
3217 |
-
|
3218 |
-
|
3219 |
-
|
3220 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3221 |
}
|
3222 |
-
}
|
3223 |
|
3224 |
-
|
3225 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3226 |
|
3227 |
-
|
3228 |
-
|
3229 |
-
*/
|
3230 |
-
public function ajax_recheck() {
|
3231 |
-
if (!current_user_can('edit_others_posts') || !check_ajax_referer('blc_recheck', false, false)){
|
3232 |
-
die( json_encode( array(
|
3233 |
-
'error' => __("You're not allowed to do that!", 'broken-link-checker')
|
3234 |
-
)));
|
3235 |
-
}
|
3236 |
|
3237 |
-
|
3238 |
-
|
3239 |
-
|
3240 |
-
|
3241 |
-
|
|
|
|
|
|
|
|
|
3242 |
|
3243 |
-
|
3244 |
-
|
3245 |
|
3246 |
-
|
3247 |
-
|
3248 |
-
|
3249 |
-
)
|
3250 |
-
}
|
3251 |
|
3252 |
-
|
3253 |
-
|
3254 |
|
3255 |
-
|
3256 |
-
$link->last_check_attempt = 0;
|
3257 |
-
$link->isOptionLinkChanged = true;
|
3258 |
-
$link->save();
|
3259 |
|
3260 |
-
|
3261 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
3262 |
|
3263 |
-
|
|
|
3264 |
|
3265 |
-
|
3266 |
-
|
3267 |
-
'status_text' => $status['text'],
|
3268 |
-
'status_code' => $status['code'],
|
3269 |
-
'http_code' => empty($link->http_code) ? '' : $link->http_code,
|
3270 |
-
'redirect_count' => $link->redirect_count,
|
3271 |
-
'final_url' => $link->final_url,
|
3272 |
-
);
|
3273 |
|
3274 |
-
|
3275 |
-
|
|
|
3276 |
|
3277 |
-
|
3278 |
-
global $wpdb; /* @var wpdb $wpdb */
|
3279 |
|
3280 |
-
|
3281 |
-
|
3282 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3283 |
|
3284 |
-
|
|
|
3285 |
|
3286 |
-
|
3287 |
-
|
3288 |
-
|
3289 |
-
|
3290 |
-
|
3291 |
-
|
3292 |
-
|
3293 |
-
|
3294 |
-
|
|
|
|
|
3295 |
}
|
3296 |
|
3297 |
-
|
3298 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3299 |
|
3300 |
-
|
3301 |
-
|
3302 |
-
|
3303 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3304 |
}
|
3305 |
-
blcTablePrinter::details_row_contents($link);
|
3306 |
-
die();
|
3307 |
-
} else {
|
3308 |
-
printf( __('Failed to load link details (%s)', 'broken-link-checker'), $wpdb->last_error );
|
3309 |
-
die();
|
3310 |
}
|
3311 |
-
}
|
3312 |
|
3313 |
-
|
3314 |
-
|
3315 |
-
|
3316 |
-
|
3317 |
-
|
3318 |
-
|
3319 |
-
|
3320 |
-
|
3321 |
-
}
|
3322 |
-
|
3323 |
-
/**
|
3324 |
-
* Relese our exclusive lock.
|
3325 |
-
* Does nothing if the lock has already been released.
|
3326 |
-
*
|
3327 |
-
* @return bool
|
3328 |
-
*/
|
3329 |
-
function release_lock(){
|
3330 |
-
return WPMutex::release('blc_lock');
|
3331 |
-
}
|
3332 |
|
3333 |
-
|
3334 |
-
|
3335 |
-
*
|
3336 |
-
* @return bool
|
3337 |
-
*/
|
3338 |
-
function server_too_busy(){
|
3339 |
-
if ( !$this->conf->options['enable_load_limit'] || !isset($this->conf->options['server_load_limit']) ){
|
3340 |
-
return false;
|
3341 |
-
}
|
3342 |
|
3343 |
-
|
3344 |
-
|
3345 |
-
|
3346 |
-
|
3347 |
-
|
3348 |
|
3349 |
-
|
3350 |
-
|
|
|
|
|
|
|
3351 |
|
3352 |
-
|
3353 |
-
|
3354 |
-
|
3355 |
-
* @return void
|
3356 |
-
*/
|
3357 |
-
function hook_wp_dashboard_setup(){
|
3358 |
-
$show_widget = current_user_can($this->conf->get('dashboard_widget_capability', 'edit_others_posts'));
|
3359 |
-
if ( function_exists( 'wp_add_dashboard_widget' ) && $show_widget ) {
|
3360 |
-
wp_add_dashboard_widget(
|
3361 |
-
'blc_dashboard_widget',
|
3362 |
-
__('Broken Link Checker', 'broken-link-checker'),
|
3363 |
-
array( $this, 'dashboard_widget' ),
|
3364 |
-
array( $this, 'dashboard_widget_control' )
|
3365 |
-
);
|
3366 |
-
}
|
3367 |
-
}
|
3368 |
|
3369 |
-
|
3370 |
-
|
3371 |
-
|
3372 |
-
|
3373 |
-
|
3374 |
-
|
3375 |
-
|
3376 |
-
|
3377 |
-
|
3378 |
-
|
3379 |
-
|
3380 |
-
|
3381 |
-
//PHP version. Any one is fine as long as WP supports it.
|
3382 |
-
$debug[ __('PHP version', 'broken-link-checker') ] = array(
|
3383 |
-
'state' => 'ok',
|
3384 |
-
'value' => phpversion(),
|
3385 |
-
);
|
3386 |
-
|
3387 |
-
//MySQL version
|
3388 |
-
$debug[ __('MySQL version', 'broken-link-checker') ] = array(
|
3389 |
-
'state' => 'ok',
|
3390 |
-
'value' => $wpdb->db_version(),
|
3391 |
-
);
|
3392 |
-
|
3393 |
-
//CURL presence and version
|
3394 |
-
if ( function_exists('curl_version') ){
|
3395 |
-
$version = curl_version();
|
3396 |
-
|
3397 |
-
if ( version_compare( $version['version'], '7.16.0', '<=' ) ){
|
3398 |
-
$data = array(
|
3399 |
-
'state' => 'warning',
|
3400 |
-
'value' => $version['version'],
|
3401 |
-
'message' => __('You have an old version of CURL. Redirect detection may not work properly.', 'broken-link-checker'),
|
3402 |
-
);
|
3403 |
} else {
|
3404 |
$data = array(
|
3405 |
-
'state' => '
|
3406 |
-
'value' =>
|
3407 |
);
|
3408 |
}
|
|
|
3409 |
|
3410 |
-
|
3411 |
-
|
3412 |
-
'state' => 'warning',
|
3413 |
-
'value' => __('Not installed', 'broken-link-checker'),
|
3414 |
-
);
|
3415 |
-
}
|
3416 |
-
$debug[ __('CURL version', 'broken-link-checker') ] = $data;
|
3417 |
-
|
3418 |
-
//Snoopy presence
|
3419 |
-
if ( class_exists('Snoopy') || file_exists(ABSPATH. WPINC . '/class-snoopy.php') ){
|
3420 |
-
$data = array(
|
3421 |
-
'state' => 'ok',
|
3422 |
-
'value' => __('Installed', 'broken-link-checker'),
|
3423 |
-
);
|
3424 |
-
} else {
|
3425 |
-
//No Snoopy? This should never happen, but if it does we *must* have CURL.
|
3426 |
-
if ( function_exists('curl_init') ){
|
3427 |
$data = array(
|
3428 |
'state' => 'ok',
|
3429 |
-
'value' => __(
|
3430 |
);
|
3431 |
} else {
|
3432 |
-
|
3433 |
-
|
3434 |
-
|
3435 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3436 |
);
|
3437 |
}
|
3438 |
|
3439 |
-
|
3440 |
-
|
3441 |
-
|
3442 |
-
|
3443 |
-
|
3444 |
-
|
3445 |
-
|
3446 |
-
|
3447 |
-
'
|
3448 |
-
|
3449 |
-
|
3450 |
-
|
3451 |
-
|
3452 |
-
'value' => __('Off', 'broken-link-checker'),
|
3453 |
-
);
|
3454 |
-
}
|
3455 |
|
3456 |
-
|
3457 |
-
|
3458 |
-
$debug['open_basedir'] = array(
|
3459 |
-
'state' => 'warning',
|
3460 |
-
'value' => sprintf( __('On ( %s )', 'broken-link-checker'), ini_get('open_basedir') ),
|
3461 |
-
'message' => __('Redirects may be detected as broken links when open_basedir is on.', 'broken-link-checker'),
|
3462 |
-
);
|
3463 |
-
} else {
|
3464 |
-
$debug['open_basedir'] = array(
|
3465 |
'state' => 'ok',
|
3466 |
-
'value' => __('
|
3467 |
-
);
|
3468 |
-
}
|
3469 |
-
|
3470 |
-
//Default PHP execution time limit
|
3471 |
-
$debug['Default PHP execution time limit'] = array(
|
3472 |
-
'state' => 'ok',
|
3473 |
-
'value' => sprintf(__('%s seconds'), ini_get('max_execution_time')),
|
3474 |
-
);
|
3475 |
-
|
3476 |
-
//Database character set. Usually it's UTF-8. Setting it to something else can cause problems
|
3477 |
-
//unless the site owner really knows what they're doing.
|
3478 |
-
$charset = $wpdb->get_charset_collate();
|
3479 |
-
$debug[ __('Database character set', 'broken-link-checker') ] = array(
|
3480 |
-
'state' => 'ok',
|
3481 |
-
'value' => !empty($charset) ? $charset : '-',
|
3482 |
-
);
|
3483 |
-
|
3484 |
-
//Resynch flag.
|
3485 |
-
$debug['Resynch. flag'] = array(
|
3486 |
-
'state' => 'ok',
|
3487 |
-
'value' => sprintf('%d', $this->conf->options['need_resynch'] ? '1 (resynch. required)' : '0 (resynch. not required)'),
|
3488 |
-
);
|
3489 |
-
|
3490 |
-
//Synch records
|
3491 |
-
$synch_records = intval($wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}blc_synch"));
|
3492 |
-
$data = array(
|
3493 |
-
'state' => 'ok',
|
3494 |
-
'value' => sprintf('%d', $synch_records),
|
3495 |
-
);
|
3496 |
-
if ( $synch_records == 0 ){
|
3497 |
-
$data['state'] = 'warning';
|
3498 |
-
$data['message'] = __('If this value is zero even after several page reloads you have probably encountered a bug.', 'broken-link-checker');
|
3499 |
-
}
|
3500 |
-
$debug['Synch. records'] = $data;
|
3501 |
-
|
3502 |
-
//Total links and instances (including invalid ones)
|
3503 |
-
$all_links = intval($wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}blc_links"));
|
3504 |
-
$all_instances = intval($wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}blc_instances"));
|
3505 |
-
|
3506 |
-
//Show the number of unparsed containers. Useful for debugging. For performance,
|
3507 |
-
//this is only shown when we have no links/instances yet.
|
3508 |
-
if( ($all_links == 0) && ($all_instances == 0) ){
|
3509 |
-
$unparsed_items = intval($wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}blc_synch WHERE synched=0"));
|
3510 |
-
$debug['Unparsed items'] = array(
|
3511 |
-
'state' => 'warning',
|
3512 |
-
'value' => $unparsed_items,
|
3513 |
);
|
3514 |
-
}
|
3515 |
|
3516 |
-
|
3517 |
-
|
3518 |
-
$
|
|
|
3519 |
'state' => 'ok',
|
3520 |
-
'value' =>
|
3521 |
-
);
|
3522 |
-
} else {
|
3523 |
-
$debug['Link records'] = array(
|
3524 |
-
'state' => 'warning',
|
3525 |
-
'value' => sprintf('%d (%d)', $all_links, $all_instances),
|
3526 |
);
|
3527 |
-
}
|
3528 |
|
3529 |
-
|
3530 |
-
|
3531 |
-
$notificationDebug = array(
|
3532 |
-
'value' => date('Y-m-d H:i:s T', $this->conf->options['last_notification_sent']),
|
3533 |
'state' => 'ok',
|
|
|
3534 |
);
|
3535 |
-
} else {
|
3536 |
-
$notificationDebug = array(
|
3537 |
-
'value' => 'Never',
|
3538 |
-
'state' => $this->conf->options['send_email_notifications'] ? 'ok' : 'warning',
|
3539 |
-
);
|
3540 |
-
}
|
3541 |
-
$debug['Last email notification'] = $notificationDebug;
|
3542 |
|
3543 |
-
|
3544 |
-
$
|
3545 |
-
$
|
3546 |
'state' => 'ok',
|
3547 |
-
'value' => sprintf(
|
3548 |
-
'"%s" on %s (%s)',
|
3549 |
-
htmlentities($email['subject']),
|
3550 |
-
date('Y-m-d H:i:s T', $email['timestamp']),
|
3551 |
-
$email['success'] ? 'success' : 'failure'
|
3552 |
-
)
|
3553 |
);
|
3554 |
-
|
3555 |
-
|
|
|
|
|
|
|
3556 |
|
3557 |
-
|
3558 |
-
|
3559 |
-
|
3560 |
-
if ( !empty($installation_log) ){
|
3561 |
-
$debug['Installation log'] = array(
|
3562 |
-
'state' => $this->conf->options['installation_complete'] ? 'ok' : 'error',
|
3563 |
-
'value' => implode("<br>\n", $installation_log),
|
3564 |
-
);
|
3565 |
-
} else {
|
3566 |
-
$debug['Installation log'] = array(
|
3567 |
-
'state' => 'warning',
|
3568 |
-
'value' => 'No installation log found found.',
|
3569 |
-
);
|
3570 |
-
}
|
3571 |
|
3572 |
-
|
3573 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3574 |
|
3575 |
-
|
3576 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3577 |
|
3578 |
-
|
3579 |
-
|
3580 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3581 |
|
3582 |
-
|
3583 |
-
|
3584 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3585 |
|
3586 |
-
|
3587 |
-
|
3588 |
-
|
3589 |
-
|
3590 |
-
|
3591 |
-
|
3592 |
-
|
3593 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
3594 |
|
3595 |
-
|
3596 |
-
return;
|
3597 |
}
|
3598 |
|
3599 |
-
|
3600 |
-
|
3601 |
-
if ( empty($email) ) {
|
3602 |
-
//Default to the admin email.
|
3603 |
-
$email = get_option('admin_email');
|
3604 |
-
}
|
3605 |
-
if ( $this->conf->options['send_email_notifications'] && !empty($email) ) {
|
3606 |
-
$this->send_admin_notification($links, $email);
|
3607 |
-
}
|
3608 |
|
3609 |
-
|
3610 |
-
|
3611 |
-
|
3612 |
-
}
|
3613 |
|
3614 |
-
|
3615 |
-
|
3616 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3617 |
|
3618 |
-
|
3619 |
-
|
3620 |
-
|
3621 |
-
__("[%s] Broken links detected", 'broken-link-checker'),
|
3622 |
-
html_entity_decode(get_option('blogname'), ENT_QUOTES)
|
3623 |
-
);
|
3624 |
|
3625 |
-
|
3626 |
-
|
3627 |
-
|
3628 |
-
|
3629 |
-
|
3630 |
-
|
3631 |
-
)
|
3632 |
-
|
3633 |
-
|
3634 |
-
$body .= "<br>";
|
3635 |
|
3636 |
-
|
3637 |
-
|
3638 |
-
|
3639 |
-
|
3640 |
-
$body .= $this->build_instance_list_for_email($instances);
|
3641 |
|
3642 |
-
|
3643 |
-
$
|
3644 |
}
|
3645 |
|
3646 |
-
|
3647 |
-
|
3648 |
-
|
3649 |
-
|
3650 |
-
|
3651 |
-
|
3652 |
-
}
|
3653 |
|
3654 |
-
|
3655 |
-
if ( count($instances) > $max_displayed_links ){
|
3656 |
-
$line = sprintf(
|
3657 |
_n(
|
3658 |
-
|
3659 |
-
|
3660 |
-
$
|
3661 |
'broken-link-checker'
|
3662 |
),
|
3663 |
-
$
|
3664 |
);
|
3665 |
-
|
3666 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3667 |
}
|
3668 |
|
3669 |
-
$
|
|
|
|
|
|
|
3670 |
|
3671 |
-
|
3672 |
-
|
3673 |
-
|
3674 |
-
|
3675 |
-
|
3676 |
-
|
3677 |
-
|
3678 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3679 |
|
3680 |
-
|
3681 |
-
|
3682 |
|
3683 |
-
|
3684 |
-
|
3685 |
-
|
|
|
3686 |
}
|
3687 |
-
}
|
3688 |
|
3689 |
-
|
3690 |
-
|
3691 |
-
|
3692 |
-
|
3693 |
-
|
3694 |
|
3695 |
-
|
3696 |
-
|
3697 |
|
3698 |
-
|
3699 |
-
|
3700 |
-
|
3701 |
|
3702 |
-
|
3703 |
-
|
3704 |
-
|
3705 |
|
3706 |
-
|
3707 |
|
3708 |
-
|
3709 |
-
|
3710 |
-
|
3711 |
|
3712 |
-
|
3713 |
-
|
3714 |
-
|
3715 |
-
|
3716 |
-
|
3717 |
-
|
3718 |
|
3719 |
-
|
3720 |
-
|
3721 |
|
3722 |
-
|
3723 |
-
|
3724 |
-
|
3725 |
-
|
3726 |
-
|
3727 |
-
|
3728 |
-
|
3729 |
-
|
3730 |
-
|
3731 |
-
|
3732 |
-
|
|
|
|
|
3733 |
}
|
3734 |
-
$authorInstances[$post->post_author][] = $instance;
|
3735 |
}
|
3736 |
-
}
|
3737 |
|
3738 |
-
|
3739 |
-
|
3740 |
-
|
3741 |
-
|
3742 |
-
|
3743 |
|
3744 |
-
|
3745 |
-
|
3746 |
-
|
3747 |
-
|
3748 |
-
|
3749 |
-
|
3750 |
-
|
3751 |
-
|
3752 |
-
|
3753 |
-
|
3754 |
|
3755 |
-
|
3756 |
-
|
3757 |
|
3758 |
-
|
3759 |
-
|
|
|
|
|
|
|
3760 |
}
|
|
|
3761 |
|
3762 |
-
|
|
|
3763 |
}
|
3764 |
-
}
|
3765 |
|
3766 |
-
|
3767 |
-
|
3768 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3769 |
|
3770 |
-
|
3771 |
-
|
3772 |
-
|
3773 |
-
|
3774 |
-
|
3775 |
-
|
3776 |
-
|
3777 |
-
|
3778 |
-
'broken' => 1,
|
3779 |
-
'warning' => 0,
|
3780 |
-
),
|
3781 |
-
array(
|
3782 |
-
'warning' => 1,
|
3783 |
-
),
|
3784 |
-
'%d'
|
3785 |
-
);
|
3786 |
-
}
|
3787 |
|
3788 |
-
|
3789 |
-
|
3790 |
-
|
3791 |
-
|
3792 |
-
|
3793 |
-
|
3794 |
-
|
3795 |
-
|
3796 |
|
3797 |
-
|
3798 |
-
|
3799 |
-
|
3800 |
-
|
|
|
|
|
|
|
3801 |
}
|
3802 |
-
} else {
|
3803 |
-
wp_clear_scheduled_hook('blc_cron_check_links');
|
3804 |
-
}
|
3805 |
|
3806 |
-
|
3807 |
-
|
3808 |
-
|
3809 |
-
wp_schedule_event(time(), $this->conf->options['notification_schedule'], 'blc_cron_email_notifications');
|
3810 |
}
|
3811 |
-
} else {
|
3812 |
-
wp_clear_scheduled_hook('blc_cron_email_notifications');
|
3813 |
}
|
3814 |
|
3815 |
-
|
3816 |
-
|
3817 |
-
|
|
|
|
|
|
|
|
|
3818 |
}
|
3819 |
-
}
|
3820 |
-
|
3821 |
-
/**
|
3822 |
-
* Load the plugin's textdomain.
|
3823 |
-
*
|
3824 |
-
* @return void
|
3825 |
-
*/
|
3826 |
-
function load_language(){
|
3827 |
-
$this->is_textdomain_loaded = load_plugin_textdomain( 'broken-link-checker', false, basename(dirname($this->loader)) . '/languages' );
|
3828 |
-
}
|
3829 |
|
3830 |
-
|
3831 |
-
|
3832 |
-
|
3833 |
-
|
3834 |
|
3835 |
-
|
3836 |
-
|
3837 |
-
|
3838 |
|
3839 |
-
}//class ends here
|
3840 |
|
3841 |
} // if class_exists...
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* Simple function to replicate PHP 5 behaviour
|
5 |
*/
|
6 |
if ( ! function_exists( 'microtime_float' ) ) {
|
7 |
function microtime_float() {
|
8 |
+
list( $usec, $sec ) = explode( ' ', microtime() );
|
9 |
+
return ( (float) $usec + (float) $sec );
|
10 |
}
|
11 |
}
|
12 |
|
15 |
require BLC_DIRECTORY . '/includes/wp-mutex.php';
|
16 |
require BLC_DIRECTORY . '/includes/transactions-manager.php';
|
17 |
|
18 |
+
if ( ! class_exists( 'wsBrokenLinkChecker' ) ) {
|
19 |
+
class wsBrokenLinkChecker {
|
20 |
+
|
21 |
+
var $conf;
|
22 |
+
var $loader;
|
23 |
+
var $my_basename = '';
|
24 |
+
var $db_version; //The required version of the plugin's DB schema.
|
25 |
+
var $execution_start_time;
|
26 |
+
var $is_textdomain_loaded = false;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Class constructor
|
30 |
+
*
|
31 |
+
* @param string $loader The fully qualified filename of the loader script that WP identifies as the "main" plugin file.
|
32 |
+
* @param blcConfigurationManager $conf An instance of the configuration manager
|
33 |
+
* @return void
|
34 |
+
*/
|
35 |
+
function __construct( $loader, $conf ) {
|
36 |
+
$this->db_version = BLC_DATABASE_VERSION;
|
37 |
+
|
38 |
+
$this->conf = $conf;
|
39 |
+
$this->loader = $loader;
|
40 |
+
$this->my_basename = plugin_basename( $this->loader );
|
41 |
+
|
42 |
+
$this->load_language();
|
43 |
+
|
44 |
+
//Unlike the activation hook, the deactivation callback *can* be registered in this file
|
45 |
+
//because deactivation happens after this class has already been instantiated (durinng the
|
46 |
+
//'init' action).
|
47 |
+
register_deactivation_hook( $loader, array( $this, 'deactivation' ) );
|
48 |
+
|
49 |
+
add_action( 'admin_menu', array( $this, 'admin_menu' ) );
|
50 |
+
|
51 |
+
//Load jQuery on Dashboard pages (probably redundant as WP already does that)
|
52 |
+
add_action( 'admin_print_scripts', array( $this, 'admin_print_scripts' ) );
|
53 |
+
|
54 |
+
//The dashboard widget
|
55 |
+
add_action( 'wp_dashboard_setup', array( $this, 'hook_wp_dashboard_setup' ) );
|
56 |
+
|
57 |
+
//AJAXy hooks
|
58 |
+
add_action( 'wp_ajax_blc_full_status', array( $this, 'ajax_full_status' ) );
|
59 |
+
add_action( 'wp_ajax_blc_dashboard_status', array( $this, 'ajax_dashboard_status' ) );
|
60 |
+
add_action( 'wp_ajax_blc_work', array( $this, 'ajax_work' ) );
|
61 |
+
add_action( 'wp_ajax_blc_discard', array( $this, 'ajax_discard' ) );
|
62 |
+
add_action( 'wp_ajax_blc_edit', array( $this, 'ajax_edit' ) );
|
63 |
+
add_action( 'wp_ajax_blc_link_details', array( $this, 'ajax_link_details' ) );
|
64 |
+
add_action( 'wp_ajax_blc_unlink', array( $this, 'ajax_unlink' ) );
|
65 |
+
add_action( 'wp_ajax_blc_recheck', array( $this, 'ajax_recheck' ) );
|
66 |
+
add_action( 'wp_ajax_blc_deredirect', array( $this, 'ajax_deredirect' ) );
|
67 |
+
add_action( 'wp_ajax_blc_current_load', array( $this, 'ajax_current_load' ) );
|
68 |
+
|
69 |
+
add_action( 'wp_ajax_blc_dismiss', array( $this, 'ajax_dismiss' ) );
|
70 |
+
add_action( 'wp_ajax_blc_undismiss', array( $this, 'ajax_undismiss' ) );
|
71 |
+
|
72 |
+
//Add/remove Cron events
|
73 |
+
$this->setup_cron_events();
|
74 |
+
|
75 |
+
//Set hooks that listen for our Cron actions
|
76 |
+
add_action( 'blc_cron_email_notifications', array( $this, 'maybe_send_email_notifications' ) );
|
77 |
+
add_action( 'blc_cron_check_links', array( $this, 'cron_check_links' ) );
|
78 |
+
add_action( 'blc_cron_database_maintenance', array( $this, 'database_maintenance' ) );
|
79 |
+
|
80 |
+
//Set the footer hook that will call the worker function via AJAX.
|
81 |
+
add_action( 'admin_footer', array( $this, 'admin_footer' ) );
|
82 |
+
//Add a "Screen Options" panel to the "Broken Links" page
|
83 |
+
add_screen_options_panel(
|
84 |
+
'blc-screen-options',
|
85 |
+
'',
|
86 |
+
array( $this, 'screen_options_html' ),
|
87 |
+
'tools_page_view-broken-links',
|
88 |
+
array( $this, 'ajax_save_screen_options' ),
|
89 |
+
true
|
90 |
+
);
|
91 |
+
|
92 |
+
//Display an explanatory note on the "Tools -> Broken Links -> Warnings" page.
|
93 |
+
add_action( 'admin_notices', array( $this, 'show_warnings_section_notice' ) );
|
94 |
+
|
95 |
+
}
|
96 |
+
|
97 |
+
/**
|
98 |
+
* Output the script that runs the link monitor while the Dashboard is open.
|
99 |
+
*
|
100 |
+
* @return void
|
101 |
+
*/
|
102 |
+
function admin_footer() {
|
103 |
+
if ( ! $this->conf->options['run_in_dashboard'] ) {
|
104 |
+
return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
105 |
}
|
106 |
+
$nonce = wp_create_nonce( 'blc_work' );
|
107 |
+
?>
|
108 |
+
<!-- wsblc admin footer -->
|
109 |
+
<script type='text/javascript'>
|
110 |
+
(function($){
|
111 |
+
|
112 |
+
//(Re)starts the background worker thread
|
113 |
+
function blcDoWork(){
|
114 |
+
$.post(
|
115 |
+
"<?php echo admin_url( 'admin-ajax.php' ); ?>",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
116 |
{
|
117 |
+
'action' : 'blc_work',
|
118 |
+
'_ajax_nonce' : '<?php echo esc_js( $nonce ); ?>'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
119 |
}
|
120 |
);
|
121 |
}
|
122 |
+
//Call it the first time
|
123 |
+
blcDoWork();
|
124 |
|
125 |
+
//Then call it periodically every X seconds
|
126 |
+
setInterval(blcDoWork, <?php echo ( intval( $this->conf->options['max_execution_time'] ) + 1 ) * 1000; ?>);
|
127 |
|
128 |
+
})(jQuery);
|
129 |
+
</script>
|
130 |
+
<!-- /wsblc admin footer -->
|
131 |
+
<?php
|
132 |
+
}
|
133 |
|
134 |
+
/**
|
135 |
+
* Check if an URL matches the exclusion list.
|
136 |
+
*
|
137 |
+
* @param string $url
|
138 |
+
* @return bool
|
139 |
+
*/
|
140 |
+
function is_excluded( $url ) {
|
141 |
+
if ( ! is_array( $this->conf->options['exclusion_list'] ) ) {
|
142 |
+
return false;
|
143 |
+
}
|
144 |
+
foreach ( $this->conf->options['exclusion_list'] as $excluded_word ) {
|
145 |
+
if ( stristr( $url, $excluded_word ) ) {
|
146 |
+
return true;
|
147 |
+
}
|
148 |
+
}
|
149 |
+
return false;
|
150 |
}
|
151 |
|
152 |
+
function dashboard_widget() {
|
153 |
+
?>
|
154 |
+
<p id='wsblc_activity_box'><?php _e( 'Loading...', 'broken-link-checker' ); ?></p>
|
155 |
+
<script type='text/javascript'>
|
156 |
+
jQuery( function($){
|
157 |
+
var blc_was_autoexpanded = false;
|
158 |
+
|
159 |
+
function blcDashboardStatus(){
|
160 |
+
$.getJSON(
|
161 |
+
"<?php echo admin_url( 'admin-ajax.php' ); ?>",
|
162 |
+
{
|
163 |
+
'action' : 'blc_dashboard_status',
|
164 |
+
'random' : Math.random()
|
165 |
+
},
|
166 |
+
function (data){
|
167 |
+
if ( data && ( typeof(data.text) != 'undefined' ) ) {
|
168 |
+
$('#wsblc_activity_box').html(data.text);
|
169 |
+
<?php if ( $this->conf->options['autoexpand_widget'] ) { ?>
|
170 |
+
//Expand the widget if there are broken links.
|
171 |
+
//Do this only once per pageload so as not to annoy the user.
|
172 |
+
if ( !blc_was_autoexpanded && ( data.status.broken_links > 0 ) ){
|
173 |
+
$('#blc_dashboard_widget.postbox').removeClass('closed');
|
174 |
+
blc_was_autoexpanded = true;
|
175 |
+
}
|
176 |
+
<?php } ?>
|
177 |
+
} else {
|
178 |
+
$('#wsblc_activity_box').html('<?php _e( '[ Network error ]', 'broken-link-checker' ); ?>');
|
179 |
+
}
|
180 |
|
181 |
+
setTimeout( blcDashboardStatus, 120*1000 ); //...update every two minutes
|
182 |
+
}
|
183 |
+
);
|
184 |
+
}
|
|
|
|
|
185 |
|
186 |
+
blcDashboardStatus();//Call it the first time
|
|
|
|
|
|
|
|
|
|
|
|
|
187 |
|
188 |
+
} );
|
189 |
+
</script>
|
190 |
+
<?php
|
191 |
+
}
|
192 |
|
193 |
+
function dashboard_widget_control(
|
194 |
+
/** @noinspection PhpUnusedParameterInspection */ $widget_id, $form_inputs = array()
|
195 |
+
) {
|
196 |
+
if ( 'POST' == $_SERVER['REQUEST_METHOD'] && 'blc_dashboard_widget' == $_POST['widget_id'] ) {
|
197 |
+
//It appears $form_inputs isn't used in the current WP version, so lets just use $_POST
|
198 |
+
$this->conf->options['autoexpand_widget'] = ! empty( $_POST['blc-autoexpand'] );
|
199 |
+
$this->conf->save_options();
|
200 |
+
}
|
201 |
|
202 |
+
?>
|
203 |
+
<p><label for="blc-autoexpand">
|
204 |
+
<input id="blc-autoexpand" name="blc-autoexpand" type="checkbox" value="1"
|
205 |
+
<?php
|
206 |
+
if ( $this->conf->options['autoexpand_widget'] ) {
|
207 |
+
echo 'checked="checked"';}
|
208 |
+
?>
|
209 |
+
/>
|
210 |
+
<?php _e( 'Automatically expand the widget if broken links have been detected', 'broken-link-checker' ); ?>
|
211 |
+
</label></p>
|
212 |
+
<?php
|
213 |
+
}
|
214 |
|
215 |
+
function admin_print_scripts() {
|
216 |
+
//jQuery is used for triggering the link monitor via AJAX when any admin page is open.
|
217 |
+
wp_enqueue_script( 'jquery' );
|
218 |
+
}
|
219 |
+
|
220 |
+
function enqueue_settings_scripts() {
|
221 |
+
//jQuery UI is used on the settings page
|
222 |
+
wp_enqueue_script( 'jquery-ui-core' ); //Used for background color animation
|
223 |
+
wp_enqueue_script( 'jquery-ui-dialog' );
|
224 |
+
wp_enqueue_script( 'jquery-ui-tabs' );
|
225 |
+
wp_enqueue_script( 'jquery-cookie', plugins_url( 'js/jquery.cookie.js', BLC_PLUGIN_FILE ) ); //Used for storing last widget states, etc
|
226 |
+
}
|
227 |
+
|
228 |
+
function enqueue_link_page_scripts() {
|
229 |
+
wp_enqueue_script( 'jquery-ui-core' );
|
230 |
+
wp_enqueue_script( 'jquery-ui-dialog' ); //Used for the search form
|
231 |
+
wp_enqueue_script( 'jquery-color' ); //Used for background color animation
|
232 |
+
wp_enqueue_script( 'sprintf', plugins_url( 'js/sprintf.js', BLC_PLUGIN_FILE ) ); //Used in error messages
|
233 |
+
}
|
234 |
+
|
235 |
+
/**
|
236 |
+
* Initiate a full recheck - reparse everything and check all links anew.
|
237 |
+
*
|
238 |
+
* @return void
|
239 |
+
*/
|
240 |
+
function initiate_recheck() {
|
241 |
+
global $wpdb; /** @var wpdb $wpdb */
|
242 |
+
|
243 |
+
//Delete all discovered instances
|
244 |
+
$wpdb->query( "TRUNCATE {$wpdb->prefix}blc_instances" );
|
245 |
+
|
246 |
+
//Delete all discovered links
|
247 |
+
$wpdb->query( "TRUNCATE {$wpdb->prefix}blc_links" );
|
248 |
+
|
249 |
+
//Mark all posts, custom fields and bookmarks for processing.
|
250 |
+
blc_resynch( true );
|
251 |
+
}
|
252 |
+
|
253 |
+
/**
|
254 |
+
* A hook executed when the plugin is deactivated.
|
255 |
+
*
|
256 |
+
* @return void
|
257 |
+
*/
|
258 |
+
function deactivation() {
|
259 |
+
//Remove our Cron events
|
260 |
+
wp_clear_scheduled_hook( 'blc_cron_check_links' );
|
261 |
+
wp_clear_scheduled_hook( 'blc_cron_email_notifications' );
|
262 |
+
wp_clear_scheduled_hook( 'blc_cron_database_maintenance' );
|
263 |
+
wp_clear_scheduled_hook( 'blc_cron_check_news' ); //Unused event.
|
264 |
+
//Note the deactivation time for each module. This will help them
|
265 |
+
//synch up propely if/when the plugin is reactivated.
|
266 |
+
$moduleManager = blcModuleManager::getInstance();
|
267 |
+
$the_time = current_time( 'timestamp' );
|
268 |
+
foreach ( $moduleManager->get_active_modules() as $module_id => $module ) {
|
269 |
+
$this->conf->options['module_deactivated_when'][ $module_id ] = $the_time;
|
270 |
+
}
|
271 |
+
$this->conf->save_options();
|
272 |
+
}
|
273 |
|
274 |
+
/**
|
275 |
+
* Perform various database maintenance tasks on the plugin's tables.
|
276 |
+
*
|
277 |
+
* Removes records that reference disabled containers and parsers,
|
278 |
+
* deletes invalid instances and links, optimizes tables, etc.
|
279 |
+
*
|
280 |
+
* @return void
|
281 |
+
*/
|
282 |
+
function database_maintenance() {
|
283 |
+
blcContainerHelper::cleanup_containers();
|
284 |
+
blc_cleanup_instances();
|
285 |
+
blc_cleanup_links();
|
|
|
|
|
|
|
286 |
|
287 |
+
blcUtility::optimize_database();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
288 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
289 |
|
290 |
+
/**
|
291 |
+
* Create the plugin's menu items and enqueue their scripts and CSS.
|
292 |
+
* Callback for the 'admin_menu' action.
|
293 |
+
*
|
294 |
+
* @return void
|
295 |
+
*/
|
296 |
+
function admin_menu() {
|
297 |
+
if ( current_user_can( 'manage_options' ) ) {
|
298 |
+
add_filter( 'plugin_action_links', array( $this, 'plugin_action_links' ), 10, 2 );
|
299 |
+
}
|
300 |
|
301 |
+
$options_page_hook = add_options_page(
|
302 |
+
__( 'Link Checker Settings', 'broken-link-checker' ),
|
303 |
+
__( 'Link Checker', 'broken-link-checker' ),
|
304 |
+
'manage_options',
|
305 |
+
'link-checker-settings',
|
306 |
+
array( $this, 'options_page' )
|
307 |
+
);
|
308 |
|
309 |
+
$menu_title = __( 'Broken Links', 'broken-link-checker' );
|
310 |
+
if ( $this->conf->options['show_link_count_bubble'] ) {
|
311 |
+
//To make it easier to notice when broken links appear, display the current number of
|
312 |
+
//broken links in a little bubble notification in the "Broken Links" menu.
|
313 |
+
//(Similar to how the number of plugin updates and unmoderated comments is displayed).
|
314 |
+
$blc_link_query = blcLinkQuery::getInstance();
|
315 |
+
$broken_links = $blc_link_query->get_filter_links( 'broken', array( 'count_only' => true ) );
|
316 |
+
if ( $broken_links > 0 ) {
|
317 |
+
//TODO: Appropriating existing CSS classes for my own purposes is hacky. Fix eventually.
|
318 |
+
$menu_title .= sprintf(
|
319 |
+
' <span class="update-plugins"><span class="update-count blc-menu-bubble">%d</span></span>',
|
320 |
+
$broken_links
|
321 |
+
);
|
322 |
+
}
|
323 |
}
|
324 |
+
$links_page_hook = add_management_page(
|
325 |
+
__( 'View Broken Links', 'broken-link-checker' ),
|
326 |
+
$menu_title,
|
327 |
+
'edit_others_posts',
|
328 |
+
'view-broken-links',
|
329 |
+
array( $this, 'links_page' )
|
330 |
+
);
|
331 |
+
|
332 |
+
//Add plugin-specific scripts and CSS only to the it's own pages
|
333 |
+
add_action( 'admin_print_styles-' . $options_page_hook, array( $this, 'options_page_css' ) );
|
334 |
+
add_action( 'admin_print_styles-' . $links_page_hook, array( $this, 'links_page_css' ) );
|
335 |
+
add_action( 'admin_print_scripts-' . $options_page_hook, array( $this, 'enqueue_settings_scripts' ) );
|
336 |
+
add_action( 'admin_print_scripts-' . $links_page_hook, array( $this, 'enqueue_link_page_scripts' ) );
|
337 |
+
|
338 |
+
//Make the Settings page link to the link list
|
339 |
+
add_screen_meta_link(
|
340 |
+
'blc-links-page-link',
|
341 |
+
__( 'Go to Broken Links', 'broken-link-checker' ),
|
342 |
+
admin_url( 'tools.php?page=view-broken-links' ),
|
343 |
+
$options_page_hook,
|
344 |
+
array( 'style' => 'font-weight: bold;' )
|
345 |
+
);
|
346 |
+
}
|
347 |
|
348 |
+
/**
|
349 |
+
* plugin_action_links()
|
350 |
+
* Handler for the 'plugin_action_links' hook. Adds a "Settings" link to this plugin's entry
|
351 |
+
* on the plugin list.
|
352 |
+
*
|
353 |
+
* @param array $links
|
354 |
+
* @param string $file
|
355 |
+
* @return array
|
356 |
+
*/
|
357 |
+
function plugin_action_links( $links, $file ) {
|
358 |
+
if ( $file == $this->my_basename ) {
|
359 |
+
$links[] = "<a href='options-general.php?page=link-checker-settings'>" . __( 'Settings' ) . '</a>';
|
360 |
}
|
361 |
+
return $links;
|
362 |
+
}
|
363 |
|
364 |
+
function options_page() {
|
365 |
+
$moduleManager = blcModuleManager::getInstance();
|
366 |
+
|
367 |
+
//Prior to 1.5.2 (released 2012-05-27), there was a bug that would cause the donation flag to be
|
368 |
+
//set incorrectly. So we'll unset the flag in that case.
|
369 |
+
$reset_donation_flag = ( $this->conf->get( 'first_installation_timestamp', 0 ) < strtotime( '2012-05-27 00:00' ) ) && ! $this->conf->get( 'donation_flag_fixed', false );
|
370 |
+
|
371 |
+
if ( $reset_donation_flag ) {
|
372 |
+
$this->conf->set( 'user_has_donated', false );
|
373 |
+
$this->conf->set( 'donation_flag_fixed', true );
|
374 |
+
$this->conf->save_options();
|
375 |
}
|
376 |
+
|
377 |
+
if ( isset( $_POST['recheck'] ) && ! empty( $_POST['recheck'] ) ) {
|
378 |
+
$this->initiate_recheck();
|
379 |
+
|
380 |
+
//Redirect back to the settings page
|
381 |
+
$base_url = remove_query_arg( array( '_wpnonce', 'noheader', 'updated', 'error', 'action', 'message' ) );
|
382 |
+
wp_redirect(
|
383 |
+
add_query_arg(
|
384 |
+
array(
|
385 |
+
'recheck-initiated' => true,
|
386 |
+
),
|
387 |
+
$base_url
|
388 |
+
)
|
389 |
+
);
|
390 |
+
die();
|
391 |
}
|
392 |
|
393 |
+
$available_link_actions = array(
|
394 |
+
'edit' => __( 'Edit URL', 'broken-link-checker' ),
|
395 |
+
'delete' => __( 'Unlink', 'broken-link-checker' ),
|
396 |
+
'blc-discard-action' => __( 'Not broken', 'broken-link-checker' ),
|
397 |
+
'blc-dismiss-action' => __( 'Dismiss', 'broken-link-checker' ),
|
398 |
+
'blc-recheck-action' => __( 'Recheck', 'broken-link-checker' ),
|
399 |
+
'blc-deredirect-action' => _x( 'Fix redirect', 'link action; replace one redirect with a direct link', 'broken-link-checker' ),
|
400 |
+
);
|
401 |
|
402 |
+
if ( isset( $_POST['submit'] ) ) {
|
403 |
+
check_admin_referer( 'link-checker-options' );
|
404 |
|
405 |
+
$cleanPost = $_POST;
|
406 |
+
if ( function_exists( 'wp_magic_quotes' ) ) {
|
407 |
+
$cleanPost = stripslashes_deep( $cleanPost ); //Ceterum censeo, WP shouldn't mangle superglobals.
|
408 |
+
}
|
|
|
409 |
|
410 |
+
//Activate/deactivate modules
|
411 |
+
if ( ! empty( $_POST['module'] ) ) {
|
412 |
+
$active = array_keys( $_POST['module'] );
|
413 |
+
$moduleManager->set_active_modules( $active );
|
414 |
+
}
|
415 |
|
416 |
+
//Only post statuses that actually exist can be selected
|
417 |
+
if ( isset( $_POST['enabled_post_statuses'] ) && is_array( $_POST['enabled_post_statuses'] ) ) {
|
418 |
+
$available_statuses = get_post_stati();
|
419 |
+
$enabled_post_statuses = array_intersect( $_POST['enabled_post_statuses'], $available_statuses );
|
420 |
+
} else {
|
421 |
+
$enabled_post_statuses = array();
|
422 |
+
}
|
423 |
+
//At least one status must be enabled; defaults to "Published".
|
424 |
+
if ( empty( $enabled_post_statuses ) ) {
|
425 |
+
$enabled_post_statuses = array( 'publish' );
|
426 |
+
}
|
427 |
|
428 |
+
//Did the user add/remove any post statuses?
|
429 |
+
$same_statuses = array_intersect( $enabled_post_statuses, $this->conf->options['enabled_post_statuses'] );
|
430 |
+
$post_statuses_changed = ( count( $same_statuses ) != count( $enabled_post_statuses ) )
|
431 |
+
|| ( count( $same_statuses ) !== count( $this->conf->options['enabled_post_statuses'] ) );
|
432 |
|
433 |
+
$this->conf->options['enabled_post_statuses'] = $enabled_post_statuses;
|
434 |
|
435 |
+
//The execution time limit must be above zero
|
436 |
+
$new_execution_time = intval( $_POST['max_execution_time'] );
|
437 |
+
if ( $new_execution_time > 0 ) {
|
438 |
+
$this->conf->options['max_execution_time'] = $new_execution_time;
|
439 |
+
}
|
440 |
|
441 |
+
//The check threshold also must be > 0
|
442 |
+
$new_check_threshold = intval( $_POST['check_threshold'] );
|
|
|
|
|
|
|
|
|
|
|
|
|
443 |
|
444 |
+
if ( $new_check_threshold > 0 ) {
|
445 |
+
$this->conf->options['check_threshold'] = $new_check_threshold;
|
446 |
+
}
|
|
|
447 |
|
448 |
+
$this->conf->options['mark_broken_links'] = ! empty( $_POST['mark_broken_links'] );
|
449 |
+
$new_broken_link_css = trim( $cleanPost['broken_link_css'] );
|
450 |
+
$this->conf->options['broken_link_css'] = $new_broken_link_css;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
451 |
|
452 |
+
$this->conf->options['mark_removed_links'] = ! empty( $_POST['mark_removed_links'] );
|
453 |
+
$new_removed_link_css = trim( $cleanPost['removed_link_css'] );
|
454 |
+
$this->conf->options['removed_link_css'] = $new_removed_link_css;
|
|
|
455 |
|
456 |
+
$this->conf->options['nofollow_broken_links'] = ! empty( $_POST['nofollow_broken_links'] );
|
|
|
|
|
|
|
457 |
|
458 |
+
$this->conf->options['suggestions_enabled'] = ! empty( $_POST['suggestions_enabled'] );
|
|
|
|
|
|
|
|
|
|
|
|
|
459 |
|
460 |
+
$this->conf->options['exclusion_list'] = array_filter(
|
461 |
+
preg_split(
|
462 |
+
'/[\s\r\n]+/', //split on newlines and whitespace
|
463 |
+
$cleanPost['exclusion_list'],
|
464 |
+
-1,
|
465 |
+
PREG_SPLIT_NO_EMPTY //skip empty values
|
466 |
+
)
|
467 |
+
);
|
468 |
|
469 |
+
//Parse the custom field list
|
470 |
+
$new_custom_fields = array_filter(
|
471 |
+
preg_split(
|
472 |
+
'/[\r\n]+/',
|
473 |
+
$cleanPost['blc_custom_fields'],
|
474 |
+
-1,
|
475 |
+
PREG_SPLIT_NO_EMPTY
|
476 |
+
)
|
477 |
+
);
|
478 |
+
|
479 |
+
//Calculate the difference between the old custom field list and the new one (used later)
|
480 |
+
$diff1 = array_diff( $new_custom_fields, $this->conf->options['custom_fields'] );
|
481 |
+
$diff2 = array_diff( $this->conf->options['custom_fields'], $new_custom_fields );
|
482 |
+
$this->conf->options['custom_fields'] = $new_custom_fields;
|
483 |
+
|
484 |
+
//Parse the custom field list
|
485 |
+
$new_acf_fields = array_filter( preg_split( '/[\r\n]+/', $cleanPost['blc_acf_fields'], -1, PREG_SPLIT_NO_EMPTY ) );
|
486 |
+
|
487 |
+
//Calculate the difference between the old custom field list and the new one (used later)
|
488 |
+
$acf_fields_diff1 = array_diff( $new_acf_fields, $this->conf->options['acf_fields'] );
|
489 |
+
$acf_fields_diff2 = array_diff( $this->conf->options['acf_fields'], $new_acf_fields );
|
490 |
+
$this->conf->options['acf_fields'] = $new_acf_fields;
|
491 |
+
|
492 |
+
//Turning off warnings turns existing warnings into "broken" links.
|
493 |
+
$warnings_enabled = ! empty( $_POST['warnings_enabled'] );
|
494 |
+
if ( $this->conf->get( 'warnings_enabled' ) && ! $warnings_enabled ) {
|
495 |
+
$this->promote_warnings_to_broken();
|
496 |
+
}
|
497 |
+
$this->conf->options['warnings_enabled'] = $warnings_enabled;
|
498 |
+
|
499 |
+
//HTTP timeout
|
500 |
+
$new_timeout = intval( $_POST['timeout'] );
|
501 |
+
if ( $new_timeout > 0 ) {
|
502 |
+
$this->conf->options['timeout'] = $new_timeout;
|
503 |
+
}
|
504 |
+
|
505 |
+
//Server load limit
|
506 |
+
if ( isset( $_POST['server_load_limit'] ) ) {
|
507 |
+
$this->conf->options['server_load_limit'] = floatval( $_POST['server_load_limit'] );
|
508 |
+
if ( $this->conf->options['server_load_limit'] < 0 ) {
|
509 |
+
$this->conf->options['server_load_limit'] = 0;
|
510 |
}
|
511 |
+
$this->conf->options['enable_load_limit'] = $this->conf->options['server_load_limit'] > 0;
|
512 |
}
|
513 |
|
514 |
+
//Target resource usage (1% to 100%)
|
515 |
+
if ( isset( $_POST['target_resource_usage'] ) ) {
|
516 |
+
$usage = floatval( $_POST['target_resource_usage'] );
|
517 |
+
$usage = max( min( $usage / 100, 1 ), 0.01 );
|
518 |
+
$this->conf->options['target_resource_usage'] = $usage;
|
519 |
+
}
|
520 |
|
521 |
+
//When to run the checker
|
522 |
+
$this->conf->options['run_in_dashboard'] = ! empty( $_POST['run_in_dashboard'] );
|
523 |
+
$this->conf->options['run_via_cron'] = ! empty( $_POST['run_via_cron'] );
|
524 |
+
|
525 |
+
//Email notifications on/off
|
526 |
+
$email_notifications = ! empty( $_POST['send_email_notifications'] );
|
527 |
+
$send_authors_email_notifications = ! empty( $_POST['send_authors_email_notifications'] );
|
528 |
+
|
529 |
+
if (
|
530 |
+
( $email_notifications && ! $this->conf->options['send_email_notifications'] )
|
531 |
+
|| ( $send_authors_email_notifications && ! $this->conf->options['send_authors_email_notifications'] )
|
532 |
+
) {
|
533 |
+
/*
|
534 |
+
The plugin should only send notifications about links that have become broken
|
535 |
+
since the time when email notifications were turned on. If we don't do this,
|
536 |
+
the first email notification will be sent nigh-immediately and list *all* broken
|
537 |
+
links that the plugin currently knows about.
|
538 |
+
*/
|
539 |
+
$this->conf->options['last_notification_sent'] = time();
|
540 |
}
|
541 |
+
$this->conf->options['send_email_notifications'] = $email_notifications;
|
542 |
+
$this->conf->options['send_authors_email_notifications'] = $send_authors_email_notifications;
|
543 |
+
$this->conf->options['notification_email_address'] = strval( $_POST['notification_email_address'] );
|
544 |
|
545 |
+
if ( ! filter_var( $this->conf->options['notification_email_address'], FILTER_VALIDATE_EMAIL ) ) {
|
546 |
+
$this->conf->options['notification_email_address'] = '';
|
|
|
547 |
}
|
|
|
548 |
|
549 |
+
$widget_cap = strval( $_POST['dashboard_widget_capability'] );
|
550 |
+
if ( ! empty( $widget_cap ) ) {
|
551 |
+
$this->conf->options['dashboard_widget_capability'] = $widget_cap;
|
552 |
+
}
|
553 |
|
554 |
+
//Link actions. The user can hide some of them to reduce UI clutter.
|
555 |
+
$show_link_actions = array();
|
556 |
+
foreach ( array_keys( $available_link_actions ) as $action ) {
|
557 |
+
$show_link_actions[ $action ] = isset( $_POST['show_link_actions'] ) && ! empty( $_POST['show_link_actions'][ $action ] );
|
558 |
+
}
|
559 |
+
$this->conf->set( 'show_link_actions', $show_link_actions );
|
560 |
|
561 |
+
//Logging. The plugin can log various events and results for debugging purposes.
|
562 |
+
$this->conf->options['logging_enabled'] = ! empty( $_POST['logging_enabled'] );
|
563 |
+
$this->conf->options['custom_log_file_enabled'] = ! empty( $_POST['custom_log_file_enabled'] );
|
564 |
+
|
565 |
+
if ( $this->conf->options['logging_enabled'] ) {
|
566 |
+
if ( $this->conf->options['custom_log_file_enabled'] ) {
|
567 |
+
$log_file = strval( $cleanPost['log_file'] );
|
568 |
+
} else {
|
569 |
+
//Default log file is /wp-content/uploads/broken-link-checker/blc-log.txt
|
570 |
+
$log_directory = self::get_default_log_directory();
|
571 |
+
$log_file = $log_directory . '/' . self::get_default_log_basename();
|
572 |
+
|
573 |
+
//Attempt to create the log directory.
|
574 |
+
if ( ! is_dir( $log_directory ) ) {
|
575 |
+
if ( mkdir( $log_directory, 0750 ) ) {
|
576 |
+
//Add a .htaccess to hide the log file from site visitors.
|
577 |
+
file_put_contents( $log_directory . '/.htaccess', 'Deny from all' );
|
578 |
+
}
|
579 |
+
}
|
580 |
+
}
|
581 |
+
|
582 |
+
$this->conf->options['log_file'] = $log_file;
|
583 |
+
|
584 |
+
//Attempt to create the log file if not already there.
|
585 |
+
if ( ! is_file( $log_file ) ) {
|
586 |
+
file_put_contents( $log_file, '' );
|
587 |
+
}
|
588 |
+
|
589 |
+
//The log file must be writable.
|
590 |
+
if ( ! is_writable( $log_file ) || ! is_file( $log_file ) ) {
|
591 |
+
$this->conf->options['logging_enabled'] = false;
|
592 |
+
}
|
593 |
}
|
|
|
594 |
|
595 |
+
//Make settings that affect our Cron events take effect immediately
|
596 |
+
$this->setup_cron_events();
|
597 |
+
$this->conf->save_options();
|
598 |
+
|
599 |
+
/*
|
600 |
+
If the list of custom fields was modified then we MUST resynchronize or
|
601 |
+
custom fields linked with existing posts may not be detected. This is somewhat
|
602 |
+
inefficient.
|
603 |
+
*/
|
604 |
+
if ( ( count( $diff1 ) > 0 ) || ( count( $diff2 ) > 0 ) ) {
|
605 |
+
$manager = blcContainerHelper::get_manager( 'custom_field' );
|
606 |
+
if ( ! is_null( $manager ) ) {
|
607 |
+
$manager->resynch();
|
608 |
+
blc_got_unsynched_items();
|
609 |
+
}
|
610 |
+
}
|
611 |
+
|
612 |
+
/*
|
613 |
+
If the list of acf fields was modified then we MUST resynchronize or
|
614 |
+
acf fields linked with existing posts may not be detected. This is somewhat
|
615 |
+
inefficient.
|
616 |
+
*/
|
617 |
+
if ( ( count( $acf_fields_diff1 ) > 0 ) || ( count( $acf_fields_diff2 ) > 0 ) ) {
|
618 |
+
$manager = blcContainerHelper::get_manager( 'acf_field' );
|
619 |
+
if ( ! is_null( $manager ) ) {
|
620 |
+
$manager->resynch();
|
621 |
+
blc_got_unsynched_items();
|
622 |
+
}
|
623 |
+
}
|
624 |
+
|
625 |
+
//Resynchronize posts when the user enables or disables post statuses.
|
626 |
+
if ( $post_statuses_changed ) {
|
627 |
+
$overlord = blcPostTypeOverlord::getInstance();
|
628 |
+
$overlord->enabled_post_statuses = $this->conf->get( 'enabled_post_statuses', array() );
|
629 |
+
$overlord->resynch( 'wsh_status_resynch_trigger' );
|
630 |
+
|
631 |
blc_got_unsynched_items();
|
632 |
+
blc_cleanup_instances();
|
633 |
+
blc_cleanup_links();
|
634 |
}
|
635 |
+
|
636 |
+
//Redirect back to the settings page
|
637 |
+
$base_url = remove_query_arg( array( '_wpnonce', 'noheader', 'updated', 'error', 'action', 'message' ) );
|
638 |
+
wp_redirect(
|
639 |
+
add_query_arg(
|
640 |
+
array(
|
641 |
+
'settings-updated' => true,
|
642 |
+
),
|
643 |
+
$base_url
|
644 |
+
)
|
645 |
+
);
|
646 |
}
|
647 |
|
648 |
+
//Show a confirmation message when settings are saved.
|
649 |
+
if ( ! empty( $_GET['settings-updated'] ) ) {
|
650 |
+
echo '<div id="message" class="updated fade"><p><strong>',__( 'Settings saved.', 'broken-link-checker' ), '</strong></p></div>';
|
|
|
|
|
651 |
|
|
|
|
|
|
|
652 |
}
|
653 |
|
654 |
+
//Show a thank-you message when a donation is made.
|
655 |
+
if ( ! empty( $_GET['donated'] ) ) {
|
656 |
+
echo '<div id="message" class="updated fade"><p><strong>',__( 'Thank you for your donation!', 'broken-link-checker' ), '</strong></p></div>';
|
657 |
+
$this->conf->set( 'user_has_donated', true );
|
658 |
+
$this->conf->save_options();
|
659 |
+
}
|
660 |
+
|
661 |
+
//Show one when recheck is started, too.
|
662 |
+
if ( ! empty( $_GET['recheck-initiated'] ) ) {
|
663 |
+
echo '<div id="message" class="updated fade"><p><strong>',
|
664 |
+
__( 'Complete site recheck started.', 'broken-link-checker' ), // -- Yoda
|
665 |
+
'</strong></p></div>';
|
666 |
+
}
|
667 |
+
|
668 |
+
//Cull invalid and missing modules
|
669 |
+
$moduleManager->validate_active_modules();
|
670 |
+
|
671 |
+
$debug = $this->get_debug_info();
|
672 |
+
|
673 |
+
add_filter( 'blc-module-settings-custom_field', array( $this, 'make_custom_field_input' ), 10, 2 );
|
674 |
+
add_filter( 'blc-module-settings-acf_field', array( $this, 'make_acf_field_input' ), 10, 2 );
|
675 |
+
|
676 |
+
//Translate and markup-ify module headers for display
|
677 |
+
$modules = $moduleManager->get_modules_by_category( '', true, true );
|
678 |
+
|
679 |
+
//Output the custom broken link/removed link styles for example links
|
680 |
+
printf(
|
681 |
+
'<style type="text/css">%s %s</style>',
|
682 |
+
$this->conf->options['broken_link_css'],
|
683 |
+
$this->conf->options['removed_link_css']
|
684 |
+
);
|
685 |
+
|
686 |
+
$section_names = array(
|
687 |
+
'general' => __( 'General', 'broken-link-checker' ),
|
688 |
+
'where' => __( 'Look For Links In', 'broken-link-checker' ),
|
689 |
+
'which' => __( 'Which Links To Check', 'broken-link-checker' ),
|
690 |
+
'how' => __( 'Protocols & APIs', 'broken-link-checker' ),
|
691 |
+
'advanced' => __( 'Advanced', 'broken-link-checker' ),
|
692 |
+
);
|
693 |
+
?>
|
694 |
+
|
695 |
+
<!--[if lte IE 7]>
|
696 |
+
<style type="text/css">
|
697 |
+
/* Simulate inline-block in IE7 */
|
698 |
+
ul.ui-tabs-nav li {
|
699 |
+
display: inline;
|
700 |
+
zoom: 1;
|
701 |
+
}
|
702 |
+
</style>
|
703 |
+
<![endif]-->
|
704 |
+
<div class="wrap" id="blc-settings-wrap">
|
705 |
+
<h2><?php _e( 'Broken Link Checker Options', 'broken-link-checker' ); ?></h2>
|
706 |
+
|
707 |
+
|
708 |
+
<div id="blc-sidebar">
|
709 |
+
<div class="metabox-holder">
|
710 |
+
<?php include BLC_DIRECTORY . '/includes/admin/sidebar.php'; ?>
|
711 |
+
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
712 |
</div>
|
|
|
713 |
|
714 |
|
715 |
+
<div id="blc-admin-content">
|
716 |
|
717 |
+
<form name="link_checker_options" id="link_checker_options" method="post" action="
|
718 |
+
<?php
|
719 |
+
echo admin_url( 'options-general.php?page=link-checker-settings&noheader=1' );
|
720 |
+
?>
|
721 |
+
">
|
722 |
+
<?php
|
723 |
+
wp_nonce_field( 'link-checker-options' );
|
724 |
+
?>
|
725 |
|
726 |
+
<div id="blc-tabs">
|
727 |
|
728 |
+
<ul class="hide-if-no-js">
|
729 |
+
<?php
|
730 |
+
foreach ( $section_names as $section_id => $section_name ) {
|
731 |
printf(
|
732 |
'<li id="tab-button-%s"><a href="#section-%s" title="%s">%s</a></li>',
|
733 |
+
esc_attr( $section_id ),
|
734 |
+
esc_attr( $section_id ),
|
735 |
+
esc_attr( $section_name ),
|
736 |
$section_name
|
737 |
);
|
738 |
}
|
739 |
+
?>
|
740 |
+
</ul>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
741 |
|
742 |
+
<div id="section-general" class="blc-section">
|
743 |
+
<h3 class="hide-if-js"><?php echo $section_names['general']; ?></h3>
|
744 |
|
745 |
+
<table class="form-table">
|
|
|
|
|
746 |
|
747 |
+
<tr valign="top">
|
748 |
+
<th scope="row">
|
749 |
+
<?php _e( 'Status', 'broken-link-checker' ); ?>
|
750 |
+
<br>
|
751 |
+
<a href="javascript:void(0)" id="blc-debug-info-toggle"><?php _e( 'Show debug info', 'broken-link-checker' ); ?></a>
|
752 |
+
</th>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
753 |
<td>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
754 |
|
755 |
+
<div id='wsblc_full_status'>
|
756 |
+
<br/><br/><br/>
|
757 |
+
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
758 |
|
759 |
+
<table id="blc-debug-info">
|
760 |
+
<?php
|
761 |
+
|
762 |
+
//Output the debug info in a table
|
763 |
+
foreach ( $debug as $key => $value ) {
|
|
|
|
|
|
|
|
|
|
|
|
|
764 |
printf(
|
765 |
+
'<tr valign="top" class="blc-debug-item-%s"><th scope="row">%s</th><td>%s<div class="blc-debug-message">%s</div></td></tr>',
|
766 |
+
$value['state'],
|
767 |
+
$key,
|
768 |
+
$value['value'],
|
769 |
+
( array_key_exists( 'message', $value ) ? $value['message'] : '' )
|
770 |
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
771 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
772 |
?>
|
773 |
+
</table>
|
774 |
|
775 |
+
</td>
|
776 |
+
</tr>
|
777 |
|
778 |
+
<tr valign="top">
|
779 |
+
<th scope="row"><?php _e( 'Check each link', 'broken-link-checker' ); ?></th>
|
780 |
+
<td>
|
|
|
|
|
|
|
|
|
781 |
|
|
|
782 |
<?php
|
783 |
+
printf(
|
784 |
+
__( 'Every %s hours', 'broken-link-checker' ),
|
785 |
+
sprintf(
|
786 |
+
'<input type="text" name="check_threshold" id="check_threshold" value="%d" size="5" maxlength="5" />',
|
787 |
+
$this->conf->options['check_threshold']
|
788 |
+
)
|
789 |
);
|
790 |
?>
|
791 |
+
<br/>
|
792 |
+
<span class="description">
|
793 |
+
<?php _e( 'Existing links will be checked this often. New links will usually be checked ASAP.', 'broken-link-checker' ); ?>
|
794 |
+
</span>
|
795 |
+
|
796 |
+
</td>
|
797 |
+
</tr>
|
798 |
+
|
799 |
+
<tr valign="top">
|
800 |
+
<th scope="row"><?php _e( 'E-mail notifications', 'broken-link-checker' ); ?></th>
|
801 |
+
<td>
|
802 |
+
<p style="margin-top: 0;">
|
803 |
+
<label for='send_email_notifications'>
|
804 |
+
<input type="checkbox" name="send_email_notifications" id="send_email_notifications"
|
805 |
+
<?php
|
806 |
+
if ( $this->conf->options['send_email_notifications'] ) {
|
807 |
+
echo ' checked="checked"';}
|
808 |
+
?>
|
809 |
+
/>
|
810 |
+
<?php _e( 'Send me e-mail notifications about newly detected broken links', 'broken-link-checker' ); ?>
|
811 |
+
</label><br />
|
812 |
+
</p>
|
813 |
+
|
814 |
+
<p>
|
815 |
+
<label for='send_authors_email_notifications'>
|
816 |
+
<input type="checkbox" name="send_authors_email_notifications" id="send_authors_email_notifications"
|
817 |
+
<?php
|
818 |
+
if ( $this->conf->options['send_authors_email_notifications'] ) {
|
819 |
+
echo ' checked="checked"';}
|
820 |
+
?>
|
821 |
+
/>
|
822 |
+
<?php _e( 'Send authors e-mail notifications about broken links in their posts', 'broken-link-checker' ); ?>
|
823 |
+
</label><br />
|
824 |
+
</p>
|
825 |
+
</td>
|
826 |
+
</tr>
|
827 |
|
828 |
<tr valign="top">
|
829 |
+
<th scope="row"><?php echo __( 'Notification e-mail address', 'broken-link-checker' ); ?></th>
|
830 |
<td>
|
831 |
+
<p>
|
832 |
<label>
|
833 |
+
<input
|
834 |
+
type="text"
|
835 |
+
name="notification_email_address"
|
836 |
+
id="notification_email_address"
|
837 |
+
value="<?php echo esc_attr( $this->conf->get( 'notification_email_address', '' ) ); ?>"
|
838 |
+
class="regular-text ltr">
|
839 |
+
</label><br>
|
840 |
+
<span class="description">
|
841 |
+
<?php echo __( 'Leave empty to use the e-mail address specified in Settings → General.', 'broken-link-checker' ); ?>
|
842 |
+
</span>
|
843 |
+
</p>
|
844 |
</td>
|
845 |
</tr>
|
846 |
|
847 |
<tr valign="top">
|
848 |
+
<th scope="row"><?php _e( 'Link tweaks', 'broken-link-checker' ); ?></th>
|
849 |
+
<td>
|
850 |
+
<p style="margin-top: 0; margin-bottom: 0.5em;">
|
851 |
+
<label for='mark_broken_links'>
|
852 |
+
<input type="checkbox" name="mark_broken_links" id="mark_broken_links"
|
853 |
+
<?php
|
854 |
+
if ( $this->conf->options['mark_broken_links'] ) {
|
855 |
+
echo ' checked="checked"';}
|
856 |
+
?>
|
857 |
+
/>
|
858 |
+
<?php _e( 'Apply custom formatting to broken links', 'broken-link-checker' ); ?>
|
859 |
+
</label>
|
860 |
+
|
|
861 |
+
<a id="toggle-broken-link-css-editor" href="#" class="blc-toggle-link">
|
862 |
+
<?php
|
863 |
+
_e( 'Edit CSS', 'broken-link-checker' );
|
864 |
+
?>
|
865 |
+
</a>
|
866 |
+
</p>
|
867 |
+
|
868 |
+
<div id="broken-link-css-wrap"
|
869 |
+
<?php
|
870 |
+
if ( ! blcUtility::get_cookie( 'broken-link-css-wrap', false ) ) {
|
871 |
+
echo ' class="hidden"';
|
872 |
+
}
|
873 |
+
?>
|
874 |
+
>
|
875 |
+
<textarea name="broken_link_css" id="broken_link_css" cols='45' rows='4'>
|
876 |
+
<?php
|
877 |
+
if ( isset( $this->conf->options['broken_link_css'] ) ) {
|
878 |
+
echo $this->conf->options['broken_link_css'];
|
879 |
+
}
|
880 |
+
?>
|
881 |
+
</textarea>
|
882 |
+
<p class="description">
|
883 |
+
<?php
|
884 |
+
printf(
|
885 |
+
__( 'Example : Lorem ipsum <a %s>broken link</a>, dolor sit amet.', 'broken-link-checker' ),
|
886 |
+
' href="#" class="broken_link" onclick="return false;"'
|
887 |
+
);
|
888 |
+
echo ' ', __( 'Click "Save Changes" to update example output.', 'broken-link-checker' );
|
889 |
+
?>
|
890 |
+
</p>
|
891 |
+
</div>
|
892 |
+
|
893 |
+
<p style="margin-bottom: 0.5em;">
|
894 |
+
<label for='mark_removed_links'>
|
895 |
+
<input type="checkbox" name="mark_removed_links" id="mark_removed_links"
|
896 |
+
<?php
|
897 |
+
if ( $this->conf->options['mark_removed_links'] ) {
|
898 |
+
echo ' checked="checked"';}
|
899 |
+
?>
|
900 |
+
/>
|
901 |
+
<?php _e( 'Apply custom formatting to removed links', 'broken-link-checker' ); ?>
|
902 |
+
</label>
|
903 |
+
|
|
904 |
+
<a id="toggle-removed-link-css-editor" href="#" class="blc-toggle-link">
|
905 |
+
<?php
|
906 |
+
_e( 'Edit CSS', 'broken-link-checker' );
|
907 |
+
?>
|
908 |
+
</a>
|
909 |
+
</p>
|
910 |
+
|
911 |
+
<div id="removed-link-css-wrap"
|
912 |
+
<?php
|
913 |
+
if ( ! blcUtility::get_cookie( 'removed-link-css-wrap', false ) ) {
|
914 |
+
echo ' class="hidden"';
|
915 |
+
}
|
916 |
+
?>
|
917 |
+
>
|
918 |
+
<textarea name="removed_link_css" id="removed_link_css" cols='45' rows='4'>
|
919 |
+
<?php
|
920 |
+
if ( isset( $this->conf->options['removed_link_css'] ) ) {
|
921 |
+
echo $this->conf->options['removed_link_css'];
|
922 |
+
}
|
923 |
+
?>
|
924 |
+
</textarea>
|
925 |
+
|
926 |
+
<p class="description">
|
927 |
+
<?php
|
928 |
+
printf(
|
929 |
+
__( 'Example : Lorem ipsum <span %s>removed link</span>, dolor sit amet.', 'broken-link-checker' ),
|
930 |
+
' class="removed_link"'
|
931 |
+
);
|
932 |
+
echo ' ', __( 'Click "Save Changes" to update example output.', 'broken-link-checker' );
|
933 |
+
?>
|
934 |
+
|
935 |
+
</p>
|
936 |
+
</div>
|
937 |
+
|
938 |
+
<p>
|
939 |
+
<label for='nofollow_broken_links'>
|
940 |
+
<input type="checkbox" name="nofollow_broken_links" id="nofollow_broken_links"
|
941 |
+
<?php
|
942 |
+
if ( $this->conf->options['nofollow_broken_links'] ) {
|
943 |
+
echo ' checked="checked"';}
|
944 |
+
?>
|
945 |
+
/>
|
946 |
+
<?php _e( 'Stop search engines from following broken links', 'broken-link-checker' ); ?>
|
947 |
+
</label>
|
948 |
+
</p>
|
949 |
+
|
950 |
+
<p class="description">
|
951 |
+
<?php
|
952 |
+
echo _x(
|
953 |
+
'These settings only apply to the content of posts, not comments or custom fields.',
|
954 |
+
'"Link tweaks" settings',
|
955 |
+
'broken-link-checker'
|
956 |
+
);
|
957 |
+
?>
|
958 |
+
</p>
|
959 |
+
</td>
|
960 |
</tr>
|
961 |
|
962 |
+
<tr valign="top">
|
963 |
+
<th scope="row"><?php echo _x( 'Suggestions', 'settings page', 'broken-link-checker' ); ?></th>
|
964 |
+
<td>
|
965 |
+
<label>
|
966 |
+
<input type="checkbox" name="suggestions_enabled" id="suggestions_enabled"
|
967 |
+
<?php checked( $this->conf->options['suggestions_enabled'] ); ?>/>
|
968 |
+
<?php _e( 'Suggest alternatives to broken links', 'broken-link-checker' ); ?>
|
969 |
+
</label>
|
970 |
+
</td>
|
971 |
+
</tr>
|
972 |
+
|
973 |
+
<tr valign="top">
|
974 |
+
<th scope="row"><?php echo _x( 'Warnings', 'settings page', 'broken-link-checker' ); ?></th>
|
975 |
+
<td id="blc_warning_settings">
|
976 |
+
<label>
|
977 |
+
<input type="checkbox" name="warnings_enabled" id="warnings_enabled"
|
978 |
+
<?php checked( $this->conf->options['warnings_enabled'] ); ?>/>
|
979 |
+
<?php _e( 'Show uncertain or minor problems as "warnings" instead of "broken"', 'broken-link-checker' ); ?>
|
980 |
+
</label>
|
981 |
+
<p class="description">
|
982 |
+
<?php
|
983 |
+
_e( 'Turning off this option will make the plugin report all problems as broken links.', 'broken-link-checker' );
|
984 |
+
?>
|
985 |
+
</p>
|
986 |
+
</td>
|
987 |
+
</tr>
|
988 |
+
|
989 |
+
</table>
|
990 |
|
991 |
+
</div>
|
992 |
|
993 |
+
<div id="section-where" class="blc-section">
|
994 |
+
<h3 class="hide-if-js"><?php echo $section_names['where']; ?></h3>
|
995 |
|
996 |
+
<table class="form-table">
|
997 |
|
998 |
+
<tr valign="top">
|
999 |
+
<th scope="row"><?php _e( 'Look for links in', 'broken-link-checker' ); ?></th>
|
1000 |
+
<td>
|
1001 |
+
<?php
|
1002 |
+
if ( ! empty( $modules['container'] ) ) {
|
1003 |
+
uasort(
|
1004 |
+
$modules['container'],
|
1005 |
+
function( $a, $b ) {
|
1006 |
+
return strcasecmp( $a['Name'], $b['Name'] );
|
1007 |
+
}
|
1008 |
+
);
|
1009 |
+
$this->print_module_list( $modules['container'], $this->conf->options );
|
1010 |
+
}
|
1011 |
+
?>
|
1012 |
+
</td></tr>
|
1013 |
+
|
1014 |
+
<tr valign="top">
|
1015 |
+
<th scope="row"><?php _e( 'Post statuses', 'broken-link-checker' ); ?></th>
|
1016 |
+
<td>
|
1017 |
+
<?php
|
1018 |
+
$available_statuses = get_post_stati( array( 'internal' => false ), 'objects' );
|
1019 |
+
|
1020 |
+
if ( isset( $this->conf->options['enabled_post_statuses'] ) ) {
|
1021 |
+
$enabled_post_statuses = $this->conf->options['enabled_post_statuses'];
|
1022 |
+
} else {
|
1023 |
+
$enabled_post_statuses = array();
|
1024 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1025 |
|
1026 |
+
foreach ( $available_statuses as $status => $status_object ) {
|
1027 |
+
printf(
|
1028 |
+
'<p><label><input type="checkbox" name="enabled_post_statuses[]" value="%s"%s> %s</label></p>',
|
1029 |
+
esc_attr( $status ),
|
1030 |
+
in_array( $status, $enabled_post_statuses ) ? ' checked="checked"' : '',
|
1031 |
+
$status_object->label
|
1032 |
+
);
|
1033 |
+
}
|
1034 |
+
?>
|
1035 |
+
</td></tr>
|
1036 |
+
|
1037 |
+
</table>
|
1038 |
|
1039 |
+
</div>
|
1040 |
|
1041 |
|
1042 |
+
<div id="section-which" class="blc-section">
|
1043 |
<h3 class="hide-if-js"><?php echo $section_names['which']; ?></h3>
|
1044 |
|
1045 |
+
<table class="form-table">
|
1046 |
+
|
1047 |
+
<tr valign="top">
|
1048 |
+
<th scope="row"><?php _e( 'Link types', 'broken-link-checker' ); ?></th>
|
1049 |
+
<td>
|
1050 |
+
<?php
|
1051 |
+
if ( ! empty( $modules['parser'] ) ) {
|
1052 |
+
$this->print_module_list( $modules['parser'], $this->conf->options );
|
1053 |
+
} else {
|
1054 |
+
echo __( 'Error : All link parsers missing!', 'broken-link-checker' );
|
1055 |
+
}
|
1056 |
+
?>
|
1057 |
+
</td>
|
1058 |
</tr>
|
1059 |
|
1060 |
+
<tr valign="top">
|
1061 |
+
<th scope="row"><?php _e( 'Exclusion list', 'broken-link-checker' ); ?></th>
|
1062 |
+
<td><?php _e( "Don't check links where the URL contains any of these words (one per line) :", 'broken-link-checker' ); ?><br/>
|
1063 |
+
<textarea name="exclusion_list" id="exclusion_list" cols='45' rows='4'>
|
1064 |
+
<?php
|
1065 |
+
if ( isset( $this->conf->options['exclusion_list'] ) ) {
|
1066 |
+
echo esc_textarea( implode( "\n", $this->conf->options['exclusion_list'] ) );
|
1067 |
+
}
|
1068 |
+
?>
|
1069 |
+
</textarea>
|
1070 |
|
1071 |
+
</td>
|
1072 |
+
</tr>
|
1073 |
|
1074 |
+
</table>
|
1075 |
+
</div>
|
1076 |
|
1077 |
+
<div id="section-how" class="blc-section">
|
1078 |
<h3 class="hide-if-js"><?php echo $section_names['how']; ?></h3>
|
1079 |
|
1080 |
+
<table class="form-table">
|
1081 |
|
1082 |
+
<tr valign="top">
|
1083 |
+
<th scope="row"><?php _e( 'Check links using', 'broken-link-checker' ); ?></th>
|
1084 |
+
<td>
|
1085 |
+
<?php
|
1086 |
+
if ( ! empty( $modules['checker'] ) ) {
|
1087 |
+
$modules['checker'] = array_reverse( $modules['checker'] );
|
1088 |
+
$this->print_module_list( $modules['checker'], $this->conf->options );
|
1089 |
+
}
|
1090 |
+
?>
|
1091 |
+
</td></tr>
|
1092 |
|
1093 |
+
</table>
|
1094 |
+
</div>
|
1095 |
|
1096 |
+
<div id="section-advanced" class="blc-section">
|
1097 |
<h3 class="hide-if-js"><?php echo $section_names['advanced']; ?></h3>
|
1098 |
|
1099 |
+
<table class="form-table">
|
1100 |
|
1101 |
+
<tr valign="top">
|
1102 |
+
<th scope="row"><?php _e( 'Timeout', 'broken-link-checker' ); ?></th>
|
1103 |
+
<td>
|
1104 |
|
1105 |
+
<?php
|
1106 |
|
1107 |
+
printf(
|
1108 |
+
__( '%s seconds', 'broken-link-checker' ),
|
1109 |
+
sprintf(
|
1110 |
+
'<input type="text" name="timeout" id="blc_timeout" value="%d" size="5" maxlength="3" />',
|
1111 |
+
$this->conf->options['timeout']
|
1112 |
+
)
|
1113 |
+
);
|
1114 |
|
1115 |
+
?>
|
1116 |
+
<br/><span class="description">
|
1117 |
+
<?php _e( 'Links that take longer than this to load will be marked as broken.', 'broken-link-checker' ); ?>
|
1118 |
</span>
|
1119 |
|
1120 |
+
</td>
|
1121 |
+
</tr>
|
1122 |
|
1123 |
+
<tr valign="top">
|
1124 |
+
<th scope="row"><?php _e( 'Link monitor', 'broken-link-checker' ); ?></th>
|
1125 |
+
<td>
|
1126 |
|
1127 |
+
<p>
|
1128 |
<label for='run_in_dashboard'>
|
1129 |
|
1130 |
+
<input type="checkbox" name="run_in_dashboard" id="run_in_dashboard"
|
1131 |
+
<?php
|
1132 |
+
if ( $this->conf->options['run_in_dashboard'] ) {
|
1133 |
+
echo ' checked="checked"';}
|
1134 |
+
?>
|
1135 |
+
/>
|
1136 |
+
<?php _e( 'Run continuously while the Dashboard is open', 'broken-link-checker' ); ?>
|
1137 |
</label>
|
1138 |
</p>
|
1139 |
|
1140 |
<p>
|
1141 |
<label for='run_via_cron'>
|
1142 |
+
<input type="checkbox" name="run_via_cron" id="run_via_cron"
|
1143 |
+
<?php
|
1144 |
+
if ( $this->conf->options['run_via_cron'] ) {
|
1145 |
+
echo ' checked="checked"';}
|
1146 |
+
?>
|
1147 |
+
/>
|
1148 |
+
<?php _e( 'Run hourly in the background', 'broken-link-checker' ); ?>
|
1149 |
</label>
|
1150 |
</p>
|
1151 |
|
1152 |
+
</td>
|
1153 |
+
</tr>
|
1154 |
|
1155 |
+
<tr valign="top">
|
1156 |
+
<th scope="row"><?php _e( 'Show the dashboard widget for', 'broken-link-checker' ); ?></th>
|
1157 |
+
<td>
|
1158 |
|
1159 |
+
<?php
|
1160 |
$widget_caps = array(
|
1161 |
+
_x( 'Administrator', 'dashboard widget visibility', 'broken-link-checker' ) => 'manage_options',
|
1162 |
+
_x( 'Editor and above', 'dashboard widget visibility', 'broken-link-checker' ) => 'edit_others_posts',
|
1163 |
+
_x( 'Nobody (disables the widget)', 'dashboard widget visibility', 'broken-link-checker' ) => 'do_not_allow',
|
1164 |
);
|
1165 |
|
1166 |
+
foreach ( $widget_caps as $title => $capability ) {
|
1167 |
+
printf(
|
1168 |
+
'<p><label><input type="radio" name="dashboard_widget_capability" value="%s"%s> %s</label></p>',
|
1169 |
+
esc_attr( $capability ),
|
1170 |
+
checked( $capability, $this->conf->get( 'dashboard_widget_capability' ), false ),
|
1171 |
+
$title
|
1172 |
+
);
|
1173 |
+
}
|
1174 |
+
?>
|
1175 |
+
</td>
|
1176 |
+
</tr>
|
1177 |
|
1178 |
<tr valign="top">
|
1179 |
+
<th scope="row"><?php echo _x( 'Show link actions', 'settings page', 'broken-link-checker' ); ?></th>
|
1180 |
<td>
|
1181 |
<?php
|
1182 |
+
$show_link_actions = $this->conf->get( 'show_link_actions', array() );
|
1183 |
+
foreach ( $available_link_actions as $action => $text ) {
|
1184 |
+
$enabled = isset( $show_link_actions[ $action ] ) ? (bool) ( $show_link_actions[ $action ] ) : true;
|
1185 |
printf(
|
1186 |
'<p><label><input type="checkbox" name="show_link_actions[%1$s]" %3$s> %2$s</label></p>',
|
1187 |
$action,
|
1188 |
$text,
|
1189 |
+
checked( $enabled, true, false )
|
1190 |
);
|
1191 |
}
|
1192 |
?>
|
1193 |
</td>
|
1194 |
</tr>
|
1195 |
|
1196 |
+
<tr valign="top">
|
1197 |
+
<th scope="row"><?php _e( 'Max. execution time', 'broken-link-checker' ); ?></th>
|
1198 |
+
<td>
|
1199 |
|
1200 |
+
<?php
|
1201 |
|
1202 |
+
printf(
|
1203 |
+
__( '%s seconds', 'broken-link-checker' ),
|
1204 |
+
sprintf(
|
1205 |
+
'<input type="text" name="max_execution_time" id="max_execution_time" value="%d" size="5" maxlength="5" />',
|
1206 |
+
$this->conf->options['max_execution_time']
|
1207 |
+
)
|
1208 |
+
);
|
1209 |
|
1210 |
+
?>
|
1211 |
+
<br/><span class="description">
|
1212 |
+
<?php
|
1213 |
|
1214 |
+
_e( 'The plugin works by periodically launching a background job that parses your posts for links, checks the discovered URLs, and performs other time-consuming tasks. Here you can set for how long, at most, the link monitor may run each time before stopping.', 'broken-link-checker' );
|
1215 |
|
1216 |
+
?>
|
1217 |
</span>
|
1218 |
|
1219 |
+
</td>
|
1220 |
+
</tr>
|
1221 |
|
1222 |
+
<tr valign="top">
|
1223 |
+
<th scope="row"><?php _e( 'Server load limit', 'broken-link-checker' ); ?></th>
|
1224 |
+
<td>
|
1225 |
+
<?php
|
1226 |
|
1227 |
+
$load = blcUtility::get_server_load();
|
1228 |
+
$available = ! empty( $load );
|
1229 |
|
1230 |
+
if ( $available ) {
|
1231 |
+
$value = ! empty( $this->conf->options['server_load_limit'] ) ? sprintf( '%.2f', $this->conf->options['server_load_limit'] ) : '';
|
1232 |
+
printf(
|
1233 |
+
'<input type="text" name="server_load_limit" id="server_load_limit" value="%s" size="5" maxlength="5"/> ',
|
1234 |
+
$value
|
1235 |
+
);
|
1236 |
|
1237 |
+
printf(
|
1238 |
+
__( 'Current load : %s', 'broken-link-checker' ),
|
1239 |
+
'<span id="wsblc_current_load">...</span>'
|
1240 |
+
);
|
1241 |
+
echo '<br/><span class="description">';
|
1242 |
+
printf(
|
1243 |
+
__(
|
1244 |
+
'Link checking will be suspended if the average <a href="%s">server load</a> rises above this number. Leave this field blank to disable load limiting.',
|
1245 |
+
'broken-link-checker'
|
1246 |
+
),
|
1247 |
+
'http://en.wikipedia.org/wiki/Load_(computing)'
|
1248 |
+
);
|
1249 |
+
echo '</span>';
|
1250 |
|
1251 |
+
} else {
|
1252 |
+
echo '<input type="text" disabled="disabled" value="', esc_attr( __( 'Not available', 'broken-link-checker' ) ), '" size="13"/><br>';
|
1253 |
+
echo '<span class="description">';
|
1254 |
+
_e( 'Load limiting only works on Linux-like systems where <code>/proc/loadavg</code> is present and accessible.', 'broken-link-checker' );
|
1255 |
+
echo '</span>';
|
1256 |
+
}
|
1257 |
+
?>
|
1258 |
+
</td>
|
1259 |
+
</tr>
|
1260 |
|
1261 |
<tr valign="top">
|
1262 |
+
<th scope="row"><?php _e( 'Target resource usage', 'broken-link-checker' ); ?></th>
|
1263 |
<td>
|
1264 |
<?php
|
1265 |
+
$target_resource_usage = $this->conf->get( 'target_resource_usage', 0.25 );
|
1266 |
printf(
|
1267 |
'<input name="target_resource_usage" value="%d"
|
1268 |
type="range" min="1" max="100" id="target_resource_usage">',
|
1270 |
);
|
1271 |
?>
|
1272 |
|
1273 |
+
<span id="target_resource_usage_percent">
|
1274 |
+
<?php
|
1275 |
+
echo sprintf( '%.0f%%', $target_resource_usage * 100 );
|
1276 |
+
?>
|
1277 |
+
</span>
|
1278 |
</td>
|
1279 |
</tr>
|
1280 |
|
1281 |
<tr valign="top">
|
1282 |
+
<th scope="row"><?php _e( 'Logging', 'broken-link-checker' ); ?></th>
|
1283 |
<td>
|
1284 |
<p>
|
1285 |
<label for='logging_enabled'>
|
1286 |
<input type="checkbox" name="logging_enabled" id="logging_enabled"
|
1287 |
+
<?php checked( $this->conf->options['logging_enabled'] ); ?>/>
|
1288 |
+
<?php _e( 'Enable logging', 'broken-link-checker' ); ?>
|
1289 |
</label>
|
1290 |
</p>
|
1291 |
</td>
|
1292 |
</tr>
|
1293 |
|
1294 |
<tr valign="top">
|
1295 |
+
<th scope="row"><?php _e( 'Log file location', 'broken-link-checker' ); ?></th>
|
1296 |
<td>
|
1297 |
|
1298 |
<div id="blc-logging-options">
|
1300 |
<p>
|
1301 |
<label>
|
1302 |
<input type="radio" name="custom_log_file_enabled" value=""
|
1303 |
+
<?php checked( ! $this->conf->options['custom_log_file_enabled'] ); ?>>
|
1304 |
+
<?php echo _x( 'Default', 'log file location', 'broken-link-checker' ); ?>
|
1305 |
</label>
|
1306 |
<br>
|
1307 |
<span class="description">
|
1308 |
+
<code>
|
1309 |
+
<?php
|
1310 |
echo self::get_default_log_directory(), '/', self::get_default_log_basename();
|
1311 |
+
?>
|
1312 |
+
</code>
|
1313 |
</span>
|
1314 |
</p>
|
1315 |
|
1316 |
<p>
|
1317 |
<label>
|
1318 |
<input type="radio" name="custom_log_file_enabled" value="1"
|
1319 |
+
<?php checked( $this->conf->options['custom_log_file_enabled'] ); ?>>
|
1320 |
+
<?php echo _x( 'Custom', 'log file location', 'broken-link-checker' ); ?>
|
1321 |
</label>
|
1322 |
<br><input type="text" name="log_file" id="log_file" size="90"
|
1323 |
+
value="<?php echo esc_attr( $this->conf->options['log_file'] ); ?>">
|
1324 |
</p>
|
1325 |
|
1326 |
</div>
|
1328 |
</tr>
|
1329 |
|
1330 |
|
1331 |
+
<tr valign="top">
|
1332 |
+
<th scope="row"><?php _e( 'Forced recheck', 'broken-link-checker' ); ?></th>
|
1333 |
+
<td>
|
1334 |
+
<input class="button" type="button" name="start-recheck" id="start-recheck"
|
1335 |
+
value="<?php _e( 'Re-check all pages', 'broken-link-checker' ); ?>" />
|
1336 |
+
<input type="hidden" name="recheck" value="" id="recheck" />
|
1337 |
<br />
|
1338 |
+
<span class="description">
|
1339 |
+
<?php
|
1340 |
+
_e( 'The "Nuclear Option". Click this button to make the plugin empty its link database and recheck the entire site from scratch.', 'broken-link-checker' );
|
1341 |
|
1342 |
+
?>
|
1343 |
+
</span>
|
1344 |
</td>
|
1345 |
</tr>
|
1346 |
|
1347 |
+
</table>
|
1348 |
+
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1349 |
|
1350 |
+
</div>
|
1351 |
|
1352 |
+
<p class="submit"><input type="submit" name="submit" class='button-primary' value="<?php _e( 'Save Changes' ); ?>" /></p>
|
1353 |
+
</form>
|
1354 |
|
1355 |
+
</div> <!-- First postbox-container -->
|
1356 |
|
1357 |
|
1358 |
+
</div>
|
|
|
|
|
|
|
1359 |
|
1360 |
+
<?php
|
1361 |
+
//The various JS for this page is stored in a separate file for the purposes readability.
|
1362 |
+
include dirname( $this->loader ) . '/includes/admin/options-page-js.php';
|
1363 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1364 |
|
|
|
|
|
1365 |
|
|
|
1366 |
|
1367 |
+
/**
|
1368 |
+
* Output a list of modules and their settings.
|
1369 |
+
*
|
1370 |
+
* Each list entry will contain a checkbox that is checked if the module is
|
1371 |
+
* currently active.
|
1372 |
+
*
|
1373 |
+
* @param array $modules Array of modules to display
|
1374 |
+
* @param array $current_settings
|
1375 |
+
* @return void
|
1376 |
+
*/
|
1377 |
+
function print_module_list( $modules, $current_settings ) {
|
1378 |
+
$moduleManager = blcModuleManager::getInstance();
|
1379 |
|
1380 |
+
foreach ( $modules as $module_id => $module_data ) {
|
1381 |
+
$module_id = $module_data['ModuleID'];
|
|
|
|
|
|
|
1382 |
|
1383 |
+
$style = $module_data['ModuleHidden'] ? ' style="display:none;"' : '';
|
1384 |
|
1385 |
printf(
|
1386 |
+
'<div class="module-container" id="module-container-%s"%s>',
|
1387 |
+
$module_id,
|
1388 |
+
$style
|
1389 |
);
|
1390 |
+
$this->print_module_checkbox( $module_id, $module_data, $moduleManager->is_active( $module_id ) );
|
1391 |
|
1392 |
+
$extra_settings = apply_filters(
|
1393 |
+
'blc-module-settings-' . $module_id,
|
1394 |
+
'',
|
1395 |
+
$current_settings
|
|
|
1396 |
);
|
1397 |
|
1398 |
+
if ( ! empty( $extra_settings ) ) {
|
1399 |
+
|
1400 |
+
printf(
|
1401 |
+
' | <a class="blc-toggle-link toggle-module-settings" id="toggle-module-settings-%s" href="#">%s</a>',
|
1402 |
+
esc_attr( $module_id ),
|
1403 |
+
__( 'Configure', 'broken-link-checker' )
|
1404 |
+
);
|
1405 |
+
|
1406 |
+
//The plugin remembers the last open/closed state of module configuration boxes
|
1407 |
+
$box_id = 'module-extra-settings-' . $module_id;
|
1408 |
+
$show = blcUtility::get_cookie(
|
1409 |
+
$box_id,
|
1410 |
+
$moduleManager->is_active( $module_id )
|
1411 |
+
);
|
1412 |
+
|
1413 |
+
printf(
|
1414 |
+
'<div class="module-extra-settings%s" id="%s">%s</div>',
|
1415 |
+
$show ? '' : ' hidden',
|
1416 |
+
$box_id,
|
1417 |
+
$extra_settings
|
1418 |
+
);
|
1419 |
+
}
|
1420 |
+
|
1421 |
+
echo '</div>';
|
1422 |
+
}
|
1423 |
+
}
|
1424 |
+
|
1425 |
+
/**
|
1426 |
+
* Output a checkbox for a module.
|
1427 |
+
*
|
1428 |
+
* Generates a simple checkbox that can be used to mark a module as active/inactive.
|
1429 |
+
* If the specified module can't be deactivated (ModuleAlwaysActive = true), the checkbox
|
1430 |
+
* will be displayed in a disabled state and a hidden field will be created to make
|
1431 |
+
* form submissions work correctly.
|
1432 |
+
*
|
1433 |
+
* @param string $module_id Module ID.
|
1434 |
+
* @param array $module_data Associative array of module data.
|
1435 |
+
* @param bool $active If true, the newly created checkbox will start out checked.
|
1436 |
+
* @return void
|
1437 |
+
*/
|
1438 |
+
function print_module_checkbox( $module_id, $module_data, $active = false ) {
|
1439 |
+
$disabled = false;
|
1440 |
+
$name_prefix = 'module';
|
1441 |
+
$label_class = '';
|
1442 |
+
$active = $active || $module_data['ModuleAlwaysActive'];
|
1443 |
+
|
1444 |
+
if ( $module_data['ModuleAlwaysActive'] ) {
|
1445 |
+
$disabled = true;
|
1446 |
+
$name_prefix = 'module-always-active';
|
1447 |
+
}
|
1448 |
+
|
1449 |
+
$checked = $active ? ' checked="checked"' : '';
|
1450 |
+
if ( $disabled ) {
|
1451 |
+
$checked .= ' disabled="disabled"';
|
1452 |
}
|
1453 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1454 |
printf(
|
1455 |
+
'<label class="%s">
|
1456 |
+
<input type="checkbox" name="%s[%s]" id="module-checkbox-%s"%s /> %s
|
1457 |
+
</label>',
|
1458 |
+
esc_attr( $label_class ),
|
1459 |
+
$name_prefix,
|
1460 |
+
esc_attr( $module_id ),
|
1461 |
+
esc_attr( $module_id ),
|
1462 |
+
$checked,
|
1463 |
+
$module_data['Name']
|
1464 |
);
|
1465 |
+
|
1466 |
+
if ( $module_data['ModuleAlwaysActive'] ) {
|
1467 |
+
printf(
|
1468 |
+
'<input type="hidden" name="module[%s]" value="on">',
|
1469 |
+
esc_attr( $module_id )
|
1470 |
+
);
|
1471 |
+
}
|
1472 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1473 |
|
1474 |
+
/**
|
1475 |
+
* Add extra settings to the "Custom fields" entry on the plugin's config. page.
|
1476 |
+
*
|
1477 |
+
* Callback for the 'blc-module-settings-custom_field' filter.
|
1478 |
+
*
|
1479 |
+
* @param string $html Current extra HTML
|
1480 |
+
* @param array $current_settings The current plugin configuration.
|
1481 |
+
* @return string New extra HTML.
|
1482 |
+
*/
|
1483 |
+
function make_custom_field_input( $html, $current_settings ) {
|
1484 |
+
$html .= '<span class="description">' .
|
1485 |
+
__(
|
1486 |
+
'Enter the names of custom fields you want to check (one per line). If a field contains HTML code, prefix its name with <code>html:</code>. For example, <code>html:field_name</code>.',
|
1487 |
+
'broken-link-checker'
|
1488 |
+
) .
|
1489 |
+
'</span>';
|
1490 |
+
$html .= '<br><textarea name="blc_custom_fields" id="blc_custom_fields" cols="45" rows="4">';
|
1491 |
+
if ( isset( $current_settings['custom_fields'] ) ) {
|
1492 |
+
$html .= esc_textarea( implode( "\n", $current_settings['custom_fields'] ) );
|
1493 |
+
}
|
1494 |
+
$html .= '</textarea>';
|
1495 |
+
|
1496 |
+
return $html;
|
1497 |
+
}
|
1498 |
+
function make_acf_field_input( $html, $current_settings ) {
|
1499 |
+
$html .= '<span class="description">' . __( 'Enter the keys of acf fields you want to check (one per line). If a field contains HTML code, prefix its name with <code>html:</code>. For example, <code>html:field_586a3eaa4091b</code>.', 'broken-link-checker' ) . '</span>';
|
1500 |
+
$html .= '<br><textarea name="blc_acf_fields" id="blc_acf_fields" cols="45" rows="4">';
|
1501 |
+
if ( isset( $current_settings['acf_fields'] ) ) {
|
1502 |
+
$html .= esc_textarea( implode( "\n", $current_settings['acf_fields'] ) );
|
1503 |
+
}
|
1504 |
+
$html .= '</textarea>';
|
1505 |
|
1506 |
+
return $html;
|
1507 |
+
}
|
1508 |
+
/**
|
1509 |
+
* Enqueue CSS file for the plugin's Settings page.
|
1510 |
+
*
|
1511 |
+
* @return void
|
1512 |
+
*/
|
1513 |
+
function options_page_css() {
|
1514 |
+
wp_enqueue_style( 'blc-options-page', plugins_url( 'css/options-page.css', BLC_PLUGIN_FILE ), array(), '20141113' );
|
1515 |
+
wp_enqueue_style( 'dashboard' );
|
1516 |
+
}
|
1517 |
|
|
|
1518 |
|
1519 |
+
/**
|
1520 |
+
* Display the "Broken Links" page, listing links detected by the plugin and their status.
|
1521 |
+
*
|
1522 |
+
* @return void
|
1523 |
+
*/
|
1524 |
+
function links_page() {
|
1525 |
+
global $wpdb; /* @var wpdb $wpdb */
|
1526 |
|
1527 |
+
$blc_link_query = blcLinkQuery::getInstance();
|
|
|
|
|
|
|
|
|
|
|
1528 |
|
1529 |
+
//Cull invalid and missing modules so that we don't get dummy links/instances showing up.
|
1530 |
+
$moduleManager = blcModuleManager::getInstance();
|
1531 |
+
$moduleManager->validate_active_modules();
|
|
|
|
|
1532 |
|
1533 |
+
if ( defined( 'BLC_DEBUG' ) && constant( 'BLC_DEBUG' ) ) {
|
1534 |
+
//Make module headers translatable. They need to be formatted corrrectly and
|
1535 |
+
//placed in a .php file to be visible to the script(s) that generate .pot files.
|
1536 |
+
$code = $moduleManager->_build_header_translation_code();
|
1537 |
+
file_put_contents( dirname( $this->loader ) . '/includes/extra-strings.php', $code );
|
1538 |
+
}
|
1539 |
+
|
1540 |
+
$action = ! empty( $_POST['action'] ) ? $_POST['action'] : '';
|
1541 |
+
if ( intval( $action ) == -1 ) {
|
1542 |
+
//Try the second bulk actions box
|
1543 |
+
$action = ! empty( $_POST['action2'] ) ? $_POST['action2'] : '';
|
1544 |
+
}
|
1545 |
+
|
1546 |
+
//Get the list of link IDs selected via checkboxes
|
1547 |
+
$selected_links = array();
|
1548 |
+
if ( isset( $_POST['selected_links'] ) && is_array( $_POST['selected_links'] ) ) {
|
1549 |
+
//Convert all link IDs to integers (non-numeric entries are converted to zero)
|
1550 |
+
$selected_links = array_map( 'intval', $_POST['selected_links'] );
|
1551 |
+
//Remove all zeroes
|
1552 |
+
$selected_links = array_filter( $selected_links );
|
1553 |
+
}
|
1554 |
+
|
1555 |
+
$message = '';
|
1556 |
+
$msg_class = 'updated';
|
1557 |
+
|
1558 |
+
//Run the selected bulk action, if any
|
1559 |
+
$force_delete = false;
|
1560 |
+
switch ( $action ) {
|
1561 |
+
case 'create-custom-filter':
|
1562 |
+
list($message, $msg_class) = $this->do_create_custom_filter();
|
1563 |
+
break;
|
1564 |
+
|
1565 |
+
case 'delete-custom-filter':
|
1566 |
+
list($message, $msg_class) = $this->do_delete_custom_filter();
|
1567 |
+
break;
|
1568 |
+
|
1569 |
+
// @noinspection PhpMissingBreakStatementInspection Deliberate fall-through.
|
1570 |
+
case 'bulk-delete-sources':
|
1571 |
+
$force_delete = true;
|
1572 |
+
//intentional fall through
|
1573 |
+
case 'bulk-trash-sources':
|
1574 |
+
list($message, $msg_class) = $this->do_bulk_delete_sources( $selected_links, $force_delete );
|
1575 |
+
break;
|
1576 |
+
|
1577 |
+
case 'bulk-unlink':
|
1578 |
+
list($message, $msg_class) = $this->do_bulk_unlink( $selected_links );
|
1579 |
+
break;
|
1580 |
+
|
1581 |
+
case 'bulk-deredirect':
|
1582 |
+
list($message, $msg_class) = $this->do_bulk_deredirect( $selected_links );
|
1583 |
+
break;
|
1584 |
+
|
1585 |
+
case 'bulk-recheck':
|
1586 |
+
list($message, $msg_class) = $this->do_bulk_recheck( $selected_links );
|
1587 |
+
break;
|
1588 |
+
|
1589 |
+
case 'bulk-not-broken':
|
1590 |
+
list($message, $msg_class) = $this->do_bulk_discard( $selected_links );
|
1591 |
+
break;
|
1592 |
+
|
1593 |
+
case 'bulk-dismiss':
|
1594 |
+
list($message, $msg_class) = $this->do_bulk_dismiss( $selected_links );
|
1595 |
+
break;
|
1596 |
+
|
1597 |
+
case 'bulk-edit':
|
1598 |
+
list($message, $msg_class) = $this->do_bulk_edit( $selected_links );
|
1599 |
+
break;
|
1600 |
+
}
|
1601 |
+
|
1602 |
+
if ( ! empty( $message ) ) {
|
1603 |
+
echo '<div id="message" class="' . $msg_class . ' fade"><p>' . $message . '</p></div>';
|
1604 |
+
}
|
1605 |
+
|
1606 |
+
$start_time = microtime_float();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1607 |
|
1608 |
+
//Load custom filters, if any
|
1609 |
+
$blc_link_query->load_custom_filters();
|
1610 |
|
1611 |
+
//Calculate the number of links matching each filter
|
1612 |
+
$blc_link_query->count_filter_results();
|
1613 |
+
|
1614 |
+
//Run the selected filter (defaults to displaying broken links)
|
1615 |
+
$selected_filter_id = isset( $_GET['filter_id'] ) ? $_GET['filter_id'] : 'broken';
|
1616 |
+
$current_filter = $blc_link_query->exec_filter(
|
1617 |
+
$selected_filter_id,
|
1618 |
+
isset( $_GET['paged'] ) ? intval( $_GET['paged'] ) : 1,
|
1619 |
+
$this->conf->options['table_links_per_page'],
|
1620 |
+
'broken',
|
1621 |
+
isset( $_GET['orderby'] ) ? $_GET['orderby'] : '',
|
1622 |
+
isset( $_GET['order'] ) ? $_GET['order'] : ''
|
1623 |
);
|
1624 |
|
1625 |
+
//exec_filter() returns an array with filter data, including the actual filter ID that was used.
|
1626 |
+
$filter_id = $current_filter['filter_id'];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1627 |
|
1628 |
+
//Error?
|
1629 |
+
if ( empty( $current_filter['links'] ) && ! empty( $wpdb->last_error ) ) {
|
1630 |
+
printf( __( 'Database error : %s', 'broken-link-checker' ), $wpdb->last_error );
|
1631 |
+
}
|
1632 |
+
?>
|
1633 |
+
|
1634 |
+
<script type='text/javascript'>
|
1635 |
+
var blc_current_filter = '<?php echo $filter_id; ?>';
|
1636 |
+
var blc_is_broken_filter = <?php echo $current_filter['is_broken_filter'] ? 'true' : 'false'; ?>;
|
1637 |
+
var blc_current_base_filter = '<?php echo esc_js( $current_filter['base_filter'] ); ?>';
|
1638 |
+
var blc_suggestions_enabled = <?php echo $this->conf->options['suggestions_enabled'] ? 'true' : 'false'; ?>;
|
1639 |
+
</script>
|
1640 |
+
|
1641 |
+
<div class="wrap">
|
1642 |
+
<?php
|
1643 |
+
$blc_link_query->print_filter_heading( $current_filter );
|
1644 |
+
$blc_link_query->print_filter_menu( $filter_id );
|
1645 |
+
|
1646 |
+
//Display the "Search" form and associated buttons.
|
1647 |
+
//The form requires the $filter_id and $current_filter variables to be set.
|
1648 |
+
include dirname( $this->loader ) . '/includes/admin/search-form.php';
|
1649 |
+
|
1650 |
+
//If the user has decided to switch the table to a different mode (compact/full),
|
1651 |
+
//save the new setting.
|
1652 |
+
if ( isset( $_GET['compact'] ) ) {
|
1653 |
+
$this->conf->options['table_compact'] = (bool) $_GET['compact'];
|
1654 |
+
$this->conf->save_options();
|
1655 |
+
}
|
1656 |
+
|
1657 |
+
//Display the links, if any
|
1658 |
+
if ( $current_filter['links'] && ( count( $current_filter['links'] ) > 0 ) ) {
|
1659 |
+
|
1660 |
+
include dirname( $this->loader ) . '/includes/admin/table-printer.php';
|
1661 |
+
$table = new blcTablePrinter( $this );
|
1662 |
+
$table->print_table(
|
1663 |
+
$current_filter,
|
1664 |
+
$this->conf->options['table_layout'],
|
1665 |
+
$this->conf->options['table_visible_columns'],
|
1666 |
+
$this->conf->options['table_compact']
|
1667 |
+
);
|
1668 |
+
|
1669 |
+
};
|
1670 |
+
printf( '<!-- Total elapsed : %.4f seconds -->', microtime_float() - $start_time );
|
1671 |
+
|
1672 |
+
//Load assorted JS event handlers and other shinies
|
1673 |
+
include dirname( $this->loader ) . '/includes/admin/links-page-js.php';
|
1674 |
+
|
1675 |
+
?>
|
1676 |
+
</div>
|
1677 |
+
<?php
|
1678 |
+
}
|
1679 |
+
|
1680 |
+
/**
|
1681 |
+
* Create a custom link filter using params passed in $_POST.
|
1682 |
+
*
|
1683 |
+
* @uses $_POST
|
1684 |
+
* @uses $_GET to replace the current filter ID (if any) with that of the newly created filter.
|
1685 |
+
*
|
1686 |
+
* @return array Message and the CSS class to apply to the message.
|
1687 |
+
*/
|
1688 |
+
function do_create_custom_filter() {
|
1689 |
+
global $wpdb;
|
1690 |
+
|
1691 |
+
//Create a custom filter!
|
1692 |
+
check_admin_referer( 'create-custom-filter' );
|
1693 |
+
$msg_class = 'updated';
|
1694 |
+
|
1695 |
+
//Filter name must be set
|
1696 |
+
if ( empty( $_POST['name'] ) ) {
|
1697 |
+
$message = __( 'You must enter a filter name!', 'broken-link-checker' );
|
1698 |
+
$msg_class = 'error';
|
1699 |
+
//Filter parameters (a search query) must also be set
|
1700 |
+
} elseif ( empty( $_POST['params'] ) ) {
|
1701 |
+
$message = __( 'Invalid search query.', 'broken-link-checker' );
|
1702 |
$msg_class = 'error';
|
1703 |
+
} else {
|
1704 |
+
//Save the new filter
|
1705 |
+
$name = strip_tags( strval( $_POST['name'] ) );
|
1706 |
+
$blc_link_query = blcLinkQuery::getInstance();
|
1707 |
+
$filter_id = $blc_link_query->create_custom_filter( $name, $_POST['params'] );
|
1708 |
+
|
1709 |
+
if ( $filter_id ) {
|
1710 |
+
//Saved
|
1711 |
+
$message = sprintf( __( 'Filter "%s" created', 'broken-link-checker' ), $name );
|
1712 |
+
//A little hack to make the filter active immediately
|
1713 |
+
$_GET['filter_id'] = $filter_id;
|
1714 |
+
} else {
|
1715 |
+
//Error
|
1716 |
+
$message = sprintf( __( 'Database error : %s', 'broken-link-checker' ), $wpdb->last_error );
|
1717 |
+
$msg_class = 'error';
|
1718 |
+
}
|
1719 |
}
|
1720 |
+
|
1721 |
+
return array( $message, $msg_class );
|
1722 |
}
|
1723 |
|
1724 |
+
/**
|
1725 |
+
* Delete a custom link filter.
|
1726 |
+
*
|
1727 |
+
* @uses $_POST
|
1728 |
+
*
|
1729 |
+
* @return array Message and a CSS class to apply to the message.
|
1730 |
+
*/
|
1731 |
+
function do_delete_custom_filter() {
|
1732 |
+
//Delete an existing custom filter!
|
1733 |
+
check_admin_referer( 'delete-custom-filter' );
|
1734 |
+
$msg_class = 'updated';
|
1735 |
|
1736 |
+
//Filter ID must be set
|
1737 |
+
if ( empty( $_POST['filter_id'] ) ) {
|
1738 |
+
$message = __( 'Filter ID not specified.', 'broken-link-checker' );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1739 |
$msg_class = 'error';
|
1740 |
+
} else {
|
1741 |
+
//Try to delete the filter
|
1742 |
+
$blc_link_query = blcLinkQuery::getInstance();
|
1743 |
+
if ( $blc_link_query->delete_custom_filter( $_POST['filter_id'] ) ) {
|
1744 |
+
//Success
|
1745 |
+
$message = __( 'Filter deleted', 'broken-link-checker' );
|
1746 |
+
} else {
|
1747 |
+
//Either the ID is wrong or there was some other error
|
1748 |
+
$message = __( 'Database error : %s', 'broken-link-checker' );
|
1749 |
+
$msg_class = 'error';
|
1750 |
+
}
|
1751 |
}
|
1752 |
+
|
1753 |
+
return array( $message, $msg_class );
|
1754 |
}
|
1755 |
|
1756 |
+
/**
|
1757 |
+
* Modify multiple links to point to their target URLs.
|
1758 |
+
*
|
1759 |
+
* @param array $selected_links
|
1760 |
+
* @return array The message to display and its CSS class.
|
1761 |
+
*/
|
1762 |
+
function do_bulk_deredirect( $selected_links ) {
|
1763 |
+
//For all selected links, replace the URL with the final URL that it redirects to.
|
1764 |
|
1765 |
+
$message = '';
|
1766 |
+
$msg_class = 'updated';
|
1767 |
+
|
1768 |
+
check_admin_referer( 'bulk-action' );
|
1769 |
+
|
1770 |
+
if ( count( $selected_links ) > 0 ) {
|
1771 |
+
//Fetch all the selected links
|
1772 |
+
$links = blc_get_links(
|
1773 |
+
array(
|
1774 |
+
'link_ids' => $selected_links,
|
1775 |
+
'purpose' => BLC_FOR_EDITING,
|
1776 |
+
)
|
1777 |
+
);
|
1778 |
+
|
1779 |
+
if ( count( $links ) > 0 ) {
|
1780 |
+
$processed_links = 0;
|
1781 |
+
$failed_links = 0;
|
1782 |
+
|
1783 |
+
//Deredirect all selected links
|
1784 |
+
foreach ( $links as $link ) {
|
1785 |
+
$rez = $link->deredirect();
|
1786 |
+
if ( ! is_wp_error( $rez ) && empty( $rez['errors'] ) ) {
|
1787 |
+
$processed_links++;
|
1788 |
+
} else {
|
1789 |
+
$failed_links++;
|
1790 |
+
}
|
1791 |
+
}
|
1792 |
+
|
1793 |
+
$message = sprintf(
|
1794 |
+
_n(
|
1795 |
+
'Replaced %d redirect with a direct link',
|
1796 |
+
'Replaced %d redirects with direct links',
|
1797 |
+
$processed_links,
|
1798 |
+
'broken-link-checker'
|
1799 |
+
),
|
1800 |
+
$processed_links
|
1801 |
+
);
|
1802 |
+
|
1803 |
+
if ( $failed_links > 0 ) {
|
1804 |
+
$message .= '<br>' . sprintf(
|
1805 |
+
_n(
|
1806 |
+
'Failed to fix %d redirect',
|
1807 |
+
'Failed to fix %d redirects',
|
1808 |
+
$failed_links,
|
1809 |
+
'broken-link-checker'
|
1810 |
+
),
|
1811 |
+
$failed_links
|
1812 |
+
);
|
1813 |
+
$msg_class = 'error';
|
1814 |
}
|
1815 |
+
} else {
|
1816 |
+
$message = __( 'None of the selected links are redirects!', 'broken-link-checker' );
|
1817 |
}
|
1818 |
+
}
|
1819 |
|
1820 |
+
return array( $message, $msg_class );
|
1821 |
+
}
|
1822 |
+
|
1823 |
+
/**
|
1824 |
+
* Edit multiple links in one go.
|
1825 |
+
*
|
1826 |
+
* @param array $selected_links
|
1827 |
+
* @return array The message to display and its CSS class.
|
1828 |
+
*/
|
1829 |
+
function do_bulk_edit( $selected_links ) {
|
1830 |
+
$message = '';
|
1831 |
+
$msg_class = 'updated';
|
1832 |
+
|
1833 |
+
check_admin_referer( 'bulk-action' );
|
1834 |
+
|
1835 |
+
$post = $_POST;
|
1836 |
+
if ( function_exists( 'wp_magic_quotes' ) ) {
|
1837 |
+
$post = stripslashes_deep( $post ); //Ceterum censeo, WP shouldn't mangle superglobals.
|
1838 |
+
}
|
1839 |
+
|
1840 |
+
$search = isset( $post['search'] ) ? esc_attr( $post['search'] ) : '';
|
1841 |
+
$replace = isset( $post['replace'] ) ? esc_attr( $post['replace'] ) : '';
|
1842 |
+
$use_regex = ! empty( $post['regex'] );
|
1843 |
+
$case_sensitive = ! empty( $post['case_sensitive'] );
|
1844 |
+
|
1845 |
+
$delimiter = '`'; //Pick a char that's uncommon in URLs so that escaping won't usually be a problem
|
1846 |
+
if ( $use_regex ) {
|
1847 |
+
$search = $delimiter . $this->escape_regex_delimiter( $search, $delimiter ) . $delimiter;
|
1848 |
+
if ( ! $case_sensitive ) {
|
1849 |
+
$search .= 'i';
|
1850 |
+
}
|
1851 |
+
} elseif ( ! $case_sensitive ) {
|
1852 |
+
//str_ireplace() would be more appropriate for case-insensitive, non-regexp replacement,
|
1853 |
+
//but that's only available in PHP5.
|
1854 |
+
$search = $delimiter . preg_quote( $search, $delimiter ) . $delimiter . 'i';
|
1855 |
+
$use_regex = true;
|
1856 |
+
}
|
1857 |
+
|
1858 |
+
if ( count( $selected_links ) > 0 ) {
|
1859 |
+
set_time_limit( 300 ); //In case the user decides to edit hundreds of links at once
|
1860 |
+
|
1861 |
+
//Fetch all the selected links
|
1862 |
+
$links = blc_get_links(
|
1863 |
+
array(
|
1864 |
+
'link_ids' => $selected_links,
|
1865 |
+
'purpose' => BLC_FOR_EDITING,
|
1866 |
+
)
|
1867 |
);
|
1868 |
|
1869 |
+
if ( count( $links ) > 0 ) {
|
1870 |
+
$processed_links = 0;
|
1871 |
+
$failed_links = 0;
|
1872 |
+
$skipped_links = 0;
|
1873 |
+
|
1874 |
+
//Edit the links
|
1875 |
+
foreach ( $links as $link ) {
|
1876 |
+
if ( $use_regex ) {
|
1877 |
+
$new_url = preg_replace( $search, $replace, $link->url );
|
1878 |
+
} else {
|
1879 |
+
$new_url = str_replace( $search, $replace, $link->url );
|
1880 |
+
}
|
1881 |
+
|
1882 |
+
if ( $new_url == $link->url ) {
|
1883 |
+
$skipped_links++;
|
1884 |
+
continue;
|
1885 |
+
}
|
1886 |
+
|
1887 |
+
$rez = $link->edit( $new_url );
|
1888 |
+
if ( ! is_wp_error( $rez ) && empty( $rez['errors'] ) ) {
|
1889 |
+
$processed_links++;
|
1890 |
+
} else {
|
1891 |
+
$failed_links++;
|
1892 |
+
}
|
1893 |
+
}
|
1894 |
+
|
1895 |
+
$message .= sprintf(
|
1896 |
_n(
|
1897 |
+
'%d link updated.',
|
1898 |
+
'%d links updated.',
|
1899 |
+
$processed_links,
|
1900 |
'broken-link-checker'
|
1901 |
),
|
1902 |
+
$processed_links
|
1903 |
);
|
1904 |
+
|
1905 |
+
if ( $failed_links > 0 ) {
|
1906 |
+
$message .= '<br>' . sprintf(
|
1907 |
+
_n(
|
1908 |
+
'Failed to update %d link.',
|
1909 |
+
'Failed to update %d links.',
|
1910 |
+
$failed_links,
|
1911 |
+
'broken-link-checker'
|
1912 |
+
),
|
1913 |
+
$failed_links
|
1914 |
+
);
|
1915 |
+
$msg_class = 'error';
|
1916 |
+
}
|
1917 |
}
|
|
|
|
|
1918 |
}
|
1919 |
+
|
1920 |
+
return array( $message, $msg_class );
|
1921 |
}
|
1922 |
|
1923 |
+
/**
|
1924 |
+
* Escape all instances of the $delimiter character with a backslash (unless already escaped).
|
1925 |
+
*
|
1926 |
+
* @param string $pattern
|
1927 |
+
* @param string $delimiter
|
1928 |
+
* @return string
|
1929 |
+
*/
|
1930 |
+
private function escape_regex_delimiter( $pattern, $delimiter ) {
|
1931 |
+
if ( empty( $pattern ) ) {
|
1932 |
+
return '';
|
1933 |
+
}
|
1934 |
|
1935 |
+
$output = '';
|
1936 |
+
$length = strlen( $pattern );
|
1937 |
+
$escaped = false;
|
1938 |
+
|
1939 |
+
for ( $i = 0; $i < $length; $i++ ) {
|
1940 |
+
$char = $pattern[ $i ];
|
1941 |
+
|
1942 |
+
if ( $escaped ) {
|
1943 |
+
$escaped = false;
|
1944 |
+
} else {
|
1945 |
+
if ( '\\' == $char ) {
|
1946 |
+
$escaped = true;
|
1947 |
+
} elseif ( $char == $delimiter ) {
|
1948 |
+
$char = '\\' . $char;
|
1949 |
+
}
|
1950 |
+
}
|
1951 |
+
|
1952 |
+
$output .= $char;
|
1953 |
+
}
|
1954 |
+
|
1955 |
+
return $output;
|
1956 |
+
}
|
1957 |
+
|
1958 |
+
/**
|
1959 |
+
* Unlink multiple links.
|
1960 |
+
*
|
1961 |
+
* @param array $selected_links
|
1962 |
+
* @return array Message and a CSS classname.
|
1963 |
+
*/
|
1964 |
+
function do_bulk_unlink( $selected_links ) {
|
1965 |
+
//Unlink all selected links.
|
1966 |
+
$message = '';
|
1967 |
+
$msg_class = 'updated';
|
1968 |
+
|
1969 |
+
check_admin_referer( 'bulk-action' );
|
1970 |
+
|
1971 |
+
if ( count( $selected_links ) > 0 ) {
|
1972 |
+
|
1973 |
+
//Fetch all the selected links
|
1974 |
+
$links = blc_get_links(
|
1975 |
+
array(
|
1976 |
+
'link_ids' => $selected_links,
|
1977 |
+
'purpose' => BLC_FOR_EDITING,
|
1978 |
+
)
|
1979 |
+
);
|
1980 |
+
|
1981 |
+
if ( count( $links ) > 0 ) {
|
1982 |
+
$processed_links = 0;
|
1983 |
+
$failed_links = 0;
|
1984 |
+
|
1985 |
+
//Unlink (delete) each one
|
1986 |
+
foreach ( $links as $link ) {
|
1987 |
+
$rez = $link->unlink();
|
1988 |
+
if ( ( false == $rez ) || is_wp_error( $rez ) ) {
|
1989 |
+
$failed_links++;
|
1990 |
+
} else {
|
1991 |
+
$processed_links++;
|
1992 |
+
}
|
1993 |
+
}
|
1994 |
+
|
1995 |
+
//This message is slightly misleading - it doesn't account for the fact that
|
1996 |
+
//a link can be present in more than one post.
|
1997 |
+
$message = sprintf(
|
1998 |
+
_n(
|
1999 |
+
'%d link removed',
|
2000 |
+
'%d links removed',
|
2001 |
+
$processed_links,
|
2002 |
+
'broken-link-checker'
|
2003 |
+
),
|
2004 |
+
$processed_links
|
2005 |
+
);
|
2006 |
+
|
2007 |
+
if ( $failed_links > 0 ) {
|
2008 |
+
$message .= '<br>' . sprintf(
|
2009 |
+
_n(
|
2010 |
+
'Failed to remove %d link',
|
2011 |
+
'Failed to remove %d links',
|
2012 |
+
$failed_links,
|
2013 |
+
'broken-link-checker'
|
2014 |
+
),
|
2015 |
+
$failed_links
|
2016 |
+
);
|
2017 |
+
$msg_class = 'error';
|
2018 |
}
|
2019 |
+
}
|
2020 |
+
}
|
2021 |
+
|
2022 |
+
return array( $message, $msg_class );
|
2023 |
+
}
|
2024 |
+
|
2025 |
+
/**
|
2026 |
+
* Delete or trash posts, bookmarks and other items that contain any of the specified links.
|
2027 |
+
*
|
2028 |
+
* Will prefer moving stuff to trash to permanent deletion. If it encounters an item that
|
2029 |
+
* can't be moved to the trash, it will skip that item by default.
|
2030 |
+
*
|
2031 |
+
* @param array $selected_links An array of link IDs
|
2032 |
+
* @param bool $force_delete Whether to bypass trash and force deletion. Defaults to false.
|
2033 |
+
* @return array Confirmation message and its CSS class.
|
2034 |
+
*/
|
2035 |
+
function do_bulk_delete_sources( $selected_links, $force_delete = false ) {
|
2036 |
+
$message = '';
|
2037 |
+
$msg_class = 'updated';
|
2038 |
+
|
2039 |
+
//Delete posts, blogroll entries and any other link containers that contain any of the selected links.
|
2040 |
+
//
|
2041 |
+
//Note that once all containers containing a particular link have been deleted,
|
2042 |
+
//there is no need to explicitly delete the link record itself. The hooks attached to
|
2043 |
+
//the actions that execute when something is deleted (e.g. "post_deleted") will
|
2044 |
+
//take care of that.
|
2045 |
+
|
2046 |
+
check_admin_referer( 'bulk-action' );
|
2047 |
+
|
2048 |
+
if ( count( $selected_links ) > 0 ) {
|
2049 |
+
$messages = array();
|
2050 |
+
|
2051 |
+
//Fetch all the selected links
|
2052 |
+
$links = blc_get_links(
|
2053 |
+
array(
|
2054 |
+
'link_ids' => $selected_links,
|
2055 |
+
'load_instances' => true,
|
2056 |
+
)
|
2057 |
+
);
|
2058 |
+
|
2059 |
+
//Make a list of all containers associated with these links, with each container
|
2060 |
+
//listed only once.
|
2061 |
+
$containers = array();
|
2062 |
+
foreach ( $links as $link ) { /* @var blcLink $link */
|
2063 |
+
$instances = $link->get_instances();
|
2064 |
+
foreach ( $instances as $instance ) { /* @var blcLinkInstance $instance */
|
2065 |
+
$key = $instance->container_type . '|' . $instance->container_id;
|
2066 |
+
$containers[ $key ] = array( $instance->container_type, $instance->container_id );
|
2067 |
+
}
|
2068 |
+
}
|
2069 |
+
|
2070 |
+
//Instantiate the containers
|
2071 |
+
$containers = blcContainerHelper::get_containers( $containers );
|
2072 |
|
2073 |
+
//Delete/trash their associated entities
|
2074 |
+
$deleted = array();
|
2075 |
+
$skipped = array();
|
2076 |
+
foreach ( $containers as $container ) { /* @var blcContainer $container */
|
2077 |
+
if ( ! $container->current_user_can_delete() ) {
|
2078 |
continue;
|
2079 |
}
|
2080 |
|
2081 |
+
if ( $force_delete ) {
|
2082 |
+
$rez = $container->delete_wrapped_object();
|
2083 |
+
} else {
|
2084 |
+
if ( $container->can_be_trashed() ) {
|
2085 |
+
$rez = $container->trash_wrapped_object();
|
2086 |
+
} else {
|
2087 |
+
$skipped[] = $container;
|
2088 |
+
continue;
|
2089 |
+
}
|
2090 |
+
}
|
2091 |
+
|
2092 |
+
if ( is_wp_error( $rez ) ) { /* @var WP_Error $rez */
|
2093 |
+
//Record error messages for later display
|
2094 |
+
$messages[] = $rez->get_error_message();
|
2095 |
+
$msg_class = 'error';
|
2096 |
} else {
|
2097 |
+
//Keep track of how many of each type were deleted.
|
2098 |
+
$container_type = $container->container_type;
|
2099 |
+
if ( isset( $deleted[ $container_type ] ) ) {
|
2100 |
+
$deleted[ $container_type ]++;
|
2101 |
+
} else {
|
2102 |
+
$deleted[ $container_type ] = 1;
|
2103 |
+
}
|
2104 |
}
|
2105 |
}
|
2106 |
|
2107 |
+
//Generate delete confirmation messages
|
2108 |
+
foreach ( $deleted as $container_type => $number ) {
|
2109 |
+
if ( $force_delete ) {
|
2110 |
+
$messages[] = blcContainerHelper::ui_bulk_delete_message( $container_type, $number );
|
2111 |
+
} else {
|
2112 |
+
$messages[] = blcContainerHelper::ui_bulk_trash_message( $container_type, $number );
|
2113 |
+
}
|
2114 |
+
}
|
|
|
2115 |
|
2116 |
+
//If some items couldn't be trashed, let the user know
|
2117 |
+
if ( count( $skipped ) > 0 ) {
|
2118 |
+
$message = sprintf(
|
2119 |
_n(
|
2120 |
+
"%d item was skipped because it can't be moved to the Trash. You need to delete it manually.",
|
2121 |
+
"%d items were skipped because they can't be moved to the Trash. You need to delete them manually.",
|
2122 |
+
count( $skipped )
|
|
|
2123 |
),
|
2124 |
+
count( $skipped )
|
2125 |
);
|
2126 |
+
$message .= '<br><ul>';
|
2127 |
+
foreach ( $skipped as $container ) {
|
2128 |
+
$message .= sprintf(
|
2129 |
+
'<li>%s</li>',
|
2130 |
+
$container->ui_get_source( '' )
|
2131 |
+
);
|
2132 |
+
}
|
2133 |
+
$message .= '</ul>';
|
2134 |
+
|
2135 |
+
$messages[] = $message;
|
2136 |
+
}
|
2137 |
+
|
2138 |
+
if ( count( $messages ) > 0 ) {
|
2139 |
+
$message = implode( '<p>', $messages );
|
2140 |
+
} else {
|
2141 |
+
$message = __( "Didn't find anything to delete!", 'broken-link-checker' );
|
2142 |
$msg_class = 'error';
|
2143 |
}
|
2144 |
}
|
2145 |
+
|
2146 |
+
return array( $message, $msg_class );
|
2147 |
}
|
2148 |
|
2149 |
+
/**
|
2150 |
+
* Mark multiple links as unchecked.
|
2151 |
+
*
|
2152 |
+
* @param array $selected_links An array of link IDs
|
2153 |
+
* @return array Confirmation nessage and the CSS class to use with that message.
|
2154 |
+
*/
|
2155 |
+
function do_bulk_recheck( $selected_links ) {
|
2156 |
+
/** @var wpdb $wpdb */
|
2157 |
+
global $wpdb;
|
2158 |
|
2159 |
+
$message = '';
|
2160 |
+
$msg_class = 'updated';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2161 |
|
2162 |
+
check_admin_referer( 'bulk-action' );
|
|
|
|
|
2163 |
|
2164 |
+
if ( count( $selected_links ) > 0 ) {
|
|
|
2165 |
|
2166 |
+
$changes = $wpdb->query(
|
2167 |
+
$wpdb->prepare(
|
2168 |
+
"UPDATE {$wpdb->prefix}blc_links
|
2169 |
+
SET last_check_attempt = '0000-00-00 00:00:00'
|
2170 |
+
WHERE link_id IN ( %s )",
|
2171 |
+
implode( '', $selected_links )
|
2172 |
+
)
|
2173 |
+
);
|
2174 |
+
|
2175 |
+
$message = sprintf(
|
2176 |
+
_n(
|
2177 |
+
'%d link scheduled for rechecking',
|
2178 |
+
'%d links scheduled for rechecking',
|
2179 |
+
$changes,
|
2180 |
+
'broken-link-checker'
|
2181 |
+
),
|
2182 |
+
$changes
|
2183 |
+
);
|
2184 |
}
|
2185 |
|
2186 |
+
return array( $message, $msg_class );
|
2187 |
}
|
2188 |
|
|
|
|
|
2189 |
|
2190 |
+
/**
|
2191 |
+
* Mark multiple links as not broken.
|
2192 |
+
*
|
2193 |
+
* @param array $selected_links An array of link IDs
|
2194 |
+
* @return array Confirmation nessage and the CSS class to use with that message.
|
2195 |
+
*/
|
2196 |
+
function do_bulk_discard( $selected_links ) {
|
2197 |
+
check_admin_referer( 'bulk-action' );
|
2198 |
+
|
2199 |
+
$messages = array();
|
2200 |
+
$msg_class = 'updated';
|
2201 |
+
$processed_links = 0;
|
2202 |
+
|
2203 |
+
if ( count( $selected_links ) > 0 ) {
|
2204 |
+
$transactionManager = TransactionManager::getInstance();
|
2205 |
+
$transactionManager->start();
|
2206 |
+
foreach ( $selected_links as $link_id ) {
|
2207 |
+
//Load the link
|
2208 |
+
$link = new blcLink( intval( $link_id ) );
|
2209 |
+
|
2210 |
+
//Skip links that don't actually exist
|
2211 |
+
if ( ! $link->valid() ) {
|
2212 |
+
continue;
|
2213 |
+
}
|
2214 |
+
|
2215 |
+
//Skip links that weren't actually detected as broken
|
2216 |
+
if ( ! $link->broken && ! $link->warning ) {
|
2217 |
+
continue;
|
2218 |
+
}
|
2219 |
+
|
2220 |
+
//Make it appear "not broken"
|
2221 |
+
$link->broken = false;
|
2222 |
+
$link->warning = false;
|
2223 |
+
$link->false_positive = true;
|
2224 |
+
$link->last_check_attempt = time();
|
2225 |
+
$link->log = __( 'This link was manually marked as working by the user.', 'broken-link-checker' );
|
2226 |
+
|
2227 |
+
$link->isOptionLinkChanged = true;
|
2228 |
+
//Save the changes
|
2229 |
+
if ( $link->save() ) {
|
2230 |
$processed_links++;
|
2231 |
+
} else {
|
2232 |
+
$messages[] = sprintf(
|
2233 |
+
__( "Couldn't modify link %d", 'broken-link-checker' ),
|
2234 |
+
$link_id
|
2235 |
+
);
|
2236 |
+
$msg_class = 'error';
|
2237 |
}
|
2238 |
}
|
2239 |
+
}
|
2240 |
|
2241 |
+
if ( $processed_links > 0 ) {
|
2242 |
+
$transactionManager->commit();
|
2243 |
+
$messages[] = sprintf(
|
2244 |
_n(
|
2245 |
+
'%d link marked as not broken',
|
2246 |
+
'%d links marked as not broken',
|
2247 |
$processed_links,
|
2248 |
'broken-link-checker'
|
2249 |
),
|
2250 |
$processed_links
|
2251 |
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2252 |
}
|
2253 |
+
|
2254 |
+
return array( implode( '<br>', $messages ), $msg_class );
|
2255 |
}
|
2256 |
|
2257 |
+
/**
|
2258 |
+
* Dismiss multiple links.
|
2259 |
+
*
|
2260 |
+
* @param array $selected_links An array of link IDs
|
2261 |
+
* @return array Confirmation message and the CSS class to use with that message.
|
2262 |
+
*/
|
2263 |
+
function do_bulk_dismiss( $selected_links ) {
|
2264 |
+
check_admin_referer( 'bulk-action' );
|
2265 |
|
2266 |
+
$messages = array();
|
2267 |
+
$msg_class = 'updated';
|
2268 |
+
$processed_links = 0;
|
2269 |
+
|
2270 |
+
if ( count( $selected_links ) > 0 ) {
|
2271 |
+
$transactionManager = TransactionManager::getInstance();
|
2272 |
+
$transactionManager->start();
|
2273 |
+
foreach ( $selected_links as $link_id ) {
|
2274 |
+
//Load the link
|
2275 |
+
$link = new blcLink( intval( $link_id ) );
|
2276 |
+
|
2277 |
+
//Skip links that don't actually exist
|
2278 |
+
if ( ! $link->valid() ) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2279 |
continue;
|
2280 |
}
|
|
|
2281 |
|
2282 |
+
//We can only dismiss broken links and redirects.
|
2283 |
+
if ( ! ( $link->broken || $link->warning || ( $link->redirect_count > 0 ) ) ) {
|
2284 |
+
continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2285 |
}
|
|
|
|
|
2286 |
|
2287 |
+
$link->dismissed = true;
|
2288 |
+
|
2289 |
+
$link->isOptionLinkChanged = true;
|
2290 |
+
//Save the changes
|
2291 |
+
if ( $link->save() ) {
|
2292 |
+
$processed_links++;
|
2293 |
+
} else {
|
2294 |
+
$messages[] = sprintf(
|
2295 |
+
__( "Couldn't modify link %d", 'broken-link-checker' ),
|
2296 |
+
$link_id
|
2297 |
+
);
|
2298 |
+
$msg_class = 'error';
|
2299 |
+
}
|
2300 |
}
|
|
|
2301 |
}
|
2302 |
|
2303 |
+
if ( $processed_links > 0 ) {
|
2304 |
+
$transactionManager->commit();
|
2305 |
+
$messages[] = sprintf(
|
2306 |
_n(
|
2307 |
+
'%d link dismissed',
|
2308 |
+
'%d links dismissed',
|
2309 |
+
$processed_links,
|
2310 |
+
'broken-link-checker'
|
2311 |
),
|
2312 |
+
$processed_links
|
2313 |
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2314 |
}
|
2315 |
|
2316 |
+
return array( implode( '<br>', $messages ), $msg_class );
|
|
|
|
|
|
|
|
|
|
|
2317 |
}
|
2318 |
|
|
|
|
|
2319 |
|
2320 |
+
/**
|
2321 |
+
* Enqueue CSS files for the "Broken Links" page
|
2322 |
+
*
|
2323 |
+
* @return void
|
2324 |
+
*/
|
2325 |
+
function links_page_css() {
|
2326 |
+
wp_enqueue_style( 'blc-links-page', plugins_url( 'css/links-page.css', $this->loader ), array(), '20141113-2' );
|
2327 |
+
}
|
2328 |
+
|
2329 |
+
/**
|
2330 |
+
* Show an admin notice that explains what the "Warnings" section under "Tools -> Broken Links" does.
|
2331 |
+
* The user can hide the notice.
|
2332 |
+
*/
|
2333 |
+
public function show_warnings_section_notice() {
|
2334 |
+
$is_warnings_section = isset( $_GET['filter_id'] )
|
2335 |
+
&& ( 'warnings' === $_GET['filter_id'] )
|
2336 |
+
&& isset( $_GET['page'] )
|
2337 |
+
&& ( 'view-broken-links' === $_GET['page'] );
|
2338 |
|
2339 |
+
if ( ! ( $is_warnings_section && current_user_can( 'edit_others_posts' ) ) ) {
|
2340 |
+
return;
|
2341 |
+
}
|
2342 |
|
2343 |
+
//Let the user hide the notice.
|
2344 |
+
$conf = blc_get_configuration();
|
2345 |
+
$notice_name = 'show_warnings_section_hint';
|
2346 |
|
2347 |
+
if ( isset( $_GET[ $notice_name ] ) && is_numeric( $_GET[ $notice_name ] ) ) {
|
2348 |
+
$conf->set( $notice_name, (bool) $_GET[ $notice_name ] );
|
2349 |
+
$conf->save_options();
|
2350 |
+
}
|
2351 |
+
if ( ! $conf->get( $notice_name, true ) ) {
|
2352 |
+
return;
|
2353 |
+
}
|
2354 |
|
2355 |
+
printf(
|
2356 |
+
'<div class="updated">
|
2357 |
+
<p>%1$s</p>
|
2358 |
+
<p>
|
2359 |
+
<a href="%2$s">%3$s</a> |
|
2360 |
+
<a href="%4$s">%5$s</a>
|
2361 |
+
<p>
|
2362 |
+
</div>',
|
2363 |
+
__(
|
2364 |
+
'The "Warnings" page lists problems that are probably temporary or suspected to be false positives.<br> Warnings that persist for a long time will usually be reclassified as broken links.',
|
2365 |
+
'broken-link-checker'
|
2366 |
+
),
|
2367 |
+
esc_attr( add_query_arg( $notice_name, '0' ) ),
|
2368 |
+
_x(
|
2369 |
+
'Hide notice',
|
2370 |
+
'admin notice under Tools - Broken links - Warnings',
|
2371 |
'broken-link-checker'
|
2372 |
),
|
2373 |
+
esc_attr( admin_url( 'options-general.php?page=link-checker-settings#blc_warning_settings' ) ),
|
2374 |
+
_x(
|
2375 |
+
'Change warning settings',
|
2376 |
+
'a link from the admin notice under Tools - Broken links - Warnings',
|
2377 |
+
'broken-link-checker'
|
2378 |
+
)
|
2379 |
);
|
2380 |
}
|
2381 |
|
2382 |
+
/**
|
2383 |
+
* Generate the HTML for the plugin's Screen Options panel.
|
2384 |
+
*
|
2385 |
+
* @return string
|
2386 |
+
*/
|
2387 |
+
function screen_options_html() {
|
2388 |
+
//Update the links-per-page setting when "Apply" is clicked
|
2389 |
+
if ( isset( $_POST['per_page'] ) && is_numeric( $_POST['per_page'] ) ) {
|
2390 |
+
check_admin_referer( 'screen-options-nonce', 'screenoptionnonce' );
|
2391 |
+
$per_page = intval( $_POST['per_page'] );
|
2392 |
+
if ( ( $per_page >= 1 ) && ( $per_page <= 500 ) ) {
|
2393 |
+
$this->conf->options['table_links_per_page'] = $per_page;
|
2394 |
+
$this->conf->save_options();
|
2395 |
+
}
|
2396 |
+
}
|
2397 |
|
2398 |
+
//Let the user show/hide individual table columns
|
2399 |
+
$html = '<h5>' . __( 'Table columns', 'broken-link-checker' ) . '</h5>';
|
2400 |
|
2401 |
+
include dirname( $this->loader ) . '/includes/admin/table-printer.php';
|
2402 |
+
$table = new blcTablePrinter( $this );
|
2403 |
+
$available_columns = $table->get_layout_columns( $this->conf->options['table_layout'] );
|
|
|
|
|
|
|
|
|
|
|
2404 |
|
2405 |
+
$html .= '<div id="blc-column-selector" class="metabox-prefs">';
|
|
|
|
|
2406 |
|
2407 |
+
foreach ( $available_columns as $column_id => $data ) {
|
2408 |
+
$html .= sprintf(
|
2409 |
+
'<label><input type="checkbox" name="visible_columns[%s]"%s>%s</label>',
|
2410 |
+
esc_attr( $column_id ),
|
2411 |
+
in_array( $column_id, $this->conf->options['table_visible_columns'] ) ? ' checked="checked"' : '',
|
2412 |
+
$data['heading']
|
2413 |
+
);
|
2414 |
+
}
|
2415 |
|
2416 |
+
$html .= '</div>';
|
|
|
|
|
|
|
2417 |
|
2418 |
+
$html .= '<h5>' . __( 'Show on screen', 'broken-link-checker' ) . '</h5>';
|
2419 |
+
$html .= '<div class="screen-options">';
|
2420 |
+
$html .= sprintf(
|
2421 |
+
'<input type="text" name="per_page" maxlength="3" value="%d" class="screen-per-page" id="blc_links_per_page" />
|
2422 |
+
<label for="blc_links_per_page">%s</label>
|
2423 |
+
<input type="button" class="button" value="%s" id="blc-per-page-apply-button" /><br />',
|
2424 |
+
$this->conf->options['table_links_per_page'],
|
2425 |
+
__( 'links', 'broken-link-checker' ),
|
2426 |
+
__( 'Apply' )
|
2427 |
+
);
|
2428 |
+
$html .= '</div>';
|
2429 |
|
2430 |
+
$html .= '<h5>' . __( 'Misc', 'broken-link-checker' ) . '</h5>';
|
2431 |
+
$html .= '<div class="screen-options">';
|
2432 |
+
/*
|
2433 |
+
Display a checkbox in "Screen Options" that lets the user highlight links that
|
2434 |
+
have been broken for at least X days.
|
2435 |
+
*/
|
2436 |
+
$html .= sprintf(
|
2437 |
+
'<label><input type="checkbox" id="highlight_permanent_failures" name="highlight_permanent_failures"%s> ',
|
2438 |
+
$this->conf->options['highlight_permanent_failures'] ? ' checked="checked"' : ''
|
2439 |
+
);
|
2440 |
+
$input_box = sprintf(
|
2441 |
+
'</label><input type="text" name="failure_duration_threshold" id="failure_duration_threshold" value="%d" size="2"><label for="highlight_permanent_failures">',
|
2442 |
+
$this->conf->options['failure_duration_threshold']
|
2443 |
+
);
|
2444 |
+
$html .= sprintf(
|
2445 |
+
__( 'Highlight links broken for at least %s days', 'broken-link-checker' ),
|
2446 |
+
$input_box
|
2447 |
+
);
|
2448 |
+
$html .= '</label>';
|
2449 |
|
2450 |
+
//Display a checkbox for turning colourful link status messages on/off
|
2451 |
+
$html .= sprintf(
|
2452 |
+
'<br/><label><input type="checkbox" id="table_color_code_status" name="table_color_code_status"%s> %s</label>',
|
2453 |
+
$this->conf->options['table_color_code_status'] ? ' checked="checked"' : '',
|
2454 |
+
__( 'Color-code status codes', 'broken-link-checker' )
|
2455 |
+
);
|
2456 |
+
|
2457 |
+
$html .= '</div>';
|
2458 |
+
|
2459 |
+
return $html;
|
2460 |
+
}
|
2461 |
+
|
2462 |
+
/**
|
2463 |
+
* AJAX callback for saving the "Screen Options" panel settings
|
2464 |
+
*
|
2465 |
+
* @param array $form
|
2466 |
+
* @return void
|
2467 |
+
*/
|
2468 |
+
function ajax_save_screen_options( $form ) {
|
2469 |
+
if ( ! current_user_can( 'edit_others_posts' ) ) {
|
2470 |
+
die(
|
2471 |
+
json_encode(
|
2472 |
+
array(
|
2473 |
+
'error' => __( "You're not allowed to do that!", 'broken-link-checker' ),
|
2474 |
+
)
|
2475 |
+
)
|
2476 |
+
);
|
2477 |
+
}
|
2478 |
+
|
2479 |
+
$this->conf->options['highlight_permanent_failures'] = ! empty( $form['highlight_permanent_failures'] );
|
2480 |
+
$this->conf->options['table_color_code_status'] = ! empty( $form['table_color_code_status'] );
|
2481 |
+
|
2482 |
+
$failure_duration_threshold = intval( $form['failure_duration_threshold'] );
|
2483 |
+
if ( $failure_duration_threshold >= 1 ) {
|
2484 |
+
$this->conf->options['failure_duration_threshold'] = $failure_duration_threshold;
|
2485 |
+
}
|
2486 |
+
|
2487 |
+
if ( isset( $form['visible_columns'] ) && is_array( $form['visible_columns'] ) ) {
|
2488 |
+
$this->conf->options['table_visible_columns'] = array_keys( $form['visible_columns'] );
|
2489 |
}
|
2490 |
+
|
2491 |
+
$this->conf->save_options();
|
2492 |
+
die( '1' );
|
2493 |
}
|
2494 |
|
2495 |
+
function start_timer() {
|
2496 |
+
$this->execution_start_time = microtime_float();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2497 |
}
|
2498 |
|
2499 |
+
function execution_time() {
|
2500 |
+
return microtime_float() - $this->execution_start_time;
|
2501 |
+
}
|
2502 |
|
2503 |
+
/**
|
2504 |
+
* The main worker function that does all kinds of things.
|
2505 |
+
*
|
2506 |
+
* @return void
|
2507 |
+
*/
|
2508 |
+
function work() {
|
2509 |
+
global $blclog;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2510 |
|
2511 |
+
//Close the session to prevent lock-ups.
|
2512 |
+
//PHP sessions are blocking. session_start() will wait until all other scripts that are using the same session
|
2513 |
+
//are finished. As a result, a long-running script that unintentionally keeps the session open can cause
|
2514 |
+
//the entire site to "lock up" for the current user/browser. WordPress itself doesn't use sessions, but some
|
2515 |
+
//plugins do, so we should explicitly close the session (if any) before starting the worker.
|
2516 |
+
if ( session_id() != '' ) {
|
2517 |
+
session_write_close();
|
2518 |
+
}
|
2519 |
|
2520 |
+
if ( ! $this->acquire_lock() ) {
|
2521 |
+
//FB::warn("Another instance of BLC is already working. Stop.");
|
2522 |
+
$blclog->info( 'Another instance of BLC is already working. Stop.' );
|
2523 |
+
return;
|
2524 |
+
}
|
2525 |
|
2526 |
+
if ( $this->server_too_busy() ) {
|
2527 |
+
//FB::warn("Server is too busy. Stop.");
|
2528 |
+
$blclog->warn( 'Server load is too high, stopping.' );
|
2529 |
+
return;
|
2530 |
+
}
|
2531 |
|
2532 |
+
$this->start_timer();
|
2533 |
+
$blclog->info( 'work() starts' );
|
2534 |
+
|
2535 |
+
$max_execution_time = $this->conf->options['max_execution_time'];
|
2536 |
+
|
2537 |
+
/*****************************************
|
2538 |
+
Preparation
|
2539 |
+
******************************************/
|
2540 |
+
// Check for safe mode
|
2541 |
+
if ( blcUtility::is_safe_mode() ) {
|
2542 |
+
// Do it the safe mode way - obey the existing max_execution_time setting
|
2543 |
+
$t = ini_get( 'max_execution_time' );
|
2544 |
+
if ( $t && ( $t < $max_execution_time ) ) {
|
2545 |
+
$max_execution_time = $t - 1;
|
2546 |
}
|
2547 |
+
} else {
|
2548 |
+
// Do it the regular way
|
2549 |
+
@set_time_limit( $max_execution_time * 2 ); //x2 should be plenty, running any longer would mean a glitch.
|
2550 |
}
|
|
|
2551 |
|
2552 |
+
//Don't stop the script when the connection is closed
|
2553 |
+
ignore_user_abort( true );
|
2554 |
+
|
2555 |
+
//Close the connection as per http://www.php.net/manual/en/features.connection-handling.php#71172
|
2556 |
+
//This reduces resource usage.
|
2557 |
+
//(Disable when debugging or you won't get the FirePHP output)
|
2558 |
+
if (
|
2559 |
+
! headers_sent()
|
2560 |
+
&& ( defined( 'DOING_AJAX' ) && constant( 'DOING_AJAX' ) )
|
2561 |
+
&& ( ! defined( 'BLC_DEBUG' ) || ! constant( 'BLC_DEBUG' ) )
|
2562 |
+
) {
|
2563 |
+
@ob_end_clean(); //Discard the existing buffer, if any
|
2564 |
+
header( 'Connection: close' );
|
2565 |
+
ob_start();
|
2566 |
+
echo ( 'Connection closed' ); //This could be anything
|
2567 |
+
$size = ob_get_length();
|
2568 |
+
header( "Content-Length: $size" );
|
2569 |
+
ob_end_flush(); // Strange behaviour, will not work
|
2570 |
+
flush(); // Unless both are called !
|
2571 |
+
}
|
2572 |
|
2573 |
+
//Load modules for this context
|
2574 |
+
$moduleManager = blcModuleManager::getInstance();
|
2575 |
+
$moduleManager->load_modules( 'work' );
|
2576 |
|
2577 |
+
$target_usage_fraction = $this->conf->get( 'target_resource_usage', 0.25 );
|
2578 |
+
//Target usage must be between 1% and 100%.
|
2579 |
+
$target_usage_fraction = max( min( $target_usage_fraction, 1 ), 0.01 );
|
2580 |
|
2581 |
+
/*****************************************
|
2582 |
+
Parse posts and bookmarks
|
2583 |
+
******************************************/
|
|
|
|
|
|
|
|
|
|
|
2584 |
|
2585 |
+
$orphans_possible = false;
|
2586 |
+
$still_need_resynch = $this->conf->options['need_resynch'];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2587 |
|
2588 |
+
if ( $still_need_resynch ) {
|
|
|
|
|
2589 |
|
2590 |
+
//FB::log("Looking for containers that need parsing...");
|
2591 |
+
$max_containers_per_query = 50;
|
|
|
2592 |
|
2593 |
+
$start = microtime( true );
|
2594 |
+
$containers = blcContainerHelper::get_unsynched_containers( $max_containers_per_query );
|
2595 |
+
$get_containers_time = microtime( true ) - $start;
|
|
|
|
|
|
|
|
|
2596 |
|
2597 |
+
while ( ! empty( $containers ) ) {
|
2598 |
+
//FB::log($containers, 'Found containers');
|
2599 |
+
$this->sleep_to_maintain_ratio( $get_containers_time, $target_usage_fraction );
|
2600 |
+
|
2601 |
+
foreach ( $containers as $container ) {
|
2602 |
+
$synch_start_time = microtime( true );
|
2603 |
+
|
2604 |
+
//FB::log($container, "Parsing container");
|
2605 |
+
$container->synch();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2606 |
|
2607 |
+
$synch_elapsed_time = microtime( true ) - $synch_start_time;
|
2608 |
+
$blclog->info(
|
2609 |
+
sprintf(
|
2610 |
+
'Parsed container %s[%s] in %.2f ms',
|
2611 |
+
$container->container_type,
|
2612 |
+
$container->container_id,
|
2613 |
+
$synch_elapsed_time * 1000
|
2614 |
+
)
|
2615 |
+
);
|
2616 |
+
|
2617 |
+
//Check if we still have some execution time left
|
2618 |
+
if ( $this->execution_time() > $max_execution_time ) {
|
2619 |
+
//FB::log('The allotted execution time has run out');
|
2620 |
+
blc_cleanup_links();
|
2621 |
+
$this->release_lock();
|
2622 |
+
return;
|
2623 |
+
}
|
2624 |
+
|
2625 |
+
//Check if the server isn't overloaded
|
2626 |
+
if ( $this->server_too_busy() ) {
|
2627 |
+
//FB::log('Server overloaded, bailing out.');
|
2628 |
+
blc_cleanup_links();
|
2629 |
+
$this->release_lock();
|
2630 |
+
return;
|
2631 |
+
}
|
2632 |
+
|
2633 |
+
//Intentionally slow down parsing to reduce the load on the server. Basically,
|
2634 |
+
//we work $target_usage_fraction of the time and sleep the rest of the time.
|
2635 |
+
$this->sleep_to_maintain_ratio( $synch_elapsed_time, $target_usage_fraction );
|
2636 |
+
}
|
2637 |
+
$orphans_possible = true;
|
2638 |
+
|
2639 |
+
$start = microtime( true );
|
2640 |
+
$containers = blcContainerHelper::get_unsynched_containers( $max_containers_per_query );
|
2641 |
+
$get_containers_time = microtime( true ) - $start;
|
2642 |
+
}
|
2643 |
+
|
2644 |
+
//FB::log('No unparsed items found.');
|
2645 |
+
$still_need_resynch = false;
|
2646 |
+
|
2647 |
+
} else {
|
2648 |
+
//FB::log('Resynch not required.');
|
2649 |
+
}
|
2650 |
+
|
2651 |
+
/******************************************
|
2652 |
+
Resynch done?
|
2653 |
+
*******************************************/
|
2654 |
+
if ( $this->conf->options['need_resynch'] && ! $still_need_resynch ) {
|
2655 |
+
$this->conf->options['need_resynch'] = $still_need_resynch;
|
2656 |
$this->conf->save_options();
|
2657 |
}
|
|
|
2658 |
|
2659 |
+
/******************************************
|
2660 |
+
Remove orphaned links
|
2661 |
+
*******************************************/
|
2662 |
|
2663 |
+
if ( $orphans_possible ) {
|
2664 |
+
$start = microtime( true );
|
|
|
2665 |
|
2666 |
+
$blclog->info( 'Removing orphaned links.' );
|
2667 |
+
blc_cleanup_links();
|
2668 |
|
2669 |
+
$get_links_time = microtime( true ) - $start;
|
2670 |
+
$this->sleep_to_maintain_ratio( $get_links_time, $target_usage_fraction );
|
2671 |
+
}
|
|
|
|
|
|
|
|
|
|
|
2672 |
|
2673 |
+
//Check if we still have some execution time left
|
2674 |
+
if ( $this->execution_time() > $max_execution_time ) {
|
2675 |
+
//FB::log('The allotted execution time has run out');
|
2676 |
+
$blclog->info( 'The allotted execution time has run out.' );
|
2677 |
+
$this->release_lock();
|
2678 |
+
return;
|
2679 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2680 |
|
2681 |
+
if ( $this->server_too_busy() ) {
|
2682 |
+
//FB::log('Server overloaded, bailing out.');
|
2683 |
+
$blclog->info( 'Server load too high, stopping.' );
|
2684 |
+
$this->release_lock();
|
2685 |
+
return;
|
2686 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2687 |
|
2688 |
+
/*****************************************
|
2689 |
+
Check links
|
2690 |
+
******************************************/
|
2691 |
+
$max_links_per_query = 30;
|
2692 |
|
2693 |
+
$start = microtime( true );
|
2694 |
+
$links = $this->get_links_to_check( $max_links_per_query );
|
2695 |
+
$get_links_time = microtime( true ) - $start;
|
|
|
2696 |
|
2697 |
+
while ( $links ) {
|
2698 |
+
$this->sleep_to_maintain_ratio( $get_links_time, $target_usage_fraction );
|
|
|
2699 |
|
2700 |
+
//Some unchecked links found
|
2701 |
+
//FB::log("Checking ".count($links)." link(s)");
|
2702 |
+
$blclog->info( 'Checking ' . count( $links ) . ' link(s)' );
|
2703 |
|
2704 |
+
//Randomizing the array reduces the chances that we'll get several links to the same domain in a row.
|
2705 |
+
shuffle( $links );
|
|
|
2706 |
|
2707 |
+
$transactionManager = TransactionManager::getInstance();
|
2708 |
+
$transactionManager->start();
|
|
|
2709 |
|
2710 |
+
foreach ( $links as $link ) {
|
2711 |
+
//Does this link need to be checked? Excluded links aren't checked, but their URLs are still
|
2712 |
+
//tested periodically to see if they're still on the exclusion list.
|
2713 |
+
if ( ! $this->is_excluded( $link->url ) ) {
|
2714 |
+
//Check the link.
|
2715 |
+
//FB::log($link->url, "Checking link {$link->link_id}");
|
2716 |
+
$link->check( true );
|
2717 |
+
} else {
|
2718 |
+
//FB::info("The URL {$link->url} is excluded, skipping link {$link->link_id}.");
|
2719 |
+
$link->last_check_attempt = time();
|
2720 |
+
$link->save();
|
2721 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2722 |
|
2723 |
//Check if we still have some execution time left
|
2724 |
+
if ( $this->execution_time() > $max_execution_time ) {
|
2725 |
+
$transactionManager->commit();
|
2726 |
//FB::log('The allotted execution time has run out');
|
2727 |
+
$blclog->info( 'The allotted execution time has run out.' );
|
2728 |
$this->release_lock();
|
2729 |
return;
|
2730 |
}
|
2731 |
|
2732 |
//Check if the server isn't overloaded
|
2733 |
+
if ( $this->server_too_busy() ) {
|
2734 |
+
$transactionManager->commit();
|
2735 |
//FB::log('Server overloaded, bailing out.');
|
2736 |
+
$blclog->info( 'Server load too high, stopping.' );
|
2737 |
$this->release_lock();
|
2738 |
return;
|
2739 |
}
|
|
|
|
|
|
|
|
|
2740 |
}
|
2741 |
+
$transactionManager->commit();
|
2742 |
|
2743 |
+
$start = microtime( true );
|
2744 |
+
$links = $this->get_links_to_check( $max_links_per_query );
|
2745 |
+
$get_links_time = microtime( true ) - $start;
|
2746 |
}
|
2747 |
+
//FB::log('No links need to be checked right now.');
|
2748 |
|
2749 |
+
$this->release_lock();
|
2750 |
+
$blclog->info( 'work(): All done.' );
|
2751 |
+
//FB::log('All done.');
|
2752 |
+
}
|
2753 |
+
|
2754 |
+
/**
|
2755 |
+
* Sleep long enough to maintain the required $ratio between $elapsed_time and total runtime.
|
2756 |
+
*
|
2757 |
+
* For example, if $ratio is 0.25 and $elapsed_time is 1 second, this method will sleep for 3 seconds.
|
2758 |
+
* Total runtime = 1 + 3 = 4, ratio = 1 / 4 = 0.25.
|
2759 |
+
*
|
2760 |
+
* @param float $elapsed_time
|
2761 |
+
* @param float $ratio
|
2762 |
+
*/
|
2763 |
+
private function sleep_to_maintain_ratio( $elapsed_time, $ratio ) {
|
2764 |
+
if ( ( $ratio <= 0 ) || ( $ratio > 1 ) ) {
|
2765 |
+
return;
|
2766 |
+
}
|
2767 |
+
$sleep_time = $elapsed_time * ( ( 1 / $ratio ) - 1 );
|
2768 |
+
if ( $sleep_time > 0.0001 ) {
|
2769 |
+
/*global $blclog;
|
2770 |
+
$blclog->debug(sprintf(
|
2771 |
+
'Task took %.2f ms, sleeping for %.2f ms',
|
2772 |
+
$elapsed_time * 1000,
|
2773 |
+
$sleep_time * 1000
|
2774 |
+
));*/
|
2775 |
+
usleep( $sleep_time * 1000000 );
|
2776 |
+
}
|
2777 |
}
|
2778 |
|
2779 |
+
/**
|
2780 |
+
* This function is called when the plugin's cron hook executes.
|
2781 |
+
* Its only purpose is to invoke the worker function.
|
2782 |
+
*
|
2783 |
+
* @uses wsBrokenLinkChecker::work()
|
2784 |
+
*
|
2785 |
+
* @return void
|
2786 |
+
*/
|
2787 |
+
function cron_check_links() {
|
2788 |
+
$this->work();
|
2789 |
+
}
|
2790 |
+
|
2791 |
+
/**
|
2792 |
+
* Retrieve links that need to be checked or re-checked.
|
2793 |
+
*
|
2794 |
+
* @param integer $max_results The maximum number of links to return. Defaults to 0 = no limit.
|
2795 |
+
* @param bool $count_only If true, only the number of found links will be returned, not the links themselves.
|
2796 |
+
* @return int|blcLink[]
|
2797 |
+
*/
|
2798 |
+
function get_links_to_check( $max_results = 0, $count_only = false ) {
|
2799 |
+
global $wpdb; /* @var wpdb $wpdb */
|
2800 |
+
|
2801 |
+
$check_threshold = date( 'Y-m-d H:i:s', strtotime( '-' . $this->conf->options['check_threshold'] . ' hours' ) );//phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
|
2802 |
+
$recheck_threshold = date( 'Y-m-d H:i:s', time() - $this->conf->options['recheck_threshold'] );//phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
|
2803 |
+
|
2804 |
+
//FB::log('Looking for links to check (threshold : '.$check_threshold.', recheck_threshold : '.$recheck_threshold.')...');
|
2805 |
+
|
2806 |
+
//Select some links that haven't been checked for a long time or
|
2807 |
+
//that are broken and need to be re-checked again. Links that are
|
2808 |
+
//marked as "being checked" and have been that way for several minutes
|
2809 |
+
//can also be considered broken/buggy, so those will be selected
|
2810 |
+
//as well.
|
2811 |
+
|
2812 |
+
//Only check links that have at least one valid instance (i.e. an instance exists and
|
2813 |
+
//it corresponds to one of the currently loaded container/parser types).
|
2814 |
+
$manager = blcModuleManager::getInstance();
|
2815 |
+
$loaded_containers = $manager->get_escaped_ids( 'container' );
|
2816 |
+
$loaded_parsers = $manager->get_escaped_ids( 'parser' );
|
2817 |
+
|
2818 |
+
//Note : This is a slow query, but AFAIK there is no way to speed it up.
|
2819 |
+
//I could put an index on last_check_attempt, but that value is almost
|
2820 |
+
//certainly unique for each row so it wouldn't be much better than a full table scan.
|
2821 |
+
if ( $count_only ) {
|
2822 |
+
$q = "SELECT COUNT(DISTINCT links.link_id)\n";
|
2823 |
+
} else {
|
2824 |
+
$q = "SELECT DISTINCT links.*\n";
|
2825 |
+
}
|
2826 |
+
$q .= "FROM {$wpdb->prefix}blc_links AS links
|
2827 |
+
INNER JOIN {$wpdb->prefix}blc_instances AS instances USING(link_id)
|
2828 |
+
WHERE
|
2829 |
+
(
|
2830 |
+
( last_check_attempt < %s )
|
2831 |
+
OR
|
2832 |
+
(
|
2833 |
+
(broken = 1 OR being_checked = 1)
|
2834 |
+
AND may_recheck = 1
|
2835 |
+
AND check_count < %d
|
2836 |
+
AND last_check_attempt < %s
|
2837 |
+
)
|
2838 |
+
)
|
2839 |
+
|
2840 |
+
AND
|
2841 |
+
( instances.container_type IN ({$loaded_containers}) )
|
2842 |
+
AND ( instances.parser_type IN ({$loaded_parsers}) )
|
2843 |
+
";
|
2844 |
+
|
2845 |
+
if ( ! $count_only ) {
|
2846 |
+
$q .= "\nORDER BY last_check_attempt ASC\n";
|
2847 |
+
if ( ! empty( $max_results ) ) {
|
2848 |
+
$q .= 'LIMIT ' . intval( $max_results );
|
2849 |
+
}
|
2850 |
+
}
|
2851 |
|
2852 |
+
$link_q = $wpdb->prepare(
|
2853 |
+
$q, //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
2854 |
+
$check_threshold,
|
2855 |
+
$this->conf->options['recheck_count'],
|
2856 |
+
$recheck_threshold
|
2857 |
+
);
|
2858 |
|
2859 |
+
//FB::log($link_q, "Find links to check");
|
2860 |
+
//$blclog->debug("Find links to check: \n" . $link_q);
|
2861 |
|
2862 |
+
//If we just need the number of links, retrieve it and return
|
2863 |
+
if ( $count_only ) {
|
2864 |
+
return $wpdb->get_var( $link_q );//phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
2865 |
+
}
|
2866 |
|
2867 |
+
//Fetch the link data
|
2868 |
+
$link_data = $wpdb->get_results( $link_q, ARRAY_A ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
2869 |
+
if ( empty( $link_data ) ) {
|
2870 |
+
return array();
|
2871 |
+
}
|
2872 |
|
2873 |
+
//Instantiate blcLink objects for all fetched links
|
2874 |
+
$links = array();
|
2875 |
+
foreach ( $link_data as $data ) {
|
2876 |
+
$links[] = new blcLink( $data );
|
2877 |
+
}
|
|
|
|
|
2878 |
|
2879 |
+
return $links;
|
|
|
|
|
|
|
|
|
2880 |
}
|
2881 |
|
2882 |
+
/**
|
2883 |
+
* Output the current link checker status in JSON format.
|
2884 |
+
* Ajax hook for the 'blc_full_status' action.
|
2885 |
+
*
|
2886 |
+
* @return void
|
2887 |
+
*/
|
2888 |
+
function ajax_full_status() {
|
2889 |
+
$status = $this->get_status();
|
2890 |
+
$text = $this->status_text( $status );
|
2891 |
+
|
2892 |
+
echo json_encode(
|
2893 |
+
array(
|
2894 |
+
'text' => $text,
|
2895 |
+
'status' => $status,
|
2896 |
+
)
|
2897 |
+
);
|
2898 |
|
2899 |
+
die();
|
2900 |
+
}
|
|
|
2901 |
|
2902 |
+
/**
|
2903 |
+
* Generates a status message based on the status info in $status
|
2904 |
+
*
|
2905 |
+
* @param array $status
|
2906 |
+
* @return string
|
2907 |
+
*/
|
2908 |
+
function status_text( $status ) {
|
2909 |
+
$text = '';
|
2910 |
|
2911 |
+
if ( $status['broken_links'] > 0 ) {
|
2912 |
+
$text .= sprintf(
|
2913 |
+
"<a href='%s' title='" . __( 'View broken links', 'broken-link-checker' ) . "'><strong>" .
|
2914 |
+
_n( 'Found %d broken link', 'Found %d broken links', $status['broken_links'], 'broken-link-checker' ) .
|
2915 |
+
'</strong></a>',
|
2916 |
+
esc_attr( admin_url( 'tools.php?page=view-broken-links' ) ),
|
2917 |
+
$status['broken_links']
|
2918 |
+
);
|
2919 |
+
} else {
|
2920 |
+
$text .= __( 'No broken links found.', 'broken-link-checker' );
|
2921 |
+
}
|
2922 |
|
2923 |
+
$text .= '<br/>';
|
|
|
2924 |
|
2925 |
+
if ( $status['unchecked_links'] > 0 ) {
|
2926 |
+
$text .= sprintf(
|
2927 |
+
_n( '%d URL in the work queue', '%d URLs in the work queue', $status['unchecked_links'], 'broken-link-checker' ),
|
2928 |
+
$status['unchecked_links']
|
2929 |
+
);
|
2930 |
+
} else {
|
2931 |
+
$text .= __( 'No URLs in the work queue.', 'broken-link-checker' );
|
2932 |
+
}
|
2933 |
+
|
2934 |
+
$text .= '<br/>';
|
2935 |
+
if ( $status['known_links'] > 0 ) {
|
2936 |
+
$url_count = sprintf(
|
2937 |
+
_nx( '%d unique URL', '%d unique URLs', $status['known_links'], 'for the "Detected X unique URLs in Y links" message', 'broken-link-checker' ),
|
2938 |
+
$status['known_links']
|
2939 |
+
);
|
2940 |
+
$link_count = sprintf(
|
2941 |
+
_nx( '%d link', '%d links', $status['known_instances'], 'for the "Detected X unique URLs in Y links" message', 'broken-link-checker' ),
|
2942 |
+
$status['known_instances']
|
2943 |
+
);
|
2944 |
|
2945 |
+
if ( $this->conf->options['need_resynch'] ) {
|
2946 |
+
$text .= sprintf(
|
2947 |
+
__( 'Detected %1$s in %2$s and still searching...', 'broken-link-checker' ),
|
2948 |
+
$url_count,
|
2949 |
+
$link_count
|
2950 |
+
);
|
|
|
2951 |
} else {
|
2952 |
+
$text .= sprintf(
|
2953 |
+
__( 'Detected %1$s in %2$s.', 'broken-link-checker' ),
|
2954 |
+
$url_count,
|
2955 |
+
$link_count
|
2956 |
+
);
|
2957 |
}
|
2958 |
+
} else {
|
2959 |
+
if ( $this->conf->options['need_resynch'] ) {
|
2960 |
+
$text .= __( 'Searching your blog for links...', 'broken-link-checker' );
|
2961 |
+
} else {
|
2962 |
+
$text .= __( 'No links detected.', 'broken-link-checker' );
|
|
|
|
|
|
|
2963 |
}
|
2964 |
+
}
|
2965 |
|
2966 |
+
return $text;
|
2967 |
+
}
|
2968 |
+
|
2969 |
+
/**
|
2970 |
+
* @uses wsBrokenLinkChecker::ajax_full_status()
|
2971 |
+
*
|
2972 |
+
* @return void
|
2973 |
+
*/
|
2974 |
+
function ajax_dashboard_status() {
|
2975 |
+
//Just display the full status.
|
2976 |
+
$this->ajax_full_status();
|
2977 |
+
}
|
2978 |
+
|
2979 |
+
/**
|
2980 |
+
* Output the current average server load (over the last one-minute period).
|
2981 |
+
* Called via AJAX.
|
2982 |
+
*
|
2983 |
+
* @return void
|
2984 |
+
*/
|
2985 |
+
function ajax_current_load() {
|
2986 |
+
$load = blcUtility::get_server_load();
|
2987 |
+
if ( empty( $load ) ) {
|
2988 |
+
die( _x( 'Unknown', 'current load', 'broken-link-checker' ) );
|
2989 |
}
|
|
|
2990 |
|
2991 |
+
$one_minute = reset( $load );
|
2992 |
+
printf( '%.2f', $one_minute );
|
2993 |
+
die();
|
2994 |
}
|
|
|
2995 |
|
2996 |
+
/**
|
2997 |
+
* Returns an array with various status information about the plugin. Array key reference:
|
2998 |
+
* check_threshold - date/time; links checked before this threshold should be checked again.
|
2999 |
+
* recheck_threshold - date/time; broken links checked before this threshold should be re-checked.
|
3000 |
+
* known_links - the number of detected unique URLs (a misleading name, yes).
|
3001 |
+
* known_instances - the number of detected link instances, i.e. actual link elements in posts and other places.
|
3002 |
+
* broken_links - the number of detected broken links.
|
3003 |
+
* unchecked_links - the number of URLs that need to be checked ASAP; based on check_threshold and recheck_threshold.
|
3004 |
+
*
|
3005 |
+
* @return array
|
3006 |
+
*/
|
3007 |
+
function get_status() {
|
3008 |
+
$blc_link_query = blcLinkQuery::getInstance();
|
3009 |
|
3010 |
+
$check_threshold = date( 'Y-m-d H:i:s', strtotime( '-' . $this->conf->options['check_threshold'] . ' hours' ) );//phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
|
3011 |
+
$recheck_threshold = date( 'Y-m-d H:i:s', time() - $this->conf->options['recheck_threshold'] );//phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3012 |
|
3013 |
+
$known_links = blc_get_links( array( 'count_only' => true ) );
|
3014 |
+
$known_instances = blc_get_usable_instance_count();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3015 |
|
3016 |
+
$broken_links = $blc_link_query->get_filter_links( 'broken', array( 'count_only' => true ) );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3017 |
|
3018 |
+
$unchecked_links = $this->get_links_to_check( 0, true );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3019 |
|
3020 |
+
return array(
|
3021 |
+
'check_threshold' => $check_threshold,
|
3022 |
+
'recheck_threshold' => $recheck_threshold,
|
3023 |
+
'known_links' => $known_links,
|
3024 |
+
'known_instances' => $known_instances,
|
3025 |
+
'broken_links' => $broken_links,
|
3026 |
+
'unchecked_links' => $unchecked_links,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3027 |
);
|
|
|
|
|
3028 |
}
|
3029 |
|
3030 |
+
function ajax_work() {
|
3031 |
+
check_ajax_referer( 'blc_work' );
|
3032 |
|
3033 |
+
//Run the worker function
|
3034 |
+
$this->work();
|
3035 |
+
die();
|
|
|
|
|
|
|
3036 |
}
|
3037 |
|
3038 |
+
/**
|
3039 |
+
* AJAX hook for the "Not broken" button. Marks a link as broken and as a likely false positive.
|
3040 |
+
*
|
3041 |
+
* @return void
|
3042 |
+
*/
|
3043 |
+
function ajax_discard() {
|
3044 |
+
if ( ! current_user_can( 'edit_others_posts' ) || ! check_ajax_referer( 'blc_discard', false, false ) ) {
|
3045 |
+
die( __( "You're not allowed to do that!", 'broken-link-checker' ) );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3046 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3047 |
|
3048 |
+
if ( isset( $_POST['link_id'] ) ) {
|
3049 |
+
//Load the link
|
3050 |
+
$link = new blcLink( intval( $_POST['link_id'] ) );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3051 |
|
3052 |
+
if ( ! $link->valid() ) {
|
3053 |
+
printf( __( "Oops, I can't find the link %d", 'broken-link-checker' ), intval( $_POST['link_id'] ) );
|
3054 |
+
die();
|
3055 |
+
}
|
3056 |
+
//Make it appear "not broken"
|
3057 |
+
$link->broken = false;
|
3058 |
+
$link->warning = false;
|
3059 |
+
$link->false_positive = true;
|
3060 |
+
$link->last_check_attempt = time();
|
3061 |
+
$link->log = __( 'This link was manually marked as working by the user.', 'broken-link-checker' );
|
3062 |
|
3063 |
+
$link->isOptionLinkChanged = true;
|
3064 |
|
3065 |
+
$transactionManager = TransactionManager::getInstance();
|
3066 |
+
$transactionManager->start();
|
3067 |
|
3068 |
+
//Save the changes
|
3069 |
+
if ( $link->save() ) {
|
3070 |
+
$transactionManager->commit();
|
3071 |
+
die( 'OK' );
|
3072 |
+
} else {
|
3073 |
+
die( __( "Oops, couldn't modify the link!", 'broken-link-checker' ) );
|
3074 |
+
}
|
3075 |
} else {
|
3076 |
+
die( __( 'Error : link_id not specified', 'broken-link-checker' ) );
|
3077 |
}
|
|
|
|
|
3078 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3079 |
|
3080 |
+
public function ajax_dismiss() {
|
3081 |
+
$this->ajax_set_link_dismissed( true );
|
3082 |
+
}
|
3083 |
|
3084 |
+
public function ajax_undismiss() {
|
3085 |
+
$this->ajax_set_link_dismissed( false );
|
3086 |
}
|
3087 |
|
3088 |
+
private function ajax_set_link_dismissed( $dismiss ) {
|
3089 |
+
$action = $dismiss ? 'blc_dismiss' : 'blc_undismiss';
|
|
|
3090 |
|
3091 |
+
if ( ! current_user_can( 'edit_others_posts' ) || ! check_ajax_referer( $action, false, false ) ) {
|
3092 |
+
die( __( "You're not allowed to do that!", 'broken-link-checker' ) );
|
|
|
3093 |
}
|
3094 |
|
3095 |
+
if ( isset( $_POST['link_id'] ) ) {
|
3096 |
+
//Load the link
|
3097 |
+
$link = new blcLink( intval( $_POST['link_id'] ) );
|
3098 |
|
3099 |
+
if ( ! $link->valid() ) {
|
3100 |
+
printf( __( "Oops, I can't find the link %d", 'broken-link-checker' ), intval( $_POST['link_id'] ) );
|
3101 |
+
die();
|
3102 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3103 |
|
3104 |
+
$link->dismissed = $dismiss;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3105 |
|
3106 |
+
//Save the changes
|
3107 |
+
$link->isOptionLinkChanged = true;
|
3108 |
+
$transactionManager = TransactionManager::getInstance();
|
3109 |
+
$transactionManager->start();
|
3110 |
+
if ( $link->save() ) {
|
3111 |
+
$transactionManager->commit();
|
3112 |
+
die( 'OK' );
|
3113 |
+
} else {
|
3114 |
+
die( __( "Oops, couldn't modify the link!", 'broken-link-checker' ) );
|
3115 |
+
}
|
3116 |
+
} else {
|
3117 |
+
die( __( 'Error : link_id not specified', 'broken-link-checker' ) );
|
3118 |
+
}
|
3119 |
}
|
3120 |
|
3121 |
+
/**
|
3122 |
+
* AJAX hook for the inline link editor on Tools -> Broken Links.
|
3123 |
+
*
|
3124 |
+
* @return void
|
3125 |
+
*/
|
3126 |
+
function ajax_edit() {
|
3127 |
+
if ( ! current_user_can( 'edit_others_posts' ) || ! check_ajax_referer( 'blc_edit', false, false ) ) {
|
3128 |
+
die(
|
3129 |
+
json_encode(
|
3130 |
+
array(
|
3131 |
+
'error' => __( "You're not allowed to do that!", 'broken-link-checker' ),
|
3132 |
+
)
|
3133 |
+
)
|
3134 |
+
);
|
3135 |
+
}
|
3136 |
|
3137 |
+
if ( empty( $_POST['link_id'] ) || empty( $_POST['new_url'] ) || ! is_numeric( $_POST['link_id'] ) ) {
|
3138 |
+
die(
|
3139 |
+
json_encode(
|
3140 |
+
array(
|
3141 |
+
'error' => __( 'Error : link_id or new_url not specified', 'broken-link-checker' ),
|
3142 |
+
)
|
3143 |
+
)
|
3144 |
+
);
|
3145 |
+
}
|
3146 |
|
3147 |
+
//Load the link
|
3148 |
+
$link = new blcLink( intval( $_POST['link_id'] ) );
|
3149 |
+
|
3150 |
+
if ( ! $link->valid() ) {
|
3151 |
+
die(
|
3152 |
+
json_encode(
|
3153 |
+
array(
|
3154 |
+
'error' => sprintf( __( "Oops, I can't find the link %d", 'broken-link-checker' ), intval( $_POST['link_id'] ) ),
|
3155 |
+
)
|
3156 |
+
)
|
3157 |
+
);
|
3158 |
}
|
|
|
3159 |
|
3160 |
+
//Validate the new URL.
|
3161 |
+
$new_url = stripslashes( $_POST['new_url'] );
|
3162 |
+
$parsed = @parse_url( $new_url );
|
3163 |
+
if ( ! $parsed ) {
|
3164 |
+
die(
|
3165 |
+
json_encode(
|
3166 |
+
array(
|
3167 |
+
'error' => __( 'Oops, the new URL is invalid!', 'broken-link-checker' ),
|
3168 |
+
)
|
3169 |
+
)
|
3170 |
+
);
|
3171 |
+
}
|
3172 |
|
3173 |
+
if ( ! current_user_can( 'unfiltered_html' ) ) {
|
3174 |
+
//Disallow potentially dangerous URLs like "javascript:...".
|
3175 |
+
$protocols = wp_allowed_protocols();
|
3176 |
+
$good_protocol_url = wp_kses_bad_protocol( $new_url, $protocols );
|
3177 |
+
if ( $new_url != $good_protocol_url ) {
|
3178 |
+
die(
|
3179 |
+
json_encode(
|
3180 |
+
array(
|
3181 |
+
'error' => __( 'Oops, the new URL is invalid!', 'broken-link-checker' ),
|
3182 |
+
)
|
3183 |
+
)
|
3184 |
+
);
|
|
|
|
|
3185 |
}
|
3186 |
}
|
3187 |
|
3188 |
+
$new_text = ( isset( $_POST['new_text'] ) && is_string( $_POST['new_text'] ) ) ? stripslashes( $_POST['new_text'] ) : null;
|
3189 |
+
if ( '' === $new_text ) {
|
3190 |
+
$new_text = null;
|
3191 |
+
}
|
3192 |
+
if ( ! empty( $new_text ) && ! current_user_can( 'unfiltered_html' ) ) {
|
3193 |
+
$new_text = stripslashes( wp_filter_post_kses( addslashes( $new_text ) ) ); //wp_filter_post_kses expects slashed data.
|
3194 |
+
}
|
3195 |
+
|
3196 |
+
$rez = $link->edit( $new_url, $new_text );
|
3197 |
+
if ( false === $rez ) {
|
3198 |
+
die(
|
3199 |
+
json_encode(
|
3200 |
+
array(
|
3201 |
+
'error' => __( 'An unexpected error occurred!', 'broken-link-checker' ),
|
3202 |
+
)
|
3203 |
+
)
|
3204 |
+
);
|
3205 |
+
} else {
|
3206 |
+
$new_link = $rez['new_link']; /** @var blcLink $new_link */
|
3207 |
+
$new_status = $new_link->analyse_status();
|
3208 |
+
$ui_link_text = null;
|
3209 |
+
if ( isset( $new_text ) ) {
|
3210 |
+
$instances = $new_link->get_instances();
|
3211 |
+
if ( ! empty( $instances ) ) {
|
3212 |
+
$first_instance = reset( $instances );
|
3213 |
+
$ui_link_text = $first_instance->ui_get_link_text();
|
3214 |
+
}
|
3215 |
+
}
|
3216 |
|
3217 |
+
$response = array(
|
3218 |
+
'new_link_id' => $rez['new_link_id'],
|
3219 |
+
'cnt_okay' => $rez['cnt_okay'],
|
3220 |
+
'cnt_error' => $rez['cnt_error'],
|
3221 |
+
|
3222 |
+
'status_text' => $new_status['text'],
|
3223 |
+
'status_code' => $new_status['code'],
|
3224 |
+
'http_code' => empty( $new_link->http_code ) ? '' : $new_link->http_code,
|
3225 |
+
'redirect_count' => $new_link->redirect_count,
|
3226 |
+
|
3227 |
+
'url' => $new_link->url,
|
3228 |
+
'escaped_url' => esc_url_raw( $new_link->url ),
|
3229 |
+
'final_url' => $new_link->final_url,
|
3230 |
+
'link_text' => isset( $new_text ) ? $new_text : null,
|
3231 |
+
'ui_link_text' => isset( $new_text ) ? $ui_link_text : null,
|
3232 |
+
|
3233 |
+
'errors' => array(),
|
3234 |
+
);
|
3235 |
+
//url, status text, status code, link text, editable link text
|
3236 |
|
3237 |
+
foreach ( $rez['errors'] as $error ) { /** @var $error WP_Error */
|
3238 |
+
array_push( $response['errors'], implode( ', ', $error->get_error_messages() ) );
|
3239 |
+
}
|
3240 |
+
die( json_encode( $response ) );
|
3241 |
}
|
|
|
3242 |
}
|
|
|
3243 |
|
3244 |
+
/**
|
3245 |
+
* AJAX hook for the "Unlink" action links in Tools -> Broken Links.
|
3246 |
+
* Removes the specified link from all posts and other supported items.
|
3247 |
+
*
|
3248 |
+
* @return void
|
3249 |
+
*/
|
3250 |
+
function ajax_unlink() {
|
3251 |
+
if ( ! current_user_can( 'edit_others_posts' ) || ! check_ajax_referer( 'blc_unlink', false, false ) ) {
|
3252 |
+
die(
|
3253 |
+
json_encode(
|
3254 |
+
array(
|
3255 |
+
'error' => __( "You're not allowed to do that!", 'broken-link-checker' ),
|
3256 |
+
)
|
3257 |
+
)
|
3258 |
+
);
|
3259 |
+
}
|
3260 |
|
3261 |
+
if ( isset( $_POST['link_id'] ) ) {
|
3262 |
+
//Load the link
|
3263 |
+
$link = new blcLink( intval( $_POST['link_id'] ) );
|
3264 |
+
|
3265 |
+
if ( ! $link->valid() ) {
|
3266 |
+
die(
|
3267 |
+
json_encode(
|
3268 |
+
array(
|
3269 |
+
'error' => sprintf( __( "Oops, I can't find the link %d", 'broken-link-checker' ), intval( $_POST['link_id'] ) ),
|
3270 |
+
)
|
3271 |
+
)
|
3272 |
+
);
|
3273 |
+
}
|
3274 |
|
3275 |
+
//Try and unlink it
|
3276 |
+
$rez = $link->unlink();
|
|
|
|
|
|
|
3277 |
|
3278 |
+
if ( false === $rez ) {
|
3279 |
+
die(
|
3280 |
+
json_encode(
|
3281 |
+
array(
|
3282 |
+
'error' => __( 'An unexpected error occured!', 'broken-link-checker' ),
|
3283 |
+
)
|
3284 |
+
)
|
3285 |
+
);
|
3286 |
+
} else {
|
3287 |
+
$response = array(
|
3288 |
+
'cnt_okay' => $rez['cnt_okay'],
|
3289 |
+
'cnt_error' => $rez['cnt_error'],
|
3290 |
+
'errors' => array(),
|
3291 |
+
);
|
3292 |
+
foreach ( $rez['errors'] as $error ) { /** @var WP_Error $error */
|
3293 |
+
array_push( $response['errors'], implode( ', ', $error->get_error_messages() ) );
|
3294 |
+
}
|
3295 |
|
3296 |
+
die( json_encode( $response ) );
|
3297 |
+
}
|
|
|
|
|
3298 |
} else {
|
3299 |
+
die(
|
3300 |
+
json_encode(
|
3301 |
+
array(
|
3302 |
+
'error' => __( 'Error : link_id not specified', 'broken-link-checker' ),
|
3303 |
+
)
|
3304 |
+
)
|
3305 |
);
|
|
|
|
|
|
|
|
|
|
|
3306 |
}
|
|
|
|
|
|
|
|
|
|
|
3307 |
}
|
|
|
3308 |
|
3309 |
+
public function ajax_deredirect() {
|
3310 |
+
if ( ! current_user_can( 'edit_others_posts' ) || ! check_ajax_referer( 'blc_deredirect', false, false ) ) {
|
3311 |
+
die(
|
3312 |
+
json_encode(
|
3313 |
+
array(
|
3314 |
+
'error' => __( "You're not allowed to do that!", 'broken-link-checker' ),
|
3315 |
+
)
|
3316 |
+
)
|
3317 |
+
);
|
3318 |
+
}
|
3319 |
|
3320 |
+
if ( ! isset( $_POST['link_id'] ) || ! is_numeric( $_POST['link_id'] ) ) {
|
3321 |
+
die(
|
3322 |
+
json_encode(
|
3323 |
+
array(
|
3324 |
+
'error' => __( 'Error : link_id not specified', 'broken-link-checker' ),
|
3325 |
+
)
|
3326 |
+
)
|
3327 |
+
);
|
3328 |
+
}
|
3329 |
|
3330 |
+
$id = intval( $_POST['link_id'] );
|
3331 |
+
$link = new blcLink( $id );
|
3332 |
|
3333 |
+
if ( ! $link->valid() ) {
|
3334 |
+
die(
|
3335 |
+
json_encode(
|
3336 |
+
array(
|
3337 |
+
'error' => sprintf( __( "Oops, I can't find the link %d", 'broken-link-checker' ), $id ),
|
3338 |
+
)
|
3339 |
+
)
|
3340 |
+
);
|
3341 |
+
}
|
3342 |
|
3343 |
+
//The actual task is simple; it's error handling that's complicated.
|
3344 |
+
$result = $link->deredirect();
|
3345 |
+
if ( is_wp_error( $result ) ) {
|
3346 |
+
die(
|
3347 |
+
json_encode(
|
3348 |
+
array(
|
3349 |
+
'error' => sprintf( '%s [%s]', $result->get_error_message(), $result->get_error_code() ),
|
3350 |
+
)
|
3351 |
+
)
|
3352 |
+
);
|
3353 |
+
}
|
3354 |
|
3355 |
+
$link = $result['new_link']; /** @var blcLink $link */
|
3356 |
|
3357 |
+
$status = $link->analyse_status();
|
3358 |
+
$response = array(
|
3359 |
+
'url' => $link->url,
|
3360 |
+
'escaped_url' => esc_url_raw( $link->url ),
|
3361 |
+
'new_link_id' => $result['new_link_id'],
|
3362 |
+
|
3363 |
+
'status_text' => $status['text'],
|
3364 |
+
'status_code' => $status['code'],
|
3365 |
+
'http_code' => empty( $link->http_code ) ? '' : $link->http_code,
|
3366 |
+
'redirect_count' => $link->redirect_count,
|
3367 |
+
'final_url' => $link->final_url,
|
3368 |
+
|
3369 |
+
'cnt_okay' => $result['cnt_okay'],
|
3370 |
+
'cnt_error' => $result['cnt_error'],
|
3371 |
+
'errors' => array(),
|
3372 |
+
);
|
3373 |
|
3374 |
+
//Convert WP_Error's to simple strings.
|
3375 |
+
if ( ! empty( $result['errors'] ) ) {
|
3376 |
+
foreach ( $result['errors'] as $error ) { /** @var WP_Error $error */
|
3377 |
+
$response['errors'][] = $error->get_error_message();
|
3378 |
+
}
|
3379 |
+
}
|
3380 |
|
3381 |
+
die( json_encode( $response ) );
|
3382 |
+
}
|
|
|
|
|
3383 |
|
3384 |
+
/**
|
3385 |
+
* AJAX hook for the "Recheck" action.
|
3386 |
+
*/
|
3387 |
+
public function ajax_recheck() {
|
3388 |
+
if ( ! current_user_can( 'edit_others_posts' ) || ! check_ajax_referer( 'blc_recheck', false, false ) ) {
|
3389 |
+
die(
|
3390 |
+
json_encode(
|
3391 |
+
array(
|
3392 |
+
'error' => __( "You're not allowed to do that!", 'broken-link-checker' ),
|
3393 |
+
)
|
3394 |
+
)
|
3395 |
+
);
|
3396 |
}
|
|
|
3397 |
|
3398 |
+
if ( ! isset( $_POST['link_id'] ) || ! is_numeric( $_POST['link_id'] ) ) {
|
3399 |
+
die(
|
3400 |
+
json_encode(
|
3401 |
+
array(
|
3402 |
+
'error' => __( 'Error : link_id not specified', 'broken-link-checker' ),
|
3403 |
+
)
|
3404 |
+
)
|
3405 |
+
);
|
3406 |
+
}
|
3407 |
|
3408 |
+
$id = intval( $_POST['link_id'] );
|
3409 |
+
$link = new blcLink( $id );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3410 |
|
3411 |
+
if ( ! $link->valid() ) {
|
3412 |
+
die(
|
3413 |
+
json_encode(
|
3414 |
+
array(
|
3415 |
+
'error' => sprintf( __( "Oops, I can't find the link %d", 'broken-link-checker' ), $id ),
|
3416 |
+
)
|
3417 |
+
)
|
3418 |
+
);
|
3419 |
+
}
|
3420 |
|
3421 |
+
$transactionManager = TransactionManager::getInstance();
|
3422 |
+
$transactionManager->start();
|
3423 |
|
3424 |
+
//In case the immediate check fails, this will ensure the link is checked during the next work() run.
|
3425 |
+
$link->last_check_attempt = 0;
|
3426 |
+
$link->isOptionLinkChanged = true;
|
3427 |
+
$link->save();
|
|
|
3428 |
|
3429 |
+
//Check the link and save the results.
|
3430 |
+
$link->check( true );
|
3431 |
|
3432 |
+
$transactionManager->commit();
|
|
|
|
|
|
|
3433 |
|
3434 |
+
$status = $link->analyse_status();
|
3435 |
+
$response = array(
|
3436 |
+
'status_text' => $status['text'],
|
3437 |
+
'status_code' => $status['code'],
|
3438 |
+
'http_code' => empty( $link->http_code ) ? '' : $link->http_code,
|
3439 |
+
'redirect_count' => $link->redirect_count,
|
3440 |
+
'final_url' => $link->final_url,
|
3441 |
+
);
|
3442 |
|
3443 |
+
die( json_encode( $response ) );
|
3444 |
+
}
|
3445 |
|
3446 |
+
function ajax_link_details() {
|
3447 |
+
global $wpdb; /* @var wpdb $wpdb */
|
|
|
|
|
|
|
|
|
|
|
|
|
3448 |
|
3449 |
+
if ( ! current_user_can( 'edit_others_posts' ) ) {
|
3450 |
+
die( __( "You don't have sufficient privileges to access this information!", 'broken-link-checker' ) );
|
3451 |
+
}
|
3452 |
|
3453 |
+
//FB::log("Loading link details via AJAX");
|
|
|
3454 |
|
3455 |
+
if ( isset( $_GET['link_id'] ) ) {
|
3456 |
+
//FB::info("Link ID found in GET");
|
3457 |
+
$link_id = intval( $_GET['link_id'] );
|
3458 |
+
} elseif ( isset( $_POST['link_id'] ) ) {
|
3459 |
+
//FB::info("Link ID found in POST");
|
3460 |
+
$link_id = intval( $_POST['link_id'] );
|
3461 |
+
} else {
|
3462 |
+
//FB::error('Link ID not specified, you hacking bastard.');
|
3463 |
+
die( __( 'Error : link ID not specified', 'broken-link-checker' ) );
|
3464 |
+
}
|
3465 |
|
3466 |
+
//Load the link.
|
3467 |
+
$link = new blcLink( $link_id );
|
3468 |
|
3469 |
+
if ( ! $link->is_new ) {
|
3470 |
+
//FB::info($link, 'Link loaded');
|
3471 |
+
if ( ! class_exists( 'blcTablePrinter' ) ) {
|
3472 |
+
require dirname( $this->loader ) . '/includes/admin/table-printer.php';
|
3473 |
+
}
|
3474 |
+
blcTablePrinter::details_row_contents( $link );
|
3475 |
+
die();
|
3476 |
+
} else {
|
3477 |
+
printf( __( 'Failed to load link details (%s)', 'broken-link-checker' ), $wpdb->last_error );
|
3478 |
+
die();
|
3479 |
+
}
|
3480 |
}
|
3481 |
|
3482 |
+
/**
|
3483 |
+
* Acquire an exclusive lock.
|
3484 |
+
* If we already hold a lock, it will be released and a new one will be acquired.
|
3485 |
+
*
|
3486 |
+
* @return bool
|
3487 |
+
*/
|
3488 |
+
function acquire_lock() {
|
3489 |
+
return WPMutex::acquire( 'blc_lock' );
|
3490 |
+
}
|
3491 |
+
|
3492 |
+
/**
|
3493 |
+
* Relese our exclusive lock.
|
3494 |
+
* Does nothing if the lock has already been released.
|
3495 |
+
*
|
3496 |
+
* @return bool
|
3497 |
+
*/
|
3498 |
+
function release_lock() {
|
3499 |
+
return WPMutex::release( 'blc_lock' );
|
3500 |
+
}
|
3501 |
+
|
3502 |
+
/**
|
3503 |
+
* Check if server is currently too overloaded to run the link checker.
|
3504 |
+
*
|
3505 |
+
* @return bool
|
3506 |
+
*/
|
3507 |
+
function server_too_busy() {
|
3508 |
+
if ( ! $this->conf->options['enable_load_limit'] || ! isset( $this->conf->options['server_load_limit'] ) ) {
|
3509 |
+
return false;
|
3510 |
+
}
|
3511 |
|
3512 |
+
$loads = blcUtility::get_server_load();
|
3513 |
+
if ( empty( $loads ) ) {
|
3514 |
+
return false;
|
3515 |
+
}
|
3516 |
+
$one_minute = floatval( reset( $loads ) );
|
3517 |
+
|
3518 |
+
return $one_minute > $this->conf->options['server_load_limit'];
|
3519 |
+
}
|
3520 |
+
|
3521 |
+
/**
|
3522 |
+
* Register BLC's Dashboard widget
|
3523 |
+
*
|
3524 |
+
* @return void
|
3525 |
+
*/
|
3526 |
+
function hook_wp_dashboard_setup() {
|
3527 |
+
$show_widget = current_user_can( $this->conf->get( 'dashboard_widget_capability', 'edit_others_posts' ) );
|
3528 |
+
if ( function_exists( 'wp_add_dashboard_widget' ) && $show_widget ) {
|
3529 |
+
wp_add_dashboard_widget(
|
3530 |
+
'blc_dashboard_widget',
|
3531 |
+
__( 'Broken Link Checker', 'broken-link-checker' ),
|
3532 |
+
array( $this, 'dashboard_widget' ),
|
3533 |
+
array( $this, 'dashboard_widget_control' )
|
3534 |
+
);
|
3535 |
}
|
|
|
|
|
|
|
|
|
|
|
3536 |
}
|
|
|
3537 |
|
3538 |
+
/**
|
3539 |
+
* Collect various debugging information and return it in an associative array
|
3540 |
+
*
|
3541 |
+
* @return array
|
3542 |
+
*/
|
3543 |
+
function get_debug_info() {
|
3544 |
+
/** @var wpdb $wpdb */
|
3545 |
+
global $wpdb;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3546 |
|
3547 |
+
//Collect some information that's useful for debugging
|
3548 |
+
$debug = array();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3549 |
|
3550 |
+
//PHP version. Any one is fine as long as WP supports it.
|
3551 |
+
$debug[ __( 'PHP version', 'broken-link-checker' ) ] = array(
|
3552 |
+
'state' => 'ok',
|
3553 |
+
'value' => phpversion(),
|
3554 |
+
);
|
3555 |
|
3556 |
+
//MySQL version
|
3557 |
+
$debug[ __( 'MySQL version', 'broken-link-checker' ) ] = array(
|
3558 |
+
'state' => 'ok',
|
3559 |
+
'value' => $wpdb->db_version(),
|
3560 |
+
);
|
3561 |
|
3562 |
+
//CURL presence and version
|
3563 |
+
if ( function_exists( 'curl_version' ) ) {
|
3564 |
+
$version = curl_version();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3565 |
|
3566 |
+
if ( version_compare( $version['version'], '7.16.0', '<=' ) ) {
|
3567 |
+
$data = array(
|
3568 |
+
'state' => 'warning',
|
3569 |
+
'value' => $version['version'],
|
3570 |
+
'message' => __( 'You have an old version of CURL. Redirect detection may not work properly.', 'broken-link-checker' ),
|
3571 |
+
);
|
3572 |
+
} else {
|
3573 |
+
$data = array(
|
3574 |
+
'state' => 'ok',
|
3575 |
+
'value' => $version['version'],
|
3576 |
+
);
|
3577 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3578 |
} else {
|
3579 |
$data = array(
|
3580 |
+
'state' => 'warning',
|
3581 |
+
'value' => __( 'Not installed', 'broken-link-checker' ),
|
3582 |
);
|
3583 |
}
|
3584 |
+
$debug[ __( 'CURL version', 'broken-link-checker' ) ] = $data;
|
3585 |
|
3586 |
+
//Snoopy presence
|
3587 |
+
if ( class_exists( 'Snoopy' ) || file_exists( ABSPATH . WPINC . '/class-snoopy.php' ) ) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3588 |
$data = array(
|
3589 |
'state' => 'ok',
|
3590 |
+
'value' => __( 'Installed', 'broken-link-checker' ),
|
3591 |
);
|
3592 |
} else {
|
3593 |
+
//No Snoopy? This should never happen, but if it does we *must* have CURL.
|
3594 |
+
if ( function_exists( 'curl_init' ) ) {
|
3595 |
+
$data = array(
|
3596 |
+
'state' => 'ok',
|
3597 |
+
'value' => __( 'Not installed', 'broken-link-checker' ),
|
3598 |
+
);
|
3599 |
+
} else {
|
3600 |
+
$data = array(
|
3601 |
+
'state' => 'error',
|
3602 |
+
'value' => __( 'Not installed', 'broken-link-checker' ),
|
3603 |
+
'message' => __( 'You must have either CURL or Snoopy installed for the plugin to work!', 'broken-link-checker' ),
|
3604 |
+
);
|
3605 |
+
}
|
3606 |
+
}
|
3607 |
+
$debug['Snoopy'] = $data;
|
3608 |
+
|
3609 |
+
//Safe_mode status
|
3610 |
+
if ( blcUtility::is_safe_mode() ) {
|
3611 |
+
$debug['Safe mode'] = array(
|
3612 |
+
'state' => 'warning',
|
3613 |
+
'value' => __( 'On', 'broken-link-checker' ),
|
3614 |
+
'message' => __( 'Redirects may be detected as broken links when safe_mode is on.', 'broken-link-checker' ),
|
3615 |
+
);
|
3616 |
+
} else {
|
3617 |
+
$debug['Safe mode'] = array(
|
3618 |
+
'state' => 'ok',
|
3619 |
+
'value' => __( 'Off', 'broken-link-checker' ),
|
3620 |
);
|
3621 |
}
|
3622 |
|
3623 |
+
//Open_basedir status
|
3624 |
+
if ( blcUtility::is_open_basedir() ) {
|
3625 |
+
$debug['open_basedir'] = array(
|
3626 |
+
'state' => 'warning',
|
3627 |
+
'value' => sprintf( __( 'On ( %s )', 'broken-link-checker' ), ini_get( 'open_basedir' ) ),
|
3628 |
+
'message' => __( 'Redirects may be detected as broken links when open_basedir is on.', 'broken-link-checker' ),
|
3629 |
+
);
|
3630 |
+
} else {
|
3631 |
+
$debug['open_basedir'] = array(
|
3632 |
+
'state' => 'ok',
|
3633 |
+
'value' => __( 'Off', 'broken-link-checker' ),
|
3634 |
+
);
|
3635 |
+
}
|
|
|
|
|
|
|
3636 |
|
3637 |
+
//Default PHP execution time limit
|
3638 |
+
$debug['Default PHP execution time limit'] = array(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3639 |
'state' => 'ok',
|
3640 |
+
'value' => sprintf( __( '%s seconds' ), ini_get( 'max_execution_time' ) ),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3641 |
);
|
|
|
3642 |
|
3643 |
+
//Database character set. Usually it's UTF-8. Setting it to something else can cause problems
|
3644 |
+
//unless the site owner really knows what they're doing.
|
3645 |
+
$charset = $wpdb->get_charset_collate();
|
3646 |
+
$debug[ __( 'Database character set', 'broken-link-checker' ) ] = array(
|
3647 |
'state' => 'ok',
|
3648 |
+
'value' => ! empty( $charset ) ? $charset : '-',
|
|
|
|
|
|
|
|
|
|
|
3649 |
);
|
|
|
3650 |
|
3651 |
+
//Resynch flag.
|
3652 |
+
$debug['Resynch. flag'] = array(
|
|
|
|
|
3653 |
'state' => 'ok',
|
3654 |
+
'value' => sprintf( '%d', $this->conf->options['need_resynch'] ? '1 (resynch. required)' : '0 (resynch. not required)' ),
|
3655 |
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3656 |
|
3657 |
+
//Synch records
|
3658 |
+
$synch_records = intval( $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}blc_synch" ) );
|
3659 |
+
$data = array(
|
3660 |
'state' => 'ok',
|
3661 |
+
'value' => sprintf( '%d', $synch_records ),
|
|
|
|
|
|
|
|
|
|
|
3662 |
);
|
3663 |
+
if ( 0 === $synch_records ) {
|
3664 |
+
$data['state'] = 'warning';
|
3665 |
+
$data['message'] = __( 'If this value is zero even after several page reloads you have probably encountered a bug.', 'broken-link-checker' );
|
3666 |
+
}
|
3667 |
+
$debug['Synch. records'] = $data;
|
3668 |
|
3669 |
+
//Total links and instances (including invalid ones)
|
3670 |
+
$all_links = intval( $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}blc_links" ) );
|
3671 |
+
$all_instances = intval( $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}blc_instances" ) );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3672 |
|
3673 |
+
//Show the number of unparsed containers. Useful for debugging. For performance,
|
3674 |
+
//this is only shown when we have no links/instances yet.
|
3675 |
+
if ( ( 0 == $all_links ) && ( 0 == $all_instances ) ) {
|
3676 |
+
$unparsed_items = intval( $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->prefix}blc_synch WHERE synched=0" ) );
|
3677 |
+
$debug['Unparsed items'] = array(
|
3678 |
+
'state' => 'warning',
|
3679 |
+
'value' => $unparsed_items,
|
3680 |
+
);
|
3681 |
+
}
|
3682 |
|
3683 |
+
//Links & instances
|
3684 |
+
if ( ( $all_links > 0 ) && ( $all_instances > 0 ) ) {
|
3685 |
+
$debug['Link records'] = array(
|
3686 |
+
'state' => 'ok',
|
3687 |
+
'value' => sprintf( '%d (%d)', $all_links, $all_instances ),
|
3688 |
+
);
|
3689 |
+
} else {
|
3690 |
+
$debug['Link records'] = array(
|
3691 |
+
'state' => 'warning',
|
3692 |
+
'value' => sprintf( '%d (%d)', $all_links, $all_instances ),
|
3693 |
+
);
|
3694 |
+
}
|
3695 |
|
3696 |
+
//Email notifications.
|
3697 |
+
if ( $this->conf->options['last_notification_sent'] ) {
|
3698 |
+
$notificationDebug = array(
|
3699 |
+
'value' => date( 'Y-m-d H:i:s T', $this->conf->options['last_notification_sent'] ), //phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
|
3700 |
+
'state' => 'ok',
|
3701 |
+
);
|
3702 |
+
} else {
|
3703 |
+
$notificationDebug = array(
|
3704 |
+
'value' => 'Never',
|
3705 |
+
'state' => $this->conf->options['send_email_notifications'] ? 'ok' : 'warning',
|
3706 |
+
);
|
3707 |
+
}
|
3708 |
+
$debug['Last email notification'] = $notificationDebug;
|
3709 |
|
3710 |
+
if ( isset( $this->conf->options['last_email'] ) ) {
|
3711 |
+
$email = $this->conf->options['last_email'];
|
3712 |
+
$debug['Last email sent'] = array(
|
3713 |
+
'state' => 'ok',
|
3714 |
+
'value' => sprintf(
|
3715 |
+
'"%s" on %s (%s)',
|
3716 |
+
htmlentities( $email['subject'] ),
|
3717 |
+
date( 'Y-m-d H:i:s T', $email['timestamp'] ), //phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
|
3718 |
+
$email['success'] ? 'success' : 'failure'
|
3719 |
+
),
|
3720 |
+
);
|
3721 |
+
}
|
3722 |
|
3723 |
+
//Installation log
|
3724 |
+
$logger = new blcCachedOptionLogger( 'blc_installation_log' );
|
3725 |
+
$installation_log = $logger->get_messages();
|
3726 |
+
if ( ! empty( $installation_log ) ) {
|
3727 |
+
$debug['Installation log'] = array(
|
3728 |
+
'state' => $this->conf->options['installation_complete'] ? 'ok' : 'error',
|
3729 |
+
'value' => implode( "<br>\n", $installation_log ),
|
3730 |
+
);
|
3731 |
+
} else {
|
3732 |
+
$debug['Installation log'] = array(
|
3733 |
+
'state' => 'warning',
|
3734 |
+
'value' => 'No installation log found found.',
|
3735 |
+
);
|
3736 |
+
}
|
3737 |
|
3738 |
+
return $debug;
|
|
|
3739 |
}
|
3740 |
|
3741 |
+
function maybe_send_email_notifications() {
|
3742 |
+
global $wpdb; /** @var wpdb $wpdb */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3743 |
|
3744 |
+
if ( ! ( $this->conf->options['send_email_notifications'] || $this->conf->options['send_authors_email_notifications'] ) ) {
|
3745 |
+
return;
|
3746 |
+
}
|
|
|
3747 |
|
3748 |
+
//Find links that have been detected as broken since the last sent notification.
|
3749 |
+
$last_notification = date( 'Y-m-d H:i:s', $this->conf->options['last_notification_sent'] );//phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
|
3750 |
+
$where = $wpdb->prepare( '( first_failure >= %s )', $last_notification );
|
3751 |
+
|
3752 |
+
$links = blc_get_links(
|
3753 |
+
array(
|
3754 |
+
's_filter' => 'broken',
|
3755 |
+
'where_expr' => $where,
|
3756 |
+
'load_instances' => true,
|
3757 |
+
'load_containers' => true,
|
3758 |
+
'load_wrapped_objects' => $this->conf->options['send_authors_email_notifications'],
|
3759 |
+
'max_results' => 0,
|
3760 |
+
)
|
3761 |
+
);
|
3762 |
|
3763 |
+
if ( empty( $links ) ) {
|
3764 |
+
return;
|
3765 |
+
}
|
|
|
|
|
|
|
3766 |
|
3767 |
+
//Send the admin/maintainer an email notification.
|
3768 |
+
$email = $this->conf->get( 'notification_email_address' );
|
3769 |
+
if ( empty( $email ) ) {
|
3770 |
+
//Default to the admin email.
|
3771 |
+
$email = get_option( 'admin_email' );
|
3772 |
+
}
|
3773 |
+
if ( $this->conf->options['send_email_notifications'] && ! empty( $email ) ) {
|
3774 |
+
$this->send_admin_notification( $links, $email );
|
3775 |
+
}
|
|
|
3776 |
|
3777 |
+
//Send notifications to post authors
|
3778 |
+
if ( $this->conf->options['send_authors_email_notifications'] ) {
|
3779 |
+
$this->send_authors_notifications( $links );
|
3780 |
+
}
|
|
|
3781 |
|
3782 |
+
$this->conf->options['last_notification_sent'] = time();
|
3783 |
+
$this->conf->save_options();
|
3784 |
}
|
3785 |
|
3786 |
+
function send_admin_notification( $links, $email ) {
|
3787 |
+
//Prepare email message
|
3788 |
+
$subject = sprintf(
|
3789 |
+
__( '[%s] Broken links detected', 'broken-link-checker' ),
|
3790 |
+
html_entity_decode( get_option( 'blogname' ), ENT_QUOTES )
|
3791 |
+
);
|
|
|
3792 |
|
3793 |
+
$body = sprintf(
|
|
|
|
|
3794 |
_n(
|
3795 |
+
'Broken Link Checker has detected %d new broken link on your site.',
|
3796 |
+
'Broken Link Checker has detected %d new broken links on your site.',
|
3797 |
+
count( $links ),
|
3798 |
'broken-link-checker'
|
3799 |
),
|
3800 |
+
count( $links )
|
3801 |
);
|
3802 |
+
$body .= '<br>';
|
3803 |
+
|
3804 |
+
$instances = array();
|
3805 |
+
foreach ( $links as $link ) { /* @var blcLink $link */
|
3806 |
+
$instances = array_merge( $instances, $link->get_instances() );
|
3807 |
+
}
|
3808 |
+
$body .= $this->build_instance_list_for_email( $instances );
|
3809 |
+
|
3810 |
+
if ( $this->is_textdomain_loaded && is_rtl() ) {
|
3811 |
+
$body = '<div dir="rtl">' . $body . '</div>';
|
3812 |
+
}
|
3813 |
+
|
3814 |
+
$this->send_html_email( $email, $subject, $body );
|
3815 |
}
|
3816 |
|
3817 |
+
function build_instance_list_for_email( $instances, $max_displayed_links = 5, $add_admin_link = true ) {
|
3818 |
+
if ( null === $max_displayed_links ) {
|
3819 |
+
$max_displayed_links = 5;
|
3820 |
+
}
|
3821 |
|
3822 |
+
$result = '';
|
3823 |
+
if ( count( $instances ) > $max_displayed_links ) {
|
3824 |
+
$line = sprintf(
|
3825 |
+
_n(
|
3826 |
+
"Here's a list of the first %d broken links:",
|
3827 |
+
"Here's a list of the first %d broken links:",
|
3828 |
+
$max_displayed_links,
|
3829 |
+
'broken-link-checker'
|
3830 |
+
),
|
3831 |
+
$max_displayed_links
|
3832 |
+
);
|
3833 |
+
} else {
|
3834 |
+
$line = __( "Here's a list of the new broken links: ", 'broken-link-checker' );
|
3835 |
+
}
|
3836 |
+
|
3837 |
+
$result .= "<p>$line</p>";
|
3838 |
+
|
3839 |
+
//Show up to $max_displayed_links broken link instances right in the email.
|
3840 |
+
$displayed = 0;
|
3841 |
+
foreach ( $instances as $instance ) { /* @var blcLinkInstance $instance */
|
3842 |
+
$pieces = array(
|
3843 |
+
sprintf( __( 'Link text : %s', 'broken-link-checker' ), $instance->ui_get_link_text( 'email' ) ),
|
3844 |
+
sprintf( __( 'Link URL : <a href="%1$s">%2$s</a>', 'broken-link-checker' ), htmlentities( $instance->get_url() ), blcUtility::truncate( $instance->get_url(), 70, '' ) ),
|
3845 |
+
sprintf( __( 'Source : %s', 'broken-link-checker' ), $instance->ui_get_source( 'email' ) ),
|
3846 |
+
);
|
3847 |
|
3848 |
+
$link_entry = implode( '<br>', $pieces );
|
3849 |
+
$result .= "$link_entry<br><br>";
|
3850 |
|
3851 |
+
$displayed++;
|
3852 |
+
if ( $displayed >= $max_displayed_links ) {
|
3853 |
+
break;
|
3854 |
+
}
|
3855 |
}
|
|
|
3856 |
|
3857 |
+
//Add a link to the "Broken Links" tab.
|
3858 |
+
if ( $add_admin_link ) {
|
3859 |
+
$result .= __( 'You can see all broken links here:', 'broken-link-checker' ) . '<br>';
|
3860 |
+
$result .= sprintf( '<a href="%1$s">%1$s</a>', admin_url( 'tools.php?page=view-broken-links' ) );
|
3861 |
+
}
|
3862 |
|
3863 |
+
return $result;
|
3864 |
+
}
|
3865 |
|
3866 |
+
function send_html_email( $email_address, $subject, $body ) {
|
3867 |
+
//Need to override the default 'text/plain' content type to send a HTML email.
|
3868 |
+
add_filter( 'wp_mail_content_type', array( $this, 'override_mail_content_type' ) );
|
3869 |
|
3870 |
+
//Let auto-responders and similar software know this is an auto-generated email
|
3871 |
+
//that they shouldn't respond to.
|
3872 |
+
$headers = array( 'Auto-Submitted: auto-generated' );
|
3873 |
|
3874 |
+
$success = wp_mail( $email_address, $subject, $body, $headers );
|
3875 |
|
3876 |
+
//Remove the override so that it doesn't interfere with other plugins that might
|
3877 |
+
//want to send normal plaintext emails.
|
3878 |
+
remove_filter( 'wp_mail_content_type', array( $this, 'override_mail_content_type' ) );
|
3879 |
|
3880 |
+
$this->conf->options['last_email'] = array(
|
3881 |
+
'subject' => $subject,
|
3882 |
+
'timestamp' => time(),
|
3883 |
+
'success' => $success,
|
3884 |
+
);
|
3885 |
+
$this->conf->save_options();
|
3886 |
|
3887 |
+
return $success;
|
3888 |
+
}
|
3889 |
|
3890 |
+
function send_authors_notifications( $links ) {
|
3891 |
+
$authorInstances = array();
|
3892 |
+
foreach ( $links as $link ) { /* @var blcLink $link */
|
3893 |
+
foreach ( $link->get_instances() as $instance ) { /* @var blcLinkInstance $instance */
|
3894 |
+
$container = $instance->get_container(); /** @var blcContainer $container */
|
3895 |
+
if ( empty( $container ) || ! ( $container instanceof blcAnyPostContainer ) ) {
|
3896 |
+
continue;
|
3897 |
+
}
|
3898 |
+
$post = $container->get_wrapped_object(); /** @var StdClass $post */
|
3899 |
+
if ( ! array_key_exists( $post->post_author, $authorInstances ) ) {
|
3900 |
+
$authorInstances[ $post->post_author ] = array();
|
3901 |
+
}
|
3902 |
+
$authorInstances[ $post->post_author ][] = $instance;
|
3903 |
}
|
|
|
3904 |
}
|
|
|
3905 |
|
3906 |
+
foreach ( $authorInstances as $author_id => $instances ) {
|
3907 |
+
$subject = sprintf(
|
3908 |
+
__( '[%s] Broken links detected', 'broken-link-checker' ),
|
3909 |
+
html_entity_decode( get_option( 'blogname' ), ENT_QUOTES )
|
3910 |
+
);
|
3911 |
|
3912 |
+
$body = sprintf(
|
3913 |
+
_n(
|
3914 |
+
'Broken Link Checker has detected %d new broken link in your posts.',
|
3915 |
+
'Broken Link Checker has detected %d new broken links in your posts.',
|
3916 |
+
count( $instances ),
|
3917 |
+
'broken-link-checker'
|
3918 |
+
),
|
3919 |
+
count( $instances )
|
3920 |
+
);
|
3921 |
+
$body .= '<br>';
|
3922 |
|
3923 |
+
$author = get_user_by( 'id', $author_id ); /** @var WP_User $author */
|
3924 |
+
$body .= $this->build_instance_list_for_email( $instances, null, $author->has_cap( 'edit_others_posts' ) );
|
3925 |
|
3926 |
+
if ( $this->is_textdomain_loaded && is_rtl() ) {
|
3927 |
+
$body = '<div dir="rtl">' . $body . '</div>';
|
3928 |
+
}
|
3929 |
+
|
3930 |
+
$this->send_html_email( $author->user_email, $subject, $body );
|
3931 |
}
|
3932 |
+
}
|
3933 |
|
3934 |
+
function override_mail_content_type( /** @noinspection PhpUnusedParameterInspection */ $content_type ) {
|
3935 |
+
return 'text/html';
|
3936 |
}
|
|
|
3937 |
|
3938 |
+
/**
|
3939 |
+
* Promote all links with the "warning" status to "broken".
|
3940 |
+
*/
|
3941 |
+
private function promote_warnings_to_broken() {
|
3942 |
+
global $wpdb; /** @var wpdb $wpdb */
|
3943 |
+
$wpdb->update(
|
3944 |
+
$wpdb->prefix . 'blc_links',
|
3945 |
+
array(
|
3946 |
+
'broken' => 1,
|
3947 |
+
'warning' => 0,
|
3948 |
+
),
|
3949 |
+
array(
|
3950 |
+
'warning' => 1,
|
3951 |
+
),
|
3952 |
+
'%d'
|
3953 |
+
);
|
3954 |
+
}
|
3955 |
|
3956 |
+
/**
|
3957 |
+
* Install or uninstall the plugin's Cron events based on current settings.
|
3958 |
+
*
|
3959 |
+
* @uses wsBrokenLinkChecker::$conf Uses $conf->options to determine if events need to be (un)installed.
|
3960 |
+
*
|
3961 |
+
* @return void
|
3962 |
+
*/
|
3963 |
+
function setup_cron_events() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3964 |
|
3965 |
+
//Link monitor
|
3966 |
+
if ( $this->conf->options['run_via_cron'] ) {
|
3967 |
+
if ( ! wp_next_scheduled( 'blc_cron_check_links' ) ) {
|
3968 |
+
wp_schedule_event( time(), '10min', 'blc_cron_check_links' );
|
3969 |
+
}
|
3970 |
+
} else {
|
3971 |
+
wp_clear_scheduled_hook( 'blc_cron_check_links' );
|
3972 |
+
}
|
3973 |
|
3974 |
+
//Email notifications about broken links
|
3975 |
+
if ( $this->conf->options['send_email_notifications'] || $this->conf->options['send_authors_email_notifications'] ) {
|
3976 |
+
if ( ! wp_next_scheduled( 'blc_cron_email_notifications' ) ) {
|
3977 |
+
wp_schedule_event( time(), $this->conf->options['notification_schedule'], 'blc_cron_email_notifications' );
|
3978 |
+
}
|
3979 |
+
} else {
|
3980 |
+
wp_clear_scheduled_hook( 'blc_cron_email_notifications' );
|
3981 |
}
|
|
|
|
|
|
|
3982 |
|
3983 |
+
//Run database maintenance every two weeks or so
|
3984 |
+
if ( ! wp_next_scheduled( 'blc_cron_database_maintenance' ) ) {
|
3985 |
+
wp_schedule_event( time(), 'daily', 'blc_cron_database_maintenance' );
|
|
|
3986 |
}
|
|
|
|
|
3987 |
}
|
3988 |
|
3989 |
+
/**
|
3990 |
+
* Load the plugin's textdomain.
|
3991 |
+
*
|
3992 |
+
* @return void
|
3993 |
+
*/
|
3994 |
+
function load_language() {
|
3995 |
+
$this->is_textdomain_loaded = load_plugin_textdomain( 'broken-link-checker', false, basename( dirname( $this->loader ) ) . '/languages' );
|
3996 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3997 |
|
3998 |
+
protected static function get_default_log_directory() {
|
3999 |
+
$uploads = wp_upload_dir();
|
4000 |
+
return $uploads['basedir'] . '/broken-link-checker';
|
4001 |
+
}
|
4002 |
|
4003 |
+
protected static function get_default_log_basename() {
|
4004 |
+
return 'blc-log.txt';
|
4005 |
+
}
|
4006 |
|
4007 |
+
}//class ends here
|
4008 |
|
4009 |
} // if class_exists...
|
core/init.php
CHANGED
@@ -1,399 +1,372 @@
|
|
1 |
-
<?php
|
2 |
-
//To prevent conflicts, only one version of the plugin can be activated at any given time.
|
3 |
-
if ( defined('BLC_ACTIVE') ){
|
4 |
-
trigger_error(
|
5 |
-
'Another version of Broken Link Checker is already active. Please deactivate it before activating this one.',
|
6 |
-
E_USER_ERROR
|
7 |
-
);
|
8 |
-
} else {
|
9 |
-
|
10 |
-
define('BLC_ACTIVE', true);
|
11 |
-
|
12 |
-
//Fail fast if the WP version is unsupported. The $wp_version variable may be obfuscated by other
|
13 |
-
//plugins, so use function detection to determine the version. get_post_stati was introduced in WP 3.0.0
|
14 |
-
if ( !function_exists('get_post_stati') ){
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
}
|
20 |
-
|
21 |
-
/***********************************************
|
22 |
-
|
23 |
-
************************************************/
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
define('
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
'
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
$
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
$blclog->info('...
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
}
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
$
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
} else {
|
374 |
-
$messages[] = "Option exists in the {$wpdb->options} table and has the following value:";
|
375 |
-
$messages[] = '';
|
376 |
-
$messages[] = '<textarea cols="120" rows="20">' . htmlentities($serialized_config) . '</textarea>';
|
377 |
-
}
|
378 |
-
|
379 |
-
} else {
|
380 |
-
$logger = new blcCachedOptionLogger('blc_installation_log');
|
381 |
-
$messages = array_merge(
|
382 |
-
$messages,
|
383 |
-
array(
|
384 |
-
'installation_complete = ' . (isset($blc_config_manager->options['installation_complete']) ? intval($blc_config_manager->options['installation_complete']) : 'no value'),
|
385 |
-
'installation_flag_cleared_on = ' . $blc_config_manager->options['installation_flag_cleared_on'],
|
386 |
-
'installation_flag_set_on = ' . $blc_config_manager->options['installation_flag_set_on'],
|
387 |
-
'',
|
388 |
-
'<em>Installation log follows :</em>'
|
389 |
-
),
|
390 |
-
$logger->get_messages()
|
391 |
-
);
|
392 |
-
}
|
393 |
-
|
394 |
-
echo "<div class='error'><p>", implode("<br>\n", $messages), "</p></div>";
|
395 |
-
}
|
396 |
-
add_action('admin_notices', 'blc_print_installation_errors');
|
397 |
-
}
|
398 |
-
|
399 |
-
}
|
1 |
+
<?php
|
2 |
+
//To prevent conflicts, only one version of the plugin can be activated at any given time.
|
3 |
+
if ( defined( 'BLC_ACTIVE' ) ) {
|
4 |
+
trigger_error(
|
5 |
+
'Another version of Broken Link Checker is already active. Please deactivate it before activating this one.',
|
6 |
+
E_USER_ERROR
|
7 |
+
);
|
8 |
+
} else {
|
9 |
+
|
10 |
+
define( 'BLC_ACTIVE', true );
|
11 |
+
|
12 |
+
//Fail fast if the WP version is unsupported. The $wp_version variable may be obfuscated by other
|
13 |
+
//plugins, so use function detection to determine the version. get_post_stati was introduced in WP 3.0.0
|
14 |
+
if ( ! function_exists( 'get_post_stati' ) ) {
|
15 |
+
trigger_error(
|
16 |
+
'This version of Broken Link Checker requires WordPress 3.0 or later!',
|
17 |
+
E_USER_ERROR
|
18 |
+
);
|
19 |
+
}
|
20 |
+
|
21 |
+
/***********************************************
|
22 |
+
Debugging stuff
|
23 |
+
************************************************/
|
24 |
+
//define('BLC_DEBUG', true);
|
25 |
+
|
26 |
+
/***********************************************
|
27 |
+
Constants
|
28 |
+
************************************************/
|
29 |
+
|
30 |
+
/**
|
31 |
+
* For performance, some internal APIs used for retrieving multiple links, instances or containers
|
32 |
+
* can take an optional "$purpose" argument. Those APIs will try to use this argument to pre-load
|
33 |
+
* any DB data required for the specified purpose ahead of time.
|
34 |
+
|
35 |
+
* For example, if you're loading a bunch of link containers for the purposes of parsing them and
|
36 |
+
* thus set $purpose to BLC_FOR_PARSING, the relevant container managers will (if applicable) precache
|
37 |
+
* the parse-able fields in each returned container object. Still, setting $purpose to any particular
|
38 |
+
* value does not *guarantee* any data will be preloaded - it's only a suggestion that it should.
|
39 |
+
|
40 |
+
* The currently supported values for the $purpose argument are :
|
41 |
+
*/
|
42 |
+
define( 'BLC_FOR_EDITING', 'edit' );
|
43 |
+
define( 'BLC_FOR_PARSING', 'parse' );
|
44 |
+
define( 'BLC_FOR_DISPLAY', 'display' );
|
45 |
+
define( 'BLC_DATABASE_VERSION', 10 );
|
46 |
+
|
47 |
+
/***********************************************
|
48 |
+
Configuration
|
49 |
+
************************************************/
|
50 |
+
|
51 |
+
//Load and initialize the plugin's configuration
|
52 |
+
require BLC_DIRECTORY . '/includes/config-manager.php';
|
53 |
+
|
54 |
+
global $blc_config_manager;
|
55 |
+
$blc_config_manager = new blcConfigurationManager(
|
56 |
+
//Save the plugin's configuration into this DB option
|
57 |
+
'wsblc_options',
|
58 |
+
//Initialize default settings
|
59 |
+
array(
|
60 |
+
'max_execution_time' => 7 * 60, // (in seconds) How long the worker instance may run, at most.
|
61 |
+
'check_threshold' => 72, // (in hours) Check each link every 72 hours.
|
62 |
+
'recheck_count' => 3, // How many times a broken link should be re-checked.
|
63 |
+
'recheck_threshold' => 30 * 60, // (in seconds) Re-check broken links after 30 minutes.
|
64 |
+
'run_in_dashboard' => true, // Run the link checker algo. continuously while the Dashboard is open.
|
65 |
+
'run_via_cron' => true, // Run it hourly via WordPress pseudo-cron.
|
66 |
+
'mark_broken_links' => true, // Whether to add the broken_link class to broken links in posts.
|
67 |
+
'broken_link_css' => ".broken_link, a.broken_link {\n\ttext-decoration: line-through;\n}",
|
68 |
+
'nofollow_broken_links' => false, // Whether to add rel="nofollow" to broken links in posts.
|
69 |
+
'mark_removed_links' => false, // Whether to add the removed_link class when un-linking a link.
|
70 |
+
'removed_link_css' => ".removed_link, a.removed_link {\n\ttext-decoration: line-through;\n}",
|
71 |
+
'exclusion_list' => array(), // Links that contain a substring listed in this array won't be checked.
|
72 |
+
'send_email_notifications' => true, // Whether to send the admin email notifications about broken links
|
73 |
+
'send_authors_email_notifications' => false, // Whether to send post authors notifications about broken links in their posts.
|
74 |
+
'notification_email_address' => '', // If set, send email notifications to this address instead of the admin.
|
75 |
+
'notification_schedule' => apply_filters( 'blc_notification_schedule_filter', 'daily' ), // How often (at most) notifications will be sent. Possible values : 'daily', 'weekly'. There is no option for this so we've added a fitler for it.
|
76 |
+
'last_notification_sent' => 0, // When the last email notification was sent (Unix timestamp)
|
77 |
+
'suggestions_enabled' => true, // Whether to suggest alternative URLs for broken links.
|
78 |
+
'warnings_enabled' => true, // Try to automatically detect temporary problems and false positives, and report them as "Warnings" instead of broken links.
|
79 |
+
'server_load_limit' => null, // Stop parsing stuff & checking links if the 1-minute load average goes over this value. Only works on Linux servers. 0 = no limit.
|
80 |
+
'enable_load_limit' => true, // Enable/disable load monitoring.
|
81 |
+
'custom_fields' => array(), // List of custom fields that can contain URLs and should be checked.
|
82 |
+
'acf_fields' => array(), // List of custom fields that can contain URLs and should be checked.
|
83 |
+
'enabled_post_statuses' => array( 'publish' ), // Only check posts that match one of these statuses
|
84 |
+
'autoexpand_widget' => true, // Autoexpand the Dashboard widget if broken links are detected
|
85 |
+
'dashboard_widget_capability' => 'edit_others_posts', // Only display the widget to users who have this capability
|
86 |
+
'show_link_count_bubble' => true, // Display a notification bubble in the menu when broken links are found
|
87 |
+
'table_layout' => 'flexible', // The layout of the link table. Possible values : 'classic', 'flexible'
|
88 |
+
'table_compact' => true, // Compact table mode on/off
|
89 |
+
'table_visible_columns' => array( 'new-url', 'status', 'used-in', 'new-link-text' ),
|
90 |
+
'table_links_per_page' => 30,
|
91 |
+
'table_color_code_status' => true, //Color-code link status text
|
92 |
+
'need_resynch' => false, // [Internal flag] True if there are unparsed items.
|
93 |
+
'current_db_version' => 0, // The currently set-up version of the plugin's tables
|
94 |
+
'timeout' => 30, // (in seconds) Links that take longer than this to respond will be treated as broken.
|
95 |
+
'highlight_permanent_failures' => false, // Highlight links that have appear to be permanently broken (in Tools -> Broken Links).
|
96 |
+
'failure_duration_threshold' => 3, // (days) Assume a link is permanently broken if it still hasn't recovered after this many days.
|
97 |
+
'logging_enabled' => false,
|
98 |
+
'log_file' => '',
|
99 |
+
'custom_log_file_enabled' => false,
|
100 |
+
'installation_complete' => false,
|
101 |
+
'installation_flag_cleared_on' => 0,
|
102 |
+
'installation_flag_set_on' => 0,
|
103 |
+
'user_has_donated' => false, // Whether the user has donated to the plugin.
|
104 |
+
'donation_flag_fixed' => false,
|
105 |
+
'show_link_actions' => array( 'blc-deredirect-action' => false ), //Visible link actions.
|
106 |
+
)
|
107 |
+
);
|
108 |
+
|
109 |
+
/***********************************************
|
110 |
+
Logging
|
111 |
+
************************************************/
|
112 |
+
|
113 |
+
include BLC_DIRECTORY . '/includes/logger.php';
|
114 |
+
|
115 |
+
global $blclog;
|
116 |
+
if ( $blc_config_manager->get( 'logging_enabled', false ) && is_writable( $blc_config_manager->get( 'log_file' ) ) ) {
|
117 |
+
$blclog = new blcFileLogger( $blc_config_manager->get( 'log_file' ) );
|
118 |
+
} else {
|
119 |
+
$blclog = new blcDummyLogger;
|
120 |
+
}
|
121 |
+
|
122 |
+
/*
|
123 |
+
if ( defined('BLC_DEBUG') && constant('BLC_DEBUG') ){
|
124 |
+
//Load FirePHP for debug logging
|
125 |
+
if ( !class_exists('FB') && file_exists(BLC_DIRECTORY . '/FirePHPCore/fb.php4') ) {
|
126 |
+
require_once BLC_DIRECTORY . '/FirePHPCore/fb.php4';
|
127 |
+
}
|
128 |
+
//FB::setEnabled(false);
|
129 |
+
}
|
130 |
+
//to comment out all calls : (^[^\/]*)(FB::) -> $1\/\/$2
|
131 |
+
//to uncomment : \/\/(\s*FB::) -> $1
|
132 |
+
//*/
|
133 |
+
|
134 |
+
/***********************************************
|
135 |
+
Global functions
|
136 |
+
************************************************/
|
137 |
+
|
138 |
+
/**
|
139 |
+
* Get the configuration object used by Broken Link Checker.
|
140 |
+
*
|
141 |
+
* @return blcConfigurationManager
|
142 |
+
*/
|
143 |
+
function blc_get_configuration() {
|
144 |
+
return $GLOBALS['blc_config_manager'];
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* Notify the link checker that there are unsynched items
|
149 |
+
* that might contain links (e.g. a new or edited post).
|
150 |
+
*
|
151 |
+
* @return void
|
152 |
+
*/
|
153 |
+
function blc_got_unsynched_items() {
|
154 |
+
$conf = blc_get_configuration();
|
155 |
+
|
156 |
+
if ( ! $conf->options['need_resynch'] ) {
|
157 |
+
$conf->options['need_resynch'] = true;
|
158 |
+
$conf->save_options();
|
159 |
+
}
|
160 |
+
}
|
161 |
+
|
162 |
+
/**
|
163 |
+
* (Re)create synchronization records for all containers and mark them all as unparsed.
|
164 |
+
*
|
165 |
+
* @param bool $forced If true, the plugin will recreate all synch. records from scratch.
|
166 |
+
* @return void
|
167 |
+
*/
|
168 |
+
function blc_resynch( $forced = false ) {
|
169 |
+
global $wpdb, $blclog;
|
170 |
+
|
171 |
+
if ( $forced ) {
|
172 |
+
$blclog->info( '... Forced resynchronization initiated' );
|
173 |
+
|
174 |
+
//Drop all synchronization records
|
175 |
+
$wpdb->query( "TRUNCATE {$wpdb->prefix}blc_synch" );
|
176 |
+
} else {
|
177 |
+
$blclog->info( '... Resynchronization initiated' );
|
178 |
+
}
|
179 |
+
|
180 |
+
//Remove invalid DB entries
|
181 |
+
blc_cleanup_database();
|
182 |
+
|
183 |
+
//(Re)create and update synch. records for all container types.
|
184 |
+
$blclog->info( '... (Re)creating container records' );
|
185 |
+
blcContainerHelper::resynch( $forced );
|
186 |
+
|
187 |
+
$blclog->info( '... Setting resync. flags' );
|
188 |
+
blc_got_unsynched_items();
|
189 |
+
|
190 |
+
//All done.
|
191 |
+
$blclog->info( 'Database resynchronization complete.' );
|
192 |
+
}
|
193 |
+
|
194 |
+
/**
|
195 |
+
* Delete synch. records, instances and links that refer to missing or invalid items.
|
196 |
+
*
|
197 |
+
* @return void
|
198 |
+
*/
|
199 |
+
function blc_cleanup_database() {
|
200 |
+
global $blclog;
|
201 |
+
|
202 |
+
//Delete synch. records for container types that don't exist
|
203 |
+
$blclog->info( '... Deleting invalid container records' );
|
204 |
+
blcContainerHelper::cleanup_containers();
|
205 |
+
|
206 |
+
//Delete invalid instances
|
207 |
+
$blclog->info( '... Deleting invalid link instances' );
|
208 |
+
blc_cleanup_instances();
|
209 |
+
|
210 |
+
//Delete orphaned links
|
211 |
+
$blclog->info( '... Deleting orphaned links' );
|
212 |
+
blc_cleanup_links();
|
213 |
+
}
|
214 |
+
|
215 |
+
/***********************************************
|
216 |
+
Utility hooks
|
217 |
+
************************************************/
|
218 |
+
|
219 |
+
/**
|
220 |
+
* Adds the following cron schedules:
|
221 |
+
* - 10min: every 10 minutes.
|
222 |
+
* - weekly: once per week.
|
223 |
+
* - bimonthly : twice per month.
|
224 |
+
*
|
225 |
+
* @param array $schedules Existing Cron schedules.
|
226 |
+
* @return array
|
227 |
+
*/
|
228 |
+
function blc_cron_schedules( $schedules ) {
|
229 |
+
|
230 |
+
if ( ! isset( $schedules['10min'] ) ) {
|
231 |
+
$schedules['10min'] = array(
|
232 |
+
'interval' => 600,
|
233 |
+
'display' => __( 'Every 10 minutes' ),
|
234 |
+
);
|
235 |
+
}
|
236 |
+
|
237 |
+
if ( ! isset( $schedules['weekly'] ) ) {
|
238 |
+
$schedules['weekly'] = array(
|
239 |
+
'interval' => 604800, //7 days
|
240 |
+
'display' => __( 'Once Weekly' ),
|
241 |
+
);
|
242 |
+
}
|
243 |
+
if ( ! isset( $schedules['bimonthly'] ) ) {
|
244 |
+
$schedules['bimonthly'] = array(
|
245 |
+
'interval' => 15 * 24 * 2600, //15 days
|
246 |
+
'display' => __( 'Twice a Month' ),
|
247 |
+
);
|
248 |
+
}
|
249 |
+
|
250 |
+
return $schedules;
|
251 |
+
}
|
252 |
+
add_filter( 'cron_schedules', 'blc_cron_schedules' );
|
253 |
+
|
254 |
+
/***********************************************
|
255 |
+
Main functionality
|
256 |
+
************************************************/
|
257 |
+
|
258 |
+
//Execute the installation/upgrade script when the plugin is activated.
|
259 |
+
function blc_activation_hook() {
|
260 |
+
require BLC_DIRECTORY . '/includes/activation.php';
|
261 |
+
}
|
262 |
+
|
263 |
+
register_activation_hook( BLC_PLUGIN_FILE, 'blc_activation_hook' );
|
264 |
+
|
265 |
+
//Load the plugin if installed successfully
|
266 |
+
if ( $blc_config_manager->options['installation_complete'] ) {
|
267 |
+
function blc_init() {
|
268 |
+
global $blc_module_manager, $blc_config_manager, $ws_link_checker;
|
269 |
+
|
270 |
+
static $init_done = false;
|
271 |
+
if ( $init_done ) {
|
272 |
+
return;
|
273 |
+
}
|
274 |
+
$init_done = true;
|
275 |
+
|
276 |
+
//Ensure the database is up to date
|
277 |
+
if ( BLC_DATABASE_VERSION !== $blc_config_manager->options['current_db_version'] ) {
|
278 |
+
require_once BLC_DIRECTORY . '/includes/admin/db-upgrade.php';
|
279 |
+
blcDatabaseUpgrader::upgrade_database(); //Also updates the DB ver. in options['current_db_version'].
|
280 |
+
}
|
281 |
+
|
282 |
+
//Load the base classes and utilities
|
283 |
+
require_once BLC_DIRECTORY . '/includes/links.php';
|
284 |
+
require_once BLC_DIRECTORY . '/includes/link-query.php';
|
285 |
+
require_once BLC_DIRECTORY . '/includes/instances.php';
|
286 |
+
require_once BLC_DIRECTORY . '/includes/utility-class.php';
|
287 |
+
|
288 |
+
//Load the module subsystem
|
289 |
+
require_once BLC_DIRECTORY . '/includes/modules.php';
|
290 |
+
|
291 |
+
//Load the modules that want to be executed in all contexts
|
292 |
+
$blc_module_manager->load_modules();
|
293 |
+
|
294 |
+
if ( is_admin() || defined( 'DOING_CRON' ) ) {
|
295 |
+
|
296 |
+
//It's an admin-side or Cron request. Load the core.
|
297 |
+
require_once BLC_DIRECTORY . '/core/core.php';
|
298 |
+
$ws_link_checker = new wsBrokenLinkChecker( BLC_PLUGIN_FILE, $blc_config_manager );
|
299 |
+
|
300 |
+
} else {
|
301 |
+
|
302 |
+
//This is user-side request, so we don't need to load the core.
|
303 |
+
//We might need to inject the CSS for removed links, though.
|
304 |
+
if ( $blc_config_manager->options['mark_removed_links'] && ! empty( $blc_config_manager->options['removed_link_css'] ) ) {
|
305 |
+
function blc_print_removed_link_css() {
|
306 |
+
global $blc_config_manager;
|
307 |
+
echo '<style type="text/css">',$blc_config_manager->options['removed_link_css'],'</style>';
|
308 |
+
}
|
309 |
+
add_action( 'wp_head', 'blc_print_removed_link_css' );
|
310 |
+
}
|
311 |
+
}
|
312 |
+
}
|
313 |
+
add_action( 'init', 'blc_init', 2000 );
|
314 |
+
} else {
|
315 |
+
//Display installation errors (if any) on the Dashboard.
|
316 |
+
function blc_print_installation_errors() {
|
317 |
+
global $blc_config_manager, $wpdb;
|
318 |
+
|
319 |
+
if ( $blc_config_manager->options['installation_complete'] ) {
|
320 |
+
return;
|
321 |
+
}
|
322 |
+
|
323 |
+
$messages = array(
|
324 |
+
'<strong>' . __( 'Broken Link Checker installation failed. Try deactivating and then reactivating the plugin.', 'broken-link-checker' ) . '</strong>',
|
325 |
+
);
|
326 |
+
|
327 |
+
if ( is_multisite() && is_plugin_active_for_network( plugin_basename( BLC_PLUGIN_FILE ) ) ) {
|
328 |
+
$messages[] = __( 'Please activate the plugin separately on each site. Network activation is not supported.', 'broken-link-checker' );
|
329 |
+
$messages[] = '';
|
330 |
+
}
|
331 |
+
|
332 |
+
if ( ! $blc_config_manager->db_option_loaded ) {
|
333 |
+
$messages[] = sprintf(
|
334 |
+
'<strong>Failed to load plugin settings from the "%s" option.</strong>',
|
335 |
+
$blc_config_manager->option_name
|
336 |
+
);
|
337 |
+
$messages[] = '';
|
338 |
+
|
339 |
+
$serialized_config = $wpdb->get_var(
|
340 |
+
$wpdb->prepare(
|
341 |
+
"SELECT `option_value` FROM `$wpdb->options` WHERE `option_name` = %s",
|
342 |
+
$blc_config_manager->option_name
|
343 |
+
)
|
344 |
+
);
|
345 |
+
|
346 |
+
if ( null === $serialized_config ) {
|
347 |
+
$messages[] = "Option doesn't exist in the {$wpdb->options} table.";
|
348 |
+
} else {
|
349 |
+
$messages[] = "Option exists in the {$wpdb->options} table and has the following value:";
|
350 |
+
$messages[] = '';
|
351 |
+
$messages[] = '<textarea cols="120" rows="20">' . htmlentities( $serialized_config ) . '</textarea>';
|
352 |
+
}
|
353 |
+
} else {
|
354 |
+
$logger = new blcCachedOptionLogger( 'blc_installation_log' );
|
355 |
+
$messages = array_merge(
|
356 |
+
$messages,
|
357 |
+
array(
|
358 |
+
'installation_complete = ' . ( isset( $blc_config_manager->options['installation_complete'] ) ? intval( $blc_config_manager->options['installation_complete'] ) : 'no value' ),
|
359 |
+
'installation_flag_cleared_on = ' . $blc_config_manager->options['installation_flag_cleared_on'],
|
360 |
+
'installation_flag_set_on = ' . $blc_config_manager->options['installation_flag_set_on'],
|
361 |
+
'',
|
362 |
+
'<em>Installation log follows :</em>',
|
363 |
+
),
|
364 |
+
$logger->get_messages()
|
365 |
+
);
|
366 |
+
}
|
367 |
+
|
368 |
+
echo '<div class="error"><p>', implode( "<br>\n", $messages ), '</p></div>';
|
369 |
+
}
|
370 |
+
add_action( 'admin_notices', 'blc_print_installation_errors' );
|
371 |
+
}
|
372 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
css/links-page.css
CHANGED
@@ -496,4 +496,10 @@ div.search-box{
|
|
496 |
#screen-meta-links #blc-plugin-news-link-wrap a.show-settings:hover
|
497 |
{
|
498 |
color: white;
|
|
|
|
|
|
|
|
|
|
|
|
|
499 |
}
|
496 |
#screen-meta-links #blc-plugin-news-link-wrap a.show-settings:hover
|
497 |
{
|
498 |
color: white;
|
499 |
+
}
|
500 |
+
|
501 |
+
@media screen and (max-width: 768px) {
|
502 |
+
table#blc-links {
|
503 |
+
table-layout: auto;
|
504 |
+
}
|
505 |
}
|
idn/idna_convert.class.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
// {{{ license
|
3 |
|
4 |
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
|
@@ -53,863 +54,901 @@
|
|
53 |
* @changelog since 0.5.1 class updated to PHP5/6 style should be compatible to PHP 4.3+
|
54 |
* - added a missing replace mapping for THAI CHARACTER SARA AM
|
55 |
*/
|
56 |
-
class idna_convert
|
57 |
-
|
58 |
-
// NP See below
|
59 |
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
|
88 |
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
|
|
103 |
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
}
|
151 |
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
$input = trim($input);
|
174 |
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
252 |
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
|
276 |
-
|
277 |
-
|
|
|
|
|
278 |
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
|
|
336 |
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
}
|
346 |
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
$enco_len = strlen($encoded);
|
375 |
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
|
|
|
|
|
|
|
|
|
|
403 |
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
$check_deco = array_slice($decoded, 0, $extract);
|
415 |
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
450 |
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
|
465 |
-
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
|
|
|
|
|
470 |
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
|
|
|
|
495 |
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
}
|
512 |
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
}
|
522 |
|
523 |
-
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
}
|
533 |
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
}
|
542 |
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
-
|
611 |
-
|
612 |
-
|
613 |
-
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
618 |
|
619 |
-
|
620 |
-
|
621 |
-
|
622 |
-
|
623 |
-
|
624 |
-
|
625 |
-
|
626 |
-
|
627 |
-
|
628 |
-
|
629 |
-
|
630 |
-
|
631 |
-
|
632 |
-
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
|
638 |
-
|
639 |
-
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
|
646 |
-
|
647 |
-
|
648 |
-
|
|
|
|
|
|
|
|
|
649 |
|
650 |
-
|
651 |
-
|
652 |
-
|
653 |
-
|
654 |
-
|
655 |
-
|
656 |
-
|
657 |
-
|
658 |
-
|
659 |
-
|
660 |
-
|
661 |
-
|
662 |
-
|
663 |
-
|
664 |
-
|
665 |
-
|
666 |
-
|
667 |
-
|
668 |
-
|
669 |
-
|
670 |
-
|
671 |
-
|
672 |
-
|
673 |
-
|
674 |
-
|
675 |
-
|
676 |
-
|
677 |
|
678 |
-
|
679 |
-
|
680 |
-
|
681 |
-
|
682 |
-
|
683 |
-
|
684 |
-
|
685 |
-
|
686 |
-
}
|
687 |
|
688 |
-
|
689 |
-
|
690 |
-
|
691 |
-
|
692 |
-
|
693 |
-
|
694 |
-
|
695 |
-
|
696 |
-
|
697 |
-
|
698 |
-
|
699 |
-
|
700 |
-
|
701 |
-
|
702 |
-
|
703 |
-
|
704 |
-
|
705 |
-
|
706 |
-
|
707 |
-
|
708 |
-
|
709 |
-
|
710 |
-
|
711 |
-
|
712 |
-
|
713 |
-
|
714 |
-
|
715 |
-
|
716 |
-
|
717 |
-
|
718 |
-
|
|
|
719 |
|
720 |
-
|
721 |
-
|
722 |
-
|
723 |
-
|
724 |
-
|
725 |
-
|
726 |
-
|
727 |
-
|
728 |
-
|
729 |
-
|
730 |
-
|
731 |
-
|
732 |
-
|
733 |
-
|
734 |
-
|
735 |
-
|
736 |
-
|
737 |
-
|
738 |
-
|
739 |
-
|
740 |
-
|
741 |
-
|
742 |
-
|
743 |
-
|
|
|
|
|
|
|
|
|
|
|
744 |
|
745 |
-
|
746 |
-
|
747 |
-
|
748 |
-
|
749 |
-
|
750 |
-
|
751 |
-
|
752 |
-
|
753 |
-
|
754 |
-
|
755 |
-
|
756 |
-
|
757 |
-
|
758 |
-
|
759 |
-
|
760 |
-
|
761 |
-
|
762 |
-
|
763 |
-
|
764 |
-
|
765 |
-
|
766 |
-
|
767 |
-
|
768 |
-
|
769 |
-
|
770 |
-
|
771 |
-
|
772 |
-
|
773 |
-
|
774 |
-
|
775 |
-
|
776 |
-
|
777 |
-
|
778 |
-
|
779 |
-
|
780 |
-
|
781 |
-
|
782 |
-
|
783 |
-
|
784 |
-
|
785 |
-
|
786 |
-
|
787 |
-
|
788 |
-
|
789 |
-
$test = 'range';
|
790 |
-
if ($v >> 5 == 6) { // &110xxxxx 10xxxxx
|
791 |
-
$next_byte = 0; // Tells, how many times subsequent bitmasks must rotate 6bits to the left
|
792 |
-
$v = ($v - 192) << 6;
|
793 |
-
} elseif ($v >> 4 == 14) { // &1110xxxx 10xxxxxx 10xxxxxx
|
794 |
-
$next_byte = 1;
|
795 |
-
$v = ($v - 224) << 12;
|
796 |
-
} elseif ($v >> 3 == 30) { // &11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
797 |
-
$next_byte = 2;
|
798 |
-
$v = ($v - 240) << 18;
|
799 |
-
} elseif ($v >> 2 == 62) { // &111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
|
800 |
-
$next_byte = 3;
|
801 |
-
$v = ($v - 248) << 24;
|
802 |
-
} elseif ($v >> 1 == 126) { // &1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
|
803 |
-
$next_byte = 4;
|
804 |
-
$v = ($v - 252) << 30;
|
805 |
-
} else {
|
806 |
-
$this->_error('This might be UTF-8, but I don\'t understand it at byte '.$k);
|
807 |
-
return false;
|
808 |
-
}
|
809 |
-
if ('add' == $mode) {
|
810 |
-
$output[$out_len] = (int) $v;
|
811 |
-
++$out_len;
|
812 |
-
continue;
|
813 |
-
}
|
814 |
-
}
|
815 |
-
if ('add' == $mode) {
|
816 |
-
if (!$this->_allow_overlong && $test == 'range') {
|
817 |
-
$test = 'none';
|
818 |
-
if (($v < 0xA0 && $start_byte == 0xE0) || ($v < 0x90 && $start_byte == 0xF0) || ($v > 0x8F && $start_byte == 0xF4)) {
|
819 |
-
$this->_error('Bogus UTF-8 character detected (out of legal range) at byte '.$k);
|
820 |
-
return false;
|
821 |
-
}
|
822 |
-
}
|
823 |
-
if ($v >> 6 == 2) { // Bit mask must be 10xxxxxx
|
824 |
-
$v = ($v - 128) << ($next_byte * 6);
|
825 |
-
$output[($out_len - 1)] += $v;
|
826 |
-
--$next_byte;
|
827 |
-
} else {
|
828 |
-
$this->_error('Conversion from UTF-8 to UCS-4 failed: malformed input at byte '.$k);
|
829 |
-
return false;
|
830 |
-
}
|
831 |
-
if ($next_byte < 0) {
|
832 |
-
$mode = 'next';
|
833 |
-
}
|
834 |
-
}
|
835 |
-
} // for
|
836 |
-
return $output;
|
837 |
-
}
|
838 |
|
839 |
-
|
840 |
-
|
841 |
-
|
842 |
-
|
843 |
-
|
844 |
-
|
845 |
-
|
846 |
-
|
847 |
-
|
848 |
-
|
849 |
-
|
850 |
-
|
851 |
-
|
852 |
-
|
853 |
-
|
854 |
-
|
855 |
-
|
856 |
-
|
857 |
-
|
858 |
-
|
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 |
-
// Empty input - return empty output
|
900 |
-
if (!$inp_len) return $output;
|
901 |
-
for ($i = 0, $out_len = -1; $i < $inp_len; ++$i) {
|
902 |
-
// Increment output position every 4 input bytes
|
903 |
-
if (!($i % 4)) {
|
904 |
-
$out_len++;
|
905 |
-
$output[$out_len] = 0;
|
906 |
-
}
|
907 |
-
$output[$out_len] += ord($input{$i}) << (8 * (3 - ($i % 4) ) );
|
908 |
-
}
|
909 |
-
return $output;
|
910 |
-
}
|
911 |
|
912 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
913 |
* Holds all relevant mapping tables, loaded from a seperate file on construct
|
914 |
* See RFC3454 for details
|
915 |
*
|
1 |
<?php
|
2 |
+
|
3 |
// {{{ license
|
4 |
|
5 |
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
|
54 |
* @changelog since 0.5.1 class updated to PHP5/6 style should be compatible to PHP 4.3+
|
55 |
* - added a missing replace mapping for THAI CHARACTER SARA AM
|
56 |
*/
|
57 |
+
class idna_convert {
|
58 |
+
// NP See below
|
|
|
59 |
|
60 |
+
// Internal settings, do not mess with them
|
61 |
+
private $_punycode_prefix = 'xn--';
|
62 |
+
private $_invalid_ucs = 0x80000000;
|
63 |
+
private $_max_ucs = 0x10FFFF;
|
64 |
+
private $_base = 36;
|
65 |
+
private $_tmin = 1;
|
66 |
+
private $_tmax = 26;
|
67 |
+
private $_skew = 38;
|
68 |
+
private $_damp = 700;
|
69 |
+
private $_initial_bias = 72;
|
70 |
+
private $_initial_n = 0x80;
|
71 |
+
private $_sbase = 0xAC00;
|
72 |
+
private $_lbase = 0x1100;
|
73 |
+
private $_vbase = 0x1161;
|
74 |
+
private $_tbase = 0x11A7;
|
75 |
+
private $_lcount = 19;
|
76 |
+
private $_vcount = 21;
|
77 |
+
private $_tcount = 28;
|
78 |
+
private $_ncount = 588; // _vcount * _tcount
|
79 |
+
private $_scount = 11172; // _lcount * _tcount * _vcount
|
80 |
+
private $_error = false;
|
81 |
|
82 |
+
// See {@link set_paramter()} for details of how to change the following
|
83 |
+
// settings from within your script / application
|
84 |
+
private $_api_encoding = 'utf8'; // Default input charset is UTF-8
|
85 |
+
private $_allow_overlong = false; // Overlong UTF-8 encodings are forbidden
|
86 |
+
private $_strict_mode = false; // Behave strict or not
|
87 |
|
88 |
|
89 |
+
/**
|
90 |
+
* the constructor
|
91 |
+
*
|
92 |
+
* @param array $options
|
93 |
+
* @return boolean
|
94 |
+
* @since 0.5.2
|
95 |
+
*/
|
96 |
+
public function __construct( $options = false ) {
|
97 |
+
$this->slast = $this->_sbase + $this->_lcount * $this->_vcount * $this->_tcount;
|
98 |
+
// If parameters are given, pass these to the respective method
|
99 |
+
if ( is_array( $options ) ) {
|
100 |
+
return $this->set_parameter( $options );
|
101 |
+
}
|
102 |
+
return true;
|
103 |
+
}
|
104 |
|
105 |
+
/**
|
106 |
+
* Sets a new option value. Available options and values:
|
107 |
+
* [encoding - Use either UTF-8, UCS4 as array or UCS4 as string as input ('utf8' for UTF-8,
|
108 |
+
* 'ucs4_string' and 'ucs4_array' respectively for UCS4); The output is always UTF-8]
|
109 |
+
* [overlong - Unicode does not allow unnecessarily long encodings of chars,
|
110 |
+
* to allow this, set this parameter to true, else to false;
|
111 |
+
* default is false.]
|
112 |
+
* [strict - true: strict mode, good for registration purposes - Causes errors
|
113 |
+
* on failures; false: loose mode, ideal for "wildlife" applications
|
114 |
+
* by silently ignoring errors and returning the original input instead
|
115 |
+
*
|
116 |
+
* @param mixed Parameter to set (string: single parameter; array of Parameter => Value pairs)
|
117 |
+
* @param string Value to use (if parameter 1 is a string)
|
118 |
+
* @return boolean true on success, false otherwise
|
119 |
+
*/
|
120 |
+
public function set_parameter( $option, $value = false ) {
|
121 |
+
if ( ! is_array( $option ) ) {
|
122 |
+
$option = array( $option => $value );
|
123 |
+
}
|
124 |
+
foreach ( $option as $k => $v ) {
|
125 |
+
switch ( $k ) {
|
126 |
+
case 'encoding':
|
127 |
+
switch ( $v ) {
|
128 |
+
case 'utf8':
|
129 |
+
case 'ucs4_string':
|
130 |
+
case 'ucs4_array':
|
131 |
+
$this->_api_encoding = $v;
|
132 |
+
break;
|
133 |
+
default:
|
134 |
+
$this->_error( 'Set Parameter: Unknown parameter ' . $v . ' for option ' . $k );
|
135 |
+
return false;
|
136 |
+
}
|
137 |
+
break;
|
138 |
+
case 'overlong':
|
139 |
+
$this->_allow_overlong = ( $v ) ? true : false;
|
140 |
+
break;
|
141 |
+
case 'strict':
|
142 |
+
$this->_strict_mode = ( $v ) ? true : false;
|
143 |
+
break;
|
144 |
+
default:
|
145 |
+
$this->_error( 'Set Parameter: Unknown option ' . $k );
|
146 |
+
return false;
|
147 |
+
}
|
148 |
+
}
|
149 |
+
return true;
|
150 |
+
}
|
|
|
151 |
|
152 |
+
/**
|
153 |
+
* Decode a given ACE domain name
|
154 |
+
* @param string Domain name (ACE string)
|
155 |
+
* [@param string Desired output encoding, see {@link set_parameter}]
|
156 |
+
* @return string Decoded Domain name (UTF-8 or UCS-4)
|
157 |
+
*/
|
158 |
+
public function decode( $input, $one_time_encoding = false ) {
|
159 |
+
// Optionally set
|
160 |
+
if ( $one_time_encoding ) {
|
161 |
+
switch ( $one_time_encoding ) {
|
162 |
+
case 'utf8':
|
163 |
+
case 'ucs4_string':
|
164 |
+
case 'ucs4_array':
|
165 |
+
break;
|
166 |
+
default:
|
167 |
+
$this->_error( 'Unknown encoding ' . $one_time_encoding );
|
168 |
+
return false;
|
169 |
+
}
|
170 |
+
}
|
171 |
+
// Make sure to drop any newline characters around
|
172 |
+
$input = trim( $input );
|
|
|
173 |
|
174 |
+
// Negotiate input and try to determine, whether it is a plain string,
|
175 |
+
// an email address or something like a complete URL
|
176 |
+
if ( strpos( $input, '@' ) ) { // Maybe it is an email address
|
177 |
+
// No no in strict mode
|
178 |
+
if ( $this->_strict_mode ) {
|
179 |
+
$this->_error( 'Only simple domain name parts can be handled in strict mode' );
|
180 |
+
return false;
|
181 |
+
}
|
182 |
+
list ($email_pref, $input) = explode( '@', $input, 2 );
|
183 |
+
$arr = explode( '.', $input );
|
184 |
+
foreach ( $arr as $k => $v ) {
|
185 |
+
if ( preg_match( '!^' . preg_quote( $this->_punycode_prefix, '!' ) . '!', $v ) ) {
|
186 |
+
$conv = $this->_decode( $v );
|
187 |
+
if ( $conv ) {
|
188 |
+
$arr[ $k ] = $conv;
|
189 |
+
}
|
190 |
+
}
|
191 |
+
}
|
192 |
+
$input = join( '.', $arr );
|
193 |
+
$arr = explode( '.', $email_pref );
|
194 |
+
foreach ( $arr as $k => $v ) {
|
195 |
+
if ( preg_match( '!^' . preg_quote( $this->_punycode_prefix, '!' ) . '!', $v ) ) {
|
196 |
+
$conv = $this->_decode( $v );
|
197 |
+
if ( $conv ) {
|
198 |
+
$arr[ $k ] = $conv;
|
199 |
+
}
|
200 |
+
}
|
201 |
+
}
|
202 |
+
$email_pref = join( '.', $arr );
|
203 |
+
$return = $email_pref . '@' . $input;
|
204 |
+
} elseif ( preg_match( '![:\./]!', $input ) ) { // Or a complete domain name (with or without paths / parameters)
|
205 |
+
// No no in strict mode
|
206 |
+
if ( $this->_strict_mode ) {
|
207 |
+
$this->_error( 'Only simple domain name parts can be handled in strict mode' );
|
208 |
+
return false;
|
209 |
+
}
|
210 |
+
$parsed = parse_url( $input );
|
211 |
+
if ( isset( $parsed['host'] ) ) {
|
212 |
+
$arr = explode( '.', $parsed['host'] );
|
213 |
+
foreach ( $arr as $k => $v ) {
|
214 |
+
$conv = $this->_decode( $v );
|
215 |
+
if ( $conv ) {
|
216 |
+
$arr[ $k ] = $conv;
|
217 |
+
}
|
218 |
+
}
|
219 |
+
$parsed['host'] = join( '.', $arr );
|
220 |
+
$return =
|
221 |
+
( empty( $parsed['scheme'] ) ? '' : $parsed['scheme'] . ( strtolower( $parsed['scheme'] ) == 'mailto' ? ':' : '://' ) )
|
222 |
+
. ( empty( $parsed['user'] ) ? '' : $parsed['user'] . ( empty( $parsed['pass'] ) ? '' : ':' . $parsed['pass'] ) . '@' )
|
223 |
+
. $parsed['host']
|
224 |
+
. ( empty( $parsed['port'] ) ? '' : ':' . $parsed['port'] )
|
225 |
+
. ( empty( $parsed['path'] ) ? '' : $parsed['path'] )
|
226 |
+
. ( empty( $parsed['query'] ) ? '' : '?' . $parsed['query'] )
|
227 |
+
. ( empty( $parsed['fragment'] ) ? '' : '#' . $parsed['fragment'] );
|
228 |
+
} else { // parse_url seems to have failed, try without it
|
229 |
+
$arr = explode( '.', $input );
|
230 |
+
foreach ( $arr as $k => $v ) {
|
231 |
+
$conv = $this->_decode( $v );
|
232 |
+
$arr[ $k ] = ( $conv ) ? $conv : $v;
|
233 |
+
}
|
234 |
+
$return = join( '.', $arr );
|
235 |
+
}
|
236 |
+
} else { // Otherwise we consider it being a pure domain name string
|
237 |
+
$return = $this->_decode( $input );
|
238 |
+
if ( ! $return ) {
|
239 |
+
$return = $input;
|
240 |
+
}
|
241 |
+
}
|
242 |
+
// The output is UTF-8 by default, other output formats need conversion here
|
243 |
+
// If one time encoding is given, use this, else the objects property
|
244 |
+
switch ( ( $one_time_encoding ) ? $one_time_encoding : $this->_api_encoding ) {
|
245 |
+
case 'utf8':
|
246 |
+
return $return;
|
247 |
+
break;
|
248 |
+
case 'ucs4_string':
|
249 |
+
return $this->_ucs4_to_ucs4_string( $this->_utf8_to_ucs4( $return ) );
|
250 |
+
break;
|
251 |
+
case 'ucs4_array':
|
252 |
+
return $this->_utf8_to_ucs4( $return );
|
253 |
+
break;
|
254 |
+
default:
|
255 |
+
$this->_error( 'Unsupported output format' );
|
256 |
+
return false;
|
257 |
+
}
|
258 |
+
}
|
259 |
|
260 |
+
/**
|
261 |
+
* Encode a given UTF-8 domain name
|
262 |
+
* @param string Domain name (UTF-8 or UCS-4)
|
263 |
+
* [@param string Desired input encoding, see {@link set_parameter}]
|
264 |
+
* @return string Encoded Domain name (ACE string)
|
265 |
+
*/
|
266 |
+
public function encode( $decoded, $one_time_encoding = false ) {
|
267 |
+
// Forcing conversion of input to UCS4 array
|
268 |
+
// If one time encoding is given, use this, else the objects property
|
269 |
+
switch ( $one_time_encoding ? $one_time_encoding : $this->_api_encoding ) {
|
270 |
+
case 'utf8':
|
271 |
+
$decoded = $this->_utf8_to_ucs4( $decoded );
|
272 |
+
break;
|
273 |
+
case 'ucs4_string':
|
274 |
+
$decoded = $this->_ucs4_string_to_ucs4( $decoded );
|
275 |
+
//no break here
|
276 |
+
case 'ucs4_array':
|
277 |
+
break;
|
278 |
+
default:
|
279 |
+
$this->_error( 'Unsupported input format: ' . ( $one_time_encoding ? $one_time_encoding : $this->_api_encoding ) );
|
280 |
+
return false;
|
281 |
+
}
|
282 |
|
283 |
+
// No input, no output, what else did you expect?
|
284 |
+
if ( empty( $decoded ) ) {
|
285 |
+
return '';
|
286 |
+
}
|
287 |
|
288 |
+
// Anchors for iteration
|
289 |
+
$last_begin = 0;
|
290 |
+
// Output string
|
291 |
+
$output = '';
|
292 |
+
foreach ( $decoded as $k => $v ) {
|
293 |
+
// Make sure to use just the plain dot
|
294 |
+
switch ( $v ) {
|
295 |
+
case 0x3002:
|
296 |
+
case 0xFF0E:
|
297 |
+
case 0xFF61:
|
298 |
+
$decoded[ $k ] = 0x2E;
|
299 |
+
// Right, no break here, the above are converted to dots anyway
|
300 |
+
// Stumbling across an anchoring character
|
301 |
+
case 0x2E:
|
302 |
+
case 0x2F:
|
303 |
+
case 0x3A:
|
304 |
+
case 0x3F:
|
305 |
+
case 0x40:
|
306 |
+
// Neither email addresses nor URLs allowed in strict mode
|
307 |
+
if ( $this->_strict_mode ) {
|
308 |
+
$this->_error( 'Neither email addresses nor URLs are allowed in strict mode.' );
|
309 |
+
return false;
|
310 |
+
} else {
|
311 |
+
// Skip first char
|
312 |
+
if ( $k ) {
|
313 |
+
$encoded = '';
|
314 |
+
$encoded = $this->_encode( array_slice( $decoded, $last_begin, ( ( $k ) - $last_begin ) ) );
|
315 |
+
if ( $encoded ) {
|
316 |
+
$output .= $encoded;
|
317 |
+
} else {
|
318 |
+
$output .= $this->_ucs4_to_utf8( array_slice( $decoded, $last_begin, ( ( $k ) - $last_begin ) ) );
|
319 |
+
}
|
320 |
+
$output .= chr( $decoded[ $k ] );
|
321 |
+
}
|
322 |
+
$last_begin = $k + 1;
|
323 |
+
}
|
324 |
+
}
|
325 |
+
}
|
326 |
+
// Catch the rest of the string
|
327 |
+
if ( $last_begin ) {
|
328 |
+
$inp_len = sizeof( $decoded );
|
329 |
+
$encoded = '';
|
330 |
+
$encoded = $this->_encode( array_slice( $decoded, $last_begin, ( ( $inp_len ) - $last_begin ) ) );
|
331 |
+
if ( $encoded ) {
|
332 |
+
$output .= $encoded;
|
333 |
+
} else {
|
334 |
+
$output .= $this->_ucs4_to_utf8( array_slice( $decoded, $last_begin, ( ( $inp_len ) - $last_begin ) ) );
|
335 |
+
}
|
336 |
+
return $output;
|
337 |
+
} else {
|
338 |
+
$output == $this->_encode( $decoded );
|
339 |
+
if ( $output ) {
|
340 |
+
return $output;
|
341 |
+
} else {
|
342 |
+
return $this->_ucs4_to_utf8( $decoded );
|
343 |
+
}
|
344 |
+
}
|
345 |
+
}
|
346 |
|
347 |
+
/**
|
348 |
+
* Use this method to get the last error ocurred
|
349 |
+
* @param void
|
350 |
+
* @return string The last error, that occured
|
351 |
+
*/
|
352 |
+
public function get_last_error() {
|
353 |
+
return $this->_error;
|
354 |
+
}
|
|
|
355 |
|
356 |
+
/**
|
357 |
+
* The actual decoding algorithm
|
358 |
+
* @param string
|
359 |
+
* @return mixed
|
360 |
+
*/
|
361 |
+
private function _decode( $encoded ) {
|
362 |
+
$decoded = array();
|
363 |
+
// find the Punycode prefix
|
364 |
+
if ( ! preg_match( '!^' . preg_quote( $this->_punycode_prefix, '!' ) . '!', $encoded ) ) {
|
365 |
+
$this->_error( 'This is not a punycode string' );
|
366 |
+
return false;
|
367 |
+
}
|
368 |
+
$encode_test = preg_replace( '!^' . preg_quote( $this->_punycode_prefix, '!' ) . '!', '', $encoded );
|
369 |
+
// If nothing left after removing the prefix, it is hopeless
|
370 |
+
if ( ! $encode_test ) {
|
371 |
+
$this->_error( 'The given encoded string was empty' );
|
372 |
+
return false;
|
373 |
+
}
|
374 |
+
// Find last occurence of the delimiter
|
375 |
+
$delim_pos = strrpos( $encoded, '-' );
|
376 |
+
if ( $delim_pos > strlen( $this->_punycode_prefix ) ) {
|
377 |
+
for ( $k = strlen( $this->_punycode_prefix ); $k < $delim_pos; ++$k ) {
|
378 |
+
$decoded[] = ord( $encoded[ $k ] );
|
379 |
+
}
|
380 |
+
}
|
381 |
+
$deco_len = count( $decoded );
|
382 |
+
$enco_len = strlen( $encoded );
|
|
|
383 |
|
384 |
+
// Wandering through the strings; init
|
385 |
+
$is_first = true;
|
386 |
+
$bias = $this->_initial_bias;
|
387 |
+
$idx = 0;
|
388 |
+
$char = $this->_initial_n;
|
389 |
|
390 |
+
for ( $enco_idx = ( $delim_pos ) ? ( $delim_pos + 1 ) : 0; $enco_idx < $enco_len; ++$deco_len ) {
|
391 |
+
for ( $old_idx = $idx, $w = 1, $k = $this->_base; 1; $k += $this->_base ) {
|
392 |
+
$digit = $this->_decode_digit( $encoded[ $enco_idx++ ] );
|
393 |
+
$idx += $digit * $w;
|
394 |
+
$t = ( $k <= $bias ) ? $this->_tmin :
|
395 |
+
( ( $k >= $bias + $this->_tmax ) ? $this->_tmax : ( $k - $bias ) );
|
396 |
+
if ( $digit < $t ) {
|
397 |
+
break;
|
398 |
+
}
|
399 |
+
$w = (int) ( $w * ( $this->_base - $t ) );
|
400 |
+
}
|
401 |
+
$bias = $this->_adapt( $idx - $old_idx, $deco_len + 1, $is_first );
|
402 |
+
$is_first = false;
|
403 |
+
$char += (int) ( $idx / ( $deco_len + 1 ) );
|
404 |
+
$idx %= ( $deco_len + 1 );
|
405 |
+
if ( $deco_len > 0 ) {
|
406 |
+
// Make room for the decoded char
|
407 |
+
for ( $i = $deco_len; $i > $idx;
|
408 |
+
$i-- ) {
|
409 |
+
$decoded[ $i ] = $decoded[ ( $i - 1 ) ];
|
410 |
+
}
|
411 |
+
}
|
412 |
+
$decoded[ $idx++ ] = $char;
|
413 |
+
}
|
414 |
+
return $this->_ucs4_to_utf8( $decoded );
|
415 |
+
}
|
416 |
|
417 |
+
/**
|
418 |
+
* The actual encoding algorithm
|
419 |
+
* @param string
|
420 |
+
* @return mixed
|
421 |
+
*/
|
422 |
+
private function _encode( $decoded ) {
|
423 |
+
// We cannot encode a domain name containing the Punycode prefix
|
424 |
+
$extract = strlen( $this->_punycode_prefix );
|
425 |
+
$check_pref = $this->_utf8_to_ucs4( $this->_punycode_prefix );
|
426 |
+
$check_deco = array_slice( $decoded, 0, $extract );
|
|
|
427 |
|
428 |
+
if ( $check_pref == $check_deco ) {
|
429 |
+
$this->_error( 'This is already a punycode string' );
|
430 |
+
return false;
|
431 |
+
}
|
432 |
+
// We will not try to encode strings consisting of basic code points only
|
433 |
+
$encodable = false;
|
434 |
+
foreach ( $decoded as $k => $v ) {
|
435 |
+
if ( $v > 0x7a ) {
|
436 |
+
$encodable = true;
|
437 |
+
break;
|
438 |
+
}
|
439 |
+
}
|
440 |
+
if ( ! $encodable ) {
|
441 |
+
$this->_error( 'The given string does not contain encodable chars' );
|
442 |
+
return false;
|
443 |
+
}
|
444 |
+
// Do NAMEPREP
|
445 |
+
$decoded = $this->_nameprep( $decoded );
|
446 |
+
if ( ! $decoded || ! is_array( $decoded ) ) {
|
447 |
+
return false; // NAMEPREP failed
|
448 |
+
}
|
449 |
+
$deco_len = count( $decoded );
|
450 |
+
if ( ! $deco_len ) {
|
451 |
+
return false; // Empty array
|
452 |
+
}
|
453 |
+
$codecount = 0; // How many chars have been consumed
|
454 |
+
$encoded = '';
|
455 |
+
// Copy all basic code points to output
|
456 |
+
for ( $i = 0; $i < $deco_len; ++$i ) {
|
457 |
+
$test = $decoded[ $i ];
|
458 |
+
// Will match [-0-9a-zA-Z]
|
459 |
+
if ( ( 0x2F < $test && $test < 0x40 ) || ( 0x40 < $test && $test < 0x5B )
|
460 |
+
|| ( 0x60 < $test && $test <= 0x7B ) || ( 0x2D == $test ) ) {
|
461 |
+
$encoded .= chr( $decoded[ $i ] );
|
462 |
+
$codecount++;
|
463 |
+
}
|
464 |
+
}
|
465 |
+
if ( $codecount == $deco_len ) {
|
466 |
+
return $encoded; // All codepoints were basic ones
|
467 |
+
}
|
468 |
|
469 |
+
// Start with the prefix; copy it to output
|
470 |
+
$encoded = $this->_punycode_prefix . $encoded;
|
471 |
+
// If we have basic code points in output, add an hyphen to the end
|
472 |
+
if ( $codecount ) {
|
473 |
+
$encoded .= '-';
|
474 |
+
}
|
475 |
+
// Now find and encode all non-basic code points
|
476 |
+
$is_first = true;
|
477 |
+
$cur_code = $this->_initial_n;
|
478 |
+
$bias = $this->_initial_bias;
|
479 |
+
$delta = 0;
|
480 |
+
while ( $codecount < $deco_len ) {
|
481 |
+
// Find the smallest code point >= the current code point and
|
482 |
+
// remember the last ouccrence of it in the input
|
483 |
+
for ( $i = 0, $next_code = $this->_max_ucs; $i < $deco_len; $i++ ) {
|
484 |
+
if ( $decoded[ $i ] >= $cur_code && $decoded[ $i ] <= $next_code ) {
|
485 |
+
$next_code = $decoded[ $i ];
|
486 |
+
}
|
487 |
+
}
|
488 |
+
$delta += ( $next_code - $cur_code ) * ( $codecount + 1 );
|
489 |
+
$cur_code = $next_code;
|
490 |
|
491 |
+
// Scan input again and encode all characters whose code point is $cur_code
|
492 |
+
for ( $i = 0; $i < $deco_len; $i++ ) {
|
493 |
+
if ( $decoded[ $i ] < $cur_code ) {
|
494 |
+
$delta++;
|
495 |
+
} elseif ( $decoded[ $i ] == $cur_code ) {
|
496 |
+
for ( $q = $delta, $k = $this->_base; 1; $k += $this->_base ) {
|
497 |
+
$t = ( $k <= $bias ) ? $this->_tmin :
|
498 |
+
( ( $k >= $bias + $this->_tmax ) ? $this->_tmax : $k - $bias );
|
499 |
+
if ( $q < $t ) {
|
500 |
+
break;
|
501 |
+
}
|
502 |
+
$encoded .= $this->_encode_digit( intval( $t + ( ( $q - $t ) % ( $this->_base - $t ) ) ) ); //v0.4.5 Changed from ceil() to intval()
|
503 |
+
$q = (int) ( ( $q - $t ) / ( $this->_base - $t ) );
|
504 |
+
}
|
505 |
+
$encoded .= $this->_encode_digit( $q );
|
506 |
+
$bias = $this->_adapt( $delta, $codecount + 1, $is_first );
|
507 |
+
$codecount++;
|
508 |
+
$delta = 0;
|
509 |
+
$is_first = false;
|
510 |
+
}
|
511 |
+
}
|
512 |
+
$delta++;
|
513 |
+
$cur_code++;
|
514 |
+
}
|
515 |
+
return $encoded;
|
516 |
+
}
|
517 |
|
518 |
+
/**
|
519 |
+
* Adapt the bias according to the current code point and position
|
520 |
+
* @param int $delta
|
521 |
+
* @param int $npoints
|
522 |
+
* @param int $is_first
|
523 |
+
* @return int
|
524 |
+
*/
|
525 |
+
private function _adapt( $delta, $npoints, $is_first ) {
|
526 |
+
$delta = intval( $is_first ? ( $delta / $this->_damp ) : ( $delta / 2 ) );
|
527 |
+
$delta += intval( $delta / $npoints );
|
528 |
+
for ( $k = 0; $delta > ( ( $this->_base - $this->_tmin ) * $this->_tmax ) / 2; $k += $this->_base ) {
|
529 |
+
$delta = intval( $delta / ( $this->_base - $this->_tmin ) );
|
530 |
+
}
|
531 |
+
return intval( $k + ( $this->_base - $this->_tmin + 1 ) * $delta / ( $delta + $this->_skew ) );
|
532 |
+
}
|
|
|
533 |
|
534 |
+
/**
|
535 |
+
* Encoding a certain digit
|
536 |
+
* @param int $d
|
537 |
+
* @return string
|
538 |
+
*/
|
539 |
+
private function _encode_digit( $d ) {
|
540 |
+
return chr( $d + 22 + 75 * ( $d < 26 ) );
|
541 |
+
}
|
|
|
542 |
|
543 |
+
/**
|
544 |
+
* Decode a certain digit
|
545 |
+
* @param int $cp
|
546 |
+
* @return int
|
547 |
+
*/
|
548 |
+
private function _decode_digit( $cp ) {
|
549 |
+
$cp = ord( $cp );
|
550 |
+
return ( $cp - 48 < 10 ) ? $cp - 22 : ( ( $cp - 65 < 26 ) ? $cp - 65 : ( ( $cp - 97 < 26 ) ? $cp - 97 : $this->_base ) );
|
551 |
+
}
|
|
|
552 |
|
553 |
+
/**
|
554 |
+
* Internal error handling method
|
555 |
+
* @param string $error
|
556 |
+
*/
|
557 |
+
private function _error( $error = '' ) {
|
558 |
+
$this->_error = $error;
|
559 |
+
}
|
|
|
560 |
|
561 |
+
/**
|
562 |
+
* Do Nameprep according to RFC3491 and RFC3454
|
563 |
+
* @param array Unicode Characters
|
564 |
+
* @return string Unicode Characters, Nameprep'd
|
565 |
+
*/
|
566 |
+
private function _nameprep( $input ) {
|
567 |
+
$output = array();
|
568 |
+
$error = false;
|
569 |
+
//
|
570 |
+
// Mapping
|
571 |
+
// Walking through the input array, performing the required steps on each of
|
572 |
+
// the input chars and putting the result into the output array
|
573 |
+
// While mapping required chars we apply the cannonical ordering
|
574 |
+
foreach ( $input as $v ) {
|
575 |
+
// Map to nothing == skip that code point
|
576 |
+
if ( in_array( $v, $this->NP['map_nothing'] ) ) {
|
577 |
+
continue;
|
578 |
+
}
|
579 |
+
// Try to find prohibited input
|
580 |
+
if ( in_array( $v, $this->NP['prohibit'] ) || in_array( $v, $this->NP['general_prohibited'] ) ) {
|
581 |
+
$this->_error( 'NAMEPREP: Prohibited input U+' . sprintf( '%08X', $v ) );
|
582 |
+
return false;
|
583 |
+
}
|
584 |
+
foreach ( $this->NP['prohibit_ranges'] as $range ) {
|
585 |
+
if ( $range[0] <= $v && $v <= $range[1] ) {
|
586 |
+
$this->_error( 'NAMEPREP: Prohibited input U+' . sprintf( '%08X', $v ) );
|
587 |
+
return false;
|
588 |
+
}
|
589 |
+
}
|
590 |
+
// Hangul syllable decomposition
|
591 |
+
if ( 0xAC00 <= $v && $v <= 0xD7AF ) {
|
592 |
+
foreach ( $this->_hangul_decompose( $v ) as $out ) {
|
593 |
+
$output[] = (int) $out;
|
594 |
+
}
|
595 |
+
// There's a decomposition mapping for that code point
|
596 |
+
} elseif ( isset( $this->NP['replacemaps'][ $v ] ) ) {
|
597 |
+
foreach ( $this->_apply_cannonical_ordering( $this->NP['replacemaps'][ $v ] ) as $out ) {
|
598 |
+
$output[] = (int) $out;
|
599 |
+
}
|
600 |
+
} else {
|
601 |
+
$output[] = (int) $v;
|
602 |
+
}
|
603 |
+
}
|
604 |
+
// Before applying any Combining, try to rearrange any Hangul syllables
|
605 |
+
$output = $this->_hangul_compose( $output );
|
606 |
+
//
|
607 |
+
// Combine code points
|
608 |
+
//
|
609 |
+
$last_class = 0;
|
610 |
+
$last_starter = 0;
|
611 |
+
$out_len = count( $output );
|
612 |
+
for ( $i = 0; $i < $out_len; ++$i ) {
|
613 |
+
$class = $this->_get_combining_class( $output[ $i ] );
|
614 |
+
if ( ( ! $last_class || $last_class > $class ) && $class ) {
|
615 |
+
// Try to match
|
616 |
+
$seq_len = $i - $last_starter;
|
617 |
+
$out = $this->_combine( array_slice( $output, $last_starter, $seq_len ) );
|
618 |
+
// On match: Replace the last starter with the composed character and remove
|
619 |
+
// the now redundant non-starter(s)
|
620 |
+
if ( $out ) {
|
621 |
+
$output[ $last_starter ] = $out;
|
622 |
+
if ( count( $out ) != $seq_len ) {
|
623 |
+
for ( $j = $i + 1; $j < $out_len;
|
624 |
+
++$j ) {
|
625 |
+
$output[ $j - 1 ] = $output[ $j ];
|
626 |
+
}
|
627 |
+
unset( $output[ $out_len ] );
|
628 |
+
}
|
629 |
+
// Rewind the for loop by one, since there can be more possible compositions
|
630 |
+
$i--;
|
631 |
+
$out_len--;
|
632 |
+
$last_class = ( $i == $last_starter ) ? 0 : $this->_get_combining_class( $output[ $i - 1 ] );
|
633 |
+
continue;
|
634 |
+
}
|
635 |
+
}
|
636 |
+
// The current class is 0
|
637 |
+
if ( ! $class ) {
|
638 |
+
$last_starter = $i;
|
639 |
+
}
|
640 |
+
$last_class = $class;
|
641 |
+
}
|
642 |
+
return $output;
|
643 |
+
}
|
644 |
|
645 |
+
/**
|
646 |
+
* Decomposes a Hangul syllable
|
647 |
+
* (see http://www.unicode.org/unicode/reports/tr15/#Hangul
|
648 |
+
* @param integer 32bit UCS4 code point
|
649 |
+
* @return array Either Hangul Syllable decomposed or original 32bit value as one value array
|
650 |
+
*/
|
651 |
+
private function _hangul_decompose( $char ) {
|
652 |
+
$sindex = (int) $char - $this->_sbase;
|
653 |
+
if ( $sindex < 0 || $sindex >= $this->_scount ) {
|
654 |
+
return array( $char );
|
655 |
+
}
|
656 |
+
$result = array();
|
657 |
+
$result[] = (int) $this->_lbase + $sindex / $this->_ncount;
|
658 |
+
$result[] = (int) $this->_vbase + ( $sindex % $this->_ncount ) / $this->_tcount;
|
659 |
+
$T = intval( $this->_tbase + $sindex % $this->_tcount );
|
660 |
+
if ( $T != $this->_tbase ) {
|
661 |
+
$result[] = $T;
|
662 |
+
}
|
663 |
+
return $result;
|
664 |
+
}
|
665 |
+
/**
|
666 |
+
* Ccomposes a Hangul syllable
|
667 |
+
* (see http://www.unicode.org/unicode/reports/tr15/#Hangul
|
668 |
+
* @param array Decomposed UCS4 sequence
|
669 |
+
* @return array UCS4 sequence with syllables composed
|
670 |
+
*/
|
671 |
+
private function _hangul_compose( $input ) {
|
672 |
+
$inp_len = count( $input );
|
673 |
+
if ( ! $inp_len ) {
|
674 |
+
return array();
|
675 |
+
}
|
676 |
+
$result = array();
|
677 |
+
$last = (int) $input[0];
|
678 |
+
$result[] = $last; // copy first char from input to output
|
679 |
|
680 |
+
for ( $i = 1; $i < $inp_len; ++$i ) {
|
681 |
+
$char = (int) $input[ $i ];
|
682 |
+
$sindex = $last - $this->_sbase;
|
683 |
+
$lindex = $last - $this->_lbase;
|
684 |
+
$vindex = $char - $this->_vbase;
|
685 |
+
$tindex = $char - $this->_tbase;
|
686 |
+
// Find out, whether two current characters are LV and T
|
687 |
+
if ( 0 <= $sindex && $sindex < $this->_scount && ( 0 == $sindex % $this->_tcount )
|
688 |
+
&& 0 <= $tindex && $tindex <= $this->_tcount ) {
|
689 |
+
// create syllable of form LVT
|
690 |
+
$last += $tindex;
|
691 |
+
$result[ ( count( $result ) - 1 ) ] = $last; // reset last
|
692 |
+
continue; // discard char
|
693 |
+
}
|
694 |
+
// Find out, whether two current characters form L and V
|
695 |
+
if ( 0 <= $lindex && $lindex < $this->_lcount && 0 <= $vindex && $vindex < $this->_vcount ) {
|
696 |
+
// create syllable of form LV
|
697 |
+
$last = (int) $this->_sbase + ( $lindex * $this->_vcount + $vindex ) * $this->_tcount;
|
698 |
+
$result[ ( count( $result ) - 1 ) ] = $last; // reset last
|
699 |
+
continue; // discard char
|
700 |
+
}
|
701 |
+
// if neither case was true, just add the character
|
702 |
+
$last = $char;
|
703 |
+
$result[] = $char;
|
704 |
+
}
|
705 |
+
return $result;
|
706 |
+
}
|
707 |
|
708 |
+
/**
|
709 |
+
* Returns the combining class of a certain wide char
|
710 |
+
* @param integer Wide char to check (32bit integer)
|
711 |
+
* @return integer Combining class if found, else 0
|
712 |
+
*/
|
713 |
+
private function _get_combining_class( $char ) {
|
714 |
+
return isset( $this->NP['norm_combcls'][ $char ] ) ? $this->NP['norm_combcls'][ $char ] : 0;
|
715 |
+
}
|
|
|
716 |
|
717 |
+
/**
|
718 |
+
* Apllies the cannonical ordering of a decomposed UCS4 sequence
|
719 |
+
* @param array Decomposed UCS4 sequence
|
720 |
+
* @return array Ordered USC4 sequence
|
721 |
+
*/
|
722 |
+
private function _apply_cannonical_ordering( $input ) {
|
723 |
+
$swap = true;
|
724 |
+
$size = count( $input );
|
725 |
+
while ( $swap ) {
|
726 |
+
$swap = false;
|
727 |
+
$last = $this->_get_combining_class( intval( $input[0] ) );
|
728 |
+
for ( $i = 0; $i < $size - 1; ++$i ) {
|
729 |
+
$next = $this->_get_combining_class( intval( $input[ $i + 1 ] ) );
|
730 |
+
if ( 0 != $next && $last > $next ) {
|
731 |
+
// Move item leftward until it fits
|
732 |
+
for ( $j = $i + 1; $j > 0; --$j ) {
|
733 |
+
if ( $this->_get_combining_class( intval( $input[ $j - 1 ] ) ) <= $next ) {
|
734 |
+
break;
|
735 |
+
}
|
736 |
+
$t = intval( $input[ $j ] );
|
737 |
+
$input[ $j ] = intval( $input[ $j - 1 ] );
|
738 |
+
$input[ $j - 1 ] = $t;
|
739 |
+
$swap = true;
|
740 |
+
}
|
741 |
+
// Reentering the loop looking at the old character again
|
742 |
+
$next = $last;
|
743 |
+
}
|
744 |
+
$last = $next;
|
745 |
+
}
|
746 |
+
}
|
747 |
+
return $input;
|
748 |
+
}
|
749 |
|
750 |
+
/**
|
751 |
+
* Do composition of a sequence of starter and non-starter
|
752 |
+
* @param array UCS4 Decomposed sequence
|
753 |
+
* @return array Ordered USC4 sequence
|
754 |
+
*/
|
755 |
+
private function _combine( $input ) {
|
756 |
+
$inp_len = count( $input );
|
757 |
+
foreach ( $this->NP['replacemaps'] as $np_src => $np_target ) {
|
758 |
+
if ( $np_target[0] != $input[0] ) {
|
759 |
+
continue;
|
760 |
+
}
|
761 |
+
if ( count( $np_target ) != $inp_len ) {
|
762 |
+
continue;
|
763 |
+
}
|
764 |
+
$hit = false;
|
765 |
+
foreach ( $input as $k2 => $v2 ) {
|
766 |
+
if ( $v2 == $np_target[ $k2 ] ) {
|
767 |
+
$hit = true;
|
768 |
+
} else {
|
769 |
+
$hit = false;
|
770 |
+
break;
|
771 |
+
}
|
772 |
+
}
|
773 |
+
if ( $hit ) {
|
774 |
+
return $np_src;
|
775 |
+
}
|
776 |
+
}
|
777 |
+
return false;
|
778 |
+
}
|
779 |
|
780 |
+
/**
|
781 |
+
* This converts an UTF-8 encoded string to its UCS-4 representation
|
782 |
+
* By talking about UCS-4 "strings" we mean arrays of 32bit integers representing
|
783 |
+
* each of the "chars". This is due to PHP not being able to handle strings with
|
784 |
+
* bit depth different from 8. This apllies to the reverse method _ucs4_to_utf8(), too.
|
785 |
+
* The following UTF-8 encodings are supported:
|
786 |
+
* bytes bits representation
|
787 |
+
* 1 7 0xxxxxxx
|
788 |
+
* 2 11 110xxxxx 10xxxxxx
|
789 |
+
* 3 16 1110xxxx 10xxxxxx 10xxxxxx
|
790 |
+
* 4 21 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
791 |
+
* 5 26 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
|
792 |
+
* 6 31 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
|
793 |
+
* Each x represents a bit that can be used to store character data.
|
794 |
+
* The five and six byte sequences are part of Annex D of ISO/IEC 10646-1:2000
|
795 |
+
* @param string $input
|
796 |
+
* @return string
|
797 |
+
*/
|
798 |
+
private function _utf8_to_ucs4( $input ) {
|
799 |
+
$output = array();
|
800 |
+
$out_len = 0;
|
801 |
+
// Patch by Daniel Hahler; work around prolbem with mbstring.func_overload
|
802 |
+
if ( function_exists( 'mb_strlen' ) ) {
|
803 |
+
$inp_len = mb_strlen( $input, '8bit' );
|
804 |
+
} else {
|
805 |
+
$inp_len = strlen( $input );
|
806 |
+
}
|
807 |
+
$mode = 'next';
|
808 |
+
$test = 'none';
|
809 |
+
for ( $k = 0; $k < $inp_len; ++$k ) {
|
810 |
+
$v = ord( $input[ $k ] ); // Extract byte from input string
|
811 |
+
if ( $v < 128 ) { // We found an ASCII char - put into stirng as is
|
812 |
+
$output[ $out_len ] = $v;
|
813 |
+
++$out_len;
|
814 |
+
if ( 'add' == $mode ) {
|
815 |
+
$this->_error( 'Conversion from UTF-8 to UCS-4 failed: malformed input at byte ' . $k );
|
816 |
+
return false;
|
817 |
+
}
|
818 |
+
continue;
|
819 |
+
}
|
820 |
+
if ( 'next' == $mode ) { // Try to find the next start byte; determine the width of the Unicode char
|
821 |
+
$start_byte = $v;
|
822 |
+
$mode = 'add';
|
823 |
+
$test = 'range';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
824 |
|
825 |
+
// &110xxxxx 10xxxxx
|
826 |
+
if ( $v >> 5 == 6 ) { //phpcs:ignore WordPress.PHP.YodaConditions.NotYoda
|
827 |
+
$next_byte = 0; // Tells, how many times subsequent bitmasks must rotate 6bits to the left
|
828 |
+
$v = ( $v - 192 ) << 6;
|
829 |
+
//phpcs:ignore WordPress.PHP.YodaConditions.NotYoda
|
830 |
+
} elseif ( $v >> 4 == 14 ) { //&1110xxxx 10xxxxxx 10xxxxxx
|
831 |
+
$next_byte = 1;
|
832 |
+
$v = ( $v - 224 ) << 12;
|
833 |
+
//phpcs:ignore WordPress.PHP.YodaConditions.NotYoda
|
834 |
+
} elseif ( $v >> 3 == 30 ) { // &11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
835 |
+
$next_byte = 2;
|
836 |
+
$v = ( $v - 240 ) << 18;
|
837 |
+
//phpcs:ignore WordPress.PHP.YodaConditions.NotYoda
|
838 |
+
} elseif ( $v >> 2 == 62 ) { // &111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
|
839 |
+
$next_byte = 3;
|
840 |
+
$v = ( $v - 248 ) << 24;
|
841 |
+
//phpcs:ignore WordPress.PHP.YodaConditions.NotYoda
|
842 |
+
} elseif ( $v >> 1 == 126 ) { // &1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
|
843 |
+
$next_byte = 4;
|
844 |
+
$v = ( $v - 252 ) << 30;
|
845 |
+
} else {
|
846 |
+
$this->_error( 'This might be UTF-8, but I don\'t understand it at byte ' . $k );
|
847 |
+
return false;
|
848 |
+
}
|
849 |
+
if ( 'add' == $mode ) {
|
850 |
+
$output[ $out_len ] = (int) $v;
|
851 |
+
++$out_len;
|
852 |
+
continue;
|
853 |
+
}
|
854 |
+
}
|
855 |
+
if ( 'add' == $mode ) {
|
856 |
+
if ( ! $this->_allow_overlong && 'range' === $test ) {
|
857 |
+
$test = 'none';
|
858 |
+
if ( ( $v < 0xA0 && 0xE0 == $start_byte ) || ( $v < 0x90 && 0xF0 == $start_byte ) || ( $v > 0x8F && 0xF4 == $start_byte ) ) {
|
859 |
+
$this->_error( 'Bogus UTF-8 character detected (out of legal range) at byte ' . $k );
|
860 |
+
return false;
|
861 |
+
}
|
862 |
+
}
|
863 |
+
//phpcs:ignore WordPress.PHP.YodaConditions.NotYoda
|
864 |
+
if ( $v >> 6 == 2 ) { // Bit mask must be 10xxxxxx
|
865 |
+
$v = ( $v - 128 ) << ( $next_byte * 6 );
|
866 |
+
$output[ ( $out_len - 1 ) ] += $v;
|
867 |
+
--$next_byte;
|
868 |
+
} else {
|
869 |
+
$this->_error( 'Conversion from UTF-8 to UCS-4 failed: malformed input at byte ' . $k );
|
870 |
+
return false;
|
871 |
+
}
|
872 |
+
if ( $next_byte < 0 ) {
|
873 |
+
$mode = 'next';
|
874 |
+
}
|
875 |
+
}
|
876 |
+
} // for
|
877 |
+
return $output;
|
878 |
+
}
|
879 |
|
880 |
+
/**
|
881 |
+
* Convert UCS-4 string into UTF-8 string
|
882 |
+
* See _utf8_to_ucs4() for details
|
883 |
+
* @param string $input
|
884 |
+
* @return string
|
885 |
+
*/
|
886 |
+
private function _ucs4_to_utf8( $input ) {
|
887 |
+
$output = '';
|
888 |
+
foreach ( $input as $k => $v ) {
|
889 |
+
if ( $v < 128 ) { // 7bit are transferred literally
|
890 |
+
$output .= chr( $v );
|
891 |
+
} elseif ( $v < ( 1 << 11 ) ) { // 2 bytes
|
892 |
+
$output .= chr( 192 + ( $v >> 6 ) ) . chr( 128 + ( $v & 63 ) );
|
893 |
+
} elseif ( $v < ( 1 << 16 ) ) { // 3 bytes
|
894 |
+
$output .= chr( 224 + ( $v >> 12 ) ) . chr( 128 + ( ( $v >> 6 ) & 63 ) ) . chr( 128 + ( $v & 63 ) );
|
895 |
+
} elseif ( $v < ( 1 << 21 ) ) { // 4 bytes
|
896 |
+
$output .= chr( 240 + ( $v >> 18 ) ) . chr( 128 + ( ( $v >> 12 ) & 63 ) ) . chr( 128 + ( ( $v >> 6 ) & 63 ) ) . chr( 128 + ( $v & 63 ) );
|
897 |
+
} elseif ( self::$safe_mode ) {
|
898 |
+
$output .= self::$safe_char;
|
899 |
+
} else {
|
900 |
+
$this->_error( 'Conversion from UCS-4 to UTF-8 failed: malformed input at byte ' . $k );
|
901 |
+
return false;
|
902 |
+
}
|
903 |
+
}
|
904 |
+
return $output;
|
905 |
+
}
|
906 |
|
907 |
+
/**
|
908 |
+
* Convert UCS-4 array into UCS-4 string
|
909 |
+
*
|
910 |
+
* @param array $input
|
911 |
+
* @return string
|
912 |
+
*/
|
913 |
+
private function _ucs4_to_ucs4_string( $input ) {
|
914 |
+
$output = '';
|
915 |
+
// Take array values and split output to 4 bytes per value
|
916 |
+
// The bit mask is 255, which reads &11111111
|
917 |
+
foreach ( $input as $v ) {
|
918 |
+
$output .= chr( ( $v >> 24 ) & 255 ) . chr( ( $v >> 16 ) & 255 ) . chr( ( $v >> 8 ) & 255 ) . chr( $v & 255 );
|
919 |
+
}
|
920 |
+
return $output;
|
921 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
922 |
|
923 |
+
/**
|
924 |
+
* Convert UCS-4 strin into UCS-4 garray
|
925 |
+
*
|
926 |
+
* @param string $input
|
927 |
+
* @return array
|
928 |
+
*/
|
929 |
+
private function _ucs4_string_to_ucs4( $input ) {
|
930 |
+
$output = array();
|
931 |
+
$inp_len = strlen( $input );
|
932 |
+
// Input length must be dividable by 4
|
933 |
+
if ( $inp_len % 4 ) {
|
934 |
+
$this->_error( 'Input UCS4 string is broken' );
|
935 |
+
return false;
|
936 |
+
}
|
937 |
+
// Empty input - return empty output
|
938 |
+
if ( ! $inp_len ) {
|
939 |
+
return $output;
|
940 |
+
}
|
941 |
+
for ( $i = 0, $out_len = -1; $i < $inp_len; ++$i ) {
|
942 |
+
// Increment output position every 4 input bytes
|
943 |
+
if ( ! ( $i % 4 ) ) {
|
944 |
+
$out_len++;
|
945 |
+
$output[ $out_len ] = 0;
|
946 |
+
}
|
947 |
+
$output[ $out_len ] += ord( $input[ $i ] ) << ( 8 * ( 3 - ( $i % 4 ) ) );
|
948 |
+
}
|
949 |
+
return $output;
|
950 |
+
}
|
951 |
+
/**
|
952 |
* Holds all relevant mapping tables, loaded from a seperate file on construct
|
953 |
* See RFC3454 for details
|
954 |
*
|
idn/transcode_wrapper.php
CHANGED
@@ -6,7 +6,7 @@
|
|
6 |
* @author Matthias Sommerfeld, <mso@phlylabs.de>
|
7 |
* @version 0.1.0
|
8 |
*/
|
9 |
-
|
10 |
/**
|
11 |
* Convert a string from any of various encodings to UTF-8
|
12 |
*
|
@@ -16,31 +16,36 @@
|
|
16 |
* @return string The encoded string or false on failure
|
17 |
* @since 0.0.1
|
18 |
*/
|
19 |
-
function encode_utf8($string = '', $encoding = 'iso-8859-1', $safe_mode = false)
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
|
|
|
|
|
|
|
|
|
|
44 |
}
|
45 |
|
46 |
/**
|
@@ -52,32 +57,39 @@ function encode_utf8($string = '', $encoding = 'iso-8859-1', $safe_mode = false)
|
|
52 |
* @return string The decoded string or false on failure
|
53 |
* @since 0.0.1
|
54 |
*/
|
55 |
-
function decode_utf8($string = '', $encoding = 'iso-8859-1', $safe_mode = false)
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
81 |
}
|
82 |
|
83 |
/**
|
@@ -87,24 +99,41 @@ function decode_utf8($string = '', $encoding = 'iso-8859-1', $safe_mode = false)
|
|
87 |
* @param string The resulting ISO-8859-1 string
|
88 |
* @since 3.0.8
|
89 |
*/
|
90 |
-
function map_w1252_iso8859_1($string = '')
|
91 |
-
{
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
108 |
}
|
109 |
|
110 |
/**
|
@@ -114,23 +143,40 @@ function map_w1252_iso8859_1($string = '')
|
|
114 |
* @param string The resulting Win1252 string
|
115 |
* @since 3.0.8
|
116 |
*/
|
117 |
-
function map_iso8859_1_w1252($string = '')
|
118 |
-
{
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
135 |
}
|
136 |
|
6 |
* @author Matthias Sommerfeld, <mso@phlylabs.de>
|
7 |
* @version 0.1.0
|
8 |
*/
|
9 |
+
|
10 |
/**
|
11 |
* Convert a string from any of various encodings to UTF-8
|
12 |
*
|
16 |
* @return string The encoded string or false on failure
|
17 |
* @since 0.0.1
|
18 |
*/
|
19 |
+
function encode_utf8( $string = '', $encoding = 'iso-8859-1', $safe_mode = false ) {
|
20 |
+
$safe = ( $safe_mode ) ? $string : false;
|
21 |
+
if ( strtoupper( $encoding ) == 'UTF-8' || strtoupper( $encoding ) == 'UTF8' ) {
|
22 |
+
return $string;
|
23 |
+
} elseif ( strtoupper( $encoding ) == 'ISO-8859-1' ) {
|
24 |
+
return utf8_encode( $string );
|
25 |
+
} elseif ( strtoupper( $encoding ) == 'WINDOWS-1252' ) {
|
26 |
+
return utf8_encode( map_w1252_iso8859_1( $string ) );
|
27 |
+
} elseif ( strtoupper( $encoding ) == 'UNICODE-1-1-UTF-7' ) {
|
28 |
+
$encoding = 'utf-7';
|
29 |
+
}
|
30 |
+
if ( function_exists( 'mb_convert_encoding' ) ) {
|
31 |
+
$conv = @mb_convert_encoding( $string, 'UTF-8', strtoupper( $encoding ) );
|
32 |
+
if ( $conv ) {
|
33 |
+
return $conv;
|
34 |
+
}
|
35 |
+
}
|
36 |
+
if ( function_exists( 'iconv' ) ) {
|
37 |
+
$conv = @iconv( strtoupper( $encoding ), 'UTF-8', $string );
|
38 |
+
if ( $conv ) {
|
39 |
+
return $conv;
|
40 |
+
}
|
41 |
+
}
|
42 |
+
if ( function_exists( 'libiconv' ) ) {
|
43 |
+
$conv = @libiconv( strtoupper( $encoding ), 'UTF-8', $string );
|
44 |
+
if ( $conv ) {
|
45 |
+
return $conv;
|
46 |
+
}
|
47 |
+
}
|
48 |
+
return $safe;
|
49 |
}
|
50 |
|
51 |
/**
|
57 |
* @return string The decoded string or false on failure
|
58 |
* @since 0.0.1
|
59 |
*/
|
60 |
+
function decode_utf8( $string = '', $encoding = 'iso-8859-1', $safe_mode = false ) {
|
61 |
+
$safe = ( $safe_mode ) ? $string : false;
|
62 |
+
if ( ! $encoding ) {
|
63 |
+
$encoding = 'ISO-8859-1';
|
64 |
+
}
|
65 |
+
if ( strtoupper( $encoding ) == 'UTF-8' || strtoupper( $encoding ) == 'UTF8' ) {
|
66 |
+
return $string;
|
67 |
+
} elseif ( strtoupper( $encoding ) == 'ISO-8859-1' ) {
|
68 |
+
return utf8_decode( $string );
|
69 |
+
} elseif ( strtoupper( $encoding ) == 'WINDOWS-1252' ) {
|
70 |
+
return map_iso8859_1_w1252( utf8_decode( $string ) );
|
71 |
+
} elseif ( strtoupper( $encoding ) == 'UNICODE-1-1-UTF-7' ) {
|
72 |
+
$encoding = 'utf-7';
|
73 |
+
}
|
74 |
+
if ( function_exists( 'mb_convert_encoding' ) ) {
|
75 |
+
$conv = @mb_convert_encoding( $string, strtoupper( $encoding ), 'UTF-8' );
|
76 |
+
if ( $conv ) {
|
77 |
+
return $conv;
|
78 |
+
}
|
79 |
+
}
|
80 |
+
if ( function_exists( 'iconv' ) ) {
|
81 |
+
$conv = @iconv( 'UTF-8', strtoupper( $encoding ), $string );
|
82 |
+
if ( $conv ) {
|
83 |
+
return $conv;
|
84 |
+
}
|
85 |
+
}
|
86 |
+
if ( function_exists( 'libiconv' ) ) {
|
87 |
+
$conv = @libiconv( 'UTF-8', strtoupper( $encoding ), $string );
|
88 |
+
if ( $conv ) {
|
89 |
+
return $conv;
|
90 |
+
}
|
91 |
+
}
|
92 |
+
return $safe;
|
93 |
}
|
94 |
|
95 |
/**
|
99 |
* @param string The resulting ISO-8859-1 string
|
100 |
* @since 3.0.8
|
101 |
*/
|
102 |
+
function map_w1252_iso8859_1( $string = '' ) {
|
103 |
+
if ( '' == $string ) {
|
104 |
+
return '';
|
105 |
+
}
|
106 |
+
$return = '';
|
107 |
+
for ( $i = 0; $i < strlen( $string ); ++$i ) {
|
108 |
+
$c = ord( $string[ $i ] );
|
109 |
+
switch ( $c ) {
|
110 |
+
case 129:
|
111 |
+
$return .= chr( 252 );
|
112 |
+
break;
|
113 |
+
case 132:
|
114 |
+
$return .= chr( 228 );
|
115 |
+
break;
|
116 |
+
case 142:
|
117 |
+
$return .= chr( 196 );
|
118 |
+
break;
|
119 |
+
case 148:
|
120 |
+
$return .= chr( 246 );
|
121 |
+
break;
|
122 |
+
case 153:
|
123 |
+
$return .= chr( 214 );
|
124 |
+
break;
|
125 |
+
case 154:
|
126 |
+
$return .= chr( 220 );
|
127 |
+
break;
|
128 |
+
case 225:
|
129 |
+
$return .= chr( 223 );
|
130 |
+
break;
|
131 |
+
default:
|
132 |
+
$return .= chr( $c );
|
133 |
+
break;
|
134 |
+
}
|
135 |
+
}
|
136 |
+
return $return;
|
137 |
}
|
138 |
|
139 |
/**
|
143 |
* @param string The resulting Win1252 string
|
144 |
* @since 3.0.8
|
145 |
*/
|
146 |
+
function map_iso8859_1_w1252( $string = '' ) {
|
147 |
+
if ( '' == $string ) {
|
148 |
+
return '';
|
149 |
+
}
|
150 |
+
$return = '';
|
151 |
+
for ( $i = 0; $i < strlen( $string ); ++$i ) {
|
152 |
+
$c = ord( $string[ $i ] );
|
153 |
+
switch ( $c ) {
|
154 |
+
case 196:
|
155 |
+
$return .= chr( 142 );
|
156 |
+
break;
|
157 |
+
case 214:
|
158 |
+
$return .= chr( 153 );
|
159 |
+
break;
|
160 |
+
case 220:
|
161 |
+
$return .= chr( 154 );
|
162 |
+
break;
|
163 |
+
case 223:
|
164 |
+
$return .= chr( 225 );
|
165 |
+
break;
|
166 |
+
case 228:
|
167 |
+
$return .= chr( 132 );
|
168 |
+
break;
|
169 |
+
case 246:
|
170 |
+
$return .= chr( 148 );
|
171 |
+
break;
|
172 |
+
case 252:
|
173 |
+
$return .= chr( 129 );
|
174 |
+
break;
|
175 |
+
default:
|
176 |
+
$return .= chr( $c );
|
177 |
+
break;
|
178 |
+
}
|
179 |
+
}
|
180 |
+
return $return;
|
181 |
}
|
182 |
|
idn/uctc.php
CHANGED
@@ -1,297 +1,310 @@
|
|
1 |
<?php
|
2 |
-
/**
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
class uctc {
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
$v = ord($input{$k}); // Extract byte from input string
|
62 |
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
|
|
|
|
|
|
102 |
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
|
|
137 |
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
}
|
163 |
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
}
|
168 |
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
$b64 = '';
|
176 |
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
|
|
|
|
219 |
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
}
|
224 |
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
|
|
257 |
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
}
|
270 |
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
$output = array();
|
278 |
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
|
|
|
|
286 |
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
|
297 |
}
|
1 |
<?php
|
2 |
+
/**
|
3 |
+
* UCTC - The Unicode Transcoder
|
4 |
+
*
|
5 |
+
* Converts between various flavours of Unicode representations like UCS-4 or UTF8
|
6 |
+
* Supported schemes:
|
7 |
+
* - UCS-4 Little Endian / Big Endian / Array (partially)
|
8 |
+
* - UTF-16 Little Endian / Big Endian (not yet)
|
9 |
+
* - UTF-8
|
10 |
+
* - UTF-7
|
11 |
+
* - UTF-7 IMAP (modified UTF-7)
|
12 |
+
*
|
13 |
+
* @author Matthias Sommerfeld <mso@phlylabs.de>
|
14 |
+
* @version 0.0.5
|
15 |
+
* @package phlyMail
|
16 |
+
*/
|
17 |
class uctc {
|
18 |
+
private static $mechs = array( 'ucs4', /*'ucs4le', 'ucs4be', */'ucs4array', /*'utf16', 'utf16le', 'utf16be', */'utf8', 'utf7', 'utf7imap' );
|
19 |
+
private static $allow_overlong = false;
|
20 |
+
private static $safe_mode;
|
21 |
+
private static $safe_char;
|
22 |
|
23 |
+
/**
|
24 |
+
* The actual conversion routine
|
25 |
+
*
|
26 |
+
* @param mixed $data The data to convert, usually a string, array when converting from UCS-4 array
|
27 |
+
* @param string $from Original encoding of the data
|
28 |
+
* @param string $to Target encoding of the data
|
29 |
+
* @param bool $safe_mode SafeMode tries to correct invalid codepoints
|
30 |
+
* @return mixed False on failure, String or array on success, depending on target encoding
|
31 |
+
* @access public
|
32 |
+
* @since 0.0.1
|
33 |
+
*/
|
34 |
+
public static function convert( $data, $from, $to, $safe_mode = false, $safe_char = 0xFFFC ) {
|
35 |
+
self::$safe_mode = ( $safe_mode ) ? true : false;
|
36 |
+
self::$safe_char = ( $safe_char ) ? $safe_char : 0xFFFC;
|
37 |
+
if ( self::$safe_mode ) {
|
38 |
+
self::$allow_overlong = true;
|
39 |
+
}
|
40 |
+
if ( ! in_array( $from, self::$mechs ) ) {
|
41 |
+
throw new Exception( 'Invalid input format specified' );
|
42 |
+
}
|
43 |
+
if ( ! in_array( $to, self::$mechs ) ) {
|
44 |
+
throw new Exception( 'Invalid output format specified' );
|
45 |
+
}
|
46 |
+
if ( 'ucs4array' != $from ) {
|
47 |
+
$data = call_user_func( array( self, $from . '_ucs4array' ), $data );
|
48 |
+
// eval( '$data = self::' . $from . '_ucs4array($data);' );
|
49 |
+
}
|
50 |
+
if ( 'ucs4array' != $to ) {
|
51 |
+
$data = call_user_func( array( self, 'ucs4array_' . $to ), $data );
|
52 |
+
// eval( '$data = self::ucs4array_' . $to . '($data);' );
|
53 |
+
}
|
54 |
+
return $data;
|
55 |
+
}
|
56 |
|
57 |
+
/**
|
58 |
+
* This converts an UTF-8 encoded string to its UCS-4 representation
|
59 |
+
*
|
60 |
+
* @param string $input The UTF-8 string to convert
|
61 |
+
* @return array Array of 32bit values representing each codepoint
|
62 |
+
* @access private
|
63 |
+
*/
|
64 |
+
private static function utf8_ucs4array( $input ) {
|
65 |
+
$output = array();
|
66 |
+
$out_len = 0;
|
67 |
+
$inp_len = strlen( $input );
|
68 |
+
$mode = 'next';
|
69 |
+
$test = 'none';
|
70 |
+
for ( $k = 0; $k < $inp_len; ++$k ) {
|
71 |
+
$v = ord( $input[ $k ] ); // Extract byte from input string
|
|
|
72 |
|
73 |
+
if ( $v < 128 ) { // We found an ASCII char - put into stirng as is
|
74 |
+
$output[ $out_len ] = $v;
|
75 |
+
++$out_len;
|
76 |
+
if ( 'add' == $mode ) {
|
77 |
+
if ( self::$safe_mode ) {
|
78 |
+
$output[ $out_len - 2 ] = self::$safe_char;
|
79 |
+
$mode = 'next';
|
80 |
+
} else {
|
81 |
+
throw new Exception( 'Conversion from UTF-8 to UCS-4 failed: malformed input at byte ' . $k );
|
82 |
+
}
|
83 |
+
}
|
84 |
+
continue;
|
85 |
+
}
|
86 |
+
if ( 'next' == $mode ) { // Try to find the next start byte; determine the width of the Unicode char
|
87 |
+
$start_byte = $v;
|
88 |
+
$mode = 'add';
|
89 |
+
$test = 'range';
|
90 |
+
//phpcs:ignore WordPress.PHP.YodaConditions.NotYoda
|
91 |
+
if ( $v >> 5 == 6 ) { // &110xxxxx 10xxxxx
|
92 |
+
$next_byte = 0; // Tells, how many times subsequent bitmasks must rotate 6bits to the left
|
93 |
+
$v = ( $v - 192 ) << 6;
|
94 |
+
//phpcs:ignore WordPress.PHP.YodaConditions.NotYoda
|
95 |
+
} elseif ( $v >> 4 == 14 ) { // &1110xxxx 10xxxxxx 10xxxxxx
|
96 |
+
$next_byte = 1;
|
97 |
+
$v = ( $v - 224 ) << 12;
|
98 |
+
//phpcs:ignore WordPress.PHP.YodaConditions.NotYoda
|
99 |
+
} elseif ( $v >> 3 == 30 ) { // &11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
100 |
+
$next_byte = 2;
|
101 |
+
$v = ( $v - 240 ) << 18;
|
102 |
+
} elseif ( self::$safe_mode ) {
|
103 |
+
$mode = 'next';
|
104 |
+
$output[ $out_len ] = self::$safe_char;
|
105 |
+
++$out_len;
|
106 |
+
continue;
|
107 |
+
} else {
|
108 |
+
throw new Exception( 'This might be UTF-8, but I don\'t understand it at byte ' . $k );
|
109 |
+
}
|
110 |
+
if ( $inp_len - $k - $next_byte < 2 ) {
|
111 |
+
$output[ $out_len ] = self::$safe_char;
|
112 |
+
$mode = 'no';
|
113 |
+
continue;
|
114 |
+
}
|
115 |
|
116 |
+
if ( 'add' == $mode ) {
|
117 |
+
$output[ $out_len ] = (int) $v;
|
118 |
+
++$out_len;
|
119 |
+
continue;
|
120 |
+
}
|
121 |
+
}
|
122 |
+
if ( 'add' == $mode ) {
|
123 |
+
if ( ! self::$allow_overlong && 'range' == $test ) {
|
124 |
+
$test = 'none';
|
125 |
+
if ( ( $v < 0xA0 && 0xE0 == $start_byte ) || ( $v < 0x90 && 0xF0 == $start_byte ) || ( $v > 0x8F && 0xF4 == $start_byte ) ) {
|
126 |
+
throw new Exception( 'Bogus UTF-8 character detected (out of legal range) at byte ' . $k );
|
127 |
+
}
|
128 |
+
}
|
129 |
+
//phpcs:ignore WordPress.PHP.YodaConditions.NotYoda
|
130 |
+
if ( $v >> 6 == 2 ) { // Bit mask must be 10xxxxxx
|
131 |
+
$v = ( $v - 128 ) << ( $next_byte * 6 );
|
132 |
+
$output[ ( $out_len - 1 ) ] += $v;
|
133 |
+
--$next_byte;
|
134 |
+
} else {
|
135 |
+
if ( self::$safe_mode ) {
|
136 |
+
$output[ $out_len - 1 ] = ord( self::$safe_char );
|
137 |
+
$k--;
|
138 |
+
$mode = 'next';
|
139 |
+
continue;
|
140 |
+
} else {
|
141 |
+
throw new Exception( 'Conversion from UTF-8 to UCS-4 failed: malformed input at byte ' . $k );
|
142 |
+
}
|
143 |
+
}
|
144 |
+
if ( $next_byte < 0 ) {
|
145 |
+
$mode = 'next';
|
146 |
+
}
|
147 |
+
}
|
148 |
+
} // for
|
149 |
+
return $output;
|
150 |
+
}
|
151 |
|
152 |
+
/**
|
153 |
+
* Convert UCS-4 string into UTF-8 string
|
154 |
+
* See utf8_ucs4array() for details
|
155 |
+
* @access private
|
156 |
+
*/
|
157 |
+
private static function ucs4array_utf8( $input ) {
|
158 |
+
$output = '';
|
159 |
+
foreach ( $input as $v ) {
|
160 |
+
if ( $v < 128 ) { // 7bit are transferred literally
|
161 |
+
$output .= chr( $v );
|
162 |
+
} elseif ( $v < ( 1 << 11 ) ) { // 2 bytes
|
163 |
+
$output .= chr( 192 + ( $v >> 6 ) ) . chr( 128 + ( $v & 63 ) );
|
164 |
+
} elseif ( $v < ( 1 << 16 ) ) { // 3 bytes
|
165 |
+
$output .= chr( 224 + ( $v >> 12 ) ) . chr( 128 + ( ( $v >> 6 ) & 63 ) ) . chr( 128 + ( $v & 63 ) );
|
166 |
+
} elseif ( $v < ( 1 << 21 ) ) { // 4 bytes
|
167 |
+
$output .= chr( 240 + ( $v >> 18 ) ) . chr( 128 + ( ( $v >> 12 ) & 63 ) ) . chr( 128 + ( ( $v >> 6 ) & 63 ) ) . chr( 128 + ( $v & 63 ) );
|
168 |
+
} elseif ( self::$safe_mode ) {
|
169 |
+
$output .= self::$safe_char;
|
170 |
+
} else {
|
171 |
+
throw new Exception( 'Conversion from UCS-4 to UTF-8 failed: malformed input at byte ' . $k );
|
172 |
+
}
|
173 |
+
}
|
174 |
+
return $output;
|
175 |
+
}
|
|
|
176 |
|
177 |
+
private static function utf7imap_ucs4array( $input ) {
|
178 |
+
return self::utf7_ucs4array( str_replace( ',', '/', $input ), '&' );
|
179 |
+
}
|
|
|
180 |
|
181 |
+
private static function utf7_ucs4array( $input, $sc = '+' ) {
|
182 |
+
$output = array();
|
183 |
+
$out_len = 0;
|
184 |
+
$inp_len = strlen( $input );
|
185 |
+
$mode = 'd';
|
186 |
+
$b64 = '';
|
|
|
187 |
|
188 |
+
for ( $k = 0; $k < $inp_len; ++$k ) {
|
189 |
+
$c = $input[ $k ];
|
190 |
+
if ( 0 == ord( $c ) ) {
|
191 |
+
continue; // Ignore zero bytes
|
192 |
+
}
|
193 |
+
if ( 'b' == $mode ) {
|
194 |
+
// Sequence got terminated
|
195 |
+
if ( ! preg_match( '![A-Za-z0-9/' . preg_quote( $sc, '!' ) . ']!', $c ) ) {
|
196 |
+
if ( '-' == $c ) {
|
197 |
+
if ( '' == $b64 ) {
|
198 |
+
$output[ $out_len ] = ord( $sc );
|
199 |
+
$out_len++;
|
200 |
+
$mode = 'd';
|
201 |
+
continue;
|
202 |
+
}
|
203 |
+
}
|
204 |
+
$tmp = base64_decode( $b64 );
|
205 |
+
$tmp = substr( $tmp, -1 * ( strlen( $tmp ) % 2 ) );
|
206 |
+
for ( $i = 0; $i < strlen( $tmp ); $i++ ) {
|
207 |
+
if ( $i % 2 ) {
|
208 |
+
$output[ $out_len ] += ord( $tmp[ $i ] );
|
209 |
+
$out_len++;
|
210 |
+
} else {
|
211 |
+
$output[ $out_len ] = ord( $tmp[ $i ] ) << 8;
|
212 |
+
}
|
213 |
+
}
|
214 |
+
$mode = 'd';
|
215 |
+
$b64 = '';
|
216 |
+
continue;
|
217 |
+
} else {
|
218 |
+
$b64 .= $c;
|
219 |
+
}
|
220 |
+
}
|
221 |
+
if ( 'd' == $mode ) {
|
222 |
+
if ( $sc == $c ) {
|
223 |
+
$mode = 'b';
|
224 |
+
continue;
|
225 |
+
}
|
226 |
+
$output[ $out_len ] = ord( $c );
|
227 |
+
$out_len++;
|
228 |
+
}
|
229 |
+
}
|
230 |
+
return $output;
|
231 |
+
}
|
232 |
|
233 |
+
private static function ucs4array_utf7imap( $input ) {
|
234 |
+
return str_replace( '/', ',', self::ucs4array_utf7( $input, '&' ) );
|
235 |
+
}
|
|
|
236 |
|
237 |
+
private static function ucs4array_utf7( $input, $sc = '+' ) {
|
238 |
+
$output = '';
|
239 |
+
$mode = 'd';
|
240 |
+
$b64 = '';
|
241 |
+
foreach ( $input as $v ) {
|
242 |
+
$is_direct = ( 0x20 <= $v && $v <= 0x7e && ord( $sc ) != $v );
|
243 |
+
if ( 'b' == $mode ) {
|
244 |
+
if ( $is_direct ) {
|
245 |
+
if ( chr( 0 ) . $sc == $b64 ) {
|
246 |
+
$output .= $sc . '-';
|
247 |
+
$b64 = '';
|
248 |
+
} else {
|
249 |
+
while ( strlen( $b64 ) % 3 ) {
|
250 |
+
$b64 .= chr( 0 );
|
251 |
+
}
|
252 |
+
$output .= $sc . base64_encode( $b64 ) . '-';
|
253 |
+
}
|
254 |
+
$mode = 'd';
|
255 |
+
} else {
|
256 |
+
$b64 .= ( chr( ( $v >> 8 ) & 255 ) . chr( $v & 255 ) );
|
257 |
+
}
|
258 |
+
}
|
259 |
+
if ( 'd' == $mode ) {
|
260 |
+
if ( $is_direct ) {
|
261 |
+
$output .= chr( $v );
|
262 |
+
} else {
|
263 |
+
$b64 = ( chr( ( $v >> 8 ) & 255 ) . chr( $v & 255 ) );
|
264 |
+
$mode = 'b';
|
265 |
+
}
|
266 |
+
}
|
267 |
+
}
|
268 |
+
return $output;
|
269 |
+
}
|
270 |
|
271 |
+
/**
|
272 |
+
* Convert UCS-4 array into UCS-4 string (Little Endian at the moment)
|
273 |
+
* @access private
|
274 |
+
*/
|
275 |
+
private static function ucs4array_ucs4( $input ) {
|
276 |
+
$output = '';
|
277 |
+
foreach ( $input as $v ) {
|
278 |
+
$output .= chr( ( $v >> 24 ) & 255 ) . chr( ( $v >> 16 ) & 255 ) . chr( ( $v >> 8 ) & 255 ) . chr( $v & 255 );
|
279 |
+
}
|
280 |
+
return $output;
|
281 |
+
}
|
|
|
282 |
|
283 |
+
/**
|
284 |
+
* Convert UCS-4 string (LE in the moment) into UCS-4 garray
|
285 |
+
* @access private
|
286 |
+
*/
|
287 |
+
private static function ucs4_ucs4array( $input ) {
|
288 |
+
$output = array();
|
|
|
289 |
|
290 |
+
$inp_len = strlen( $input );
|
291 |
+
// Input length must be dividable by 4
|
292 |
+
if ( $inp_len % 4 ) {
|
293 |
+
throw new Exception( 'Input UCS4 string is broken' );
|
294 |
+
}
|
295 |
+
// Empty input - return empty output
|
296 |
+
if ( ! $inp_len ) {
|
297 |
+
return $output;
|
298 |
+
}
|
299 |
|
300 |
+
for ( $i = 0, $out_len = -1; $i < $inp_len; ++$i ) {
|
301 |
+
if ( ! ( $i % 4 ) ) { // Increment output position every 4 input bytes
|
302 |
+
$out_len++;
|
303 |
+
$output[ $out_len ] = 0;
|
304 |
+
}
|
305 |
+
$output[ $out_len ] += ord( $input[ $i ] ) << ( 8 * ( 3 - ( $i % 4 ) ) );
|
306 |
+
}
|
307 |
+
return $output;
|
308 |
+
}
|
309 |
|
310 |
}
|
includes/activation.php
CHANGED
@@ -1,114 +1,118 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
global $blclog, $blc_config_manager, $wpdb;
|
4 |
-
$queryCnt = $wpdb->num_queries;
|
5 |
-
|
6 |
-
//Completing the installation/upgrade is required for the plugin to work, so make sure
|
7 |
-
//the script doesn't get aborted by (for example) the browser timing out.
|
8 |
-
set_time_limit(300); //5 minutes should be plenty, anything more would probably indicate an infinite loop or a deadlock
|
9 |
-
ignore_user_abort(true);
|
10 |
-
|
11 |
-
//Log installation progress to a DB option
|
12 |
-
$blclog = new blcCachedOptionLogger('blc_installation_log');
|
13 |
-
register_shutdown_function(array(&$blclog, 'save')); //Make sure the log is saved even if the plugin crashes
|
14 |
-
|
15 |
-
$blclog->clear();
|
16 |
-
$blclog->info( sprintf('Plugin activated at %s.', date_i18n('Y-m-d H:i:s')) );
|
17 |
-
$activation_start = microtime(true);
|
18 |
-
|
19 |
-
//Reset the "installation_complete" flag
|
20 |
-
$blc_config_manager->options['installation_complete']
|
21 |
-
$blc_config_manager->options['installation_flag_cleared_on'] = date('c') . ' (' . microtime(true) . ')';
|
22 |
-
//Note the time of the first installation (not very accurate, but still useful)
|
23 |
-
if ( empty($blc_config_manager->options['first_installation_timestamp']) ){
|
24 |
-
$blc_config_manager->options['first_installation_timestamp'] = time();
|
25 |
-
}
|
26 |
-
$blc_config_manager->save_options();
|
27 |
-
$blclog->info('Installation/update begins.');
|
28 |
-
|
29 |
-
//Load the base classes and utilities
|
30 |
-
require_once BLC_DIRECTORY . '/includes/links.php';
|
31 |
-
require_once BLC_DIRECTORY . '/includes/link-query.php';
|
32 |
-
require_once BLC_DIRECTORY . '/includes/instances.php';
|
33 |
-
require_once BLC_DIRECTORY . '/includes/utility-class.php';
|
34 |
-
|
35 |
-
//Load the module subsystem
|
36 |
-
require_once BLC_DIRECTORY . '/includes/modules.php';
|
37 |
-
$moduleManager = blcModuleManager::getInstance();
|
38 |
-
|
39 |
-
//If upgrading, activate/deactivate custom field and comment containers based on old ver. settings
|
40 |
-
if ( isset($blc_config_manager->options['check_comment_links']) ){
|
41 |
-
if (
|
42 |
-
$moduleManager->deactivate('comment');
|
43 |
-
}
|
44 |
-
unset($blc_config_manager->options['check_comment_links']);
|
45 |
-
}
|
46 |
-
if ( empty($blc_config_manager->options['custom_fields']) ){
|
47 |
-
$moduleManager->deactivate('custom_field');
|
48 |
-
}
|
49 |
-
if ( empty($blc_config_manager->options['acf_fields']) ){
|
50 |
-
$moduleManager->deactivate('acf_field');
|
51 |
-
}
|
52 |
-
|
53 |
-
//Prepare the database.
|
54 |
-
$blclog->info('Upgrading the database...');
|
55 |
-
$upgrade_start = microtime(true);
|
56 |
-
require_once BLC_DIRECTORY . '/includes/admin/db-upgrade.php';
|
57 |
-
blcDatabaseUpgrader::upgrade_database();
|
58 |
-
$blclog->info(sprintf('--- Total: %.3f seconds', microtime(true) - $upgrade_start));
|
59 |
-
|
60 |
-
//Remove invalid DB entries
|
61 |
-
$blclog->info('Cleaning up the database...');
|
62 |
-
$cleanup_start = microtime(true);
|
63 |
-
blc_cleanup_database();
|
64 |
-
$blclog->info(sprintf('--- Total: %.3f seconds', microtime(true) - $cleanup_start));
|
65 |
-
|
66 |
-
//Notify modules that the plugin has been activated. This will cause container
|
67 |
-
//modules to create and update synch. records for all new/modified posts and other items.
|
68 |
-
$blclog->info('Notifying modules...');
|
69 |
-
$notification_start = microtime(true);
|
70 |
-
$moduleManager->plugin_activated();
|
71 |
-
blc_got_unsynched_items();
|
72 |
-
$blclog->info(sprintf('--- Total: %.3f seconds', microtime(true) - $notification_start));
|
73 |
-
|
74 |
-
//Turn off load limiting if it's not available on this server.
|
75 |
-
$blclog->info('Updating server load limit settings...');
|
76 |
-
$load = blcUtility::get_server_load();
|
77 |
-
if ( empty($load) ){
|
78 |
-
$blc_config_manager->options['enable_load_limit'] = false;
|
79 |
-
$blclog->info('Disable load limit. Cannot retrieve current load average.');
|
80 |
-
} elseif ( $blc_config_manager->options['enable_load_limit'] && !isset($blc_config_manager->options['server_load_limit']) ) {
|
81 |
-
$fifteen_minutes
|
82 |
-
$default_load_limit
|
83 |
-
$blc_config_manager->options['server_load_limit'] = $default_load_limit;
|
84 |
-
|
85 |
-
$blclog->info(
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
$
|
97 |
-
|
98 |
-
$blclog->info('
|
99 |
-
|
100 |
-
$
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
}
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
$
|
114 |
-
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
global $blclog, $blc_config_manager, $wpdb;
|
4 |
+
$queryCnt = $wpdb->num_queries;
|
5 |
+
|
6 |
+
//Completing the installation/upgrade is required for the plugin to work, so make sure
|
7 |
+
//the script doesn't get aborted by (for example) the browser timing out.
|
8 |
+
set_time_limit( 300 ); //5 minutes should be plenty, anything more would probably indicate an infinite loop or a deadlock
|
9 |
+
ignore_user_abort( true );
|
10 |
+
|
11 |
+
//Log installation progress to a DB option
|
12 |
+
$blclog = new blcCachedOptionLogger( 'blc_installation_log' );
|
13 |
+
register_shutdown_function( array( &$blclog, 'save' ) ); //Make sure the log is saved even if the plugin crashes
|
14 |
+
|
15 |
+
$blclog->clear();
|
16 |
+
$blclog->info( sprintf( 'Plugin activated at %s.', date_i18n( 'Y-m-d H:i:s' ) ) );
|
17 |
+
$activation_start = microtime( true );
|
18 |
+
|
19 |
+
//Reset the "installation_complete" flag
|
20 |
+
$blc_config_manager->options['installation_complete'] = false;
|
21 |
+
$blc_config_manager->options['installation_flag_cleared_on'] = date( 'c' ) . ' (' . microtime( true ) . ')'; //phpcs:ignore
|
22 |
+
//Note the time of the first installation (not very accurate, but still useful)
|
23 |
+
if ( empty( $blc_config_manager->options['first_installation_timestamp'] ) ) {
|
24 |
+
$blc_config_manager->options['first_installation_timestamp'] = time();
|
25 |
+
}
|
26 |
+
$blc_config_manager->save_options();
|
27 |
+
$blclog->info( 'Installation/update begins.' );
|
28 |
+
|
29 |
+
//Load the base classes and utilities
|
30 |
+
require_once BLC_DIRECTORY . '/includes/links.php';
|
31 |
+
require_once BLC_DIRECTORY . '/includes/link-query.php';
|
32 |
+
require_once BLC_DIRECTORY . '/includes/instances.php';
|
33 |
+
require_once BLC_DIRECTORY . '/includes/utility-class.php';
|
34 |
+
|
35 |
+
//Load the module subsystem
|
36 |
+
require_once BLC_DIRECTORY . '/includes/modules.php';
|
37 |
+
$moduleManager = blcModuleManager::getInstance();
|
38 |
+
|
39 |
+
//If upgrading, activate/deactivate custom field and comment containers based on old ver. settings
|
40 |
+
if ( isset( $blc_config_manager->options['check_comment_links'] ) ) {
|
41 |
+
if ( ! $blc_config_manager->options['check_comment_links'] ) {
|
42 |
+
$moduleManager->deactivate( 'comment' );
|
43 |
+
}
|
44 |
+
unset( $blc_config_manager->options['check_comment_links'] );
|
45 |
+
}
|
46 |
+
if ( empty( $blc_config_manager->options['custom_fields'] ) ) {
|
47 |
+
$moduleManager->deactivate( 'custom_field' );
|
48 |
+
}
|
49 |
+
if ( empty( $blc_config_manager->options['acf_fields'] ) ) {
|
50 |
+
$moduleManager->deactivate( 'acf_field' );
|
51 |
+
}
|
52 |
+
|
53 |
+
//Prepare the database.
|
54 |
+
$blclog->info( 'Upgrading the database...' );
|
55 |
+
$upgrade_start = microtime( true );
|
56 |
+
require_once BLC_DIRECTORY . '/includes/admin/db-upgrade.php';
|
57 |
+
blcDatabaseUpgrader::upgrade_database();
|
58 |
+
$blclog->info( sprintf( '--- Total: %.3f seconds', microtime( true ) - $upgrade_start ) );
|
59 |
+
|
60 |
+
//Remove invalid DB entries
|
61 |
+
$blclog->info( 'Cleaning up the database...' );
|
62 |
+
$cleanup_start = microtime( true );
|
63 |
+
blc_cleanup_database();
|
64 |
+
$blclog->info( sprintf( '--- Total: %.3f seconds', microtime( true ) - $cleanup_start ) );
|
65 |
+
|
66 |
+
//Notify modules that the plugin has been activated. This will cause container
|
67 |
+
//modules to create and update synch. records for all new/modified posts and other items.
|
68 |
+
$blclog->info( 'Notifying modules...' );
|
69 |
+
$notification_start = microtime( true );
|
70 |
+
$moduleManager->plugin_activated();
|
71 |
+
blc_got_unsynched_items();
|
72 |
+
$blclog->info( sprintf( '--- Total: %.3f seconds', microtime( true ) - $notification_start ) );
|
73 |
+
|
74 |
+
//Turn off load limiting if it's not available on this server.
|
75 |
+
$blclog->info( 'Updating server load limit settings...' );
|
76 |
+
$load = blcUtility::get_server_load();
|
77 |
+
if ( empty( $load ) ) {
|
78 |
+
$blc_config_manager->options['enable_load_limit'] = false;
|
79 |
+
$blclog->info( 'Disable load limit. Cannot retrieve current load average.' );
|
80 |
+
} elseif ( $blc_config_manager->options['enable_load_limit'] && ! isset( $blc_config_manager->options['server_load_limit'] ) ) {
|
81 |
+
$fifteen_minutes = floatval( end( $load ) );
|
82 |
+
$default_load_limit = round( max( min( $fifteen_minutes * 2, $fifteen_minutes + 2 ), 4 ) );
|
83 |
+
$blc_config_manager->options['server_load_limit'] = $default_load_limit;
|
84 |
+
|
85 |
+
$blclog->info(
|
86 |
+
sprintf(
|
87 |
+
'Set server load limit to %.2f. Current load average is %.2f',
|
88 |
+
$default_load_limit,
|
89 |
+
$fifteen_minutes
|
90 |
+
)
|
91 |
+
);
|
92 |
+
}
|
93 |
+
|
94 |
+
//And optimize my DB tables, too (for good measure)
|
95 |
+
$blclog->info( 'Optimizing the database...' );
|
96 |
+
$optimize_start = microtime( true );
|
97 |
+
blcUtility::optimize_database();
|
98 |
+
$blclog->info( sprintf( '--- Total: %.3f seconds', microtime( true ) - $optimize_start ) );
|
99 |
+
|
100 |
+
$blclog->info( 'Completing installation...' );
|
101 |
+
$blc_config_manager->options['installation_complete'] = true;
|
102 |
+
$blc_config_manager->options['installation_flag_set_on'] = date( 'c' ) . ' (' . microtime( true ) . ')'; //phpcs:ignore
|
103 |
+
if ( $blc_config_manager->save_options() ) {
|
104 |
+
$blclog->info( 'Configuration saved.' );
|
105 |
+
} else {
|
106 |
+
$blclog->error( 'Error saving plugin configuration!' );
|
107 |
+
};
|
108 |
+
|
109 |
+
$blclog->info(
|
110 |
+
sprintf(
|
111 |
+
'Installation/update completed at %s with %d queries executed.',
|
112 |
+
date_i18n( 'Y-m-d H:i:s' ),
|
113 |
+
$wpdb->num_queries - $queryCnt
|
114 |
+
)
|
115 |
+
);
|
116 |
+
$blclog->info( sprintf( 'Total time: %.3f seconds', microtime( true ) - $activation_start ) );
|
117 |
+
$blclog->save();
|
118 |
+
|
includes/admin/db-schema.php
CHANGED
@@ -1,96 +1,106 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
if( !function_exists('blc_get_db_schema') ){
|
4 |
-
|
5 |
-
function blc_get_db_schema(){
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
`
|
37 |
-
`
|
38 |
-
`
|
39 |
-
|
40 |
-
`
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
`
|
51 |
-
`
|
52 |
-
`
|
53 |
-
|
54 |
-
`
|
55 |
-
`
|
56 |
-
`
|
57 |
-
`
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
`
|
62 |
-
`
|
63 |
-
`
|
64 |
-
`
|
65 |
-
`
|
66 |
-
`
|
67 |
-
`
|
68 |
-
`
|
69 |
-
|
70 |
-
`
|
71 |
-
`
|
72 |
-
`
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
`
|
83 |
-
`
|
84 |
-
|
85 |
-
`
|
86 |
-
|
87 |
-
|
88 |
-
KEY `
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
if ( ! function_exists( 'blc_get_db_schema' ) ) {
|
4 |
+
|
5 |
+
function blc_get_db_schema() {
|
6 |
+
global $wpdb;
|
7 |
+
|
8 |
+
//Use the character set and collation that's configured for WP tables
|
9 |
+
$charset_collate = '';
|
10 |
+
if ( ! empty( $wpdb->charset ) ) {
|
11 |
+
//Some German installs use "utf-8" (invalid) instead of "utf8" (valid). None of
|
12 |
+
//the charset ids supported by MySQL contain dashes, so we can safely strip them.
|
13 |
+
//See http://dev.mysql.com/doc/refman/5.0/en/charset-charsets.html
|
14 |
+
$charset = str_replace( '-', '', $wpdb->charset );
|
15 |
+
|
16 |
+
$charset_collate = "DEFAULT CHARACTER SET {$charset}";
|
17 |
+
}
|
18 |
+
|
19 |
+
//Sometimes when WP is installed from Cpanel ( checked on GoDaddy ) uses different
|
20 |
+
//collation for tables. Instead of using default collations which can cause problem when
|
21 |
+
//the plugin uses SQL join.
|
22 |
+
//TODO: USE alterative for JOIN statements and remove this as a whole
|
23 |
+
$posts_collate_sql = "SELECT TABLE_COLLATION FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME ='{$wpdb->posts}';";
|
24 |
+
$posts_collate = $wpdb->get_row( $posts_collate_sql ); //phpcs:ignore
|
25 |
+
|
26 |
+
//by default use the same collation that the posts table is using.
|
27 |
+
if ( ! empty( $posts_collate ) ) {
|
28 |
+
$charset_collate .= " COLLATE {$posts_collate->TABLE_COLLATION}";
|
29 |
+
} elseif ( ! empty( $wpdb->collate ) ) {
|
30 |
+
$charset_collate .= " COLLATE {$wpdb->collate}";
|
31 |
+
}
|
32 |
+
|
33 |
+
$blc_db_schema = <<<EOM
|
34 |
+
|
35 |
+
CREATE TABLE IF NOT EXISTS `{$wpdb->prefix}blc_filters` (
|
36 |
+
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
37 |
+
`name` varchar(100) NOT NULL,
|
38 |
+
`params` text NOT NULL,
|
39 |
+
|
40 |
+
PRIMARY KEY (`id`)
|
41 |
+
) {$charset_collate};
|
42 |
+
|
43 |
+
CREATE TABLE IF NOT EXISTS `{$wpdb->prefix}blc_instances` (
|
44 |
+
`instance_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
45 |
+
`link_id` int(10) unsigned NOT NULL,
|
46 |
+
`container_id` int(10) unsigned NOT NULL,
|
47 |
+
`container_type` varchar(40) NOT NULL DEFAULT 'post',
|
48 |
+
`link_text` text NOT NULL DEFAULT '',
|
49 |
+
`parser_type` varchar(40) NOT NULL DEFAULT 'link',
|
50 |
+
`container_field` varchar(250) NOT NULL DEFAULT '',
|
51 |
+
`link_context` varchar(250) NOT NULL DEFAULT '',
|
52 |
+
`raw_url` text NOT NULL,
|
53 |
+
|
54 |
+
PRIMARY KEY (`instance_id`),
|
55 |
+
KEY `link_id` (`link_id`),
|
56 |
+
KEY `source_id` (`container_type`, `container_id`),
|
57 |
+
KEY `parser_type` (`parser_type`)
|
58 |
+
) {$charset_collate};
|
59 |
+
|
60 |
+
CREATE TABLE IF NOT EXISTS `{$wpdb->prefix}blc_links` (
|
61 |
+
`link_id` int(20) unsigned NOT NULL AUTO_INCREMENT,
|
62 |
+
`url` text CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
|
63 |
+
`first_failure` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
|
64 |
+
`last_check` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
|
65 |
+
`last_success` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
|
66 |
+
`last_check_attempt` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
|
67 |
+
`check_count` int(4) unsigned NOT NULL DEFAULT '0',
|
68 |
+
`final_url` text CHARACTER SET latin1 COLLATE latin1_general_cs NOT NULL,
|
69 |
+
`redirect_count` smallint(5) unsigned NOT NULL DEFAULT '0',
|
70 |
+
`log` text NOT NULL,
|
71 |
+
`http_code` smallint(6) NOT NULL DEFAULT '0',
|
72 |
+
`status_code` varchar(100) DEFAULT '',
|
73 |
+
`status_text` varchar(250) DEFAULT '',
|
74 |
+
`request_duration` float NOT NULL DEFAULT '0',
|
75 |
+
`timeout` tinyint(1) unsigned NOT NULL DEFAULT '0',
|
76 |
+
`broken` tinyint(1) unsigned NOT NULL DEFAULT '0',
|
77 |
+
`warning` tinyint(1) unsigned NOT NULL DEFAULT '0',
|
78 |
+
`may_recheck` tinyint(1) NOT NULL DEFAULT '1',
|
79 |
+
`being_checked` tinyint(1) NOT NULL DEFAULT '0',
|
80 |
+
|
81 |
+
`result_hash` varchar(200) NOT NULL DEFAULT '',
|
82 |
+
`false_positive` tinyint(1) NOT NULL DEFAULT '0',
|
83 |
+
`dismissed` tinyint(1) NOT NULL DEFAULT '0',
|
84 |
+
|
85 |
+
PRIMARY KEY (`link_id`),
|
86 |
+
KEY `url` (`url`(150)),
|
87 |
+
KEY `final_url` (`final_url`(150)),
|
88 |
+
KEY `http_code` (`http_code`),
|
89 |
+
KEY `broken` (`broken`)
|
90 |
+
) {$charset_collate};
|
91 |
+
|
92 |
+
CREATE TABLE IF NOT EXISTS `{$wpdb->prefix}blc_synch` (
|
93 |
+
`container_id` int(20) unsigned NOT NULL,
|
94 |
+
`container_type` varchar(40) NOT NULL,
|
95 |
+
`synched` tinyint(2) unsigned NOT NULL,
|
96 |
+
`last_synch` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
|
97 |
+
|
98 |
+
PRIMARY KEY (`container_type`,`container_id`),
|
99 |
+
KEY `synched` (`synched`)
|
100 |
+
) {$charset_collate};
|
101 |
+
|
102 |
+
EOM;
|
103 |
+
|
104 |
+
return $blc_db_schema;
|
105 |
+
}
|
106 |
+
}
|
includes/admin/db-upgrade.php
CHANGED
@@ -1,585 +1,591 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
class blcDatabaseUpgrader {
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
global $blclog;
|
12 |
-
|
13 |
-
$conf
|
14 |
-
$current = $conf->options['current_db_version'];
|
15 |
-
|
16 |
-
if ( ($current
|
17 |
-
//The 4th DB version makes a lot of backwards-incompatible changes to the main
|
18 |
-
//BLC tables, so instead of upgrading we just throw them away and recreate.
|
19 |
-
if ( !blcDatabaseUpgrader::drop_tables() ){
|
20 |
-
return false;
|
21 |
-
};
|
22 |
-
$current = 0;
|
23 |
-
}
|
24 |
-
|
25 |
-
//Create/update the plugin's tables
|
26 |
-
if ( !blcDatabaseUpgrader::make_schema_current() ) {
|
27 |
-
return false;
|
28 |
-
}
|
29 |
-
|
30 |
-
if (
|
31 |
-
|
32 |
-
if ( $current < 5 ){
|
33 |
-
blcDatabaseUpgrader::upgrade_095();
|
34 |
-
}
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
$conf->
|
39 |
-
$
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
$
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
$blclog->error('
|
65 |
-
$
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
*
|
77 |
-
*
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
$
|
84 |
-
|
85 |
-
$wpdb->prefix . '
|
86 |
-
$wpdb->prefix . '
|
87 |
-
$wpdb->prefix . '
|
88 |
-
$wpdb->prefix . '
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
$
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
//
|
116 |
-
//
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
{$wpdb->
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
{$wpdb->
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
*
|
147 |
-
*
|
148 |
-
*
|
149 |
-
*
|
150 |
-
*
|
151 |
-
*
|
152 |
-
*
|
153 |
-
*
|
154 |
-
*
|
155 |
-
* '
|
156 |
-
* '
|
157 |
-
*
|
158 |
-
*
|
159 |
-
*
|
160 |
-
*
|
161 |
-
* @param
|
162 |
-
* @param bool $
|
163 |
-
* @param bool $
|
164 |
-
* @
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
$
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
$
|
184 |
-
$
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
$
|
208 |
-
|
209 |
-
//
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
( $tablefield->
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
//
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
*
|
362 |
-
*
|
363 |
-
*
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
$
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
//
|
421 |
-
$definition['
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
$definition =
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
*
|
437 |
-
*
|
438 |
-
*
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
$
|
462 |
-
|
463 |
-
|
464 |
-
|
465 |
-
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
*
|
474 |
-
|
475 |
-
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
$
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
//
|
516 |
-
$
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
}
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
if (
|
541 |
-
$column_definition .= "
|
542 |
-
}
|
543 |
-
if ( !
|
544 |
-
$column_definition .= "
|
545 |
-
}
|
546 |
-
if ( $
|
547 |
-
$column_definition .=
|
548 |
-
}
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
}
|
585 |
-
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class blcDatabaseUpgrader {
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Create and/or upgrade the plugin's database tables.
|
7 |
+
*
|
8 |
+
* @return bool
|
9 |
+
*/
|
10 |
+
public static function upgrade_database() {
|
11 |
+
global $blclog;
|
12 |
+
|
13 |
+
$conf = blc_get_configuration();
|
14 |
+
$current = $conf->options['current_db_version'];
|
15 |
+
|
16 |
+
if ( ( 0 != $current ) && ( $current < 4 ) ) {
|
17 |
+
//The 4th DB version makes a lot of backwards-incompatible changes to the main
|
18 |
+
//BLC tables, so instead of upgrading we just throw them away and recreate.
|
19 |
+
if ( ! blcDatabaseUpgrader::drop_tables() ) {
|
20 |
+
return false;
|
21 |
+
};
|
22 |
+
$current = 0;
|
23 |
+
}
|
24 |
+
|
25 |
+
//Create/update the plugin's tables
|
26 |
+
if ( ! blcDatabaseUpgrader::make_schema_current() ) {
|
27 |
+
return false;
|
28 |
+
}
|
29 |
+
|
30 |
+
if ( 0 != $current ) {
|
31 |
+
|
32 |
+
if ( $current < 5 ) {
|
33 |
+
blcDatabaseUpgrader::upgrade_095();
|
34 |
+
}
|
35 |
+
}
|
36 |
+
|
37 |
+
$conf->options['current_db_version'] = BLC_DATABASE_VERSION;
|
38 |
+
$conf->save_options();
|
39 |
+
$blclog->info( 'Database successfully upgraded.' );
|
40 |
+
|
41 |
+
return true;
|
42 |
+
}
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Create or update the plugin's DB tables.
|
46 |
+
*
|
47 |
+
* @return bool
|
48 |
+
*/
|
49 |
+
static function make_schema_current() {
|
50 |
+
global $blclog;
|
51 |
+
|
52 |
+
$start = microtime( true );
|
53 |
+
if ( ! function_exists( 'blc_get_db_schema' ) ) {
|
54 |
+
require 'db-schema.php';
|
55 |
+
}
|
56 |
+
list($dummy, $query_log) = blcTableDelta::delta( blc_get_db_schema() );
|
57 |
+
|
58 |
+
$have_errors = false;
|
59 |
+
foreach ( $query_log as $item ) {
|
60 |
+
if ( $item['success'] ) {
|
61 |
+
$blclog->info( ' [OK] ' . $item['query'] . sprintf( ' (%.3f seconds)', $item['query_time'] ) );
|
62 |
+
} else {
|
63 |
+
$blclog->error( ' [ ] ' . $item['query'] );
|
64 |
+
$blclog->error( ' Database error : ' . $item['error_message'] );
|
65 |
+
$have_errors = true;
|
66 |
+
}
|
67 |
+
}
|
68 |
+
$blclog->info( sprintf( 'Schema update took %.3f seconds', microtime( true ) - $start ) );
|
69 |
+
|
70 |
+
$blclog->info( 'Database schema updated.' );
|
71 |
+
return ! $have_errors;
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Drop the plugin's tables.
|
76 |
+
*
|
77 |
+
* @return bool
|
78 |
+
*/
|
79 |
+
static function drop_tables() {
|
80 |
+
global $wpdb, $blclog; /** @var wpdb $wpdb */
|
81 |
+
|
82 |
+
$blclog->info( 'Deleting the plugin\'s database tables' );
|
83 |
+
$tables = array(
|
84 |
+
$wpdb->prefix . 'blc_linkdata',
|
85 |
+
$wpdb->prefix . 'blc_postdata',
|
86 |
+
$wpdb->prefix . 'blc_instances',
|
87 |
+
$wpdb->prefix . 'blc_synch',
|
88 |
+
$wpdb->prefix . 'blc_links',
|
89 |
+
);
|
90 |
+
|
91 |
+
$q = 'DROP TABLE IF EXISTS ' . implode( ', ', $tables );
|
92 |
+
$rez = $wpdb->query( $q ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
93 |
+
|
94 |
+
if ( false === $rez ) {
|
95 |
+
$error = sprintf(
|
96 |
+
__( 'Failed to delete old DB tables. Database error : %s', 'broken-link-checker' ),
|
97 |
+
$wpdb->last_error
|
98 |
+
);
|
99 |
+
|
100 |
+
$blclog->error( $error );
|
101 |
+
/*
|
102 |
+
//FIXME: In very rare cases, DROP TABLE IF EXISTS throws an error when the table(s) don't exist.
|
103 |
+
return false;
|
104 |
+
//*/
|
105 |
+
}
|
106 |
+
$blclog->info( 'Done.' );
|
107 |
+
|
108 |
+
return true;
|
109 |
+
}
|
110 |
+
|
111 |
+
static function upgrade_095( $trigger_errors = false ) {
|
112 |
+
global $wpdb; /** @var wpdb $wpdb */
|
113 |
+
|
114 |
+
//Prior to 0.9.5 all supported post types were internally represented using
|
115 |
+
//a common 'post' container type. The current version creates a unique container
|
116 |
+
//type to each post type.
|
117 |
+
|
118 |
+
//Update synch records and instances to reflect this change
|
119 |
+
$q = "
|
120 |
+
UPDATE
|
121 |
+
{$wpdb->prefix}blc_synch AS synch
|
122 |
+
LEFT JOIN {$wpdb->posts} AS posts ON (posts.ID = synch.container_id)
|
123 |
+
SET
|
124 |
+
synch.container_type = posts.post_type
|
125 |
+
WHERE
|
126 |
+
synch.container_type = 'post' AND posts.post_type IS NOT NULL";
|
127 |
+
$wpdb->query( $q ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
128 |
+
|
129 |
+
$q = "
|
130 |
+
UPDATE
|
131 |
+
{$wpdb->prefix}blc_instances AS instances
|
132 |
+
LEFT JOIN {$wpdb->posts} AS posts ON (posts.ID = instances.container_id)
|
133 |
+
SET
|
134 |
+
instances.container_type = posts.post_type
|
135 |
+
WHERE
|
136 |
+
instances.container_type = 'post' AND posts.post_type IS NOT NULL";
|
137 |
+
$wpdb->query( $q ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
138 |
+
}
|
139 |
+
|
140 |
+
}
|
141 |
+
|
142 |
+
class blcTableDelta {
|
143 |
+
|
144 |
+
/**
|
145 |
+
* Parse one or more CREATE TABLE queries and generate a list of SQL queries that need
|
146 |
+
* to be executed to make the current database schema match those queries. Will also
|
147 |
+
* execute those queries by default.
|
148 |
+
*
|
149 |
+
* This function returns an array with two items. The first is a list of human-readable
|
150 |
+
* messages explaining what database changes were/would be made. The second array item
|
151 |
+
* is an array of the generated SQL queries and (if $execute was True) their results.
|
152 |
+
*
|
153 |
+
* Each item of this second array is itself an associative array with these keys :
|
154 |
+
* 'query' - the generated query.
|
155 |
+
* 'success' - True if the query was executed successfully, False if it caused an error.
|
156 |
+
* 'error_message' - the MySQL error message (only meaningful when 'success' = false).
|
157 |
+
*
|
158 |
+
* The 'success' and 'error_message' keys will only be present if $execute was set to True.
|
159 |
+
*
|
160 |
+
* @param string $queries One or more CREATE TABLE queries separated by a semicolon.
|
161 |
+
* @param bool $execute Whether to apply the schema changes. Defaults to true.
|
162 |
+
* @param bool $drop_columns Whether to drop columns not present in the input. Defaults to true.
|
163 |
+
* @param bool $drop_indexes Whether to drop indexes not present in the input. Defaults to true.
|
164 |
+
* @return array
|
165 |
+
*/
|
166 |
+
static function delta( $queries, $execute = true, $drop_columns = true, $drop_indexes = true ) {
|
167 |
+
global $wpdb, $blclog; /** @var wpdb $wpdb */
|
168 |
+
|
169 |
+
// Separate individual queries into an array
|
170 |
+
if ( ! is_array( $queries ) ) {
|
171 |
+
$queries = explode( ';', $queries );
|
172 |
+
if ( '' == $queries[ count( $queries ) - 1 ] ) {
|
173 |
+
array_pop( $queries );
|
174 |
+
}
|
175 |
+
}
|
176 |
+
|
177 |
+
$cqueries = array(); // Creation Queries
|
178 |
+
$for_update = array();
|
179 |
+
|
180 |
+
// Create a tablename index for an array ($cqueries) of queries
|
181 |
+
foreach ( $queries as $qry ) {
|
182 |
+
if ( preg_match( '|CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?([^\s(]+)|i', $qry, $matches ) ) {
|
183 |
+
$table = trim( $matches[1], '`' );
|
184 |
+
$cqueries[ $table ] = $qry;
|
185 |
+
$for_update[ $table ] = 'Create table `' . $table . '`';
|
186 |
+
}
|
187 |
+
}
|
188 |
+
|
189 |
+
// Check to see which tables and fields exist
|
190 |
+
$start_show_tables = microtime( true );
|
191 |
+
$tables = $wpdb->get_col('SHOW TABLES;');
|
192 |
+
if ( $tables ) {
|
193 |
+
$blclog->info( sprintf( '... SHOW TABLES (%.3f seconds)', microtime( true ) - $start_show_tables ) );
|
194 |
+
|
195 |
+
// For every table in the database
|
196 |
+
foreach ( $tables as $table ) {
|
197 |
+
|
198 |
+
// If a table query exists for the database table...
|
199 |
+
if ( array_key_exists( $table, $cqueries ) ) {
|
200 |
+
|
201 |
+
// Clear the field and index arrays
|
202 |
+
$cfields = array();
|
203 |
+
$indices = array();
|
204 |
+
|
205 |
+
// Get all of the field names in the query from between the parens
|
206 |
+
preg_match( '|\((.*)\)|ms', $cqueries[ $table ], $match2 );
|
207 |
+
$qryline = trim( $match2[1] );
|
208 |
+
|
209 |
+
// Separate field lines into an array
|
210 |
+
$flds = preg_split( '@[\r\n]+@', $qryline );
|
211 |
+
|
212 |
+
//echo "<hr/><pre>\n".print_r(strtolower($table), true).":\n".print_r($flds, true)."</pre><hr/>";
|
213 |
+
|
214 |
+
// For every field line specified in the query
|
215 |
+
foreach ( $flds as $fld ) {
|
216 |
+
$definition = blcTableDelta::parse_create_definition( $fld );
|
217 |
+
|
218 |
+
if ( $definition ) {
|
219 |
+
if ( $definition['index'] ) {
|
220 |
+
$indices[ $definition['index_definition'] ] = $definition; //Index
|
221 |
+
} else {
|
222 |
+
$cfields[ $definition['name'] ] = $definition; //Column
|
223 |
+
}
|
224 |
+
}
|
225 |
+
}
|
226 |
+
|
227 |
+
//echo "Detected fields : <br>"; print_r($cfields);
|
228 |
+
|
229 |
+
// Fetch the table column structure from the database
|
230 |
+
$start = microtime( true );
|
231 |
+
$tablefields = $wpdb->get_results( "SHOW FULL COLUMNS FROM {$table};" );//phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
232 |
+
|
233 |
+
$blclog->info( sprintf( '... SHOW FULL COLUMNS FROM %s %.3f seconds', $table, microtime( true ) - $start ) );
|
234 |
+
|
235 |
+
// For every field in the table
|
236 |
+
foreach ( $tablefields as $tablefield ) {
|
237 |
+
$field_name = strtolower( $tablefield->Field ); //Field names are case-insensitive in MySQL
|
238 |
+
|
239 |
+
// If the table field exists in the field array...
|
240 |
+
if ( array_key_exists( $field_name, $cfields ) ) {
|
241 |
+
$definition = $cfields[ $field_name ];
|
242 |
+
|
243 |
+
// Is actual field definition different from that in the query?
|
244 |
+
$different =
|
245 |
+
( $tablefield->Type != $definition['data_type'] ) ||
|
246 |
+
( $definition['collation'] && ( $tablefield->Collation != $definition['collation'] ) ) ||
|
247 |
+
( $definition['null_allowed'] && ( 'NO' == $tablefield->Null ) ) ||
|
248 |
+
( ! $definition['null_allowed'] && ( 'NO' == $tablefield->Null ) ) ||
|
249 |
+
( $tablefield->Default !== $definition['default'] );
|
250 |
+
|
251 |
+
// Add a query to change the column type
|
252 |
+
if ( $different ) {
|
253 |
+
$cqueries[] = "ALTER TABLE `{$table}` MODIFY COLUMN `{$field_name}` {$definition['column_definition']}";
|
254 |
+
$for_update[ $table . '.' . $field_name ] = "Changed type of {$table}.{$field_name} from {$tablefield->Type} to {$definition['column_definition']}";
|
255 |
+
}
|
256 |
+
|
257 |
+
// Remove the field from the array (so it's not added)
|
258 |
+
unset( $cfields[ $field_name ] );
|
259 |
+
} else {
|
260 |
+
// This field exists in the table, but not in the creation queries? Drop it.
|
261 |
+
if ( $drop_columns ) {
|
262 |
+
$cqueries[] = "ALTER TABLE `{$table}` DROP COLUMN `$field_name`";
|
263 |
+
$for_update[ $table . '.' . $field_name ] = 'Removed column ' . $table . '.' . $field_name;
|
264 |
+
}
|
265 |
+
}
|
266 |
+
}
|
267 |
+
|
268 |
+
// For every remaining field specified for the table
|
269 |
+
foreach ( $cfields as $field_name => $definition ) {
|
270 |
+
// Push a query line into $cqueries that adds the field to that table
|
271 |
+
$cqueries[] = "ALTER TABLE `{$table}` ADD COLUMN `$field_name` {$definition['column_definition']}";
|
272 |
+
$for_update[ $table . '.' . $field_name ] = 'Added column ' . $table . '.' . $field_name;
|
273 |
+
}
|
274 |
+
|
275 |
+
// Index stuff goes here
|
276 |
+
//echo 'Detected indexes : <br>'; print_r($indices);
|
277 |
+
|
278 |
+
// Fetch the table index structure from the database
|
279 |
+
$start = microtime( true );
|
280 |
+
$tableindices = $wpdb->get_results( "SHOW INDEX FROM `{$table}`;" ); //phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
281 |
+
$blclog->info( sprintf( '... SHOW INDEX FROM %s %.3f seconds', $table, microtime( true ) - $start ) );
|
282 |
+
|
283 |
+
if ( $tableindices ) {
|
284 |
+
// Clear the index array
|
285 |
+
$index_ary = array();
|
286 |
+
|
287 |
+
// For every index in the table
|
288 |
+
foreach ( $tableindices as $tableindex ) {
|
289 |
+
// Add the index to the index data array
|
290 |
+
$keyname = strtolower( $tableindex->Key_name );
|
291 |
+
$index_ary[ $keyname ]['name'] = $keyname;
|
292 |
+
|
293 |
+
$index_ary[ $keyname ]['columns'][] = array(
|
294 |
+
'column_name' => strtolower( $tableindex->Column_name ),
|
295 |
+
'length' => $tableindex->Sub_part,
|
296 |
+
);
|
297 |
+
|
298 |
+
if ( ! isset( $index_ary[ $keyname ]['index_modifier'] ) ) {
|
299 |
+
if ( 'primary' == $keyname ) {
|
300 |
+
$index_ary[ $keyname ]['index_modifier'] = 'primary';
|
301 |
+
} elseif ( 0 == $tableindex->Non_unique ) {
|
302 |
+
$index_ary[ $keyname ]['index_modifier'] = 'unique';
|
303 |
+
}
|
304 |
+
}
|
305 |
+
}
|
306 |
+
|
307 |
+
// For each actual index in the index array
|
308 |
+
foreach ( $index_ary as $index_name => $index_data ) {
|
309 |
+
// Build a create string to compare to the query
|
310 |
+
$index_string = blcTableDelta::generate_index_string( $index_data );
|
311 |
+
if ( array_key_exists( $index_string, $indices ) ) {
|
312 |
+
//echo "Found index $index_string<br>";
|
313 |
+
unset( $indices[ $index_string ] );
|
314 |
+
} else {
|
315 |
+
//echo "Didn't find index $index_string<br>";
|
316 |
+
if ( $drop_indexes ) {
|
317 |
+
if ( 'primary' == $index_name ) {
|
318 |
+
$cqueries[] = "ALTER TABLE `{$table}` DROP PRIMARY KEY";
|
319 |
+
} else {
|
320 |
+
$cqueries[] = "ALTER TABLE `{$table}` DROP KEY `$index_name`";
|
321 |
+
}
|
322 |
+
$for_update[ $table . '.' . $index_name ] = 'Removed index ' . $table . '.' . $index_name;
|
323 |
+
}
|
324 |
+
}
|
325 |
+
}
|
326 |
+
}
|
327 |
+
|
328 |
+
// For every remaining index specified for the table
|
329 |
+
foreach ( $indices as $index ) {
|
330 |
+
// Push a query line into $cqueries that adds the index to that table
|
331 |
+
$cqueries[] = "ALTER TABLE `{$table}` ADD {$index['index_definition']}";
|
332 |
+
$for_update[ $table . '.' . $index['name'] ] = 'Added index ' . $table . ' ' . $index['index_definition'];
|
333 |
+
}
|
334 |
+
|
335 |
+
// Remove the original table creation query from processing
|
336 |
+
unset( $cqueries[ $table ] );
|
337 |
+
unset( $for_update[ $table ] );
|
338 |
+
} else {
|
339 |
+
// This table exists in the database, but not in the creation queries?
|
340 |
+
}
|
341 |
+
}
|
342 |
+
}
|
343 |
+
|
344 |
+
//echo "Execute queries : <br>"; print_r($cqueries);
|
345 |
+
$query_log = array();
|
346 |
+
foreach ( $cqueries as $query ) {
|
347 |
+
$log_item = array( 'query' => $query );
|
348 |
+
if ( $execute ) {
|
349 |
+
$start = microtime( true );
|
350 |
+
$log_item['success'] = ( false !== $wpdb->query( $query ) );//phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
351 |
+
$log_item['error_message'] = $wpdb->last_error;
|
352 |
+
$log_item['query_time'] = microtime( true ) - $start;
|
353 |
+
}
|
354 |
+
$query_log[] = $log_item;
|
355 |
+
}
|
356 |
+
|
357 |
+
return array( $for_update, $query_log );
|
358 |
+
}
|
359 |
+
|
360 |
+
/**
|
361 |
+
* Parse a a single column or index definition.
|
362 |
+
*
|
363 |
+
* This function can parse many (but not all) types of syntax used to define columns
|
364 |
+
* and indexes in a "CREATE TABLE" query.
|
365 |
+
*
|
366 |
+
* @param string $line
|
367 |
+
* @return array
|
368 |
+
*/
|
369 |
+
static function parse_create_definition( $line ) {
|
370 |
+
$line = preg_replace( '@[,\r\n\s]+$@', '', $line ); //Strip the ", " line separator
|
371 |
+
|
372 |
+
$pieces = preg_split( '@\s+|(?=\()@', $line, -1, PREG_SPLIT_NO_EMPTY );
|
373 |
+
if ( empty( $pieces ) ) {
|
374 |
+
return null;
|
375 |
+
}
|
376 |
+
|
377 |
+
$token = strtolower( array_shift( $pieces ) );
|
378 |
+
|
379 |
+
$index_modifier = '';
|
380 |
+
$index = false;
|
381 |
+
|
382 |
+
//Determine if this line defines an index
|
383 |
+
if ( in_array( $token, array( 'primary', 'unique', 'fulltext' ) ) ) {
|
384 |
+
$index_modifier = $token;
|
385 |
+
$index = true;
|
386 |
+
$token = strtolower( array_shift( $pieces ) );
|
387 |
+
}
|
388 |
+
|
389 |
+
if ( in_array( $token, array( 'index', 'key' ) ) ) {
|
390 |
+
$index = true;
|
391 |
+
$token = strtolower( array_shift( $pieces ) );
|
392 |
+
}
|
393 |
+
|
394 |
+
//Determine column/index name
|
395 |
+
$name = '';
|
396 |
+
if ( $index ) {
|
397 |
+
//Names are optional for indexes; the INDEX/etc keyword can be immediately
|
398 |
+
//followed by a column list (or index_type, but we're ignoring that possibility).
|
399 |
+
if ( strpos( $token, '(' ) === false ) {
|
400 |
+
$name = $token;
|
401 |
+
} else {
|
402 |
+
if ( 'primary' == $index_modifier ) {
|
403 |
+
$name = 'primary';
|
404 |
+
}
|
405 |
+
array_unshift( $pieces, $token );
|
406 |
+
}
|
407 |
+
} else {
|
408 |
+
$name = $token;
|
409 |
+
}
|
410 |
+
$name = strtolower( trim( $name, '`' ) );
|
411 |
+
|
412 |
+
$definition = compact( 'name', 'index', 'index_modifier' );
|
413 |
+
|
414 |
+
//Parse the rest of the line
|
415 |
+
$remainder = implode( ' ', $pieces );
|
416 |
+
if ( $index ) {
|
417 |
+
$definition['columns'] = blcTableDelta::parse_index_column_list( $remainder );
|
418 |
+
|
419 |
+
//If the index doesn't have a name, use the name of the first column
|
420 |
+
//(this is what MySQL does, but only when there isn't already an index with that name).
|
421 |
+
if ( empty( $definition['name'] ) ) {
|
422 |
+
$definition['name'] = $definition['columns'][0]['column_name'];
|
423 |
+
}
|
424 |
+
//Rebuild the index def. in a normalized form
|
425 |
+
$definition['index_definition'] = blcTableDelta::generate_index_string( $definition );
|
426 |
+
|
427 |
+
} else {
|
428 |
+
$column_def = blcTableDelta::parse_column_definition( $remainder );
|
429 |
+
$definition = array_merge( $definition, $column_def );
|
430 |
+
}
|
431 |
+
|
432 |
+
return $definition;
|
433 |
+
}
|
434 |
+
|
435 |
+
/**
|
436 |
+
* Parse the list of columns included in an index.
|
437 |
+
*
|
438 |
+
* This function returns a list of column descriptors. Each descriptor is
|
439 |
+
* an associative array with the keys 'column_name', 'length' and 'order'.
|
440 |
+
*
|
441 |
+
* @param string $line
|
442 |
+
* @return array Array of index columns
|
443 |
+
*/
|
444 |
+
static function parse_index_column_list( $line ) {
|
445 |
+
$line = preg_replace( '@^\s*\(|\)\s*$@', '', $line ); //Strip the braces that surround the column list
|
446 |
+
$pieces = preg_split( '@\s*,\s*@', $line );
|
447 |
+
|
448 |
+
$columns = array();
|
449 |
+
foreach ( $pieces as $piece ) {
|
450 |
+
if ( preg_match( '@`?(?P<column_name>[^\s`]+)`?(?:\s*\(\s*(?P<length>\d+)\s*\))?(?:\s+(?P<order>ASC|DESC))?@i', $piece, $matches ) ) {
|
451 |
+
|
452 |
+
$column = array(
|
453 |
+
'column_name' => strtolower( $matches['column_name'] ),
|
454 |
+
'length' => null,
|
455 |
+
'order' => null, //unused; included for completeness
|
456 |
+
);
|
457 |
+
|
458 |
+
if ( isset( $matches['length'] ) && is_numeric( $matches['length'] ) ) {
|
459 |
+
$column['length'] = intval( $matches['length'] );
|
460 |
+
}
|
461 |
+
if ( isset( $matches['order'] ) && ! empty( $matches['order'] ) ) {
|
462 |
+
$column['order'] = strtolower( $matches['order'] );
|
463 |
+
}
|
464 |
+
|
465 |
+
$columns[] = $column;
|
466 |
+
};
|
467 |
+
}
|
468 |
+
|
469 |
+
return $columns;
|
470 |
+
}
|
471 |
+
|
472 |
+
/**
|
473 |
+
* Parse column datatype and flags.
|
474 |
+
*
|
475 |
+
*
|
476 |
+
* @param string $line
|
477 |
+
* @return array
|
478 |
+
*/
|
479 |
+
static function parse_column_definition( $line ) {
|
480 |
+
$line = trim( $line );
|
481 |
+
|
482 |
+
//Extract datatype. This regexp is not entirely reliable - for example, it won't work
|
483 |
+
//with enum fields where one of values contains brackets "()".
|
484 |
+
$data_type = '';
|
485 |
+
$regexp = '
|
486 |
+
@
|
487 |
+
(?P<type_name>^\w+)
|
488 |
+
|
489 |
+
# followed by an optional length or a list of enum values
|
490 |
+
(?:\s*
|
491 |
+
\(
|
492 |
+
\s* (?P<length>[^()]+) \s*
|
493 |
+
\)
|
494 |
+
)?
|
495 |
+
|
496 |
+
# various type modifiers/keywords
|
497 |
+
(?P<keywords>
|
498 |
+
(?:\s+
|
499 |
+
(?: BINARY | UNSIGNED | ZEROFILL )
|
500 |
+
)*
|
501 |
+
)?
|
502 |
+
@xi';
|
503 |
+
|
504 |
+
if ( preg_match( $regexp, $line, $matches ) ) {
|
505 |
+
$data_type = strtolower( $matches['type_name'] );
|
506 |
+
if ( ! empty( $matches['length'] ) ) {
|
507 |
+
$data_type .= '(' . trim( $matches['length'] ) . ')';
|
508 |
+
}
|
509 |
+
if ( ! empty( $matches['keywords'] ) ) {
|
510 |
+
$data_type .= preg_replace( '@\s+@', ' ', $matches['keywords'] ); //Collapse spaces
|
511 |
+
}
|
512 |
+
$line = substr( $line, strlen( $data_type ) );
|
513 |
+
}
|
514 |
+
|
515 |
+
//Extract flags
|
516 |
+
$null_allowed = ! preg_match( '@\sNOT\s+NULL\b@i', $line );
|
517 |
+
$auto_increment = preg_match( '@\sAUTO_INCREMENT\b@i', $line );
|
518 |
+
|
519 |
+
//Got a default value?
|
520 |
+
$default = null;
|
521 |
+
if ( preg_match( "@\sDEFAULT\s+('[^']*'|\"[^\"]*\"|\d+)@i", $line, $matches ) ) {
|
522 |
+
$default = trim( $matches[1], '"\'' );
|
523 |
+
}
|
524 |
+
|
525 |
+
//Custom character set and/or collation?
|
526 |
+
$charset = null;
|
527 |
+
$collation = null;
|
528 |
+
|
529 |
+
if ( preg_match( '@ (?:\s CHARACTER \s+ SET \s+ (?P<charset>[^\s()]+) )? (?:\s COLLATE \s+ (?P<collation>[^\s()]+) )? @xi', $line, $matches ) ) {
|
530 |
+
if ( isset( $matches['charset'] ) ) {
|
531 |
+
$charset = $matches['charset'];
|
532 |
+
}
|
533 |
+
if ( isset( $matches['collation'] ) ) {
|
534 |
+
$collation = $matches['collation'];
|
535 |
+
}
|
536 |
+
}
|
537 |
+
|
538 |
+
//Generate the normalized column definition
|
539 |
+
$column_definition = $data_type;
|
540 |
+
if ( ! empty( $charset ) ) {
|
541 |
+
$column_definition .= " CHARACTER SET {$charset}";
|
542 |
+
}
|
543 |
+
if ( ! empty( $collation ) ) {
|
544 |
+
$column_definition .= " COLLATE {$collation}";
|
545 |
+
}
|
546 |
+
if ( ! $null_allowed ) {
|
547 |
+
$column_definition .= ' NOT NULL';
|
548 |
+
}
|
549 |
+
if ( ! is_null( $default ) ) {
|
550 |
+
$column_definition .= " DEFAULT '{$default}'";
|
551 |
+
}
|
552 |
+
if ( $auto_increment ) {
|
553 |
+
$column_definition .= ' AUTO_INCREMENT';
|
554 |
+
}
|
555 |
+
|
556 |
+
return compact( 'data_type', 'null_allowed', 'auto_increment', 'default', 'charset', 'collation', 'column_definition' );
|
557 |
+
}
|
558 |
+
|
559 |
+
/**
|
560 |
+
* Generate an index's definition string from its parsed representation.
|
561 |
+
*
|
562 |
+
* @param array $definition The return value of blcTableDelta::parse_create_definition()
|
563 |
+
* @return string
|
564 |
+
*/
|
565 |
+
static function generate_index_string( $definition ) {
|
566 |
+
|
567 |
+
//Rebuild the index def. in a normalized form
|
568 |
+
$index_definition = '';
|
569 |
+
if ( ! empty( $definition['index_modifier'] ) ) {
|
570 |
+
$index_definition .= strtoupper( $definition['index_modifier'] ) . ' ';
|
571 |
+
}
|
572 |
+
$index_definition .= 'KEY';
|
573 |
+
if ( empty( $definition['index_modifier'] ) || ( 'primary' != $definition['index_modifier'] ) ) {
|
574 |
+
$index_definition .= ' `' . $definition['name'] . '`';
|
575 |
+
}
|
576 |
+
|
577 |
+
$column_strings = array();
|
578 |
+
foreach ( $definition['columns'] as $column ) {
|
579 |
+
$c = '`' . $column['column_name'] . '`';
|
580 |
+
if ( $column['length'] ) {
|
581 |
+
$c .= '(' . $column['length'] . ')';
|
582 |
+
}
|
583 |
+
$column_strings[] = $c;
|
584 |
+
}
|
585 |
+
|
586 |
+
$index_definition .= ' (' . implode( ', ', $column_strings ) . ')';
|
587 |
+
return $index_definition;
|
588 |
+
}
|
589 |
+
|
590 |
+
}
|
591 |
+
|
includes/admin/links-page-js.php
CHANGED
@@ -1,946 +1,946 @@
|
|
1 |
-
<script type='text/javascript'>
|
2 |
-
|
3 |
-
function alterLinkCounter(factor, filterId){
|
4 |
-
var counter;
|
5 |
-
if (filterId) {
|
6 |
-
counter = jQuery('.filter-' + filterId + '-link-count');
|
7 |
-
} else {
|
8 |
-
counter = jQuery('.current-link-count');
|
9 |
-
}
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
if ( blc_is_broken_filter ){
|
16 |
-
//Update the broken link count displayed beside the "Broken Links" menu
|
17 |
-
var menuBubble = jQuery('span.blc-menu-bubble');
|
18 |
-
if ( menuBubble.length > 0 ){
|
19 |
-
cnt = parseInt(menuBubble.eq(0).html());
|
20 |
-
cnt = cnt + factor;
|
21 |
-
if ( cnt > 0 ){
|
22 |
-
menuBubble.html(cnt);
|
23 |
-
} else {
|
24 |
-
menuBubble.parent().hide();
|
25 |
-
}
|
26 |
-
}
|
27 |
-
}
|
28 |
-
}
|
29 |
-
|
30 |
-
function replaceLinkId(old_id, new_id){
|
31 |
-
var master = jQuery('#blc-row-'+old_id);
|
32 |
-
|
33 |
-
master.attr('id', 'blc-row-'+new_id);
|
34 |
-
master.find('.blc-link-id').html(new_id);
|
35 |
-
master.find('th.check-column input[type="checkbox"]').val(new_id);
|
36 |
-
|
37 |
-
var details_row = jQuery('#link-details-'+old_id);
|
38 |
-
details_row.attr('id', 'link-details-'+new_id);
|
39 |
-
}
|
40 |
-
|
41 |
-
function reloadDetailsRow(link_id){
|
42 |
-
var details_row = jQuery('#link-details-'+link_id);
|
43 |
-
|
44 |
-
//Load up the new link info (so sue me)
|
45 |
-
details_row.find('td').html('<center><?php echo esc_js(__('Loading...'
|
46 |
-
"<?php echo admin_url('admin-ajax.php'); ?>",
|
47 |
-
{
|
48 |
-
'action' : 'blc_link_details',
|
49 |
-
'link_id' : link_id
|
50 |
-
}
|
51 |
-
);
|
52 |
-
}
|
53 |
-
|
54 |
-
jQuery(function($){
|
55 |
-
|
56 |
-
//The details button - display/hide detailed info about a link
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
$('#link-details-'+link_id).toggle();
|
61 |
-
return false;
|
62 |
-
|
63 |
-
|
64 |
-
var ajaxInProgressHtml = '<?php echo esc_js(__('Wait...', 'broken-link-checker')); ?>';
|
65 |
-
|
66 |
-
//The "Not broken" button - manually mark the link as valid. The link will be checked again later.
|
67 |
-
$(".blc-discard-button").click(function () {
|
68 |
-
var me = $(this);
|
69 |
-
me.html(ajaxInProgressHtml);
|
70 |
-
|
71 |
-
var master = me.parents('.blc-row');
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
"<?php echo admin_url('admin-ajax.php'); ?>",
|
76 |
-
{
|
77 |
-
'action' : 'blc_discard',
|
78 |
-
'link_id' : link_id,
|
79 |
-
'_ajax_nonce' : '<?php echo esc_js(wp_create_nonce('blc_discard'));
|
80 |
-
},
|
81 |
-
function (data, textStatus){
|
82 |
-
if (data == 'OK'){
|
83 |
-
var details = $('#link-details-'+link_id);
|
84 |
-
|
85 |
-
//Remove the "Not broken" action
|
86 |
-
me.parent().remove();
|
87 |
-
|
88 |
-
//Set the displayed link status to OK
|
89 |
-
var classNames = master.attr('class');
|
90 |
-
classNames = classNames.replace(/(^|\s)link-status-[^\s]+(\s|$)/, ' ') + ' link-status-ok';
|
91 |
-
master.attr('class', classNames);
|
92 |
-
|
93 |
-
//Flash the main row green to indicate success, then remove it if the current view
|
94 |
-
//is supposed to show only broken links or warnings.
|
95 |
-
var should_hide_link = blc_is_broken_filter || (blc_current_base_filter == 'warnings');
|
96 |
-
|
97 |
-
flashElementGreen(master, function(){
|
98 |
-
if ( should_hide_link ){
|
99 |
-
details.remove();
|
100 |
-
master.remove();
|
101 |
-
} else {
|
102 |
-
reloadDetailsRow(link_id);
|
103 |
-
}
|
104 |
-
});
|
105 |
-
|
106 |
-
//Update the elements displaying the number of results for the current filter.
|
107 |
-
if( should_hide_link ){
|
108 |
-
|
109 |
-
|
110 |
-
} else {
|
111 |
-
me.html('<?php echo esc_js(__('Not broken'
|
112 |
-
alert(data);
|
113 |
-
}
|
114 |
-
}
|
115 |
-
);
|
116 |
-
|
117 |
-
return false;
|
118 |
-
|
119 |
-
|
120 |
-
//The "Dismiss" button - hide the link from the "Broken" and "Redirects" filters, but still apply link tweaks and so on.
|
121 |
-
$(".blc-dismiss-button").click(function () {
|
122 |
-
var me = $(this);
|
123 |
-
var oldButtonHtml = me.html();
|
124 |
-
me.html(ajaxInProgressHtml);
|
125 |
-
|
126 |
-
var master = me.closest('.blc-row');
|
127 |
-
var link_id = master.attr('id').split('-')[2];
|
128 |
-
var should_hide_link = $.inArray(blc_current_base_filter, ['broken', 'redirects', 'warnings']) > -1;
|
129 |
-
|
130 |
-
$.post(
|
131 |
-
"<?php echo admin_url('admin-ajax.php'); ?>",
|
132 |
-
{
|
133 |
-
'action' : 'blc_dismiss',
|
134 |
-
'link_id' : link_id,
|
135 |
-
'_ajax_nonce' : '<?php echo esc_js(wp_create_nonce('blc_dismiss'));
|
136 |
-
},
|
137 |
-
function (data, textStatus){
|
138 |
-
if (data == 'OK'){
|
139 |
-
var details = $('#link-details-'+link_id);
|
140 |
-
|
141 |
-
//Remove the "Dismiss" action
|
142 |
-
me.parent().hide();
|
143 |
-
|
144 |
-
//Flash the main row green to indicate success, then remove it if necessary.
|
145 |
-
flashElementGreen(master, function(){
|
146 |
-
if ( should_hide_link ){
|
147 |
-
details.remove();
|
148 |
-
master.remove();
|
149 |
-
}
|
150 |
-
});
|
151 |
-
|
152 |
-
//Update the elements displaying the number of results for the current filter.
|
153 |
-
if( should_hide_link ){
|
154 |
-
alterLinkCounter(-1);
|
155 |
-
alterLinkCounter(1, 'dismissed');
|
156 |
-
}
|
157 |
-
} else {
|
158 |
-
me.html(oldButtonHtml);
|
159 |
-
alert(data);
|
160 |
-
}
|
161 |
-
}
|
162 |
-
);
|
163 |
-
|
164 |
-
return false;
|
165 |
-
});
|
166 |
-
|
167 |
-
//The "Undismiss" button.
|
168 |
-
$(".blc-undismiss-button").click(function () {
|
169 |
-
var me = $(this);
|
170 |
-
var oldButtonHtml = me.html();
|
171 |
-
me.html(ajaxInProgressHtml);
|
172 |
-
|
173 |
-
var master = me.closest('.blc-row');
|
174 |
-
var link_id = master.attr('id').split('-')[2];
|
175 |
-
var should_hide_link = (blc_current_base_filter == 'dismissed');
|
176 |
-
|
177 |
-
$.post(
|
178 |
-
"<?php echo admin_url('admin-ajax.php'); ?>",
|
179 |
-
{
|
180 |
-
'action' : 'blc_undismiss',
|
181 |
-
'link_id' : link_id,
|
182 |
-
'_ajax_nonce' : '<?php echo esc_js(wp_create_nonce('blc_undismiss'));
|
183 |
-
},
|
184 |
-
function (data, textStatus){
|
185 |
-
if (data == 'OK'){
|
186 |
-
var details = $('#link-details-'+link_id);
|
187 |
-
|
188 |
-
//Remove the action.
|
189 |
-
me.parent().hide();
|
190 |
-
|
191 |
-
//Flash the main row green to indicate success, then remove it if necessary.
|
192 |
-
flashElementGreen(master, function(){
|
193 |
-
if ( should_hide_link ){
|
194 |
-
details.remove();
|
195 |
-
master.remove();
|
196 |
-
}
|
197 |
-
});
|
198 |
-
|
199 |
-
//Update the elements displaying the number of results for the current filter.
|
200 |
-
if( should_hide_link ){
|
201 |
-
alterLinkCounter(-1);
|
202 |
-
}
|
203 |
-
} else {
|
204 |
-
me.html(oldButtonHtml);
|
205 |
-
alert(data);
|
206 |
-
}
|
207 |
-
}
|
208 |
-
);
|
209 |
-
|
210 |
-
return false;
|
211 |
-
});
|
212 |
-
|
213 |
-
//The "Recheck" button.
|
214 |
-
$(".blc-recheck-button").click(function () {
|
215 |
-
var me = $(this);
|
216 |
-
var oldButtonHtml = me.html();
|
217 |
-
me.html(ajaxInProgressHtml);
|
218 |
-
|
219 |
-
var master = me.closest('.blc-row');
|
220 |
-
var link_id = master.attr('id').split('-')[2];
|
221 |
-
|
222 |
-
$.post(
|
223 |
-
"<?php echo admin_url('admin-ajax.php'); ?>",
|
224 |
-
{
|
225 |
-
'action' : 'blc_recheck',
|
226 |
-
'link_id' : link_id,
|
227 |
-
'_ajax_nonce' : '<?php echo esc_js(wp_create_nonce('blc_recheck'));
|
228 |
-
},
|
229 |
-
function (response){
|
230 |
-
me.html(oldButtonHtml);
|
231 |
-
|
232 |
-
if (response && (typeof(response['error']) != 'undefined')){
|
233 |
-
//An internal error occurred before the plugin could check the link (e.g. database error).
|
234 |
-
alert(response.error);
|
235 |
-
} else {
|
236 |
-
//Display the new status in the link row.
|
237 |
-
displayLinkStatus(master, response);
|
238 |
-
reloadDetailsRow(link_id);
|
239 |
-
|
240 |
-
//Flash the row green to indicate success
|
241 |
-
flashElementGreen(master);
|
242 |
-
}
|
243 |
-
},
|
244 |
-
'json'
|
245 |
-
);
|
246 |
-
|
247 |
-
return false;
|
248 |
-
});
|
249 |
-
|
250 |
-
//The "Fix redirect" action.
|
251 |
-
$('.blc-deredirect-button').click(function() {
|
252 |
-
//This action can only be used once. If it succeeds, it will no longer be applicable to the current link.
|
253 |
-
//If it fails, something is broken and trying again probably won't help.
|
254 |
-
var me = $(this);
|
255 |
-
me.text(ajaxInProgressHtml);
|
256 |
-
|
257 |
-
var master = me.closest('.blc-row');
|
258 |
-
var linkId = master.attr('id').split('-')[2];
|
259 |
-
var shouldHideLink = blc_current_base_filter == 'redirects';
|
260 |
-
var details = $('#link-details-' + linkId);
|
261 |
-
|
262 |
-
$.post(
|
263 |
-
"<?php echo admin_url('admin-ajax.php'); ?>",
|
264 |
-
{
|
265 |
-
'action' : 'blc_deredirect',
|
266 |
-
'link_id' : linkId,
|
267 |
-
'_ajax_nonce' : '<?php echo esc_js(wp_create_nonce('blc_deredirect'));
|
268 |
-
},
|
269 |
-
function (response){
|
270 |
-
me.closest('span').hide();
|
271 |
-
|
272 |
-
if (handleEditResponse(response, master, linkId, null)) {
|
273 |
-
if (shouldHideLink) {
|
274 |
-
details.remove();
|
275 |
-
master.remove();
|
276 |
-
}
|
277 |
-
}
|
278 |
-
},
|
279 |
-
'json'
|
280 |
-
);
|
281 |
-
|
282 |
-
return false;
|
283 |
-
});
|
284 |
-
|
285 |
-
function flashElementGreen(element, callback) {
|
286 |
-
var oldColor = element.css('background-color');
|
287 |
-
element.animate({ backgroundColor: "#E0FFB3" }, 200).animate({ backgroundColor: oldColor }, 300, callback);
|
288 |
-
}
|
289 |
-
|
290 |
-
|
291 |
-
/**
|
292 |
-
* Update status indicators for a link. This includes the contents of the "Status" column, CSS classes and so on.
|
293 |
-
*
|
294 |
-
* @param {Object} row Table row as a jQuery object.
|
295 |
-
* @param {Object} status
|
296 |
-
*/
|
297 |
-
function displayLinkStatus(row, status) {
|
298 |
-
//Update the status code and class.
|
299 |
-
var statusColumn = row.find('td.column-status');
|
300 |
-
if (status.status_text) {
|
301 |
-
statusColumn.find('.status-text').text(status.status_text);
|
302 |
-
}
|
303 |
-
statusColumn.find('.http-code').text(status.http_code ? status.http_code : '');
|
304 |
-
|
305 |
-
var oldStatusClass = row.attr('class').match(/(?:^|\s)(link-status-[^\s]+)(?:\s|$)/);
|
306 |
-
oldStatusClass = oldStatusClass ? oldStatusClass[1] : '';
|
307 |
-
var newStatusClass = 'link-status-' + status.status_code;
|
308 |
-
|
309 |
-
statusColumn.find('.link-status-row').removeClass(oldStatusClass).addClass(newStatusClass);
|
310 |
-
row.removeClass(oldStatusClass).addClass(newStatusClass);
|
311 |
-
|
312 |
-
//Last check time and failure duration are complicated to update, so we'll just hide them.
|
313 |
-
//The user can refresh the page to get the new values.
|
314 |
-
statusColumn.find('.link-last-checked td').html(' ');
|
315 |
-
statusColumn.find('.link-broken-for td').html(' ');
|
316 |
-
|
317 |
-
//The link may or may not be a redirect now.
|
318 |
-
row.toggleClass('blc-redirect', status.redirect_count > 0);
|
319 |
-
|
320 |
-
if (typeof status['redirect_count'] !== 'undefined') {
|
321 |
-
var redirectColumn = row.find('td.column-redirect-url').empty();
|
322 |
-
|
323 |
-
if (status.redirect_count > 0 && status.final_url) {
|
324 |
-
redirectColumn.append(
|
325 |
-
$(
|
326 |
-
'<a></a>',
|
327 |
-
{
|
328 |
-
href: status.final_url,
|
329 |
-
text: status.final_url,
|
330 |
-
title: status.final_url,
|
331 |
-
'class': 'blc-redirect-url',
|
332 |
-
target: '_blank'
|
333 |
-
}
|
334 |
-
)
|
335 |
-
);
|
336 |
-
}
|
337 |
-
}
|
338 |
-
}
|
339 |
-
|
340 |
-
|
341 |
-
/**
|
342 |
-
* Display the inline link editor.
|
343 |
-
*
|
344 |
-
* @param {Number} link_id Link ID. The link must be visible in the current view.
|
345 |
-
*/
|
346 |
-
function showLinkEditor(link_id) {
|
347 |
-
var master = $('#blc-row-' + link_id),
|
348 |
-
editorId = 'blc-edit-row-' + link_id,
|
349 |
-
editRow;
|
350 |
-
|
351 |
-
//Get rid of all existing inline editors.
|
352 |
-
master.closest('table').find('tr.blc-inline-editor').each(function() {
|
353 |
-
hideLinkEditor($(this));
|
354 |
-
});
|
355 |
-
|
356 |
-
//Create an inline editor for this link.
|
357 |
-
editRow = $('#blc-inline-edit-row').clone(true).attr('id', editorId);
|
358 |
-
editRow.toggleClass('alternate', master.hasClass('alternate'));
|
359 |
-
master.after(editRow);
|
360 |
-
|
361 |
-
//Populate editor fields.
|
362 |
-
var urlElement = master.find('a.blc-link-url');
|
363 |
-
var linkUrl = urlElement.data('editable-url') || urlElement.attr('href');
|
364 |
-
var urlInput = editRow.find('.blc-link-url-field').val(linkUrl);
|
365 |
-
|
366 |
-
var titleInput = editRow.find('.blc-link-text-field');
|
367 |
-
var linkText = master.data('link-text'),
|
368 |
-
canEditText = master.data('can-edit-text') == 1, //jQuery will convert a '1' to 1 (number) when reading a data attribute.
|
369 |
-
canEditUrl = master.data('can-edit-url') == 1,
|
370 |
-
noneText = '<?php echo esc_js(_x('(None)', 'link text', 'broken-link-checker')); ?>',
|
371 |
-
multipleLinksText = '<?php echo esc_js(_x('(Multiple links)', 'link text', 'broken-link-checker')); ?>';
|
372 |
-
|
373 |
-
titleInput.prop('readonly', !canEditText);
|
374 |
-
urlInput.prop('readonly', !canEditUrl);
|
375 |
-
|
376 |
-
if ( (typeof linkText !== 'undefined') && (linkText !== null) ) {
|
377 |
-
if (linkText === '') {
|
378 |
-
titleInput.val(canEditText ? linkText : noneText);
|
379 |
-
} else {
|
380 |
-
titleInput.val(linkText)
|
381 |
-
}
|
382 |
-
titleInput.prop('placeholder', noneText);
|
383 |
-
} else {
|
384 |
-
if (canEditText) {
|
385 |
-
titleInput.val('').prop('placeholder', multipleLinksText);
|
386 |
-
} else {
|
387 |
-
titleInput.val(multipleLinksText)
|
388 |
-
}
|
389 |
-
}
|
390 |
-
|
391 |
-
//Populate the list of URL replacement suggestions.
|
392 |
-
if (canEditUrl && blc_suggestions_enabled && (master.hasClass('link-status-error') || master.hasClass('link-status-warning'))) {
|
393 |
-
editRow.find('.blc-url-replacement-suggestions').show();
|
394 |
-
var suggestionList = editRow.find('.blc-suggestion-list');
|
395 |
-
findReplacementSuggestions(linkUrl, suggestionList);
|
396 |
-
}
|
397 |
-
|
398 |
-
editRow.find('.blc-update-link-button').prop('disabled', !(canEditUrl || canEditText));
|
399 |
-
|
400 |
-
//Make the editor span the entire width of the table.
|
401 |
-
editRow.find('td.blc-colspan-change').attr('colspan', master.closest('table').find('thead th:visible').length);
|
402 |
-
|
403 |
-
master.hide();
|
404 |
-
editRow.show();
|
405 |
-
urlInput.focus();
|
406 |
-
if (canEditUrl) {
|
407 |
-
urlInput.select();
|
408 |
-
}
|
409 |
-
}
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
*
|
414 |
-
* @param link_id Either a numeric link ID or a jQuery object that represents the editor row.
|
415 |
-
|
416 |
-
function hideLinkEditor(link_id) {
|
417 |
-
var editRow = isNaN(link_id) ? link_id : $('#blc-edit-row-' + link_id);
|
418 |
-
editRow.prev('tr.blc-row').show();
|
419 |
-
editRow.remove();
|
420 |
-
}
|
421 |
-
|
422 |
-
|
423 |
-
* Find possible replacements for a broken link and display them in a list.
|
424 |
-
|
425 |
-
* @param {String} url The current link URL.
|
426 |
-
* @param suggestionList jQuery object that represents a list element.
|
427 |
-
|
428 |
-
function findReplacementSuggestions(url, suggestionList) {
|
429 |
-
var searchingText = '<?php echo esc_js(_x('Searching...', 'link suggestions', 'broken-link-checker')) ?>';
|
430 |
-
var noSuggestionsText = '<?php echo esc_js(_x('No suggestions available.', 'link suggestions', 'broken-link-checker')) ?>';
|
431 |
-
var iaSuggestionName = '<?php echo esc_js(_x('Archived page from %s (via the Wayback Machine)', 'link suggestions', 'broken-link-checker')); ?>';
|
432 |
-
|
433 |
-
suggestionList.empty().append('<li>' + searchingText + '</li>');
|
434 |
-
|
435 |
-
var suggestionTemplate = $('#blc-suggestion-template').find('li').first();
|
436 |
-
|
437 |
-
//Check the Wayback Machine for an archived version of the page.
|
438 |
-
$.getJSON(
|
439 |
-
'https://archive.org/wayback/available?callback=?',
|
440 |
-
{ url: url },
|
441 |
-
|
442 |
-
function(data) {
|
443 |
-
suggestionList.empty();
|
444 |
-
|
445 |
-
//Check if there are any results.
|
446 |
-
if (!data || !data.archived_snapshots || !data.archived_snapshots.closest || !data.archived_snapshots.closest.available ) {
|
447 |
-
suggestionList.append('<li>' + noSuggestionsText + '</li>');
|
448 |
-
return;
|
449 |
-
}
|
450 |
-
|
451 |
-
var snapshot = data.archived_snapshots.closest;
|
452 |
-
|
453 |
-
//Convert the timestamp from YYYYMMDDHHMMSS to ISO 8601 date format.
|
454 |
-
var readableTimestamp = snapshot.timestamp.substr(0, 4) +
|
455 |
-
'-' + snapshot.timestamp.substr(4, 2) +
|
456 |
-
'-' + snapshot.timestamp.substr(6, 2);
|
457 |
-
var name = sprintf(iaSuggestionName, readableTimestamp);
|
458 |
-
|
459 |
-
//Enforce HTTPS by default
|
460 |
-
snapshot.url = (snapshot.url).replace( new RegExp("^http:", "m"), "https:");
|
461 |
-
|
462 |
-
//Display the suggestion.
|
463 |
-
var item = suggestionTemplate.clone();
|
464 |
-
item.find('.blc-suggestion-name a').text(name).attr('href', snapshot.url);
|
465 |
-
item.find('.blc-suggestion-url').text(snapshot.url);
|
466 |
-
suggestionList.append(item);
|
467 |
-
}
|
468 |
-
);
|
469 |
-
}
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
* Updates UI with the new link info and displays any error messages that might be generated.
|
474 |
-
*
|
475 |
-
* @param linkId Either a numeric link ID or a jQuery object representing the link row.
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
function updateLink(linkId, newUrl, newText) {
|
480 |
-
var master, editRow;
|
481 |
-
if ( isNaN(linkId) ){
|
482 |
-
master = linkId;
|
483 |
-
linkId = master.attr('id').split("-")[2]; //id="blc-row-$linkid"
|
484 |
-
} else {
|
485 |
-
master = $('#blc-row-' + linkId);
|
486 |
-
}
|
487 |
-
editRow = $('#blc-edit-row-' + linkId);
|
488 |
-
|
489 |
-
var urlElement = master.find('a.blc-link-url');
|
490 |
-
var progressIndicator = editRow.find('.waiting'),
|
491 |
-
updateButton = editRow.find('.blc-update-link-button');
|
492 |
-
progressIndicator.show();
|
493 |
-
updateButton.prop('disabled', true);
|
494 |
-
|
495 |
-
$.post(
|
496 |
-
'<?php echo admin_url('admin-ajax.php'); ?>',
|
497 |
-
{
|
498 |
-
'action' : 'blc_edit',
|
499 |
-
'link_id' : linkId,
|
500 |
-
'new_url' : newUrl,
|
501 |
-
'new_text' : newText,
|
502 |
-
'_ajax_nonce' : '<?php echo esc_js(wp_create_nonce('blc_edit'));
|
503 |
-
},
|
504 |
-
function(response) {
|
505 |
-
progressIndicator.hide();
|
506 |
-
updateButton.prop('disabled', false);
|
507 |
-
|
508 |
-
handleEditResponse(response, master, linkId, newText);
|
509 |
-
|
510 |
-
hideLinkEditor(editRow);
|
511 |
-
},
|
512 |
-
'json'
|
513 |
-
);
|
514 |
-
|
515 |
-
}
|
516 |
-
|
517 |
-
function handleEditResponse(response, master, linkId, newText) {
|
518 |
-
if (response && (typeof(response['error']) != 'undefined')){
|
519 |
-
//An internal error occurred before the link could be edited.
|
520 |
-
alert(response.error);
|
521 |
-
return false;
|
522 |
-
} else if (response.errors.length > 0) {
|
523 |
-
//Build and display an error message.
|
524 |
-
var msg = '';
|
525 |
-
|
526 |
-
if ( response.cnt_okay > 0 ){
|
527 |
-
var fragment = sprintf(
|
528 |
-
'<?php echo esc_js(__('%d instances of the link were successfully modified.', 'broken-link-checker')); ?>',
|
529 |
-
response.cnt_okay
|
530 |
-
);
|
531 |
-
msg = msg + fragment + '\n';
|
532 |
-
if ( response.cnt_error > 0 ){
|
533 |
-
fragment = sprintf(
|
534 |
-
'<?php echo esc_js(__("However, %d instances couldn't be edited and still point to the old URL.", 'broken-link-checker')); ?>',
|
535 |
-
response.cnt_error
|
536 |
-
);
|
537 |
-
msg = msg + fragment + "\n";
|
538 |
-
}
|
539 |
-
} else {
|
540 |
-
msg = msg + '<?php echo esc_js(__('The link could not be modified.', 'broken-link-checker')); ?>\n';
|
541 |
-
}
|
542 |
-
|
543 |
-
msg = msg + '\n<?php echo esc_js(__(
|
544 |
-
msg = msg + response.errors.join('\n* ');
|
545 |
-
|
546 |
-
alert(msg);
|
547 |
-
return false;
|
548 |
-
} else {
|
549 |
-
//Everything went well. Update the link row with the new values.
|
550 |
-
|
551 |
-
//Replace the displayed link URL with the new one.
|
552 |
-
var urlElement = master.find('a.blc-link-url');
|
553 |
-
urlElement
|
554 |
-
.attr('href', response.url)
|
555 |
-
.text(response.url)
|
556 |
-
.data('editable-url', response.url)
|
557 |
-
.prop('title', response.url);
|
558 |
-
if ( typeof response['escaped_url'] != 'undefined' ) {
|
559 |
-
urlElement.attr('href', response.escaped_url)
|
560 |
-
}
|
561 |
-
|
562 |
-
//Save the new ID
|
563 |
-
replaceLinkId(linkId, response.new_link_id);
|
564 |
-
//Load up the new link info
|
565 |
-
reloadDetailsRow(response.new_link_id);
|
566 |
-
|
567 |
-
//Update the link text if it was edited.
|
568 |
-
if ((newText !== null) && (response.link_text !== null)) {
|
569 |
-
master.data('link-text', response.link_text);
|
570 |
-
if (response.ui_link_text !== null) {
|
571 |
-
master.find('.column-new-link-text').html(response.ui_link_text);
|
572 |
-
}
|
573 |
-
}
|
574 |
-
|
575 |
-
//Update the status code and class.
|
576 |
-
displayLinkStatus(master, response);
|
577 |
-
|
578 |
-
//Flash the row green to indicate success
|
579 |
-
flashElementGreen(master);
|
580 |
-
|
581 |
-
return true;
|
582 |
-
}
|
583 |
-
}
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
var master = $(this).closest('.blc-row');
|
588 |
-
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
var editRow = $(this).closest('.blc-inline-editor');
|
595 |
-
if (e.which == 13) {
|
596 |
-
editRow.find('.blc-update-link-button').click();
|
597 |
-
return false;
|
598 |
-
} else if (e.which == 27) {
|
599 |
-
editRow.find('.blc-cancel-button').click();
|
600 |
-
return false;
|
601 |
-
}
|
602 |
-
return true;
|
603 |
-
});
|
604 |
-
|
605 |
-
|
606 |
-
//The "Update" button in the inline editor.
|
607 |
-
$('.blc-update-link-button').click(function() {
|
608 |
-
var editRow = $(this).closest('tr'),
|
609 |
-
master = editRow.prev('.blc-row');
|
610 |
-
|
611 |
-
//Ensure the new URL is not empty.
|
612 |
-
var urlField = editRow.find('.blc-link-url-field');
|
613 |
-
var newUrl = urlField.val();
|
614 |
-
if ($.trim(newUrl) == '') {
|
615 |
-
alert('<?php echo esc_js(__('Error: Link URL must not be empty.', 'broken-link-checker')); ?>');
|
616 |
-
urlField.focus();
|
617 |
-
return;
|
618 |
-
}
|
619 |
-
|
620 |
-
var newLinkText = null,
|
621 |
-
linkTextField = editRow.find('.blc-link-text-field'),
|
622 |
-
oldLinkText = master.data('link-text');
|
623 |
-
if (!linkTextField.prop('readonly')) {
|
624 |
-
newLinkText = linkTextField.val();
|
625 |
-
//Empty or identical to the original = leave the text unchanged.
|
626 |
-
if ((newLinkText === '') || (newLinkText === oldLinkText)) {
|
627 |
-
newLinkText = null;
|
628 |
-
}
|
629 |
-
}
|
630 |
-
|
631 |
-
updateLink(master, newUrl, newLinkText);
|
632 |
-
});
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
var editRow = $(this).closest('tr');
|
637 |
-
hideLinkEditor(editRow);
|
638 |
-
|
639 |
-
|
640 |
-
//The "Use this URL" button in the inline editor replaces the link URL
|
641 |
-
//with the selected suggestion URL.
|
642 |
-
$('#blc-links').on('click', '.blc-use-url-button', function() {
|
643 |
-
var button = $(this);
|
644 |
-
var suggestionUrl = button.closest('tr').find('.blc-suggestion-name a').attr('href');
|
645 |
-
button.closest('.blc-inline-editor').find('.blc-link-url-field').val(suggestionUrl);
|
646 |
-
});
|
647 |
-
|
648 |
-
|
649 |
-
|
650 |
-
|
651 |
-
|
652 |
-
|
653 |
-
$(me).html('<?php echo esc_js(__('Wait...'
|
654 |
-
|
655 |
-
//Find the link ID
|
656 |
-
|
657 |
-
|
658 |
-
|
659 |
-
"<?php echo admin_url('admin-ajax.php'); ?>",
|
660 |
-
{
|
661 |
-
'action' : 'blc_unlink',
|
662 |
-
'link_id' : link_id,
|
663 |
-
'_ajax_nonce' : '<?php echo esc_js(wp_create_nonce('blc_unlink'));
|
664 |
-
},
|
665 |
-
function (data, textStatus){
|
666 |
-
eval('data = ' + data);
|
667 |
-
|
668 |
-
if ( data && (typeof(data['error']) != 'undefined') ){
|
669 |
-
//An internal error occurred before the link could be edited.
|
670 |
-
//data.error is an error message.
|
671 |
-
alert(data.error);
|
672 |
-
} else {
|
673 |
-
if ( data.errors.length == 0 ){
|
674 |
-
//The link was successfully removed. Hide its details.
|
675 |
-
$('#link-details-'+link_id).hide();
|
676 |
-
//Flash the main row green to indicate success, then hide it.
|
677 |
-
var oldColor = master.css('background-color');
|
678 |
-
master.animate({ backgroundColor: "#E0FFB3" }, 200).animate({ backgroundColor: oldColor }, 300, function(){
|
679 |
-
master.hide();
|
680 |
-
});
|
681 |
-
|
682 |
-
alterLinkCounter(-1);
|
683 |
-
|
684 |
-
return;
|
685 |
-
} else {
|
686 |
-
//Build and display an error message.
|
687 |
-
var msg = '';
|
688 |
-
|
689 |
-
if ( data.cnt_okay > 0 ){
|
690 |
-
msg = msg + sprintf(
|
691 |
-
'<?php echo esc_js(__(
|
692 |
-
data.cnt_okay
|
693 |
-
);
|
694 |
-
|
695 |
-
if ( data.cnt_error > 0 ){
|
696 |
-
msg = msg + sprintf(
|
697 |
-
'<?php echo esc_js(__("However, %d instances couldn't be removed.", 'broken-link-checker')); ?>\n',
|
698 |
-
data.cnt_error
|
699 |
-
);
|
700 |
-
}
|
701 |
-
} else {
|
702 |
-
msg = msg + '<?php echo esc_js(__(
|
703 |
-
}
|
704 |
-
|
705 |
-
msg = msg + '\n<?php echo esc_js(__(
|
706 |
-
msg = msg + data.errors.join('\n* ');
|
707 |
-
|
708 |
-
//Show the error message
|
709 |
-
alert(msg);
|
710 |
-
}
|
711 |
-
}
|
712 |
-
|
713 |
-
$(me).html('<?php echo esc_js(__('Unlink'
|
714 |
-
}
|
715 |
-
);
|
716 |
-
|
717 |
-
|
718 |
-
|
719 |
-
|
720 |
-
|
721 |
-
|
722 |
-
|
723 |
-
|
724 |
-
|
725 |
-
autoOpen : false,
|
726 |
-
dialogClass : 'blc-search-container',
|
727 |
-
resizable: false
|
728 |
-
});
|
729 |
-
|
730 |
-
|
731 |
-
|
732 |
-
searchForm.dialog('close');
|
733 |
-
} else {
|
734 |
-
searchForm
|
735 |
-
.dialog('open')
|
736 |
-
.dialog('widget')
|
737 |
-
.position({
|
738 |
-
my: 'right top',
|
739 |
-
at: 'right bottom',
|
740 |
-
of: $('#blc-open-search-box')
|
741 |
-
});
|
742 |
-
}
|
743 |
-
});
|
744 |
-
|
745 |
-
$('#blc-cancel-search').click(function(){
|
746 |
-
searchForm.dialog('close');
|
747 |
-
});
|
748 |
-
|
749 |
-
//The "Save This Search Query" button creates a new custom filter based on the current search
|
750 |
-
$('#blc-create-filter').click(function(){
|
751 |
-
var filter_name = prompt("<?php echo esc_js(__(
|
752 |
-
if ( filter_name ){
|
753 |
-
$('#blc-custom-filter-name').val(filter_name);
|
754 |
-
$('#custom-filter-form').submit();
|
755 |
-
}
|
756 |
-
});
|
757 |
-
|
758 |
-
//Display a confirmation dialog when the user clicks the "Delete This Filter" button
|
759 |
-
$('#blc-delete-filter').click(function(){
|
760 |
-
var message = '<?php
|
761 |
-
echo esc_js(
|
762 |
-
html_entity_decode(
|
763 |
-
__("You are about to delete the current filter.\n'Cancel' to stop, 'OK' to delete", 'broken-link-checker'),
|
764 |
-
ENT_QUOTES,
|
765 |
-
get_bloginfo('charset')
|
766 |
-
)
|
767 |
-
);
|
768 |
-
?>';
|
769 |
-
return confirm(message);
|
770 |
-
});
|
771 |
-
|
772 |
-
//--------------------------------------------
|
773 |
-
|
774 |
-
|
775 |
-
|
776 |
-
|
777 |
-
|
778 |
-
|
779 |
-
action = $('#blc-bulk-action2').val();
|
780 |
-
}
|
781 |
-
|
782 |
-
|
783 |
-
|
784 |
-
|
785 |
-
echo esc_js(
|
786 |
-
html_entity_decode(
|
787 |
-
__("Are you sure you want to delete all posts, bookmarks or other items that contain any of the selected links? This action can't be undone.\n'Cancel' to stop, 'OK' to delete", 'broken-link-checker'),
|
788 |
-
ENT_QUOTES,
|
789 |
-
get_bloginfo('charset')
|
790 |
-
)
|
791 |
-
);
|
792 |
-
|
793 |
-
if ( !confirm(message) ){
|
794 |
-
return false;
|
795 |
-
}
|
796 |
-
} else if ( action == 'bulk-unlink' ){
|
797 |
-
//Likewise for unlinking.
|
798 |
-
message = '<?php
|
799 |
-
echo esc_js(
|
800 |
-
html_entity_decode(
|
801 |
-
__("Are you sure you want to remove the selected links? This action can't be undone.\n'Cancel' to stop, 'OK' to remove", 'broken-link-checker'),
|
802 |
-
ENT_QUOTES,
|
803 |
-
get_bloginfo('charset')
|
804 |
-
)
|
805 |
-
);
|
806 |
-
|
807 |
-
if ( !confirm(message) ){
|
808 |
-
return false;
|
809 |
-
}
|
810 |
-
}
|
811 |
-
});
|
812 |
-
|
813 |
-
//Automatically disable bulk actions that don't apply to the currently selected links.
|
814 |
-
$('#blc-bulk-action').focus(function() {
|
815 |
-
var redirectsSelected = false, brokenLinksSelected = false;
|
816 |
-
$('tr th.check-column input:checked', '#blc-links').each(function() {
|
817 |
-
var row = $(this).closest('tr');
|
818 |
-
if (row.hasClass('blc-redirect')) {
|
819 |
-
redirectsSelected = true
|
820 |
-
}
|
821 |
-
if (row.hasClass('link-status-error') || row.hasClass('link-status-warning')) {
|
822 |
-
brokenLinksSelected = true;
|
823 |
-
}
|
824 |
-
});
|
825 |
-
|
826 |
-
var bulkAction = $(this);
|
827 |
-
bulkAction.find('option[value="bulk-deredirect"]').prop('disabled', !redirectsSelected);
|
828 |
-
bulkAction.find('option[value="bulk-not-broken"]').prop('disabled', !brokenLinksSelected);
|
829 |
-
});
|
830 |
-
|
831 |
-
//------------------------------------------------------------
|
832 |
-
|
833 |
-
|
834 |
-
|
835 |
-
var failure_duration_threshold_input = $('#failure_duration_threshold');
|
836 |
-
|
837 |
-
|
838 |
-
|
839 |
-
|
840 |
-
|
841 |
-
if ( this.checked ){
|
842 |
-
$('#blc-links tr.blc-permanently-broken').addClass('blc-permanently-broken-hl');
|
843 |
-
} else {
|
844 |
-
$('#blc-links tr.blc-permanently-broken').removeClass('blc-permanently-broken-hl');
|
845 |
-
}
|
846 |
-
});
|
847 |
-
|
848 |
-
//Apply/remove highlights when the duration threshold is changed.
|
849 |
-
failure_duration_threshold_input.change(function(){
|
850 |
-
var new_threshold = parseInt($(this).val());
|
851 |
-
//save_highlight_settings();
|
852 |
-
if (isNaN(new_threshold) || (new_threshold < 1)) {
|
853 |
-
return;
|
854 |
-
}
|
855 |
-
|
856 |
-
highlight_permanent_failures = highlight_permanent_failures_checkbox.is(':checked');
|
857 |
-
|
858 |
-
$('#blc-links tr.blc-row').each(function(index){
|
859 |
-
var days_broken = $(this).attr('data-days-broken');
|
860 |
-
if ( days_broken >= new_threshold ){
|
861 |
-
$(this).addClass('blc-permanently-broken');
|
862 |
-
if ( highlight_permanent_failures ){
|
863 |
-
$(this).addClass('blc-permanently-broken-hl');
|
864 |
-
}
|
865 |
-
} else {
|
866 |
-
$(this).removeClass('blc-permanently-broken').removeClass('blc-permanently-broken-hl');
|
867 |
-
}
|
868 |
-
});
|
869 |
-
});
|
870 |
-
|
871 |
-
//Show/hide table columns dynamically
|
872 |
-
$('#blc-column-selector input[type="checkbox"]').change(function(){
|
873 |
-
var checkbox = $(this);
|
874 |
-
var column_id = checkbox.attr('name').split(/\[|\]/)[1];
|
875 |
-
if (checkbox.is(':checked')){
|
876 |
-
$('td.column-'+column_id+', th.column-'+column_id, '#blc-links').removeClass('hidden');
|
877 |
-
} else {
|
878 |
-
$('td.column-'+column_id+', th.column-'+column_id, '#blc-links').addClass('hidden');
|
879 |
-
}
|
880 |
-
|
881 |
-
//Recalculate colspan's for detail rows to take into account the changed number of
|
882 |
-
//visible columns. Otherwise you can get some ugly layout glitches.
|
883 |
-
$('#blc-links tr.blc-link-details td').attr(
|
884 |
-
'colspan',
|
885 |
-
$('#blc-column-selector input[type="checkbox"]:checked').length+1
|
886 |
-
);
|
887 |
-
});
|
888 |
-
|
889 |
-
//Unlike other fields in "Screen Options", the links-per-page setting
|
890 |
-
//is handled using straight form submission (POST), not AJAX.
|
891 |
-
$('#blc-per-page-apply-button').click(function(){
|
892 |
-
$('#adv-settings').submit();
|
893 |
-
});
|
894 |
-
|
895 |
-
$('#blc_links_per_page').keypress(function(e){
|
896 |
-
if ((e.which && e.which == 13) || (e.keyCode && e.keyCode == 13)) {
|
897 |
-
$('#adv-settings').submit();
|
898 |
-
}
|
899 |
-
});
|
900 |
-
|
901 |
-
//Toggle status code colors when the corresponding checkbox is toggled
|
902 |
-
$('#table_color_code_status').click(function(){
|
903 |
-
if ( $(this).is(':checked') ){
|
904 |
-
$('#blc-links').addClass('color-code-link-status');
|
905 |
-
} else {
|
906 |
-
$('#blc-links').removeClass('color-code-link-status');
|
907 |
-
}
|
908 |
-
});
|
909 |
-
|
910 |
-
//Show the bulk edit/find & replace form when the user applies the appropriate bulk action
|
911 |
-
$('#doaction, #doaction2').click(function(e){
|
912 |
-
var n = $(this).attr('id').substr(2);
|
913 |
-
if ( $('select[name="'+n+'"]').val() == 'bulk-edit' ) {
|
914 |
-
e.preventDefault();
|
915 |
-
//Any links selected?
|
916 |
-
if ($('tbody th.check-column input:checked').length > 0){
|
917 |
-
$('#bulk-edit').show();
|
918 |
-
}
|
919 |
-
}
|
920 |
-
});
|
921 |
-
|
922 |
-
//Hide the bulk edit/find & replace form when "Cancel" is clicked
|
923 |
-
$('#bulk-edit .cancel').click(function(){
|
924 |
-
$('#bulk-edit').hide();
|
925 |
-
return false;
|
926 |
-
});
|
927 |
-
|
928 |
-
//Minimal input validation for the bulk edit form
|
929 |
-
$('#bulk-edit input[type="submit"]').click(function(e){
|
930 |
-
if( $('#bulk-edit input[name="search"]').val() == '' ){
|
931 |
-
alert('<?php echo esc_js(__('Enter a search string first.', 'broken-link-checker')); ?>');
|
932 |
-
$('#bulk-edit input[name="search"]').focus();
|
933 |
-
e.preventDefault();
|
934 |
-
return;
|
935 |
-
}
|
936 |
-
|
937 |
-
if ($('tbody th.check-column input:checked').length == 0){
|
938 |
-
alert('<?php echo esc_js(__('Select one or more links to edit.', 'broken-link-checker')); ?>');
|
939 |
-
e.preventDefault();
|
940 |
-
}
|
941 |
-
});
|
942 |
-
|
943 |
-
|
944 |
-
});
|
945 |
-
|
946 |
-
</script>
|
1 |
+
<script type='text/javascript'>
|
2 |
+
|
3 |
+
function alterLinkCounter(factor, filterId){
|
4 |
+
var counter;
|
5 |
+
if (filterId) {
|
6 |
+
counter = jQuery('.filter-' + filterId + '-link-count');
|
7 |
+
} else {
|
8 |
+
counter = jQuery('.current-link-count');
|
9 |
+
}
|
10 |
+
|
11 |
+
var cnt = parseInt(counter.eq(0).html(), 10);
|
12 |
+
cnt = cnt + factor;
|
13 |
+
counter.html(cnt);
|
14 |
+
|
15 |
+
if ( blc_is_broken_filter ){
|
16 |
+
//Update the broken link count displayed beside the "Broken Links" menu
|
17 |
+
var menuBubble = jQuery('span.blc-menu-bubble');
|
18 |
+
if ( menuBubble.length > 0 ){
|
19 |
+
cnt = parseInt(menuBubble.eq(0).html());
|
20 |
+
cnt = cnt + factor;
|
21 |
+
if ( cnt > 0 ){
|
22 |
+
menuBubble.html(cnt);
|
23 |
+
} else {
|
24 |
+
menuBubble.parent().hide();
|
25 |
+
}
|
26 |
+
}
|
27 |
+
}
|
28 |
+
}
|
29 |
+
|
30 |
+
function replaceLinkId(old_id, new_id){
|
31 |
+
var master = jQuery('#blc-row-'+old_id);
|
32 |
+
|
33 |
+
master.attr('id', 'blc-row-'+new_id);
|
34 |
+
master.find('.blc-link-id').html(new_id);
|
35 |
+
master.find('th.check-column input[type="checkbox"]').val(new_id);
|
36 |
+
|
37 |
+
var details_row = jQuery('#link-details-'+old_id);
|
38 |
+
details_row.attr('id', 'link-details-'+new_id);
|
39 |
+
}
|
40 |
+
|
41 |
+
function reloadDetailsRow(link_id){
|
42 |
+
var details_row = jQuery('#link-details-'+link_id);
|
43 |
+
|
44 |
+
//Load up the new link info (so sue me)
|
45 |
+
details_row.find('td').html('<center><?php echo esc_js( __( 'Loading...', 'broken-link-checker' ) ); ?></center>').load(
|
46 |
+
"<?php echo admin_url( 'admin-ajax.php' ); ?>",
|
47 |
+
{
|
48 |
+
'action' : 'blc_link_details',
|
49 |
+
'link_id' : link_id
|
50 |
+
}
|
51 |
+
);
|
52 |
+
}
|
53 |
+
|
54 |
+
jQuery(function($){
|
55 |
+
|
56 |
+
//The details button - display/hide detailed info about a link
|
57 |
+
$(".blc-details-button, td.column-link-text, td.column-status, td.column-new-link-text").click(function () {
|
58 |
+
var master = $(this).parents('.blc-row');
|
59 |
+
var link_id = master.attr('id').split('-')[2];
|
60 |
+
$('#link-details-'+link_id).toggle();
|
61 |
+
return false;
|
62 |
+
});
|
63 |
+
|
64 |
+
var ajaxInProgressHtml = '<?php echo esc_js( __( 'Wait...', 'broken-link-checker' ) ); ?>';
|
65 |
+
|
66 |
+
//The "Not broken" button - manually mark the link as valid. The link will be checked again later.
|
67 |
+
$(".blc-discard-button").click(function () {
|
68 |
+
var me = $(this);
|
69 |
+
me.html(ajaxInProgressHtml);
|
70 |
+
|
71 |
+
var master = me.parents('.blc-row');
|
72 |
+
var link_id = master.attr('id').split('-')[2];
|
73 |
+
|
74 |
+
$.post(
|
75 |
+
"<?php echo admin_url( 'admin-ajax.php' ); ?>",
|
76 |
+
{
|
77 |
+
'action' : 'blc_discard',
|
78 |
+
'link_id' : link_id,
|
79 |
+
'_ajax_nonce' : '<?php echo esc_js( wp_create_nonce( 'blc_discard' ) ); ?>'
|
80 |
+
},
|
81 |
+
function (data, textStatus){
|
82 |
+
if (data == 'OK'){
|
83 |
+
var details = $('#link-details-'+link_id);
|
84 |
+
|
85 |
+
//Remove the "Not broken" action
|
86 |
+
me.parent().remove();
|
87 |
+
|
88 |
+
//Set the displayed link status to OK
|
89 |
+
var classNames = master.attr('class');
|
90 |
+
classNames = classNames.replace(/(^|\s)link-status-[^\s]+(\s|$)/, ' ') + ' link-status-ok';
|
91 |
+
master.attr('class', classNames);
|
92 |
+
|
93 |
+
//Flash the main row green to indicate success, then remove it if the current view
|
94 |
+
//is supposed to show only broken links or warnings.
|
95 |
+
var should_hide_link = blc_is_broken_filter || (blc_current_base_filter == 'warnings');
|
96 |
+
|
97 |
+
flashElementGreen(master, function(){
|
98 |
+
if ( should_hide_link ){
|
99 |
+
details.remove();
|
100 |
+
master.remove();
|
101 |
+
} else {
|
102 |
+
reloadDetailsRow(link_id);
|
103 |
+
}
|
104 |
+
});
|
105 |
+
|
106 |
+
//Update the elements displaying the number of results for the current filter.
|
107 |
+
if( should_hide_link ){
|
108 |
+
alterLinkCounter(-1);
|
109 |
+
}
|
110 |
+
} else {
|
111 |
+
me.html('<?php echo esc_js( __( 'Not broken', 'broken-link-checker' ) ); ?>');
|
112 |
+
alert(data);
|
113 |
+
}
|
114 |
+
}
|
115 |
+
);
|
116 |
+
|
117 |
+
return false;
|
118 |
+
});
|
119 |
+
|
120 |
+
//The "Dismiss" button - hide the link from the "Broken" and "Redirects" filters, but still apply link tweaks and so on.
|
121 |
+
$(".blc-dismiss-button").click(function () {
|
122 |
+
var me = $(this);
|
123 |
+
var oldButtonHtml = me.html();
|
124 |
+
me.html(ajaxInProgressHtml);
|
125 |
+
|
126 |
+
var master = me.closest('.blc-row');
|
127 |
+
var link_id = master.attr('id').split('-')[2];
|
128 |
+
var should_hide_link = $.inArray(blc_current_base_filter, ['broken', 'redirects', 'warnings']) > -1;
|
129 |
+
|
130 |
+
$.post(
|
131 |
+
"<?php echo admin_url( 'admin-ajax.php' ); ?>",
|
132 |
+
{
|
133 |
+
'action' : 'blc_dismiss',
|
134 |
+
'link_id' : link_id,
|
135 |
+
'_ajax_nonce' : '<?php echo esc_js( wp_create_nonce( 'blc_dismiss' ) ); ?>'
|
136 |
+
},
|
137 |
+
function (data, textStatus){
|
138 |
+
if (data == 'OK'){
|
139 |
+
var details = $('#link-details-'+link_id);
|
140 |
+
|
141 |
+
//Remove the "Dismiss" action
|
142 |
+
me.parent().hide();
|
143 |
+
|
144 |
+
//Flash the main row green to indicate success, then remove it if necessary.
|
145 |
+
flashElementGreen(master, function(){
|
146 |
+
if ( should_hide_link ){
|
147 |
+
details.remove();
|
148 |
+
master.remove();
|
149 |
+
}
|
150 |
+
});
|
151 |
+
|
152 |
+
//Update the elements displaying the number of results for the current filter.
|
153 |
+
if( should_hide_link ){
|
154 |
+
alterLinkCounter(-1);
|
155 |
+
alterLinkCounter(1, 'dismissed');
|
156 |
+
}
|
157 |
+
} else {
|
158 |
+
me.html(oldButtonHtml);
|
159 |
+
alert(data);
|
160 |
+
}
|
161 |
+
}
|
162 |
+
);
|
163 |
+
|
164 |
+
return false;
|
165 |
+
});
|
166 |
+
|
167 |
+
//The "Undismiss" button.
|
168 |
+
$(".blc-undismiss-button").click(function () {
|
169 |
+
var me = $(this);
|
170 |
+
var oldButtonHtml = me.html();
|
171 |
+
me.html(ajaxInProgressHtml);
|
172 |
+
|
173 |
+
var master = me.closest('.blc-row');
|
174 |
+
var link_id = master.attr('id').split('-')[2];
|
175 |
+
var should_hide_link = (blc_current_base_filter == 'dismissed');
|
176 |
+
|
177 |
+
$.post(
|
178 |
+
"<?php echo admin_url( 'admin-ajax.php' ); ?>",
|
179 |
+
{
|
180 |
+
'action' : 'blc_undismiss',
|
181 |
+
'link_id' : link_id,
|
182 |
+
'_ajax_nonce' : '<?php echo esc_js( wp_create_nonce( 'blc_undismiss' ) ); ?>'
|
183 |
+
},
|
184 |
+
function (data, textStatus){
|
185 |
+
if (data == 'OK'){
|
186 |
+
var details = $('#link-details-'+link_id);
|
187 |
+
|
188 |
+
//Remove the action.
|
189 |
+
me.parent().hide();
|
190 |
+
|
191 |
+
//Flash the main row green to indicate success, then remove it if necessary.
|
192 |
+
flashElementGreen(master, function(){
|
193 |
+
if ( should_hide_link ){
|
194 |
+
details.remove();
|
195 |
+
master.remove();
|
196 |
+
}
|
197 |
+
});
|
198 |
+
|
199 |
+
//Update the elements displaying the number of results for the current filter.
|
200 |
+
if( should_hide_link ){
|
201 |
+
alterLinkCounter(-1);
|
202 |
+
}
|
203 |
+
} else {
|
204 |
+
me.html(oldButtonHtml);
|
205 |
+
alert(data);
|
206 |
+
}
|
207 |
+
}
|
208 |
+
);
|
209 |
+
|
210 |
+
return false;
|
211 |
+
});
|
212 |
+
|
213 |
+
//The "Recheck" button.
|
214 |
+
$(".blc-recheck-button").click(function () {
|
215 |
+
var me = $(this);
|
216 |
+
var oldButtonHtml = me.html();
|
217 |
+
me.html(ajaxInProgressHtml);
|
218 |
+
|
219 |
+
var master = me.closest('.blc-row');
|
220 |
+
var link_id = master.attr('id').split('-')[2];
|
221 |
+
|
222 |
+
$.post(
|
223 |
+
"<?php echo admin_url( 'admin-ajax.php' ); ?>",
|
224 |
+
{
|
225 |
+
'action' : 'blc_recheck',
|
226 |
+
'link_id' : link_id,
|
227 |
+
'_ajax_nonce' : '<?php echo esc_js( wp_create_nonce( 'blc_recheck' ) ); ?>'
|
228 |
+
},
|
229 |
+
function (response){
|
230 |
+
me.html(oldButtonHtml);
|
231 |
+
|
232 |
+
if (response && (typeof(response['error']) != 'undefined')){
|
233 |
+
//An internal error occurred before the plugin could check the link (e.g. database error).
|
234 |
+
alert(response.error);
|
235 |
+
} else {
|
236 |
+
//Display the new status in the link row.
|
237 |
+
displayLinkStatus(master, response);
|
238 |
+
reloadDetailsRow(link_id);
|
239 |
+
|
240 |
+
//Flash the row green to indicate success
|
241 |
+
flashElementGreen(master);
|
242 |
+
}
|
243 |
+
},
|
244 |
+
'json'
|
245 |
+
);
|
246 |
+
|
247 |
+
return false;
|
248 |
+
});
|
249 |
+
|
250 |
+
//The "Fix redirect" action.
|
251 |
+
$('.blc-deredirect-button').click(function() {
|
252 |
+
//This action can only be used once. If it succeeds, it will no longer be applicable to the current link.
|
253 |
+
//If it fails, something is broken and trying again probably won't help.
|
254 |
+
var me = $(this);
|
255 |
+
me.text(ajaxInProgressHtml);
|
256 |
+
|
257 |
+
var master = me.closest('.blc-row');
|
258 |
+
var linkId = master.attr('id').split('-')[2];
|
259 |
+
var shouldHideLink = blc_current_base_filter == 'redirects';
|
260 |
+
var details = $('#link-details-' + linkId);
|
261 |
+
|
262 |
+
$.post(
|
263 |
+
"<?php echo admin_url( 'admin-ajax.php' ); ?>",
|
264 |
+
{
|
265 |
+
'action' : 'blc_deredirect',
|
266 |
+
'link_id' : linkId,
|
267 |
+
'_ajax_nonce' : '<?php echo esc_js( wp_create_nonce( 'blc_deredirect' ) ); ?>'
|
268 |
+
},
|
269 |
+
function (response){
|
270 |
+
me.closest('span').hide();
|
271 |
+
|
272 |
+
if (handleEditResponse(response, master, linkId, null)) {
|
273 |
+
if (shouldHideLink) {
|
274 |
+
details.remove();
|
275 |
+
master.remove();
|
276 |
+
}
|
277 |
+
}
|
278 |
+
},
|
279 |
+
'json'
|
280 |
+
);
|
281 |
+
|
282 |
+
return false;
|
283 |
+
});
|
284 |
+
|
285 |
+
function flashElementGreen(element, callback) {
|
286 |
+
var oldColor = element.css('background-color');
|
287 |
+
element.animate({ backgroundColor: "#E0FFB3" }, 200).animate({ backgroundColor: oldColor }, 300, callback);
|
288 |
+
}
|
289 |
+
|
290 |
+
|
291 |
+
/**
|
292 |
+
* Update status indicators for a link. This includes the contents of the "Status" column, CSS classes and so on.
|
293 |
+
*
|
294 |
+
* @param {Object} row Table row as a jQuery object.
|
295 |
+
* @param {Object} status
|
296 |
+
*/
|
297 |
+
function displayLinkStatus(row, status) {
|
298 |
+
//Update the status code and class.
|
299 |
+
var statusColumn = row.find('td.column-status');
|
300 |
+
if (status.status_text) {
|
301 |
+
statusColumn.find('.status-text').text(status.status_text);
|
302 |
+
}
|
303 |
+
statusColumn.find('.http-code').text(status.http_code ? status.http_code : '');
|
304 |
+
|
305 |
+
var oldStatusClass = row.attr('class').match(/(?:^|\s)(link-status-[^\s]+)(?:\s|$)/);
|
306 |
+
oldStatusClass = oldStatusClass ? oldStatusClass[1] : '';
|
307 |
+
var newStatusClass = 'link-status-' + status.status_code;
|
308 |
+
|
309 |
+
statusColumn.find('.link-status-row').removeClass(oldStatusClass).addClass(newStatusClass);
|
310 |
+
row.removeClass(oldStatusClass).addClass(newStatusClass);
|
311 |
+
|
312 |
+
//Last check time and failure duration are complicated to update, so we'll just hide them.
|
313 |
+
//The user can refresh the page to get the new values.
|
314 |
+
statusColumn.find('.link-last-checked td').html(' ');
|
315 |
+
statusColumn.find('.link-broken-for td').html(' ');
|
316 |
+
|
317 |
+
//The link may or may not be a redirect now.
|
318 |
+
row.toggleClass('blc-redirect', status.redirect_count > 0);
|
319 |
+
|
320 |
+
if (typeof status['redirect_count'] !== 'undefined') {
|
321 |
+
var redirectColumn = row.find('td.column-redirect-url').empty();
|
322 |
+
|
323 |
+
if (status.redirect_count > 0 && status.final_url) {
|
324 |
+
redirectColumn.append(
|
325 |
+
$(
|
326 |
+
'<a></a>',
|
327 |
+
{
|
328 |
+
href: status.final_url,
|
329 |
+
text: status.final_url,
|
330 |
+
title: status.final_url,
|
331 |
+
'class': 'blc-redirect-url',
|
332 |
+
target: '_blank'
|
333 |
+
}
|
334 |
+
)
|
335 |
+
);
|
336 |
+
}
|
337 |
+
}
|
338 |
+
}
|
339 |
+
|
340 |
+
|
341 |
+
/**
|
342 |
+
* Display the inline link editor.
|
343 |
+
*
|
344 |
+
* @param {Number} link_id Link ID. The link must be visible in the current view.
|
345 |
+
*/
|
346 |
+
function showLinkEditor(link_id) {
|
347 |
+
var master = $('#blc-row-' + link_id),
|
348 |
+
editorId = 'blc-edit-row-' + link_id,
|
349 |
+
editRow;
|
350 |
+
|
351 |
+
//Get rid of all existing inline editors.
|
352 |
+
master.closest('table').find('tr.blc-inline-editor').each(function() {
|
353 |
+
hideLinkEditor($(this));
|
354 |
+
});
|
355 |
+
|
356 |
+
//Create an inline editor for this link.
|
357 |
+
editRow = $('#blc-inline-edit-row').clone(true).attr('id', editorId);
|
358 |
+
editRow.toggleClass('alternate', master.hasClass('alternate'));
|
359 |
+
master.after(editRow);
|
360 |
+
|
361 |
+
//Populate editor fields.
|
362 |
+
var urlElement = master.find('a.blc-link-url');
|
363 |
+
var linkUrl = urlElement.data('editable-url') || urlElement.attr('href');
|
364 |
+
var urlInput = editRow.find('.blc-link-url-field').val(linkUrl);
|
365 |
+
|
366 |
+
var titleInput = editRow.find('.blc-link-text-field');
|
367 |
+
var linkText = master.data('link-text'),
|
368 |
+
canEditText = master.data('can-edit-text') == 1, //jQuery will convert a '1' to 1 (number) when reading a data attribute.
|
369 |
+
canEditUrl = master.data('can-edit-url') == 1,
|
370 |
+
noneText = '<?php echo esc_js( _x( '(None)', 'link text', 'broken-link-checker' ) ); ?>',
|
371 |
+
multipleLinksText = '<?php echo esc_js( _x( '(Multiple links)', 'link text', 'broken-link-checker' ) ); ?>';
|
372 |
+
|
373 |
+
titleInput.prop('readonly', !canEditText);
|
374 |
+
urlInput.prop('readonly', !canEditUrl);
|
375 |
+
|
376 |
+
if ( (typeof linkText !== 'undefined') && (linkText !== null) ) {
|
377 |
+
if (linkText === '') {
|
378 |
+
titleInput.val(canEditText ? linkText : noneText);
|
379 |
+
} else {
|
380 |
+
titleInput.val(linkText)
|
381 |
+
}
|
382 |
+
titleInput.prop('placeholder', noneText);
|
383 |
+
} else {
|
384 |
+
if (canEditText) {
|
385 |
+
titleInput.val('').prop('placeholder', multipleLinksText);
|
386 |
+
} else {
|
387 |
+
titleInput.val(multipleLinksText)
|
388 |
+
}
|
389 |
+
}
|
390 |
+
|
391 |
+
//Populate the list of URL replacement suggestions.
|
392 |
+
if (canEditUrl && blc_suggestions_enabled && (master.hasClass('link-status-error') || master.hasClass('link-status-warning'))) {
|
393 |
+
editRow.find('.blc-url-replacement-suggestions').show();
|
394 |
+
var suggestionList = editRow.find('.blc-suggestion-list');
|
395 |
+
findReplacementSuggestions(linkUrl, suggestionList);
|
396 |
+
}
|
397 |
+
|
398 |
+
editRow.find('.blc-update-link-button').prop('disabled', !(canEditUrl || canEditText));
|
399 |
+
|
400 |
+
//Make the editor span the entire width of the table.
|
401 |
+
editRow.find('td.blc-colspan-change').attr('colspan', master.closest('table').find('thead th:visible').length);
|
402 |
+
|
403 |
+
master.hide();
|
404 |
+
editRow.show();
|
405 |
+
urlInput.focus();
|
406 |
+
if (canEditUrl) {
|
407 |
+
urlInput.select();
|
408 |
+
}
|
409 |
+
}
|
410 |
+
|
411 |
+
/**
|
412 |
+
* Hide the inline editor for a particular link.
|
413 |
+
*
|
414 |
+
* @param link_id Either a numeric link ID or a jQuery object that represents the editor row.
|
415 |
+
*/
|
416 |
+
function hideLinkEditor(link_id) {
|
417 |
+
var editRow = isNaN(link_id) ? link_id : $('#blc-edit-row-' + link_id);
|
418 |
+
editRow.prev('tr.blc-row').show();
|
419 |
+
editRow.remove();
|
420 |
+
}
|
421 |
+
|
422 |
+
/**
|
423 |
+
* Find possible replacements for a broken link and display them in a list.
|
424 |
+
*
|
425 |
+
* @param {String} url The current link URL.
|
426 |
+
* @param suggestionList jQuery object that represents a list element.
|
427 |
+
*/
|
428 |
+
function findReplacementSuggestions(url, suggestionList) {
|
429 |
+
var searchingText = '<?php echo esc_js( _x( 'Searching...', 'link suggestions', 'broken-link-checker' ) ); ?>';
|
430 |
+
var noSuggestionsText = '<?php echo esc_js( _x( 'No suggestions available.', 'link suggestions', 'broken-link-checker' ) ); ?>';
|
431 |
+
var iaSuggestionName = '<?php echo esc_js( _x( 'Archived page from %s (via the Wayback Machine)', 'link suggestions', 'broken-link-checker' ) ); ?>';
|
432 |
+
|
433 |
+
suggestionList.empty().append('<li>' + searchingText + '</li>');
|
434 |
+
|
435 |
+
var suggestionTemplate = $('#blc-suggestion-template').find('li').first();
|
436 |
+
|
437 |
+
//Check the Wayback Machine for an archived version of the page.
|
438 |
+
$.getJSON(
|
439 |
+
'https://archive.org/wayback/available?callback=?',
|
440 |
+
{ url: url },
|
441 |
+
|
442 |
+
function(data) {
|
443 |
+
suggestionList.empty();
|
444 |
+
|
445 |
+
//Check if there are any results.
|
446 |
+
if (!data || !data.archived_snapshots || !data.archived_snapshots.closest || !data.archived_snapshots.closest.available ) {
|
447 |
+
suggestionList.append('<li>' + noSuggestionsText + '</li>');
|
448 |
+
return;
|
449 |
+
}
|
450 |
+
|
451 |
+
var snapshot = data.archived_snapshots.closest;
|
452 |
+
|
453 |
+
//Convert the timestamp from YYYYMMDDHHMMSS to ISO 8601 date format.
|
454 |
+
var readableTimestamp = snapshot.timestamp.substr(0, 4) +
|
455 |
+
'-' + snapshot.timestamp.substr(4, 2) +
|
456 |
+
'-' + snapshot.timestamp.substr(6, 2);
|
457 |
+
var name = sprintf(iaSuggestionName, readableTimestamp);
|
458 |
+
|
459 |
+
//Enforce HTTPS by default
|
460 |
+
snapshot.url = (snapshot.url).replace( new RegExp("^http:", "m"), "https:");
|
461 |
+
|
462 |
+
//Display the suggestion.
|
463 |
+
var item = suggestionTemplate.clone();
|
464 |
+
item.find('.blc-suggestion-name a').text(name).attr('href', snapshot.url);
|
465 |
+
item.find('.blc-suggestion-url').text(snapshot.url);
|
466 |
+
suggestionList.append(item);
|
467 |
+
}
|
468 |
+
);
|
469 |
+
}
|
470 |
+
|
471 |
+
/**
|
472 |
+
* Call our PHP backend and tell it to edit all occurrences of particular link.
|
473 |
+
* Updates UI with the new link info and displays any error messages that might be generated.
|
474 |
+
*
|
475 |
+
* @param linkId Either a numeric link ID or a jQuery object representing the link row.
|
476 |
+
* @param {String} newUrl The new link URL.
|
477 |
+
* @param {String} newText The new link text. Optional. Set to null to leave it unchanged.
|
478 |
+
*/
|
479 |
+
function updateLink(linkId, newUrl, newText) {
|
480 |
+
var master, editRow;
|
481 |
+
if ( isNaN(linkId) ){
|
482 |
+
master = linkId;
|
483 |
+
linkId = master.attr('id').split("-")[2]; //id="blc-row-$linkid"
|
484 |
+
} else {
|
485 |
+
master = $('#blc-row-' + linkId);
|
486 |
+
}
|
487 |
+
editRow = $('#blc-edit-row-' + linkId);
|
488 |
+
|
489 |
+
var urlElement = master.find('a.blc-link-url');
|
490 |
+
var progressIndicator = editRow.find('.waiting'),
|
491 |
+
updateButton = editRow.find('.blc-update-link-button');
|
492 |
+
progressIndicator.show();
|
493 |
+
updateButton.prop('disabled', true);
|
494 |
+
|
495 |
+
$.post(
|
496 |
+
'<?php echo admin_url( 'admin-ajax.php' ); ?>',
|
497 |
+
{
|
498 |
+
'action' : 'blc_edit',
|
499 |
+
'link_id' : linkId,
|
500 |
+
'new_url' : newUrl,
|
501 |
+
'new_text' : newText,
|
502 |
+
'_ajax_nonce' : '<?php echo esc_js( wp_create_nonce( 'blc_edit' ) ); ?>'
|
503 |
+
},
|
504 |
+
function(response) {
|
505 |
+
progressIndicator.hide();
|
506 |
+
updateButton.prop('disabled', false);
|
507 |
+
|
508 |
+
handleEditResponse(response, master, linkId, newText);
|
509 |
+
|
510 |
+
hideLinkEditor(editRow);
|
511 |
+
},
|
512 |
+
'json'
|
513 |
+
);
|
514 |
+
|
515 |
+
}
|
516 |
+
|
517 |
+
function handleEditResponse(response, master, linkId, newText) {
|
518 |
+
if (response && (typeof(response['error']) != 'undefined')){
|
519 |
+
//An internal error occurred before the link could be edited.
|
520 |
+
alert(response.error);
|
521 |
+
return false;
|
522 |
+
} else if (response.errors.length > 0) {
|
523 |
+
//Build and display an error message.
|
524 |
+
var msg = '';
|
525 |
+
|
526 |
+
if ( response.cnt_okay > 0 ){
|
527 |
+
var fragment = sprintf(
|
528 |
+
'<?php echo esc_js( __( '%d instances of the link were successfully modified.', 'broken-link-checker' ) ); ?>',
|
529 |
+
response.cnt_okay
|
530 |
+
);
|
531 |
+
msg = msg + fragment + '\n';
|
532 |
+
if ( response.cnt_error > 0 ){
|
533 |
+
fragment = sprintf(
|
534 |
+
'<?php echo esc_js( __( "However, %d instances couldn't be edited and still point to the old URL.", 'broken-link-checker' ) ); ?>',
|
535 |
+
response.cnt_error
|
536 |
+
);
|
537 |
+
msg = msg + fragment + "\n";
|
538 |
+
}
|
539 |
+
} else {
|
540 |
+
msg = msg + '<?php echo esc_js( __( 'The link could not be modified.', 'broken-link-checker' ) ); ?>\n';
|
541 |
+
}
|
542 |
+
|
543 |
+
msg = msg + '\n<?php echo esc_js( __( 'The following error(s) occurred :', 'broken-link-checker' ) ); ?>\n* ';
|
544 |
+
msg = msg + response.errors.join('\n* ');
|
545 |
+
|
546 |
+
alert(msg);
|
547 |
+
return false;
|
548 |
+
} else {
|
549 |
+
//Everything went well. Update the link row with the new values.
|
550 |
+
|
551 |
+
//Replace the displayed link URL with the new one.
|
552 |
+
var urlElement = master.find('a.blc-link-url');
|
553 |
+
urlElement
|
554 |
+
.attr('href', response.url)
|
555 |
+
.text(response.url)
|
556 |
+
.data('editable-url', response.url)
|
557 |
+
.prop('title', response.url);
|
558 |
+
if ( typeof response['escaped_url'] != 'undefined' ) {
|
559 |
+
urlElement.attr('href', response.escaped_url)
|
560 |
+
}
|
561 |
+
|
562 |
+
//Save the new ID
|
563 |
+
replaceLinkId(linkId, response.new_link_id);
|
564 |
+
//Load up the new link info
|
565 |
+
reloadDetailsRow(response.new_link_id);
|
566 |
+
|
567 |
+
//Update the link text if it was edited.
|
568 |
+
if ((newText !== null) && (response.link_text !== null)) {
|
569 |
+
master.data('link-text', response.link_text);
|
570 |
+
if (response.ui_link_text !== null) {
|
571 |
+
master.find('.column-new-link-text').html(response.ui_link_text);
|
572 |
+
}
|
573 |
+
}
|
574 |
+
|
575 |
+
//Update the status code and class.
|
576 |
+
displayLinkStatus(master, response);
|
577 |
+
|
578 |
+
//Flash the row green to indicate success
|
579 |
+
flashElementGreen(master);
|
580 |
+
|
581 |
+
return true;
|
582 |
+
}
|
583 |
+
}
|
584 |
+
|
585 |
+
//The "Edit URL" button - displays the inline editor
|
586 |
+
$(".blc-edit-button").click(function () {
|
587 |
+
var master = $(this).closest('.blc-row');
|
588 |
+
var link_id = master.attr('id').split('-')[2];
|
589 |
+
showLinkEditor(link_id);
|
590 |
+
});
|
591 |
+
|
592 |
+
//Let the user use Enter and Esc as shortcuts for "Update" and "Cancel"
|
593 |
+
$('.blc-inline-editor input[type="text"]').keypress(function (e) {
|
594 |
+
var editRow = $(this).closest('.blc-inline-editor');
|
595 |
+
if (e.which == 13) {
|
596 |
+
editRow.find('.blc-update-link-button').click();
|
597 |
+
return false;
|
598 |
+
} else if (e.which == 27) {
|
599 |
+
editRow.find('.blc-cancel-button').click();
|
600 |
+
return false;
|
601 |
+
}
|
602 |
+
return true;
|
603 |
+
});
|
604 |
+
|
605 |
+
|
606 |
+
//The "Update" button in the inline editor.
|
607 |
+
$('.blc-update-link-button').click(function() {
|
608 |
+
var editRow = $(this).closest('tr'),
|
609 |
+
master = editRow.prev('.blc-row');
|
610 |
+
|
611 |
+
//Ensure the new URL is not empty.
|
612 |
+
var urlField = editRow.find('.blc-link-url-field');
|
613 |
+
var newUrl = urlField.val();
|
614 |
+
if ($.trim(newUrl) == '') {
|
615 |
+
alert('<?php echo esc_js( __( 'Error: Link URL must not be empty.', 'broken-link-checker' ) ); ?>');
|
616 |
+
urlField.focus();
|
617 |
+
return;
|
618 |
+
}
|
619 |
+
|
620 |
+
var newLinkText = null,
|
621 |
+
linkTextField = editRow.find('.blc-link-text-field'),
|
622 |
+
oldLinkText = master.data('link-text');
|
623 |
+
if (!linkTextField.prop('readonly')) {
|
624 |
+
newLinkText = linkTextField.val();
|
625 |
+
//Empty or identical to the original = leave the text unchanged.
|
626 |
+
if ((newLinkText === '') || (newLinkText === oldLinkText)) {
|
627 |
+
newLinkText = null;
|
628 |
+
}
|
629 |
+
}
|
630 |
+
|
631 |
+
updateLink(master, newUrl, newLinkText);
|
632 |
+
});
|
633 |
+
|
634 |
+
//The "Cancel" in the inline editor.
|
635 |
+
$(".blc-cancel-button").click(function () {
|
636 |
+
var editRow = $(this).closest('tr');
|
637 |
+
hideLinkEditor(editRow);
|
638 |
+
});
|
639 |
+
|
640 |
+
//The "Use this URL" button in the inline editor replaces the link URL
|
641 |
+
//with the selected suggestion URL.
|
642 |
+
$('#blc-links').on('click', '.blc-use-url-button', function() {
|
643 |
+
var button = $(this);
|
644 |
+
var suggestionUrl = button.closest('tr').find('.blc-suggestion-name a').attr('href');
|
645 |
+
button.closest('.blc-inline-editor').find('.blc-link-url-field').val(suggestionUrl);
|
646 |
+
});
|
647 |
+
|
648 |
+
|
649 |
+
//The "Unlink" button - remove the link/image from all posts, custom fields, etc.
|
650 |
+
$(".blc-unlink-button").click(function () {
|
651 |
+
var me = this;
|
652 |
+
var master = $(me).parents('.blc-row');
|
653 |
+
$(me).html('<?php echo esc_js( __( 'Wait...', 'broken-link-checker' ) ); ?>');
|
654 |
+
|
655 |
+
//Find the link ID
|
656 |
+
var link_id = master.attr('id').split('-')[2];
|
657 |
+
|
658 |
+
$.post(
|
659 |
+
"<?php echo admin_url( 'admin-ajax.php' ); ?>",
|
660 |
+
{
|
661 |
+
'action' : 'blc_unlink',
|
662 |
+
'link_id' : link_id,
|
663 |
+
'_ajax_nonce' : '<?php echo esc_js( wp_create_nonce( 'blc_unlink' ) ); ?>'
|
664 |
+
},
|
665 |
+
function (data, textStatus){
|
666 |
+
eval('data = ' + data);
|
667 |
+
|
668 |
+
if ( data && (typeof(data['error']) != 'undefined') ){
|
669 |
+
//An internal error occurred before the link could be edited.
|
670 |
+
//data.error is an error message.
|
671 |
+
alert(data.error);
|
672 |
+
} else {
|
673 |
+
if ( data.errors.length == 0 ){
|
674 |
+
//The link was successfully removed. Hide its details.
|
675 |
+
$('#link-details-'+link_id).hide();
|
676 |
+
//Flash the main row green to indicate success, then hide it.
|
677 |
+
var oldColor = master.css('background-color');
|
678 |
+
master.animate({ backgroundColor: "#E0FFB3" }, 200).animate({ backgroundColor: oldColor }, 300, function(){
|
679 |
+
master.hide();
|
680 |
+
});
|
681 |
+
|
682 |
+
alterLinkCounter(-1);
|
683 |
+
|
684 |
+
return;
|
685 |
+
} else {
|
686 |
+
//Build and display an error message.
|
687 |
+
var msg = '';
|
688 |
+
|
689 |
+
if ( data.cnt_okay > 0 ){
|
690 |
+
msg = msg + sprintf(
|
691 |
+
'<?php echo esc_js( __( '%d instances of the link were successfully unlinked.', 'broken-link-checker' ) ); ?>\n',
|
692 |
+
data.cnt_okay
|
693 |
+
);
|
694 |
+
|
695 |
+
if ( data.cnt_error > 0 ){
|
696 |
+
msg = msg + sprintf(
|
697 |
+
'<?php echo esc_js( __( "However, %d instances couldn't be removed.", 'broken-link-checker' ) ); ?>\n',
|
698 |
+
data.cnt_error
|
699 |
+
);
|
700 |
+
}
|
701 |
+
} else {
|
702 |
+
msg = msg + '<?php echo esc_js( __( 'The plugin failed to remove the link.', 'broken-link-checker' ) ); ?>\n';
|
703 |
+
}
|
704 |
+
|
705 |
+
msg = msg + '\n<?php echo esc_js( __( 'The following error(s) occured :', 'broken-link-checker' ) ); ?>\n* ';
|
706 |
+
msg = msg + data.errors.join('\n* ');
|
707 |
+
|
708 |
+
//Show the error message
|
709 |
+
alert(msg);
|
710 |
+
}
|
711 |
+
}
|
712 |
+
|
713 |
+
$(me).html('<?php echo esc_js( __( 'Unlink', 'broken-link-checker' ) ); ?>');
|
714 |
+
}
|
715 |
+
);
|
716 |
+
});
|
717 |
+
|
718 |
+
//--------------------------------------------
|
719 |
+
//The search box(es)
|
720 |
+
//--------------------------------------------
|
721 |
+
|
722 |
+
var searchForm = $('#search-links-dialog');
|
723 |
+
|
724 |
+
searchForm.dialog({
|
725 |
+
autoOpen : false,
|
726 |
+
dialogClass : 'blc-search-container',
|
727 |
+
resizable: false
|
728 |
+
});
|
729 |
+
|
730 |
+
$('#blc-open-search-box').click(function(){
|
731 |
+
if ( searchForm.dialog('isOpen') ){
|
732 |
+
searchForm.dialog('close');
|
733 |
+
} else {
|
734 |
+
searchForm
|
735 |
+
.dialog('open')
|
736 |
+
.dialog('widget')
|
737 |
+
.position({
|
738 |
+
my: 'right top',
|
739 |
+
at: 'right bottom',
|
740 |
+
of: $('#blc-open-search-box')
|
741 |
+
});
|
742 |
+
}
|
743 |
+
});
|
744 |
+
|
745 |
+
$('#blc-cancel-search').click(function(){
|
746 |
+
searchForm.dialog('close');
|
747 |
+
});
|
748 |
+
|
749 |
+
//The "Save This Search Query" button creates a new custom filter based on the current search
|
750 |
+
$('#blc-create-filter').click(function(){
|
751 |
+
var filter_name = prompt("<?php echo esc_js( __( 'Enter a name for the new custom filter', 'broken-link-checker' ) ); ?>", "");
|
752 |
+
if ( filter_name ){
|
753 |
+
$('#blc-custom-filter-name').val(filter_name);
|
754 |
+
$('#custom-filter-form').submit();
|
755 |
+
}
|
756 |
+
});
|
757 |
+
|
758 |
+
//Display a confirmation dialog when the user clicks the "Delete This Filter" button
|
759 |
+
$('#blc-delete-filter').click(function(){
|
760 |
+
var message = '<?php
|
761 |
+
echo esc_js(
|
762 |
+
html_entity_decode(
|
763 |
+
__( "You are about to delete the current filter.\n'Cancel' to stop, 'OK' to delete", 'broken-link-checker' ),
|
764 |
+
ENT_QUOTES,
|
765 |
+
get_bloginfo( 'charset' )
|
766 |
+
)
|
767 |
+
);
|
768 |
+
?>';
|
769 |
+
return confirm(message);
|
770 |
+
});
|
771 |
+
|
772 |
+
//--------------------------------------------
|
773 |
+
// Bulk actions
|
774 |
+
//--------------------------------------------
|
775 |
+
|
776 |
+
$('#blc-bulk-action-form').submit(function(){
|
777 |
+
var action = $('#blc-bulk-action').val(), message;
|
778 |
+
if ( action == '-1' ){
|
779 |
+
action = $('#blc-bulk-action2').val();
|
780 |
+
}
|
781 |
+
|
782 |
+
if ( action == 'bulk-delete-sources' ){
|
783 |
+
//Convey the gravitas of deleting link sources.
|
784 |
+
message = '<?php
|
785 |
+
echo esc_js(
|
786 |
+
html_entity_decode(
|
787 |
+
__( "Are you sure you want to delete all posts, bookmarks or other items that contain any of the selected links? This action can't be undone.\n'Cancel' to stop, 'OK' to delete", 'broken-link-checker' ),
|
788 |
+
ENT_QUOTES,
|
789 |
+
get_bloginfo( 'charset' )
|
790 |
+
)
|
791 |
+
);
|
792 |
+
?>';
|
793 |
+
if ( !confirm(message) ){
|
794 |
+
return false;
|
795 |
+
}
|
796 |
+
} else if ( action == 'bulk-unlink' ){
|
797 |
+
//Likewise for unlinking.
|
798 |
+
message = '<?php
|
799 |
+
echo esc_js(
|
800 |
+
html_entity_decode(
|
801 |
+
__( "Are you sure you want to remove the selected links? This action can't be undone.\n'Cancel' to stop, 'OK' to remove", 'broken-link-checker' ),
|
802 |
+
ENT_QUOTES,
|
803 |
+
get_bloginfo( 'charset' )
|
804 |
+
)
|
805 |
+
);
|
806 |
+
?>';
|
807 |
+
if ( !confirm(message) ){
|
808 |
+
return false;
|
809 |
+
}
|
810 |
+
}
|
811 |
+
});
|
812 |
+
|
813 |
+
//Automatically disable bulk actions that don't apply to the currently selected links.
|
814 |
+
$('#blc-bulk-action').focus(function() {
|
815 |
+
var redirectsSelected = false, brokenLinksSelected = false;
|
816 |
+
$('tr th.check-column input:checked', '#blc-links').each(function() {
|
817 |
+
var row = $(this).closest('tr');
|
818 |
+
if (row.hasClass('blc-redirect')) {
|
819 |
+
redirectsSelected = true
|
820 |
+
}
|
821 |
+
if (row.hasClass('link-status-error') || row.hasClass('link-status-warning')) {
|
822 |
+
brokenLinksSelected = true;
|
823 |
+
}
|
824 |
+
});
|
825 |
+
|
826 |
+
var bulkAction = $(this);
|
827 |
+
bulkAction.find('option[value="bulk-deredirect"]').prop('disabled', !redirectsSelected);
|
828 |
+
bulkAction.find('option[value="bulk-not-broken"]').prop('disabled', !brokenLinksSelected);
|
829 |
+
});
|
830 |
+
|
831 |
+
//------------------------------------------------------------
|
832 |
+
// Manipulate highlight settings for permanently broken links
|
833 |
+
//------------------------------------------------------------
|
834 |
+
var highlight_permanent_failures_checkbox = $('#highlight_permanent_failures');
|
835 |
+
var failure_duration_threshold_input = $('#failure_duration_threshold');
|
836 |
+
|
837 |
+
//Apply/remove highlights when the checkbox is (un)checked
|
838 |
+
highlight_permanent_failures_checkbox.change(function(){
|
839 |
+
//save_highlight_settings();
|
840 |
+
|
841 |
+
if ( this.checked ){
|
842 |
+
$('#blc-links tr.blc-permanently-broken').addClass('blc-permanently-broken-hl');
|
843 |
+
} else {
|
844 |
+
$('#blc-links tr.blc-permanently-broken').removeClass('blc-permanently-broken-hl');
|
845 |
+
}
|
846 |
+
});
|
847 |
+
|
848 |
+
//Apply/remove highlights when the duration threshold is changed.
|
849 |
+
failure_duration_threshold_input.change(function(){
|
850 |
+
var new_threshold = parseInt($(this).val());
|
851 |
+
//save_highlight_settings();
|
852 |
+
if (isNaN(new_threshold) || (new_threshold < 1)) {
|
853 |
+
return;
|
854 |
+
}
|
855 |
+
|
856 |
+
highlight_permanent_failures = highlight_permanent_failures_checkbox.is(':checked');
|
857 |
+
|
858 |
+
$('#blc-links tr.blc-row').each(function(index){
|
859 |
+
var days_broken = $(this).attr('data-days-broken');
|
860 |
+
if ( days_broken >= new_threshold ){
|
861 |
+
$(this).addClass('blc-permanently-broken');
|
862 |
+
if ( highlight_permanent_failures ){
|
863 |
+
$(this).addClass('blc-permanently-broken-hl');
|
864 |
+
}
|
865 |
+
} else {
|
866 |
+
$(this).removeClass('blc-permanently-broken').removeClass('blc-permanently-broken-hl');
|
867 |
+
}
|
868 |
+
});
|
869 |
+
});
|
870 |
+
|
871 |
+
//Show/hide table columns dynamically
|
872 |
+
$('#blc-column-selector input[type="checkbox"]').change(function(){
|
873 |
+
var checkbox = $(this);
|
874 |
+
var column_id = checkbox.attr('name').split(/\[|\]/)[1];
|
875 |
+
if (checkbox.is(':checked')){
|
876 |
+
$('td.column-'+column_id+', th.column-'+column_id, '#blc-links').removeClass('hidden');
|
877 |
+
} else {
|
878 |
+
$('td.column-'+column_id+', th.column-'+column_id, '#blc-links').addClass('hidden');
|
879 |
+
}
|
880 |
+
|
881 |
+
//Recalculate colspan's for detail rows to take into account the changed number of
|
882 |
+
//visible columns. Otherwise you can get some ugly layout glitches.
|
883 |
+
$('#blc-links tr.blc-link-details td').attr(
|
884 |
+
'colspan',
|
885 |
+
$('#blc-column-selector input[type="checkbox"]:checked').length+1
|
886 |
+
);
|
887 |
+
});
|
888 |
+
|
889 |
+
//Unlike other fields in "Screen Options", the links-per-page setting
|
890 |
+
//is handled using straight form submission (POST), not AJAX.
|
891 |
+
$('#blc-per-page-apply-button').click(function(){
|
892 |
+
$('#adv-settings').submit();
|
893 |
+
});
|
894 |
+
|
895 |
+
$('#blc_links_per_page').keypress(function(e){
|
896 |
+
if ((e.which && e.which == 13) || (e.keyCode && e.keyCode == 13)) {
|
897 |
+
$('#adv-settings').submit();
|
898 |
+
}
|
899 |
+
});
|
900 |
+
|
901 |
+
//Toggle status code colors when the corresponding checkbox is toggled
|
902 |
+
$('#table_color_code_status').click(function(){
|
903 |
+
if ( $(this).is(':checked') ){
|
904 |
+
$('#blc-links').addClass('color-code-link-status');
|
905 |
+
} else {
|
906 |
+
$('#blc-links').removeClass('color-code-link-status');
|
907 |
+
}
|
908 |
+
});
|
909 |
+
|
910 |
+
//Show the bulk edit/find & replace form when the user applies the appropriate bulk action
|
911 |
+
$('#doaction, #doaction2').click(function(e){
|
912 |
+
var n = $(this).attr('id').substr(2);
|
913 |
+
if ( $('select[name="'+n+'"]').val() == 'bulk-edit' ) {
|
914 |
+
e.preventDefault();
|
915 |
+
//Any links selected?
|
916 |
+
if ($('tbody th.check-column input:checked').length > 0){
|
917 |
+
$('#bulk-edit').show();
|
918 |
+
}
|
919 |
+
}
|
920 |
+
});
|
921 |
+
|
922 |
+
//Hide the bulk edit/find & replace form when "Cancel" is clicked
|
923 |
+
$('#bulk-edit .cancel').click(function(){
|
924 |
+
$('#bulk-edit').hide();
|
925 |
+
return false;
|
926 |
+
});
|
927 |
+
|
928 |
+
//Minimal input validation for the bulk edit form
|
929 |
+
$('#bulk-edit input[type="submit"]').click(function(e){
|
930 |
+
if( $('#bulk-edit input[name="search"]').val() == '' ){
|
931 |
+
alert('<?php echo esc_js( __( 'Enter a search string first.', 'broken-link-checker' ) ); ?>');
|
932 |
+
$('#bulk-edit input[name="search"]').focus();
|
933 |
+
e.preventDefault();
|
934 |
+
return;
|
935 |
+
}
|
936 |
+
|
937 |
+
if ($('tbody th.check-column input:checked').length == 0){
|
938 |
+
alert('<?php echo esc_js( __( 'Select one or more links to edit.', 'broken-link-checker' ) ); ?>');
|
939 |
+
e.preventDefault();
|
940 |
+
}
|
941 |
+
});
|
942 |
+
|
943 |
+
|
944 |
+
});
|
945 |
+
|
946 |
+
</script>
|
includes/admin/options-page-js.php
CHANGED
@@ -1,147 +1,147 @@
|
|
1 |
-
<script type="text/javascript">
|
2 |
-
|
3 |
-
jQuery(function($){
|
4 |
-
$('#blc-tabs').tabs();
|
5 |
-
|
6 |
-
//Refresh the "Status" box every 10 seconds
|
7 |
-
function blcUpdateStatus(){
|
8 |
-
$.getJSON(
|
9 |
-
"<?php echo admin_url('admin-ajax.php'); ?>",
|
10 |
-
{
|
11 |
-
'action' : 'blc_full_status',
|
12 |
-
'random' : Math.random()
|
13 |
-
},
|
14 |
-
function (data, textStatus){
|
15 |
-
if ( data && ( typeof(data['text']) != 'undefined' ) ){
|
16 |
-
$('#wsblc_full_status').html(data.text);
|
17 |
-
} else {
|
18 |
-
$('#wsblc_full_status').html('<?php _e('[ Network error ]', 'broken-link-checker'); ?>');
|
19 |
-
}
|
20 |
-
|
21 |
-
setTimeout(blcUpdateStatus, 10000);
|
22 |
-
}
|
23 |
-
);
|
24 |
-
}
|
25 |
-
blcUpdateStatus();
|
26 |
-
|
27 |
-
//Refresh the avg. load display every 10 seconds
|
28 |
-
function blcUpdateLoad(){
|
29 |
-
$.get(
|
30 |
-
"<?php echo admin_url('admin-ajax.php'); ?>",
|
31 |
-
{
|
32 |
-
'action' : 'blc_current_load'
|
33 |
-
},
|
34 |
-
function (data, textStatus){
|
35 |
-
$('#wsblc_current_load').html(data);
|
36 |
-
|
37 |
-
setTimeout(blcUpdateLoad, 10000); //...update every 10 seconds
|
38 |
-
}
|
39 |
-
);
|
40 |
-
}
|
41 |
-
//Only do it if load limiting is available on this server, though.
|
42 |
-
if ( $('#wsblc_current_load').length > 0 ){
|
43 |
-
blcUpdateLoad();
|
44 |
-
}
|
45 |
-
|
46 |
-
|
47 |
-
var toggleButton = $('#blc-debug-info-toggle');
|
48 |
-
|
49 |
-
toggleButton.click(function(){
|
50 |
-
|
51 |
-
var box = $('#blc-debug-info');
|
52 |
-
box.toggle();
|
53 |
-
if( box.is(':visible') ){
|
54 |
-
toggleButton.text('<?php _e('Hide debug info', 'broken-link-checker'); ?>');
|
55 |
-
} else {
|
56 |
-
toggleButton.text('<?php _e('Show debug info', 'broken-link-checker'); ?>');
|
57 |
-
}
|
58 |
-
|
59 |
-
});
|
60 |
-
|
61 |
-
$('#toggle-broken-link-css-editor').click(function(){
|
62 |
-
var box = $('#broken-link-css-wrap').toggleClass('hidden');
|
63 |
-
|
64 |
-
$.cookie(
|
65 |
-
box.attr('id'),
|
66 |
-
box.hasClass('hidden')?'0':'1',
|
67 |
-
{
|
68 |
-
expires : 365
|
69 |
-
}
|
70 |
-
);
|
71 |
-
|
72 |
-
return false;
|
73 |
-
});
|
74 |
-
|
75 |
-
$('#toggle-removed-link-css-editor').click(function(){
|
76 |
-
var box = $('#removed-link-css-wrap').toggleClass('hidden');
|
77 |
-
|
78 |
-
$.cookie(
|
79 |
-
box.attr('id'),
|
80 |
-
box.hasClass('hidden')?'0':'1',
|
81 |
-
{
|
82 |
-
expires : 365
|
83 |
-
}
|
84 |
-
);
|
85 |
-
|
86 |
-
return false;
|
87 |
-
});
|
88 |
-
|
89 |
-
//Show/hide per-module settings
|
90 |
-
$('.toggle-module-settings').click(function(){
|
91 |
-
var settingsBox = $(this).parent().find('.module-extra-settings');
|
92 |
-
if ( settingsBox.length > 0 ){
|
93 |
-
settingsBox.toggleClass('hidden');
|
94 |
-
$.cookie(
|
95 |
-
settingsBox.attr('id'),
|
96 |
-
settingsBox.hasClass('hidden')?'0':'1',
|
97 |
-
{
|
98 |
-
expires : 365
|
99 |
-
}
|
100 |
-
);
|
101 |
-
}
|
102 |
-
return false;
|
103 |
-
});
|
104 |
-
|
105 |
-
//When the user ticks the "Custom fields" box, display the field list input
|
106 |
-
//so that they notice that they need to enter the field names.
|
107 |
-
$('#module-checkbox-custom_field').click(function(){
|
108 |
-
var box = $(this);
|
109 |
-
var fieldList = $('#blc_custom_fields');
|
110 |
-
if ( box.is(':checked') && ( $.trim(fieldList.val()) == '' ) ){
|
111 |
-
$('#module-extra-settings-custom_field').removeClass('hidden');
|
112 |
-
}
|
113 |
-
});
|
114 |
-
|
115 |
-
//When the user ticks the "Custom fields" box, display the field list input
|
116 |
-
//so that they notice that they need to enter the field names.
|
117 |
-
$('#module-checkbox-acf_field').click(function(){
|
118 |
-
var box = $(this);
|
119 |
-
var fieldList = $('#blc_acf_fields');
|
120 |
-
if ( box.is(':checked') && ( $.trim(fieldList.val()) == '' ) ){
|
121 |
-
$('#module-extra-settings-acf_field').removeClass('hidden');
|
122 |
-
}
|
123 |
-
});
|
124 |
-
|
125 |
-
//Handle the "Recheck" button
|
126 |
-
$('#start-recheck').click(function(){
|
127 |
-
$('#recheck').val('1'); //populate the hidden field
|
128 |
-
$('#link_checker_options input[name="submit"]').click(); //.submit() didn't work for some reason
|
129 |
-
});
|
130 |
-
|
131 |
-
//Enable/disable log-related options depending on whether "Enable logging" is on.
|
132 |
-
function blcToggleLogOptions() {
|
133 |
-
$('#blc-logging-options')
|
134 |
-
.find('input')
|
135 |
-
.prop('disabled', ! $('#logging_enabled').is(':checked'));
|
136 |
-
}
|
137 |
-
|
138 |
-
blcToggleLogOptions();
|
139 |
-
$('#logging_enabled').change(blcToggleLogOptions);
|
140 |
-
|
141 |
-
//
|
142 |
-
$('#target_resource_usage').change(function() {
|
143 |
-
$('#target_resource_usage_percent').text($(this).val() + '%')
|
144 |
-
});
|
145 |
-
});
|
146 |
-
|
147 |
-
</script>
|
1 |
+
<script type="text/javascript">
|
2 |
+
|
3 |
+
jQuery(function($){
|
4 |
+
$('#blc-tabs').tabs();
|
5 |
+
|
6 |
+
//Refresh the "Status" box every 10 seconds
|
7 |
+
function blcUpdateStatus(){
|
8 |
+
$.getJSON(
|
9 |
+
"<?php echo admin_url( 'admin-ajax.php' ); ?>",
|
10 |
+
{
|
11 |
+
'action' : 'blc_full_status',
|
12 |
+
'random' : Math.random()
|
13 |
+
},
|
14 |
+
function (data, textStatus){
|
15 |
+
if ( data && ( typeof(data['text']) != 'undefined' ) ){
|
16 |
+
$('#wsblc_full_status').html(data.text);
|
17 |
+
} else {
|
18 |
+
$('#wsblc_full_status').html('<?php _e( '[ Network error ]', 'broken-link-checker' ); ?>');
|
19 |
+
}
|
20 |
+
|
21 |
+
setTimeout(blcUpdateStatus, 10000);
|
22 |
+
}
|
23 |
+
);
|
24 |
+
}
|
25 |
+
blcUpdateStatus();
|
26 |
+
|
27 |
+
//Refresh the avg. load display every 10 seconds
|
28 |
+
function blcUpdateLoad(){
|
29 |
+
$.get(
|
30 |
+
"<?php echo admin_url( 'admin-ajax.php' ); ?>",
|
31 |
+
{
|
32 |
+
'action' : 'blc_current_load'
|
33 |
+
},
|
34 |
+
function (data, textStatus){
|
35 |
+
$('#wsblc_current_load').html(data);
|
36 |
+
|
37 |
+
setTimeout(blcUpdateLoad, 10000); //...update every 10 seconds
|
38 |
+
}
|
39 |
+
);
|
40 |
+
}
|
41 |
+
//Only do it if load limiting is available on this server, though.
|
42 |
+
if ( $('#wsblc_current_load').length > 0 ){
|
43 |
+
blcUpdateLoad();
|
44 |
+
}
|
45 |
+
|
46 |
+
|
47 |
+
var toggleButton = $('#blc-debug-info-toggle');
|
48 |
+
|
49 |
+
toggleButton.click(function(){
|
50 |
+
|
51 |
+
var box = $('#blc-debug-info');
|
52 |
+
box.toggle();
|
53 |
+
if( box.is(':visible') ){
|
54 |
+
toggleButton.text('<?php _e( 'Hide debug info', 'broken-link-checker' ); ?>');
|
55 |
+
} else {
|
56 |
+
toggleButton.text('<?php _e( 'Show debug info', 'broken-link-checker' ); ?>');
|
57 |
+
}
|
58 |
+
|
59 |
+
});
|
60 |
+
|
61 |
+
$('#toggle-broken-link-css-editor').click(function(){
|
62 |
+
var box = $('#broken-link-css-wrap').toggleClass('hidden');
|
63 |
+
|
64 |
+
$.cookie(
|
65 |
+
box.attr('id'),
|
66 |
+
box.hasClass('hidden')?'0':'1',
|
67 |
+
{
|
68 |
+
expires : 365
|
69 |
+
}
|
70 |
+
);
|
71 |
+
|
72 |
+
return false;
|
73 |
+
});
|
74 |
+
|
75 |
+
$('#toggle-removed-link-css-editor').click(function(){
|
76 |
+
var box = $('#removed-link-css-wrap').toggleClass('hidden');
|
77 |
+
|
78 |
+
$.cookie(
|
79 |
+
box.attr('id'),
|
80 |
+
box.hasClass('hidden')?'0':'1',
|
81 |
+
{
|
82 |
+
expires : 365
|
83 |
+
}
|
84 |
+
);
|
85 |
+
|
86 |
+
return false;
|
87 |
+
});
|
88 |
+
|
89 |
+
//Show/hide per-module settings
|
90 |
+
$('.toggle-module-settings').click(function(){
|
91 |
+
var settingsBox = $(this).parent().find('.module-extra-settings');
|
92 |
+
if ( settingsBox.length > 0 ){
|
93 |
+
settingsBox.toggleClass('hidden');
|
94 |
+
$.cookie(
|
95 |
+
settingsBox.attr('id'),
|
96 |
+
settingsBox.hasClass('hidden')?'0':'1',
|
97 |
+
{
|
98 |
+
expires : 365
|
99 |
+
}
|
100 |
+
);
|
101 |
+
}
|
102 |
+
return false;
|
103 |
+
});
|
104 |
+
|
105 |
+
//When the user ticks the "Custom fields" box, display the field list input
|
106 |
+
//so that they notice that they need to enter the field names.
|
107 |
+
$('#module-checkbox-custom_field').click(function(){
|
108 |
+
var box = $(this);
|
109 |
+
var fieldList = $('#blc_custom_fields');
|
110 |
+
if ( box.is(':checked') && ( $.trim(fieldList.val()) == '' ) ){
|
111 |
+
$('#module-extra-settings-custom_field').removeClass('hidden');
|
112 |
+
}
|
113 |
+
});
|
114 |
+
|
115 |
+
//When the user ticks the "Custom fields" box, display the field list input
|
116 |
+
//so that they notice that they need to enter the field names.
|
117 |
+
$('#module-checkbox-acf_field').click(function(){
|
118 |
+
var box = $(this);
|
119 |
+
var fieldList = $('#blc_acf_fields');
|
120 |
+
if ( box.is(':checked') && ( $.trim(fieldList.val()) == '' ) ){
|
121 |
+
$('#module-extra-settings-acf_field').removeClass('hidden');
|
122 |
+
}
|
123 |
+
});
|
124 |
+
|
125 |
+
//Handle the "Recheck" button
|
126 |
+
$('#start-recheck').click(function(){
|
127 |
+
$('#recheck').val('1'); //populate the hidden field
|
128 |
+
$('#link_checker_options input[name="submit"]').click(); //.submit() didn't work for some reason
|
129 |
+
});
|
130 |
+
|
131 |
+
//Enable/disable log-related options depending on whether "Enable logging" is on.
|
132 |
+
function blcToggleLogOptions() {
|
133 |
+
$('#blc-logging-options')
|
134 |
+
.find('input')
|
135 |
+
.prop('disabled', ! $('#logging_enabled').is(':checked'));
|
136 |
+
}
|
137 |
+
|
138 |
+
blcToggleLogOptions();
|
139 |
+
$('#logging_enabled').change(blcToggleLogOptions);
|
140 |
+
|
141 |
+
//
|
142 |
+
$('#target_resource_usage').change(function() {
|
143 |
+
$('#target_resource_usage_percent').text($(this).val() + '%')
|
144 |
+
});
|
145 |
+
});
|
146 |
+
|
147 |
+
</script>
|
includes/admin/search-form.php
CHANGED
@@ -1,117 +1,117 @@
|
|
1 |
-
<?php
|
2 |
-
$search_params = $current_filter['search_params'];
|
3 |
-
?>
|
4 |
-
<div class="search-box">
|
5 |
-
|
6 |
-
<?php
|
7 |
-
//If we're currently displaying search results offer the user the option to
|
8 |
-
//save the search query as a custom filter.
|
9 |
-
|
10 |
-
|
11 |
-
<form name="save-search-query" id="custom-filter-form" action="<?php echo admin_url(
|
12 |
-
<?php wp_nonce_field('create-custom-filter');
|
13 |
-
<input type="hidden" name="name" id="blc-custom-filter-name" value="" />
|
14 |
-
<input type="hidden" name="params" id="blc-custom-filter-params" value="<?php echo http_build_query($search_params, null, '&'); ?>" />
|
15 |
-
<input type="hidden" name="action" value="create-custom-filter" />
|
16 |
-
<input type="button" value="<?php esc_attr_e( 'Save This Search As a Filter', 'broken-link-checker' ); ?>" id="blc-create-filter" class="button" />
|
17 |
-
</form>
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
<form name="save-search-query" id="custom-filter-form" action="<?php echo admin_url(
|
23 |
-
<?php wp_nonce_field('delete-custom-filter');
|
24 |
-
<input type="hidden" name="filter_id" id="blc-custom-filter-id" value="<?php echo $filter_id; ?>" />
|
25 |
-
<input type="hidden" name="action" value="delete-custom-filter" />
|
26 |
-
<input type="submit" value="<?php esc_attr_e( 'Delete This Filter', 'broken-link-checker' ); ?>" id="blc-delete-filter" class="button" />
|
27 |
-
</form>
|
28 |
-
|
29 |
-
|
30 |
-
?>
|
31 |
-
|
32 |
-
<input type="button" value="<?php esc_attr_e( 'Search', 'broken-link-checker' ); ?> »" id="blc-open-search-box" class="button" />
|
33 |
-
</div>
|
34 |
-
|
35 |
-
<!-- The search dialog -->
|
36 |
-
<div id='search-links-dialog' title='Search'>
|
37 |
-
<form class="search-form" action="<?php echo admin_url('tools.php?page=view-broken-links'); ?>" method="get">
|
38 |
-
<input type="hidden" name="page" value="view-broken-links" />
|
39 |
-
<input type="hidden" name="filter_id" value="search" />
|
40 |
-
<fieldset>
|
41 |
-
|
42 |
-
<label for="s_link_text"><?php _e('Link text', 'broken-link-checker'); ?></label>
|
43 |
-
<input type="text" name="s_link_text" value="<?php
|
44 |
-
|
45 |
-
<label for="s_link_url"><?php _e('URL', 'broken-link-checker'); ?></label>
|
46 |
-
<input type="text" name="s_link_url" id="s_link_url" value="<?php
|
47 |
-
|
48 |
-
<label for="s_http_code"><?php _e('HTTP code', 'broken-link-checker'); ?></label>
|
49 |
-
<input type="text" name="s_http_code" id="s_http_code" value="<?php
|
50 |
-
|
51 |
-
<label for="s_filter"><?php _e('Link status', 'broken-link-checker'); ?></label>
|
52 |
-
<select name="s_filter" id="s_filter">
|
53 |
-
<?php
|
54 |
-
if ( !empty($search_params['s_filter']) ){
|
55 |
-
$search_subfilter = $search_params['s_filter'];
|
56 |
-
} else {
|
57 |
-
$search_subfilter = 'all';
|
58 |
-
}
|
59 |
-
|
60 |
-
$linkQuery = blcLinkQuery::getInstance();
|
61 |
-
foreach ($linkQuery->native_filters as $filter => $data){
|
62 |
-
$selected = ($search_subfilter == $filter)?' selected="selected"':'';
|
63 |
-
printf('<option value="%s"%s>%s</option>', $filter, $selected, $data['name']);
|
64 |
-
}
|
65 |
-
?>
|
66 |
-
</select>
|
67 |
-
|
68 |
-
<label for="s_link_type"><?php _e('Link type', 'broken-link-checker'); ?></label>
|
69 |
-
<select name="s_link_type" id="s_link_type">
|
70 |
-
<option value=""><?php _e('Any', 'broken-link-checker'); ?></option>
|
71 |
-
<?php
|
72 |
-
$moduleManager = blcModuleManager::getInstance();
|
73 |
-
|
74 |
-
printf('<optgroup label="%s">', esc_attr(__('Links used in', 'broken-link-checker')));
|
75 |
-
$containers = $moduleManager->get_modules_by_category('container', false, true);
|
76 |
-
foreach($containers as $container_type => $module_data){
|
77 |
-
if ( !empty($module_data['ModuleHidden']) ||
|
78 |
-
continue;
|
79 |
-
}
|
80 |
-
$selected = ( isset($search_params['s_link_type']) && $search_params['s_link_type'] == $container_type )?' selected="selected"':'';
|
81 |
-
printf('<option value="%s"%s>%s</option>', $container_type, $selected, $module_data['Name']);
|
82 |
-
}
|
83 |
-
echo '</optgroup>';
|
84 |
-
//TODO: Better group labels
|
85 |
-
printf('<optgroup label="%s">', esc_attr(__('Link type', 'broken-link-checker')));
|
86 |
-
$parsers = $moduleManager->get_modules_by_category('parser', false, true);
|
87 |
-
foreach($parsers as $parser_type => $module_data){
|
88 |
-
if ( !empty($module_data['ModuleHidden']) ||
|
89 |
-
continue;
|
90 |
-
}
|
91 |
-
$selected = ( isset($search_params['s_link_type']) && $search_params['s_link_type'] == $parser_type )?' selected="selected"':'';
|
92 |
-
printf('<option value="%s"%s>%s</option>', $parser_type, $selected, $module_data['Name']);
|
93 |
-
}
|
94 |
-
echo '</optgroup>';
|
95 |
-
|
96 |
-
/*
|
97 |
-
$link_types = array(
|
98 |
-
__('Any', 'broken-link-checker') => '',
|
99 |
-
__('Normal link', 'broken-link-checker') => 'link',
|
100 |
-
__('Image', 'broken-link-checker') => 'image',
|
101 |
-
__('Custom field', 'broken-link-checker') => 'custom_field',
|
102 |
-
__('Bookmark', 'broken-link-checker') => 'blogroll',
|
103 |
-
__('Comment', 'broken-link-checker') => 'comment',
|
104 |
-
);
|
105 |
-
*/
|
106 |
-
?>
|
107 |
-
</select>
|
108 |
-
|
109 |
-
</fieldset>
|
110 |
-
|
111 |
-
<div id="blc-search-button-row">
|
112 |
-
<input type="submit" value="<?php esc_attr_e( 'Search Links', 'broken-link-checker' ); ?>" id="blc-search-button" name="search_button" class="button-primary" />
|
113 |
-
<input type="button" value="<?php esc_attr_e( 'Cancel', 'broken-link-checker' ); ?>" id="blc-cancel-search" class="button" />
|
114 |
-
</div>
|
115 |
-
|
116 |
-
</form>
|
117 |
-
</div>
|
1 |
+
<?php
|
2 |
+
$search_params = $current_filter['search_params'];
|
3 |
+
?>
|
4 |
+
<div class="search-box">
|
5 |
+
|
6 |
+
<?php
|
7 |
+
//If we're currently displaying search results offer the user the option to
|
8 |
+
//save the search query as a custom filter.
|
9 |
+
if ( 'search' == $filter_id ) {
|
10 |
+
?>
|
11 |
+
<form name="save-search-query" id="custom-filter-form" action="<?php echo admin_url( 'tools.php?page=view-broken-links' ); ?>" method="post" class="blc-inline-form">
|
12 |
+
<?php wp_nonce_field( 'create-custom-filter' ); ?>
|
13 |
+
<input type="hidden" name="name" id="blc-custom-filter-name" value="" />
|
14 |
+
<input type="hidden" name="params" id="blc-custom-filter-params" value="<?php echo http_build_query( $search_params, null, '&' ); ?>" />
|
15 |
+
<input type="hidden" name="action" value="create-custom-filter" />
|
16 |
+
<input type="button" value="<?php esc_attr_e( 'Save This Search As a Filter', 'broken-link-checker' ); ?>" id="blc-create-filter" class="button" />
|
17 |
+
</form>
|
18 |
+
<?php
|
19 |
+
} elseif ( ! empty( $current_filter['custom'] ) ) {
|
20 |
+
//If we're displaying a custom filter give an option to delete it.
|
21 |
+
?>
|
22 |
+
<form name="save-search-query" id="custom-filter-form" action="<?php echo admin_url( 'tools.php?page=view-broken-links' ); ?>" method="post" class="blc-inline-form">
|
23 |
+
<?php wp_nonce_field( 'delete-custom-filter' ); ?>
|
24 |
+
<input type="hidden" name="filter_id" id="blc-custom-filter-id" value="<?php echo $filter_id; ?>" />
|
25 |
+
<input type="hidden" name="action" value="delete-custom-filter" />
|
26 |
+
<input type="submit" value="<?php esc_attr_e( 'Delete This Filter', 'broken-link-checker' ); ?>" id="blc-delete-filter" class="button" />
|
27 |
+
</form>
|
28 |
+
<?php
|
29 |
+
}
|
30 |
+
?>
|
31 |
+
|
32 |
+
<input type="button" value="<?php esc_attr_e( 'Search', 'broken-link-checker' ); ?> »" id="blc-open-search-box" class="button" />
|
33 |
+
</div>
|
34 |
+
|
35 |
+
<!-- The search dialog -->
|
36 |
+
<div id='search-links-dialog' title='Search'>
|
37 |
+
<form class="search-form" action="<?php echo admin_url( 'tools.php?page=view-broken-links' ); ?>" method="get">
|
38 |
+
<input type="hidden" name="page" value="view-broken-links" />
|
39 |
+
<input type="hidden" name="filter_id" value="search" />
|
40 |
+
<fieldset>
|
41 |
+
|
42 |
+
<label for="s_link_text"><?php _e( 'Link text', 'broken-link-checker' ); ?></label>
|
43 |
+
<input type="text" name="s_link_text" value="<?php echo ! empty( $search_params['s_link_text'] ) ? esc_attr( $search_params['s_link_text'] ) : ''; ?>" id="s_link_text" class="text ui-widget-content" />
|
44 |
+
|
45 |
+
<label for="s_link_url"><?php _e( 'URL', 'broken-link-checker' ); ?></label>
|
46 |
+
<input type="text" name="s_link_url" id="s_link_url" value="<?php echo ! empty( $search_params['s_link_url'] ) ? esc_attr( $search_params['s_link_url'] ) : ''; ?>" class="text ui-widget-content" />
|
47 |
+
|
48 |
+
<label for="s_http_code"><?php _e( 'HTTP code', 'broken-link-checker' ); ?></label>
|
49 |
+
<input type="text" name="s_http_code" id="s_http_code" value="<?php echo ! empty( $search_params['s_http_code'] ) ? esc_attr( $search_params['s_http_code'] ) : ''; ?>" class="text ui-widget-content" />
|
50 |
+
|
51 |
+
<label for="s_filter"><?php _e( 'Link status', 'broken-link-checker' ); ?></label>
|
52 |
+
<select name="s_filter" id="s_filter">
|
53 |
+
<?php
|
54 |
+
if ( ! empty( $search_params['s_filter'] ) ) {
|
55 |
+
$search_subfilter = $search_params['s_filter'];
|
56 |
+
} else {
|
57 |
+
$search_subfilter = 'all';
|
58 |
+
}
|
59 |
+
|
60 |
+
$linkQuery = blcLinkQuery::getInstance();
|
61 |
+
foreach ( $linkQuery->native_filters as $filter => $data ) {
|
62 |
+
$selected = ( $search_subfilter == $filter ) ? ' selected="selected"' : '';
|
63 |
+
printf( '<option value="%s"%s>%s</option>', $filter, $selected, $data['name'] );
|
64 |
+
}
|
65 |
+
?>
|
66 |
+
</select>
|
67 |
+
|
68 |
+
<label for="s_link_type"><?php _e( 'Link type', 'broken-link-checker' ); ?></label>
|
69 |
+
<select name="s_link_type" id="s_link_type">
|
70 |
+
<option value=""><?php _e( 'Any', 'broken-link-checker' ); ?></option>
|
71 |
+
<?php
|
72 |
+
$moduleManager = blcModuleManager::getInstance();
|
73 |
+
|
74 |
+
printf( '<optgroup label="%s">', esc_attr( __( 'Links used in', 'broken-link-checker' ) ) );
|
75 |
+
$containers = $moduleManager->get_modules_by_category( 'container', false, true );
|
76 |
+
foreach ( $containers as $container_type => $module_data ) {
|
77 |
+
if ( ! empty( $module_data['ModuleHidden'] ) || ! $moduleManager->is_active( $container_type ) ) {
|
78 |
+
continue;
|
79 |
+
}
|
80 |
+
$selected = ( isset( $search_params['s_link_type'] ) && $search_params['s_link_type'] == $container_type ) ? ' selected="selected"' : '';
|
81 |
+
printf( '<option value="%s"%s>%s</option>', $container_type, $selected, $module_data['Name'] );
|
82 |
+
}
|
83 |
+
echo '</optgroup>';
|
84 |
+
//TODO: Better group labels
|
85 |
+
printf( '<optgroup label="%s">', esc_attr( __( 'Link type', 'broken-link-checker' ) ) );
|
86 |
+
$parsers = $moduleManager->get_modules_by_category( 'parser', false, true );
|
87 |
+
foreach ( $parsers as $parser_type => $module_data ) {
|
88 |
+
if ( ! empty( $module_data['ModuleHidden'] ) || ! $moduleManager->is_active( $parser_type ) ) {
|
89 |
+
continue;
|
90 |
+
}
|
91 |
+
$selected = ( isset( $search_params['s_link_type'] ) && $search_params['s_link_type'] == $parser_type ) ? ' selected="selected"' : '';
|
92 |
+
printf( '<option value="%s"%s>%s</option>', $parser_type, $selected, $module_data['Name'] );
|
93 |
+
}
|
94 |
+
echo '</optgroup>';
|
95 |
+
|
96 |
+
/*
|
97 |
+
$link_types = array(
|
98 |
+
__('Any', 'broken-link-checker') => '',
|
99 |
+
__('Normal link', 'broken-link-checker') => 'link',
|
100 |
+
__('Image', 'broken-link-checker') => 'image',
|
101 |
+
__('Custom field', 'broken-link-checker') => 'custom_field',
|
102 |
+
__('Bookmark', 'broken-link-checker') => 'blogroll',
|
103 |
+
__('Comment', 'broken-link-checker') => 'comment',
|
104 |
+
);
|
105 |
+
*/
|
106 |
+
?>
|
107 |
+
</select>
|
108 |
+
|
109 |
+
</fieldset>
|
110 |
+
|
111 |
+
<div id="blc-search-button-row">
|
112 |
+
<input type="submit" value="<?php esc_attr_e( 'Search Links', 'broken-link-checker' ); ?>" id="blc-search-button" name="search_button" class="button-primary" />
|
113 |
+
<input type="button" value="<?php esc_attr_e( 'Cancel', 'broken-link-checker' ); ?>" id="blc-cancel-search" class="button" />
|
114 |
+
</div>
|
115 |
+
|
116 |
+
</form>
|
117 |
+
</div>
|
includes/admin/sidebar.php
CHANGED
@@ -1,14 +1,14 @@
|
|
1 |
-
<!-- Advertising -->
|
2 |
-
<?php
|
3 |
-
$configuration = blc_get_configuration();
|
4 |
-
if (
|
5 |
-
?>
|
6 |
-
<div id="managewp-ad" class="postbox">
|
7 |
-
<div class="inside">
|
8 |
-
<a href="http://managewp.com/?utm_source=broken_link_checker&utm_medium=Banner&utm_content=mwp250_2&utm_campaign=Plugins" title="ManageWP">
|
9 |
-
<img src="<?php echo plugins_url('images/mwp250_2.png', BLC_PLUGIN_FILE) ?>" width="250" height="250" alt="ManageWP">
|
10 |
-
</a>
|
11 |
-
</div>
|
12 |
-
</div>
|
13 |
-
<?php
|
14 |
-
endif; ?>
|
1 |
+
<!-- Advertising -->
|
2 |
+
<?php
|
3 |
+
$configuration = blc_get_configuration();
|
4 |
+
if ( ! $configuration->get( 'user_has_donated' ) ) :
|
5 |
+
?>
|
6 |
+
<div id="managewp-ad" class="postbox">
|
7 |
+
<div class="inside">
|
8 |
+
<a href="http://managewp.com/?utm_source=broken_link_checker&utm_medium=Banner&utm_content=mwp250_2&utm_campaign=Plugins" title="ManageWP">
|
9 |
+
<img src="<?php echo plugins_url( 'images/mwp250_2.png', BLC_PLUGIN_FILE ); ?>" width="250" height="250" alt="ManageWP">
|
10 |
+
</a>
|
11 |
+
</div>
|
12 |
+
</div>
|
13 |
+
<?php
|
14 |
+
endif; ?>
|
includes/admin/table-printer.php
CHANGED
@@ -17,8 +17,8 @@ if ( ! class_exists( 'blcTablePrinter' ) ) {
|
|
17 |
var $core; //A reference to the main plugin object
|
18 |
var $neutral_current_url; //The "safe" version of the current URL, for use in the bulk action form.
|
19 |
|
20 |
-
var $bulk_actions_html
|
21 |
-
var $pagination_html
|
22 |
var $searched_link_type = '';
|
23 |
|
24 |
var $columns;
|
@@ -34,7 +34,7 @@ if ( ! class_exists( 'blcTablePrinter' ) ) {
|
|
34 |
|
35 |
// Figure out what the "safe" URL to acccess the current page would be.
|
36 |
// This is used by the bulk action form.
|
37 |
-
$special_args
|
38 |
$this->neutral_current_url = remove_query_arg( $special_args );
|
39 |
}
|
40 |
|
@@ -50,10 +50,10 @@ if ( ! class_exists( 'blcTablePrinter' ) ) {
|
|
50 |
*/
|
51 |
function print_table( $current_filter, $layout = 'flexible', $visible_columns = null, $compact = false ) {
|
52 |
$this->current_filter = $current_filter;
|
53 |
-
$this->page
|
54 |
-
$this->per_page
|
55 |
|
56 |
-
$current_layout = $this->layouts[$layout];
|
57 |
if ( empty( $visible_columns ) ) {
|
58 |
$visible_columns = $current_layout;
|
59 |
}
|
@@ -98,29 +98,33 @@ if ( ! class_exists( 'blcTablePrinter' ) ) {
|
|
98 |
|
99 |
$heading = $column['heading'];
|
100 |
if ( isset( $column['sortable'] ) && $column['sortable'] ) {
|
101 |
-
$orderby
|
102 |
$current_orderby = isset( $_GET['orderby'] ) ? $_GET['orderby'] : '';
|
103 |
-
$current_order
|
104 |
-
if ( ! in_array( $current_order, array( 'asc', 'desc') ) ) {
|
105 |
$current_order = 'asc';
|
106 |
}
|
107 |
|
108 |
if ( $orderby == $current_orderby ) {
|
109 |
$column_classes[] = 'sorted';
|
110 |
$column_classes[] = $current_order;
|
111 |
-
$order
|
112 |
} else {
|
113 |
-
$order
|
114 |
$column_classes[] = 'desc';
|
115 |
$column_classes[] = 'sortable';
|
116 |
}
|
117 |
|
118 |
$heading = sprintf(
|
119 |
'<a href="%s"><span>%s</span><span class="sorting-indicator"></span></a>',
|
120 |
-
esc_attr(
|
121 |
-
|
122 |
-
|
123 |
-
|
|
|
|
|
|
|
|
|
124 |
$heading
|
125 |
);
|
126 |
}
|
@@ -128,7 +132,7 @@ if ( ! class_exists( 'blcTablePrinter' ) ) {
|
|
128 |
printf(
|
129 |
'<th scope="col" class="%s"%s>%s</th>',
|
130 |
implode( ' ', $column_classes ),
|
131 |
-
isset
|
132 |
$heading
|
133 |
);
|
134 |
}
|
@@ -179,22 +183,22 @@ if ( ! class_exists( 'blcTablePrinter' ) ) {
|
|
179 |
|
180 |
// Display the view switch (only in the top nav. area).
|
181 |
if ( empty( $suffix ) ) {
|
182 |
-
|
183 |
|
184 |
<div class="view-switch">
|
185 |
<a
|
186 |
-
href="<?php echo esc_url( add_query_arg( 'compact', '1', $_SERVER['REQUEST_URI'] ) ) ?>"
|
187 |
-
class="view-list <?php
|
188 |
title="<?php echo esc_attr( __( 'Compact View', 'broken-link-checker' ) ); ?>">
|
189 |
</a>
|
190 |
<a
|
191 |
-
href="<?php echo esc_url( add_query_arg( 'compact', '0', $_SERVER['REQUEST_URI'] ) ) ?>"
|
192 |
-
class="view-excerpt <?php
|
193 |
title="<?php echo esc_attr( __( 'Detailed View', 'broken-link-checker' ) ); ?>">
|
194 |
</a>
|
195 |
</div>
|
196 |
|
197 |
-
|
198 |
}
|
199 |
|
200 |
echo '</div>';
|
@@ -205,38 +209,38 @@ if ( ! class_exists( 'blcTablePrinter' ) ) {
|
|
205 |
*
|
206 |
* @return void
|
207 |
*/
|
208 |
-
function setup_columns(){
|
209 |
$this->columns = array(
|
210 |
-
'status'
|
211 |
'heading' => __( 'Status', 'broken-link-checker' ),
|
212 |
'content' => array( $this, 'column_status' ),
|
213 |
),
|
214 |
|
215 |
-
'new-url'
|
216 |
-
'heading'
|
217 |
-
'content'
|
218 |
'sortable' => true,
|
219 |
-
'orderby'
|
220 |
),
|
221 |
|
222 |
-
'used-in'
|
223 |
'heading' => __( 'Source', 'broken-link-checker' ),
|
224 |
-
'class'
|
225 |
'content' => array( $this, 'column_used_in' ),
|
226 |
),
|
227 |
|
228 |
'new-link-text' => array(
|
229 |
-
'heading'
|
230 |
-
'content'
|
231 |
'sortable' => true,
|
232 |
-
'orderby'
|
233 |
),
|
234 |
|
235 |
-
'redirect-url'
|
236 |
-
'heading'
|
237 |
-
'content'
|
238 |
'sortable' => true,
|
239 |
-
'orderby'
|
240 |
),
|
241 |
);
|
242 |
}
|
@@ -248,7 +252,7 @@ if ( ! class_exists( 'blcTablePrinter' ) ) {
|
|
248 |
*/
|
249 |
function setup_layouts() {
|
250 |
$this->layouts = array(
|
251 |
-
'classic'
|
252 |
'flexible' => array( 'new-url', 'status', 'new-link-text', 'redirect-url', 'used-in' ),
|
253 |
);
|
254 |
}
|
@@ -280,17 +284,17 @@ if ( ! class_exists( 'blcTablePrinter' ) ) {
|
|
280 |
*
|
281 |
* @return void
|
282 |
*/
|
283 |
-
function prepare_nav_html(){
|
284 |
//Generate an <option> element for each possible bulk action. The list doesn't change,
|
285 |
//so we can do it once and reuse the generated HTML.
|
286 |
$bulk_actions = array(
|
287 |
-
'-1'
|
288 |
-
'bulk-edit'
|
289 |
-
'bulk-recheck'
|
290 |
'bulk-deredirect' => __( 'Fix redirects', 'broken-link-checker' ),
|
291 |
'bulk-not-broken' => __( 'Mark as not broken', 'broken-link-checker' ),
|
292 |
-
'bulk-dismiss'
|
293 |
-
'bulk-unlink'
|
294 |
);
|
295 |
if ( EMPTY_TRASH_DAYS ) {
|
296 |
$bulk_actions['bulk-trash-sources'] = __( 'Move sources to Trash', 'broken-link-checker' );
|
@@ -307,19 +311,21 @@ if ( ! class_exists( 'blcTablePrinter' ) ) {
|
|
307 |
|
308 |
// Pagination links can also be pre-generated.
|
309 |
// WP has a built-in function for pagination :).
|
310 |
-
$page_links = paginate_links(
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
|
|
|
|
318 |
|
319 |
if ( $page_links ) {
|
320 |
-
$this->pagination_html
|
321 |
$this->pagination_html .= sprintf(
|
322 |
-
'<span class="displaying-num">' . __( 'Displaying %s–%s of <span class="current-link-count">%s</span>', 'broken-link-checker' ) . '</span>%s',
|
323 |
number_format_i18n( ( $this->page - 1 ) * $this->per_page + 1 ),
|
324 |
number_format_i18n( min( $this->page * $this->per_page, $this->current_filter['count'] ) ),
|
325 |
number_format_i18n( $this->current_filter['count'] ),
|
@@ -367,9 +373,11 @@ if ( ! class_exists( 'blcTablePrinter' ) ) {
|
|
367 |
|
368 |
<p class="submit inline-edit-save">
|
369 |
<a href="#bulk-edit" class="button-secondary cancel alignleft" title="<?php echo esc_attr( __( 'Cancel', 'broken-link-checker' ) ); ?>" accesskey="c"><?php _e( 'Cancel', 'broken-link-checker' ); ?></a>
|
370 |
-
<input type="submit" name="bulk_edit" class="button-primary alignright" value="
|
|
|
371 |
_e( 'Update', 'broken-link-checker' );
|
372 |
-
?>
|
|
|
373 |
</p>
|
374 |
</div>
|
375 |
</td></tr>
|
@@ -388,7 +396,7 @@ if ( ! class_exists( 'blcTablePrinter' ) ) {
|
|
388 |
function link_row( $link, $layout, $visible_columns, $rownum = 0 ) {
|
389 |
|
390 |
//Figure out what CSS classes the link row should have
|
391 |
-
$rowclass = ($rownum % 2)? 'alternate' : '';
|
392 |
|
393 |
$excluded = $this->core->is_excluded( $link->url );
|
394 |
if ( $excluded ) {
|
@@ -411,7 +419,7 @@ if ( ! class_exists( 'blcTablePrinter' ) ) {
|
|
411 |
}
|
412 |
}
|
413 |
|
414 |
-
$status
|
415 |
$rowclass .= ' link-status-' . $status['code'];
|
416 |
|
417 |
// Retrieve link instances to display in the table.
|
@@ -428,12 +436,14 @@ if ( ! class_exists( 'blcTablePrinter' ) ) {
|
|
428 |
}
|
429 |
|
430 |
// For inline editing, we'll need to know if any instances have editable link text, and what it is.
|
431 |
-
$can_edit_text
|
432 |
-
$can_edit_url
|
433 |
-
$editable_link_texts =
|
|
|
|
|
434 |
foreach ( $instances as $instance ) {
|
435 |
if ( $instance->is_link_text_editable() ) {
|
436 |
-
$can_edit_text
|
437 |
$editable_link_texts[ $instance->link_text ] = true;
|
438 |
} else {
|
439 |
$non_editable_link_texts[ $instance->link_text ] = true;
|
@@ -444,11 +454,11 @@ if ( ! class_exists( 'blcTablePrinter' ) ) {
|
|
444 |
}
|
445 |
}
|
446 |
|
447 |
-
$link_texts
|
448 |
$data_link_text = '';
|
449 |
if ( 1 === count( $link_texts ) ) {
|
450 |
// All instances have the same text - use it.
|
451 |
-
$link_text
|
452 |
$data_link_text = ' data-link-text="' . esc_attr( $link_text ) . '"';
|
453 |
}
|
454 |
|
@@ -524,54 +534,68 @@ if ( ! class_exists( 'blcTablePrinter' ) ) {
|
|
524 |
<ol style='list-style-type: none;'>
|
525 |
<?php if ( ! empty( $link->post_date ) ) { ?>
|
526 |
<li><strong><?php _e( 'Post published on', 'broken-link-checker' ); ?>:</strong>
|
527 |
-
<span class='post_date'
|
|
|
528 |
echo date_i18n( get_option( 'date_format' ), strtotime( $link->post_date ) );
|
529 |
-
|
|
|
530 |
<?php } ?>
|
531 |
<li><strong><?php _e( 'Link last checked', 'broken-link-checker' ); ?>:</strong>
|
532 |
-
<span class='check_date'
|
|
|
533 |
$last_check = $link->last_check;
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
|
|
|
544 |
|
545 |
<li><strong><?php _e( 'HTTP code', 'broken-link-checker' ); ?>:</strong>
|
546 |
-
<span class='http_code'
|
|
|
547 |
print $link->http_code;
|
548 |
-
|
|
|
549 |
|
550 |
<li><strong><?php _e( 'Response time', 'broken-link-checker' ); ?>:</strong>
|
551 |
-
<span class='request_duration'
|
|
|
552 |
printf( __( '%2.3f seconds', 'broken-link-checker' ), $link->request_duration );
|
553 |
-
|
|
|
554 |
|
555 |
<li><strong><?php _e( 'Final URL', 'broken-link-checker' ); ?>:</strong>
|
556 |
-
<span class='final_url'
|
|
|
557 |
print esc_html( $link->final_url );
|
558 |
-
|
|
|
559 |
|
560 |
<li><strong><?php _e( 'Redirect count', 'broken-link-checker' ); ?>:</strong>
|
561 |
-
<span class='redirect_count'
|
|
|
562 |
print $link->redirect_count;
|
563 |
-
|
|
|
564 |
|
565 |
<li><strong><?php _e( 'Instance count', 'broken-link-checker' ); ?>:</strong>
|
566 |
-
<span class='instance_count'
|
|
|
567 |
print count( $link->get_instances() );
|
568 |
-
|
|
|
569 |
|
570 |
-
<?php if ( ( $link->broken || $link->warning ) && (intval( $link->check_count ) > 0) ) { ?>
|
571 |
<li><br/>
|
572 |
-
|
573 |
printf(
|
574 |
-
_n('This link has failed %d time.', 'This link has failed %d times.', $link->check_count, 'broken-link-checker'),
|
575 |
$link->check_count
|
576 |
);
|
577 |
|
@@ -579,10 +603,10 @@ if ( ! class_exists( 'blcTablePrinter' ) ) {
|
|
579 |
|
580 |
$delta = time() - $link->first_failure;
|
581 |
printf(
|
582 |
-
__('This link has been broken for %s.', 'broken-link-checker'),
|
583 |
-
blcUtility::fuzzy_delta($delta)
|
584 |
);
|
585 |
-
|
586 |
</li>
|
587 |
<?php } ?>
|
588 |
</ol>
|
@@ -590,10 +614,12 @@ if ( ! class_exists( 'blcTablePrinter' ) ) {
|
|
590 |
|
591 |
<div class="blc-detail-block" style="float: right; width: 50%;">
|
592 |
<ol style='list-style-type: none;'>
|
593 |
-
<li><strong><?php _e('Log', 'broken-link-checker'); ?>:</strong>
|
594 |
-
<span class='blc_log'
|
|
|
595 |
print nl2br( $link->log );
|
596 |
-
|
|
|
597 |
</ol>
|
598 |
</div>
|
599 |
|
@@ -632,8 +658,8 @@ if ( ! class_exists( 'blcTablePrinter' ) ) {
|
|
632 |
);
|
633 |
|
634 |
// Last checked...
|
635 |
-
if ( $link->last_check
|
636 |
-
$last_check
|
637 |
$last_check .= blcUtility::fuzzy_delta( time() - $link->last_check, 'ago' );
|
638 |
|
639 |
printf(
|
@@ -642,10 +668,9 @@ if ( ! class_exists( 'blcTablePrinter' ) ) {
|
|
642 |
);
|
643 |
}
|
644 |
|
645 |
-
|
646 |
// Broken for...
|
647 |
if ( $link->broken ) {
|
648 |
-
$delta
|
649 |
$broken_for = blcUtility::fuzzy_delta( $delta );
|
650 |
printf(
|
651 |
'<tr class="link-broken-for"><td>%s %s</td></tr>',
|
@@ -682,9 +707,9 @@ if ( ! class_exists( 'blcTablePrinter' ) ) {
|
|
682 |
// Output inline action links for the link/URL.
|
683 |
$actions = array();
|
684 |
|
685 |
-
$actions['edit'] = "<a href='javascript:void(0)' class='blc-edit-button' title='" . esc_attr( __('Edit this link'
|
686 |
|
687 |
-
$actions['delete'] = "<a class='submitdelete blc-unlink-button' title='" . esc_attr( __( 'Remove this link from all posts', 'broken-link-checker') ). "' ".
|
688 |
"href='javascript:void(0);'>" . __( 'Unlink', 'broken-link-checker' ) . '</a>';
|
689 |
|
690 |
if ( $link->broken || $link->warning ) {
|
@@ -698,10 +723,10 @@ if ( ! class_exists( 'blcTablePrinter' ) ) {
|
|
698 |
if ( ! $link->dismissed && ( $link->broken || $link->warning || ( $link->redirect_count > 0 ) ) ) {
|
699 |
$actions['blc-dismiss-action'] = sprintf(
|
700 |
'<a href="#" title="%s" class="blc-dismiss-button">%s</a>',
|
701 |
-
esc_attr( __( 'Hide this link and do not report it again unless its status changes'
|
702 |
__( 'Dismiss', 'broken-link-checker' )
|
703 |
);
|
704 |
-
}
|
705 |
$actions['blc-undismiss-action'] = sprintf(
|
706 |
'<a href="#" title="%s" class="blc-undismiss-button">%s</a>',
|
707 |
esc_attr( __( 'Undismiss this link', 'broken-link-checker' ) ),
|
@@ -725,7 +750,7 @@ if ( ! class_exists( 'blcTablePrinter' ) ) {
|
|
725 |
|
726 |
// Only show the enabled actions.
|
727 |
$conf = blc_get_configuration();
|
728 |
-
foreach ( $conf->get( 'show_link_actions', $actions ) as $name => $enabled) {
|
729 |
if ( ! $enabled ) {
|
730 |
unset( $actions[ $name ] );
|
731 |
}
|
@@ -733,12 +758,12 @@ if ( ! class_exists( 'blcTablePrinter' ) ) {
|
|
733 |
|
734 |
// Wrap actions with <span></span> and separate them with | characters.
|
735 |
// Basically, this emulates the HTML structure that WP uses for post actions under Posts -> All Posts.
|
736 |
-
$spans
|
737 |
$is_first_action = true;
|
738 |
foreach ( $actions as $name => $html ) {
|
739 |
-
$spans[]
|
740 |
'<span class="%s">%s%s</span>',
|
741 |
-
esc_attr($name),
|
742 |
$is_first_action ? '' : ' | ',
|
743 |
$html
|
744 |
);
|
17 |
var $core; //A reference to the main plugin object
|
18 |
var $neutral_current_url; //The "safe" version of the current URL, for use in the bulk action form.
|
19 |
|
20 |
+
var $bulk_actions_html = '';
|
21 |
+
var $pagination_html = '';
|
22 |
var $searched_link_type = '';
|
23 |
|
24 |
var $columns;
|
34 |
|
35 |
// Figure out what the "safe" URL to acccess the current page would be.
|
36 |
// This is used by the bulk action form.
|
37 |
+
$special_args = array( '_wpnonce', '_wp_http_referer', 'action', 'selected_links' );
|
38 |
$this->neutral_current_url = remove_query_arg( $special_args );
|
39 |
}
|
40 |
|
50 |
*/
|
51 |
function print_table( $current_filter, $layout = 'flexible', $visible_columns = null, $compact = false ) {
|
52 |
$this->current_filter = $current_filter;
|
53 |
+
$this->page = $current_filter['page'];
|
54 |
+
$this->per_page = $current_filter['per_page'];
|
55 |
|
56 |
+
$current_layout = $this->layouts[ $layout ];
|
57 |
if ( empty( $visible_columns ) ) {
|
58 |
$visible_columns = $current_layout;
|
59 |
}
|
98 |
|
99 |
$heading = $column['heading'];
|
100 |
if ( isset( $column['sortable'] ) && $column['sortable'] ) {
|
101 |
+
$orderby = $column['orderby'];
|
102 |
$current_orderby = isset( $_GET['orderby'] ) ? $_GET['orderby'] : '';
|
103 |
+
$current_order = isset( $_GET['order'] ) ? $_GET['order'] : 'asc';
|
104 |
+
if ( ! in_array( $current_order, array( 'asc', 'desc' ) ) ) {
|
105 |
$current_order = 'asc';
|
106 |
}
|
107 |
|
108 |
if ( $orderby == $current_orderby ) {
|
109 |
$column_classes[] = 'sorted';
|
110 |
$column_classes[] = $current_order;
|
111 |
+
$order = ( 'asc' == $current_order ) ? 'desc' : 'asc'; //Reverse the sort direction
|
112 |
} else {
|
113 |
+
$order = 'asc';
|
114 |
$column_classes[] = 'desc';
|
115 |
$column_classes[] = 'sortable';
|
116 |
}
|
117 |
|
118 |
$heading = sprintf(
|
119 |
'<a href="%s"><span>%s</span><span class="sorting-indicator"></span></a>',
|
120 |
+
esc_attr(
|
121 |
+
add_query_arg(
|
122 |
+
array(
|
123 |
+
'orderby' => $orderby,
|
124 |
+
'order' => $order,
|
125 |
+
)
|
126 |
+
)
|
127 |
+
),
|
128 |
$heading
|
129 |
);
|
130 |
}
|
132 |
printf(
|
133 |
'<th scope="col" class="%s"%s>%s</th>',
|
134 |
implode( ' ', $column_classes ),
|
135 |
+
isset( $column['id'] ) ? ' id="' . $column['id'] . '"' : '',
|
136 |
$heading
|
137 |
);
|
138 |
}
|
183 |
|
184 |
// Display the view switch (only in the top nav. area).
|
185 |
if ( empty( $suffix ) ) {
|
186 |
+
?>
|
187 |
|
188 |
<div class="view-switch">
|
189 |
<a
|
190 |
+
href="<?php echo esc_url( add_query_arg( 'compact', '1', $_SERVER['REQUEST_URI'] ) ); ?>"
|
191 |
+
class="view-list <?php echo $table_compact ? 'current' : ''; ?>"
|
192 |
title="<?php echo esc_attr( __( 'Compact View', 'broken-link-checker' ) ); ?>">
|
193 |
</a>
|
194 |
<a
|
195 |
+
href="<?php echo esc_url( add_query_arg( 'compact', '0', $_SERVER['REQUEST_URI'] ) ); ?>"
|
196 |
+
class="view-excerpt <?php echo ! $table_compact ? 'current' : ''; ?>"
|
197 |
title="<?php echo esc_attr( __( 'Detailed View', 'broken-link-checker' ) ); ?>">
|
198 |
</a>
|
199 |
</div>
|
200 |
|
201 |
+
<?php
|
202 |
}
|
203 |
|
204 |
echo '</div>';
|
209 |
*
|
210 |
* @return void
|
211 |
*/
|
212 |
+
function setup_columns() {
|
213 |
$this->columns = array(
|
214 |
+
'status' => array(
|
215 |
'heading' => __( 'Status', 'broken-link-checker' ),
|
216 |
'content' => array( $this, 'column_status' ),
|
217 |
),
|
218 |
|
219 |
+
'new-url' => array(
|
220 |
+
'heading' => __( 'URL', 'broken-link-checker' ),
|
221 |
+
'content' => array( $this, 'column_new_url' ),
|
222 |
'sortable' => true,
|
223 |
+
'orderby' => 'url',
|
224 |
),
|
225 |
|
226 |
+
'used-in' => array(
|
227 |
'heading' => __( 'Source', 'broken-link-checker' ),
|
228 |
+
'class' => 'column-title',
|
229 |
'content' => array( $this, 'column_used_in' ),
|
230 |
),
|
231 |
|
232 |
'new-link-text' => array(
|
233 |
+
'heading' => __( 'Link Text', 'broken-link-checker' ),
|
234 |
+
'content' => array( $this, 'column_new_link_text' ),
|
235 |
'sortable' => true,
|
236 |
+
'orderby' => 'link_text',
|
237 |
),
|
238 |
|
239 |
+
'redirect-url' => array(
|
240 |
+
'heading' => __( 'Redirect URL', 'broken-link-checker' ),
|
241 |
+
'content' => array( $this, 'column_redirect_url' ),
|
242 |
'sortable' => true,
|
243 |
+
'orderby' => 'redirect_url',
|
244 |
),
|
245 |
);
|
246 |
}
|
252 |
*/
|
253 |
function setup_layouts() {
|
254 |
$this->layouts = array(
|
255 |
+
'classic' => array( 'used-in', 'new-link-text', 'new-url' ),
|
256 |
'flexible' => array( 'new-url', 'status', 'new-link-text', 'redirect-url', 'used-in' ),
|
257 |
);
|
258 |
}
|
284 |
*
|
285 |
* @return void
|
286 |
*/
|
287 |
+
function prepare_nav_html() {
|
288 |
//Generate an <option> element for each possible bulk action. The list doesn't change,
|
289 |
//so we can do it once and reuse the generated HTML.
|
290 |
$bulk_actions = array(
|
291 |
+
'-1' => __( 'Bulk Actions', 'broken-link-checker' ),
|
292 |
+
'bulk-edit' => __( 'Edit URL', 'broken-link-checker' ),
|
293 |
+
'bulk-recheck' => __( 'Recheck', 'broken-link-checker' ),
|
294 |
'bulk-deredirect' => __( 'Fix redirects', 'broken-link-checker' ),
|
295 |
'bulk-not-broken' => __( 'Mark as not broken', 'broken-link-checker' ),
|
296 |
+
'bulk-dismiss' => __( 'Dismiss', 'broken-link-checker' ),
|
297 |
+
'bulk-unlink' => __( 'Unlink', 'broken-link-checker' ),
|
298 |
);
|
299 |
if ( EMPTY_TRASH_DAYS ) {
|
300 |
$bulk_actions['bulk-trash-sources'] = __( 'Move sources to Trash', 'broken-link-checker' );
|
311 |
|
312 |
// Pagination links can also be pre-generated.
|
313 |
// WP has a built-in function for pagination :).
|
314 |
+
$page_links = paginate_links(
|
315 |
+
array(
|
316 |
+
'base' => add_query_arg( 'paged', '%#%' ),
|
317 |
+
'format' => '',
|
318 |
+
'prev_text' => __( '«' ),
|
319 |
+
'next_text' => __( '»' ),
|
320 |
+
'total' => $this->current_filter['max_pages'],
|
321 |
+
'current' => $this->page,
|
322 |
+
)
|
323 |
+
);
|
324 |
|
325 |
if ( $page_links ) {
|
326 |
+
$this->pagination_html = '<div class="tablenav-pages">';
|
327 |
$this->pagination_html .= sprintf(
|
328 |
+
'<span class="displaying-num">' . __( 'Displaying %1$s–%2$s of <span class="current-link-count">%3$s</span>', 'broken-link-checker' ) . '</span>%4$s',
|
329 |
number_format_i18n( ( $this->page - 1 ) * $this->per_page + 1 ),
|
330 |
number_format_i18n( min( $this->page * $this->per_page, $this->current_filter['count'] ) ),
|
331 |
number_format_i18n( $this->current_filter['count'] ),
|
373 |
|
374 |
<p class="submit inline-edit-save">
|
375 |
<a href="#bulk-edit" class="button-secondary cancel alignleft" title="<?php echo esc_attr( __( 'Cancel', 'broken-link-checker' ) ); ?>" accesskey="c"><?php _e( 'Cancel', 'broken-link-checker' ); ?></a>
|
376 |
+
<input type="submit" name="bulk_edit" class="button-primary alignright" value="
|
377 |
+
<?php
|
378 |
_e( 'Update', 'broken-link-checker' );
|
379 |
+
?>
|
380 |
+
" accesskey="s">
|
381 |
</p>
|
382 |
</div>
|
383 |
</td></tr>
|
396 |
function link_row( $link, $layout, $visible_columns, $rownum = 0 ) {
|
397 |
|
398 |
//Figure out what CSS classes the link row should have
|
399 |
+
$rowclass = ( $rownum % 2 ) ? 'alternate' : '';
|
400 |
|
401 |
$excluded = $this->core->is_excluded( $link->url );
|
402 |
if ( $excluded ) {
|
419 |
}
|
420 |
}
|
421 |
|
422 |
+
$status = $link->analyse_status();
|
423 |
$rowclass .= ' link-status-' . $status['code'];
|
424 |
|
425 |
// Retrieve link instances to display in the table.
|
436 |
}
|
437 |
|
438 |
// For inline editing, we'll need to know if any instances have editable link text, and what it is.
|
439 |
+
$can_edit_text = false;
|
440 |
+
$can_edit_url = false;
|
441 |
+
$editable_link_texts = array();
|
442 |
+
$non_editable_link_texts = array();
|
443 |
+
|
444 |
foreach ( $instances as $instance ) {
|
445 |
if ( $instance->is_link_text_editable() ) {
|
446 |
+
$can_edit_text = true;
|
447 |
$editable_link_texts[ $instance->link_text ] = true;
|
448 |
} else {
|
449 |
$non_editable_link_texts[ $instance->link_text ] = true;
|
454 |
}
|
455 |
}
|
456 |
|
457 |
+
$link_texts = $can_edit_text ? $editable_link_texts : $non_editable_link_texts;
|
458 |
$data_link_text = '';
|
459 |
if ( 1 === count( $link_texts ) ) {
|
460 |
// All instances have the same text - use it.
|
461 |
+
$link_text = key( $link_texts );
|
462 |
$data_link_text = ' data-link-text="' . esc_attr( $link_text ) . '"';
|
463 |
}
|
464 |
|
534 |
<ol style='list-style-type: none;'>
|
535 |
<?php if ( ! empty( $link->post_date ) ) { ?>
|
536 |
<li><strong><?php _e( 'Post published on', 'broken-link-checker' ); ?>:</strong>
|
537 |
+
<span class='post_date'>
|
538 |
+
<?php
|
539 |
echo date_i18n( get_option( 'date_format' ), strtotime( $link->post_date ) );
|
540 |
+
?>
|
541 |
+
</span></li>
|
542 |
<?php } ?>
|
543 |
<li><strong><?php _e( 'Link last checked', 'broken-link-checker' ); ?>:</strong>
|
544 |
+
<span class='check_date'>
|
545 |
+
<?php
|
546 |
$last_check = $link->last_check;
|
547 |
+
if ( $last_check < strtotime( '-10 years' ) ) {
|
548 |
+
_e( 'Never', 'broken-link-checker' );
|
549 |
+
} else {
|
550 |
+
printf(
|
551 |
+
'<time datetime="%s">%s</time>',
|
552 |
+
esc_attr( date( 'c', $last_check ) ), //phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
|
553 |
+
date_i18n( get_option( 'date_format' ), $last_check )
|
554 |
+
);
|
555 |
+
}
|
556 |
+
?>
|
557 |
+
</span></li>
|
558 |
|
559 |
<li><strong><?php _e( 'HTTP code', 'broken-link-checker' ); ?>:</strong>
|
560 |
+
<span class='http_code'>
|
561 |
+
<?php
|
562 |
print $link->http_code;
|
563 |
+
?>
|
564 |
+
</span></li>
|
565 |
|
566 |
<li><strong><?php _e( 'Response time', 'broken-link-checker' ); ?>:</strong>
|
567 |
+
<span class='request_duration'>
|
568 |
+
<?php
|
569 |
printf( __( '%2.3f seconds', 'broken-link-checker' ), $link->request_duration );
|
570 |
+
?>
|
571 |
+
</span></li>
|
572 |
|
573 |
<li><strong><?php _e( 'Final URL', 'broken-link-checker' ); ?>:</strong>
|
574 |
+
<span class='final_url'>
|
575 |
+
<?php
|
576 |
print esc_html( $link->final_url );
|
577 |
+
?>
|
578 |
+
</span></li>
|
579 |
|
580 |
<li><strong><?php _e( 'Redirect count', 'broken-link-checker' ); ?>:</strong>
|
581 |
+
<span class='redirect_count'>
|
582 |
+
<?php
|
583 |
print $link->redirect_count;
|
584 |
+
?>
|
585 |
+
</span></li>
|
586 |
|
587 |
<li><strong><?php _e( 'Instance count', 'broken-link-checker' ); ?>:</strong>
|
588 |
+
<span class='instance_count'>
|
589 |
+
<?php
|
590 |
print count( $link->get_instances() );
|
591 |
+
?>
|
592 |
+
</span></li>
|
593 |
|
594 |
+
<?php if ( ( $link->broken || $link->warning ) && ( intval( $link->check_count ) > 0 ) ) { ?>
|
595 |
<li><br/>
|
596 |
+
<?php
|
597 |
printf(
|
598 |
+
_n( 'This link has failed %d time.', 'This link has failed %d times.', $link->check_count, 'broken-link-checker' ),
|
599 |
$link->check_count
|
600 |
);
|
601 |
|
603 |
|
604 |
$delta = time() - $link->first_failure;
|
605 |
printf(
|
606 |
+
__( 'This link has been broken for %s.', 'broken-link-checker' ),
|
607 |
+
blcUtility::fuzzy_delta( $delta )
|
608 |
);
|
609 |
+
?>
|
610 |
</li>
|
611 |
<?php } ?>
|
612 |
</ol>
|
614 |
|
615 |
<div class="blc-detail-block" style="float: right; width: 50%;">
|
616 |
<ol style='list-style-type: none;'>
|
617 |
+
<li><strong><?php _e( 'Log', 'broken-link-checker' ); ?>:</strong>
|
618 |
+
<span class='blc_log'>
|
619 |
+
<?php
|
620 |
print nl2br( $link->log );
|
621 |
+
?>
|
622 |
+
</span></li>
|
623 |
</ol>
|
624 |
</div>
|
625 |
|
658 |
);
|
659 |
|
660 |
// Last checked...
|
661 |
+
if ( 0 != $link->last_check ) {
|
662 |
+
$last_check = _x( 'Checked', 'checked how long ago', 'broken-link-checker' ) . ' ';
|
663 |
$last_check .= blcUtility::fuzzy_delta( time() - $link->last_check, 'ago' );
|
664 |
|
665 |
printf(
|
668 |
);
|
669 |
}
|
670 |
|
|
|
671 |
// Broken for...
|
672 |
if ( $link->broken ) {
|
673 |
+
$delta = time() - $link->first_failure;
|
674 |
$broken_for = blcUtility::fuzzy_delta( $delta );
|
675 |
printf(
|
676 |
'<tr class="link-broken-for"><td>%s %s</td></tr>',
|
707 |
// Output inline action links for the link/URL.
|
708 |
$actions = array();
|
709 |
|
710 |
+
$actions['edit'] = "<a href='javascript:void(0)' class='blc-edit-button' title='" . esc_attr( __( 'Edit this link', 'broken-link-checker' ) ) . "'>" . __( 'Edit URL', 'broken-link-checker' ) . '</a>';
|
711 |
|
712 |
+
$actions['delete'] = "<a class='submitdelete blc-unlink-button' title='" . esc_attr( __( 'Remove this link from all posts', 'broken-link-checker' ) ) . "' " .
|
713 |
"href='javascript:void(0);'>" . __( 'Unlink', 'broken-link-checker' ) . '</a>';
|
714 |
|
715 |
if ( $link->broken || $link->warning ) {
|
723 |
if ( ! $link->dismissed && ( $link->broken || $link->warning || ( $link->redirect_count > 0 ) ) ) {
|
724 |
$actions['blc-dismiss-action'] = sprintf(
|
725 |
'<a href="#" title="%s" class="blc-dismiss-button">%s</a>',
|
726 |
+
esc_attr( __( 'Hide this link and do not report it again unless its status changes', 'broken-link-checker' ) ),
|
727 |
__( 'Dismiss', 'broken-link-checker' )
|
728 |
);
|
729 |
+
} elseif ( $link->dismissed ) {
|
730 |
$actions['blc-undismiss-action'] = sprintf(
|
731 |
'<a href="#" title="%s" class="blc-undismiss-button">%s</a>',
|
732 |
esc_attr( __( 'Undismiss this link', 'broken-link-checker' ) ),
|
750 |
|
751 |
// Only show the enabled actions.
|
752 |
$conf = blc_get_configuration();
|
753 |
+
foreach ( $conf->get( 'show_link_actions', $actions ) as $name => $enabled ) {
|
754 |
if ( ! $enabled ) {
|
755 |
unset( $actions[ $name ] );
|
756 |
}
|
758 |
|
759 |
// Wrap actions with <span></span> and separate them with | characters.
|
760 |
// Basically, this emulates the HTML structure that WP uses for post actions under Posts -> All Posts.
|
761 |
+
$spans = array();
|
762 |
$is_first_action = true;
|
763 |
foreach ( $actions as $name => $html ) {
|
764 |
+
$spans[] = sprintf(
|
765 |
'<span class="%s">%s%s</span>',
|
766 |
+
esc_attr( $name ),
|
767 |
$is_first_action ? '' : ' | ',
|
768 |
$html
|
769 |
);
|
includes/any-post.php
CHANGED
@@ -1,804 +1,810 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* The manager to rule all (post) managers.
|
5 |
-
*
|
6 |
-
* This class dynamically registers container modules for the available post types
|
7 |
-
* (including custom post types) and does stuff that pertain to all of them, such
|
8 |
-
* as handling save/delete hooks and (re)creating synch records.
|
9 |
-
*
|
10 |
-
* @package Broken Link Checker
|
11 |
-
* @author Janis Elsts
|
12 |
-
* @access private
|
13 |
-
*/
|
14 |
-
class blcPostTypeOverlord {
|
15 |
-
public $enabled_post_types
|
16 |
-
public $enabled_post_statuses = array('publish'); //Only posts that have one of these statuses shall be checked
|
17 |
-
|
18 |
-
var $plugin_conf;
|
19 |
-
var $resynch_already_done = false;
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
function init(){
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
//Register a virtual container module for each enabled post type
|
43 |
-
$module_manager = blcModuleManager::getInstance();
|
44 |
-
|
45 |
-
$post_types = get_post_types(array(), 'objects');
|
46 |
-
$exceptions = array('revision', 'nav_menu_item', 'attachment');
|
47 |
-
|
48 |
-
foreach($post_types as $data){
|
49 |
-
$post_type = $data->name;
|
50 |
-
|
51 |
-
if ( in_array($post_type, $exceptions) ){
|
52 |
-
continue;
|
53 |
-
}
|
54 |
-
|
55 |
-
$module_manager->register_virtual_module(
|
56 |
-
$post_type,
|
57 |
-
array(
|
58 |
-
'Name'
|
59 |
-
'ModuleCategory'
|
60 |
-
'ModuleContext'
|
61 |
-
'ModuleClassName' => 'blcAnyPostContainerManager',
|
62 |
-
)
|
63 |
-
);
|
64 |
-
}
|
65 |
-
|
66 |
-
//These hooks update the synch & instance records when posts are added, deleted or modified.
|
67 |
-
add_action('delete_post', array(&$this,'post_deleted'));
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
}
|
79 |
-
|
80 |
-
}
|
81 |
-
|
82 |
-
/**
|
83 |
-
* Retrieve an instance of the overlord class.
|
84 |
-
*
|
85 |
-
* @return blcPostTypeOverlord
|
86 |
-
*/
|
87 |
-
static function getInstance(){
|
88 |
-
static $instance = null;
|
89 |
-
if ( is_null($instance) ){
|
90 |
-
$instance = new blcPostTypeOverlord;
|
91 |
-
$instance->init();
|
92 |
-
}
|
93 |
-
return $instance;
|
94 |
-
}
|
95 |
-
|
96 |
-
/**
|
97 |
-
* Notify the overlord that a post type is active.
|
98 |
-
*
|
99 |
-
* Called by individual instances of blcAnyPostContainerManager to let
|
100 |
-
* the overlord know that they've been created. Since a module instance
|
101 |
-
* is only created if the module is active, this event indicates that
|
102 |
-
* the user has enabled the corresponding post type for link checking.
|
103 |
-
*
|
104 |
-
* @param string $post_type
|
105 |
-
* @return void
|
106 |
-
*/
|
107 |
-
function post_type_enabled($post_type){
|
108 |
-
if ( !in_array($post_type, $this->enabled_post_types) ){
|
109 |
-
$this->enabled_post_types[] = $post_type;
|
110 |
-
}
|
111 |
-
}
|
112 |
-
|
113 |
-
/**
|
114 |
-
* Remove the synch. record and link instances associated with a post when it's deleted
|
115 |
-
*
|
116 |
-
* @param int $post_id
|
117 |
-
* @return void
|
118 |
-
*/
|
119 |
-
function post_deleted($post_id){
|
120 |
-
global $wpdb;
|
121 |
-
|
122 |
-
$post_id = intval( $post_id );
|
123 |
-
//Get the container type matching the type of the deleted post
|
124 |
-
$post = get_post( $post_id );
|
125 |
-
if (
|
126 |
-
return;
|
127 |
-
}
|
128 |
-
//Get the associated container object
|
129 |
-
$post_type
|
130 |
-
$post_container = blcContainerHelper::get_container( array( $post_type, $post_id ) );
|
131 |
-
|
132 |
-
if ( $post_container ){
|
133 |
-
//Delete the container
|
134 |
-
$post_container->delete();
|
135 |
-
|
136 |
-
// Firstly: See if we have any current instances
|
137 |
-
$q_current_instance_ids = $wpdb->prepare(
|
138 |
-
'SELECT instance_id FROM `'
|
139 |
-
$post_id,
|
140 |
-
$post_type
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
if (
|
214 |
-
$blclog->
|
215 |
-
return;
|
216 |
-
}
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
$
|
242 |
-
$
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
$
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
$
|
259 |
-
$
|
260 |
-
$
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
$
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
$blclog->
|
279 |
-
$
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
$
|
294 |
-
$
|
295 |
-
$
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
$
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
//
|
352 |
-
$
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
$
|
360 |
-
}
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
if ( $
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
$link['class']
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
$link['rel']
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
class
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
|
465 |
-
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
-
|
471 |
-
esc_attr(
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
$
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
-
|
611 |
-
|
612 |
-
|
613 |
-
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
|
620 |
-
|
621 |
-
|
622 |
-
|
623 |
-
|
624 |
-
|
625 |
-
|
626 |
-
|
627 |
-
|
628 |
-
|
629 |
-
|
630 |
-
|
631 |
-
|
632 |
-
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
|
638 |
-
|
639 |
-
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
|
646 |
-
|
647 |
-
|
648 |
-
|
649 |
-
|
650 |
-
|
651 |
-
|
652 |
-
|
653 |
-
|
654 |
-
|
655 |
-
|
656 |
-
|
657 |
-
|
658 |
-
if (
|
659 |
-
|
660 |
-
|
661 |
-
return
|
662 |
-
|
663 |
-
|
664 |
-
|
665 |
-
|
666 |
-
|
667 |
-
|
668 |
-
|
669 |
-
|
670 |
-
|
671 |
-
|
672 |
-
|
673 |
-
|
674 |
-
|
675 |
-
|
676 |
-
|
677 |
-
|
678 |
-
|
679 |
-
|
680 |
-
|
681 |
-
|
682 |
-
|
683 |
-
function
|
684 |
-
|
685 |
-
|
686 |
-
|
687 |
-
|
688 |
-
|
689 |
-
|
690 |
-
|
691 |
-
|
692 |
-
|
693 |
-
|
694 |
-
|
695 |
-
|
696 |
-
|
697 |
-
|
698 |
-
|
699 |
-
|
700 |
-
|
701 |
-
|
702 |
-
|
703 |
-
|
704 |
-
|
705 |
-
|
706 |
-
|
707 |
-
|
708 |
-
|
709 |
-
|
710 |
-
|
711 |
-
|
712 |
-
|
713 |
-
|
714 |
-
|
715 |
-
|
716 |
-
|
717 |
-
|
718 |
-
|
719 |
-
|
720 |
-
|
721 |
-
|
722 |
-
|
723 |
-
|
724 |
-
|
725 |
-
|
726 |
-
|
727 |
-
|
728 |
-
|
729 |
-
|
730 |
-
$
|
731 |
-
|
732 |
-
|
733 |
-
|
734 |
-
|
735 |
-
|
736 |
-
|
737 |
-
|
738 |
-
|
739 |
-
|
740 |
-
|
741 |
-
|
742 |
-
|
743 |
-
|
744 |
-
|
745 |
-
|
746 |
-
|
747 |
-
|
748 |
-
|
749 |
-
|
750 |
-
|
751 |
-
|
752 |
-
|
753 |
-
|
754 |
-
|
755 |
-
|
756 |
-
|
757 |
-
|
758 |
-
|
759 |
-
|
760 |
-
|
761 |
-
|
762 |
-
|
763 |
-
|
764 |
-
|
765 |
-
|
766 |
-
|
767 |
-
|
768 |
-
|
769 |
-
|
770 |
-
|
771 |
-
|
772 |
-
|
773 |
-
|
774 |
-
|
775 |
-
|
776 |
-
|
777 |
-
|
778 |
-
|
779 |
-
|
780 |
-
|
781 |
-
|
782 |
-
|
783 |
-
|
784 |
-
|
785 |
-
|
786 |
-
|
787 |
-
|
788 |
-
|
789 |
-
|
790 |
-
|
791 |
-
|
792 |
-
|
793 |
-
|
794 |
-
|
795 |
-
|
796 |
-
|
797 |
-
|
798 |
-
|
799 |
-
|
800 |
-
|
801 |
-
|
802 |
-
|
803 |
-
|
804 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* The manager to rule all (post) managers.
|
5 |
+
*
|
6 |
+
* This class dynamically registers container modules for the available post types
|
7 |
+
* (including custom post types) and does stuff that pertain to all of them, such
|
8 |
+
* as handling save/delete hooks and (re)creating synch records.
|
9 |
+
*
|
10 |
+
* @package Broken Link Checker
|
11 |
+
* @author Janis Elsts
|
12 |
+
* @access private
|
13 |
+
*/
|
14 |
+
class blcPostTypeOverlord {
|
15 |
+
public $enabled_post_types = array(); //Post types currently selected for link checking
|
16 |
+
public $enabled_post_statuses = array( 'publish' ); //Only posts that have one of these statuses shall be checked
|
17 |
+
|
18 |
+
var $plugin_conf;
|
19 |
+
var $resynch_already_done = false;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Class "constructor". Can't use an actual constructor due to how PHP4 handles object references.
|
23 |
+
*
|
24 |
+
* Specifically, this class is a singleton. The function needs to pass $this to several other
|
25 |
+
* functions (to set up hooks), which will store the reference for later use. However, it appears
|
26 |
+
* that in PHP4 the actual value of $this is thrown away right after the constructor finishes, and
|
27 |
+
* `new` returns a *copy* of $this. The result is that getInstance() won't be returning a ref.
|
28 |
+
* to the same object as is used for hook callbacks. And that's horrible.
|
29 |
+
*
|
30 |
+
* Sets up hooks that monitor added/modified/deleted posts and registers
|
31 |
+
* virtual modules for all post types.
|
32 |
+
*
|
33 |
+
* @return void
|
34 |
+
*/
|
35 |
+
function init() {
|
36 |
+
$this->plugin_conf = blc_get_configuration();
|
37 |
+
|
38 |
+
if ( isset( $this->plugin_conf->options['enabled_post_statuses'] ) ) {
|
39 |
+
$this->enabled_post_statuses = $this->plugin_conf->options['enabled_post_statuses'];
|
40 |
+
}
|
41 |
+
|
42 |
+
//Register a virtual container module for each enabled post type
|
43 |
+
$module_manager = blcModuleManager::getInstance();
|
44 |
+
|
45 |
+
$post_types = get_post_types( array(), 'objects' );
|
46 |
+
$exceptions = array( 'revision', 'nav_menu_item', 'attachment' );
|
47 |
+
|
48 |
+
foreach ( $post_types as $data ) {
|
49 |
+
$post_type = $data->name;
|
50 |
+
|
51 |
+
if ( in_array( $post_type, $exceptions ) ) {
|
52 |
+
continue;
|
53 |
+
}
|
54 |
+
|
55 |
+
$module_manager->register_virtual_module(
|
56 |
+
$post_type,
|
57 |
+
array(
|
58 |
+
'Name' => $data->labels->name,
|
59 |
+
'ModuleCategory' => 'container',
|
60 |
+
'ModuleContext' => 'all',
|
61 |
+
'ModuleClassName' => 'blcAnyPostContainerManager',
|
62 |
+
)
|
63 |
+
);
|
64 |
+
}
|
65 |
+
|
66 |
+
//These hooks update the synch & instance records when posts are added, deleted or modified.
|
67 |
+
add_action( 'delete_post', array( &$this, 'post_deleted' ) );
|
68 |
+
add_action( 'save_post', array( &$this, 'post_saved' ) );
|
69 |
+
//We also treat post trashing/untrashing as delete/save.
|
70 |
+
add_action( 'trash_post', array( &$this, 'post_deleted' ) );
|
71 |
+
add_action( 'untrash_post', array( &$this, 'post_saved' ) );
|
72 |
+
|
73 |
+
//Highlight and nofollow broken links in posts & pages
|
74 |
+
if ( $this->plugin_conf->options['mark_broken_links'] || $this->plugin_conf->options['nofollow_broken_links'] ) {
|
75 |
+
add_filter( 'the_content', array( &$this, 'hook_the_content' ) );
|
76 |
+
if ( $this->plugin_conf->options['mark_broken_links'] && ! empty( $this->plugin_conf->options['broken_link_css'] ) ) {
|
77 |
+
add_action( 'wp_head', array( &$this, 'hook_wp_head' ) );
|
78 |
+
}
|
79 |
+
}
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Retrieve an instance of the overlord class.
|
84 |
+
*
|
85 |
+
* @return blcPostTypeOverlord
|
86 |
+
*/
|
87 |
+
static function getInstance() {
|
88 |
+
static $instance = null;
|
89 |
+
if ( is_null( $instance ) ) {
|
90 |
+
$instance = new blcPostTypeOverlord;
|
91 |
+
$instance->init();
|
92 |
+
}
|
93 |
+
return $instance;
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
* Notify the overlord that a post type is active.
|
98 |
+
*
|
99 |
+
* Called by individual instances of blcAnyPostContainerManager to let
|
100 |
+
* the overlord know that they've been created. Since a module instance
|
101 |
+
* is only created if the module is active, this event indicates that
|
102 |
+
* the user has enabled the corresponding post type for link checking.
|
103 |
+
*
|
104 |
+
* @param string $post_type
|
105 |
+
* @return void
|
106 |
+
*/
|
107 |
+
function post_type_enabled( $post_type ) {
|
108 |
+
if ( ! in_array( $post_type, $this->enabled_post_types ) ) {
|
109 |
+
$this->enabled_post_types[] = $post_type;
|
110 |
+
}
|
111 |
+
}
|
112 |
+
|
113 |
+
/**
|
114 |
+
* Remove the synch. record and link instances associated with a post when it's deleted
|
115 |
+
*
|
116 |
+
* @param int $post_id
|
117 |
+
* @return void
|
118 |
+
*/
|
119 |
+
function post_deleted( $post_id ) {
|
120 |
+
global $wpdb;
|
121 |
+
|
122 |
+
$post_id = intval( $post_id );
|
123 |
+
//Get the container type matching the type of the deleted post
|
124 |
+
$post = get_post( $post_id );
|
125 |
+
if ( ! $post ) {
|
126 |
+
return;
|
127 |
+
}
|
128 |
+
//Get the associated container object
|
129 |
+
$post_type = get_post_type( $post );
|
130 |
+
$post_container = blcContainerHelper::get_container( array( $post_type, $post_id ) );
|
131 |
+
|
132 |
+
if ( $post_container ) {
|
133 |
+
//Delete the container
|
134 |
+
$post_container->delete();
|
135 |
+
|
136 |
+
// Firstly: See if we have any current instances
|
137 |
+
$q_current_instance_ids = $wpdb->prepare(
|
138 |
+
'SELECT instance_id FROM `' . $wpdb->prefix . 'blc_instances` WHERE container_id = %d AND container_type = %s',
|
139 |
+
$post_id,
|
140 |
+
$post_type
|
141 |
+
);
|
142 |
+
|
143 |
+
$current_instance_ids_results = $wpdb->get_results( $q_current_instance_ids, ARRAY_A );
|
144 |
+
|
145 |
+
if ( $wpdb->num_rows == 0 ) {
|
146 |
+
// No current instances present, skip cleanup at once
|
147 |
+
return;
|
148 |
+
}
|
149 |
+
|
150 |
+
$current_instance_ids = wp_list_pluck( $current_instance_ids_results, 'instance_id' );
|
151 |
+
|
152 |
+
// Secondly: Get all link_ids used in our current instances
|
153 |
+
$q_current_link_ids = 'SELECT DISTINCT link_id FROM `' . $wpdb->prefix . 'blc_instances` WHERE instance_id IN (\'' . implode( "', '", $current_instance_ids ) . '\')';
|
154 |
+
|
155 |
+
$q_current_link_ids_results = $wpdb->get_results( $q_current_link_ids, ARRAY_A );
|
156 |
+
|
157 |
+
$current_link_ids = wp_list_pluck( $q_current_link_ids_results, 'link_id' );
|
158 |
+
|
159 |
+
// Go ahead and remove blc_instances for this container, blc_cleanup_links( $current_link_ids ) will find and remove any dangling links in the blc_links table
|
160 |
+
$wpdb->query( 'DELETE FROM `' . $wpdb->prefix . 'blc_instances` WHERE instance_id IN (\'' . implode( "', '", $current_instance_ids ) . '\')' );
|
161 |
+
|
162 |
+
//Clean up any dangling links
|
163 |
+
blc_cleanup_links( $current_link_ids );
|
164 |
+
}
|
165 |
+
}
|
166 |
+
|
167 |
+
/**
|
168 |
+
* When a post is saved or modified, mark it as unparsed.
|
169 |
+
*
|
170 |
+
* @param int $post_id
|
171 |
+
* @return void
|
172 |
+
*/
|
173 |
+
function post_saved( $post_id ) {
|
174 |
+
//Get the container type matching the type of the deleted post
|
175 |
+
$post = get_post( $post_id );
|
176 |
+
if ( ! $post ) {
|
177 |
+
return;
|
178 |
+
}
|
179 |
+
|
180 |
+
//Only check links in currently enabled post types
|
181 |
+
if ( ! in_array( $post->post_type, $this->enabled_post_types ) ) {
|
182 |
+
return;
|
183 |
+
}
|
184 |
+
|
185 |
+
//Only check posts that have one of the allowed statuses
|
186 |
+
if ( ! in_array( $post->post_status, $this->enabled_post_statuses ) ) {
|
187 |
+
return;
|
188 |
+
}
|
189 |
+
|
190 |
+
//Get the container & mark it as unparsed
|
191 |
+
$args = array( $post->post_type, intval( $post_id ) );
|
192 |
+
$post_container = blcContainerHelper::get_container( $args );
|
193 |
+
|
194 |
+
$post_container->mark_as_unsynched();
|
195 |
+
}
|
196 |
+
|
197 |
+
|
198 |
+
/**
|
199 |
+
* Create or update synchronization records for all posts.
|
200 |
+
*
|
201 |
+
* @param string $container_type
|
202 |
+
* @param bool $forced If true, assume that all synch. records are gone and will need to be recreated from scratch.
|
203 |
+
* @return void
|
204 |
+
*/
|
205 |
+
function resynch( $container_type = '', $forced = false ) {
|
206 |
+
global $wpdb; /** @var wpdb $wpdb */
|
207 |
+
global $blclog;
|
208 |
+
|
209 |
+
//Resynch is expensive in terms of DB performance. Thus we only do it once, processing
|
210 |
+
//all post types in one go and ignoring any further resynch requests during this pageload.
|
211 |
+
//BUG: This might be a problem if there ever is an actual need to run resynch twice or
|
212 |
+
//more per pageload.
|
213 |
+
if ( $this->resynch_already_done ) {
|
214 |
+
$blclog->log( sprintf( '...... Skipping "%s" resyncyh since all post types were already synched.', $container_type ) );
|
215 |
+
return;
|
216 |
+
}
|
217 |
+
|
218 |
+
if ( empty( $this->enabled_post_types ) ) {
|
219 |
+
$blclog->warn( sprintf( '...... Skipping "%s" resyncyh since no post types are enabled.', $container_type ) );
|
220 |
+
return;
|
221 |
+
}
|
222 |
+
|
223 |
+
$escaped_post_types = array_map( 'esc_sql', $this->enabled_post_types );
|
224 |
+
$escaped_post_statuses = array_map( 'esc_sql', $this->enabled_post_statuses );
|
225 |
+
|
226 |
+
if ( $forced ) {
|
227 |
+
//Create new synchronization records for all posts.
|
228 |
+
$blclog->log( '...... Creating synch records for these post types: ' . implode( ', ', $escaped_post_types ) . ' that have one of these statuses: ' . implode( ', ', $escaped_post_statuses ) );
|
229 |
+
$start = microtime( true );
|
230 |
+
$q = "INSERT INTO {$wpdb->prefix}blc_synch(container_id, container_type, synched)
|
231 |
+
SELECT posts.id, posts.post_type, 0
|
232 |
+
FROM {$wpdb->posts} AS posts
|
233 |
+
WHERE
|
234 |
+
posts.post_status IN (%s)
|
235 |
+
AND posts.post_type IN (%s)";
|
236 |
+
$q = sprintf(
|
237 |
+
$q,
|
238 |
+
"'" . implode( "', '", $escaped_post_statuses ) . "'",
|
239 |
+
"'" . implode( "', '", $escaped_post_types ) . "'"
|
240 |
+
);
|
241 |
+
$wpdb->query( $q );
|
242 |
+
$blclog->log( sprintf( '...... %d rows inserted in %.3f seconds', $wpdb->rows_affected, microtime( true ) - $start ) );
|
243 |
+
} else {
|
244 |
+
//Delete synch records corresponding to posts that no longer exist.
|
245 |
+
$blclog->log( '...... Deleting synch records for removed posts' );
|
246 |
+
$start = microtime( true );
|
247 |
+
$q = "DELETE synch.*
|
248 |
+
FROM
|
249 |
+
{$wpdb->prefix}blc_synch AS synch LEFT JOIN {$wpdb->posts} AS posts
|
250 |
+
ON posts.ID = synch.container_id
|
251 |
+
WHERE
|
252 |
+
synch.container_type IN (%s) AND posts.ID IS NULL";
|
253 |
+
$q = sprintf(
|
254 |
+
$q,
|
255 |
+
"'" . implode( "', '", $escaped_post_types ) . "'"
|
256 |
+
);
|
257 |
+
$wpdb->query( $q );
|
258 |
+
$elapsed = microtime( true ) - $start;
|
259 |
+
$blclog->debug( $q );
|
260 |
+
$blclog->log( sprintf( '...... %d rows deleted in %.3f seconds', $wpdb->rows_affected, $elapsed ) );
|
261 |
+
|
262 |
+
//Delete records where the post status is not one of the enabled statuses.
|
263 |
+
$blclog->log( '...... Deleting synch records for posts that have a disallowed status' );
|
264 |
+
$start = microtime( true );
|
265 |
+
$q = "DELETE synch.*
|
266 |
+
FROM
|
267 |
+
{$wpdb->prefix}blc_synch AS synch
|
268 |
+
LEFT JOIN {$wpdb->posts} AS posts
|
269 |
+
ON (synch.container_id = posts.ID and synch.container_type = posts.post_type)
|
270 |
+
WHERE
|
271 |
+
posts.post_status NOT IN (%s)";
|
272 |
+
$q = sprintf(
|
273 |
+
$q,
|
274 |
+
"'" . implode( "', '", $escaped_post_statuses ) . "'"
|
275 |
+
);
|
276 |
+
$wpdb->query( $q );
|
277 |
+
$elapsed = microtime( true ) - $start;
|
278 |
+
$blclog->debug( $q );
|
279 |
+
$blclog->log( sprintf( '...... %d rows deleted in %.3f seconds', $wpdb->rows_affected, $elapsed ) );
|
280 |
+
|
281 |
+
//Remove the 'synched' flag from all posts that have been updated
|
282 |
+
//since the last time they were parsed/synchronized.
|
283 |
+
$blclog->log( '...... Marking changed posts as unsynched' );
|
284 |
+
$start = microtime( true );
|
285 |
+
$q = "UPDATE
|
286 |
+
{$wpdb->prefix}blc_synch AS synch
|
287 |
+
JOIN {$wpdb->posts} AS posts ON (synch.container_id = posts.ID and synch.container_type=posts.post_type)
|
288 |
+
SET
|
289 |
+
synched = 0
|
290 |
+
WHERE
|
291 |
+
synch.last_synch < posts.post_modified";
|
292 |
+
$wpdb->query( $q );
|
293 |
+
$elapsed = microtime( true ) - $start;
|
294 |
+
$blclog->debug( $q );
|
295 |
+
$blclog->log( sprintf( '...... %d rows updated in %.3f seconds', $wpdb->rows_affected, $elapsed ) );
|
296 |
+
|
297 |
+
//Create synch. records for posts that don't have them.
|
298 |
+
$blclog->log( '...... Creating synch records for new posts' );
|
299 |
+
$start = microtime( true );
|
300 |
+
$q = "INSERT INTO {$wpdb->prefix}blc_synch(container_id, container_type, synched)
|
301 |
+
SELECT posts.id, posts.post_type, 0
|
302 |
+
FROM
|
303 |
+
{$wpdb->posts} AS posts LEFT JOIN {$wpdb->prefix}blc_synch AS synch
|
304 |
+
ON (synch.container_id = posts.ID and synch.container_type=posts.post_type)
|
305 |
+
WHERE
|
306 |
+
posts.post_status IN (%s)
|
307 |
+
AND posts.post_type IN (%s)
|
308 |
+
AND synch.container_id IS NULL";
|
309 |
+
$q = sprintf(
|
310 |
+
$q,
|
311 |
+
"'" . implode( "', '", $escaped_post_statuses ) . "'",
|
312 |
+
"'" . implode( "', '", $escaped_post_types ) . "'"
|
313 |
+
);
|
314 |
+
$wpdb->query( $q );
|
315 |
+
$elapsed = microtime( true ) - $start;
|
316 |
+
$blclog->debug( $q );
|
317 |
+
$blclog->log( sprintf( '...... %d rows inserted in %.3f seconds', $wpdb->rows_affected, $elapsed ) );
|
318 |
+
}
|
319 |
+
|
320 |
+
$this->resynch_already_done = true;
|
321 |
+
}
|
322 |
+
|
323 |
+
/**
|
324 |
+
* Hook for the 'the_content' filter. Scans the current post and adds the 'broken_link'
|
325 |
+
* CSS class to all links that are known to be broken. Currently works only on standard
|
326 |
+
* HTML links (i.e. the '<a href=...' kind).
|
327 |
+
*
|
328 |
+
* @param string $content Post content
|
329 |
+
* @return string Modified post content.
|
330 |
+
*/
|
331 |
+
function hook_the_content( $content ) {
|
332 |
+
global $post, $wpdb; /** @var wpdb $wpdb */
|
333 |
+
if ( empty( $post ) || ! in_array( $post->post_type, $this->enabled_post_types ) ) {
|
334 |
+
return $content;
|
335 |
+
}
|
336 |
+
|
337 |
+
//Retrieve info about all occurrences of broken links in the current post
|
338 |
+
$q = "
|
339 |
+
SELECT instances.raw_url
|
340 |
+
FROM {$wpdb->prefix}blc_instances AS instances JOIN {$wpdb->prefix}blc_links AS links
|
341 |
+
ON instances.link_id = links.link_id
|
342 |
+
WHERE
|
343 |
+
instances.container_type = %s
|
344 |
+
AND instances.container_id = %d
|
345 |
+
AND links.broken = 1
|
346 |
+
AND parser_type = 'link'
|
347 |
+
";
|
348 |
+
$q = $wpdb->prepare( $q, $post->post_type, $post->ID );
|
349 |
+
$links = $wpdb->get_results( $q, ARRAY_A );
|
350 |
+
|
351 |
+
//Return the content unmodified if there are no broken links in this post.
|
352 |
+
if ( empty( $links ) || ! is_array( $links ) ) {
|
353 |
+
return $content;
|
354 |
+
}
|
355 |
+
|
356 |
+
//Put the broken link URLs in an array
|
357 |
+
$broken_link_urls = array();
|
358 |
+
foreach ( $links as $link ) {
|
359 |
+
$broken_link_urls[] = $link['raw_url'];
|
360 |
+
}
|
361 |
+
|
362 |
+
//Iterate over all HTML links and modify the broken ones
|
363 |
+
$parser = blcParserHelper::get_parser( 'link' );
|
364 |
+
if ( $parser ) {
|
365 |
+
$content = $parser->multi_edit( $content, array( &$this, 'highlight_broken_link' ), $broken_link_urls );
|
366 |
+
}
|
367 |
+
|
368 |
+
return $content;
|
369 |
+
}
|
370 |
+
|
371 |
+
/**
|
372 |
+
* Analyse a link and add 'broken_link' CSS class if the link is broken.
|
373 |
+
*
|
374 |
+
* @see blcHtmlLink::multi_edit()
|
375 |
+
*
|
376 |
+
* @param array $link Associative array of link data.
|
377 |
+
* @param array $broken_link_urls List of broken link URLs present in the current post.
|
378 |
+
* @return array|string The modified link
|
379 |
+
*/
|
380 |
+
function highlight_broken_link( $link, $broken_link_urls ) {
|
381 |
+
if ( ! in_array( $link['href'], $broken_link_urls ) ) {
|
382 |
+
//Link not broken = return the original link tag
|
383 |
+
return $link['#raw'];
|
384 |
+
}
|
385 |
+
|
386 |
+
//Add 'broken_link' to the 'class' attribute (unless already present).
|
387 |
+
if ( $this->plugin_conf->options['mark_broken_links'] ) {
|
388 |
+
if ( isset( $link['class'] ) ) {
|
389 |
+
$classes = explode( ' ', $link['class'] );
|
390 |
+
if ( ! in_array( 'broken_link', $classes ) ) {
|
391 |
+
$classes[] = 'broken_link';
|
392 |
+
$link['class'] = implode( ' ', $classes );
|
393 |
+
}
|
394 |
+
} else {
|
395 |
+
$link['class'] = 'broken_link';
|
396 |
+
}
|
397 |
+
}
|
398 |
+
|
399 |
+
//Nofollow the link (unless it's already nofollow'ed)
|
400 |
+
if ( $this->plugin_conf->options['nofollow_broken_links'] ) {
|
401 |
+
if ( isset( $link['rel'] ) ) {
|
402 |
+
$relations = explode( ' ', $link['rel'] );
|
403 |
+
if ( ! in_array( 'nofollow', $relations ) ) {
|
404 |
+
$relations[] = 'nofollow';
|
405 |
+
$link['rel'] = implode( ' ', $relations );
|
406 |
+
}
|
407 |
+
} else {
|
408 |
+
$link['rel'] = 'nofollow';
|
409 |
+
}
|
410 |
+
}
|
411 |
+
|
412 |
+
return $link;
|
413 |
+
}
|
414 |
+
|
415 |
+
/**
|
416 |
+
* A hook for the 'wp_head' action. Outputs the user-defined broken link CSS.
|
417 |
+
*
|
418 |
+
* @return void
|
419 |
+
*/
|
420 |
+
function hook_wp_head() {
|
421 |
+
echo '<style type="text/css">',$this->plugin_conf->options['broken_link_css'],'</style>';
|
422 |
+
}
|
423 |
+
}
|
424 |
+
|
425 |
+
//Start up the post overlord
|
426 |
+
blcPostTypeOverlord::getInstance();
|
427 |
+
|
428 |
+
|
429 |
+
/**
|
430 |
+
* Universal container item class used for all post types.
|
431 |
+
*
|
432 |
+
* @package Broken Link Checker
|
433 |
+
* @author Janis Elsts
|
434 |
+
* @access public
|
435 |
+
*/
|
436 |
+
class blcAnyPostContainer extends blcContainer {
|
437 |
+
var $default_field = 'post_content';
|
438 |
+
|
439 |
+
/**
|
440 |
+
* Get action links for this post.
|
441 |
+
*
|
442 |
+
* @param string $container_field Ignored.
|
443 |
+
* @return array of action link HTML.
|
444 |
+
*/
|
445 |
+
function ui_get_action_links( $container_field = '' ) {
|
446 |
+
$actions = array();
|
447 |
+
|
448 |
+
//Fetch the post (it should be cached already)
|
449 |
+
$post = $this->get_wrapped_object();
|
450 |
+
if ( ! $post ) {
|
451 |
+
return $actions;
|
452 |
+
}
|
453 |
+
|
454 |
+
$post_type_object = get_post_type_object( $post->post_type );
|
455 |
+
|
456 |
+
//Each post type can have its own cap requirements
|
457 |
+
if ( current_user_can( $post_type_object->cap->edit_post, $this->container_id ) ) {
|
458 |
+
$actions['edit'] = sprintf(
|
459 |
+
'<span class="edit"><a href="%s" title="%s">%s</a>',
|
460 |
+
$this->get_edit_url(),
|
461 |
+
$post_type_object->labels->edit_item,
|
462 |
+
__( 'Edit' )
|
463 |
+
);
|
464 |
+
|
465 |
+
//Trash/Delete link
|
466 |
+
if ( current_user_can( $post_type_object->cap->delete_post, $this->container_id ) ) {
|
467 |
+
if ( $this->can_be_trashed() ) {
|
468 |
+
$actions['trash'] = sprintf(
|
469 |
+
"<span class='trash'><a class='submitdelete' title='%s' href='%s'>%s</a>",
|
470 |
+
esc_attr( __( 'Move this item to the Trash' ) ),
|
471 |
+
esc_attr( get_delete_post_link( $this->container_id, '', false ) ),
|
472 |
+
__( 'Trash' )
|
473 |
+
);
|
474 |
+
} else {
|
475 |
+
$actions['delete'] = sprintf(
|
476 |
+
"<span><a class='submitdelete' title='%s' href='%s'>%s</a>",
|
477 |
+
esc_attr( __( 'Delete this item permanently' ) ),
|
478 |
+
esc_attr( get_delete_post_link( $this->container_id, '', true ) ),
|
479 |
+
__( 'Delete' )
|
480 |
+
);
|
481 |
+
}
|
482 |
+
}
|
483 |
+
}
|
484 |
+
|
485 |
+
//View/Preview link
|
486 |
+
$title = get_the_title( $this->container_id );
|
487 |
+
if ( in_array( $post->post_status, array( 'pending', 'draft' ) ) ) {
|
488 |
+
if ( current_user_can( $post_type_object->cap->edit_post, $this->container_id ) ) {
|
489 |
+
$actions['view'] = sprintf(
|
490 |
+
'<span class="view"><a href="%s" title="%s" rel="permalink">%s</a>',
|
491 |
+
esc_url( add_query_arg( 'preview', 'true', get_permalink( $this->container_id ) ) ),
|
492 |
+
esc_attr( sprintf( __( 'Preview “%s”' ), $title ) ),
|
493 |
+
__( 'Preview' )
|
494 |
+
);
|
495 |
+
}
|
496 |
+
} elseif ( 'trash' != $post->post_status ) {
|
497 |
+
$actions['view'] = sprintf(
|
498 |
+
'<span class="view"><a href="%s" title="%s" rel="permalink">%s</a>',
|
499 |
+
esc_url( get_permalink( $this->container_id ) ),
|
500 |
+
esc_attr( sprintf( __( 'View “%s”' ), $title ) ),
|
501 |
+
__( 'View' )
|
502 |
+
);
|
503 |
+
}
|
504 |
+
|
505 |
+
return $actions;
|
506 |
+
}
|
507 |
+
|
508 |
+
/**
|
509 |
+
* Get the HTML for displaying the post title in the "Source" column.
|
510 |
+
*
|
511 |
+
* @param string $container_field Ignored.
|
512 |
+
* @param string $context How to filter the output. Optional, defaults to 'display'.
|
513 |
+
* @return string HTML
|
514 |
+
*/
|
515 |
+
function ui_get_source( $container_field = '', $context = 'display' ) {
|
516 |
+
$source = '<a class="row-title" href="%s" title="%s">%s</a>';
|
517 |
+
$source = sprintf(
|
518 |
+
$source,
|
519 |
+
$this->get_edit_url(),
|
520 |
+
esc_attr( __( 'Edit this item' ) ),
|
521 |
+
get_the_title( $this->container_id )
|
522 |
+
);
|
523 |
+
|
524 |
+
return $source;
|
525 |
+
}
|
526 |
+
|
527 |
+
/**
|
528 |
+
* Get edit URL for this container. Returns the URL of the Dashboard page where the item
|
529 |
+
* associated with this container can be edited.
|
530 |
+
*
|
531 |
+
* @access protected
|
532 |
+
*
|
533 |
+
* @return string
|
534 |
+
*/
|
535 |
+
function get_edit_url() {
|
536 |
+
/*
|
537 |
+
The below is a near-exact copy of the get_post_edit_link() function.
|
538 |
+
Unfortunately we can't just call that function because it has a hardcoded
|
539 |
+
caps-check which fails when called from the email notification script
|
540 |
+
executed by Cron.
|
541 |
+
*/
|
542 |
+
$post = $this->get_wrapped_object();
|
543 |
+
if ( ! $post ) {
|
544 |
+
return '';
|
545 |
+
}
|
546 |
+
|
547 |
+
$context = 'display';
|
548 |
+
$action = '&action=edit';
|
549 |
+
|
550 |
+
$post_type_object = get_post_type_object( $post->post_type );
|
551 |
+
if ( ! $post_type_object ) {
|
552 |
+
return '';
|
553 |
+
}
|
554 |
+
|
555 |
+
return apply_filters( 'get_edit_post_link', admin_url( sprintf( $post_type_object->_edit_link . $action, $post->ID ) ), $post->ID, $context );
|
556 |
+
}
|
557 |
+
|
558 |
+
/**
|
559 |
+
* Retrieve the post associated with this container.
|
560 |
+
*
|
561 |
+
* @access protected
|
562 |
+
*
|
563 |
+
* @param bool $ensure_consistency Set this to true to ignore the cached $wrapped_object value and retrieve an up-to-date copy of the wrapped object from the DB (or WP's internal cache).
|
564 |
+
* @return object Post data.
|
565 |
+
*/
|
566 |
+
function get_wrapped_object( $ensure_consistency = false ) {
|
567 |
+
if ( $ensure_consistency || is_null( $this->wrapped_object ) ) {
|
568 |
+
$this->wrapped_object = get_post( $this->container_id );
|
569 |
+
}
|
570 |
+
return $this->wrapped_object;
|
571 |
+
}
|
572 |
+
|
573 |
+
/**
|
574 |
+
* Update the post associated with this container.
|
575 |
+
*
|
576 |
+
* @access protected
|
577 |
+
*
|
578 |
+
* @return bool|WP_Error True on success, an error if something went wrong.
|
579 |
+
*/
|
580 |
+
function update_wrapped_object() {
|
581 |
+
if ( is_null( $this->wrapped_object ) ) {
|
582 |
+
return new WP_Error(
|
583 |
+
'no_wrapped_object',
|
584 |
+
__( 'Nothing to update', 'broken-link-checker' )
|
585 |
+
);
|
586 |
+
}
|
587 |
+
|
588 |
+
$post_id = wp_update_post( $this->wrapped_object, true );
|
589 |
+
if ( is_wp_error( $post_id ) ) {
|
590 |
+
return $post_id;
|
591 |
+
} elseif ( $post_id == 0 ) {
|
592 |
+
return new WP_Error(
|
593 |
+
'update_failed',
|
594 |
+
sprintf( __( 'Updating post %d failed', 'broken-link-checker' ), $this->container_id )
|
595 |
+
);
|
596 |
+
} else {
|
597 |
+
return true;
|
598 |
+
}
|
599 |
+
}
|
600 |
+
|
601 |
+
/**
|
602 |
+
* Get the base URL of the container. For posts, the post permalink is used
|
603 |
+
* as the base URL when normalizing relative links.
|
604 |
+
*
|
605 |
+
* @return string
|
606 |
+
*/
|
607 |
+
function base_url() {
|
608 |
+
return get_permalink( $this->container_id );
|
609 |
+
}
|
610 |
+
|
611 |
+
/**
|
612 |
+
* Delete or trash the post corresponding to this container.
|
613 |
+
* Will always move to trash instead of deleting if trash is enabled.
|
614 |
+
*
|
615 |
+
* @return bool|WP_error
|
616 |
+
*/
|
617 |
+
function delete_wrapped_object() {
|
618 |
+
//Note that we don't need to delete the synch record and instances here -
|
619 |
+
//wp_delete_post()/wp_trash_post() will run the post_delete/trash hook,
|
620 |
+
//which will be caught by blcPostContainerManager, which will in turn
|
621 |
+
//delete anything that needs to be deleted.
|
622 |
+
if ( EMPTY_TRASH_DAYS ) {
|
623 |
+
return $this->trash_wrapped_object();
|
624 |
+
} else {
|
625 |
+
if ( wp_delete_post( $this->container_id, true ) ) {
|
626 |
+
return true;
|
627 |
+
} else {
|
628 |
+
return new WP_Error(
|
629 |
+
'delete_failed',
|
630 |
+
sprintf(
|
631 |
+
__( 'Failed to delete post "%1$s" (%2$d)', 'broken-link-checker' ),
|
632 |
+
get_the_title( $this->container_id ),
|
633 |
+
$this->container_id
|
634 |
+
)
|
635 |
+
);
|
636 |
+
}
|
637 |
+
}
|
638 |
+
}
|
639 |
+
|
640 |
+
/**
|
641 |
+
* Move the post corresponding to this container to the Trash.
|
642 |
+
*
|
643 |
+
* @return bool|WP_Error
|
644 |
+
*/
|
645 |
+
function trash_wrapped_object() {
|
646 |
+
if ( ! EMPTY_TRASH_DAYS ) {
|
647 |
+
return new WP_Error(
|
648 |
+
'trash_disabled',
|
649 |
+
sprintf(
|
650 |
+
__( 'Can\'t move post "%1$s" (%2$d) to the trash because the trash feature is disabled', 'broken-link-checker' ),
|
651 |
+
get_the_title( $this->container_id ),
|
652 |
+
$this->container_id
|
653 |
+
)
|
654 |
+
);
|
655 |
+
}
|
656 |
+
|
657 |
+
$post = get_post( $this->container_id );
|
658 |
+
if ( $post->post_status == 'trash' ) {
|
659 |
+
//Prevent conflicts between post and custom field containers trying to trash the same post.
|
660 |
+
//BUG: Post and custom field containers shouldn't wrap the same object
|
661 |
+
return true;
|
662 |
+
}
|
663 |
+
|
664 |
+
if ( wp_trash_post( $this->container_id ) ) {
|
665 |
+
return true;
|
666 |
+
} else {
|
667 |
+
return new WP_Error(
|
668 |
+
'trash_failed',
|
669 |
+
sprintf(
|
670 |
+
__( 'Failed to move post "%1$s" (%2$d) to the trash', 'broken-link-checker' ),
|
671 |
+
get_the_title( $this->container_id ),
|
672 |
+
$this->container_id
|
673 |
+
)
|
674 |
+
);
|
675 |
+
}
|
676 |
+
}
|
677 |
+
|
678 |
+
/**
|
679 |
+
* Check if the current user can delete/trash this post.
|
680 |
+
*
|
681 |
+
* @return bool
|
682 |
+
*/
|
683 |
+
function current_user_can_delete() {
|
684 |
+
$post = $this->get_wrapped_object();
|
685 |
+
$post_type_object = get_post_type_object( $post->post_type );
|
686 |
+
return current_user_can( $post_type_object->cap->delete_post, $this->container_id );
|
687 |
+
}
|
688 |
+
|
689 |
+
function can_be_trashed() {
|
690 |
+
return defined( 'EMPTY_TRASH_DAYS' ) && EMPTY_TRASH_DAYS;
|
691 |
+
}
|
692 |
+
}
|
693 |
+
|
694 |
+
|
695 |
+
|
696 |
+
/**
|
697 |
+
* Universal manager usable for most post types.
|
698 |
+
*
|
699 |
+
* @package Broken Link Checker
|
700 |
+
* @access public
|
701 |
+
*/
|
702 |
+
class blcAnyPostContainerManager extends blcContainerManager {
|
703 |
+
var $container_class_name = 'blcAnyPostContainer';
|
704 |
+
var $fields = array( 'post_content' => 'html' );
|
705 |
+
|
706 |
+
function init() {
|
707 |
+
parent::init();
|
708 |
+
|
709 |
+
//Notify the overlord that the post/container type that this instance is
|
710 |
+
//responsible for is enabled.
|
711 |
+
$overlord = blcPostTypeOverlord::getInstance();
|
712 |
+
$overlord->post_type_enabled( $this->container_type );
|
713 |
+
}
|
714 |
+
|
715 |
+
/**
|
716 |
+
* Instantiate multiple containers of the container type managed by this class.
|
717 |
+
*
|
718 |
+
* @param array $containers Array of assoc. arrays containing container data.
|
719 |
+
* @param string $purpose An optional code indicating how the retrieved containers will be used.
|
720 |
+
* @param bool $load_wrapped_objects Preload wrapped objects regardless of purpose.
|
721 |
+
*
|
722 |
+
* @return array of blcPostContainer indexed by "container_type|container_id"
|
723 |
+
*/
|
724 |
+
function get_containers( $containers, $purpose = '', $load_wrapped_objects = false ) {
|
725 |
+
$containers = $this->make_containers( $containers );
|
726 |
+
|
727 |
+
//Preload post data if it is likely to be useful later
|
728 |
+
$preload = $load_wrapped_objects || in_array( $purpose, array( BLC_FOR_DISPLAY, BLC_FOR_PARSING ) );
|
729 |
+
if ( $preload ) {
|
730 |
+
$post_ids = array();
|
731 |
+
foreach ( $containers as $container ) {
|
732 |
+
$post_ids[] = $container->container_id;
|
733 |
+
}
|
734 |
+
|
735 |
+
$args = array( 'include' => implode( ',', $post_ids ) );
|
736 |
+
$posts = get_posts( $args );
|
737 |
+
|
738 |
+
foreach ( $posts as $post ) {
|
739 |
+
$key = $this->container_type . '|' . $post->ID;
|
740 |
+
if ( isset( $containers[ $key ] ) ) {
|
741 |
+
$containers[ $key ]->wrapped_object = $post;
|
742 |
+
}
|
743 |
+
}
|
744 |
+
}
|
745 |
+
|
746 |
+
return $containers;
|
747 |
+
}
|
748 |
+
|
749 |
+
/**
|
750 |
+
* Create or update synchronization records for all posts.
|
751 |
+
*
|
752 |
+
* @param bool $forced If true, assume that all synch. records are gone and will need to be recreated from scratch.
|
753 |
+
* @return void
|
754 |
+
*/
|
755 |
+
function resynch( $forced = false ) {
|
756 |
+
$overlord = blcPostTypeOverlord::getInstance();
|
757 |
+
$overlord->resynch( $this->container_type, $forced );
|
758 |
+
}
|
759 |
+
|
760 |
+
/**
|
761 |
+
* Get the message to display after $n posts have been deleted.
|
762 |
+
*
|
763 |
+
* @param int $n Number of deleted posts.
|
764 |
+
* @return string A delete confirmation message, e.g. "5 posts were moved deleted"
|
765 |
+
*/
|
766 |
+
function ui_bulk_delete_message( $n ) {
|
767 |
+
//Since the "Trash" feature has been introduced, calling wp_delete_post
|
768 |
+
//doesn't actually delete the post (unless you set force_delete to True),
|
769 |
+
//just moves it to the trash. So we pick the message accordingly.
|
770 |
+
//(If possible, BLC *always* moves to trash instead of deleting permanently.)
|
771 |
+
if ( function_exists( 'wp_trash_post' ) && EMPTY_TRASH_DAYS ) {
|
772 |
+
return blcAnyPostContainerManager::ui_bulk_trash_message( $n );
|
773 |
+
} else {
|
774 |
+
$post_type_object = get_post_type_object( $this->container_type );
|
775 |
+
$type_name = '';
|
776 |
+
|
777 |
+
if ( $this->container_type == 'post' || is_null( $post_type_object ) ) {
|
778 |
+
$delete_msg = _n( '%d post deleted.', '%d posts deleted.', $n, 'broken-link-checker' );
|
779 |
+
} elseif ( $this->container_type == 'page' ) {
|
780 |
+
$delete_msg = _n( '%d page deleted.', '%d pages deleted.', $n, 'broken-link-checker' );
|
781 |
+
} else {
|
782 |
+
$delete_msg = _n( '%1$d "%2$s" deleted.', '%1$d "%2$s" deleted.', $n, 'broken-link-checker' );
|
783 |
+
$type_name = ( $n == 1 ? $post_type_object->labels->singular_name : $post_type_object->labels->name );
|
784 |
+
}
|
785 |
+
return sprintf( $delete_msg, $n, $type_name );
|
786 |
+
}
|
787 |
+
}
|
788 |
+
|
789 |
+
|
790 |
+
/**
|
791 |
+
* Get the message to display after $n posts have been trashed.
|
792 |
+
*
|
793 |
+
* @param int $n Number of deleted posts.
|
794 |
+
* @return string A confirmation message, e.g. "5 posts were moved to trash"
|
795 |
+
*/
|
796 |
+
function ui_bulk_trash_message( $n ) {
|
797 |
+
$post_type_object = get_post_type_object( $this->container_type );
|
798 |
+
$type_name = '';
|
799 |
+
|
800 |
+
if ( $this->container_type == 'post' || is_null( $post_type_object ) ) {
|
801 |
+
$delete_msg = _n( '%d post moved to the Trash.', '%d posts moved to the Trash.', $n, 'broken-link-checker' );
|
802 |
+
} elseif ( $this->container_type == 'page' ) {
|
803 |
+
$delete_msg = _n( '%d page moved to the Trash.', '%d pages moved to the Trash.', $n, 'broken-link-checker' );
|
804 |
+
} else {
|
805 |
+
$delete_msg = _n( '%1$d "%2$s" moved to the Trash.', '%1$d "%2$s" moved to the Trash.', $n, 'broken-link-checker' );
|
806 |
+
$type_name = ( $n == 1 ? $post_type_object->labels->singular_name : $post_type_object->labels->name );
|
807 |
+
}
|
808 |
+
return sprintf( $delete_msg, $n, $type_name );
|
809 |
+
}
|
810 |
+
}
|
includes/checkers.php
CHANGED
@@ -1,120 +1,120 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Base class for link checking algorithms.
|
5 |
-
*
|
6 |
-
* All link checkering algorithms should extend this class.
|
7 |
-
*
|
8 |
-
* @package Broken Link Checker
|
9 |
-
* @access public
|
10 |
-
*/
|
11 |
-
class blcChecker extends blcModule {
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
var $priority = -100;
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
function can_check($url, $parsed_url){
|
37 |
-
return false;
|
38 |
-
}
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
function check($url){
|
62 |
-
trigger_error('Function blcChecker::check() must be over-ridden in a subclass', E_USER_ERROR);
|
63 |
-
}
|
64 |
-
}
|
65 |
-
|
66 |
-
class blcCheckerHelper {
|
67 |
-
|
68 |
-
/**
|
69 |
-
* Get a reference to a specific checker.
|
70 |
-
*
|
71 |
-
* @uses blcModuleManager::get_module()
|
72 |
-
*
|
73 |
-
* @param string $checker_id
|
74 |
-
* @return blcChecker
|
75 |
-
*/
|
76 |
-
static function get_checker($checker_id){
|
77 |
-
$manager = blcModuleManager::getInstance();
|
78 |
-
return $manager->get_module($checker_id, true, 'checker');
|
79 |
-
}
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
static function get_checker_for($url){
|
88 |
-
$parsed = @parse_url($url);
|
89 |
-
|
90 |
-
$manager
|
91 |
-
$active_checkers = $manager->get_active_by_category('checker');
|
92 |
-
|
93 |
-
foreach($active_checkers as $module_id => $module_data){
|
94 |
-
//Try the URL pattern in the header first. If it doesn't match,
|
95 |
-
//we can avoid loading the module altogether.
|
96 |
-
if ( !empty($module_data['ModuleCheckerUrlPattern']) ){
|
97 |
-
if ( !preg_match($module_data['ModuleCheckerUrlPattern'], $url) ){
|
98 |
-
continue;
|
99 |
-
}
|
100 |
-
}
|
101 |
-
|
102 |
-
$checker = $manager->get_module($module_id);
|
103 |
-
|
104 |
-
if (
|
105 |
-
continue;
|
106 |
-
}
|
107 |
-
|
108 |
-
//The can_check() method can perform more sophisticated filtering,
|
109 |
-
//or just return true if the checker thinks matching the URL regex
|
110 |
-
//is sufficient.
|
111 |
-
if ( $checker->can_check($url, $parsed) ){
|
112 |
-
return $checker;
|
113 |
-
}
|
114 |
-
}
|
115 |
-
|
116 |
-
$checker = null;
|
117 |
-
return $checker;
|
118 |
-
}
|
119 |
-
}
|
120 |
-
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Base class for link checking algorithms.
|
5 |
+
*
|
6 |
+
* All link checkering algorithms should extend this class.
|
7 |
+
*
|
8 |
+
* @package Broken Link Checker
|
9 |
+
* @access public
|
10 |
+
*/
|
11 |
+
class blcChecker extends blcModule {
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Priority determines the order in which the plugin will try all registered checkers
|
15 |
+
* when looking for one that can check a particular URL. Registered checkers will be
|
16 |
+
* tried in order, from highest to lowest priority, and the first one that returns
|
17 |
+
* true when its can_check() method is called will be used.
|
18 |
+
*
|
19 |
+
* Checker implementations should set their priority depending on how specific they are
|
20 |
+
* in choosing the URLs that they check.
|
21 |
+
*
|
22 |
+
* -10 .. 10 : checks all URLs that have a certain protocol, e.g. all HTTP URLs.
|
23 |
+
* 11 .. 100 : checks only URLs from a restricted number of domains, e.g. video site URLs.
|
24 |
+
* 100+ : checks only certain URLs from a certain domain, e.g. YouTube video links.
|
25 |
+
*
|
26 |
+
*/
|
27 |
+
var $priority = -100;
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Check if this checker knows how to check a particular URL.
|
31 |
+
*
|
32 |
+
* @param string $url
|
33 |
+
* @param array|bool $parsed_url The result of parsing $url with parse_url(). See PHP docs for details.
|
34 |
+
* @return bool
|
35 |
+
*/
|
36 |
+
function can_check( $url, $parsed_url ) {
|
37 |
+
return false;
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Check an URL.
|
42 |
+
*
|
43 |
+
* This method returns an associative array containing results of
|
44 |
+
* the check. The following array keys are recognized by the plugin and
|
45 |
+
* their values will be stored in the link's DB record :
|
46 |
+
* 'broken' (bool) - True if the URL points to a missing/broken page. Required.
|
47 |
+
* 'http_code' (int) - HTTP code returned when requesting the URL. Defaults to 0.
|
48 |
+
* 'redirect_count' (int) - The number of redirects. Defaults to 0.
|
49 |
+
* 'final_url' (string) - The redirected-to URL. Assumed to be equal to the checked URL by default.
|
50 |
+
* 'request_duration' (float) - How long it took for the server to respond. Defaults to 0 seconds.
|
51 |
+
* 'timeout' (bool) - True if checking the URL resulted in a timeout. Defaults to false.
|
52 |
+
* 'may_recheck' (bool) - Allow the plugin to re-check the URL after 'recheck_threshold' seconds (see broken-link-checker.php).
|
53 |
+
* 'log' (string) - Free-form log of the performed check. It will be displayed in the "Details" section of the checked link.
|
54 |
+
* 'result_hash' (string) - A free-form hash or code uniquely identifying the detected link status. See sub-classes for examples. Max 200 characters.
|
55 |
+
*
|
56 |
+
* @see blcLink:check()
|
57 |
+
*
|
58 |
+
* @param string $url
|
59 |
+
* @return array
|
60 |
+
*/
|
61 |
+
function check( $url ) {
|
62 |
+
trigger_error( 'Function blcChecker::check() must be over-ridden in a subclass', E_USER_ERROR );
|
63 |
+
}
|
64 |
+
}
|
65 |
+
|
66 |
+
class blcCheckerHelper {
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Get a reference to a specific checker.
|
70 |
+
*
|
71 |
+
* @uses blcModuleManager::get_module()
|
72 |
+
*
|
73 |
+
* @param string $checker_id
|
74 |
+
* @return blcChecker
|
75 |
+
*/
|
76 |
+
static function get_checker( $checker_id ) {
|
77 |
+
$manager = blcModuleManager::getInstance();
|
78 |
+
return $manager->get_module( $checker_id, true, 'checker' );
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Get a checker object that can check the specified URL.
|
83 |
+
*
|
84 |
+
* @param string $url
|
85 |
+
* @return blcChecker|null
|
86 |
+
*/
|
87 |
+
static function get_checker_for( $url ) {
|
88 |
+
$parsed = @parse_url( $url );
|
89 |
+
|
90 |
+
$manager = blcModuleManager::getInstance();
|
91 |
+
$active_checkers = $manager->get_active_by_category( 'checker' );
|
92 |
+
|
93 |
+
foreach ( $active_checkers as $module_id => $module_data ) {
|
94 |
+
//Try the URL pattern in the header first. If it doesn't match,
|
95 |
+
//we can avoid loading the module altogether.
|
96 |
+
if ( ! empty( $module_data['ModuleCheckerUrlPattern'] ) ) {
|
97 |
+
if ( ! preg_match( $module_data['ModuleCheckerUrlPattern'], $url ) ) {
|
98 |
+
continue;
|
99 |
+
}
|
100 |
+
}
|
101 |
+
|
102 |
+
$checker = $manager->get_module( $module_id );
|
103 |
+
|
104 |
+
if ( ! $checker ) {
|
105 |
+
continue;
|
106 |
+
}
|
107 |
+
|
108 |
+
//The can_check() method can perform more sophisticated filtering,
|
109 |
+
//or just return true if the checker thinks matching the URL regex
|
110 |
+
//is sufficient.
|
111 |
+
if ( $checker->can_check( $url, $parsed ) ) {
|
112 |
+
return $checker;
|
113 |
+
}
|
114 |
+
}
|
115 |
+
|
116 |
+
$checker = null;
|
117 |
+
return $checker;
|
118 |
+
}
|
119 |
+
}
|
120 |
+
|
includes/config-manager.php
CHANGED
@@ -1,127 +1,131 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* @author W-Shadow
|
5 |
-
* @copyright 2009
|
6 |
-
*/
|
7 |
-
|
8 |
-
if ( !class_exists('blcConfigurationManager') ){
|
9 |
-
|
10 |
-
class blcConfigurationManager {
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @author W-Shadow
|
5 |
+
* @copyright 2009
|
6 |
+
*/
|
7 |
+
|
8 |
+
if ( ! class_exists( 'blcConfigurationManager' ) ) {
|
9 |
+
|
10 |
+
class blcConfigurationManager {
|
11 |
+
|
12 |
+
var $option_name;
|
13 |
+
|
14 |
+
var $options;
|
15 |
+
var $defaults;
|
16 |
+
var $loaded_values;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @var bool Whether options have been successfully loaded from the database.
|
20 |
+
*/
|
21 |
+
public $db_option_loaded = false;
|
22 |
+
|
23 |
+
function __construct( $option_name = '', $default_settings = null ) {
|
24 |
+
$this->option_name = $option_name;
|
25 |
+
|
26 |
+
if ( is_array( $default_settings ) ) {
|
27 |
+
$this->defaults = $default_settings;
|
28 |
+
} else {
|
29 |
+
$this->defaults = array();
|
30 |
+
}
|
31 |
+
$this->loaded_values = array();
|
32 |
+
|
33 |
+
$this->options = $this->defaults;
|
34 |
+
|
35 |
+
if ( ! empty( $this->option_name ) ) {
|
36 |
+
$this->load_options();
|
37 |
+
}
|
38 |
+
}
|
39 |
+
|
40 |
+
function set_defaults( $default_settings = null ) {
|
41 |
+
if ( is_array( $default_settings ) ) {
|
42 |
+
$this->defaults = array();
|
43 |
+
} else {
|
44 |
+
$this->defaults = $default_settings;
|
45 |
+
}
|
46 |
+
$this->options = array_merge( $this->defaults, $this->loaded_values );
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* blcOptionManager::load_options()
|
51 |
+
* Load plugin options from the database. The current $options values are not affected
|
52 |
+
* if this function fails.
|
53 |
+
*
|
54 |
+
* @param string $option_name
|
55 |
+
* @return bool True if options were loaded, false otherwise.
|
56 |
+
*/
|
57 |
+
function load_options( $option_name = '' ) {
|
58 |
+
$this->db_option_loaded = false;
|
59 |
+
|
60 |
+
if ( ! empty( $option_name ) ) {
|
61 |
+
$this->option_name = $option_name;
|
62 |
+
}
|
63 |
+
|
64 |
+
if ( empty( $this->option_name ) ) {
|
65 |
+
return false;
|
66 |
+
}
|
67 |
+
|
68 |
+
$new_options = get_option( $this->option_name );
|
69 |
+
|
70 |
+
//Decode JSON (if applicable).
|
71 |
+
if ( is_string( $new_options ) && ! empty( $new_options ) ) {
|
72 |
+
$new_options = json_decode( $new_options, true );
|
73 |
+
}
|
74 |
+
|
75 |
+
if ( ! is_array( $new_options ) ) {
|
76 |
+
return false;
|
77 |
+
} else {
|
78 |
+
$this->loaded_values = $new_options;
|
79 |
+
$this->options = array_merge( $this->defaults, $this->loaded_values );
|
80 |
+
$this->db_option_loaded = true;
|
81 |
+
return true;
|
82 |
+
}
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* blcOptionManager::save_options()
|
87 |
+
* Save plugin options to the database.
|
88 |
+
*
|
89 |
+
* @param string $option_name (Optional) Save the options under this name
|
90 |
+
* @return bool True if settings were saved, false if settings haven't been changed or if there was an error.
|
91 |
+
*/
|
92 |
+
function save_options( $option_name = '' ) {
|
93 |
+
if ( ! empty( $option_name ) ) {
|
94 |
+
$this->option_name = $option_name;
|
95 |
+
}
|
96 |
+
|
97 |
+
if ( empty( $this->option_name ) ) {
|
98 |
+
return false;
|
99 |
+
}
|
100 |
+
|
101 |
+
return update_option( $this->option_name, json_encode( $this->options ) );
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Retrieve a specific setting.
|
106 |
+
*
|
107 |
+
* @param string $key
|
108 |
+
* @param mixed $default
|
109 |
+
* @return mixed
|
110 |
+
*/
|
111 |
+
function get( $key, $default = null ) {
|
112 |
+
if ( array_key_exists( $key, $this->options ) ) {
|
113 |
+
return $this->options[ $key ];
|
114 |
+
} else {
|
115 |
+
return $default;
|
116 |
+
}
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* Update or add a setting.
|
121 |
+
*
|
122 |
+
* @param string $key
|
123 |
+
* @param mixed $value
|
124 |
+
* @return void
|
125 |
+
*/
|
126 |
+
function set( $key, $value ) {
|
127 |
+
$this->options[ $key ] = $value;
|
128 |
+
}
|
129 |
+
}
|
130 |
+
|
131 |
+
}
|
includes/containers.php
CHANGED
@@ -1,911 +1,946 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* The base class for link container managers.
|
5 |
-
*
|
6 |
-
* Sub-classes should override at least the get_containers() and resynch() methods.
|
7 |
-
*
|
8 |
-
* @package Broken Link Checker
|
9 |
-
* @access public
|
10 |
-
*/
|
11 |
-
class blcContainerManager extends blcModule {
|
12 |
-
|
13 |
-
var $container_type
|
14 |
-
var $fields
|
15 |
-
var $container_class_name = 'blcContainer';
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
function init(){
|
26 |
-
parent::init();
|
27 |
-
$this->container_type = $this->module_id;
|
28 |
-
//Sub-classes might also use it to set up hooks, etc.
|
29 |
-
}
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
function get_container($container){
|
38 |
-
$container['fields'] = $this->get_parseable_fields();
|
39 |
-
|
40 |
-
|
41 |
-
}
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
function get_containers($containers, $purpose = '', $load_wrapped_objects = false){
|
63 |
-
return $this->make_containers($containers);
|
64 |
-
}
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
function make_containers($containers){
|
73 |
-
$results = array();
|
74 |
-
foreach($containers as $container){
|
75 |
-
$key
|
76 |
-
$results[ $key ] = $this->get_container($container);
|
77 |
-
}
|
78 |
-
return $results;
|
79 |
-
}
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
function resynch($forced = false){
|
90 |
-
trigger_error('Function blcContainerManager::resynch() must be over-ridden in a sub-class', E_USER_ERROR);
|
91 |
-
}
|
92 |
-
|
93 |
-
/**
|
94 |
-
* Resynch when activated.
|
95 |
-
*
|
96 |
-
* @uses blcContainerManager::resynch()
|
97 |
-
*
|
98 |
-
* @return void
|
99 |
-
*/
|
100 |
-
function activated(){
|
101 |
-
$this->resynch();
|
102 |
-
blc_got_unsynched_items();
|
103 |
-
}
|
104 |
-
|
105 |
-
/**
|
106 |
-
* Get a list of the parseable fields and their formats common to all containers of this type.
|
107 |
-
*
|
108 |
-
* @return array Associative array of formats indexed by field name.
|
109 |
-
*/
|
110 |
-
function get_parseable_fields(){
|
111 |
-
return $this->fields;
|
112 |
-
}
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
function ui_bulk_delete_message($n){
|
121 |
-
return sprintf(
|
122 |
-
_n(
|
123 |
-
"%d '%s' has been deleted",
|
124 |
-
"%d '%s' have been deleted",
|
125 |
-
$n,
|
126 |
-
'broken-link-checker'
|
127 |
-
),
|
128 |
-
$n,
|
129 |
-
$this->container_type
|
130 |
-
);
|
131 |
-
}
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
function ui_bulk_trash_message($n){
|
140 |
-
return $this->ui_bulk_delete_message($n);
|
141 |
-
}
|
142 |
-
}
|
143 |
-
|
144 |
-
/**
|
145 |
-
* The base class for link containers. All containers should extend this class.
|
146 |
-
*
|
147 |
-
* @package Broken Link Checker
|
148 |
-
* @access public
|
149 |
-
*/
|
150 |
-
class blcContainer {
|
151 |
-
|
152 |
-
var $fields
|
153 |
-
var $default_field = '';
|
154 |
-
|
155 |
-
var $container_type;
|
156 |
-
var $container_id = 0;
|
157 |
-
|
158 |
-
var $synched
|
159 |
-
var $last_synch = '0000-00-00 00:00:00';
|
160 |
-
|
161 |
-
var $wrapped_object = null;
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
function __construct( $data = null, $wrapped_object = null ){
|
171 |
-
$this->wrapped_object = $wrapped_object;
|
172 |
-
if ( !empty($data) && is_array($data) ){
|
173 |
-
foreach($data as $name => $value){
|
174 |
-
$this->$name = $value;
|
175 |
-
}
|
176 |
-
}
|
177 |
-
}
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
function get_field($field = ''){
|
188 |
-
if ( empty($field) ){
|
189 |
-
$field = $this->default_field;
|
190 |
-
}
|
191 |
-
|
192 |
-
$w = $this->get_wrapped_object();
|
193 |
-
return $w->$field;
|
194 |
-
}
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
function update_field($field, $new_value, $old_value = ''){
|
208 |
-
$w
|
209 |
-
$w->$field = $new_value;
|
210 |
-
return $this->update_wrapped_object();
|
211 |
-
}
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
function get_wrapped_object($ensure_consistency = false){
|
223 |
-
trigger_error('Function blcContainer::get_wrapped_object() must be over-ridden in a sub-class', E_USER_ERROR);
|
224 |
-
}
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
function update_wrapped_object(){
|
234 |
-
trigger_error('Function blcContainer::update_wrapped_object() must be over-ridden in a sub-class', E_USER_ERROR);
|
235 |
-
}
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
function synch(){
|
243 |
-
//FB::log("Parsing {$this->container_type}[{$this->container_id}]");
|
244 |
-
|
245 |
-
//Remove any existing link instance records associated with the container
|
246 |
-
$this->delete_instances();
|
247 |
-
|
248 |
-
//Load the wrapped object, if not done already
|
249 |
-
$this->get_wrapped_object();
|
250 |
-
|
251 |
-
//FB::log($this->fields, "Parseable fields :");
|
252 |
-
|
253 |
-
//Iterate over all parse-able fields
|
254 |
-
foreach($this->fields as $name => $format){
|
255 |
-
//Get the field value
|
256 |
-
$value = $this->get_field($name);
|
257 |
-
if ( empty($value) ){
|
258 |
-
//FB::log($name, "Skipping empty field");
|
259 |
-
continue;
|
260 |
-
}
|
261 |
-
//FB::log($name, "Parsing field");
|
262 |
-
|
263 |
-
//Get all parsers applicable to this field
|
264 |
-
$parsers = blcParserHelper::get_parsers( $format, $this->container_type );
|
265 |
-
//FB::log($parsers, "Applicable parsers");
|
266 |
-
|
267 |
-
if ( empty($parsers) )
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
//FB::log($
|
277 |
-
|
278 |
-
$
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
$
|
304 |
-
|
305 |
-
$
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
function
|
354 |
-
return
|
355 |
-
}
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
return
|
403 |
-
}
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
}
|
430 |
-
|
431 |
-
/**
|
432 |
-
*
|
433 |
-
*
|
434 |
-
*
|
435 |
-
*
|
436 |
-
*
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
*
|
445 |
-
*
|
446 |
-
*
|
447 |
-
*
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
|
465 |
-
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
|
490 |
-
//
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
-
$
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
return $
|
533 |
-
}
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
);
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
-
|
611 |
-
|
612 |
-
|
613 |
-
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
|
620 |
-
|
621 |
-
|
622 |
-
|
623 |
-
|
624 |
-
|
625 |
-
|
626 |
-
|
627 |
-
|
628 |
-
static function
|
629 |
-
|
630 |
-
|
631 |
-
|
632 |
-
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
|
638 |
-
|
639 |
-
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
|
646 |
-
|
647 |
-
|
648 |
-
|
649 |
-
|
650 |
-
|
651 |
-
|
652 |
-
|
653 |
-
|
654 |
-
|
655 |
-
|
656 |
-
|
657 |
-
|
658 |
-
|
659 |
-
|
660 |
-
|
661 |
-
|
662 |
-
|
663 |
-
|
664 |
-
|
665 |
-
|
666 |
-
|
667 |
-
|
668 |
-
|
669 |
-
|
670 |
-
|
671 |
-
|
672 |
-
|
673 |
-
|
674 |
-
|
675 |
-
|
676 |
-
|
677 |
-
|
678 |
-
|
679 |
-
|
680 |
-
|
681 |
-
|
682 |
-
|
683 |
-
|
684 |
-
|
685 |
-
|
686 |
-
|
687 |
-
|
688 |
-
|
689 |
-
|
690 |
-
|
691 |
-
|
692 |
-
|
693 |
-
|
694 |
-
|
695 |
-
|
696 |
-
|
697 |
-
|
698 |
-
|
699 |
-
|
700 |
-
|
701 |
-
|
702 |
-
|
703 |
-
|
704 |
-
|
705 |
-
|
706 |
-
|
707 |
-
|
708 |
-
|
709 |
-
|
710 |
-
|
711 |
-
|
712 |
-
|
713 |
-
|
714 |
-
|
715 |
-
}
|
716 |
-
|
717 |
-
|
718 |
-
|
719 |
-
|
720 |
-
|
721 |
-
|
722 |
-
|
723 |
-
|
724 |
-
|
725 |
-
|
726 |
-
|
727 |
-
|
728 |
-
$by_type[$container[
|
729 |
-
|
730 |
-
|
731 |
-
|
732 |
-
|
733 |
-
|
734 |
-
|
735 |
-
|
736 |
-
|
737 |
-
|
738 |
-
|
739 |
-
|
740 |
-
|
741 |
-
|
742 |
-
|
743 |
-
|
744 |
-
|
745 |
-
|
746 |
-
|
747 |
-
|
748 |
-
|
749 |
-
|
750 |
-
|
751 |
-
|
752 |
-
|
753 |
-
|
754 |
-
|
755 |
-
|
756 |
-
|
757 |
-
|
758 |
-
$
|
759 |
-
|
760 |
-
|
761 |
-
|
762 |
-
|
763 |
-
|
764 |
-
|
765 |
-
|
766 |
-
|
767 |
-
|
768 |
-
|
769 |
-
|
770 |
-
|
771 |
-
|
772 |
-
|
773 |
-
|
774 |
-
|
775 |
-
|
776 |
-
|
777 |
-
|
778 |
-
|
779 |
-
|
780 |
-
|
781 |
-
|
782 |
-
|
783 |
-
|
784 |
-
|
785 |
-
|
786 |
-
|
787 |
-
|
788 |
-
|
789 |
-
|
790 |
-
|
791 |
-
|
792 |
-
|
793 |
-
|
794 |
-
|
795 |
-
|
796 |
-
|
797 |
-
|
798 |
-
|
799 |
-
|
800 |
-
|
801 |
-
|
802 |
-
|
803 |
-
|
804 |
-
|
805 |
-
|
806 |
-
|
807 |
-
|
808 |
-
|
809 |
-
|
810 |
-
|
811 |
-
|
812 |
-
|
813 |
-
|
814 |
-
|
815 |
-
|
816 |
-
|
817 |
-
|
818 |
-
|
819 |
-
|
820 |
-
|
821 |
-
|
822 |
-
|
823 |
-
|
824 |
-
|
825 |
-
|
826 |
-
|
827 |
-
|
828 |
-
|
829 |
-
|
830 |
-
|
831 |
-
|
832 |
-
|
833 |
-
|
834 |
-
|
835 |
-
|
836 |
-
|
837 |
-
|
838 |
-
|
839 |
-
|
840 |
-
|
841 |
-
|
842 |
-
$
|
843 |
-
|
844 |
-
|
845 |
-
|
846 |
-
|
847 |
-
|
848 |
-
|
849 |
-
|
850 |
-
|
851 |
-
|
852 |
-
|
853 |
-
|
854 |
-
|
855 |
-
|
856 |
-
|
857 |
-
|
858 |
-
|
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 |
-
|
907 |
-
|
908 |
-
|
909 |
-
|
910 |
-
|
911 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* The base class for link container managers.
|
5 |
+
*
|
6 |
+
* Sub-classes should override at least the get_containers() and resynch() methods.
|
7 |
+
*
|
8 |
+
* @package Broken Link Checker
|
9 |
+
* @access public
|
10 |
+
*/
|
11 |
+
class blcContainerManager extends blcModule {
|
12 |
+
|
13 |
+
var $container_type = '';
|
14 |
+
var $fields = array();
|
15 |
+
var $container_class_name = 'blcContainer';
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Do whatever setup necessary that wasn't already done in the constructor.
|
19 |
+
*
|
20 |
+
* This method was added so that sub-classes would have something "safe" to
|
21 |
+
* over-ride without having to deal with PHP4/5 constructors.
|
22 |
+
*
|
23 |
+
* @return void
|
24 |
+
*/
|
25 |
+
function init() {
|
26 |
+
parent::init();
|
27 |
+
$this->container_type = $this->module_id;
|
28 |
+
//Sub-classes might also use it to set up hooks, etc.
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Instantiate a link container.
|
33 |
+
*
|
34 |
+
* @param array $container An associative array of container data.
|
35 |
+
* @return blcContainer
|
36 |
+
*/
|
37 |
+
function get_container( $container ) {
|
38 |
+
$container['fields'] = $this->get_parseable_fields();
|
39 |
+
$container_obj = new $this->container_class_name( $container );
|
40 |
+
return $container_obj;
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Instantiate multiple containers of the container type managed by this class and optionally
|
45 |
+
* pre-load container data used for display/parsing.
|
46 |
+
*
|
47 |
+
* Sub-classes should, if possible, use the $purpose argument to pre-load any extra data required for
|
48 |
+
* the specified task right away, instead of making multiple DB roundtrips later. For example, if
|
49 |
+
* $purpose is set to the BLC_FOR_DISPLAY constant, you might want to preload any DB data that the
|
50 |
+
* container will need in blcContainer::ui_get_source().
|
51 |
+
*
|
52 |
+
* @see blcContainer::make_containers()
|
53 |
+
* @see blcContainer::ui_get_source()
|
54 |
+
* @see blcContainer::ui_get_action_links()
|
55 |
+
*
|
56 |
+
* @param array $containers Array of assoc. arrays containing container data.
|
57 |
+
* @param string $purpose An optional code indicating how the retrieved containers will be used.
|
58 |
+
* @param bool $load_wrapped_objects Preload wrapped objects regardless of purpose.
|
59 |
+
*
|
60 |
+
* @return array of blcContainer indexed by "container_type|container_id"
|
61 |
+
*/
|
62 |
+
function get_containers( $containers, $purpose = '', $load_wrapped_objects = false ) {
|
63 |
+
return $this->make_containers( $containers );
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Instantiate multiple containers of the container type managed by this class
|
68 |
+
*
|
69 |
+
* @param array $containers Array of assoc. arrays containing container data.
|
70 |
+
* @return array of blcContainer indexed by "container_type|container_id"
|
71 |
+
*/
|
72 |
+
function make_containers( $containers ) {
|
73 |
+
$results = array();
|
74 |
+
foreach ( $containers as $container ) {
|
75 |
+
$key = $container['container_type'] . '|' . $container['container_id'];
|
76 |
+
$results[ $key ] = $this->get_container( $container );
|
77 |
+
}
|
78 |
+
return $results;
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Create or update synchronization records for all containers managed by this class.
|
83 |
+
*
|
84 |
+
* Must be over-ridden in subclasses.
|
85 |
+
*
|
86 |
+
* @param bool $forced If true, assume that all synch. records are gone and will need to be recreated from scratch.
|
87 |
+
* @return void
|
88 |
+
*/
|
89 |
+
function resynch( $forced = false ) {
|
90 |
+
trigger_error( 'Function blcContainerManager::resynch() must be over-ridden in a sub-class', E_USER_ERROR );
|
91 |
+
}
|
92 |
+
|
93 |
+
/**
|
94 |
+
* Resynch when activated.
|
95 |
+
*
|
96 |
+
* @uses blcContainerManager::resynch()
|
97 |
+
*
|
98 |
+
* @return void
|
99 |
+
*/
|
100 |
+
function activated() {
|
101 |
+
$this->resynch();
|
102 |
+
blc_got_unsynched_items();
|
103 |
+
}
|
104 |
+
|
105 |
+
/**
|
106 |
+
* Get a list of the parseable fields and their formats common to all containers of this type.
|
107 |
+
*
|
108 |
+
* @return array Associative array of formats indexed by field name.
|
109 |
+
*/
|
110 |
+
function get_parseable_fields() {
|
111 |
+
return $this->fields;
|
112 |
+
}
|
113 |
+
|
114 |
+
/**
|
115 |
+
* Get the message to display after $n containers have been deleted.
|
116 |
+
*
|
117 |
+
* @param int $n Number of deleted containers.
|
118 |
+
* @return string A delete confirmation message, e.g. "5 posts were moved to trash"
|
119 |
+
*/
|
120 |
+
function ui_bulk_delete_message( $n ) {
|
121 |
+
return sprintf(
|
122 |
+
_n(
|
123 |
+
"%1\$d '%2\$s' has been deleted",
|
124 |
+
"%1\$d '%2\$s' have been deleted",
|
125 |
+
$n,
|
126 |
+
'broken-link-checker'
|
127 |
+
),
|
128 |
+
$n,
|
129 |
+
$this->container_type
|
130 |
+
);
|
131 |
+
}
|
132 |
+
|
133 |
+
/**
|
134 |
+
* Get the message to display after $n containers have been moved to the trash.
|
135 |
+
*
|
136 |
+
* @param int $n Number of trashed containers.
|
137 |
+
* @return string A delete confirmation message, e.g. "5 posts were moved to trash"
|
138 |
+
*/
|
139 |
+
function ui_bulk_trash_message( $n ) {
|
140 |
+
return $this->ui_bulk_delete_message( $n );
|
141 |
+
}
|
142 |
+
}
|
143 |
+
|
144 |
+
/**
|
145 |
+
* The base class for link containers. All containers should extend this class.
|
146 |
+
*
|
147 |
+
* @package Broken Link Checker
|
148 |
+
* @access public
|
149 |
+
*/
|
150 |
+
class blcContainer {
|
151 |
+
|
152 |
+
var $fields = array();
|
153 |
+
var $default_field = '';
|
154 |
+
|
155 |
+
var $container_type;
|
156 |
+
var $container_id = 0;
|
157 |
+
|
158 |
+
var $synched = false;
|
159 |
+
var $last_synch = '0000-00-00 00:00:00';
|
160 |
+
|
161 |
+
var $wrapped_object = null;
|
162 |
+
|
163 |
+
/**
|
164 |
+
* Constructor
|
165 |
+
*
|
166 |
+
* @param array $data
|
167 |
+
* @param object $wrapped_object
|
168 |
+
* @return void
|
169 |
+
*/
|
170 |
+
function __construct( $data = null, $wrapped_object = null ) {
|
171 |
+
$this->wrapped_object = $wrapped_object;
|
172 |
+
if ( ! empty( $data ) && is_array( $data ) ) {
|
173 |
+
foreach ( $data as $name => $value ) {
|
174 |
+
$this->$name = $value;
|
175 |
+
}
|
176 |
+
}
|
177 |
+
}
|
178 |
+
|
179 |
+
/**
|
180 |
+
* Get the value of the specified field of the object wrapped by this container.
|
181 |
+
*
|
182 |
+
* @access protected
|
183 |
+
*
|
184 |
+
* @param string $field Field name. If omitted, the value of the default field will be returned.
|
185 |
+
* @return string
|
186 |
+
*/
|
187 |
+
function get_field( $field = '' ) {
|
188 |
+
if ( empty( $field ) ) {
|
189 |
+
$field = $this->default_field;
|
190 |
+
}
|
191 |
+
|
192 |
+
$w = $this->get_wrapped_object();
|
193 |
+
return $w->$field;
|
194 |
+
}
|
195 |
+
|
196 |
+
/**
|
197 |
+
* Update the value of the specified field in the wrapped object.
|
198 |
+
* This method will also immediately save the changed value by calling update_wrapped_object().
|
199 |
+
*
|
200 |
+
* @access protected
|
201 |
+
*
|
202 |
+
* @param string $field Field name.
|
203 |
+
* @param string $new_value Set the field to this value.
|
204 |
+
* @param string $old_value The previous value of the field. Optional, but can be useful for container that need the old value to distinguish between several instances of the same field (e.g. post metadata).
|
205 |
+
* @return bool|WP_Error True on success, an error object if something went wrong.
|
206 |
+
*/
|
207 |
+
function update_field( $field, $new_value, $old_value = '' ) {
|
208 |
+
$w = $this->get_wrapped_object();
|
209 |
+
$w->$field = $new_value;
|
210 |
+
return $this->update_wrapped_object();
|
211 |
+
}
|
212 |
+
|
213 |
+
/**
|
214 |
+
* Retrieve the entity wrapped by this container.
|
215 |
+
* The fetched object will also be cached in the $wrapped_object variable.
|
216 |
+
*
|
217 |
+
* @access protected
|
218 |
+
*
|
219 |
+
* @param bool $ensure_consistency Set this to true to ignore the cached $wrapped_object value and retrieve an up-to-date copy of the wrapped object from the DB (or WP's internal cache).
|
220 |
+
* @return object The wrapped object.
|
221 |
+
*/
|
222 |
+
function get_wrapped_object( $ensure_consistency = false ) {
|
223 |
+
trigger_error( 'Function blcContainer::get_wrapped_object() must be over-ridden in a sub-class', E_USER_ERROR );
|
224 |
+
}
|
225 |
+
|
226 |
+
/**
|
227 |
+
* Update the entity wrapped by the container with values currently in the $wrapped_object.
|
228 |
+
*
|
229 |
+
* @access protected
|
230 |
+
*
|
231 |
+
* @return bool|WP_Error True on success, an error if something went wrong.
|
232 |
+
*/
|
233 |
+
function update_wrapped_object() {
|
234 |
+
trigger_error( 'Function blcContainer::update_wrapped_object() must be over-ridden in a sub-class', E_USER_ERROR );
|
235 |
+
}
|
236 |
+
|
237 |
+
/**
|
238 |
+
* Parse the container for links and save the results to the DB.
|
239 |
+
*
|
240 |
+
* @return void
|
241 |
+
*/
|
242 |
+
function synch() {
|
243 |
+
//FB::log("Parsing {$this->container_type}[{$this->container_id}]");
|
244 |
+
|
245 |
+
//Remove any existing link instance records associated with the container
|
246 |
+
$this->delete_instances();
|
247 |
+
|
248 |
+
//Load the wrapped object, if not done already
|
249 |
+
$this->get_wrapped_object();
|
250 |
+
|
251 |
+
//FB::log($this->fields, "Parseable fields :");
|
252 |
+
|
253 |
+
//Iterate over all parse-able fields
|
254 |
+
foreach ( $this->fields as $name => $format ) {
|
255 |
+
//Get the field value
|
256 |
+
$value = $this->get_field( $name );
|
257 |
+
if ( empty( $value ) ) {
|
258 |
+
//FB::log($name, "Skipping empty field");
|
259 |
+
continue;
|
260 |
+
}
|
261 |
+
//FB::log($name, "Parsing field");
|
262 |
+
|
263 |
+
//Get all parsers applicable to this field
|
264 |
+
$parsers = blcParserHelper::get_parsers( $format, $this->container_type );
|
265 |
+
//FB::log($parsers, "Applicable parsers");
|
266 |
+
|
267 |
+
if ( empty( $parsers ) ) {
|
268 |
+
continue;
|
269 |
+
}
|
270 |
+
|
271 |
+
$base_url = $this->base_url();
|
272 |
+
$default_link_text = $this->default_link_text( $name );
|
273 |
+
|
274 |
+
//Parse the field with each parser
|
275 |
+
foreach ( $parsers as $parser ) {
|
276 |
+
//FB::log("Parsing $name with '{$parser->parser_type}' parser");
|
277 |
+
$found_instances = $parser->parse( $value, $base_url, $default_link_text );
|
278 |
+
//FB::log($found_instances, "Found instances");
|
279 |
+
|
280 |
+
$transactionManager = TransactionManager::getInstance();
|
281 |
+
$transactionManager->start();
|
282 |
+
|
283 |
+
//Complete the link instances by adding container info, then save them to the DB.
|
284 |
+
foreach ( $found_instances as $instance ) {
|
285 |
+
$instance->set_container( $this, $name );
|
286 |
+
$instance->save();
|
287 |
+
}
|
288 |
+
|
289 |
+
$transactionManager->commit();
|
290 |
+
|
291 |
+
}
|
292 |
+
}
|
293 |
+
|
294 |
+
$this->mark_as_synched();
|
295 |
+
}
|
296 |
+
|
297 |
+
/**
|
298 |
+
* Mark the container as successfully synchronized (parsed for links).
|
299 |
+
*
|
300 |
+
* @return bool
|
301 |
+
*/
|
302 |
+
function mark_as_synched() {
|
303 |
+
global $wpdb; /* @var wpdb $wpdb */
|
304 |
+
|
305 |
+
$this->last_synch = time();
|
306 |
+
|
307 |
+
$rez = $wpdb->query(
|
308 |
+
$wpdb->prepare(
|
309 |
+
"INSERT INTO {$wpdb->prefix}blc_synch( container_id, container_type, synched, last_synch)
|
310 |
+
VALUES( %d, %s, %d, NOW() )
|
311 |
+
ON DUPLICATE KEY UPDATE synched = VALUES(synched), last_synch = VALUES(last_synch)",
|
312 |
+
$this->container_id,
|
313 |
+
$this->container_type,
|
314 |
+
1
|
315 |
+
)
|
316 |
+
);
|
317 |
+
|
318 |
+
return ( false !== $rez );
|
319 |
+
}
|
320 |
+
|
321 |
+
/**
|
322 |
+
* blcContainer::mark_as_unsynched()
|
323 |
+
* Mark the container as not synchronized (not parsed, or modified since the last parse).
|
324 |
+
* The plugin will attempt to (re)parse the container at the earliest opportunity.
|
325 |
+
*
|
326 |
+
* @return bool
|
327 |
+
*/
|
328 |
+
function mark_as_unsynched() {
|
329 |
+
global $wpdb; /* @var wpdb $wpdb */
|
330 |
+
|
331 |
+
$rez = $wpdb->query(
|
332 |
+
$wpdb->prepare(
|
333 |
+
"INSERT INTO {$wpdb->prefix}blc_synch( container_id, container_type, synched, last_synch)
|
334 |
+
VALUES( %d, %s, %d, '0000-00-00 00:00:00' )
|
335 |
+
ON DUPLICATE KEY UPDATE synched = VALUES(synched)",
|
336 |
+
$this->container_id,
|
337 |
+
$this->container_type,
|
338 |
+
0
|
339 |
+
)
|
340 |
+
);
|
341 |
+
|
342 |
+
blc_got_unsynched_items();
|
343 |
+
|
344 |
+
return ( false !== $rez );
|
345 |
+
}
|
346 |
+
|
347 |
+
/**
|
348 |
+
* Get the base URL of the container. Used to normalize relative URLs found
|
349 |
+
* in the container. For example, for posts this would be the post permalink.
|
350 |
+
*
|
351 |
+
* @return string
|
352 |
+
*/
|
353 |
+
function base_url() {
|
354 |
+
return home_url();
|
355 |
+
}
|
356 |
+
|
357 |
+
/**
|
358 |
+
* Get the default link text to use for links found in a specific container field.
|
359 |
+
*
|
360 |
+
* This is generally only meaningful for non-HTML container fields.
|
361 |
+
* For example, if the container is post metadata, the default
|
362 |
+
* link text might be equal to the name of the custom field.
|
363 |
+
*
|
364 |
+
* @param string $field
|
365 |
+
* @return string
|
366 |
+
*/
|
367 |
+
function default_link_text( $field = '' ) {
|
368 |
+
return '';
|
369 |
+
}
|
370 |
+
|
371 |
+
|
372 |
+
|
373 |
+
/**
|
374 |
+
* Delete the DB record of this container.
|
375 |
+
* Also deletes the DB records of all link instances associated with it.
|
376 |
+
* Calling this method will not affect the WP entity (e.g. a post) corresponding to this container.
|
377 |
+
*
|
378 |
+
* @return bool
|
379 |
+
*/
|
380 |
+
function delete() {
|
381 |
+
global $wpdb; /* @var wpdb $wpdb */
|
382 |
+
|
383 |
+
//Delete instances first.
|
384 |
+
$rez = $this->delete_instances();
|
385 |
+
if ( ! $rez ) {
|
386 |
+
return false;
|
387 |
+
}
|
388 |
+
|
389 |
+
//Now delete the container record.
|
390 |
+
$q = $wpdb->query(
|
391 |
+
$wpdb->prepare(
|
392 |
+
"DELETE FROM {$wpdb->prefix}blc_synch
|
393 |
+
WHERE container_id = %d AND container_type = %s",
|
394 |
+
$this->container_id,
|
395 |
+
$this->container_type
|
396 |
+
)
|
397 |
+
);
|
398 |
+
|
399 |
+
if ( false === $q ) {
|
400 |
+
return false;
|
401 |
+
} else {
|
402 |
+
return true;
|
403 |
+
}
|
404 |
+
}
|
405 |
+
|
406 |
+
/**
|
407 |
+
* Delete all link instance records associated with this container.
|
408 |
+
* NB: Calling this method will not affect the WP entity (e.g. a post) corresponding to this container.
|
409 |
+
*
|
410 |
+
* @return bool
|
411 |
+
*/
|
412 |
+
function delete_instances() {
|
413 |
+
global $wpdb; /* @var wpdb $wpdb */
|
414 |
+
|
415 |
+
$q = $wpdb->query(
|
416 |
+
$wpdb->prepare(
|
417 |
+
"DELETE FROM {$wpdb->prefix}blc_instances
|
418 |
+
WHERE container_id = %d AND container_type = %s",
|
419 |
+
$this->container_id,
|
420 |
+
$this->container_type
|
421 |
+
)
|
422 |
+
);
|
423 |
+
|
424 |
+
if ( false === $q ) {
|
425 |
+
return false;
|
426 |
+
} else {
|
427 |
+
return true;
|
428 |
+
}
|
429 |
+
}
|
430 |
+
|
431 |
+
/**
|
432 |
+
* Delete or trash the WP entity corresponding to this container. Should prefer moving to trash, if possible.
|
433 |
+
* Also remove the synch. record of the container and all associated instances.
|
434 |
+
*
|
435 |
+
* Must be over-ridden in a sub-class.
|
436 |
+
*
|
437 |
+
* @return bool|WP_Error
|
438 |
+
*/
|
439 |
+
function delete_wrapped_object() {
|
440 |
+
trigger_error( 'Function blcContainer::delete_wrapped_object() must be over-ridden in a sub-class', E_USER_ERROR );
|
441 |
+
}
|
442 |
+
|
443 |
+
/**
|
444 |
+
* Move the WP entity corresponding to this container to the Trash.
|
445 |
+
*
|
446 |
+
* Must be over-riden in a subclass.
|
447 |
+
*
|
448 |
+
* @return bool|WP_Error
|
449 |
+
*/
|
450 |
+
function trash_wrapped_object() {
|
451 |
+
trigger_error( 'Function blcContainer::trash_wrapped_object() must be over-ridden in a sub-class', E_USER_ERROR );
|
452 |
+
}
|
453 |
+
|
454 |
+
/**
|
455 |
+
* Check if the current user can delete/trash this container.
|
456 |
+
*
|
457 |
+
* Should be over-ridden in a subclass.
|
458 |
+
*
|
459 |
+
* @return bool
|
460 |
+
*/
|
461 |
+
function current_user_can_delete() {
|
462 |
+
return false;
|
463 |
+
}
|
464 |
+
|
465 |
+
/**
|
466 |
+
* Determine if this container can be moved to the trash.
|
467 |
+
*
|
468 |
+
* Should be over-ridden in a subclass.
|
469 |
+
*
|
470 |
+
* @return bool
|
471 |
+
*/
|
472 |
+
function can_be_trashed() {
|
473 |
+
return false;
|
474 |
+
}
|
475 |
+
|
476 |
+
|
477 |
+
/**
|
478 |
+
* Change all links with the specified URL to a new URL.
|
479 |
+
*
|
480 |
+
* @param string $field_name
|
481 |
+
* @param blcParser $parser
|
482 |
+
* @param string $new_url
|
483 |
+
* @param string $old_url
|
484 |
+
* @param string $old_raw_url
|
485 |
+
* @param string $new_text Optional.
|
486 |
+
*
|
487 |
+
* @return array|WP_Error The new value of raw_url on success, or an error object if something went wrong.
|
488 |
+
*/
|
489 |
+
function edit_link( $field_name, $parser, $new_url, $old_url = '', $old_raw_url = '', $new_text = null ) {
|
490 |
+
//Ensure we're operating on a consistent copy of the wrapped object.
|
491 |
+
/*
|
492 |
+
Explanation
|
493 |
+
|
494 |
+
Consider this scenario where the container object wraps a blog post :
|
495 |
+
1) The container object gets created and loads the post data.
|
496 |
+
2) Someone modifies the DB data corresponding to the post.
|
497 |
+
3) The container tries to edit a link present in the post. However, the post
|
498 |
+
has changed since the time it was first cached, so when the container updates
|
499 |
+
the post with it's changes, it will overwrite whatever modifications were made
|
500 |
+
in step 2.
|
501 |
+
|
502 |
+
This would not be a problem if WP entities like posts and comments were
|
503 |
+
actually real objects, not just bags of key=>value pairs, but oh well.
|
504 |
+
|
505 |
+
Therefore, it is necessary to re-load the wrapped object before editing it.
|
506 |
+
*/
|
507 |
+
$this->get_wrapped_object( true );
|
508 |
+
|
509 |
+
//Get the current value of the field that needs to be edited.
|
510 |
+
$old_value = $this->get_field( $field_name );
|
511 |
+
|
512 |
+
//Have the parser modify the specified link. If successful, the parser will
|
513 |
+
//return an associative array with two keys - 'content' and 'raw_url'.
|
514 |
+
//Otherwise we'll get an instance of WP_Error.
|
515 |
+
if ( $parser->is_link_text_editable() ) {
|
516 |
+
$edit_result = $parser->edit( $old_value, $new_url, $old_url, $old_raw_url, $new_text );
|
517 |
+
} else {
|
518 |
+
$edit_result = $parser->edit( $old_value, $new_url, $old_url, $old_raw_url );
|
519 |
+
}
|
520 |
+
if ( is_wp_error( $edit_result ) ) {
|
521 |
+
return $edit_result;
|
522 |
+
}
|
523 |
+
|
524 |
+
//Update the field with the new value returned by the parser.
|
525 |
+
$update_result = $this->update_field( $field_name, $edit_result['content'], $old_value );
|
526 |
+
if ( is_wp_error( $update_result ) ) {
|
527 |
+
return $update_result;
|
528 |
+
}
|
529 |
+
|
530 |
+
//Return the new values to the instance.
|
531 |
+
unset( $edit_result['content'] ); //(Except content, which it doesn't need.)
|
532 |
+
return $edit_result;
|
533 |
+
}
|
534 |
+
|
535 |
+
/**
|
536 |
+
* Remove all links with the specified URL, leaving their anchor text intact.
|
537 |
+
*
|
538 |
+
* @param string $field_name
|
539 |
+
* @param blcParser $parser
|
540 |
+
* @param string $url
|
541 |
+
* @param string $raw_url
|
542 |
+
* @return bool|WP_Error True on success, or an error object if something went wrong.
|
543 |
+
*/
|
544 |
+
function unlink( $field_name, $parser, $url, $raw_url = '' ) {
|
545 |
+
//Ensure we're operating on a consistent copy of the wrapped object.
|
546 |
+
$this->get_wrapped_object( true );
|
547 |
+
|
548 |
+
$old_value = $this->get_field( $field_name );
|
549 |
+
|
550 |
+
$new_value = $parser->unlink( $old_value, $url, $raw_url );
|
551 |
+
if ( is_wp_error( $new_value ) ) {
|
552 |
+
return $new_value;
|
553 |
+
}
|
554 |
+
|
555 |
+
return $this->update_field( $field_name, $new_value, $old_value );
|
556 |
+
}
|
557 |
+
|
558 |
+
/**
|
559 |
+
* Retrieve a list of links found in this container.
|
560 |
+
*
|
561 |
+
* @access public
|
562 |
+
*
|
563 |
+
* @return array of blcLink
|
564 |
+
*/
|
565 |
+
function get_links() {
|
566 |
+
$params = array(
|
567 |
+
's_container_type' => $this->container_type,
|
568 |
+
's_container_id' => $this->container_id,
|
569 |
+
);
|
570 |
+
return blc_get_links( $params );
|
571 |
+
}
|
572 |
+
|
573 |
+
|
574 |
+
/**
|
575 |
+
* Get action links to display in the "Source" column of the Tools -> Broken Links link table.
|
576 |
+
*
|
577 |
+
* @param string $container_field
|
578 |
+
* @return array
|
579 |
+
*/
|
580 |
+
function ui_get_action_links( $container_field ) {
|
581 |
+
return array();
|
582 |
+
}
|
583 |
+
|
584 |
+
/**
|
585 |
+
* Get the container name to display in the "Source" column of the Tools -> Broken Links link table.
|
586 |
+
*
|
587 |
+
* @param string $container_field
|
588 |
+
* @param string $context
|
589 |
+
* @return string
|
590 |
+
*/
|
591 |
+
function ui_get_source( $container_field, $context = 'display' ) {
|
592 |
+
return sprintf( '%s[%d] : %s', $this->container_type, $this->container_id, $container_field );
|
593 |
+
}
|
594 |
+
|
595 |
+
/**
|
596 |
+
* Get edit URL. Returns the URL of the Dashboard page where the item associated with this
|
597 |
+
* container can be edited.
|
598 |
+
*
|
599 |
+
* HTML entities like '&' will be properly escaped for display.
|
600 |
+
*
|
601 |
+
* @access protected
|
602 |
+
*
|
603 |
+
* @return string
|
604 |
+
*/
|
605 |
+
function get_edit_url() {
|
606 |
+
//Should be over-ridden in a sub-class.
|
607 |
+
return '';
|
608 |
+
}
|
609 |
+
|
610 |
+
}
|
611 |
+
|
612 |
+
|
613 |
+
/**
|
614 |
+
* An utility class for working with link container types.
|
615 |
+
* All methods of this class should be called statically.
|
616 |
+
*
|
617 |
+
* @package Broken Link Checker
|
618 |
+
*/
|
619 |
+
class blcContainerHelper {
|
620 |
+
|
621 |
+
/**
|
622 |
+
* Get the manager associated with a container type.
|
623 |
+
*
|
624 |
+
* @param string $container_type
|
625 |
+
* @param string $fallback If there is no manager associated with $container_type, return the manager of this container type instead.
|
626 |
+
* @return blcContainerManager|null
|
627 |
+
*/
|
628 |
+
static function get_manager( $container_type, $fallback = '' ) {
|
629 |
+
$module_manager = blcModuleManager::getInstance();
|
630 |
+
$container_manager = null;
|
631 |
+
$container_manager = $module_manager->get_module( $container_type, true, 'container' );
|
632 |
+
|
633 |
+
if ( $container_manager ) {
|
634 |
+
return $container_manager;
|
635 |
+
} elseif ( ! empty( $fallback ) ) {
|
636 |
+
$container_manager = $module_manager->get_module( $fallback, true, 'container' );
|
637 |
+
if ( $container_manager ) {
|
638 |
+
return $container_manager;
|
639 |
+
}
|
640 |
+
}
|
641 |
+
|
642 |
+
return null;
|
643 |
+
}
|
644 |
+
|
645 |
+
/**
|
646 |
+
* Retrieve or instantiate a container object.
|
647 |
+
*
|
648 |
+
* Pass an array containing the container type (string) and ID (int) to retrieve the container
|
649 |
+
* from the database. Alternatively, pass an associative array to create a new container object
|
650 |
+
* from the data in the array.
|
651 |
+
*
|
652 |
+
* @param array $container Either [container_type, container_id], or an assoc. array of container data.
|
653 |
+
* @return blcContainer|null
|
654 |
+
*/
|
655 |
+
static function get_container( $container ) {
|
656 |
+
global $wpdb; /* @var wpdb $wpdb */
|
657 |
+
|
658 |
+
if ( ! is_array( $container ) || ( count( $container ) < 2 ) ) {
|
659 |
+
return null;
|
660 |
+
}
|
661 |
+
|
662 |
+
if ( is_string( $container[0] ) && is_numeric( $container[1] ) ) {
|
663 |
+
//The argument is in the [container_type, id] format
|
664 |
+
//Fetch the container's synch record.
|
665 |
+
$rez = $wpdb->get_row(
|
666 |
+
$wpdb->prepare(
|
667 |
+
"SELECT * FROM {$wpdb->prefix}blc_synch
|
668 |
+
WHERE container_type = %s AND container_id = %d",
|
669 |
+
$container[0],
|
670 |
+
$container[1]
|
671 |
+
),
|
672 |
+
ARRAY_A
|
673 |
+
);
|
674 |
+
|
675 |
+
if ( empty( $rez ) ) {
|
676 |
+
//The container wasn't found, so we'll create a new one.
|
677 |
+
$container = array(
|
678 |
+
'container_type' => $container[0],
|
679 |
+
'container_id' => $container[1],
|
680 |
+
);
|
681 |
+
} else {
|
682 |
+
$container = $rez;
|
683 |
+
}
|
684 |
+
}
|
685 |
+
|
686 |
+
$manager = blcContainerHelper::get_manager( $container['container_type'] );
|
687 |
+
if ( ! $manager ) {
|
688 |
+
return null;
|
689 |
+
}
|
690 |
+
|
691 |
+
return $manager->get_container( $container );
|
692 |
+
}
|
693 |
+
|
694 |
+
/**
|
695 |
+
* Retrieve or instantiate multiple link containers.
|
696 |
+
*
|
697 |
+
* Takes an array of container specifications and returns an array of container objects.
|
698 |
+
* Each input array entry should be an array and consist either of a container type (string)
|
699 |
+
* and container ID (int), or name => value pairs describing a container object.
|
700 |
+
*
|
701 |
+
* @see blcContainerHelper::get_container()
|
702 |
+
*
|
703 |
+
* @param array $containers
|
704 |
+
* @param string $purpose Optional code indicating how the retrieved containers will be used.
|
705 |
+
* @param string $fallback The fallback container type to use for unrecognized containers.
|
706 |
+
* @param bool $load_wrapped_objects Preload wrapped objects regardless of purpose.
|
707 |
+
* @return blcContainer[] Array of blcContainer indexed by "container_type|container_id"
|
708 |
+
*/
|
709 |
+
static function get_containers( $containers, $purpose = '', $fallback = '', $load_wrapped_objects = false ) {
|
710 |
+
global $wpdb; /* @var wpdb $wpdb */
|
711 |
+
|
712 |
+
//If the input is invalid or empty, return an empty array.
|
713 |
+
if ( ! is_array( $containers ) || ( count( $containers ) < 1 ) ) {
|
714 |
+
return array();
|
715 |
+
}
|
716 |
+
|
717 |
+
$first = reset( $containers );
|
718 |
+
if ( ! is_array( $first ) ) {
|
719 |
+
return array();
|
720 |
+
}
|
721 |
+
|
722 |
+
if ( isset( $first[0] ) && is_string( $first[0] ) && is_numeric( $first[1] ) ) {
|
723 |
+
//The argument is an array of [container_type, id].
|
724 |
+
//Divide the container IDs by container type.
|
725 |
+
$by_type = array();
|
726 |
+
|
727 |
+
foreach ( $containers as $container ) {
|
728 |
+
if ( isset( $by_type[ $container[0] ] ) ) {
|
729 |
+
array_push( $by_type[ $container[0] ], intval( $container[1] ) );
|
730 |
+
} else {
|
731 |
+
$by_type[ $container[0] ] = array( intval( $container[1] ) );
|
732 |
+
}
|
733 |
+
}
|
734 |
+
|
735 |
+
//Build the SQL to fetch all the specified containers
|
736 |
+
$q = "SELECT *
|
737 |
+
FROM {$wpdb->prefix}blc_synch
|
738 |
+
WHERE";
|
739 |
+
|
740 |
+
$pieces = array();
|
741 |
+
foreach ( $by_type as $container_type => $container_ids ) {
|
742 |
+
$pieces[] = '( container_type = "' . esc_sql( $container_type ) . '" AND container_id IN (' . implode( ', ', $container_ids ) . ') )';
|
743 |
+
}
|
744 |
+
|
745 |
+
$q .= implode( "\n\t OR ", $pieces );
|
746 |
+
|
747 |
+
//Fetch the container synch. records from the DB.
|
748 |
+
$containers = $wpdb->get_results( $q, ARRAY_A ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
749 |
+
}
|
750 |
+
|
751 |
+
/*
|
752 |
+
Divide the inputs into separate arrays by container type (again), then invoke
|
753 |
+
the appropriate manager for each type to instantiate the container objects.
|
754 |
+
*/
|
755 |
+
|
756 |
+
//At this point, $containers is an array of assoc. arrays comprising container data.
|
757 |
+
$by_type = array();
|
758 |
+
foreach ( $containers as $container ) {
|
759 |
+
if ( isset( $by_type[ $container['container_type'] ] ) ) {
|
760 |
+
array_push( $by_type[ $container['container_type'] ], $container );
|
761 |
+
} else {
|
762 |
+
$by_type[ $container['container_type'] ] = array( $container );
|
763 |
+
}
|
764 |
+
}
|
765 |
+
|
766 |
+
$results = array();
|
767 |
+
foreach ( $by_type as $container_type => $entries ) {
|
768 |
+
$manager = blcContainerHelper::get_manager( $container_type, $fallback );
|
769 |
+
if ( ! is_null( $manager ) ) {
|
770 |
+
$partial_results = $manager->get_containers( $entries, $purpose, $load_wrapped_objects );
|
771 |
+
$results = array_merge( $results, $partial_results );
|
772 |
+
}
|
773 |
+
}
|
774 |
+
|
775 |
+
return $results;
|
776 |
+
}
|
777 |
+
|
778 |
+
/**
|
779 |
+
* Retrieve link containers that need to be synchronized (parsed).
|
780 |
+
*
|
781 |
+
* @param integer $max_results The maximum number of containers to return. Defaults to returning all unsynched containers.
|
782 |
+
* @return blcContainer[]
|
783 |
+
*/
|
784 |
+
static function get_unsynched_containers( $max_results = 0 ) {
|
785 |
+
global $wpdb; /* @var wpdb $wpdb */
|
786 |
+
|
787 |
+
$q = "SELECT * FROM {$wpdb->prefix}blc_synch WHERE synched = 0";
|
788 |
+
if ( $max_results > 0 ) {
|
789 |
+
$q .= " LIMIT $max_results";
|
790 |
+
}
|
791 |
+
|
792 |
+
$container_data = $wpdb->get_results( $q, ARRAY_A ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
793 |
+
//FB::log($container_data, "Unsynched containers");
|
794 |
+
if ( empty( $container_data ) ) {
|
795 |
+
return array();
|
796 |
+
}
|
797 |
+
|
798 |
+
$containers = blcContainerHelper::get_containers( $container_data, BLC_FOR_PARSING, 'dummy' );
|
799 |
+
return $containers;
|
800 |
+
}
|
801 |
+
|
802 |
+
/**
|
803 |
+
* (Re)create and update synchronization records for all supported containers.
|
804 |
+
* Calls the resynch() method of all registered managers.
|
805 |
+
*
|
806 |
+
* @param bool $forced If true, assume that no synch. records exist and build all of them from scratch.
|
807 |
+
* @return void
|
808 |
+
*/
|
809 |
+
static function resynch( $forced = false ) {
|
810 |
+
global $wpdb;
|
811 |
+
|
812 |
+
$module_manager = blcModuleManager::getInstance();
|
813 |
+
$active_managers = $module_manager->get_active_by_category( 'container' );
|
814 |
+
foreach ( $active_managers as $module_id => $module_data ) {
|
815 |
+
$manager = $module_manager->get_module( $module_id );
|
816 |
+
if ( $manager ) {
|
817 |
+
$manager->resynch( $forced );
|
818 |
+
}
|
819 |
+
}
|
820 |
+
}
|
821 |
+
|
822 |
+
/**
|
823 |
+
* Mark as unparsed all containers that match one of the the specified formats or
|
824 |
+
* container types and that were last parsed after a specific timestamp.
|
825 |
+
*
|
826 |
+
* Used by newly activated parsers to force the containers they're interested in
|
827 |
+
* to resynchronize and thus let the parser process them.
|
828 |
+
*
|
829 |
+
* @param array $formats Associative array of timestamps, indexed by format IDs.
|
830 |
+
* @param array $container_types Associative array of timestamps, indexed by container types.
|
831 |
+
* @return bool
|
832 |
+
*/
|
833 |
+
static function mark_as_unsynched_where( $formats, $container_types ) {
|
834 |
+
global $wpdb; /* @var wpdb $wpdb */
|
835 |
+
global $blclog;
|
836 |
+
|
837 |
+
//Find containers that match any of the specified formats and add them to
|
838 |
+
//the list of container types that need to be marked as unsynched.
|
839 |
+
$module_manager = blcModuleManager::getInstance();
|
840 |
+
$containers = $module_manager->get_active_by_category( 'container' );
|
841 |
+
|
842 |
+
foreach ( $containers as $module_id => $module_data ) {
|
843 |
+
$container_manager = $module_manager->get_module( $module_id );
|
844 |
+
if ( $container_manager ) {
|
845 |
+
$fields = $container_manager->get_parseable_fields();
|
846 |
+
$container_type = $container_manager->container_type;
|
847 |
+
foreach ( $formats as $format => $timestamp ) {
|
848 |
+
if ( in_array( $format, $fields ) ) {
|
849 |
+
//Choose the earliest timestamp
|
850 |
+
if ( isset( $container_types[ $container_type ] ) ) {
|
851 |
+
$container_types[ $container_type ] = min( $timestamp, $container_types[ $container_type ] );
|
852 |
+
} else {
|
853 |
+
$container_types[ $container_type ] = $timestamp;
|
854 |
+
}
|
855 |
+
}
|
856 |
+
}
|
857 |
+
};
|
858 |
+
}
|
859 |
+
|
860 |
+
if ( empty( $container_types ) ) {
|
861 |
+
return true;
|
862 |
+
}
|
863 |
+
|
864 |
+
//Build the query to update all synch. records that match one of the specified
|
865 |
+
//container types and have been parsed after the specified time.
|
866 |
+
$q = "UPDATE {$wpdb->prefix}blc_synch SET synched = 0 WHERE ";
|
867 |
+
|
868 |
+
$pieces = array();
|
869 |
+
foreach ( $container_types as $container_type => $timestamp ) {
|
870 |
+
$pieces[] = $wpdb->prepare(
|
871 |
+
'(container_type = %s AND last_synch >= %s)',
|
872 |
+
$container_type,
|
873 |
+
date( 'Y-m-d H:i:s', $timestamp ) //phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
|
874 |
+
);
|
875 |
+
}
|
876 |
+
|
877 |
+
$q .= implode( ' OR ', $pieces );
|
878 |
+
$blclog->log( '...... Executing query: ' . $q );
|
879 |
+
|
880 |
+
$start_time = microtime( true );
|
881 |
+
$rez = ( $wpdb->query( $q ) !== false ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
882 |
+
$blclog->log( sprintf( '...... %d rows affected, %.3f seconds', $wpdb->rows_affected, microtime( true ) - $start_time ) );
|
883 |
+
|
884 |
+
blc_got_unsynched_items();
|
885 |
+
|
886 |
+
return $rez;
|
887 |
+
}
|
888 |
+
|
889 |
+
/**
|
890 |
+
* Remove synch. records that reference container types not currently loaded
|
891 |
+
*
|
892 |
+
* @return bool
|
893 |
+
*/
|
894 |
+
static function cleanup_containers() {
|
895 |
+
global $wpdb; /* @var wpdb $wpdb */
|
896 |
+
global $blclog;
|
897 |
+
|
898 |
+
$module_manager = blcModuleManager::getInstance();
|
899 |
+
|
900 |
+
$start = microtime( true );
|
901 |
+
$active_containers = $module_manager->get_escaped_ids( 'container' );
|
902 |
+
$q = "DELETE synch.*
|
903 |
+
FROM {$wpdb->prefix}blc_synch AS synch
|
904 |
+
WHERE
|
905 |
+
synch.container_type NOT IN ({$active_containers})";
|
906 |
+
$rez = $wpdb->query( $q ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
907 |
+
$elapsed = microtime( true ) - $start;
|
908 |
+
$blclog->log( sprintf( '... %d synch records deleted in %.3f seconds', $wpdb->rows_affected, $elapsed ) );
|
909 |
+
|
910 |
+
return false !== $rez;
|
911 |
+
}
|
912 |
+
|
913 |
+
/**
|
914 |
+
* Get the message to display after $n containers of a specific type have been deleted.
|
915 |
+
*
|
916 |
+
* @param string $container_type
|
917 |
+
* @param int $n Number of deleted containers.
|
918 |
+
* @return string A delete confirmation message, e.g. "5 posts were moved to trash"
|
919 |
+
*/
|
920 |
+
static function ui_bulk_delete_message( $container_type, $n ) {
|
921 |
+
$manager = blcContainerHelper::get_manager( $container_type );
|
922 |
+
if ( is_null( $manager ) ) {
|
923 |
+
return sprintf( __( "Container type '%s' not recognized", 'broken-link-checker' ), $container_type );
|
924 |
+
} else {
|
925 |
+
return $manager->ui_bulk_delete_message( $n );
|
926 |
+
}
|
927 |
+
}
|
928 |
+
|
929 |
+
/**
|
930 |
+
* Get the message to display after $n containers of a specific type have been moved to the trash.
|
931 |
+
*
|
932 |
+
* @see blcContainerHelper::ui_bulk_delete_message()
|
933 |
+
*
|
934 |
+
* @param string $container_type
|
935 |
+
* @param int $n
|
936 |
+
* @return string
|
937 |
+
*/
|
938 |
+
static function ui_bulk_trash_message( $container_type, $n ) {
|
939 |
+
$manager = blcContainerHelper::get_manager( $container_type );
|
940 |
+
if ( is_null( $manager ) ) {
|
941 |
+
return sprintf( __( "Container type '%s' not recognized", 'broken-link-checker' ), $container_type );
|
942 |
+
} else {
|
943 |
+
return $manager->ui_bulk_trash_message( $n );
|
944 |
+
}
|
945 |
+
}
|
946 |
+
}
|
includes/extra-strings.php
CHANGED
@@ -1,20 +1,20 @@
|
|
1 |
<?php
|
2 |
-
_x(
|
3 |
-
_x(
|
4 |
-
_x(
|
5 |
-
_x(
|
6 |
-
_x(
|
7 |
-
_x(
|
8 |
-
_x(
|
9 |
-
_x(
|
10 |
-
_x(
|
11 |
-
_x(
|
12 |
-
_x(
|
13 |
-
_x(
|
14 |
-
_x(
|
15 |
-
_x(
|
16 |
-
_x(
|
17 |
-
_x(
|
18 |
-
_x(
|
19 |
-
_x(
|
20 |
-
_x(
|
1 |
<?php
|
2 |
+
_x( 'Basic HTTP', 'module name', 'broken-link-checker' );
|
3 |
+
_x( 'Blogroll items', 'module name', 'broken-link-checker' );
|
4 |
+
_x( 'Comments', 'module name', 'broken-link-checker' );
|
5 |
+
_x( 'Custom fields', 'module name', 'broken-link-checker' );
|
6 |
+
_x( 'Embedded DailyMotion videos', 'module name', 'broken-link-checker' );
|
7 |
+
_x( 'Embedded GoogleVideo videos', 'module name', 'broken-link-checker' );
|
8 |
+
_x( 'Embedded Vimeo videos', 'module name', 'broken-link-checker' );
|
9 |
+
_x( 'Embedded YouTube playlists (old embed code)', 'module name', 'broken-link-checker' );
|
10 |
+
_x( 'Embedded YouTube videos', 'module name', 'broken-link-checker' );
|
11 |
+
_x( 'Embedded YouTube videos (old embed code)', 'module name', 'broken-link-checker' );
|
12 |
+
_x( 'HTML images', 'module name', 'broken-link-checker' );
|
13 |
+
_x( 'HTML links', 'module name', 'broken-link-checker' );
|
14 |
+
_x( 'MediaFire API', 'module name', 'broken-link-checker' );
|
15 |
+
_x( 'Plaintext URLs', 'module name', 'broken-link-checker' );
|
16 |
+
_x( 'RapidShare API', 'module name', 'broken-link-checker' );
|
17 |
+
_x( 'Smart YouTube httpv:// URLs', 'module name', 'broken-link-checker' );
|
18 |
+
_x( 'YouTube API', 'module name', 'broken-link-checker' );
|
19 |
+
_x( 'Posts', 'module name', 'broken-link-checker' );
|
20 |
+
_x( 'Pages', 'module name', 'broken-link-checker' );
|
includes/instances.php
CHANGED
@@ -1,639 +1,633 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* @author W-Shadow
|
5 |
-
* @copyright 2009
|
6 |
-
*/
|
7 |
-
|
8 |
-
if (!class_exists('blcLinkInstance')) {
|
9 |
-
class blcLinkInstance {
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
)
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
)
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
//
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
)
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
)
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
//
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
}
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
}
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
return
|
396 |
-
}
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
}
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
|
465 |
-
|
466 |
-
|
467 |
-
|
468 |
-
}
|
469 |
-
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
$
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
$
|
552 |
-
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
-
|
611 |
-
|
612 |
-
|
613 |
-
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
|
620 |
-
|
621 |
-
|
622 |
-
|
623 |
-
|
624 |
-
|
625 |
-
|
626 |
-
|
627 |
-
|
628 |
-
|
629 |
-
|
630 |
-
|
631 |
-
|
632 |
-
|
633 |
-
|
634 |
-
return ($rez !== false) && ($rez2 !== false);
|
635 |
-
}
|
636 |
-
|
637 |
-
|
638 |
-
}//class_exists
|
639 |
-
|
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 |
+
|
18 |
+
var $container_id = 0;
|
19 |
+
var $container_type = '';
|
20 |
+
var $container_field = '';
|
21 |
+
|
22 |
+
var $parser_type = '';
|
23 |
+
|
24 |
+
var $link_text = '';
|
25 |
+
var $link_context = '';
|
26 |
+
var $raw_url = '';
|
27 |
+
|
28 |
+
/** @var blcContainer */
|
29 |
+
var $_container = null;
|
30 |
+
var $_parser = null;
|
31 |
+
/** @var blcLink|null */
|
32 |
+
var $_link = null;
|
33 |
+
|
34 |
+
/**
|
35 |
+
* blcLinkInstance::__construct()
|
36 |
+
* Class constructor
|
37 |
+
*
|
38 |
+
* @param int|array $arg Either the instance ID or an associate array representing the instance's DB record. Should be NULL for new instances.
|
39 |
+
*/
|
40 |
+
function __construct( $arg = null ) {
|
41 |
+
global $wpdb; /** @var wpdb $wpdb */
|
42 |
+
|
43 |
+
if ( is_int( $arg ) ) {
|
44 |
+
//Load an instance with ID = $arg from the DB.
|
45 |
+
$arr = $wpdb->get_row(
|
46 |
+
$wpdb->prepare(
|
47 |
+
"SELECT * FROM {$wpdb->prefix}blc_instances WHERE instance_id=%d LIMIT 1",
|
48 |
+
$arg
|
49 |
+
),
|
50 |
+
ARRAY_A
|
51 |
+
);
|
52 |
+
|
53 |
+
if ( is_array( $arr ) ) { //Loaded successfully
|
54 |
+
$this->set_values( $arr );
|
55 |
+
} else {
|
56 |
+
//Link instance not found. The object is invalid.
|
57 |
+
}
|
58 |
+
} elseif ( is_array( $arg ) ) {
|
59 |
+
$this->set_values( $arg );
|
60 |
+
|
61 |
+
//Is this a new instance?
|
62 |
+
$this->is_new = empty( $this->instance_id );
|
63 |
+
|
64 |
+
} else {
|
65 |
+
$this->is_new = true;
|
66 |
+
}
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* blcLinkInstance::blcLinkInstance()
|
71 |
+
* Old-style constructor for PHP 4. Do not use.
|
72 |
+
*
|
73 |
+
* @param mixed $arg
|
74 |
+
* @return void
|
75 |
+
*/
|
76 |
+
function blcLinkInstance( $arg = null ) {
|
77 |
+
$this->__construct( $arg );
|
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 |
+
* Replace this instance's URL with a new one.
|
95 |
+
* Warning : this shouldn't be called directly. Use blcLink->edit() instead.
|
96 |
+
*
|
97 |
+
* @param string $new_url
|
98 |
+
* @param string $old_url
|
99 |
+
* @param string $new_text
|
100 |
+
* @return bool|WP_Error True on success, or an instance of WP_Error if something went wrong.
|
101 |
+
*/
|
102 |
+
function edit( $new_url, $old_url = '', $new_text = null ) {
|
103 |
+
|
104 |
+
//Get the container that contains this link
|
105 |
+
$container = $this->get_container();
|
106 |
+
if ( is_null( $container ) ) {
|
107 |
+
return new WP_Error(
|
108 |
+
'container_not_found',
|
109 |
+
sprintf( __( 'Container %1$s[%2$d] not found', 'broken-link-checker' ), $this->container_type, $this->container_id )
|
110 |
+
);
|
111 |
+
}
|
112 |
+
|
113 |
+
//Get the parser.
|
114 |
+
$parser = $this->get_parser();
|
115 |
+
if ( is_null( $parser ) ) {
|
116 |
+
return new WP_Error(
|
117 |
+
'parser_not_found',
|
118 |
+
sprintf( __( "Parser '%s' not found.", 'broken-link-checker' ), $this->parser_type )
|
119 |
+
);
|
120 |
+
}
|
121 |
+
|
122 |
+
//If the old URL isn't specified get it from the link record
|
123 |
+
if ( empty( $old_url ) ) {
|
124 |
+
$old_url = $this->get_url();
|
125 |
+
}
|
126 |
+
|
127 |
+
//Attempt to modify the link(s)
|
128 |
+
$result = $container->edit_link( $this->container_field, $parser, $new_url, $old_url, $this->raw_url, $new_text );
|
129 |
+
if ( is_string( $result ) ) {
|
130 |
+
//If the modification was successful, the container will return
|
131 |
+
//the new raw_url for the instance. Save the URL and return true,
|
132 |
+
//indicating success.
|
133 |
+
$this->raw_url = $result;
|
134 |
+
return true;
|
135 |
+
} elseif ( is_array( $result ) ) {
|
136 |
+
//More advanced containers/parsers may return an array of values to
|
137 |
+
//modify several fields at once.
|
138 |
+
$allowed_fields = array( 'raw_url', 'link_text', 'link_context' );
|
139 |
+
foreach ( $result as $key => $value ) {
|
140 |
+
if ( in_array( $key, $allowed_fields ) ) {
|
141 |
+
$this->$key = $value;
|
142 |
+
}
|
143 |
+
}
|
144 |
+
return true;
|
145 |
+
} else {
|
146 |
+
//Otherwise, it will return an error object. In this case we'll
|
147 |
+
//just pass it back to the caller and let them sort it out.
|
148 |
+
return $result;
|
149 |
+
}
|
150 |
+
}
|
151 |
+
|
152 |
+
/**
|
153 |
+
* blcLinkInstance::unlink()
|
154 |
+
* Remove this instance from the post/blogroll/etc. Also deletes the appropriate DB record(s).
|
155 |
+
*
|
156 |
+
* @return bool|WP_Error
|
157 |
+
*/
|
158 |
+
function unlink( $url = null ) {
|
159 |
+
|
160 |
+
//Get the container that contains this link
|
161 |
+
$container = $this->get_container();
|
162 |
+
if ( is_null( $container ) ) {
|
163 |
+
return new WP_Error(
|
164 |
+
'container_not_found',
|
165 |
+
sprintf( __( 'Container %1$s[%2$d] not found', 'broken-link-checker' ), $this->container_type, $this->container_id )
|
166 |
+
);
|
167 |
+
}
|
168 |
+
|
169 |
+
//Get the parser.
|
170 |
+
$parser = $this->get_parser();
|
171 |
+
if ( is_null( $parser ) ) {
|
172 |
+
return new WP_Error(
|
173 |
+
'parser_not_found',
|
174 |
+
sprintf( __( "Parser '%s' not found.", 'broken-link-checker' ), $this->parser_type )
|
175 |
+
);
|
176 |
+
}
|
177 |
+
|
178 |
+
//If the old URL isn't specified get it from the link record
|
179 |
+
if ( empty( $url ) ) {
|
180 |
+
$url = $this->get_url();
|
181 |
+
}
|
182 |
+
|
183 |
+
//Attempt to remove the link(s)
|
184 |
+
return $container->unlink( $this->container_field, $parser, $url, $this->raw_url );
|
185 |
+
}
|
186 |
+
|
187 |
+
/**
|
188 |
+
* Remove the link instance record from database. Doesn't affect the thing that contains the link.
|
189 |
+
*
|
190 |
+
* @return mixed 1 on success, 0 if the instance wasn't found, false on error
|
191 |
+
*/
|
192 |
+
function forget() {
|
193 |
+
global $wpdb; /** @var wpdb $wpdb */
|
194 |
+
|
195 |
+
if ( ! empty( $this->instance_id ) ) {
|
196 |
+
$rez = $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}blc_instances WHERE instance_id=%d", $this->instance_id ) );
|
197 |
+
return $rez;
|
198 |
+
} else {
|
199 |
+
return false;
|
200 |
+
}
|
201 |
+
}
|
202 |
+
|
203 |
+
/**
|
204 |
+
* Store the link instance in the database.
|
205 |
+
* Saving the instance will also implicitly save the link record associated with it, if it wasn't already saved.
|
206 |
+
*
|
207 |
+
* @return bool TRUE on success, FALSE on error
|
208 |
+
*/
|
209 |
+
function save() {
|
210 |
+
global $wpdb; /** @var wpdb $wpdb */
|
211 |
+
|
212 |
+
//Refresh the locally cached link & container properties, in case
|
213 |
+
//the objects have changed since they were set.
|
214 |
+
|
215 |
+
if ( ! is_null( $this->_link ) ) {
|
216 |
+
|
217 |
+
//If we have a link object assigned, but it's new, it won't have a DB ID yet.
|
218 |
+
//We need to save the link to get the ID and be able to maintain the link <-> instance
|
219 |
+
//association.
|
220 |
+
if ( $this->_link->is_new ) {
|
221 |
+
$rez = $this->_link->save();
|
222 |
+
if ( ! $rez ) {
|
223 |
+
return false;
|
224 |
+
}
|
225 |
+
}
|
226 |
+
|
227 |
+
$this->link_id = $this->_link->link_id;
|
228 |
+
}
|
229 |
+
|
230 |
+
if ( ! is_null( $this->_container ) ) {
|
231 |
+
$this->container_type = $this->_container->container_type;
|
232 |
+
$this->container_id = $this->_container->container_id;
|
233 |
+
}
|
234 |
+
|
235 |
+
//If the link is new, insert a new row into the DB. Otherwise update the existing row.
|
236 |
+
if ( $this->is_new ) {
|
237 |
+
|
238 |
+
$rez = $wpdb->query(
|
239 |
+
$wpdb->prepare(
|
240 |
+
"INSERT INTO {$wpdb->prefix}blc_instances
|
241 |
+
( link_id, container_type, container_id, container_field, parser_type, link_text, link_context, raw_url )
|
242 |
+
VALUES( %d, %s, %d, %s, %s, %s, %s, %s )",
|
243 |
+
$this->link_id,
|
244 |
+
$this->container_type,
|
245 |
+
$this->container_id,
|
246 |
+
$this->container_field,
|
247 |
+
$this->parser_type,
|
248 |
+
$this->link_text,
|
249 |
+
$this->link_context,
|
250 |
+
$this->raw_url
|
251 |
+
)
|
252 |
+
);
|
253 |
+
|
254 |
+
$rez = false !== $rez;
|
255 |
+
|
256 |
+
if ( $rez ) {
|
257 |
+
$this->instance_id = $wpdb->insert_id;
|
258 |
+
//If the instance was successfully saved then it's no longer "new".
|
259 |
+
$this->is_new = ! $rez;
|
260 |
+
}
|
261 |
+
|
262 |
+
return $rez;
|
263 |
+
|
264 |
+
} else {
|
265 |
+
|
266 |
+
$rez = false !== $wpdb->query(
|
267 |
+
$wpdb->prepare(
|
268 |
+
"UPDATE {$wpdb->prefix}blc_instances
|
269 |
+
SET
|
270 |
+
link_id = %d,
|
271 |
+
container_type = %s,
|
272 |
+
container_id = %d,
|
273 |
+
container_field = %s,
|
274 |
+
parser_type = %s,
|
275 |
+
link_text = %s,
|
276 |
+
link_context = %s,
|
277 |
+
raw_url = %s
|
278 |
+
WHERE instance_id = %d",
|
279 |
+
$this->link_id,
|
280 |
+
$this->container_type,
|
281 |
+
$this->container_id,
|
282 |
+
$this->container_field,
|
283 |
+
$this->parser_type,
|
284 |
+
$this->link_text,
|
285 |
+
$this->link_context,
|
286 |
+
$this->raw_url,
|
287 |
+
$this->instance_id
|
288 |
+
)
|
289 |
+
);
|
290 |
+
|
291 |
+
if ( $rez ) {
|
292 |
+
//FB::info($this, "Instance updated");
|
293 |
+
} else {
|
294 |
+
//FB::error("DB error while updating instance {$this->instance_id} : {$wpdb->last_error}");
|
295 |
+
}
|
296 |
+
|
297 |
+
return $rez;
|
298 |
+
|
299 |
+
}
|
300 |
+
}
|
301 |
+
|
302 |
+
/**
|
303 |
+
* Get the URL associated with this instance.
|
304 |
+
*
|
305 |
+
* @return string The associated URL, or an empty string if the instance is currently not assigned to any link.
|
306 |
+
*/
|
307 |
+
function get_url() {
|
308 |
+
$link = $this->get_link();
|
309 |
+
|
310 |
+
if ( ! is_null( $link ) ) {
|
311 |
+
return $link->url;
|
312 |
+
} else {
|
313 |
+
return '';
|
314 |
+
}
|
315 |
+
}
|
316 |
+
|
317 |
+
/**
|
318 |
+
* Get the container object associated with this link instance
|
319 |
+
*
|
320 |
+
* @return blcContainer|null
|
321 |
+
*/
|
322 |
+
function get_container() {
|
323 |
+
if ( is_null( $this->_container ) ) {
|
324 |
+
$this->_container = blcContainerHelper::get_container( array( $this->container_type, $this->container_id ) );
|
325 |
+
}
|
326 |
+
|
327 |
+
return $this->_container;
|
328 |
+
}
|
329 |
+
|
330 |
+
/**
|
331 |
+
* Set a new container for the link instance.
|
332 |
+
*
|
333 |
+
* @param blcContainer $new_container
|
334 |
+
* @param string $field
|
335 |
+
* @return void
|
336 |
+
*/
|
337 |
+
function set_container( &$new_container, $field = '' ) {
|
338 |
+
$this->_container = &$new_container;
|
339 |
+
|
340 |
+
$this->container_field = $field;
|
341 |
+
|
342 |
+
if ( ! is_null( $new_container ) ) {
|
343 |
+
$this->container_type = $new_container->container_type;
|
344 |
+
$this->container_id = $new_container->container_id;
|
345 |
+
} else {
|
346 |
+
$this->container_type = '';
|
347 |
+
$this->container_id = 0;
|
348 |
+
}
|
349 |
+
}
|
350 |
+
|
351 |
+
/**
|
352 |
+
* Get the parser associated with this link instance.
|
353 |
+
*
|
354 |
+
* @return blcParser|null
|
355 |
+
*/
|
356 |
+
function get_parser() {
|
357 |
+
if ( is_null( $this->_parser ) ) {
|
358 |
+
$this->_parser = blcParserHelper::get_parser( $this->parser_type );
|
359 |
+
}
|
360 |
+
|
361 |
+
return $this->_parser;
|
362 |
+
}
|
363 |
+
|
364 |
+
/**
|
365 |
+
* Set a new parser fo this link instance.
|
366 |
+
*
|
367 |
+
* @param blcParser|null $new_parser
|
368 |
+
* @return void
|
369 |
+
*/
|
370 |
+
function set_parser( &$new_parser ) {
|
371 |
+
$this->_parser = &$new_parser;
|
372 |
+
|
373 |
+
if ( is_null( $new_parser ) ) {
|
374 |
+
$this->parser_type = '';
|
375 |
+
} else {
|
376 |
+
$this->parser_type = $new_parser->parser_type;
|
377 |
+
}
|
378 |
+
}
|
379 |
+
|
380 |
+
/**
|
381 |
+
* Get the link object associated with this link intance.
|
382 |
+
*
|
383 |
+
* @return blcLink|null
|
384 |
+
*/
|
385 |
+
function get_link() {
|
386 |
+
if ( ! is_null( $this->_link ) ) {
|
387 |
+
return $this->_link;
|
388 |
+
}
|
389 |
+
|
390 |
+
if ( empty( $this->link_id ) ) {
|
391 |
+
return null;
|
392 |
+
}
|
393 |
+
|
394 |
+
$this->_link = new blcLink( $this->link_id );
|
395 |
+
return $this->_link;
|
396 |
+
}
|
397 |
+
|
398 |
+
/**
|
399 |
+
* Set the link associated with this link instance.
|
400 |
+
*
|
401 |
+
* @param blcLink $new_link
|
402 |
+
* @return void
|
403 |
+
*/
|
404 |
+
function set_link( $new_link ) {
|
405 |
+
$this->_link = $new_link;
|
406 |
+
|
407 |
+
if ( is_null( $new_link ) ) {
|
408 |
+
$this->link_id = 0;
|
409 |
+
} else {
|
410 |
+
$this->link_id = $new_link->link_id;
|
411 |
+
}
|
412 |
+
}
|
413 |
+
|
414 |
+
/**
|
415 |
+
* Get the link text for printing in the "Broken Links" table.
|
416 |
+
*
|
417 |
+
* @param string $context How to filter the link text. Optional, defaults to 'display'.
|
418 |
+
* @return string HTML
|
419 |
+
*/
|
420 |
+
function ui_get_link_text( $context = 'display' ) {
|
421 |
+
$parser = $this->get_parser();
|
422 |
+
|
423 |
+
if ( ! is_null( $parser ) ) {
|
424 |
+
$text = $parser->ui_get_link_text( $this, $context );
|
425 |
+
} else {
|
426 |
+
$text = strip_tags( $this->link_text );
|
427 |
+
}
|
428 |
+
|
429 |
+
if ( empty( $text ) ) {
|
430 |
+
$text = '<em>(None)</em>';
|
431 |
+
}
|
432 |
+
|
433 |
+
return $text;
|
434 |
+
}
|
435 |
+
|
436 |
+
/**
|
437 |
+
* Get action links that should be displayed in the "Source" column of the "Broken Links" table.
|
438 |
+
*
|
439 |
+
* @return array An array of HTML links.
|
440 |
+
*/
|
441 |
+
function ui_get_action_links() {
|
442 |
+
//The container is responsible for generating the links.
|
443 |
+
$container = $this->get_container();
|
444 |
+
if ( ! is_null( $container ) ) {
|
445 |
+
return $container->ui_get_action_links( $this->container_field );
|
446 |
+
} else {
|
447 |
+
//No valid container = no links.
|
448 |
+
return array();
|
449 |
+
}
|
450 |
+
}
|
451 |
+
|
452 |
+
/**
|
453 |
+
* Get the HTML describing the "source" of the instance. For example, for links found in posts,
|
454 |
+
* this could be the post title.
|
455 |
+
*
|
456 |
+
* @param string $context How to filter the output. Optional, defaults to 'display'.
|
457 |
+
* @return string HTML
|
458 |
+
*/
|
459 |
+
function ui_get_source( $context = 'display' ) {
|
460 |
+
//The container is also responsible for generating the "Source" column HTML.
|
461 |
+
$container = $this->get_container();
|
462 |
+
if ( ! is_null( $container ) ) {
|
463 |
+
return $container->ui_get_source( $this->container_field, $context );
|
464 |
+
} else {
|
465 |
+
//No valid container = generate some bare-bones debug output.
|
466 |
+
return sprintf( '%s[%d] : %s', $this->container_type, $this->container_id, $this->container_field );
|
467 |
+
}
|
468 |
+
}
|
469 |
+
|
470 |
+
/**
|
471 |
+
* Check if the link text associated with this instance can be edited.
|
472 |
+
*
|
473 |
+
* @return bool
|
474 |
+
*/
|
475 |
+
public function is_link_text_editable() {
|
476 |
+
$parser = $this->get_parser();
|
477 |
+
if ( null === $parser ) {
|
478 |
+
return false;
|
479 |
+
}
|
480 |
+
return $parser->is_link_text_editable();
|
481 |
+
}
|
482 |
+
|
483 |
+
/**
|
484 |
+
* Check if the URL of this instance can be edited.
|
485 |
+
*
|
486 |
+
* @return bool
|
487 |
+
*/
|
488 |
+
public function is_url_editable() {
|
489 |
+
$parser = $this->get_parser();
|
490 |
+
if ( null === $parser ) {
|
491 |
+
return false;
|
492 |
+
}
|
493 |
+
return $parser->is_url_editable();
|
494 |
+
}
|
495 |
+
|
496 |
+
}
|
497 |
+
|
498 |
+
/**
|
499 |
+
* Get all link instances associated with one or more links.
|
500 |
+
*
|
501 |
+
* @param array $link_ids Array of link IDs.
|
502 |
+
* @param string $purpose An optional code indicating how the instances will be used. Available predefined constants : BLC_FOR_DISPLAY, BLC_FOR_EDITING
|
503 |
+
* @param bool $load_containers Preload containers regardless of purpose. Defaults to false.
|
504 |
+
* @param bool $load_wrapped_objects Preload wrapped objects regardless of purpose. Defaults to false.
|
505 |
+
* @param bool $include_invalid Include instances that refer to not-loaded containers or parsers. Defaults to false.
|
506 |
+
* @return blcLinkInstance[] An array indexed by link ID. Each item of the array will be an array of blcLinkInstance objects.
|
507 |
+
*/
|
508 |
+
function blc_get_instances( $link_ids, $purpose = '', $load_containers = false, $load_wrapped_objects = false, $include_invalid = false ) {
|
509 |
+
global $wpdb; /** @var wpdb $wpdb */
|
510 |
+
|
511 |
+
if ( empty( $link_ids ) ) {
|
512 |
+
return array();
|
513 |
+
}
|
514 |
+
|
515 |
+
$link_ids_in = implode( ', ', $link_ids );
|
516 |
+
|
517 |
+
$q = "SELECT * FROM {$wpdb->prefix}blc_instances WHERE link_id IN ($link_ids_in)";
|
518 |
+
|
519 |
+
//Skip instances that reference containers or parsers that aren't currently loaded
|
520 |
+
if ( ! $include_invalid ) {
|
521 |
+
$manager = blcModuleManager::getInstance();
|
522 |
+
$active_containers = $manager->get_escaped_ids( 'container' );
|
523 |
+
$active_parsers = $manager->get_escaped_ids( 'parser' );
|
524 |
+
|
525 |
+
$q .= " AND container_type IN ({$active_containers}) ";
|
526 |
+
$q .= " AND parser_type IN ({$active_parsers}) ";
|
527 |
+
}
|
528 |
+
|
529 |
+
$results = $wpdb->get_results( $q, ARRAY_A ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
530 |
+
|
531 |
+
if ( empty( $results ) ) {
|
532 |
+
return array();
|
533 |
+
}
|
534 |
+
|
535 |
+
//Also retrieve the containers, if it could be useful.
|
536 |
+
$load_containers = $load_containers || in_array( $purpose, array( BLC_FOR_DISPLAY, BLC_FOR_EDITING ) );
|
537 |
+
if ( $load_containers ) {
|
538 |
+
//Collect a list of (container_type, container_id) pairs
|
539 |
+
$container_ids = array();
|
540 |
+
|
541 |
+
foreach ( $results as $result ) {
|
542 |
+
array_push(
|
543 |
+
$container_ids,
|
544 |
+
array( $result['container_type'], intval( $result['container_id'] ) )
|
545 |
+
);
|
546 |
+
}
|
547 |
+
$containers = blcContainerHelper::get_containers( $container_ids, $purpose, '', $load_wrapped_objects );
|
548 |
+
}
|
549 |
+
|
550 |
+
//Create an object for each instance and group them by link ID
|
551 |
+
$instances = array();
|
552 |
+
foreach ( $results as $result ) {
|
553 |
+
$instance = new blcLinkInstance( $result );
|
554 |
+
|
555 |
+
//Assign a container to the link instance, if available
|
556 |
+
if ( $load_containers && ! empty( $containers ) ) {
|
557 |
+
$key = $instance->container_type . '|' . $instance->container_id;
|
558 |
+
if ( isset( $containers[ $key ] ) ) {
|
559 |
+
$instance->_container = $containers[ $key ];
|
560 |
+
}
|
561 |
+
}
|
562 |
+
|
563 |
+
if ( isset( $instances[ $instance->link_id ] ) ) {
|
564 |
+
array_push( $instances[ $instance->link_id ], $instance );
|
565 |
+
} else {
|
566 |
+
$instances[ $instance->link_id ] = array( $instance );
|
567 |
+
}
|
568 |
+
}
|
569 |
+
|
570 |
+
return $instances;
|
571 |
+
}
|
572 |
+
|
573 |
+
/**
|
574 |
+
* Get the number of instances that reference only currently loaded containers and parsers.
|
575 |
+
*
|
576 |
+
* @return int
|
577 |
+
*/
|
578 |
+
function blc_get_usable_instance_count() {
|
579 |
+
global $wpdb; /** @var wpdb $wpdb */
|
580 |
+
|
581 |
+
$q = "SELECT COUNT(instance_id) FROM {$wpdb->prefix}blc_instances WHERE 1";
|
582 |
+
|
583 |
+
//Skip instances that reference containers or parsers that aren't currently loaded
|
584 |
+
$manager = blcModuleManager::getInstance();
|
585 |
+
$active_containers = $manager->get_escaped_ids( 'container' );
|
586 |
+
$active_parsers = $manager->get_escaped_ids( 'parser' );
|
587 |
+
|
588 |
+
$q .= " AND container_type IN ({$active_containers}) ";
|
589 |
+
$q .= " AND parser_type IN ({$active_parsers}) ";
|
590 |
+
|
591 |
+
return $wpdb->get_var( $q ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
592 |
+
}
|
593 |
+
|
594 |
+
/**
|
595 |
+
* Remove instances that reference invalid containers or containers/parsers that are not currently loaded
|
596 |
+
*
|
597 |
+
* @return bool
|
598 |
+
*/
|
599 |
+
function blc_cleanup_instances() {
|
600 |
+
global $wpdb; /** @var wpdb $wpdb */
|
601 |
+
global $blclog;
|
602 |
+
|
603 |
+
//Delete all instances that reference non-existent containers
|
604 |
+
$start = microtime( true );
|
605 |
+
$q = "DELETE instances.*
|
606 |
+
FROM
|
607 |
+
{$wpdb->prefix}blc_instances AS instances LEFT JOIN {$wpdb->prefix}blc_synch AS synch
|
608 |
+
ON instances.container_type = synch.container_type AND instances.container_id = synch.container_id
|
609 |
+
WHERE
|
610 |
+
synch.container_id IS NULL";
|
611 |
+
$rez = $wpdb->query( $q ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
612 |
+
$elapsed = microtime( true ) - $start;
|
613 |
+
$blclog->log( sprintf( '... %d instances deleted in %.3f seconds', $wpdb->rows_affected, $elapsed ) );
|
614 |
+
|
615 |
+
//Delete instances that reference containers and parsers that are no longer active
|
616 |
+
$start = microtime( true );
|
617 |
+
$manager = blcModuleManager::getInstance();
|
618 |
+
$active_containers = $manager->get_escaped_ids( 'container' );
|
619 |
+
$active_parsers = $manager->get_escaped_ids( 'parser' );
|
620 |
+
|
621 |
+
$q = "DELETE instances.*
|
622 |
+
FROM {$wpdb->prefix}blc_instances AS instances
|
623 |
+
WHERE
|
624 |
+
instances.container_type NOT IN ({$active_containers}) OR
|
625 |
+
instances.parser_type NOT IN ({$active_parsers})";
|
626 |
+
$rez2 = $wpdb->query( $q ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
627 |
+
$elapsed = microtime( true ) - $start;
|
628 |
+
$blclog->log( sprintf( '... %d more instances deleted in %.3f seconds', $wpdb->rows_affected, $elapsed ) );
|
629 |
+
|
630 |
+
return ( false !== $rez ) && ( false !== $rez2 );
|
631 |
+
}
|
632 |
+
}//class_exists
|
633 |
+
|
|
|
|
|
|
|
|
|
|
|
|
includes/link-query.php
CHANGED
@@ -1,870 +1,879 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Class for querying, sorting and filtering links.
|
5 |
-
* Used as a singleton.
|
6 |
-
*
|
7 |
-
* @package Broken Link Checker
|
8 |
-
* @access public
|
9 |
-
*/
|
10 |
-
class blcLinkQuery {
|
11 |
-
|
12 |
-
var $native_filters;
|
13 |
-
var $search_filter;
|
14 |
-
var $custom_filters = array();
|
15 |
-
|
16 |
-
var $valid_url_params = array();
|
17 |
-
|
18 |
-
function __construct(){
|
19 |
-
//Init. the available native filters.
|
20 |
-
$this->native_filters = array(
|
21 |
-
'all'
|
22 |
-
'params'
|
23 |
-
'where_expr' => '1',
|
24 |
-
),
|
25 |
-
'name'
|
26 |
-
'heading'
|
27 |
-
'heading_zero' => __('No links found (yet)', 'broken-link-checker'),
|
28 |
-
'native'
|
29 |
-
),
|
30 |
-
|
31 |
-
'broken'
|
32 |
-
'params'
|
33 |
-
'where_expr'
|
34 |
-
's_include_dismissed' => false,
|
35 |
-
),
|
36 |
-
'name'
|
37 |
-
'heading'
|
38 |
-
'heading_zero' => __('No broken links found', 'broken-link-checker'),
|
39 |
-
'native'
|
40 |
-
),
|
41 |
-
'warnings'
|
42 |
-
'params'
|
43 |
-
'where_expr'
|
44 |
-
's_include_dismissed' => false,
|
45 |
-
),
|
46 |
-
'name'
|
47 |
-
'heading'
|
48 |
-
'heading_zero' => __('No warnings found', 'broken-link-checker'),
|
49 |
-
'native'
|
50 |
-
),
|
51 |
-
'redirects' => array(
|
52 |
-
'params'
|
53 |
-
'where_expr'
|
54 |
-
's_include_dismissed' => false,
|
55 |
-
),
|
56 |
-
'name'
|
57 |
-
'heading'
|
58 |
-
'heading_zero' => __('No redirects found', 'broken-link-checker'),
|
59 |
-
'native'
|
60 |
-
),
|
61 |
-
|
62 |
-
'dismissed' => array(
|
63 |
-
'params'
|
64 |
-
'where_expr' => '( dismissed = 1 )',
|
65 |
-
),
|
66 |
-
'name'
|
67 |
-
'heading'
|
68 |
-
'heading_zero' => __('No dismissed links found', 'broken-link-checker'),
|
69 |
-
'native'
|
70 |
-
),
|
71 |
-
);
|
72 |
-
|
73 |
-
//The user can turn off warnings. In that case, all errors will show up in the "broken" filter instead.
|
74 |
-
$conf = blc_get_configuration();
|
75 |
-
if (
|
76 |
-
unset($this->native_filters['warnings']);
|
77 |
-
}
|
78 |
-
|
79 |
-
//Create the special "search" filter
|
80 |
-
$this->search_filter = array(
|
81 |
-
'name'
|
82 |
-
'heading'
|
83 |
-
'heading_zero'
|
84 |
-
'params'
|
85 |
-
'use_url_params' => true,
|
86 |
-
'hidden'
|
87 |
-
);
|
88 |
-
|
89 |
-
//These search arguments may be passed via the URL if the filter's 'use_url_params' field is set to True.
|
90 |
-
//They map to the fields of the search form on the Tools -> Broken Links page. Only these arguments
|
91 |
-
//can be used in user-defined filters.
|
92 |
-
$this->valid_url_params = array(
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
);
|
101 |
-
}
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
$
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
'
|
129 |
-
'
|
130 |
-
'
|
131 |
-
'
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
$name,
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
return
|
166 |
-
}
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
return
|
190 |
-
}
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
return
|
211 |
-
}
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
$
|
295 |
-
|
296 |
-
$
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
$s_parser_type =
|
302 |
-
}
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
$s_container_type =
|
308 |
-
}
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
$pieces[] = "instances.parser_type
|
320 |
-
}
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
$pieces[] = "instances.container_type
|
335 |
-
}
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
$
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
//
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
$
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
$
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
//
|
408 |
-
$
|
409 |
-
|
410 |
-
$
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
//
|
454 |
-
|
455 |
-
|
456 |
-
$
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
'
|
465 |
-
'
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
|
485 |
-
//
|
486 |
-
|
487 |
-
|
488 |
-
$
|
489 |
-
|
490 |
-
|
491 |
-
$
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
'
|
497 |
-
'
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
-
'
|
528 |
-
'
|
529 |
-
'
|
530 |
-
'
|
531 |
-
'
|
532 |
-
'
|
533 |
-
'
|
534 |
-
'
|
535 |
-
'
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
-
$
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
$
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
-
|
611 |
-
|
612 |
-
$
|
613 |
-
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
*
|
620 |
-
|
621 |
-
|
622 |
-
|
623 |
-
|
624 |
-
|
625 |
-
$
|
626 |
-
|
627 |
-
|
628 |
-
|
629 |
-
|
630 |
-
|
631 |
-
|
632 |
-
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
|
638 |
-
|
639 |
-
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
|
646 |
-
|
647 |
-
|
648 |
-
|
649 |
-
|
650 |
-
|
651 |
-
|
652 |
-
|
653 |
-
|
654 |
-
|
655 |
-
|
656 |
-
|
657 |
-
|
658 |
-
|
659 |
-
|
660 |
-
|
661 |
-
|
662 |
-
|
663 |
-
|
664 |
-
|
665 |
-
|
666 |
-
|
667 |
-
|
668 |
-
|
669 |
-
|
670 |
-
|
671 |
-
|
672 |
-
|
673 |
-
|
674 |
-
|
675 |
-
|
676 |
-
|
677 |
-
|
678 |
-
|
679 |
-
|
680 |
-
|
681 |
-
|
682 |
-
|
683 |
-
|
684 |
-
|
685 |
-
|
686 |
-
*
|
687 |
-
*
|
688 |
-
|
689 |
-
|
690 |
-
|
691 |
-
|
692 |
-
|
693 |
-
|
694 |
-
|
695 |
-
|
696 |
-
|
697 |
-
|
698 |
-
|
699 |
-
|
700 |
-
|
701 |
-
|
702 |
-
|
703 |
-
|
704 |
-
|
705 |
-
|
706 |
-
|
707 |
-
|
708 |
-
|
709 |
-
|
710 |
-
|
711 |
-
|
712 |
-
|
713 |
-
$
|
714 |
-
|
715 |
-
|
716 |
-
|
717 |
-
|
718 |
-
|
719 |
-
|
720 |
-
|
721 |
-
|
722 |
-
|
723 |
-
|
724 |
-
|
725 |
-
|
726 |
-
|
727 |
-
|
728 |
-
|
729 |
-
|
730 |
-
|
731 |
-
|
732 |
-
|
733 |
-
|
734 |
-
|
735 |
-
|
736 |
-
|
737 |
-
|
738 |
-
|
739 |
-
|
740 |
-
|
741 |
-
|
742 |
-
|
743 |
-
*
|
744 |
-
*
|
745 |
-
*
|
746 |
-
*
|
747 |
-
* '
|
748 |
-
* '
|
749 |
-
* '
|
750 |
-
*
|
751 |
-
*
|
752 |
-
*
|
753 |
-
*
|
754 |
-
*
|
755 |
-
* @param string $
|
756 |
-
* @param
|
757 |
-
* @
|
758 |
-
|
759 |
-
|
760 |
-
|
761 |
-
|
762 |
-
|
763 |
-
|
764 |
-
|
765 |
-
|
766 |
-
|
767 |
-
|
768 |
-
|
769 |
-
|
770 |
-
|
771 |
-
|
772 |
-
|
773 |
-
|
774 |
-
|
775 |
-
|
776 |
-
|
777 |
-
|
778 |
-
|
779 |
-
|
780 |
-
|
781 |
-
|
782 |
-
|
783 |
-
|
784 |
-
|
785 |
-
|
786 |
-
|
787 |
-
|
788 |
-
|
789 |
-
|
790 |
-
|
791 |
-
|
792 |
-
|
793 |
-
|
794 |
-
|
795 |
-
|
796 |
-
|
797 |
-
|
798 |
-
|
799 |
-
|
800 |
-
|
801 |
-
|
802 |
-
|
803 |
-
|
804 |
-
|
805 |
-
|
806 |
-
|
807 |
-
|
808 |
-
|
809 |
-
|
810 |
-
|
811 |
-
|
812 |
-
|
813 |
-
|
814 |
-
}
|
815 |
-
|
816 |
-
|
817 |
-
|
818 |
-
|
819 |
-
|
820 |
-
|
821 |
-
|
822 |
-
|
823 |
-
|
824 |
-
|
825 |
-
|
826 |
-
|
827 |
-
|
828 |
-
|
829 |
-
|
830 |
-
|
831 |
-
|
832 |
-
|
833 |
-
|
834 |
-
|
835 |
-
|
836 |
-
|
837 |
-
|
838 |
-
|
839 |
-
|
840 |
-
|
841 |
-
|
842 |
-
|
843 |
-
|
844 |
-
|
845 |
-
*
|
846 |
-
*
|
847 |
-
*
|
848 |
-
*
|
849 |
-
* '
|
850 |
-
* '
|
851 |
-
* '
|
852 |
-
* '
|
853 |
-
* '
|
854 |
-
* '
|
855 |
-
* '
|
856 |
-
* '
|
857 |
-
* '
|
858 |
-
*
|
859 |
-
*
|
860 |
-
*
|
861 |
-
*
|
862 |
-
*
|
863 |
-
*
|
864 |
-
*
|
865 |
-
|
866 |
-
|
867 |
-
|
868 |
-
|
869 |
-
|
870 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Class for querying, sorting and filtering links.
|
5 |
+
* Used as a singleton.
|
6 |
+
*
|
7 |
+
* @package Broken Link Checker
|
8 |
+
* @access public
|
9 |
+
*/
|
10 |
+
class blcLinkQuery {
|
11 |
+
|
12 |
+
var $native_filters;
|
13 |
+
var $search_filter;
|
14 |
+
var $custom_filters = array();
|
15 |
+
|
16 |
+
var $valid_url_params = array();
|
17 |
+
|
18 |
+
function __construct() {
|
19 |
+
//Init. the available native filters.
|
20 |
+
$this->native_filters = array(
|
21 |
+
'all' => array(
|
22 |
+
'params' => array(
|
23 |
+
'where_expr' => '1',
|
24 |
+
),
|
25 |
+
'name' => __( 'All', 'broken-link-checker' ),
|
26 |
+
'heading' => __( 'Detected Links', 'broken-link-checker' ),
|
27 |
+
'heading_zero' => __( 'No links found (yet)', 'broken-link-checker' ),
|
28 |
+
'native' => true,
|
29 |
+
),
|
30 |
+
|
31 |
+
'broken' => array(
|
32 |
+
'params' => array(
|
33 |
+
'where_expr' => '( broken = 1 )',
|
34 |
+
's_include_dismissed' => false,
|
35 |
+
),
|
36 |
+
'name' => __( 'Broken', 'broken-link-checker' ),
|
37 |
+
'heading' => __( 'Broken Links', 'broken-link-checker' ),
|
38 |
+
'heading_zero' => __( 'No broken links found', 'broken-link-checker' ),
|
39 |
+
'native' => true,
|
40 |
+
),
|
41 |
+
'warnings' => array(
|
42 |
+
'params' => array(
|
43 |
+
'where_expr' => '( warning = 1 )',
|
44 |
+
's_include_dismissed' => false,
|
45 |
+
),
|
46 |
+
'name' => _x( 'Warnings', 'filter name', 'broken-link-checker' ),
|
47 |
+
'heading' => _x( 'Warnings', 'filter heading', 'broken-link-checker' ),
|
48 |
+
'heading_zero' => __( 'No warnings found', 'broken-link-checker' ),
|
49 |
+
'native' => true,
|
50 |
+
),
|
51 |
+
'redirects' => array(
|
52 |
+
'params' => array(
|
53 |
+
'where_expr' => '( redirect_count > 0 )',
|
54 |
+
's_include_dismissed' => false,
|
55 |
+
),
|
56 |
+
'name' => __( 'Redirects', 'broken-link-checker' ),
|
57 |
+
'heading' => __( 'Redirected Links', 'broken-link-checker' ),
|
58 |
+
'heading_zero' => __( 'No redirects found', 'broken-link-checker' ),
|
59 |
+
'native' => true,
|
60 |
+
),
|
61 |
+
|
62 |
+
'dismissed' => array(
|
63 |
+
'params' => array(
|
64 |
+
'where_expr' => '( dismissed = 1 )',
|
65 |
+
),
|
66 |
+
'name' => __( 'Dismissed', 'broken-link-checker' ),
|
67 |
+
'heading' => __( 'Dismissed Links', 'broken-link-checker' ),
|
68 |
+
'heading_zero' => __( 'No dismissed links found', 'broken-link-checker' ),
|
69 |
+
'native' => true,
|
70 |
+
),
|
71 |
+
);
|
72 |
+
|
73 |
+
//The user can turn off warnings. In that case, all errors will show up in the "broken" filter instead.
|
74 |
+
$conf = blc_get_configuration();
|
75 |
+
if ( ! $conf->get( 'warnings_enabled' ) ) {
|
76 |
+
unset( $this->native_filters['warnings'] );
|
77 |
+
}
|
78 |
+
|
79 |
+
//Create the special "search" filter
|
80 |
+
$this->search_filter = array(
|
81 |
+
'name' => __( 'Search', 'broken-link-checker' ),
|
82 |
+
'heading' => __( 'Search Results', 'broken-link-checker' ),
|
83 |
+
'heading_zero' => __( 'No links found for your query', 'broken-link-checker' ),
|
84 |
+
'params' => array(),
|
85 |
+
'use_url_params' => true,
|
86 |
+
'hidden' => true,
|
87 |
+
);
|
88 |
+
|
89 |
+
//These search arguments may be passed via the URL if the filter's 'use_url_params' field is set to True.
|
90 |
+
//They map to the fields of the search form on the Tools -> Broken Links page. Only these arguments
|
91 |
+
//can be used in user-defined filters.
|
92 |
+
$this->valid_url_params = array(
|
93 |
+
's_link_text',
|
94 |
+
's_link_url',
|
95 |
+
's_parser_type',
|
96 |
+
's_container_type',
|
97 |
+
's_link_type',
|
98 |
+
's_http_code',
|
99 |
+
's_filter',
|
100 |
+
);
|
101 |
+
}
|
102 |
+
|
103 |
+
//phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
|
104 |
+
static function getInstance() {
|
105 |
+
static $instance = null;
|
106 |
+
if ( is_null( $instance ) ) {
|
107 |
+
$instance = new blcLinkQuery;
|
108 |
+
}
|
109 |
+
return $instance;
|
110 |
+
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Load and return the list of user-defined link filters.
|
114 |
+
*
|
115 |
+
* @return array An array of custom filter definitions. If there are no custom filters defined returns an empty array.
|
116 |
+
*/
|
117 |
+
function load_custom_filters() {
|
118 |
+
global $wpdb; /** @var wpdb $wpdb */
|
119 |
+
|
120 |
+
$filter_data = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}blc_filters ORDER BY name ASC", ARRAY_A );
|
121 |
+
$filters = array();
|
122 |
+
|
123 |
+
if ( ! empty( $filter_data ) ) {
|
124 |
+
foreach ( $filter_data as $data ) {
|
125 |
+
wp_parse_str( $data['params'], $params );
|
126 |
+
|
127 |
+
$filters[ 'f' . $data['id'] ] = array(
|
128 |
+
'name' => $data['name'],
|
129 |
+
'params' => $params,
|
130 |
+
'heading' => ucwords( $data['name'] ),
|
131 |
+
'heading_zero' => __( 'No links found for your query', 'broken-link-checker' ),
|
132 |
+
'custom' => true,
|
133 |
+
);
|
134 |
+
}
|
135 |
+
}
|
136 |
+
|
137 |
+
$this->custom_filters = $filters;
|
138 |
+
|
139 |
+
return $filters;
|
140 |
+
}
|
141 |
+
|
142 |
+
/**
|
143 |
+
* Add a custom link filter.
|
144 |
+
*
|
145 |
+
* @param string $name Filter name.
|
146 |
+
* @param string|array $params Filter params. Either as a query string, or an array.
|
147 |
+
* @return string|bool The ID of the newly added filter, or False.
|
148 |
+
*/
|
149 |
+
function create_custom_filter( $name, $params ) {
|
150 |
+
global $wpdb; /** @var wpdb $wpdb */
|
151 |
+
|
152 |
+
if ( is_array( $params ) ) {
|
153 |
+
$params = http_build_query( $params, null, '&' );
|
154 |
+
}
|
155 |
+
|
156 |
+
//Save the new filter
|
157 |
+
$q = $wpdb->prepare(
|
158 |
+
"INSERT INTO {$wpdb->prefix}blc_filters(name, params) VALUES (%s, %s)",
|
159 |
+
$name,
|
160 |
+
$params
|
161 |
+
);
|
162 |
+
|
163 |
+
if ( $wpdb->query( $q ) !== false ) { //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
164 |
+
$filter_id = 'f' . $wpdb->insert_id;
|
165 |
+
return $filter_id;
|
166 |
+
} else {
|
167 |
+
return false;
|
168 |
+
}
|
169 |
+
}
|
170 |
+
|
171 |
+
/**
|
172 |
+
* Delete a custom filter
|
173 |
+
*
|
174 |
+
* @param string $filter_id
|
175 |
+
* @return bool True on success, False if a database error occured.
|
176 |
+
*/
|
177 |
+
function delete_custom_filter( $filter_id ) {
|
178 |
+
global $wpdb; /** @var wpdb $wpdb */
|
179 |
+
|
180 |
+
if ( ! isset( $filter_id ) ) {
|
181 |
+
$filter_id = $_POST['filter_id'];
|
182 |
+
}
|
183 |
+
//Remove the "f" character from the filter ID to get its database key
|
184 |
+
$filter_id = intval( ltrim( $filter_id, 'f' ) );
|
185 |
+
|
186 |
+
//Try to delete the filter
|
187 |
+
$q = $wpdb->prepare( "DELETE FROM {$wpdb->prefix}blc_filters WHERE id = %d", $filter_id );
|
188 |
+
if ( false !== $wpdb->query( $q ) ) { //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
189 |
+
return true;
|
190 |
+
} else {
|
191 |
+
return false;
|
192 |
+
}
|
193 |
+
}
|
194 |
+
|
195 |
+
function get_filters() {
|
196 |
+
$filters = array_merge( $this->native_filters, $this->custom_filters );
|
197 |
+
$filters['search'] = $this->search_filter;
|
198 |
+
return $filters;
|
199 |
+
}
|
200 |
+
|
201 |
+
/**
|
202 |
+
* Get a link search filter by filter ID.
|
203 |
+
*
|
204 |
+
* @param string $filter_id
|
205 |
+
* @return array|null
|
206 |
+
*/
|
207 |
+
function get_filter( $filter_id ) {
|
208 |
+
$filters = $this->get_filters();
|
209 |
+
if ( isset( $filters[ $filter_id ] ) ) {
|
210 |
+
return $filters[ $filter_id ];
|
211 |
+
} else {
|
212 |
+
return null;
|
213 |
+
}
|
214 |
+
}
|
215 |
+
|
216 |
+
/**
|
217 |
+
* Get link search parameters from the specified filter.
|
218 |
+
*
|
219 |
+
* @param array $filter
|
220 |
+
* @return array An array of parameters suitable for use with blcLinkQuery::get_links()
|
221 |
+
*/
|
222 |
+
function get_search_params( $filter = null ) {
|
223 |
+
//If present, the filter's parameters may be saved either as an array or a string.
|
224 |
+
$params = array();
|
225 |
+
if ( ! empty( $filter ) && ! empty( $filter['params'] ) ) {
|
226 |
+
$params = $filter['params'];
|
227 |
+
if ( is_string( $params ) ) {
|
228 |
+
wp_parse_str( $params, $params );
|
229 |
+
}
|
230 |
+
}
|
231 |
+
|
232 |
+
//Merge in the parameters from the current request, if required
|
233 |
+
if ( isset( $filter['use_url_params'] ) && $filter['use_url_params'] ) {
|
234 |
+
$params = array_merge( $params, $this->get_url_search_params() );
|
235 |
+
}
|
236 |
+
|
237 |
+
return $params;
|
238 |
+
}
|
239 |
+
|
240 |
+
/**
|
241 |
+
* Extract search query parameters from the current URL
|
242 |
+
*
|
243 |
+
* @return array
|
244 |
+
*/
|
245 |
+
function get_url_search_params() {
|
246 |
+
$url_params = array();
|
247 |
+
foreach ( $_GET as $param => $value ) {
|
248 |
+
if ( in_array( $param, $this->valid_url_params ) ) {
|
249 |
+
$url_params[ $param ] = esc_html( $value );
|
250 |
+
}
|
251 |
+
}
|
252 |
+
return $url_params;
|
253 |
+
}
|
254 |
+
|
255 |
+
|
256 |
+
|
257 |
+
/**
|
258 |
+
* A helper method for parsing a list of search criteria and generating the parts of the SQL query.
|
259 |
+
*
|
260 |
+
* @see blcLinkQuery::get_links()
|
261 |
+
*
|
262 |
+
* @param array $params An array of search criteria.
|
263 |
+
* @return array 'where_exprs' - an array of search expressions, 'join_instances' - whether joining the instance table is required.
|
264 |
+
*/
|
265 |
+
function compile_search_params( $params ) {
|
266 |
+
global $wpdb; /** @var wpdb $wpdb */
|
267 |
+
|
268 |
+
//Track whether we'll need to left-join the instance table to run the query.
|
269 |
+
$join_instances = false;
|
270 |
+
|
271 |
+
//Generate the individual clauses of the WHERE expression and store them in an array.
|
272 |
+
$pieces = array();
|
273 |
+
|
274 |
+
//Convert parser and container type lists to arrays of valid values
|
275 |
+
$s_parser_type = array();
|
276 |
+
if ( ! empty( $params['s_parser_type'] ) ) {
|
277 |
+
$s_parser_type = $params['s_parser_type'];
|
278 |
+
if ( is_string( $s_parser_type ) ) {
|
279 |
+
$s_parser_type = preg_split( '/[,\s]+/', $s_parser_type );
|
280 |
+
}
|
281 |
+
}
|
282 |
+
|
283 |
+
$s_container_type = array();
|
284 |
+
if ( ! empty( $params['s_container_type'] ) ) {
|
285 |
+
$s_container_type = $params['s_container_type'];
|
286 |
+
if ( is_string( $s_container_type ) ) {
|
287 |
+
$s_container_type = preg_split( '/[,\s]+/', $s_container_type );
|
288 |
+
}
|
289 |
+
}
|
290 |
+
|
291 |
+
//Don't include links with instances that reference invalid (not currently loaded)
|
292 |
+
//containers and parsers (unless specifically told to also include invalid links).
|
293 |
+
if ( empty( $params['include_invalid'] ) ) {
|
294 |
+
$join_instances = true;
|
295 |
+
|
296 |
+
$module_manager = blcModuleManager::getInstance();
|
297 |
+
$loaded_containers = array_keys( $module_manager->get_active_by_category( 'container' ) );
|
298 |
+
$loaded_parsers = array_keys( $module_manager->get_active_by_category( 'parser' ) );
|
299 |
+
|
300 |
+
if ( empty( $s_parser_type ) ) {
|
301 |
+
$s_parser_type = $loaded_parsers;
|
302 |
+
} else {
|
303 |
+
$s_parser_type = array_intersect( $s_parser_type, $loaded_parsers );
|
304 |
+
}
|
305 |
+
|
306 |
+
if ( empty( $s_container_type ) ) {
|
307 |
+
$s_container_type = $loaded_containers;
|
308 |
+
} else {
|
309 |
+
$s_container_type = array_intersect( $s_container_type, $loaded_containers );
|
310 |
+
}
|
311 |
+
}
|
312 |
+
|
313 |
+
//Parser type should match the parser_type column in the instance table.
|
314 |
+
if ( ! empty( $s_parser_type ) ) {
|
315 |
+
$s_parser_type = array_map( 'trim', array_unique( $s_parser_type ) );
|
316 |
+
$s_parser_type = array_map( 'esc_sql', $s_parser_type );
|
317 |
+
|
318 |
+
if ( count( $s_parser_type ) == 1 ) {
|
319 |
+
$pieces[] = sprintf( "instances.parser_type = '%s'", reset( $s_parser_type ) );
|
320 |
+
} else {
|
321 |
+
$pieces[] = "instances.parser_type IN ('" . implode( "', '", $s_parser_type ) . "')";
|
322 |
+
}
|
323 |
+
|
324 |
+
$join_instances = true;
|
325 |
+
}
|
326 |
+
|
327 |
+
//Container type should match the container_type column in the instance table.
|
328 |
+
if ( ! empty( $s_container_type ) ) {
|
329 |
+
//Sanitize for use in SQL
|
330 |
+
$s_container_type = array_map( 'trim', array_unique( $s_container_type ) );
|
331 |
+
$s_container_type = array_map( 'esc_sql', $s_container_type );
|
332 |
+
|
333 |
+
if ( count( $s_container_type ) == 1 ) {
|
334 |
+
$pieces[] = sprintf( "instances.container_type = '%s'", reset( $s_container_type ) );
|
335 |
+
} else {
|
336 |
+
$pieces[] = "instances.container_type IN ('" . implode( "', '", $s_container_type ) . "')";
|
337 |
+
}
|
338 |
+
|
339 |
+
$join_instances = true;
|
340 |
+
}
|
341 |
+
|
342 |
+
//A part of the WHERE expression can be specified explicitly
|
343 |
+
if ( ! empty( $params['where_expr'] ) ) {
|
344 |
+
$pieces[] = $params['where_expr'];
|
345 |
+
$join_instances = $join_instances || ( stripos( $params['where_expr'], 'instances' ) !== false );
|
346 |
+
}
|
347 |
+
|
348 |
+
//List of allowed link ids (either an array or comma-separated)
|
349 |
+
if ( ! empty( $params['link_ids'] ) ) {
|
350 |
+
$link_ids = $params['link_ids'];
|
351 |
+
|
352 |
+
if ( is_string( $link_ids ) ) {
|
353 |
+
$link_ids = preg_split( '/[,\s]+/', $link_ids );
|
354 |
+
}
|
355 |
+
|
356 |
+
//Only accept non-zero integers
|
357 |
+
$sanitized_link_ids = array();
|
358 |
+
foreach ( $link_ids as $id ) {
|
359 |
+
$id = intval( $id );
|
360 |
+
if ( 0 !== $id ) {
|
361 |
+
$sanitized_link_ids[] = $id;
|
362 |
+
}
|
363 |
+
}
|
364 |
+
|
365 |
+
$pieces[] = 'links.link_id IN (' . implode( ', ', $sanitized_link_ids ) . ')';
|
366 |
+
}
|
367 |
+
|
368 |
+
//Anchor text - use LIKE search
|
369 |
+
if ( ! empty( $params['s_link_text'] ) ) {
|
370 |
+
$s_link_text = esc_sql( $this->esc_like( $params['s_link_text'] ) );
|
371 |
+
$s_link_text = str_replace( '*', '%', $s_link_text );
|
372 |
+
|
373 |
+
$pieces[] = '(instances.link_text LIKE "%' . $s_link_text . '%")';
|
374 |
+
$join_instances = true;
|
375 |
+
}
|
376 |
+
|
377 |
+
//URL - try to match both the initial URL and the final URL.
|
378 |
+
//There is limited wildcard support, e.g. "google.*/search" will match both
|
379 |
+
//"google.com/search" and "google.lv/search"
|
380 |
+
if ( ! empty( $params['s_link_url'] ) ) {
|
381 |
+
$s_link_url = esc_sql( $this->esc_like( $params['s_link_url'] ) );
|
382 |
+
$s_link_url = str_replace( '*', '%', $s_link_url );
|
383 |
+
|
384 |
+
$pieces[] = '(links.url LIKE "%' . $s_link_url . '%") OR ' .
|
385 |
+
'(links.final_url LIKE "%' . $s_link_url . '%")';
|
386 |
+
}
|
387 |
+
|
388 |
+
//Container ID should match... you guessed it - container_id
|
389 |
+
if ( ! empty( $params['s_container_id'] ) ) {
|
390 |
+
$s_container_id = intval( $params['s_container_id'] );
|
391 |
+
if ( 0 !== $s_container_id ) {
|
392 |
+
$pieces[] = "instances.container_id = $s_container_id";
|
393 |
+
$join_instances = true;
|
394 |
+
}
|
395 |
+
}
|
396 |
+
|
397 |
+
//Link type can match either the the parser_type or the container_type.
|
398 |
+
if ( ! empty( $params['s_link_type'] ) ) {
|
399 |
+
$s_link_type = esc_sql( $params['s_link_type'] );
|
400 |
+
$pieces[] = "instances.parser_type = '$s_link_type' OR instances.container_type='$s_link_type'";
|
401 |
+
$join_instances = true;
|
402 |
+
}
|
403 |
+
|
404 |
+
//HTTP code - the user can provide a list of HTTP response codes and code ranges.
|
405 |
+
//Example : 201,400-410,500
|
406 |
+
if ( ! empty( $params['s_http_code'] ) ) {
|
407 |
+
//Strip spaces.
|
408 |
+
$params['s_http_code'] = str_replace( ' ', '', $params['s_http_code'] );
|
409 |
+
//Split by comma
|
410 |
+
$codes = explode( ',', $params['s_http_code'] );
|
411 |
+
|
412 |
+
$individual_codes = array();
|
413 |
+
$ranges = array();
|
414 |
+
|
415 |
+
//Try to parse each response code or range. Invalid ones are simply ignored.
|
416 |
+
foreach ( $codes as $code ) {
|
417 |
+
if ( is_numeric( $code ) ) {
|
418 |
+
//It's a single number
|
419 |
+
$individual_codes[] = abs( intval( $code ) );
|
420 |
+
} elseif ( strpos( $code, '-' ) !== false ) {
|
421 |
+
//Try to parse it as a range
|
422 |
+
$range = explode( '-', $code, 2 );
|
423 |
+
if ( ( count( $range ) == 2 ) && is_numeric( $range[0] ) && is_numeric( $range[0] ) ) {
|
424 |
+
//Make sure the smaller code comes first
|
425 |
+
$range = array( intval( $range[0] ), intval( $range[1] ) );
|
426 |
+
$ranges[] = array( min( $range ), max( $range ) );
|
427 |
+
}
|
428 |
+
}
|
429 |
+
}
|
430 |
+
|
431 |
+
$piece = array();
|
432 |
+
|
433 |
+
//All individual response codes get one "http_code IN (...)" clause
|
434 |
+
if ( ! empty( $individual_codes ) ) {
|
435 |
+
$piece[] = '(links.http_code IN (' . implode( ', ', $individual_codes ) . '))';
|
436 |
+
}
|
437 |
+
|
438 |
+
//Ranges get a "http_code BETWEEN min AND max" clause each
|
439 |
+
if ( ! empty( $ranges ) ) {
|
440 |
+
$range_strings = array();
|
441 |
+
foreach ( $ranges as $range ) {
|
442 |
+
$range_strings[] = "(links.http_code BETWEEN $range[0] AND $range[1])";
|
443 |
+
}
|
444 |
+
$piece[] = '( ' . implode( ' OR ', $range_strings ) . ' )';
|
445 |
+
}
|
446 |
+
|
447 |
+
//Finally, generate a composite WHERE clause for both types of response code queries
|
448 |
+
if ( ! empty( $piece ) ) {
|
449 |
+
$pieces[] = implode( ' OR ', $piece );
|
450 |
+
}
|
451 |
+
}
|
452 |
+
|
453 |
+
//Dismissed links are included by default, but can explicitly included
|
454 |
+
//or filtered out by passing a special param.
|
455 |
+
if ( isset( $params['s_include_dismissed'] ) ) {
|
456 |
+
$s_include_dismissed = ! empty( $params['s_include_dismissed'] );
|
457 |
+
$pieces['filter_dismissed'] = $s_include_dismissed ? '1' : '(dismissed = 0)';
|
458 |
+
}
|
459 |
+
|
460 |
+
//Optionally sorting is also possible
|
461 |
+
$order_exprs = array();
|
462 |
+
if ( ! empty( $params['orderby'] ) ) {
|
463 |
+
$allowed_columns = array(
|
464 |
+
'url' => 'links.url',
|
465 |
+
'link_text' => 'instances.link_text',
|
466 |
+
'redirect_url' => 'links.final_url',
|
467 |
+
);
|
468 |
+
$column = $params['orderby'];
|
469 |
+
|
470 |
+
$direction = ! empty( $params['order'] ) ? strtolower( $params['order'] ) : 'asc';
|
471 |
+
if ( ! in_array( $direction, array( 'asc', 'desc' ) ) ) {
|
472 |
+
$direction = 'asc';
|
473 |
+
}
|
474 |
+
|
475 |
+
if ( array_key_exists( $column, $allowed_columns ) ) {
|
476 |
+
if ( 'redirect_url' === $column ) {
|
477 |
+
//Sort links that are not redirects last.
|
478 |
+
$order_exprs[] = '(links.redirect_count > 0) DESC';
|
479 |
+
}
|
480 |
+
|
481 |
+
$order_exprs[] = $allowed_columns[ $column ] . ' ' . $direction;
|
482 |
+
}
|
483 |
+
}
|
484 |
+
|
485 |
+
//Custom filters can optionally call one of the native filters
|
486 |
+
//to narrow down the result set.
|
487 |
+
if ( ! empty( $params['s_filter'] ) && isset( $this->native_filters[ $params['s_filter'] ] ) ) {
|
488 |
+
$the_filter = $this->native_filters[ $params['s_filter'] ];
|
489 |
+
$extra_criteria = $this->compile_search_params( $the_filter['params'] );
|
490 |
+
|
491 |
+
$pieces = array_merge( $extra_criteria['where_exprs'], $pieces );
|
492 |
+
$join_instances = $join_instances || $extra_criteria['join_instances'];
|
493 |
+
}
|
494 |
+
|
495 |
+
return array(
|
496 |
+
'where_exprs' => $pieces,
|
497 |
+
'join_instances' => $join_instances,
|
498 |
+
'order_exprs' => $order_exprs,
|
499 |
+
);
|
500 |
+
}
|
501 |
+
|
502 |
+
private function esc_like( $input ) {
|
503 |
+
global $wpdb; /** @var wpdb $wpdb */
|
504 |
+
if ( method_exists( $wpdb, 'esc_like' ) ) {
|
505 |
+
return $wpdb->esc_like( $input );
|
506 |
+
} else {
|
507 |
+
return like_escape( $input );
|
508 |
+
}
|
509 |
+
}
|
510 |
+
|
511 |
+
/**
|
512 |
+
* blcLinkQuery::get_links()
|
513 |
+
*
|
514 |
+
* @see blc_get_links()
|
515 |
+
*
|
516 |
+
* @param array $params
|
517 |
+
* @return array|int
|
518 |
+
*/
|
519 |
+
function get_links( $params = null ) {
|
520 |
+
global $wpdb; /** @var wpdb $wpdb */
|
521 |
+
|
522 |
+
if ( ! is_array( $params ) ) {
|
523 |
+
$params = array();
|
524 |
+
}
|
525 |
+
|
526 |
+
$defaults = array(
|
527 |
+
'offset' => 0,
|
528 |
+
'max_results' => 0,
|
529 |
+
'load_instances' => false,
|
530 |
+
'load_containers' => false,
|
531 |
+
'load_wrapped_objects' => false,
|
532 |
+
'count_only' => false,
|
533 |
+
'purpose' => '',
|
534 |
+
'include_invalid' => false,
|
535 |
+
'orderby' => '',
|
536 |
+
'order' => '',
|
537 |
+
);
|
538 |
+
|
539 |
+
$params = array_merge( $defaults, $params );
|
540 |
+
|
541 |
+
//Compile the search-related params into search expressions usable in a WHERE clause
|
542 |
+
$criteria = $this->compile_search_params( $params );
|
543 |
+
|
544 |
+
//Build the WHERE clause
|
545 |
+
if ( ! empty( $criteria['where_exprs'] ) ) {
|
546 |
+
$where_expr = "\t( " . implode( " ) AND\n\t( ", $criteria['where_exprs'] ) . ' ) ';
|
547 |
+
} else {
|
548 |
+
$where_expr = '1';
|
549 |
+
}
|
550 |
+
|
551 |
+
//Join the blc_instances table if it's required to perform the search.
|
552 |
+
$joins = '';
|
553 |
+
if ( $criteria['join_instances'] ) {
|
554 |
+
$joins = "JOIN {$wpdb->prefix}blc_instances AS instances ON links.link_id = instances.link_id";
|
555 |
+
}
|
556 |
+
|
557 |
+
//Optional sorting
|
558 |
+
if ( ! empty( $criteria['order_exprs'] ) ) {
|
559 |
+
$order_clause = 'ORDER BY ' . implode( ', ', $criteria['order_exprs'] );
|
560 |
+
} else {
|
561 |
+
$order_clause = '';
|
562 |
+
}
|
563 |
+
|
564 |
+
if ( $params['count_only'] ) {
|
565 |
+
//Only get the number of matching links.
|
566 |
+
$q = "
|
567 |
+
SELECT COUNT(*)
|
568 |
+
FROM (
|
569 |
+
SELECT 0
|
570 |
+
|
571 |
+
FROM
|
572 |
+
{$wpdb->prefix}blc_links AS links
|
573 |
+
$joins
|
574 |
+
|
575 |
+
WHERE
|
576 |
+
$where_expr
|
577 |
+
|
578 |
+
GROUP BY links.link_id) AS foo";
|
579 |
+
|
580 |
+
return $wpdb->get_var( $q ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
581 |
+
}
|
582 |
+
|
583 |
+
//Select the required links.
|
584 |
+
$q = "SELECT
|
585 |
+
links.*
|
586 |
+
|
587 |
+
FROM
|
588 |
+
{$wpdb->prefix}blc_links AS links
|
589 |
+
$joins
|
590 |
+
|
591 |
+
WHERE
|
592 |
+
$where_expr
|
593 |
+
|
594 |
+
GROUP BY links.link_id
|
595 |
+
|
596 |
+
{$order_clause}"; //Note: would be a lot faster without GROUP BY
|
597 |
+
|
598 |
+
//Add the LIMIT clause
|
599 |
+
if ( $params['max_results'] || $params['offset'] ) {
|
600 |
+
$q .= sprintf( "\nLIMIT %d, %d", $params['offset'], $params['max_results'] );
|
601 |
+
}
|
602 |
+
|
603 |
+
$results = $wpdb->get_results( $q, ARRAY_A ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
604 |
+
if ( empty( $results ) ) {
|
605 |
+
return array();
|
606 |
+
}
|
607 |
+
|
608 |
+
//Create the link objects
|
609 |
+
$links = array();
|
610 |
+
|
611 |
+
foreach ( $results as $result ) {
|
612 |
+
$link = new blcLink( $result );
|
613 |
+
$links[ $link->link_id ] = $link;
|
614 |
+
}
|
615 |
+
|
616 |
+
$purpose = $params['purpose'];
|
617 |
+
/*
|
618 |
+
Preload instances if :
|
619 |
+
* It has been requested via the 'load_instances' argument.
|
620 |
+
* The links are going to be displayed or edited, which involves instances.
|
621 |
+
*/
|
622 |
+
$load_instances = $params['load_instances'] || in_array( $purpose, array( BLC_FOR_DISPLAY, BLC_FOR_EDITING ) );
|
623 |
+
|
624 |
+
if ( $load_instances ) {
|
625 |
+
$link_ids = array_keys( $links );
|
626 |
+
$all_instances = blc_get_instances( $link_ids, $purpose, $params['load_containers'], $params['load_wrapped_objects'] );
|
627 |
+
//Assign each batch of instances to the right link
|
628 |
+
foreach ( $all_instances as $link_id => $instances ) {
|
629 |
+
foreach ( $instances as $instance ) { /** @var blcLinkInstance $instance */
|
630 |
+
$instance->_link = $links[ $link_id ];
|
631 |
+
}
|
632 |
+
$links[ $link_id ]->_instances = $instances;
|
633 |
+
}
|
634 |
+
}
|
635 |
+
|
636 |
+
return $links;
|
637 |
+
}
|
638 |
+
|
639 |
+
/**
|
640 |
+
* Calculate the number of results for all known filters
|
641 |
+
*
|
642 |
+
* @return void
|
643 |
+
*/
|
644 |
+
function count_filter_results() {
|
645 |
+
foreach ( $this->native_filters as $filter_id => $filter ) {
|
646 |
+
$this->native_filters[ $filter_id ]['count'] = $this->get_filter_links(
|
647 |
+
$filter,
|
648 |
+
array( 'count_only' => true )
|
649 |
+
);
|
650 |
+
}
|
651 |
+
|
652 |
+
foreach ( $this->custom_filters as $filter_id => $filter ) {
|
653 |
+
$this->custom_filters[ $filter_id ]['count'] = $this->get_filter_links(
|
654 |
+
$filter,
|
655 |
+
array( 'count_only' => true )
|
656 |
+
);
|
657 |
+
}
|
658 |
+
|
659 |
+
$this->search_filter['count'] = $this->get_filter_links( $this->search_filter, array( 'count_only' => true ) );
|
660 |
+
}
|
661 |
+
|
662 |
+
/**
|
663 |
+
* Retrieve a list of links matching a filter.
|
664 |
+
*
|
665 |
+
* @uses blcLinkQuery::get_links()
|
666 |
+
*
|
667 |
+
* @param string|array $filter Either a filter ID or an array containing filter data.
|
668 |
+
* @param array $extra_params Optional extra criteria that will override those set by the filter. See blc_get_links() for details.
|
669 |
+
* @return array|int Either an array of blcLink objects, or an integer indicating the number of links that match the filter.
|
670 |
+
*/
|
671 |
+
function get_filter_links( $filter, $extra_params = null ) {
|
672 |
+
if ( is_string( $filter ) ) {
|
673 |
+
$filter = $this->get_filter( $filter );
|
674 |
+
}
|
675 |
+
|
676 |
+
$params = $this->get_search_params( $filter );
|
677 |
+
|
678 |
+
if ( ! empty( $extra_params ) ) {
|
679 |
+
$params = array_merge( $params, $extra_params );
|
680 |
+
}
|
681 |
+
|
682 |
+
return $this->get_links( $params );
|
683 |
+
}
|
684 |
+
|
685 |
+
/**
|
686 |
+
* Print a menu of available filters, both native and user-created.
|
687 |
+
*
|
688 |
+
* @param string $current Current filter ID.
|
689 |
+
* @return void
|
690 |
+
*/
|
691 |
+
function print_filter_menu( $current = '' ) {
|
692 |
+
$filters = $this->get_filters();
|
693 |
+
|
694 |
+
echo '<ul class="subsubsub">';
|
695 |
+
|
696 |
+
//Construct a submenu of filter types
|
697 |
+
$items = array();
|
698 |
+
foreach ( $filters as $filter => $data ) {
|
699 |
+
if ( ! empty( $data['hidden'] ) ) {
|
700 |
+
continue; //skip hidden filters
|
701 |
+
}
|
702 |
+
|
703 |
+
$class = '';
|
704 |
+
$number_class = 'filter-' . $filter . '-link-count';
|
705 |
+
|
706 |
+
if ( $current == $filter ) {
|
707 |
+
$class = 'class="current"';
|
708 |
+
$number_class .= ' current-link-count';
|
709 |
+
}
|
710 |
+
|
711 |
+
$items[] = sprintf(
|
712 |
+
"<li><a href='tools.php?page=view-broken-links&filter_id=%s' %s>%s</a> <span class='count'>(<span class='%s'>%d</span>)</span>",
|
713 |
+
esc_attr( $filter ),
|
714 |
+
$class,
|
715 |
+
esc_html( $data['name'] ),
|
716 |
+
$number_class,
|
717 |
+
$data['count']
|
718 |
+
);
|
719 |
+
}
|
720 |
+
echo implode( ' |</li>', $items );
|
721 |
+
|
722 |
+
echo '</ul>';
|
723 |
+
}
|
724 |
+
|
725 |
+
/**
|
726 |
+
* Print the appropriate heading for the given filter.
|
727 |
+
*
|
728 |
+
* @param array $current_filter
|
729 |
+
* @return void
|
730 |
+
*/
|
731 |
+
function print_filter_heading( $current_filter ) {
|
732 |
+
echo '<h2>';
|
733 |
+
//Output a header matching the current filter
|
734 |
+
if ( $current_filter['count'] > 0 ) {
|
735 |
+
echo $current_filter['heading'] . " (<span class='current-link-count'>{$current_filter['count']}</span>)";
|
736 |
+
} else {
|
737 |
+
echo $current_filter['heading_zero'] . "<span class='current-link-count'></span>";
|
738 |
+
}
|
739 |
+
echo '</h2>';
|
740 |
+
}
|
741 |
+
|
742 |
+
/**
|
743 |
+
* Execute a filter.
|
744 |
+
*
|
745 |
+
* Gathers paging and search parameters from $_GET and executes the specified filter.
|
746 |
+
* The returned array contains standard filter data plus several additional fields :
|
747 |
+
* 'filter_id' - Which filter was used. May differ from the specified $filter_id due to fallback settings.
|
748 |
+
* 'per_page' - How many results per page the method tried to retrieve.
|
749 |
+
* 'page' - Which page of results was retrieved.
|
750 |
+
* 'max_pages' - The total number of results pages, calculated using the above 'per_page' value.
|
751 |
+
* 'links' - An array of retrieved links (blcLink objects).
|
752 |
+
* 'search_params' - An associative array of the current search parameters as extracted either from the current URL or the filter itself.
|
753 |
+
* 'is_broken_filter' - TRUE if the filter was set to retrieve only broken links, FALSE otherwise.
|
754 |
+
*
|
755 |
+
* @param string $filter_id Filter ID.
|
756 |
+
* @param int $page Optional. Which page of results to retrieve. Defaults to returning the first page of results.
|
757 |
+
* @param int $per_page Optional. The number of results per page. Defaults to 30.
|
758 |
+
* @param string $fallback Optional. Which filter to use if none match the specified $filter_id. Defaults to the native broken link filter.
|
759 |
+
* @param string $orderby Optional. Sort results by this column.
|
760 |
+
* @param string $order Optional. Sort direction ('asc' or 'desc').
|
761 |
+
* @return array Associative array of filter data and the results of its execution.
|
762 |
+
*/
|
763 |
+
function exec_filter( $filter_id, $page = 1, $per_page = 30, $fallback = 'broken', $orderby = '', $order = 'asc' ) {
|
764 |
+
//The only valid sort directions are 'asc' and 'desc'.
|
765 |
+
if ( ! in_array( $order, array( 'asc', 'desc' ) ) ) {
|
766 |
+
$order = 'asc';
|
767 |
+
}
|
768 |
+
|
769 |
+
//Get the selected filter (defaults to displaying broken links)
|
770 |
+
$current_filter = $this->get_filter( $filter_id );
|
771 |
+
if ( empty( $current_filter ) ) {
|
772 |
+
$current_filter = $this->get_filter( $fallback );
|
773 |
+
$filter_id = $fallback;
|
774 |
+
}
|
775 |
+
|
776 |
+
//Page number must be > 0
|
777 |
+
if ( $page < 1 ) {
|
778 |
+
$page = 1;
|
779 |
+
}
|
780 |
+
|
781 |
+
//Links per page [1 - 500]
|
782 |
+
if ( $per_page < 1 ) {
|
783 |
+
$per_page = 30;
|
784 |
+
} elseif ( $per_page > 500 ) {
|
785 |
+
$per_page = 500;
|
786 |
+
}
|
787 |
+
|
788 |
+
//Calculate the maximum number of pages.
|
789 |
+
$max_pages = ceil( $current_filter['count'] / $per_page );
|
790 |
+
|
791 |
+
//Select the required links
|
792 |
+
$extra_params = array(
|
793 |
+
'offset' => ( ( $page - 1 ) * $per_page ),
|
794 |
+
'max_results' => $per_page,
|
795 |
+
'purpose' => BLC_FOR_DISPLAY,
|
796 |
+
'orderby' => $orderby,
|
797 |
+
'order' => $order,
|
798 |
+
);
|
799 |
+
$links = $this->get_filter_links( $current_filter, $extra_params );
|
800 |
+
|
801 |
+
//If the current request is a user-initiated search query (either directly or
|
802 |
+
//via a custom filter), save the search params. They can later be used to pre-fill
|
803 |
+
//the search form or build a new/modified custom filter.
|
804 |
+
$search_params = array();
|
805 |
+
if ( ! empty( $current_filter['custom'] ) || ( 'search' == $filter_id ) ) {
|
806 |
+
$search_params = $this->get_search_params( $current_filter );
|
807 |
+
}
|
808 |
+
|
809 |
+
$base_filter = '';
|
810 |
+
if ( array_key_exists( $filter_id, $this->native_filters ) ) {
|
811 |
+
$base_filter = $filter_id;
|
812 |
+
} elseif ( isset( $current_filter['params']['s_filter'] ) && ! empty( $current_filter['params']['s_filter'] ) ) {
|
813 |
+
$base_filter = $current_filter['params']['s_filter'];
|
814 |
+
} elseif ( isset( $_GET['s_filter'] ) && ! empty( $_GET['s_filter'] ) ) {
|
815 |
+
if ( array_key_exists( $_GET['s_filter'], $this->native_filters ) ) {
|
816 |
+
$base_filter = esc_html( $_GET['s_filter'] );
|
817 |
+
} else {
|
818 |
+
$base_filter = 'all';
|
819 |
+
}
|
820 |
+
}
|
821 |
+
|
822 |
+
$is_broken_filter = ( 'broken' == $base_filter );
|
823 |
+
|
824 |
+
//Save the effective filter data in the filter array.
|
825 |
+
//It can be used later to print the link table.
|
826 |
+
$current_filter = array_merge(
|
827 |
+
array(
|
828 |
+
'filter_id' => $filter_id,
|
829 |
+
'page' => $page,
|
830 |
+
'per_page' => $per_page,
|
831 |
+
'max_pages' => $max_pages,
|
832 |
+
'links' => $links,
|
833 |
+
'search_params' => $search_params,
|
834 |
+
'is_broken_filter' => $is_broken_filter,
|
835 |
+
'base_filter' => $base_filter,
|
836 |
+
),
|
837 |
+
$current_filter
|
838 |
+
);
|
839 |
+
|
840 |
+
return $current_filter;
|
841 |
+
}
|
842 |
+
}
|
843 |
+
|
844 |
+
/**
|
845 |
+
* Retrieve a list of links matching some criteria.
|
846 |
+
*
|
847 |
+
* The function argument should be an associative array describing the criteria.
|
848 |
+
* The supported keys are :
|
849 |
+
* 'offset' - Skip the first X results. Default is 0.
|
850 |
+
* 'max_results' - The maximum number of links to return. Defaults to returning all results.
|
851 |
+
* 'link_ids' - Retrieve only links with these IDs. This should either be a comma-separated list or an array.
|
852 |
+
* 's_link_text' - Link text must match this keyphrase (performs a fulltext search).
|
853 |
+
* 's_link_url' - Link URL must contain this string. You can use "*" as a wildcard.
|
854 |
+
* 's_parser_type' - Filter links by the type of link parser that was used to find them.
|
855 |
+
* 's_container_type' - Filter links by where they were found, e.g. 'post'.
|
856 |
+
* 's_container_id' - Find links that belong to a container with this ID (should be used together with s_container_type).
|
857 |
+
* 's_link_type' - Either parser type or container type must match this.
|
858 |
+
* 's_http_code' - Filter by HTTP code. Example : 201,400-410,500
|
859 |
+
* 's_filter' - Use a built-in filter. Available filters : 'broken', 'redirects', 'all'
|
860 |
+
* 'where_expr' - Advanced. Lets you directly specify a part of the WHERE clause.
|
861 |
+
* 'load_instances' - Pre-load all link instance data for each link. Default is false.
|
862 |
+
* 'load_containers' - Pre-load container data for each instance. Default is false.
|
863 |
+
* 'load_wrapped_objects' - Pre-load wrapped object data (e.g. posts, comments, etc) for each container. Default is false.
|
864 |
+
* 'count_only' - Only return the number of results (int), not the whole result set. 'offset' and 'max_results' will be ignored if this is set. Default is false.
|
865 |
+
* 'purpose' - An optional code indicating how the links will be used.
|
866 |
+
* 'include_invalid' - Include links that have no instances and links that only have instances that reference not-loaded containers or parsers. Defaults to false.
|
867 |
+
*
|
868 |
+
* All keys are optional.
|
869 |
+
*
|
870 |
+
* @uses blcLinkQuery::get_links();
|
871 |
+
*
|
872 |
+
* @param array $params
|
873 |
+
* @return int|blcLink[] Either an array of blcLink objects, or the number of results for the query.
|
874 |
+
*/
|
875 |
+
function blc_get_links( $params = null ) {
|
876 |
+
$instance = blcLinkQuery::getInstance();
|
877 |
+
return $instance->get_links( $params );
|
878 |
+
}
|
879 |
+
|
includes/links.php
CHANGED
@@ -5,1106 +5,1110 @@
|
|
5 |
* @copyright 2010
|
6 |
*/
|
7 |
|
8 |
-
if (!class_exists('blcLink')){
|
9 |
-
|
10 |
-
define('BLC_LINK_STATUS_UNKNOWN', 'unknown');
|
11 |
-
define('BLC_LINK_STATUS_OK', 'ok');
|
12 |
-
define('BLC_LINK_STATUS_INFO', 'info');
|
13 |
-
define('BLC_LINK_STATUS_WARNING', 'warning');
|
14 |
-
define('BLC_LINK_STATUS_ERROR', 'error');
|
15 |
-
|
16 |
-
class blcLink {
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
);
|
108 |
-
var $isOptionLinkChanged = false;
|
109 |
-
function __construct($arg = null){
|
110 |
-
global $wpdb, $blclog; /** @var wpdb $wpdb */
|
111 |
-
|
112 |
-
$this->field_format = array(
|
113 |
-
'url' => '%s',
|
114 |
-
'first_failure' => 'datetime',
|
115 |
-
'last_check' => 'datetime',
|
116 |
-
'last_success' => 'datetime',
|
117 |
-
'last_check_attempt' => 'datetime',
|
118 |
-
'check_count' => '%d',
|
119 |
-
'final_url' => '%s',
|
120 |
-
'redirect_count' => '%d',
|
121 |
-
'log' => '%s',
|
122 |
-
'http_code' => '%d',
|
123 |
-
'request_duration' => '%F',
|
124 |
-
'timeout' => 'bool',
|
125 |
-
'result_hash' => '%s',
|
126 |
-
'broken' => 'bool',
|
127 |
-
'warning' => 'bool',
|
128 |
-
'false_positive' => 'bool',
|
129 |
-
'may_recheck' => 'bool',
|
130 |
-
'being_checked' => 'bool',
|
131 |
-
'status_text' => '%s',
|
132 |
-
'status_code' => '%s',
|
133 |
-
'dismissed' => 'bool',
|
134 |
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
135 |
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
|
141 |
-
|
142 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
143 |
} else {
|
144 |
-
//Link not found. The object is invalid.
|
145 |
-
//I'd throw an error, but that wouldn't be PHP 4 compatible...
|
146 |
-
$blclog->warn(__CLASS__ .':' . __FUNCTION__ . ' Link not found.', $arg);
|
147 |
-
}
|
148 |
-
|
149 |
-
} else if (is_string($arg)){
|
150 |
-
//Load a link with URL = $arg from the DB. Create a new one if the record isn't found.
|
151 |
-
// $blclog->debug(__CLASS__ .':' . __FUNCTION__ . ' Trying to load a link by URL:', $arg);
|
152 |
-
$q = $wpdb->prepare("SELECT * FROM {$wpdb->prefix}blc_links WHERE url=%s LIMIT 1", $arg);
|
153 |
-
$arr = $wpdb->get_row( $q, ARRAY_A );
|
154 |
-
|
155 |
-
if ( is_array($arr) ){ //Loaded successfully
|
156 |
-
// $blclog->debug(__CLASS__ .':' . __FUNCTION__ . ' Success!');
|
157 |
-
$this->set_values($arr);
|
158 |
-
} else { //Link not found, treat as new
|
159 |
-
// $blclog->debug(__CLASS__ .':' . __FUNCTION__ . ' Link not found.');
|
160 |
-
$this->url = $arg;
|
161 |
$this->is_new = true;
|
162 |
}
|
163 |
-
|
164 |
-
} else if (is_array($arg)){
|
165 |
-
$this->set_values($arg);
|
166 |
-
//Is this a new link?
|
167 |
-
$this->is_new = empty($this->link_id);
|
168 |
-
} else {
|
169 |
-
$this->is_new = true;
|
170 |
}
|
171 |
-
}
|
172 |
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
|
|
189 |
}
|
190 |
-
}
|
191 |
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
$this->last_check_attempt = time();
|
211 |
-
|
212 |
-
/*
|
213 |
-
If the link is still marked as in the process of being checked, that probably means
|
214 |
-
that the last time the plugin tried to check it the script got terminated by PHP for
|
215 |
-
running over the execution time limit or causing a fatal error.
|
216 |
-
|
217 |
-
This problem is likely to be temporary for most links, so we leave it be and treat it
|
218 |
-
as any other link (i.e. check it again later using the default recheck periodicity).
|
219 |
-
*/
|
220 |
-
if ( $this->being_checked ) {
|
221 |
-
$this->being_checked = false;
|
222 |
-
|
223 |
-
//Add an explanatory notice to the link's log
|
224 |
-
$error_notice = "[" . __("The plugin script was terminated while trying to check the link.", 'broken-link-checker') . "]";
|
225 |
-
if ( strpos($this->log, $error_notice) === false ){
|
226 |
-
$this->log = $error_notice . "\r\n" . $this->log;
|
227 |
-
}
|
228 |
-
|
229 |
-
if ( $save_results ){
|
230 |
-
$this->save();
|
231 |
}
|
232 |
|
233 |
-
|
234 |
-
}
|
235 |
-
|
236 |
-
$this->being_checked = true;
|
237 |
-
$this->check_count++;
|
238 |
-
|
239 |
-
if ( $save_results ) {
|
240 |
-
|
241 |
-
//Update the DB record before actually performing the check.
|
242 |
-
//Useful if something goes terribly wrong while checking this particular URL
|
243 |
-
//(e.g. the server might kill the script for running over the exec. time limit).
|
244 |
-
//Note : might be unnecessary.
|
245 |
-
$this->save();
|
246 |
-
}
|
247 |
-
|
248 |
-
$defaults = array(
|
249 |
-
'broken' => false,
|
250 |
-
'warning' => false,
|
251 |
-
'http_code' => 0,
|
252 |
-
'redirect_count' => 0,
|
253 |
-
'final_url' => $this->url,
|
254 |
-
'request_duration' => 0,
|
255 |
-
'timeout' => false,
|
256 |
-
'may_recheck' => true,
|
257 |
-
'log' => '',
|
258 |
-
'result_hash' => '',
|
259 |
-
'status_text' => '',
|
260 |
-
'status_code' => '',
|
261 |
-
);
|
262 |
|
|
|
|
|
|
|
|
|
263 |
|
264 |
-
|
|
|
|
|
|
|
|
|
265 |
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
272 |
|
273 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
274 |
$this->save();
|
275 |
}
|
276 |
|
277 |
-
|
278 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
279 |
|
280 |
-
|
281 |
-
|
282 |
-
//FB::info($rez, "Check results");
|
283 |
|
284 |
-
|
|
|
|
|
285 |
|
286 |
-
|
287 |
-
$results = $this->decide_warning_state($results);
|
288 |
|
289 |
-
|
290 |
-
|
291 |
|
292 |
-
|
293 |
-
|
294 |
-
unset($results['result_hash']);
|
295 |
|
296 |
-
|
297 |
-
|
|
|
298 |
|
299 |
-
|
300 |
-
|
301 |
-
$this->being_checked = false;
|
302 |
|
303 |
-
|
304 |
-
|
305 |
-
$this->
|
306 |
-
}
|
307 |
|
308 |
-
|
309 |
-
|
|
|
|
|
310 |
|
311 |
-
|
312 |
-
* Decide whether the result of the latest check means that the link is really broken
|
313 |
-
* or should just be reported as a warning.
|
314 |
-
*
|
315 |
-
* @param array $check_results
|
316 |
-
* @return array
|
317 |
-
*/
|
318 |
-
private function decide_warning_state($check_results) {
|
319 |
-
if ( !$check_results['broken'] && !$check_results['warning'] ) {
|
320 |
-
//Nothing to do, this is a working link.
|
321 |
-
return $check_results;
|
322 |
}
|
323 |
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
|
|
|
|
|
|
|
|
|
|
330 |
}
|
331 |
-
return $check_results;
|
332 |
-
}
|
333 |
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
$maybe_temporary_error = false;
|
344 |
-
|
345 |
-
//Some basic heuristics to determine if this failure might be temporary.
|
346 |
-
//----------------------------------------------------------------------
|
347 |
-
if ( $check_results['timeout'] ) {
|
348 |
-
$maybe_temporary_error = true;
|
349 |
-
$warning_reason = 'Timeouts are sometimes caused by high server load or other temporary issues.';
|
350 |
-
}
|
351 |
|
352 |
-
|
353 |
-
|
354 |
-
$
|
355 |
-
|
356 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
357 |
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
382 |
}
|
383 |
-
}
|
384 |
|
385 |
-
|
386 |
|
387 |
-
|
388 |
-
|
389 |
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
. 'and that your server is not set up to block automated requests.';
|
397 |
-
|
398 |
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
. 'hosting provider has disabled loopback connections.';
|
406 |
-
|
407 |
-
|
408 |
. 'This could mean DNS is configured incorrectly on your server.';
|
|
|
409 |
}
|
410 |
-
}
|
411 |
|
412 |
-
|
413 |
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
|
|
427 |
}
|
428 |
-
}
|
429 |
|
430 |
-
|
431 |
-
|
432 |
. 'Severity: Warning' . "\n"
|
433 |
-
. 'Reason: ' . trim($warning_reason)
|
434 |
. "\n==========\n";
|
435 |
|
436 |
-
|
437 |
-
|
438 |
|
439 |
-
|
440 |
-
|
441 |
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
|
|
|
|
461 |
}
|
462 |
-
$this->dismissed = false;
|
463 |
-
}
|
464 |
|
465 |
-
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
|
|
|
|
|
|
|
|
|
474 |
} else {
|
475 |
-
//
|
|
|
476 |
$this->false_positive = false;
|
477 |
}
|
478 |
-
} else {
|
479 |
-
//The plugin now thinks the link is working,
|
480 |
-
//so it's no longer a false positive.
|
481 |
-
$this->false_positive = false;
|
482 |
}
|
483 |
-
}
|
484 |
|
485 |
-
|
486 |
-
|
487 |
|
488 |
-
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
|
|
|
|
|
|
|
|
|
|
493 |
}
|
494 |
-
} else {
|
495 |
-
$this->first_failure = 0;
|
496 |
-
$this->last_success = $this->last_check;
|
497 |
-
$this->check_count = 0;
|
498 |
-
}
|
499 |
-
|
500 |
-
//Add a line indicating link status to the log
|
501 |
-
if ( $this->broken || $this->warning ) {
|
502 |
-
$this->log .= "\n" . __("Link is broken.", 'broken-link-checker');
|
503 |
-
} else {
|
504 |
-
$this->log .= "\n" . __("Link is valid.", 'broken-link-checker');
|
505 |
-
}
|
506 |
-
}
|
507 |
-
|
508 |
-
/**
|
509 |
-
* blcLink::save()
|
510 |
-
* Save link data to DB.
|
511 |
-
*
|
512 |
-
* @return bool True if saved successfully, false otherwise.
|
513 |
-
*/
|
514 |
-
function save(){
|
515 |
-
global $wpdb, $blclog; /** @var wpdb $wpdb */
|
516 |
-
|
517 |
-
if ( !$this->valid() ) return false;
|
518 |
|
519 |
-
|
520 |
-
|
521 |
-
|
|
|
|
|
|
|
522 |
}
|
523 |
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
530 |
|
531 |
-
|
|
|
|
|
|
|
532 |
|
533 |
-
//
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
|
|
538 |
|
539 |
-
|
540 |
-
$q = $wpdb->prepare(
|
541 |
-
"SELECT link_id FROM {$wpdb->prefix}blc_links WHERE url = %s",
|
542 |
-
$this->url
|
543 |
-
);
|
544 |
-
$existing_id = $wpdb->get_var($q);
|
545 |
|
546 |
-
|
547 |
-
//
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
}
|
552 |
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
//FB::log($q, 'Link add query');
|
560 |
-
$blclog->debug(__CLASS__ .':' . __FUNCTION__ . ' Adding a new link. SQL query:' . "\n", $q);
|
561 |
|
562 |
-
|
|
|
|
|
|
|
|
|
|
|
563 |
|
564 |
-
|
565 |
-
$
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
$blclog->
|
572 |
-
//FB::error($wpdb->last_error, "Error adding link {$this->url}");
|
573 |
-
}
|
574 |
|
575 |
-
|
576 |
|
577 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
578 |
|
579 |
-
|
580 |
-
if ($this->isOptionLinkChanged !== true ) {
|
581 |
-
TransactionManager::getInstance()->start();
|
582 |
-
}
|
583 |
-
$this->isOptionLinkChanged = false;
|
584 |
-
//Generate the field = dbvalue expressions
|
585 |
-
$set_exprs = array();
|
586 |
-
foreach($values as $name => $value){
|
587 |
-
$set_exprs[] = "$name = $value";
|
588 |
-
}
|
589 |
-
$set_exprs = implode(', ', $set_exprs);
|
590 |
|
591 |
-
|
592 |
-
$q = sprintf(
|
593 |
-
"UPDATE {$wpdb->prefix}blc_links SET %s WHERE link_id=%d",
|
594 |
-
$set_exprs,
|
595 |
-
intval($this->link_id)
|
596 |
-
);
|
597 |
-
//FB::log($q, 'Link update query');
|
598 |
-
$blclog->debug(__CLASS__ .':' . __FUNCTION__ . ' Updating a link. SQL query:'. "\n", $q);
|
599 |
|
600 |
-
$rez = $wpdb->query($q) !== false;
|
601 |
-
if ( $rez ){
|
602 |
-
//FB::log($this->link_id, "Link updated");
|
603 |
-
$blclog->debug(__CLASS__ .':' . __FUNCTION__ . ' Link updated.');
|
604 |
} else {
|
605 |
-
|
606 |
-
|
607 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
608 |
|
609 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
610 |
|
611 |
-
|
612 |
-
}
|
613 |
-
}
|
614 |
|
615 |
-
|
616 |
-
* A helper method for converting the link's field values to DB format and escaping them
|
617 |
-
* for use in SQL queries.
|
618 |
-
*
|
619 |
-
* @param array $values
|
620 |
-
* @return array
|
621 |
-
*/
|
622 |
-
function to_db_format($values){
|
623 |
-
global $wpdb; /** @var wpdb $wpdb */
|
624 |
-
|
625 |
-
$dbvalues = array();
|
626 |
-
|
627 |
-
foreach($values as $name => $value){
|
628 |
-
//Skip fields that don't exist in the blc_links table.
|
629 |
-
if ( !isset($this->field_format[$name]) ){
|
630 |
-
continue;
|
631 |
}
|
|
|
632 |
|
633 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
634 |
|
635 |
-
|
636 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
637 |
|
638 |
-
|
639 |
-
|
640 |
-
$value = '0000-00-00 00:00:00';
|
641 |
-
} else {
|
642 |
-
$value = date('Y-m-d H:i:s', $value);
|
643 |
-
}
|
644 |
-
$format = '%s';
|
645 |
-
break;
|
646 |
|
647 |
-
|
648 |
-
if ( $value ){
|
649 |
-
$value = 1;
|
650 |
-
} else {
|
651 |
-
$value = 0;
|
652 |
-
}
|
653 |
-
$format = '%d';
|
654 |
-
break;
|
655 |
}
|
656 |
|
657 |
-
|
658 |
-
$value = $wpdb->prepare($format, $value);
|
659 |
-
|
660 |
-
$dbvalues[$name] = $value;
|
661 |
}
|
662 |
|
663 |
-
|
664 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
665 |
|
666 |
-
|
667 |
-
* A helper method for converting values fetched from the database to native datatypes.
|
668 |
-
*
|
669 |
-
* @param array $values
|
670 |
-
* @return array
|
671 |
-
*/
|
672 |
-
function to_native_format($values){
|
673 |
-
|
674 |
-
foreach($values as $name => $value){
|
675 |
-
//Don't process fields that don't exist in the blc_links table.
|
676 |
-
if ( !isset($this->field_format[$name]) ){
|
677 |
-
continue;
|
678 |
-
}
|
679 |
|
680 |
-
|
|
|
681 |
|
682 |
-
|
683 |
-
|
|
|
|
|
|
|
|
|
|
|
684 |
|
685 |
-
|
686 |
-
|
687 |
-
|
688 |
-
} elseif (is_string($value)) {
|
689 |
-
$value = strtotime($value);
|
690 |
-
}
|
691 |
-
break;
|
692 |
|
693 |
-
|
694 |
-
|
695 |
-
|
696 |
|
697 |
-
|
698 |
-
|
699 |
-
|
700 |
|
701 |
-
|
702 |
-
$value = floatval($value);
|
703 |
-
break;
|
704 |
|
|
|
705 |
}
|
706 |
|
707 |
-
$values
|
708 |
}
|
709 |
|
710 |
-
|
711 |
-
|
712 |
-
|
713 |
-
|
714 |
-
|
715 |
-
|
716 |
-
|
717 |
-
|
718 |
-
|
719 |
-
|
720 |
-
|
721 |
-
|
722 |
-
|
723 |
-
|
724 |
-
|
725 |
-
|
726 |
-
|
727 |
-
|
728 |
-
|
729 |
-
|
730 |
-
|
731 |
-
|
732 |
-
|
733 |
-
|
734 |
-
|
735 |
-
__("Link is not valid", 'broken-link-checker')
|
736 |
-
);
|
737 |
-
}
|
738 |
|
739 |
-
|
740 |
|
741 |
-
|
742 |
-
|
743 |
-
|
744 |
-
|
745 |
-
|
746 |
-
|
747 |
-
|
748 |
-
|
749 |
-
|
750 |
-
|
751 |
-
|
752 |
-
|
753 |
-
|
754 |
-
|
755 |
-
|
756 |
-
|
757 |
-
|
758 |
-
|
759 |
-
|
760 |
-
|
761 |
-
|
762 |
-
|
763 |
-
|
764 |
-
|
765 |
-
|
766 |
-
//FB::log("Changing link to $new_url");
|
767 |
|
768 |
-
|
769 |
-
//FB::error("Failed to create a new link record");
|
770 |
-
return array(
|
771 |
-
'new_link_id' => $this->link_id,
|
772 |
-
'new_link' => $this,
|
773 |
-
'cnt_okay' => 0,
|
774 |
-
'cnt_error' => 0,
|
775 |
-
'errors' => array(
|
776 |
-
new WP_Error(
|
777 |
-
'link_creation_failed',
|
778 |
-
__('Failed to create a DB entry for the new URL.', 'broken-link-checker')
|
779 |
-
)
|
780 |
-
)
|
781 |
-
);
|
782 |
-
}
|
783 |
|
784 |
-
|
785 |
-
|
786 |
-
|
787 |
-
|
788 |
-
|
789 |
-
|
790 |
-
|
791 |
-
|
792 |
-
|
793 |
-
|
794 |
-
|
795 |
-
|
796 |
-
|
797 |
-
|
798 |
-
$instance->save();
|
799 |
-
//FB::info($instance, 'Successfully edited instance ' . $instance->instance_id);
|
800 |
}
|
801 |
-
}
|
802 |
|
803 |
-
|
804 |
-
|
805 |
-
|
806 |
-
|
807 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
808 |
|
809 |
-
|
810 |
-
|
811 |
-
|
812 |
-
|
813 |
-
|
814 |
-
}
|
815 |
|
816 |
-
|
817 |
-
|
818 |
-
|
819 |
-
|
820 |
-
|
821 |
-
|
822 |
-
);
|
823 |
-
}
|
824 |
|
825 |
-
|
826 |
-
|
827 |
-
|
828 |
-
|
829 |
-
|
830 |
-
|
831 |
-
* @return array|WP_Error
|
832 |
-
*/
|
833 |
-
function deredirect(){
|
834 |
-
if ( !$this->valid() ){
|
835 |
-
return new WP_Error(
|
836 |
-
'link_invalid',
|
837 |
-
__("Link is not valid", 'broken-link-checker')
|
838 |
);
|
839 |
}
|
840 |
|
841 |
-
|
842 |
-
|
843 |
-
|
844 |
-
|
845 |
-
|
846 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
847 |
|
848 |
-
|
849 |
-
|
850 |
-
|
851 |
-
|
852 |
-
|
853 |
-
|
854 |
|
855 |
-
|
856 |
-
|
|
|
|
|
|
|
|
|
857 |
|
858 |
-
|
859 |
-
* Unlink all instances and delete the link record.
|
860 |
-
*
|
861 |
-
* @return array|WP_Error An associative array with these keys :
|
862 |
-
* cnt_okay - the number of successfully removed instances.
|
863 |
-
* cnt_error - the number of instances that couldn't be removed.
|
864 |
-
* link_deleted - true if the link record was deleted.
|
865 |
-
* errors - an array of WP_Error objects describing the errors that were encountered, if any.
|
866 |
-
*/
|
867 |
-
function unlink(){
|
868 |
-
if ( !$this->valid() ){
|
869 |
-
return new WP_Error(
|
870 |
-
'link_invalid',
|
871 |
-
__("Link is not valid", 'broken-link-checker')
|
872 |
-
);
|
873 |
}
|
874 |
|
875 |
-
|
876 |
-
|
877 |
-
|
878 |
-
|
879 |
-
|
880 |
-
|
881 |
-
|
882 |
-
|
883 |
-
|
884 |
-
|
885 |
-
|
886 |
-
|
887 |
-
'
|
888 |
-
'
|
889 |
-
);
|
890 |
-
} else {
|
891 |
-
return array(
|
892 |
-
'cnt_okay' => 0,
|
893 |
-
'cnt_error' => 0,
|
894 |
-
'link_deleted' => false,
|
895 |
-
'errors' => array(
|
896 |
-
new WP_Error(
|
897 |
-
"deletion_failed",
|
898 |
-
__("Couldn't delete the link's database record", 'broken-link-checker')
|
899 |
-
)
|
900 |
-
),
|
901 |
);
|
902 |
}
|
903 |
-
}
|
904 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
905 |
|
906 |
-
|
907 |
|
908 |
-
|
909 |
-
|
|
|
910 |
|
911 |
-
|
912 |
-
|
913 |
-
|
914 |
|
915 |
-
|
916 |
-
|
917 |
-
|
918 |
-
|
919 |
-
|
920 |
-
|
921 |
-
|
|
|
922 |
}
|
923 |
-
}
|
924 |
|
925 |
-
|
926 |
-
|
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 |
-
* @param bool $remove_instances
|
957 |
-
* @return mixed 1 on success, 0 if link not found, false on error.
|
958 |
-
*/
|
959 |
-
function forget($remove_instances = true){
|
960 |
-
global $wpdb; /** @var wpdb $wpdb */
|
961 |
-
if ( !$this->valid() ) return false;
|
962 |
-
|
963 |
-
if ( !empty($this->link_id) ){
|
964 |
-
//FB::info($this, 'Deleting link from DB');
|
965 |
-
|
966 |
-
if ( $remove_instances ){
|
967 |
-
//Remove instances, if any
|
968 |
-
$wpdb->query( $wpdb->prepare("DELETE FROM {$wpdb->prefix}blc_instances WHERE link_id=%d", $this->link_id) );
|
969 |
}
|
970 |
|
971 |
-
|
972 |
-
|
973 |
-
$this->link_id = 0;
|
974 |
|
975 |
-
|
976 |
-
|
977 |
-
|
978 |
-
|
979 |
|
980 |
-
|
|
|
|
|
981 |
|
982 |
-
|
983 |
-
|
984 |
-
|
985 |
-
* @param bool $ignore_cache Don't use the internally cached instance list.
|
986 |
-
* @param string $purpose
|
987 |
-
* @return blcLinkInstance[] An array of instance objects or FALSE on failure.
|
988 |
-
*/
|
989 |
-
function get_instances( $ignore_cache = false, $purpose = '' ){
|
990 |
-
if ( !$this->valid() || empty($this->link_id) ) return false;
|
991 |
-
|
992 |
-
if ( $ignore_cache || is_null($this->_instances) ){
|
993 |
-
$instances = blc_get_instances( array($this->link_id), $purpose );
|
994 |
-
if ( !empty($instances) ){
|
995 |
-
$this->_instances = $instances[$this->link_id];
|
996 |
}
|
|
|
997 |
}
|
998 |
|
999 |
-
|
1000 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1001 |
|
1002 |
-
|
1003 |
-
|
1004 |
-
|
1005 |
-
|
1006 |
-
|
1007 |
-
|
1008 |
-
$code = BLC_LINK_STATUS_UNKNOWN;
|
1009 |
-
$text = _x('Unknown', 'link status', 'broken-link-checker');
|
1010 |
|
1011 |
-
|
1012 |
-
|
1013 |
|
1014 |
-
|
1015 |
-
|
1016 |
-
|
|
|
|
|
|
|
|
|
|
|
1017 |
|
1018 |
-
|
|
|
1019 |
|
1020 |
-
|
1021 |
-
$
|
1022 |
-
$
|
1023 |
|
1024 |
-
|
1025 |
|
1026 |
-
|
1027 |
$code = BLC_LINK_STATUS_WARNING;
|
|
|
1028 |
|
1029 |
-
|
1030 |
|
1031 |
-
|
1032 |
-
if ( in_array($this->http_code, array(404, 410)) ){
|
1033 |
-
$code = BLC_LINK_STATUS_ERROR;
|
1034 |
-
} else {
|
1035 |
$code = BLC_LINK_STATUS_WARNING;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1036 |
}
|
|
|
1037 |
|
1038 |
-
if (
|
1039 |
-
$text =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1040 |
}
|
1041 |
}
|
|
|
1042 |
|
1043 |
-
|
|
|
1044 |
|
1045 |
-
|
1046 |
-
|
1047 |
-
|
1048 |
-
|
1049 |
-
|
1050 |
-
|
1051 |
-
|
1052 |
-
|
1053 |
-
$code = BLC_LINK_STATUS_OK;
|
1054 |
-
}
|
1055 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1056 |
}
|
1057 |
-
}
|
1058 |
|
1059 |
-
|
1060 |
-
|
|
|
|
|
1061 |
|
1062 |
-
|
1063 |
-
|
1064 |
-
*
|
1065 |
-
* @return string
|
1066 |
-
*/
|
1067 |
-
function get_ascii_url(){
|
1068 |
-
return blcUtility::idn_to_ascii($this->url);
|
1069 |
-
}
|
1070 |
|
1071 |
-
|
1072 |
-
|
1073 |
-
*
|
1074 |
-
* Note: Only checks the domain name, not subdirectory. If there are two separate WP sites A and B installed
|
1075 |
-
* in two different subdirectories of the same domain, this method will treat a link from site A to B as internal.
|
1076 |
-
*
|
1077 |
-
* @return bool
|
1078 |
-
*/
|
1079 |
-
public function is_internal_to_domain() {
|
1080 |
-
$host = @parse_url($this->url, PHP_URL_HOST);
|
1081 |
-
if ( empty($host) ) {
|
1082 |
-
return false;
|
1083 |
}
|
1084 |
|
1085 |
-
|
1086 |
-
|
1087 |
-
|
|
|
|
|
|
|
|
|
|
|
1088 |
}
|
1089 |
-
|
1090 |
-
//Some users are inconsistent with using/not using the www prefix, so get rid of it.
|
1091 |
-
$site_host = preg_replace('@^www\.@', '', $site_host, 1);
|
1092 |
-
|
1093 |
-
//Check if $host ends with $site_host. This means blah.example.com will match example.com.
|
1094 |
-
return (substr($host, -strlen($site_host)) === $site_host);
|
1095 |
}
|
1096 |
|
1097 |
-
/**
|
1098 |
-
* Remove the query string from an URL.
|
1099 |
-
*
|
1100 |
-
* @param string $url
|
1101 |
-
* @return string
|
1102 |
-
*/
|
1103 |
-
public static function remove_query_string($url) {
|
1104 |
-
return preg_replace('@\?[^#]*?(#|$)@', '$1', $url);
|
1105 |
-
}
|
1106 |
-
}
|
1107 |
-
|
1108 |
} //class_exists
|
1109 |
|
1110 |
/**
|
@@ -1113,28 +1117,28 @@ class blcLink {
|
|
1113 |
* @param int|array $link_id (optional) Only check these links
|
1114 |
* @return bool
|
1115 |
*/
|
1116 |
-
function blc_cleanup_links( $link_id = null ){
|
1117 |
global $wpdb; /* @var wpdb $wpdb */
|
1118 |
global $blclog;
|
1119 |
|
1120 |
-
$start = microtime(true);
|
1121 |
-
$q
|
1122 |
-
USING {$wpdb->prefix}blc_links LEFT JOIN {$wpdb->prefix}blc_instances
|
1123 |
ON {$wpdb->prefix}blc_instances.link_id = {$wpdb->prefix}blc_links.link_id
|
1124 |
WHERE
|
1125 |
{$wpdb->prefix}blc_instances.link_id IS NULL";
|
1126 |
|
1127 |
-
if (
|
1128 |
-
if ( !is_array($link_id) ){
|
1129 |
-
$link_id = array( intval($link_id) );
|
1130 |
}
|
1131 |
-
$q .= " AND {$wpdb->prefix}blc_links.link_id IN (" . implode(', ', $link_id) . ')';
|
1132 |
}
|
1133 |
|
1134 |
-
$rez
|
1135 |
-
$elapsed = microtime(true) - $start;
|
1136 |
-
$blclog->log(sprintf('... %d links deleted in %.3f seconds', $wpdb->rows_affected, $elapsed));
|
1137 |
|
1138 |
-
return
|
1139 |
}
|
1140 |
|
5 |
* @copyright 2010
|
6 |
*/
|
7 |
|
8 |
+
if ( ! class_exists( 'blcLink' ) ) {
|
9 |
+
|
10 |
+
define( 'BLC_LINK_STATUS_UNKNOWN', 'unknown' );
|
11 |
+
define( 'BLC_LINK_STATUS_OK', 'ok' );
|
12 |
+
define( 'BLC_LINK_STATUS_INFO', 'info' );
|
13 |
+
define( 'BLC_LINK_STATUS_WARNING', 'warning' );
|
14 |
+
define( 'BLC_LINK_STATUS_ERROR', 'error' );
|
15 |
+
|
16 |
+
class blcLink {
|
17 |
+
|
18 |
+
//Object state
|
19 |
+
var $is_new = false;
|
20 |
+
|
21 |
+
//DB fields
|
22 |
+
var $link_id = 0;
|
23 |
+
var $url = '';
|
24 |
+
|
25 |
+
var $being_checked = false;
|
26 |
+
var $last_check = 0;
|
27 |
+
var $last_check_attempt = 0;
|
28 |
+
var $check_count = 0;
|
29 |
+
var $http_code = 0;
|
30 |
+
var $request_duration = 0;
|
31 |
+
var $timeout = false;
|
32 |
+
|
33 |
+
var $redirect_count = 0;
|
34 |
+
var $final_url = '';
|
35 |
+
|
36 |
+
var $broken = false;
|
37 |
+
public $warning = false;
|
38 |
+
var $first_failure = 0;
|
39 |
+
var $last_success = 0;
|
40 |
+
var $may_recheck = 1;
|
41 |
+
|
42 |
+
var $false_positive = false;
|
43 |
+
var $result_hash = '';
|
44 |
+
|
45 |
+
var $dismissed = false;
|
46 |
+
|
47 |
+
var $status_text = '';
|
48 |
+
var $status_code = '';
|
49 |
+
|
50 |
+
var $log = '';
|
51 |
+
|
52 |
+
//A list of DB fields and their storage formats
|
53 |
+
var $field_format;
|
54 |
+
|
55 |
+
//A cached list of the link's instances
|
56 |
+
var $_instances = null;
|
57 |
+
|
58 |
+
var $http_status_codes = array(
|
59 |
+
// [Informational 1xx]
|
60 |
+
100 => 'Continue',
|
61 |
+
101 => 'Switching Protocols',
|
62 |
+
// [Successful 2xx]
|
63 |
+
200 => 'OK',
|
64 |
+
201 => 'Created',
|
65 |
+
202 => 'Accepted',
|
66 |
+
203 => 'Non-Authoritative Information',
|
67 |
+
204 => 'No Content',
|
68 |
+
205 => 'Reset Content',
|
69 |
+
206 => 'Partial Content',
|
70 |
+
// [Redirection 3xx]
|
71 |
+
300 => 'Multiple Choices',
|
72 |
+
301 => 'Moved Permanently',
|
73 |
+
302 => 'Moved Temporarily',
|
74 |
+
303 => 'See Other',
|
75 |
+
304 => 'Not Modified',
|
76 |
+
305 => 'Use Proxy',
|
77 |
+
//306=>'(Unused)',
|
78 |
+
307 => 'Temporary Redirect',
|
79 |
+
// [Client Error 4xx]
|
80 |
+
400 => 'Bad Request',
|
81 |
+
401 => 'Unauthorized',
|
82 |
+
402 => 'Payment Required',
|
83 |
+
403 => 'Forbidden',
|
84 |
+
404 => 'Not Found',
|
85 |
+
405 => 'Method Not Allowed',
|
86 |
+
406 => 'Not Acceptable',
|
87 |
+
407 => 'Proxy Authentication Required',
|
88 |
+
408 => 'Request Timeout',
|
89 |
+
409 => 'Conflict',
|
90 |
+
410 => 'Gone',
|
91 |
+
411 => 'Length Required',
|
92 |
+
412 => 'Precondition Failed',
|
93 |
+
413 => 'Request Entity Too Large',
|
94 |
+
414 => 'Request-URI Too Long',
|
95 |
+
415 => 'Unsupported Media Type',
|
96 |
+
416 => 'Requested Range Not Satisfiable',
|
97 |
+
417 => 'Expectation Failed',
|
98 |
+
// [Server Error 5xx]
|
99 |
+
500 => 'Internal Server Error',
|
100 |
+
501 => 'Not Implemented',
|
101 |
+
502 => 'Bad Gateway',
|
102 |
+
503 => 'Service Unavailable',
|
103 |
+
504 => 'Gateway Timeout',
|
104 |
+
505 => 'HTTP Version Not Supported',
|
105 |
+
509 => 'Bandwidth Limit Exceeded',
|
106 |
+
510 => 'Not Extended',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
107 |
);
|
108 |
+
var $isOptionLinkChanged = false; //phpcs:ignore WordPress.NamingConventions.ValidVariableName.PropertyNotSnakeCase
|
109 |
+
|
110 |
+
function __construct( $arg = null ) {
|
111 |
+
global $wpdb, $blclog; /** @var wpdb $wpdb */
|
112 |
+
|
113 |
+
$this->field_format = array(
|
114 |
+
'url' => '%s',
|
115 |
+
'first_failure' => 'datetime',
|
116 |
+
'last_check' => 'datetime',
|
117 |
+
'last_success' => 'datetime',
|
118 |
+
'last_check_attempt' => 'datetime',
|
119 |
+
'check_count' => '%d',
|
120 |
+
'final_url' => '%s',
|
121 |
+
'redirect_count' => '%d',
|
122 |
+
'log' => '%s',
|
123 |
+
'http_code' => '%d',
|
124 |
+
'request_duration' => '%F',
|
125 |
+
'timeout' => 'bool',
|
126 |
+
'result_hash' => '%s',
|
127 |
+
'broken' => 'bool',
|
128 |
+
'warning' => 'bool',
|
129 |
+
'false_positive' => 'bool',
|
130 |
+
'may_recheck' => 'bool',
|
131 |
+
'being_checked' => 'bool',
|
132 |
+
'status_text' => '%s',
|
133 |
+
'status_code' => '%s',
|
134 |
+
'dismissed' => 'bool',
|
135 |
+
);
|
136 |
|
137 |
+
if ( is_numeric( $arg ) ) {
|
138 |
+
//Load a link with ID = $arg from the DB.
|
139 |
+
$q = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}blc_links WHERE link_id=%d LIMIT 1", $arg );
|
140 |
+
$arr = $wpdb->get_row( $q, ARRAY_A ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
141 |
|
142 |
+
if ( is_array( $arr ) ) { //Loaded successfully
|
143 |
+
$this->set_values( $arr );
|
144 |
+
} else {
|
145 |
+
//Link not found. The object is invalid.
|
146 |
+
//I'd throw an error, but that wouldn't be PHP 4 compatible...
|
147 |
+
$blclog->warn( __CLASS__ . ':' . __FUNCTION__ . ' Link not found.', $arg );
|
148 |
+
}
|
149 |
+
} elseif ( is_string( $arg ) ) {
|
150 |
+
//Load a link with URL = $arg from the DB. Create a new one if the record isn't found.
|
151 |
+
// $blclog->debug(__CLASS__ .':' . __FUNCTION__ . ' Trying to load a link by URL:', $arg);
|
152 |
+
$q = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}blc_links WHERE url=%s LIMIT 1", $arg );
|
153 |
+
$arr = $wpdb->get_row( $q, ARRAY_A ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
154 |
+
|
155 |
+
if ( is_array( $arr ) ) { //Loaded successfully
|
156 |
+
// $blclog->debug(__CLASS__ .':' . __FUNCTION__ . ' Success!');
|
157 |
+
$this->set_values( $arr );
|
158 |
+
} else { //Link not found, treat as new
|
159 |
+
// $blclog->debug(__CLASS__ .':' . __FUNCTION__ . ' Link not found.');
|
160 |
+
$this->url = $arg;
|
161 |
+
$this->is_new = true;
|
162 |
+
}
|
163 |
+
} elseif ( is_array( $arg ) ) {
|
164 |
+
$this->set_values( $arg );
|
165 |
+
//Is this a new link?
|
166 |
+
$this->is_new = empty( $this->link_id );
|
167 |
} else {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
168 |
$this->is_new = true;
|
169 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
170 |
}
|
|
|
171 |
|
172 |
+
function blcLink( $arg = null ) {
|
173 |
+
$this->__construct( $arg );
|
174 |
+
}
|
175 |
|
176 |
+
/**
|
177 |
+
* blcLink::set_values()
|
178 |
+
* Set the internal values to the ones provided in an array (doesn't sanitize).
|
179 |
+
*
|
180 |
+
* @param array $arr An associative array of values
|
181 |
+
* @return void
|
182 |
+
*/
|
183 |
+
function set_values( $arr ) {
|
184 |
+
$arr = $this->to_native_format( $arr );
|
185 |
+
|
186 |
+
foreach ( $arr as $key => $value ) {
|
187 |
+
$this->$key = $value;
|
188 |
+
}
|
189 |
}
|
|
|
190 |
|
191 |
+
/**
|
192 |
+
* Check whether the object represents a valid link
|
193 |
+
*
|
194 |
+
* @return bool
|
195 |
+
*/
|
196 |
+
function valid() {
|
197 |
+
return ! empty( $this->url ) && ( ! empty( $this->link_id ) || $this->is_new );
|
198 |
+
}
|
199 |
|
200 |
+
/**
|
201 |
+
* Check if the link is working.
|
202 |
+
*
|
203 |
+
* @param bool $save_results Automatically save the results of the check.
|
204 |
+
* @return bool
|
205 |
+
*/
|
206 |
+
function check( $save_results = true ) {
|
207 |
+
if ( ! $this->valid() ) {
|
208 |
+
return false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
209 |
}
|
210 |
|
211 |
+
$this->last_check_attempt = time();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
212 |
|
213 |
+
/*
|
214 |
+
If the link is still marked as in the process of being checked, that probably means
|
215 |
+
that the last time the plugin tried to check it the script got terminated by PHP for
|
216 |
+
running over the execution time limit or causing a fatal error.
|
217 |
|
218 |
+
This problem is likely to be temporary for most links, so we leave it be and treat it
|
219 |
+
as any other link (i.e. check it again later using the default recheck periodicity).
|
220 |
+
*/
|
221 |
+
if ( $this->being_checked ) {
|
222 |
+
$this->being_checked = false;
|
223 |
|
224 |
+
//Add an explanatory notice to the link's log
|
225 |
+
$error_notice = '[' . __( 'The plugin script was terminated while trying to check the link.', 'broken-link-checker' ) . ']';
|
226 |
+
if ( strpos( $this->log, $error_notice ) === false ) {
|
227 |
+
$this->log = $error_notice . "\r\n" . $this->log;
|
228 |
+
}
|
229 |
+
|
230 |
+
if ( $save_results ) {
|
231 |
+
$this->save();
|
232 |
+
}
|
233 |
+
|
234 |
+
return false;
|
235 |
+
}
|
236 |
|
237 |
+
$this->being_checked = true;
|
238 |
+
$this->check_count++;
|
239 |
+
|
240 |
+
if ( $save_results ) {
|
241 |
+
|
242 |
+
//Update the DB record before actually performing the check.
|
243 |
+
//Useful if something goes terribly wrong while checking this particular URL
|
244 |
+
//(e.g. the server might kill the script for running over the exec. time limit).
|
245 |
+
//Note : might be unnecessary.
|
246 |
$this->save();
|
247 |
}
|
248 |
|
249 |
+
$defaults = array(
|
250 |
+
'broken' => false,
|
251 |
+
'warning' => false,
|
252 |
+
'http_code' => 0,
|
253 |
+
'redirect_count' => 0,
|
254 |
+
'final_url' => $this->url,
|
255 |
+
'request_duration' => 0,
|
256 |
+
'timeout' => false,
|
257 |
+
'may_recheck' => true,
|
258 |
+
'log' => '',
|
259 |
+
'result_hash' => '',
|
260 |
+
'status_text' => '',
|
261 |
+
'status_code' => '',
|
262 |
+
);
|
263 |
+
|
264 |
+
$checker = blcCheckerHelper::get_checker_for( $this->get_ascii_url() );
|
265 |
+
|
266 |
+
if ( is_null( $checker ) ) {
|
267 |
+
//Oops, there are no checker implementations that can handle this link.
|
268 |
+
//Assume the link is working, but leave a note in the log.
|
269 |
+
$this->broken = false;
|
270 |
+
$this->being_checked = false;
|
271 |
+
$this->log = __( "The plugin doesn't know how to check this type of link.", 'broken-link-checker' );
|
272 |
+
|
273 |
+
if ( $save_results ) {
|
274 |
+
$this->save();
|
275 |
+
}
|
276 |
|
277 |
+
return true;
|
278 |
+
}
|
|
|
279 |
|
280 |
+
//Check the link
|
281 |
+
$rez = $checker->check( $this->get_ascii_url() );
|
282 |
+
//FB::info($rez, "Check results");
|
283 |
|
284 |
+
$results = array_merge( $defaults, $rez );
|
|
|
285 |
|
286 |
+
//Some HTTP errors can be treated as warnings.
|
287 |
+
$results = $this->decide_warning_state( $results );
|
288 |
|
289 |
+
//Filter the returned array to leave only the restricted set of keys that we're interested in.
|
290 |
+
$results = array_intersect_key( $results, $defaults );
|
|
|
291 |
|
292 |
+
//The result hash is special - see blcLink::status_changed()
|
293 |
+
$new_result_hash = $results['result_hash'];
|
294 |
+
unset( $results['result_hash'] );
|
295 |
|
296 |
+
//Update the object's fields with the new results
|
297 |
+
$this->set_values( $results );
|
|
|
298 |
|
299 |
+
//Update timestamps & state-dependent fields
|
300 |
+
$this->status_changed( $results['broken'], $new_result_hash );
|
301 |
+
$this->being_checked = false;
|
|
|
302 |
|
303 |
+
//Save results to the DB
|
304 |
+
if ( $save_results ) {
|
305 |
+
$this->save();
|
306 |
+
}
|
307 |
|
308 |
+
return $this->broken;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
309 |
}
|
310 |
|
311 |
+
/**
|
312 |
+
* Decide whether the result of the latest check means that the link is really broken
|
313 |
+
* or should just be reported as a warning.
|
314 |
+
*
|
315 |
+
* @param array $check_results
|
316 |
+
* @return array
|
317 |
+
*/
|
318 |
+
private function decide_warning_state( $check_results ) {
|
319 |
+
if ( ! $check_results['broken'] && ! $check_results['warning'] ) {
|
320 |
+
//Nothing to do, this is a working link.
|
321 |
+
return $check_results;
|
322 |
}
|
|
|
|
|
323 |
|
324 |
+
$configuration = blc_get_configuration();
|
325 |
+
if ( ! $configuration->get( 'warnings_enabled', true ) ) {
|
326 |
+
//The user wants all failures to be reported as "broken", regardless of severity.
|
327 |
+
if ( $check_results['warning'] ) {
|
328 |
+
$check_results['broken'] = true;
|
329 |
+
$check_results['warning'] = false;
|
330 |
+
}
|
331 |
+
return $check_results;
|
332 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
333 |
|
334 |
+
$warning_reason = null;
|
335 |
+
$failure_count = $this->check_count;
|
336 |
+
$failure_duration = ( 0 != $this->first_failure ) ? ( time() - $this->first_failure ) : 0;
|
337 |
+
//These could be configurable, but lets put that off until someone actually asks for it.
|
338 |
+
$duration_threshold = 24 * 3600;
|
339 |
+
$count_threshold = 3;
|
340 |
+
|
341 |
+
//We can't just use ($check_results['status_code'] == 'warning' because some "warning" problems are not
|
342 |
+
//temporary. For example, region-restricted YouTube videos use the "warning" status code.
|
343 |
+
$maybe_temporary_error = false;
|
344 |
+
|
345 |
+
//Some basic heuristics to determine if this failure might be temporary.
|
346 |
+
//----------------------------------------------------------------------
|
347 |
+
if ( $check_results['timeout'] ) {
|
348 |
+
$maybe_temporary_error = true;
|
349 |
+
$warning_reason = 'Timeouts are sometimes caused by high server load or other temporary issues.';
|
350 |
+
}
|
351 |
|
352 |
+
$error_code = isset( $check_results['error_code'] ) ? $check_results['error_code'] : '';
|
353 |
+
if ( 'connection_failed' === $error_code ) {
|
354 |
+
$maybe_temporary_error = true;
|
355 |
+
$warning_reason = 'Connection failures are sometimes caused by high server load or other temporary issues.';
|
356 |
+
}
|
357 |
+
|
358 |
+
$http_code = intval( $check_results['http_code'] );
|
359 |
+
$temporary_http_errors = array(
|
360 |
+
408, //Request timeout. Probably a plugin bug, but could just be an overloaded client server.
|
361 |
+
420, //Custom Twitter code returned when the client gets rate-limited.
|
362 |
+
429, //Client has sent too many requests in a given amount of time.
|
363 |
+
502, //Bad Gateway. Often a sign of a temporarily overloaded or misconfigured server.
|
364 |
+
503, //Service Unavailable.
|
365 |
+
504, //Gateway Timeout.
|
366 |
+
509, //Bandwidth Limit Exceeded.
|
367 |
+
520, //CloudFlare-specific "Origin Error" code.
|
368 |
+
522, //CloudFlare-specific "Connection timed out" code.
|
369 |
+
524, //Another CloudFlare-specific timeout code.
|
370 |
+
);
|
371 |
+
if ( in_array( $http_code, $temporary_http_errors ) ) {
|
372 |
+
$maybe_temporary_error = true;
|
373 |
+
|
374 |
+
if ( in_array( $http_code, array( 502, 503, 504, 509 ) ) ) {
|
375 |
+
$warning_reason = sprintf(
|
376 |
+
'HTTP error %d usually means that the site is down due to high server load or a configuration problem. '
|
377 |
+
. 'This error is often temporary and will go away after while.',
|
378 |
+
$http_code
|
379 |
+
);
|
380 |
+
} else {
|
381 |
+
$warning_reason = 'This HTTP error is often temporary.';
|
382 |
+
}
|
383 |
}
|
|
|
384 |
|
385 |
+
//----------------------------------------------------------------------
|
386 |
|
387 |
+
//Attempt to detect false positives.
|
388 |
+
$suspected_false_positive = false;
|
389 |
|
390 |
+
//A "403 Forbidden" error on an internal link usually means something on the site is blocking automated
|
391 |
+
//requests. Possible culprits include hotlink protection rules in .htaccess, badly configured IDS, and so on.
|
392 |
+
$is_internal_link = $this->is_internal_to_domain();
|
393 |
+
if ( $is_internal_link && ( 403 === $http_code ) ) {
|
394 |
+
$suspected_false_positive = true;
|
395 |
+
$warning_reason = 'This might be a false positive. Make sure the link is not password-protected, '
|
396 |
. 'and that your server is not set up to block automated requests.';
|
397 |
+
}
|
398 |
|
399 |
+
//Some hosting providers turn off loopback connections. This causes all internal links to be reported as broken.
|
400 |
+
if ( $is_internal_link && in_array( $error_code, array( 'connection_failed', 'couldnt_resolve_host' ) ) ) {
|
401 |
+
$suspected_false_positive = true;
|
402 |
+
$warning_reason = 'This is probably a false positive. ';
|
403 |
+
if ( 'connection_failed' === $error_code ) {
|
404 |
+
$warning_reason .= 'The plugin could not connect to your site. That usually means that your '
|
405 |
. 'hosting provider has disabled loopback connections.';
|
406 |
+
} elseif ( 'couldnt_resolve_host' === $error_code ) {
|
407 |
+
$warning_reason .= 'The plugin could not connect to your site because DNS resolution failed. '
|
408 |
. 'This could mean DNS is configured incorrectly on your server.';
|
409 |
+
}
|
410 |
}
|
|
|
411 |
|
412 |
+
//----------------------------------------------------------------------
|
413 |
|
414 |
+
//Temporary problems and suspected false positives start out as warnings. False positives stay that way
|
415 |
+
//indefinitely because they are usually caused by bugs and server configuration issues, not temporary downtime.
|
416 |
+
if ( ( $maybe_temporary_error && ( $failure_count < $count_threshold ) ) || $suspected_false_positive ) {
|
417 |
+
$check_results['warning'] = true;
|
418 |
+
$check_results['broken'] = false;
|
419 |
+
}
|
420 |
|
421 |
+
//Upgrade temporary warnings to "broken" after X consecutive failures or Y hours, whichever comes first.
|
422 |
+
$threshold_reached = ( $failure_count >= $count_threshold ) || ( $failure_duration >= $duration_threshold );
|
423 |
+
if ( $check_results['warning'] ) {
|
424 |
+
if ( ( $maybe_temporary_error && $threshold_reached ) && ! $suspected_false_positive ) {
|
425 |
+
$check_results['warning'] = false;
|
426 |
+
$check_results['broken'] = true;
|
427 |
+
}
|
428 |
}
|
|
|
429 |
|
430 |
+
if ( ! empty( $warning_reason ) && $check_results['warning'] ) {
|
431 |
+
$formatted_reason = "\n==========\n"
|
432 |
. 'Severity: Warning' . "\n"
|
433 |
+
. 'Reason: ' . trim( $warning_reason )
|
434 |
. "\n==========\n";
|
435 |
|
436 |
+
$check_results['log'] .= $formatted_reason;
|
437 |
+
}
|
438 |
|
439 |
+
return $check_results;
|
440 |
+
}
|
441 |
|
442 |
+
/**
|
443 |
+
* A helper method used to update timestamps & other state-dependent fields
|
444 |
+
* after the state of the link (broken vs working) has just been determined.
|
445 |
+
*
|
446 |
+
* @access private
|
447 |
+
*
|
448 |
+
* @param bool $broken
|
449 |
+
* @param string $new_result_hash
|
450 |
+
* @return void
|
451 |
+
*/
|
452 |
+
private function status_changed( $broken, $new_result_hash = '' ) {
|
453 |
+
//If a link's status changes, un-dismiss it.
|
454 |
+
if ( $this->result_hash != $new_result_hash ) {
|
455 |
+
if ( $this->dismissed ) {
|
456 |
+
$this->log .= sprintf(
|
457 |
+
"Restoring a dismissed link. \nOld status: \n%s\nNew status: \n%s\n",
|
458 |
+
$this->result_hash,
|
459 |
+
$new_result_hash
|
460 |
+
);
|
461 |
+
}
|
462 |
+
$this->dismissed = false;
|
463 |
}
|
|
|
|
|
464 |
|
465 |
+
if ( $this->false_positive && ! empty( $new_result_hash ) ) {
|
466 |
+
//If the link has been marked as a (probable) false positive,
|
467 |
+
//mark it as broken *only* if the new result is different from
|
468 |
+
//the one that caused the user to mark it as a false positive.
|
469 |
+
if ( $broken || $this->warning ) {
|
470 |
+
if ( $this->result_hash == $new_result_hash ) {
|
471 |
+
//Got the same result as before, assume it's still incorrect and the link actually works.
|
472 |
+
$broken = false;
|
473 |
+
$this->warning = false;
|
474 |
+
} else {
|
475 |
+
//Got a new result. Assume (quite optimistically) that it's not a false positive.
|
476 |
+
$this->false_positive = false;
|
477 |
+
}
|
478 |
} else {
|
479 |
+
//The plugin now thinks the link is working,
|
480 |
+
//so it's no longer a false positive.
|
481 |
$this->false_positive = false;
|
482 |
}
|
|
|
|
|
|
|
|
|
483 |
}
|
|
|
484 |
|
485 |
+
$this->broken = $broken;
|
486 |
+
$this->result_hash = $new_result_hash;
|
487 |
|
488 |
+
//Update timestamps
|
489 |
+
$this->last_check = $this->last_check_attempt;
|
490 |
+
if ( $this->broken || $this->warning ) {
|
491 |
+
if ( empty( $this->first_failure ) ) {
|
492 |
+
$this->first_failure = $this->last_check;
|
493 |
+
}
|
494 |
+
} else {
|
495 |
+
$this->first_failure = 0;
|
496 |
+
$this->last_success = $this->last_check;
|
497 |
+
$this->check_count = 0;
|
498 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
499 |
|
500 |
+
//Add a line indicating link status to the log
|
501 |
+
if ( $this->broken || $this->warning ) {
|
502 |
+
$this->log .= "\n" . __( 'Link is broken.', 'broken-link-checker' );
|
503 |
+
} else {
|
504 |
+
$this->log .= "\n" . __( 'Link is valid.', 'broken-link-checker' );
|
505 |
+
}
|
506 |
}
|
507 |
|
508 |
+
/**
|
509 |
+
* blcLink::save()
|
510 |
+
* Save link data to DB.
|
511 |
+
*
|
512 |
+
* @return bool True if saved successfully, false otherwise.
|
513 |
+
*/
|
514 |
+
function save() {
|
515 |
+
global $wpdb, $blclog; /** @var wpdb $wpdb */
|
516 |
+
|
517 |
+
if ( ! $this->valid() ) {
|
518 |
+
return false;
|
519 |
+
}
|
520 |
|
521 |
+
//A link can't be broken and treated as a warning at the same time.
|
522 |
+
if ( $this->broken && $this->warning ) {
|
523 |
+
$this->warning = false;
|
524 |
+
}
|
525 |
|
526 |
+
//Make a list of fields to be saved and their values in DB format
|
527 |
+
$values = array();
|
528 |
+
foreach ( $this->field_format as $field => $format ) {
|
529 |
+
$values[ $field ] = $this->$field;
|
530 |
+
}
|
531 |
+
$values = $this->to_db_format( $values );
|
532 |
|
533 |
+
if ( $this->is_new ) {
|
|
|
|
|
|
|
|
|
|
|
534 |
|
535 |
+
//BUG: Technically, there should be a 'LOCK TABLES wp_blc_links WRITE' here. In fact,
|
536 |
+
//the plugin should probably lock all involved tables whenever it parses something, lest
|
537 |
+
//the user (ot another plugin) modify the thing being parsed while we're working.
|
538 |
+
//The problem with table locking, though, is that parsing takes a long time and having
|
539 |
+
//all of WP freeze while the plugin is working would be a Bad Thing. Food for thought.
|
|
|
540 |
|
541 |
+
//Check if there's already a link with this URL present
|
542 |
+
$q = $wpdb->prepare(
|
543 |
+
"SELECT link_id FROM {$wpdb->prefix}blc_links WHERE url = %s",
|
544 |
+
$this->url
|
545 |
+
);
|
546 |
+
$existing_id = $wpdb->get_var( $q ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
|
|
|
|
547 |
|
548 |
+
if ( ! empty( $existing_id ) ) {
|
549 |
+
//Dammit.
|
550 |
+
$this->link_id = $existing_id;
|
551 |
+
$this->is_new = false;
|
552 |
+
return true;
|
553 |
+
}
|
554 |
|
555 |
+
//Insert a new row
|
556 |
+
$q = sprintf(
|
557 |
+
"INSERT INTO {$wpdb->prefix}blc_links( %s ) VALUES( %s )",
|
558 |
+
implode( ', ', array_keys( $values ) ),
|
559 |
+
implode( ', ', array_values( $values ) )
|
560 |
+
);
|
561 |
+
//FB::log($q, 'Link add query');
|
562 |
+
$blclog->debug( __CLASS__ . ':' . __FUNCTION__ . ' Adding a new link. SQL query:' . "\n", $q );
|
|
|
|
|
563 |
|
564 |
+
$rez = false !== $wpdb->query( $q ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
565 |
|
566 |
+
if ( $rez ) {
|
567 |
+
$this->link_id = $wpdb->insert_id;
|
568 |
+
$blclog->debug( __CLASS__ . ':' . __FUNCTION__ . ' Database record created. ID = ' . $this->link_id );
|
569 |
+
//FB::info($this->link_id, "Link added");
|
570 |
+
//If the link was successfully saved then it's no longer "new"
|
571 |
+
$this->is_new = false;
|
572 |
+
} else {
|
573 |
+
$blclog->error( __CLASS__ . ':' . __FUNCTION__ . ' Error adding link', $this->url );
|
574 |
+
//FB::error($wpdb->last_error, "Error adding link {$this->url}");
|
575 |
+
}
|
576 |
|
577 |
+
TransactionManager::getInstance()->commit();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
578 |
|
579 |
+
return $rez;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
580 |
|
|
|
|
|
|
|
|
|
581 |
} else {
|
582 |
+
if ( true !== $this->isOptionLinkChanged ) {
|
583 |
+
TransactionManager::getInstance()->start();
|
584 |
+
}
|
585 |
+
$this->isOptionLinkChanged = false;
|
586 |
+
//Generate the field = dbvalue expressions
|
587 |
+
$set_exprs = array();
|
588 |
+
foreach ( $values as $name => $value ) {
|
589 |
+
$set_exprs[] = "$name = $value";
|
590 |
+
}
|
591 |
+
$set_exprs = implode( ', ', $set_exprs );
|
592 |
+
|
593 |
+
//Update an existing DB record
|
594 |
+
$q = sprintf(
|
595 |
+
"UPDATE {$wpdb->prefix}blc_links SET %s WHERE link_id=%d",
|
596 |
+
$set_exprs,
|
597 |
+
intval( $this->link_id )
|
598 |
+
);
|
599 |
+
//FB::log($q, 'Link update query');
|
600 |
+
$blclog->debug( __CLASS__ . ':' . __FUNCTION__ . ' Updating a link. SQL query:' . "\n", $q );
|
601 |
|
602 |
+
$rez = false !== $wpdb->query( $q ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
603 |
+
if ( $rez ) {
|
604 |
+
//FB::log($this->link_id, "Link updated");
|
605 |
+
$blclog->debug( __CLASS__ . ':' . __FUNCTION__ . ' Link updated.' );
|
606 |
+
} else {
|
607 |
+
$blclog->error( __CLASS__ . ':' . __FUNCTION__ . ' Error updating link', $this->url );
|
608 |
+
//FB::error($wpdb->last_error, "Error updating link {$this->url}");
|
609 |
+
}
|
610 |
|
611 |
+
TransactionManager::getInstance()->commit();
|
|
|
|
|
612 |
|
613 |
+
return $rez;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
614 |
}
|
615 |
+
}
|
616 |
|
617 |
+
/**
|
618 |
+
* A helper method for converting the link's field values to DB format and escaping them
|
619 |
+
* for use in SQL queries.
|
620 |
+
*
|
621 |
+
* @param array $values
|
622 |
+
* @return array
|
623 |
+
*/
|
624 |
+
function to_db_format( $values ) {
|
625 |
+
global $wpdb; /** @var wpdb $wpdb */
|
626 |
+
|
627 |
+
$dbvalues = array();
|
628 |
+
|
629 |
+
foreach ( $values as $name => $value ) {
|
630 |
+
//Skip fields that don't exist in the blc_links table.
|
631 |
+
if ( ! isset( $this->field_format[ $name ] ) ) {
|
632 |
+
continue;
|
633 |
+
}
|
634 |
|
635 |
+
$format = $this->field_format[ $name ];
|
636 |
+
|
637 |
+
//Convert native values to a format comprehensible to the DB
|
638 |
+
switch ( $format ) {
|
639 |
+
|
640 |
+
case 'datetime':
|
641 |
+
if ( empty( $value ) ) {
|
642 |
+
$value = '0000-00-00 00:00:00';
|
643 |
+
} else {
|
644 |
+
$value = date( 'Y-m-d H:i:s', $value ); //phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
|
645 |
+
}
|
646 |
+
$format = '%s';
|
647 |
+
break;
|
648 |
+
|
649 |
+
case 'bool':
|
650 |
+
if ( $value ) {
|
651 |
+
$value = 1;
|
652 |
+
} else {
|
653 |
+
$value = 0;
|
654 |
+
}
|
655 |
+
$format = '%d';
|
656 |
+
break;
|
657 |
+
}
|
658 |
|
659 |
+
//Escapize
|
660 |
+
$value = $wpdb->prepare( $format, $value ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
|
|
|
|
|
|
|
|
|
|
|
|
661 |
|
662 |
+
$dbvalues[ $name ] = $value;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
663 |
}
|
664 |
|
665 |
+
return $dbvalues;
|
|
|
|
|
|
|
666 |
}
|
667 |
|
668 |
+
/**
|
669 |
+
* A helper method for converting values fetched from the database to native datatypes.
|
670 |
+
*
|
671 |
+
* @param array $values
|
672 |
+
* @return array
|
673 |
+
*/
|
674 |
+
function to_native_format( $values ) {
|
675 |
+
|
676 |
+
foreach ( $values as $name => $value ) {
|
677 |
+
//Don't process fields that don't exist in the blc_links table.
|
678 |
+
if ( ! isset( $this->field_format[ $name ] ) ) {
|
679 |
+
continue;
|
680 |
+
}
|
681 |
|
682 |
+
$format = $this->field_format[ $name ];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
683 |
|
684 |
+
//Convert values in DB format to native datatypes.
|
685 |
+
switch ( $format ) {
|
686 |
|
687 |
+
case 'datetime':
|
688 |
+
if ( '0000-00-00 00:00:00' == $value ) {
|
689 |
+
$value = 0;
|
690 |
+
} elseif ( is_string( $value ) ) {
|
691 |
+
$value = strtotime( $value );
|
692 |
+
}
|
693 |
+
break;
|
694 |
|
695 |
+
case 'bool':
|
696 |
+
$value = (bool) $value;
|
697 |
+
break;
|
|
|
|
|
|
|
|
|
698 |
|
699 |
+
case '%d':
|
700 |
+
$value = intval( $value );
|
701 |
+
break;
|
702 |
|
703 |
+
case '%f':
|
704 |
+
$value = floatval( $value );
|
705 |
+
break;
|
706 |
|
707 |
+
}
|
|
|
|
|
708 |
|
709 |
+
$values[ $name ] = $value;
|
710 |
}
|
711 |
|
712 |
+
return $values;
|
713 |
}
|
714 |
|
715 |
+
/**
|
716 |
+
* blcLink::edit()
|
717 |
+
* Edit all instances of the link by changing the URL.
|
718 |
+
*
|
719 |
+
* Here's how this really works : create a new link with the new URL. Then edit()
|
720 |
+
* all instances and point them to the new link record. If some instance can't be
|
721 |
+
* edited they will still point to the old record. The old record is deleted
|
722 |
+
* if all instances were edited successfully.
|
723 |
+
*
|
724 |
+
* @param string $new_url
|
725 |
+
* @param string $new_text Optional.
|
726 |
+
* @return array An associative array with these keys :
|
727 |
+
* new_link_id - the database ID of the new link.
|
728 |
+
* new_link - the new link (an instance of blcLink).
|
729 |
+
* cnt_okay - the number of successfully edited link instances.
|
730 |
+
* cnt_error - the number of instances that caused problems.
|
731 |
+
* errors - an array of WP_Error objects corresponding to the failed edits.
|
732 |
+
*/
|
733 |
+
function edit( $new_url, $new_text = null ) {
|
734 |
+
if ( ! $this->valid() ) {
|
735 |
+
return new WP_Error(
|
736 |
+
'link_invalid',
|
737 |
+
__( 'Link is not valid', 'broken-link-checker' )
|
738 |
+
);
|
739 |
+
}
|
|
|
|
|
|
|
740 |
|
741 |
+
//FB::info('Changing link '.$this->link_id .' to URL "'.$new_url.'"');
|
742 |
|
743 |
+
$instances = $this->get_instances();
|
744 |
+
//Fail if there are no instances
|
745 |
+
if ( empty( $instances ) ) {
|
746 |
+
return array(
|
747 |
+
'new_link_id' => $this->link_id,
|
748 |
+
'new_link' => $this,
|
749 |
+
'cnt_okay' => 0,
|
750 |
+
'cnt_error' => 0,
|
751 |
+
'errors' => array(
|
752 |
+
new WP_Error(
|
753 |
+
'no_instances_found',
|
754 |
+
__( 'This link can not be edited because it is not used anywhere on this site.', 'broken-link-checker' )
|
755 |
+
),
|
756 |
+
),
|
757 |
+
);
|
758 |
+
};
|
759 |
+
|
760 |
+
//Load or create a link with the URL = $new_url
|
761 |
+
$new_link = new blcLink( $new_url );
|
762 |
+
$was_new = $new_link->is_new;
|
763 |
+
if ( $new_link->is_new ) {
|
764 |
+
//FB::log($new_link, 'Saving a new link');
|
765 |
+
$new_link->save(); //so that we get a valid link_id
|
766 |
+
}
|
|
|
|
|
767 |
|
768 |
+
//FB::log("Changing link to $new_url");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
769 |
|
770 |
+
if ( empty( $new_link->link_id ) ) {
|
771 |
+
//FB::error("Failed to create a new link record");
|
772 |
+
return array(
|
773 |
+
'new_link_id' => $this->link_id,
|
774 |
+
'new_link' => $this,
|
775 |
+
'cnt_okay' => 0,
|
776 |
+
'cnt_error' => 0,
|
777 |
+
'errors' => array(
|
778 |
+
new WP_Error(
|
779 |
+
'link_creation_failed',
|
780 |
+
__( 'Failed to create a DB entry for the new URL.', 'broken-link-checker' )
|
781 |
+
),
|
782 |
+
),
|
783 |
+
);
|
|
|
|
|
784 |
}
|
|
|
785 |
|
786 |
+
$cnt_okay = 0;
|
787 |
+
$cnt_error = 0;
|
788 |
+
$errors = array();
|
789 |
+
|
790 |
+
//Edit each instance.
|
791 |
+
//FB::info('Editing ' . count($instances) . ' instances');
|
792 |
+
foreach ( $instances as $instance ) {
|
793 |
+
$rez = $instance->edit( $new_url, $this->url, $new_text );
|
794 |
+
if ( is_wp_error( $rez ) ) {
|
795 |
+
$cnt_error++;
|
796 |
+
array_push( $errors, $rez );
|
797 |
+
//FB::error($instance, 'Failed to edit instance ' . $instance->instance_id);
|
798 |
+
} else {
|
799 |
+
$cnt_okay++;
|
800 |
+
$instance->link_id = $new_link->link_id;
|
801 |
+
$instance->save();
|
802 |
+
//FB::info($instance, 'Successfully edited instance ' . $instance->instance_id);
|
803 |
+
}
|
804 |
+
}
|
805 |
|
806 |
+
//If all instances were edited successfully we can delete the old link record.
|
807 |
+
//UNLESS this link is equal to the new link (which should never happen, but whatever).
|
808 |
+
if ( ( 0 == $cnt_error ) && ( $cnt_okay > 0 ) && ( $this->link_id != $new_link->link_id ) ) {
|
809 |
+
$this->forget( false );
|
810 |
+
}
|
|
|
811 |
|
812 |
+
//On the other hand, if no instances could be edited and the $new_link was really new,
|
813 |
+
//then delete it.
|
814 |
+
if ( ( 0 == $cnt_okay ) && $was_new ) {
|
815 |
+
$new_link->forget( false );
|
816 |
+
$new_link = $this;
|
817 |
+
}
|
|
|
|
|
818 |
|
819 |
+
return array(
|
820 |
+
'new_link_id' => $new_link->link_id,
|
821 |
+
'new_link' => $new_link,
|
822 |
+
'cnt_okay' => $cnt_okay,
|
823 |
+
'cnt_error' => $cnt_error,
|
824 |
+
'errors' => $errors,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
825 |
);
|
826 |
}
|
827 |
|
828 |
+
/**
|
829 |
+
* Edit all of of this link's instances and replace the URL with the URL that it redirects to.
|
830 |
+
* This method does nothing if the link isn't a redirect.
|
831 |
+
*
|
832 |
+
* @see blcLink::edit()
|
833 |
+
*
|
834 |
+
* @return array|WP_Error
|
835 |
+
*/
|
836 |
+
function deredirect() {
|
837 |
+
if ( ! $this->valid() ) {
|
838 |
+
return new WP_Error(
|
839 |
+
'link_invalid',
|
840 |
+
__( 'Link is not valid', 'broken-link-checker' )
|
841 |
+
);
|
842 |
+
}
|
843 |
|
844 |
+
if ( ( $this->redirect_count <= 0 ) || empty( $this->final_url ) ) {
|
845 |
+
return new WP_Error(
|
846 |
+
'not_redirect',
|
847 |
+
__( 'This link is not a redirect', 'broken-link-checker' )
|
848 |
+
);
|
849 |
+
}
|
850 |
|
851 |
+
//Preserve the existing #anchor if the redirect doesn't include one.
|
852 |
+
$new_url = $this->final_url;
|
853 |
+
$anchor = @parse_url( $this->url, PHP_URL_FRAGMENT );
|
854 |
+
if ( ! empty( $anchor ) && ( strrpos( $new_url, '#' ) === false ) ) {
|
855 |
+
$new_url .= '#' . $anchor;
|
856 |
+
}
|
857 |
|
858 |
+
return $this->edit( $new_url );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
859 |
}
|
860 |
|
861 |
+
/**
|
862 |
+
* Unlink all instances and delete the link record.
|
863 |
+
*
|
864 |
+
* @return array|WP_Error An associative array with these keys :
|
865 |
+
* cnt_okay - the number of successfully removed instances.
|
866 |
+
* cnt_error - the number of instances that couldn't be removed.
|
867 |
+
* link_deleted - true if the link record was deleted.
|
868 |
+
* errors - an array of WP_Error objects describing the errors that were encountered, if any.
|
869 |
+
*/
|
870 |
+
function unlink() {
|
871 |
+
if ( ! $this->valid() ) {
|
872 |
+
return new WP_Error(
|
873 |
+
'link_invalid',
|
874 |
+
__( 'Link is not valid', 'broken-link-checker' )
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
875 |
);
|
876 |
}
|
|
|
877 |
|
878 |
+
//FB::info($this, 'Removing link');
|
879 |
+
$instances = $this->get_instances();
|
880 |
+
|
881 |
+
//No instances? Just remove the link then.
|
882 |
+
if ( empty( $instances ) ) {
|
883 |
+
//FB::warn("This link has no instances. Deleting the link.");
|
884 |
+
$rez = $this->forget( false ) !== false;
|
885 |
+
|
886 |
+
if ( $rez ) {
|
887 |
+
return array(
|
888 |
+
'cnt_okay' => 1,
|
889 |
+
'cnt_error' => 0,
|
890 |
+
'link_deleted' => true,
|
891 |
+
'errors' => array(),
|
892 |
+
);
|
893 |
+
} else {
|
894 |
+
return array(
|
895 |
+
'cnt_okay' => 0,
|
896 |
+
'cnt_error' => 0,
|
897 |
+
'link_deleted' => false,
|
898 |
+
'errors' => array(
|
899 |
+
new WP_Error(
|
900 |
+
'deletion_failed',
|
901 |
+
__( "Couldn't delete the link's database record", 'broken-link-checker' )
|
902 |
+
),
|
903 |
+
),
|
904 |
+
);
|
905 |
+
}
|
906 |
+
}
|
907 |
|
908 |
+
//FB::info('Unlinking ' . count($instances) . ' instances');
|
909 |
|
910 |
+
$cnt_okay = 0;
|
911 |
+
$cnt_error = 0;
|
912 |
+
$errors = array();
|
913 |
|
914 |
+
//Unlink each instance.
|
915 |
+
foreach ( $instances as $instance ) {
|
916 |
+
$rez = $instance->unlink( $this->url );
|
917 |
|
918 |
+
if ( is_wp_error( $rez ) ) {
|
919 |
+
$cnt_error++;
|
920 |
+
array_push( $errors, $rez );
|
921 |
+
//FB::error( $instance, 'Failed to unlink instance' );
|
922 |
+
} else {
|
923 |
+
$cnt_okay++;
|
924 |
+
//FB::info( $instance, 'Successfully unlinked instance' );
|
925 |
+
}
|
926 |
}
|
|
|
927 |
|
928 |
+
//If all instances were unlinked successfully we can delete the link record.
|
929 |
+
if ( ( 0 == $cnt_error ) && ( $cnt_okay > 0 ) ) {
|
930 |
+
//FB::log('Instances removed, deleting the link.');
|
931 |
+
$link_deleted = $this->forget() !== false;
|
932 |
+
|
933 |
+
if ( ! $link_deleted ) {
|
934 |
+
array_push(
|
935 |
+
$errors,
|
936 |
+
new WP_Error(
|
937 |
+
'deletion_failed',
|
938 |
+
__( "Couldn't delete the link's database record", 'broken-link-checker' )
|
939 |
+
)
|
940 |
+
);
|
941 |
+
}
|
942 |
+
} else {
|
943 |
+
//FB::error("Something went wrong. Unlinked instances : $cnt_okay, errors : $cnt_error");
|
944 |
+
$link_deleted = false;
|
945 |
}
|
946 |
|
947 |
+
return array(
|
948 |
+
'cnt_okay' => $cnt_okay,
|
949 |
+
'cnt_error' => $cnt_error,
|
950 |
+
'link_deleted' => $link_deleted,
|
951 |
+
'errors' => $errors,
|
952 |
+
);
|
953 |
}
|
954 |
|
955 |
+
/**
|
956 |
+
* Remove the link and (optionally) its instance records from the DB. Doesn't alter posts/etc.
|
957 |
+
*
|
958 |
+
* @param bool $remove_instances
|
959 |
+
* @return mixed 1 on success, 0 if link not found, false on error.
|
960 |
+
*/
|
961 |
+
function forget( $remove_instances = true ) {
|
962 |
+
global $wpdb; /** @var wpdb $wpdb */
|
963 |
+
if ( ! $this->valid() ) {
|
964 |
+
return false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
965 |
}
|
966 |
|
967 |
+
if ( ! empty( $this->link_id ) ) {
|
968 |
+
//FB::info($this, 'Deleting link from DB');
|
|
|
969 |
|
970 |
+
if ( $remove_instances ) {
|
971 |
+
//Remove instances, if any
|
972 |
+
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}blc_instances WHERE link_id=%d", $this->link_id ) );
|
973 |
+
}
|
974 |
|
975 |
+
//Remove the link itself
|
976 |
+
$rez = $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}blc_links WHERE link_id=%d", $this->link_id ) );
|
977 |
+
$this->link_id = 0;
|
978 |
|
979 |
+
return $rez;
|
980 |
+
} else {
|
981 |
+
return false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
982 |
}
|
983 |
+
|
984 |
}
|
985 |
|
986 |
+
/**
|
987 |
+
* Get a list of the link's instances
|
988 |
+
*
|
989 |
+
* @param bool $ignore_cache Don't use the internally cached instance list.
|
990 |
+
* @param string $purpose
|
991 |
+
* @return blcLinkInstance[] An array of instance objects or FALSE on failure.
|
992 |
+
*/
|
993 |
+
function get_instances( $ignore_cache = false, $purpose = '' ) {
|
994 |
+
if ( ! $this->valid() || empty( $this->link_id ) ) {
|
995 |
+
return false;
|
996 |
+
}
|
997 |
|
998 |
+
if ( $ignore_cache || is_null( $this->_instances ) ) {
|
999 |
+
$instances = blc_get_instances( array( $this->link_id ), $purpose );
|
1000 |
+
if ( ! empty( $instances ) ) {
|
1001 |
+
$this->_instances = $instances[ $this->link_id ];
|
1002 |
+
}
|
1003 |
+
}
|
|
|
|
|
1004 |
|
1005 |
+
return $this->_instances;
|
1006 |
+
}
|
1007 |
|
1008 |
+
/**
|
1009 |
+
* Determine the status text and status code corresponding to the current state of this link.
|
1010 |
+
*
|
1011 |
+
* @return array Associative array with two keys, 'text' and 'code'.
|
1012 |
+
*/
|
1013 |
+
function analyse_status() {
|
1014 |
+
$code = BLC_LINK_STATUS_UNKNOWN;
|
1015 |
+
$text = _x( 'Unknown', 'link status', 'broken-link-checker' );
|
1016 |
|
1017 |
+
//Status text
|
1018 |
+
if ( isset( $this->status_text ) && ! empty( $this->status_text ) && ! empty( $this->status_code ) ) {
|
1019 |
|
1020 |
+
//Lucky, the checker module has already set it for us.
|
1021 |
+
$text = $this->status_text;
|
1022 |
+
$code = $this->status_code;
|
1023 |
|
1024 |
+
} else {
|
1025 |
|
1026 |
+
if ( $this->broken || $this->warning ) {
|
1027 |
$code = BLC_LINK_STATUS_WARNING;
|
1028 |
+
$text = __( 'Unknown Error', 'broken-link-checker' );
|
1029 |
|
1030 |
+
if ( $this->timeout ) {
|
1031 |
|
1032 |
+
$text = __( 'Timeout', 'broken-link-checker' );
|
|
|
|
|
|
|
1033 |
$code = BLC_LINK_STATUS_WARNING;
|
1034 |
+
|
1035 |
+
} elseif ( $this->http_code ) {
|
1036 |
+
|
1037 |
+
//Only 404 (Not Found) and 410 (Gone) are treated as broken-for-sure.
|
1038 |
+
if ( in_array( $this->http_code, array( 404, 410 ) ) ) {
|
1039 |
+
$code = BLC_LINK_STATUS_ERROR;
|
1040 |
+
} else {
|
1041 |
+
$code = BLC_LINK_STATUS_WARNING;
|
1042 |
+
}
|
1043 |
+
|
1044 |
+
if ( array_key_exists( intval( $this->http_code ), $this->http_status_codes ) ) {
|
1045 |
+
$text = $this->http_status_codes[ intval( $this->http_code ) ];
|
1046 |
+
}
|
1047 |
}
|
1048 |
+
} else {
|
1049 |
|
1050 |
+
if ( ! $this->last_check ) {
|
1051 |
+
$text = __( 'Not checked', 'broken-link-checker' );
|
1052 |
+
$code = BLC_LINK_STATUS_UNKNOWN;
|
1053 |
+
} elseif ( $this->false_positive ) {
|
1054 |
+
$text = __( 'False positive', 'broken-link-checker' );
|
1055 |
+
$code = BLC_LINK_STATUS_UNKNOWN;
|
1056 |
+
} else {
|
1057 |
+
$text = _x( 'OK', 'link status', 'broken-link-checker' );
|
1058 |
+
$code = BLC_LINK_STATUS_OK;
|
1059 |
}
|
1060 |
}
|
1061 |
+
}
|
1062 |
|
1063 |
+
return compact( 'text', 'code' );
|
1064 |
+
}
|
1065 |
|
1066 |
+
/**
|
1067 |
+
* Get the link URL in ASCII-compatible encoding.
|
1068 |
+
*
|
1069 |
+
* @return string
|
1070 |
+
*/
|
1071 |
+
function get_ascii_url() {
|
1072 |
+
return blcUtility::idn_to_ascii( $this->url );
|
1073 |
+
}
|
|
|
|
|
1074 |
|
1075 |
+
/**
|
1076 |
+
* Check if this link points to a page on the same domain as the current site.
|
1077 |
+
*
|
1078 |
+
* Note: Only checks the domain name, not subdirectory. If there are two separate WP sites A and B installed
|
1079 |
+
* in two different subdirectories of the same domain, this method will treat a link from site A to B as internal.
|
1080 |
+
*
|
1081 |
+
* @return bool
|
1082 |
+
*/
|
1083 |
+
public function is_internal_to_domain() {
|
1084 |
+
$host = @parse_url( $this->url, PHP_URL_HOST );
|
1085 |
+
if ( empty( $host ) ) {
|
1086 |
+
return false;
|
1087 |
}
|
|
|
1088 |
|
1089 |
+
$site_host = @parse_url( get_site_url(), PHP_URL_HOST );
|
1090 |
+
if ( empty( $site_host ) ) {
|
1091 |
+
return false;
|
1092 |
+
}
|
1093 |
|
1094 |
+
//Some users are inconsistent with using/not using the www prefix, so get rid of it.
|
1095 |
+
$site_host = preg_replace( '@^www\.@', '', $site_host, 1 );
|
|
|
|
|
|
|
|
|
|
|
|
|
1096 |
|
1097 |
+
//Check if $host ends with $site_host. This means blah.example.com will match example.com.
|
1098 |
+
return ( substr( $host, -strlen( $site_host ) ) === $site_host );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1099 |
}
|
1100 |
|
1101 |
+
/**
|
1102 |
+
* Remove the query string from an URL.
|
1103 |
+
*
|
1104 |
+
* @param string $url
|
1105 |
+
* @return string
|
1106 |
+
*/
|
1107 |
+
public static function remove_query_string( $url ) {
|
1108 |
+
return preg_replace( '@\?[^#]*?(#|$)@', '$1', $url );
|
1109 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
1110 |
}
|
1111 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1112 |
} //class_exists
|
1113 |
|
1114 |
/**
|
1117 |
* @param int|array $link_id (optional) Only check these links
|
1118 |
* @return bool
|
1119 |
*/
|
1120 |
+
function blc_cleanup_links( $link_id = null ) {
|
1121 |
global $wpdb; /* @var wpdb $wpdb */
|
1122 |
global $blclog;
|
1123 |
|
1124 |
+
$start = microtime( true );
|
1125 |
+
$q = "DELETE FROM {$wpdb->prefix}blc_links
|
1126 |
+
USING {$wpdb->prefix}blc_links LEFT JOIN {$wpdb->prefix}blc_instances
|
1127 |
ON {$wpdb->prefix}blc_instances.link_id = {$wpdb->prefix}blc_links.link_id
|
1128 |
WHERE
|
1129 |
{$wpdb->prefix}blc_instances.link_id IS NULL";
|
1130 |
|
1131 |
+
if ( null !== $link_id ) {
|
1132 |
+
if ( ! is_array( $link_id ) ) {
|
1133 |
+
$link_id = array( intval( $link_id ) );
|
1134 |
}
|
1135 |
+
$q .= " AND {$wpdb->prefix}blc_links.link_id IN (" . implode( ', ', $link_id ) . ')';
|
1136 |
}
|
1137 |
|
1138 |
+
$rez = $wpdb->query( $q ); //phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
1139 |
+
$elapsed = microtime( true ) - $start;
|
1140 |
+
$blclog->log( sprintf( '... %d links deleted in %.3f seconds', $wpdb->rows_affected, $elapsed ) );
|
1141 |
|
1142 |
+
return false !== $rez;
|
1143 |
}
|
1144 |
|
includes/logger.php
CHANGED
@@ -1,188 +1,188 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
if ( !class_exists('blcLogger') ):
|
4 |
-
|
5 |
-
define('BLC_LEVEL_DEBUG', 0);
|
6 |
-
define('BLC_LEVEL_INFO', 1);
|
7 |
-
define('BLC_LEVEL_WARNING', 2);
|
8 |
-
define('BLC_LEVEL_ERROR', 3);
|
9 |
-
|
10 |
-
/**
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
class blcLogger {
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
}
|
63 |
-
|
64 |
-
/**
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
class blcCachedOptionLogger extends blcLogger {
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
}
|
120 |
-
|
121 |
-
/**
|
122 |
-
|
123 |
-
|
124 |
-
class blcDummyLogger extends blcLogger { }
|
125 |
-
|
126 |
-
/**
|
127 |
-
|
128 |
-
|
129 |
-
class blcFileLogger extends blcLogger {
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
}
|
186 |
-
|
187 |
-
endif;
|
188 |
-
|
1 |
+
<?php
|
2 |
+
|
3 |
+
if ( ! class_exists( 'blcLogger' ) ) :
|
4 |
+
|
5 |
+
define( 'BLC_LEVEL_DEBUG', 0 );
|
6 |
+
define( 'BLC_LEVEL_INFO', 1 );
|
7 |
+
define( 'BLC_LEVEL_WARNING', 2 );
|
8 |
+
define( 'BLC_LEVEL_ERROR', 3 );
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Base class for loggers. Doesn't actually log anything anywhere.
|
12 |
+
*
|
13 |
+
* @package Broken Link Checker
|
14 |
+
* @author Janis Elsts
|
15 |
+
*/
|
16 |
+
class blcLogger {
|
17 |
+
protected $log_level = BLC_LEVEL_DEBUG;
|
18 |
+
|
19 |
+
function __construct( $param = '' ) {
|
20 |
+
|
21 |
+
}
|
22 |
+
|
23 |
+
function blcLogger( $param = '' ) {
|
24 |
+
$this->__construct( $param );
|
25 |
+
}
|
26 |
+
|
27 |
+
function log( $message, $object = null, $level = BLC_LEVEL_INFO ) {
|
28 |
+
|
29 |
+
}
|
30 |
+
|
31 |
+
function debug( $message, $object = null ) {
|
32 |
+
$this->log( $message, $object, BLC_LEVEL_DEBUG );
|
33 |
+
}
|
34 |
+
|
35 |
+
function info( $message, $object = null ) {
|
36 |
+
$this->log( $message, $object, BLC_LEVEL_INFO );
|
37 |
+
}
|
38 |
+
|
39 |
+
function warn( $message, $object = null ) {
|
40 |
+
$this->log( $message, $object, BLC_LEVEL_WARNING );
|
41 |
+
}
|
42 |
+
|
43 |
+
function error( $message, $object = null ) {
|
44 |
+
$this->log( $message, $object, BLC_LEVEL_ERROR );
|
45 |
+
}
|
46 |
+
|
47 |
+
function get_messages( $min_level = BLC_LEVEL_DEBUG ) {
|
48 |
+
return array();
|
49 |
+
}
|
50 |
+
|
51 |
+
function get_log( $min_level = BLC_LEVEL_DEBUG ) {
|
52 |
+
return array();
|
53 |
+
}
|
54 |
+
|
55 |
+
function clear() {
|
56 |
+
|
57 |
+
}
|
58 |
+
|
59 |
+
public function set_log_level( $level ) {
|
60 |
+
$this->log_level = $level;
|
61 |
+
}
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* A basic logger that uses WP options for permanent storage.
|
66 |
+
*
|
67 |
+
* Log entries are initially stored in memory and need to explicitly
|
68 |
+
* flushed to the database by calling blcCachedOptionLogger::save().
|
69 |
+
*
|
70 |
+
* @package Broken Link Checker
|
71 |
+
* @author Janis Elsts
|
72 |
+
*/
|
73 |
+
class blcCachedOptionLogger extends blcLogger {
|
74 |
+
var $option_name = '';
|
75 |
+
var $log;
|
76 |
+
var $filter_level = BLC_LEVEL_DEBUG;
|
77 |
+
|
78 |
+
function __construct( $option_name = '' ) {
|
79 |
+
$this->option_name = $option_name;
|
80 |
+
$oldLog = get_option( $this->option_name );
|
81 |
+
if ( is_array( $oldLog ) && ! empty( $oldLog ) ) {
|
82 |
+
$this->log = $oldLog;
|
83 |
+
} else {
|
84 |
+
$this->log = array();
|
85 |
+
}
|
86 |
+
}
|
87 |
+
|
88 |
+
function log( $message, $object = null, $level = BLC_LEVEL_DEBUG ) {
|
89 |
+
$new_entry = array( $level, $message, $object );
|
90 |
+
array_push( $this->log, $new_entry );
|
91 |
+
}
|
92 |
+
|
93 |
+
function get_log( $min_level = BLC_LEVEL_DEBUG ) {
|
94 |
+
$this->filter_level = $min_level;
|
95 |
+
return array_filter( $this->log, array( $this, '_filter_log' ) );
|
96 |
+
}
|
97 |
+
|
98 |
+
function _filter_log( $entry ) {
|
99 |
+
return ( $entry[0] >= $this->filter_level );
|
100 |
+
}
|
101 |
+
|
102 |
+
function get_messages( $min_level = BLC_LEVEL_DEBUG ) {
|
103 |
+
$messages = $this->get_log( $min_level );
|
104 |
+
return array_map( array( $this, '_get_log_message' ), $messages );
|
105 |
+
}
|
106 |
+
|
107 |
+
function _get_log_message( $entry ) {
|
108 |
+
return $entry[1];
|
109 |
+
}
|
110 |
+
|
111 |
+
function clear() {
|
112 |
+
$this->log = array();
|
113 |
+
delete_option( $this->option_name );
|
114 |
+
}
|
115 |
+
|
116 |
+
function save() {
|
117 |
+
update_option( $this->option_name, $this->log );
|
118 |
+
}
|
119 |
+
}
|
120 |
+
|
121 |
+
/**
|
122 |
+
* A dummy logger that doesn't log anything.
|
123 |
+
*/
|
124 |
+
class blcDummyLogger extends blcLogger { }
|
125 |
+
|
126 |
+
/**
|
127 |
+
* A basic logger that logs messages to a file.
|
128 |
+
*/
|
129 |
+
class blcFileLogger extends blcLogger {
|
130 |
+
protected $fileName; //phpcs:ignore WordPress.NamingConventions.ValidVariableName.PropertyNotSnakeCase
|
131 |
+
|
132 |
+
public function __construct( $fileName = '' ) {
|
133 |
+
$this->fileName = $fileName;
|
134 |
+
}
|
135 |
+
|
136 |
+
function log( $message, $object = null, $level = BLC_LEVEL_INFO ) {
|
137 |
+
if ( $level < $this->log_level ) {
|
138 |
+
return;
|
139 |
+
}
|
140 |
+
|
141 |
+
$line = sprintf(
|
142 |
+
'[%1$s] %2$s %3$s',
|
143 |
+
date( 'Y-m-d H:i:s P' ), //phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
|
144 |
+
$this->get_level_string( $level ),
|
145 |
+
$message
|
146 |
+
);
|
147 |
+
|
148 |
+
if ( isset( $object ) ) {
|
149 |
+
$line .= ' ' . var_export( $object, true );
|
150 |
+
}
|
151 |
+
|
152 |
+
$line .= "\n";
|
153 |
+
|
154 |
+
error_log( $line, 3, $this->fileName );
|
155 |
+
}
|
156 |
+
|
157 |
+
function get_messages( $min_level = BLC_LEVEL_DEBUG ) {
|
158 |
+
return array( __CLASS__ . ':get_messages() is not implemented' );
|
159 |
+
}
|
160 |
+
|
161 |
+
function get_log( $min_level = BLC_LEVEL_DEBUG ) {
|
162 |
+
return array( __CLASS__ . ':get_log() is not implemented' );
|
163 |
+
}
|
164 |
+
|
165 |
+
public function clear() {
|
166 |
+
if ( is_file( $this->fileName ) && is_writable( $this->fileName ) ) {
|
167 |
+
$handle = fopen( $this->fileName, 'w' );
|
168 |
+
fclose( $handle );
|
169 |
+
}
|
170 |
+
}
|
171 |
+
|
172 |
+
protected function get_level_string( $level ) {
|
173 |
+
switch ( $level ) {
|
174 |
+
case BLC_LEVEL_DEBUG:
|
175 |
+
return 'DEBUG:';
|
176 |
+
case BLC_LEVEL_ERROR:
|
177 |
+
return 'ERROR:';
|
178 |
+
case BLC_LEVEL_WARNING:
|
179 |
+
return 'WARN:';
|
180 |
+
case BLC_LEVEL_INFO:
|
181 |
+
return 'INFO:';
|
182 |
+
}
|
183 |
+
return 'LOG:';
|
184 |
+
}
|
185 |
+
}
|
186 |
+
|
187 |
+
endif;
|
188 |
+
|
includes/module-base.php
CHANGED
@@ -1,80 +1,80 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* @author Janis Elsts
|
5 |
-
* @copyright 2010
|
6 |
-
*/
|
7 |
-
|
8 |
-
/**
|
9 |
-
* Base class for BLC modules.
|
10 |
-
*
|
11 |
-
* @package Broken Link Checker
|
12 |
-
* @author Janis Elsts
|
13 |
-
* @access public
|
14 |
-
*/
|
15 |
-
class blcModule {
|
16 |
-
|
17 |
-
var $module_id; //The ID of this module. Usually a lowercase string.
|
18 |
-
var $cached_header; //An associative array containing the header data of the module file.
|
19 |
-
/** @var blcConfigurationManager $plugin__conf */
|
20 |
-
var $plugin_conf; //A reference to the plugin's global configuration object.
|
21 |
-
var $module_manager; //A reference to the module manager.
|
22 |
-
|
23 |
-
/**
|
24 |
-
* Class constructor
|
25 |
-
*
|
26 |
-
* @param string $module_id
|
27 |
-
* @param array $cached_header
|
28 |
-
* @param blcConfigurationManager $plugin_conf
|
29 |
-
* @param blcModuleManager $module_manager
|
30 |
-
* @return void
|
31 |
-
*/
|
32 |
-
function __construct($module_id, $cached_header, &$plugin_conf, &$module_manager){
|
33 |
-
$this->module_id
|
34 |
-
$this->cached_header
|
35 |
-
$this->plugin_conf
|
36 |
-
$this->module_manager = &$module_manager;
|
37 |
-
|
38 |
-
$this->init();
|
39 |
-
}
|
40 |
-
|
41 |
-
/**
|
42 |
-
* Module initializer. Called when the module is first instantiated.
|
43 |
-
* The default implementation does nothing. Override it in a subclass to
|
44 |
-
* specify some sort of start-up behaviour.
|
45 |
-
*
|
46 |
-
* @return void
|
47 |
-
*/
|
48 |
-
function init(){
|
49 |
-
//Should be overridden in a sub-class.
|
50 |
-
}
|
51 |
-
|
52 |
-
/**
|
53 |
-
* Called when the module is activated.
|
54 |
-
* Should be overridden in a sub-class.
|
55 |
-
*
|
56 |
-
* @return void
|
57 |
-
*/
|
58 |
-
function activated(){
|
59 |
-
//Should be overridden in a sub-class.
|
60 |
-
}
|
61 |
-
|
62 |
-
/**
|
63 |
-
* Called when the module is deactivated.
|
64 |
-
* Should be overridden in a sub-class.
|
65 |
-
*
|
66 |
-
* @return void
|
67 |
-
*/
|
68 |
-
function deactivated(){
|
69 |
-
//Should be overridden in a sub-class.
|
70 |
-
}
|
71 |
-
|
72 |
-
/**
|
73 |
-
* Called when BLC itself is activated.
|
74 |
-
* Usually this method just calls activated(), but subclasses could override it for special handling.
|
75 |
-
*/
|
76 |
-
function plugin_activated() {
|
77 |
-
$this->activated();
|
78 |
-
}
|
79 |
-
}
|
80 |
-
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @author Janis Elsts
|
5 |
+
* @copyright 2010
|
6 |
+
*/
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Base class for BLC modules.
|
10 |
+
*
|
11 |
+
* @package Broken Link Checker
|
12 |
+
* @author Janis Elsts
|
13 |
+
* @access public
|
14 |
+
*/
|
15 |
+
class blcModule {
|
16 |
+
|
17 |
+
var $module_id; //The ID of this module. Usually a lowercase string.
|
18 |
+
var $cached_header; //An associative array containing the header data of the module file.
|
19 |
+
/** @var blcConfigurationManager $plugin__conf */
|
20 |
+
var $plugin_conf; //A reference to the plugin's global configuration object.
|
21 |
+
var $module_manager; //A reference to the module manager.
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Class constructor
|
25 |
+
*
|
26 |
+
* @param string $module_id
|
27 |
+
* @param array $cached_header
|
28 |
+
* @param blcConfigurationManager $plugin_conf
|
29 |
+
* @param blcModuleManager $module_manager
|
30 |
+
* @return void
|
31 |
+
*/
|
32 |
+
function __construct( $module_id, $cached_header, &$plugin_conf, &$module_manager ) {
|
33 |
+
$this->module_id = $module_id;
|
34 |
+
$this->cached_header = $cached_header;
|
35 |
+
$this->plugin_conf = &$plugin_conf;
|
36 |
+
$this->module_manager = &$module_manager;
|
37 |
+
|
38 |
+
$this->init();
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Module initializer. Called when the module is first instantiated.
|
43 |
+
* The default implementation does nothing. Override it in a subclass to
|
44 |
+
* specify some sort of start-up behaviour.
|
45 |
+
*
|
46 |
+
* @return void
|
47 |
+
*/
|
48 |
+
function init() {
|
49 |
+
//Should be overridden in a sub-class.
|
50 |
+
}
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Called when the module is activated.
|
54 |
+
* Should be overridden in a sub-class.
|
55 |
+
*
|
56 |
+
* @return void
|
57 |
+
*/
|
58 |
+
function activated() {
|
59 |
+
//Should be overridden in a sub-class.
|
60 |
+
}
|
61 |
+
|
62 |
+
/**
|
63 |
+
* Called when the module is deactivated.
|
64 |
+
* Should be overridden in a sub-class.
|
65 |
+
*
|
66 |
+
* @return void
|
67 |
+
*/
|
68 |
+
function deactivated() {
|
69 |
+
//Should be overridden in a sub-class.
|
70 |
+
}
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Called when BLC itself is activated.
|
74 |
+
* Usually this method just calls activated(), but subclasses could override it for special handling.
|
75 |
+
*/
|
76 |
+
function plugin_activated() {
|
77 |
+
$this->activated();
|
78 |
+
}
|
79 |
+
}
|
80 |
+
|
includes/module-manager.php
CHANGED
@@ -1,860 +1,861 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
class blcModuleManager {
|
4 |
-
|
5 |
-
/* @var blcConfigurationManager */
|
6 |
-
var $plugin_conf;
|
7 |
-
var $module_dir = '';
|
8 |
-
|
9 |
-
var $_module_cache;
|
10 |
-
var $_category_cache;
|
11 |
-
var $_category_cache_active;
|
12 |
-
var $_virtual_modules = array();
|
13 |
-
|
14 |
-
var $loaded;
|
15 |
-
var $instances;
|
16 |
-
var $default_active_modules;
|
17 |
-
|
18 |
-
|
19 |
-
/**
|
20 |
-
* Class "constructor".
|
21 |
-
*
|
22 |
-
* @param array $default_active_modules An array of module ids specifying which modules are active by default.
|
23 |
-
* @return void
|
24 |
-
*/
|
25 |
-
function init($default_active_modules = null){
|
26 |
-
$this->module_dir = realpath(dirname(__FILE__) . '/../modules');
|
27 |
-
|
28 |
-
$this->plugin_conf
|
29 |
-
$this->default_active_modules = $default_active_modules;
|
30 |
-
|
31 |
-
$this->loaded
|
32 |
-
$this->instances = array();
|
33 |
-
|
34 |
-
add_filter('extra_plugin_headers', array(&$this, 'inject_module_headers'));
|
35 |
-
}
|
36 |
-
|
37 |
-
/**
|
38 |
-
* Get an instance of the module manager.
|
39 |
-
*
|
40 |
-
* @param array|null $default_active_modules
|
41 |
-
* @return blcModuleManager
|
42 |
-
*/
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
$instance
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
*
|
55 |
-
*
|
56 |
-
*
|
57 |
-
*
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
$this->
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
*
|
93 |
-
*
|
94 |
-
*
|
95 |
-
*
|
96 |
-
*
|
97 |
-
*
|
98 |
-
*
|
99 |
-
*
|
100 |
-
*
|
101 |
-
*
|
102 |
-
*
|
103 |
-
*
|
104 |
-
*
|
105 |
-
* @param
|
106 |
-
* @param bool $
|
107 |
-
* @
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
*
|
157 |
-
*
|
158 |
-
*
|
159 |
-
*
|
160 |
-
* @
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
*
|
181 |
-
*
|
182 |
-
*
|
183 |
-
* @
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
$modules =
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
*
|
203 |
-
*
|
204 |
-
*
|
205 |
-
*
|
206 |
-
* @
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
*
|
243 |
-
*
|
244 |
-
*
|
245 |
-
* the
|
246 |
-
*
|
247 |
-
*
|
248 |
-
* @param
|
249 |
-
* @param
|
250 |
-
* @
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
*
|
284 |
-
*
|
285 |
-
*
|
286 |
-
* @param
|
287 |
-
* @
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
*
|
315 |
-
*
|
316 |
-
*
|
317 |
-
*
|
318 |
-
*
|
319 |
-
*
|
320 |
-
*
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
$
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
$this->plugin_conf->
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
*
|
347 |
-
*
|
348 |
-
* @
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
*
|
357 |
-
*
|
358 |
-
*
|
359 |
-
* @
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
$this->plugin_conf->
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
*
|
394 |
-
*
|
395 |
-
*
|
396 |
-
* @
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
*
|
434 |
-
*
|
435 |
-
* @
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
*
|
448 |
-
* be
|
449 |
-
*
|
450 |
-
*
|
451 |
-
* @
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
$
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
|
465 |
-
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
477 |
-
|
478 |
-
*
|
479 |
-
*
|
480 |
-
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
|
485 |
-
|
486 |
-
$
|
487 |
-
$
|
488 |
-
$
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
$
|
493 |
-
$
|
494 |
-
$
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
$module
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
*
|
511 |
-
*
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
$this->
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
-
*
|
528 |
-
*
|
529 |
-
* @
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
|
547 |
-
*
|
548 |
-
*
|
549 |
-
*
|
550 |
-
*
|
551 |
-
* @param
|
552 |
-
* @
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
$
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
|
589 |
-
//be
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
|
609 |
-
*
|
610 |
-
*
|
611 |
-
* @param
|
612 |
-
* @
|
613 |
-
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
|
620 |
-
|
621 |
-
|
622 |
-
|
623 |
-
|
624 |
-
|
625 |
-
|
626 |
-
|
627 |
-
|
628 |
-
|
629 |
-
$
|
630 |
-
|
631 |
-
$
|
632 |
-
$
|
633 |
-
$this
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
|
638 |
-
|
639 |
-
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
|
646 |
-
|
647 |
-
*
|
648 |
-
*
|
649 |
-
*
|
650 |
-
*
|
651 |
-
*
|
652 |
-
* @param string $
|
653 |
-
* @
|
654 |
-
|
655 |
-
|
656 |
-
|
657 |
-
$module_data
|
658 |
-
$
|
659 |
-
|
660 |
-
|
661 |
-
|
662 |
-
|
663 |
-
*
|
664 |
-
*
|
665 |
-
* @
|
666 |
-
|
667 |
-
|
668 |
-
|
669 |
-
|
670 |
-
|
671 |
-
|
672 |
-
|
673 |
-
|
674 |
-
*
|
675 |
-
*
|
676 |
-
* @param array $
|
677 |
-
* @
|
678 |
-
|
679 |
-
|
680 |
-
|
681 |
-
|
682 |
-
|
683 |
-
|
684 |
-
|
685 |
-
|
686 |
-
|
687 |
-
|
688 |
-
|
689 |
-
*
|
690 |
-
*
|
691 |
-
*
|
692 |
-
*
|
693 |
-
*
|
694 |
-
|
695 |
-
|
696 |
-
|
697 |
-
|
698 |
-
|
699 |
-
|
700 |
-
|
701 |
-
|
702 |
-
|
703 |
-
|
704 |
-
|
705 |
-
|
706 |
-
$
|
707 |
-
|
708 |
-
|
709 |
-
|
710 |
-
|
711 |
-
|
712 |
-
|
713 |
-
|
714 |
-
|
715 |
-
*
|
716 |
-
*
|
717 |
-
*
|
718 |
-
*
|
719 |
-
*
|
720 |
-
* @
|
721 |
-
|
722 |
-
|
723 |
-
|
724 |
-
|
725 |
-
|
726 |
-
|
727 |
-
|
728 |
-
|
729 |
-
|
730 |
-
|
731 |
-
|
732 |
-
|
733 |
-
|
734 |
-
|
735 |
-
|
736 |
-
|
737 |
-
|
738 |
-
|
739 |
-
//
|
740 |
-
//
|
741 |
-
|
742 |
-
|
743 |
-
|
744 |
-
|
745 |
-
|
746 |
-
|
747 |
-
|
748 |
-
|
749 |
-
|
750 |
-
|
751 |
-
|
752 |
-
|
753 |
-
|
754 |
-
|
755 |
-
|
756 |
-
|
757 |
-
*
|
758 |
-
*
|
759 |
-
*
|
760 |
-
* @
|
761 |
-
|
762 |
-
|
763 |
-
|
764 |
-
|
765 |
-
'
|
766 |
-
'
|
767 |
-
'
|
768 |
-
'
|
769 |
-
'
|
770 |
-
'
|
771 |
-
'
|
772 |
-
'
|
773 |
-
'
|
774 |
-
|
775 |
-
|
776 |
-
|
777 |
-
|
778 |
-
|
779 |
-
|
780 |
-
|
781 |
-
*
|
782 |
-
*
|
783 |
-
* @param
|
784 |
-
* @param string $
|
785 |
-
* @
|
786 |
-
|
787 |
-
|
788 |
-
|
789 |
-
|
790 |
-
|
791 |
-
'
|
792 |
-
'
|
793 |
-
'
|
794 |
-
'
|
795 |
-
'
|
796 |
-
'
|
797 |
-
'
|
798 |
-
|
799 |
-
|
800 |
-
|
801 |
-
$module_header['
|
802 |
-
|
803 |
-
|
804 |
-
|
805 |
-
|
806 |
-
|
807 |
-
|
808 |
-
|
809 |
-
|
810 |
-
|
811 |
-
|
812 |
-
$module_header['
|
813 |
-
$module_header['
|
814 |
-
$module_header['
|
815 |
-
$module_header['
|
816 |
-
|
817 |
-
|
818 |
-
|
819 |
-
|
820 |
-
|
821 |
-
|
822 |
-
*
|
823 |
-
*
|
824 |
-
*
|
825 |
-
* @
|
826 |
-
|
827 |
-
|
828 |
-
|
829 |
-
|
830 |
-
|
831 |
-
|
832 |
-
|
833 |
-
|
834 |
-
*
|
835 |
-
*
|
836 |
-
*
|
837 |
-
*
|
838 |
-
*
|
839 |
-
*
|
840 |
-
|
841 |
-
|
842 |
-
|
843 |
-
$
|
844 |
-
|
845 |
-
|
846 |
-
|
847 |
-
|
848 |
-
|
849 |
-
|
850 |
-
|
851 |
-
|
852 |
-
|
853 |
-
|
854 |
-
|
855 |
-
|
856 |
-
|
857 |
-
|
858 |
-
|
859 |
-
|
860 |
-
}
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class blcModuleManager {
|
4 |
+
|
5 |
+
/* @var blcConfigurationManager */
|
6 |
+
var $plugin_conf;
|
7 |
+
var $module_dir = '';
|
8 |
+
|
9 |
+
var $_module_cache;
|
10 |
+
var $_category_cache;
|
11 |
+
var $_category_cache_active;
|
12 |
+
var $_virtual_modules = array();
|
13 |
+
|
14 |
+
var $loaded;
|
15 |
+
var $instances;
|
16 |
+
var $default_active_modules;
|
17 |
+
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Class "constructor".
|
21 |
+
*
|
22 |
+
* @param array $default_active_modules An array of module ids specifying which modules are active by default.
|
23 |
+
* @return void
|
24 |
+
*/
|
25 |
+
function init( $default_active_modules = null ) {
|
26 |
+
$this->module_dir = realpath( dirname( __FILE__ ) . '/../modules' );
|
27 |
+
|
28 |
+
$this->plugin_conf = blc_get_configuration();
|
29 |
+
$this->default_active_modules = $default_active_modules;
|
30 |
+
|
31 |
+
$this->loaded = array();
|
32 |
+
$this->instances = array();
|
33 |
+
|
34 |
+
add_filter( 'extra_plugin_headers', array( &$this, 'inject_module_headers' ) );
|
35 |
+
}
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Get an instance of the module manager.
|
39 |
+
*
|
40 |
+
* @param array|null $default_active_modules
|
41 |
+
* @return blcModuleManager
|
42 |
+
*/
|
43 |
+
//phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
|
44 |
+
static function getInstance( $default_active_modules = null ) {
|
45 |
+
static $instance = null;
|
46 |
+
if ( is_null( $instance ) ) {
|
47 |
+
$instance = new blcModuleManager();
|
48 |
+
$instance->init( $default_active_modules );
|
49 |
+
}
|
50 |
+
return $instance;
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Retrieve a list of all installed BLC modules.
|
55 |
+
*
|
56 |
+
* This is essentially a slightly modified copy of get_plugins().
|
57 |
+
*
|
58 |
+
* @return array An associative array of module headers indexed by module ID.
|
59 |
+
*/
|
60 |
+
function get_modules() {
|
61 |
+
if ( ! isset( $this->_module_cache ) ) {
|
62 |
+
//Refresh the module cache.
|
63 |
+
|
64 |
+
$relative_path = '/' . plugin_basename( $this->module_dir );
|
65 |
+
if ( ! function_exists( 'get_plugins' ) ) {
|
66 |
+
//BUG: Potentional security flaw/bug. plugin.php is not meant to be loaded outside admin panel.
|
67 |
+
require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
|
68 |
+
}
|
69 |
+
$modules = get_plugins( $relative_path );
|
70 |
+
|
71 |
+
$this->_module_cache = array();
|
72 |
+
|
73 |
+
foreach ( $modules as $module_filename => $module_header ) {
|
74 |
+
//Figure out the module ID. If not specified, it is equal to module's filename (sans the .php)
|
75 |
+
if ( ! empty( $module_header['ModuleID'] ) ) {
|
76 |
+
$module_id = strtolower( trim( $module_header['ModuleID'] ) );
|
77 |
+
} else {
|
78 |
+
$module_id = strtolower( basename( $module_filename, '.php' ) );
|
79 |
+
}
|
80 |
+
|
81 |
+
$module_header = $this->normalize_module_header( $module_header, $module_id, $module_filename );
|
82 |
+
$this->_module_cache[ $module_id ] = $module_header;
|
83 |
+
}
|
84 |
+
|
85 |
+
$this->_category_cache = null;
|
86 |
+
}
|
87 |
+
|
88 |
+
return array_merge( $this->_module_cache, $this->_virtual_modules );
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Retrieve modules that match a specific category, or all modules sorted by categories.
|
93 |
+
*
|
94 |
+
* If a category ID is specified, this method returns the modules that have the "ModuleCategory:"
|
95 |
+
* file header set to that value, or an empty array if no modules match that category. The
|
96 |
+
* return array is indexed by module id :
|
97 |
+
* [module_id1 => module1_data, module_id1 => module2_data, ...]
|
98 |
+
*
|
99 |
+
* If category is omitted, this method returns a list of all categories plus the modules
|
100 |
+
* they contain. Only categories that have at least one module will be included. The return
|
101 |
+
* value is an array of arrays, indexed by category ID :
|
102 |
+
* [category1 => [module1_id => module1_data, module2_id => module2_data, ...], ...]
|
103 |
+
*
|
104 |
+
*
|
105 |
+
* @param string $category Category id, e.g. "parser" or "container". Optional.
|
106 |
+
* @param bool $markup Apply markup to module headers. Not implemented.
|
107 |
+
* @param bool $translate Translate module headers. Defaults to false.
|
108 |
+
* @return array An array of categories or module data.
|
109 |
+
*/
|
110 |
+
function get_modules_by_category( $category = '', $markup = false, $translate = false ) {
|
111 |
+
if ( ! isset( $this->_category_cache ) ) {
|
112 |
+
$this->_category_cache = $this->sort_into_categories( $this->get_modules() );
|
113 |
+
}
|
114 |
+
|
115 |
+
if ( empty( $category ) ) {
|
116 |
+
if ( $markup || $translate ) {
|
117 |
+
|
118 |
+
//Translate/apply markup to module headers
|
119 |
+
$processed = array();
|
120 |
+
foreach ( $this->_category_cache as $category_id => $modules ) {
|
121 |
+
$processed[ $category_id ] = array();
|
122 |
+
foreach ( $modules as $module_id => $module_data ) {
|
123 |
+
if ( $translate ) {
|
124 |
+
$module_data['Name'] = _x( $module_data['Name'], 'module name', 'broken-link-checker' ); //phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText
|
125 |
+
}
|
126 |
+
$processed[ $category_id ][ $module_id ] = $module_data;
|
127 |
+
}
|
128 |
+
}
|
129 |
+
|
130 |
+
return $processed;
|
131 |
+
} else {
|
132 |
+
return $this->_category_cache;
|
133 |
+
}
|
134 |
+
} else {
|
135 |
+
if ( isset( $this->_category_cache[ $category ] ) ) {
|
136 |
+
if ( $markup || $translate ) {
|
137 |
+
//Translate/apply markup to module headers
|
138 |
+
$processed = array();
|
139 |
+
foreach ( $this->_category_cache[ $category ] as $module_id => $module_data ) {
|
140 |
+
if ( $translate ) {
|
141 |
+
$module_data['Name'] = _x( $module_data['Name'], 'module name', 'broken-link-checker' ); //phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText
|
142 |
+
}
|
143 |
+
$processed[ $module_id ] = $module_data;
|
144 |
+
}
|
145 |
+
return $processed;
|
146 |
+
} else {
|
147 |
+
return $this->_category_cache[ $category ];
|
148 |
+
}
|
149 |
+
} else {
|
150 |
+
return array();
|
151 |
+
}
|
152 |
+
}
|
153 |
+
}
|
154 |
+
|
155 |
+
/**
|
156 |
+
* Retrieve active modules that match a specific category, or all active modules sorted by categories.
|
157 |
+
*
|
158 |
+
* @see blcModuleManager::get_modules_by_category()
|
159 |
+
*
|
160 |
+
* @param string $category Category id. Optional.
|
161 |
+
* @return array An associative array of categories or module data.
|
162 |
+
*/
|
163 |
+
function get_active_by_category( $category = '' ) {
|
164 |
+
if ( ! isset( $this->_category_cache_active ) ) {
|
165 |
+
$this->_category_cache_active = $this->sort_into_categories( $this->get_active_modules() );
|
166 |
+
}
|
167 |
+
|
168 |
+
if ( empty( $category ) ) {
|
169 |
+
return $this->_category_cache_active;
|
170 |
+
} else {
|
171 |
+
if ( isset( $this->_category_cache_active[ $category ] ) ) {
|
172 |
+
return $this->_category_cache_active[ $category ];
|
173 |
+
} else {
|
174 |
+
return array();
|
175 |
+
}
|
176 |
+
}
|
177 |
+
}
|
178 |
+
|
179 |
+
/**
|
180 |
+
* Get the module ids of all active modules that belong to a specific category,
|
181 |
+
* quoted and ready for use in SQL.
|
182 |
+
*
|
183 |
+
* @param string $category Category ID. If not specified, a list of all active modules will be returned.
|
184 |
+
* @return string A comma separated list of single-quoted module ids, e.g. 'module-foo','module-bar','modbaz'
|
185 |
+
*/
|
186 |
+
function get_escaped_ids( $category = '' ) {
|
187 |
+
global $wpdb;
|
188 |
+
|
189 |
+
if ( empty( $category ) ) {
|
190 |
+
$modules = $this->get_active_modules();
|
191 |
+
} else {
|
192 |
+
$modules = $this->get_active_by_category( $category );
|
193 |
+
}
|
194 |
+
|
195 |
+
$modules = array_map( 'esc_sql', array_keys( $modules ) );
|
196 |
+
$modules = "'" . implode( "', '", $modules ) . "'";
|
197 |
+
|
198 |
+
return $modules;
|
199 |
+
}
|
200 |
+
|
201 |
+
/**
|
202 |
+
* Sort a list of modules into categories. Inside each category, modules are sorted by priority (descending).
|
203 |
+
*
|
204 |
+
* @access private
|
205 |
+
*
|
206 |
+
* @param array $modules
|
207 |
+
* @return array
|
208 |
+
*/
|
209 |
+
function sort_into_categories( $modules ) {
|
210 |
+
$categories = array();
|
211 |
+
|
212 |
+
foreach ( $modules as $module_id => $module_data ) {
|
213 |
+
$cat = $module_data['ModuleCategory'];
|
214 |
+
if ( isset( $categories[ $cat ] ) ) {
|
215 |
+
$categories[ $cat ][ $module_id ] = $module_data;
|
216 |
+
} else {
|
217 |
+
$categories[ $cat ] = array( $module_id => $module_data );
|
218 |
+
}
|
219 |
+
}
|
220 |
+
|
221 |
+
foreach ( $categories as $cat => $cat_modules ) {
|
222 |
+
uasort( $categories[ $cat ], array( &$this, 'compare_priorities' ) );
|
223 |
+
}
|
224 |
+
|
225 |
+
return $categories;
|
226 |
+
}
|
227 |
+
|
228 |
+
/**
|
229 |
+
* Callback for sorting modules by priority.
|
230 |
+
*
|
231 |
+
* @access private
|
232 |
+
*
|
233 |
+
* @param array $a First module header.
|
234 |
+
* @param array $b Second module header.
|
235 |
+
* @return int
|
236 |
+
*/
|
237 |
+
function compare_priorities( $a, $b ) {
|
238 |
+
return $b['ModulePriority'] - $a['ModulePriority'];
|
239 |
+
}
|
240 |
+
|
241 |
+
/**
|
242 |
+
* Retrieve a reference to an active module.
|
243 |
+
*
|
244 |
+
* Each module is instantiated only once, so if the module was already loaded you'll get back
|
245 |
+
* a reference to the existing module object. If the module isn't loaded or instantiated yet,
|
246 |
+
* the function will do it automatically (but only for active modules).
|
247 |
+
*
|
248 |
+
* @param string $module_id Module ID.
|
249 |
+
* @param bool $autoload Optional. Whether to load the module file if it's not loaded yet. Defaults to TRUE.
|
250 |
+
* @param string $category Optional. Return the module only if it's in a specific category. Categories are ignored by default.
|
251 |
+
* @return blcModule A reference to a module object, or NULL on error.
|
252 |
+
*/
|
253 |
+
function get_module( $module_id, $autoload = true, $category = '' ) {
|
254 |
+
$no_result = null;
|
255 |
+
if ( ! is_string( $module_id ) ) {
|
256 |
+
//$backtrace = debug_backtrace();
|
257 |
+
//FB::error($backtrace, "get_module called with a non-string argument");
|
258 |
+
return $no_result;
|
259 |
+
}
|
260 |
+
|
261 |
+
if ( empty( $this->loaded[ $module_id ] ) ) {
|
262 |
+
if ( $autoload && $this->is_active( $module_id ) ) {
|
263 |
+
if ( ! $this->load_module( $module_id ) ) {
|
264 |
+
return $no_result;
|
265 |
+
}
|
266 |
+
} else {
|
267 |
+
return $no_result;
|
268 |
+
}
|
269 |
+
}
|
270 |
+
|
271 |
+
if ( ! empty( $category ) ) {
|
272 |
+
$data = $this->get_module_data( $module_id );
|
273 |
+
if ( $data['ModuleCategory'] != $category ) {
|
274 |
+
return $no_result;
|
275 |
+
}
|
276 |
+
}
|
277 |
+
|
278 |
+
$module = $this->init_module( $module_id );
|
279 |
+
return $module;
|
280 |
+
}
|
281 |
+
|
282 |
+
/**
|
283 |
+
* Retrieve the header data of a specific module.
|
284 |
+
* Uses cached module info if available.
|
285 |
+
*
|
286 |
+
* @param string $module_id
|
287 |
+
* @param bool $use_active_cache Check the active module cache before the general one. Defaults to true.
|
288 |
+
* @return array Associative array of module data, or FALSE if the specified module was not found.
|
289 |
+
*/
|
290 |
+
function get_module_data( $module_id, $use_active_cache = true ) {
|
291 |
+
//Check virtual modules
|
292 |
+
if ( isset( $this->_virtual_modules[ $module_id ] ) ) {
|
293 |
+
return $this->_virtual_modules[ $module_id ];
|
294 |
+
}
|
295 |
+
|
296 |
+
//Check active modules
|
297 |
+
if ( $use_active_cache && isset( $this->plugin_conf->options['active_modules'][ $module_id ] ) ) {
|
298 |
+
return $this->plugin_conf->options['active_modules'][ $module_id ];
|
299 |
+
}
|
300 |
+
|
301 |
+
//Otherwise, use the general module cache
|
302 |
+
if ( ! isset( $this->_module_cache ) ) {
|
303 |
+
$this->get_modules(); //Populates the cache
|
304 |
+
}
|
305 |
+
|
306 |
+
if ( isset( $this->_module_cache[ $module_id ] ) ) {
|
307 |
+
return $this->_module_cache[ $module_id ];
|
308 |
+
} else {
|
309 |
+
return false;
|
310 |
+
}
|
311 |
+
}
|
312 |
+
|
313 |
+
/**
|
314 |
+
* Retrieve a list of active modules.
|
315 |
+
*
|
316 |
+
* The list of active modules is stored in the 'active_modules' key of the
|
317 |
+
* plugin configuration object. If this key is not set, this function will
|
318 |
+
* create it and populate it using the list of default active modules passed
|
319 |
+
* to the module manager's constructor.
|
320 |
+
*
|
321 |
+
* @return array Associative array of module data indexed by module ID.
|
322 |
+
*/
|
323 |
+
function get_active_modules() {
|
324 |
+
if ( isset( $this->plugin_conf->options['active_modules'] ) ) {
|
325 |
+
return $this->plugin_conf->options['active_modules'];
|
326 |
+
}
|
327 |
+
|
328 |
+
$active = array();
|
329 |
+
$modules = $this->get_modules();
|
330 |
+
|
331 |
+
if ( is_array( $this->default_active_modules ) ) {
|
332 |
+
foreach ( $this->default_active_modules as $module_id ) {
|
333 |
+
if ( array_key_exists( $module_id, $modules ) ) {
|
334 |
+
$active[ $module_id ] = $modules[ $module_id ];
|
335 |
+
}
|
336 |
+
}
|
337 |
+
}
|
338 |
+
|
339 |
+
$this->plugin_conf->options['active_modules'] = $active;
|
340 |
+
$this->plugin_conf->save_options();
|
341 |
+
|
342 |
+
return $this->plugin_conf->options['active_modules'];
|
343 |
+
}
|
344 |
+
|
345 |
+
/**
|
346 |
+
* Determine if module is active.
|
347 |
+
*
|
348 |
+
* @param string $module_id
|
349 |
+
* @return bool
|
350 |
+
*/
|
351 |
+
function is_active( $module_id ) {
|
352 |
+
return array_key_exists( $module_id, $this->get_active_modules() );
|
353 |
+
}
|
354 |
+
|
355 |
+
/**
|
356 |
+
* Activate a module.
|
357 |
+
* Does nothing if the module is already active.
|
358 |
+
*
|
359 |
+
* @param string $module_id
|
360 |
+
* @return bool True if module was activated successfully, false otherwise.
|
361 |
+
*/
|
362 |
+
function activate( $module_id ) {
|
363 |
+
if ( $this->is_active( $module_id ) ) {
|
364 |
+
return true;
|
365 |
+
}
|
366 |
+
|
367 |
+
//Retrieve the module header
|
368 |
+
$module_data = $this->get_module_data( $module_id, false );
|
369 |
+
if ( ! $module_data ) {
|
370 |
+
return false;
|
371 |
+
}
|
372 |
+
|
373 |
+
//Attempt to load the module
|
374 |
+
if ( $this->load_module( $module_id, $module_data ) ) {
|
375 |
+
//Okay, if it loads, we can assume it works.
|
376 |
+
$this->plugin_conf->options['active_modules'][ $module_id ] = $module_data;
|
377 |
+
$this->plugin_conf->save_options();
|
378 |
+
//Invalidate the per-category active module cache
|
379 |
+
$this->_category_cache_active = null;
|
380 |
+
|
381 |
+
//Notify the module that it's been activated
|
382 |
+
$module = $this->get_module( $module_id );
|
383 |
+
if ( $module ) {
|
384 |
+
$module->activated();
|
385 |
+
}
|
386 |
+
return true;
|
387 |
+
} else {
|
388 |
+
return false;
|
389 |
+
}
|
390 |
+
}
|
391 |
+
|
392 |
+
/**
|
393 |
+
* Deactivate a module.
|
394 |
+
* Does nothing if the module is already inactive.
|
395 |
+
*
|
396 |
+
* @param string $module_id
|
397 |
+
* @return bool
|
398 |
+
*/
|
399 |
+
function deactivate( $module_id ) {
|
400 |
+
if ( ! $this->is_active( $module_id ) ) {
|
401 |
+
return true;
|
402 |
+
}
|
403 |
+
|
404 |
+
//Some modules are supposed to be always active and thus can't be deactivated
|
405 |
+
$module_data = $this->get_module_data( $module_id, false );
|
406 |
+
if ( isset( $module_data['ModuleAlwaysActive'] ) && $module_data['ModuleAlwaysActive'] ) {
|
407 |
+
return false;
|
408 |
+
}
|
409 |
+
|
410 |
+
//Notify the module that it's being deactivated
|
411 |
+
$module = $this->get_module( $module_id );
|
412 |
+
if ( $module ) {
|
413 |
+
$module->deactivated();
|
414 |
+
}
|
415 |
+
|
416 |
+
unset( $this->plugin_conf->options['active_modules'][ $module_id ] );
|
417 |
+
|
418 |
+
//Keep track of when each module was last deactivated. Used for parser resynchronization.
|
419 |
+
if ( isset( $this->plugin_conf->options['module_deactivated_when'] ) ) {
|
420 |
+
$this->plugin_conf->options['module_deactivated_when'][ $module_id ] = current_time( 'timestamp' );
|
421 |
+
} else {
|
422 |
+
$this->plugin_conf->options['module_deactivated_when'] = array(
|
423 |
+
$module_id => current_time( 'timestamp' ),
|
424 |
+
);
|
425 |
+
}
|
426 |
+
$this->plugin_conf->save_options();
|
427 |
+
|
428 |
+
$this->_category_cache_active = null; //Invalidate the by-category cache since we just changed something
|
429 |
+
return true;
|
430 |
+
}
|
431 |
+
|
432 |
+
/**
|
433 |
+
* Determine when a module was last deactivated.
|
434 |
+
*
|
435 |
+
* @param string $module_id Module ID.
|
436 |
+
* @return int Timestamp of last deactivation, or 0 if the module has never been deactivated.
|
437 |
+
*/
|
438 |
+
function get_last_deactivation_time( $module_id ) {
|
439 |
+
if ( isset( $this->plugin_conf->options['module_deactivated_when'][ $module_id ] ) ) {
|
440 |
+
return $this->plugin_conf->options['module_deactivated_when'][ $module_id ];
|
441 |
+
} else {
|
442 |
+
return 0;
|
443 |
+
}
|
444 |
+
}
|
445 |
+
|
446 |
+
/**
|
447 |
+
* Set the current list of active modules. If any of the modules are not currently active,
|
448 |
+
* they will be activated. Any currently active modules that are not on the new list will
|
449 |
+
* be deactivated.
|
450 |
+
*
|
451 |
+
* @param array $ids An array of module IDs.
|
452 |
+
* @return void
|
453 |
+
*/
|
454 |
+
function set_active_modules( $ids ) {
|
455 |
+
$current_active = array_keys( $this->get_active_modules() );
|
456 |
+
|
457 |
+
$activate = array_diff( $ids, $current_active );
|
458 |
+
$deactivate = array_diff( $current_active, $ids );
|
459 |
+
|
460 |
+
//Deactivate any modules not present in the new list
|
461 |
+
foreach ( $deactivate as $module_id ) {
|
462 |
+
$this->deactivate( $module_id );
|
463 |
+
}
|
464 |
+
|
465 |
+
//Activate modules present in the new list but not in the old list
|
466 |
+
foreach ( $activate as $module_id ) {
|
467 |
+
$this->activate( $module_id );
|
468 |
+
}
|
469 |
+
|
470 |
+
//Ensure all active modules have the latest headers
|
471 |
+
$this->refresh_active_module_cache();
|
472 |
+
|
473 |
+
//Invalidate the per-category active module cache
|
474 |
+
$this->_category_cache_active = null;
|
475 |
+
}
|
476 |
+
|
477 |
+
/**
|
478 |
+
* Send the activation message to all currently active plugins when the plugin is activated.
|
479 |
+
*
|
480 |
+
* @return void
|
481 |
+
*/
|
482 |
+
function plugin_activated() {
|
483 |
+
global $blclog;
|
484 |
+
|
485 |
+
//Ensure all active modules have the latest headers
|
486 |
+
$blclog->log( '... Updating module cache' );
|
487 |
+
$start = microtime( true );
|
488 |
+
$this->refresh_active_module_cache();
|
489 |
+
$blclog->info( sprintf( '... Cache refresh took %.3f seconds', microtime( true ) - $start ) );
|
490 |
+
|
491 |
+
//Notify them that we've been activated
|
492 |
+
$blclog->log( '... Loading modules' );
|
493 |
+
$start = microtime( true );
|
494 |
+
$this->load_modules();
|
495 |
+
$blclog->info( sprintf( '... %d modules loaded in %.3f seconds', count( $this->loaded ), microtime( true ) - $start ) );
|
496 |
+
|
497 |
+
$active = $this->get_active_modules();
|
498 |
+
foreach ( $active as $module_id => $module_data ) {
|
499 |
+
$blclog->log( sprintf( '... Notifying module "%s"', $module_id ) );
|
500 |
+
$module = $this->get_module( $module_id );
|
501 |
+
if ( $module ) {
|
502 |
+
$module->plugin_activated();
|
503 |
+
} else {
|
504 |
+
$blclog->warn( sprintf( '... Module "%s" failed to load!', $module_id ) );
|
505 |
+
}
|
506 |
+
}
|
507 |
+
}
|
508 |
+
|
509 |
+
/**
|
510 |
+
* Refresh the cached data of all active modules.
|
511 |
+
*
|
512 |
+
* @return array An updated list of active modules.
|
513 |
+
*/
|
514 |
+
function refresh_active_module_cache() {
|
515 |
+
$modules = $this->get_modules();
|
516 |
+
foreach ( $this->plugin_conf->options['active_modules'] as $module_id => $module_header ) {
|
517 |
+
if ( array_key_exists( $module_id, $modules ) ) {
|
518 |
+
$this->plugin_conf->options['active_modules'][ $module_id ] = $modules[ $module_id ];
|
519 |
+
}
|
520 |
+
}
|
521 |
+
$this->plugin_conf->save_options();
|
522 |
+
$this->_category_cache_active = null; //Invalidate the by-category active module cache
|
523 |
+
return $this->plugin_conf->options['active_modules'];
|
524 |
+
}
|
525 |
+
|
526 |
+
/**
|
527 |
+
* Load active modules.
|
528 |
+
*
|
529 |
+
* @param string $context Optional. If specified, only the modules that match this context (or the "all" context) will be loaded.
|
530 |
+
* @return void
|
531 |
+
*/
|
532 |
+
function load_modules( $context = '' ) {
|
533 |
+
$active = $this->get_active_modules();
|
534 |
+
//Avoid trying to load a virtual module before the module that registered it has been loaded.
|
535 |
+
$active = $this->put_virtual_last( $active );
|
536 |
+
|
537 |
+
foreach ( $active as $module_id => $module_data ) {
|
538 |
+
//Load the module
|
539 |
+
$should_load = ( 'all' == $module_data['ModuleContext'] ) || ( ! empty( $context ) && $module_data['ModuleContext'] == $context );
|
540 |
+
if ( $should_load ) {
|
541 |
+
$this->load_module( $module_id, $module_data );
|
542 |
+
}
|
543 |
+
}
|
544 |
+
}
|
545 |
+
|
546 |
+
/**
|
547 |
+
* Load and possibly instantiate a specific module.
|
548 |
+
*
|
549 |
+
* @access private
|
550 |
+
*
|
551 |
+
* @param string $module_id
|
552 |
+
* @param array $module_data
|
553 |
+
* @return bool True if the module was successfully loaded, false otherwise.
|
554 |
+
*/
|
555 |
+
function load_module( $module_id, $module_data = null ) {
|
556 |
+
|
557 |
+
//Only load each module once.
|
558 |
+
if ( ! empty( $this->loaded[ $module_id ] ) ) {
|
559 |
+
return true;
|
560 |
+
}
|
561 |
+
|
562 |
+
if ( ! isset( $module_data ) ) {
|
563 |
+
$module_data = $this->get_module_data( $module_id );
|
564 |
+
if ( empty( $module_data ) ) {
|
565 |
+
return false;
|
566 |
+
}
|
567 |
+
}
|
568 |
+
|
569 |
+
//Load a normal module
|
570 |
+
if ( empty( $module_data['virtual'] ) ) {
|
571 |
+
|
572 |
+
//Skip invalid and missing modules
|
573 |
+
if ( empty( $module_data['file'] ) ) {
|
574 |
+
return false;
|
575 |
+
}
|
576 |
+
|
577 |
+
//Get the full path to the module file
|
578 |
+
$filename = $this->module_dir . '/' . $module_data['file'];
|
579 |
+
if ( ! file_exists( $filename ) ) {
|
580 |
+
return false;
|
581 |
+
}
|
582 |
+
|
583 |
+
//Load it
|
584 |
+
include $filename;
|
585 |
+
$this->loaded[ $module_id ] = true;
|
586 |
+
|
587 |
+
} else {
|
588 |
+
|
589 |
+
//Virtual modules don't need to be explicitly loaded, but they must
|
590 |
+
//be registered.
|
591 |
+
if ( ! array_key_exists( $module_id, $this->_virtual_modules ) ) {
|
592 |
+
return false;
|
593 |
+
}
|
594 |
+
$this->loaded[ $module_id ] = true;
|
595 |
+
|
596 |
+
}
|
597 |
+
|
598 |
+
//Instantiate the main module class unless lazy init is on (default is off)
|
599 |
+
if ( ! array_key_exists( $module_id, $this->instances ) ) { //Only try to instantiate once
|
600 |
+
if ( ! $module_data['ModuleLazyInit'] ) {
|
601 |
+
$this->init_module( $module_id, $module_data );
|
602 |
+
}
|
603 |
+
}
|
604 |
+
|
605 |
+
return true;
|
606 |
+
}
|
607 |
+
|
608 |
+
/**
|
609 |
+
* Instantiate a certain module.
|
610 |
+
*
|
611 |
+
* @param string $module_id
|
612 |
+
* @param array $module_data Optional. The header data of the module that needs to be instantiated. If not specified, it will be retrieved automatically.
|
613 |
+
* @return object The newly instantiated module object (extends blcModule), or NULL on error.
|
614 |
+
*/
|
615 |
+
function init_module( $module_id, $module_data = null ) {
|
616 |
+
//Each module is only instantiated once.
|
617 |
+
if ( isset( $this->instances[ $module_id ] ) ) {
|
618 |
+
return $this->instances[ $module_id ];
|
619 |
+
}
|
620 |
+
|
621 |
+
if ( ! isset( $module_data ) ) {
|
622 |
+
$module_data = $this->get_module_data( $module_id );
|
623 |
+
if ( empty( $module_data ) ) {
|
624 |
+
return null;
|
625 |
+
}
|
626 |
+
}
|
627 |
+
|
628 |
+
if ( ! empty( $module_data['ModuleClassName'] ) && class_exists( $module_data['ModuleClassName'] ) ) {
|
629 |
+
$className = $module_data['ModuleClassName'];
|
630 |
+
$this->instances[ $module_id ] = new $className(
|
631 |
+
$module_id,
|
632 |
+
$module_data,
|
633 |
+
$this->plugin_conf,
|
634 |
+
$this
|
635 |
+
);
|
636 |
+
return $this->instances[ $module_id ];
|
637 |
+
};
|
638 |
+
|
639 |
+
return null;
|
640 |
+
}
|
641 |
+
|
642 |
+
function is_instantiated( $module_id ) {
|
643 |
+
return ! empty( $this->instances[ $module_id ] );
|
644 |
+
}
|
645 |
+
|
646 |
+
/**
|
647 |
+
* Register a virtual module.
|
648 |
+
*
|
649 |
+
* Virtual modules are the same as normal modules, except that they can be added
|
650 |
+
* on the fly, e.g. by other modules.
|
651 |
+
*
|
652 |
+
* @param string $module_id Module Id.
|
653 |
+
* @param string $module_data Associative array of module data. All module header fields are allowed, except ModuleID.
|
654 |
+
* @return void
|
655 |
+
*/
|
656 |
+
function register_virtual_module( $module_id, $module_data ) {
|
657 |
+
$module_data = $this->normalize_module_header( $module_data, $module_id );
|
658 |
+
$module_data['virtual'] = true;
|
659 |
+
$this->_virtual_modules[ $module_id ] = $module_data;
|
660 |
+
}
|
661 |
+
|
662 |
+
/**
|
663 |
+
* Sort an array of modules so that all virtual modules are placed at its end.
|
664 |
+
*
|
665 |
+
* @param array $modules Input array, [module_id => module_data, ...].
|
666 |
+
* @return array Sorted array.
|
667 |
+
*/
|
668 |
+
function put_virtual_last( $modules ) {
|
669 |
+
uasort( $modules, array( &$this, 'compare_virtual_flags' ) );
|
670 |
+
return $modules;
|
671 |
+
}
|
672 |
+
|
673 |
+
/**
|
674 |
+
* Callback function for sorting modules by the state of their 'virtual' flag.
|
675 |
+
*
|
676 |
+
* @param array $a Associative array of module A data
|
677 |
+
* @param array $b Associative array of module B data
|
678 |
+
* @return int
|
679 |
+
*/
|
680 |
+
function compare_virtual_flags( $a, $b ) {
|
681 |
+
if ( empty( $a['virtual'] ) ) {
|
682 |
+
return empty( $b['virtual'] ) ? 0 : -1;
|
683 |
+
} else {
|
684 |
+
return empty( $b['virtual'] ) ? 1 : 0;
|
685 |
+
}
|
686 |
+
}
|
687 |
+
|
688 |
+
/**
|
689 |
+
* Validate active modules.
|
690 |
+
*
|
691 |
+
* Validates all active modules, deactivates invalid ones and returns
|
692 |
+
* an array of deactivated modules.
|
693 |
+
*
|
694 |
+
* @return array
|
695 |
+
*/
|
696 |
+
function validate_active_modules() {
|
697 |
+
$active = $this->get_active_modules();
|
698 |
+
if ( empty( $active ) ) {
|
699 |
+
return array();
|
700 |
+
}
|
701 |
+
|
702 |
+
$invalid = array();
|
703 |
+
foreach ( $active as $module_id => $module_data ) {
|
704 |
+
$rez = $this->validate_module( $module_data );
|
705 |
+
if ( is_wp_error( $rez ) ) {
|
706 |
+
$invalid[ $module_id ] = $rez;
|
707 |
+
$this->deactivate( $module_id );
|
708 |
+
}
|
709 |
+
}
|
710 |
+
|
711 |
+
return $invalid;
|
712 |
+
}
|
713 |
+
|
714 |
+
/**
|
715 |
+
* Validate module data.
|
716 |
+
*
|
717 |
+
* Checks that the module file exists or that the module
|
718 |
+
* is a currently registered virtual module.
|
719 |
+
*
|
720 |
+
* @param array $module_data Associative array of module data.
|
721 |
+
* @return bool|WP_Error True on success, an error object if the module fails validation
|
722 |
+
*/
|
723 |
+
function validate_module( $module_data ) {
|
724 |
+
if ( empty( $module_data['ModuleID'] ) ) {
|
725 |
+
return new WP_Error( 'invalid_cached_header', 'The cached module header is invalid' );
|
726 |
+
}
|
727 |
+
|
728 |
+
if ( empty( $module_data['virtual'] ) ) {
|
729 |
+
//Normal modules must have a valid filename
|
730 |
+
if ( empty( $module_data['file'] ) ) {
|
731 |
+
return new WP_Error( 'module_not_found', 'Invalid module file' );
|
732 |
+
}
|
733 |
+
|
734 |
+
$filename = $this->module_dir . '/' . $module_data['file'];
|
735 |
+
if ( ! file_exists( $filename ) ) {
|
736 |
+
return new WP_Error( 'module_not_found', 'Module file not found' );
|
737 |
+
}
|
738 |
+
|
739 |
+
//The module file header must be in the proper format. While $module_data comes
|
740 |
+
//from cache and can be assumed to be correct, get_modules() will attempt to load
|
741 |
+
//the current headers and only return modules with semi-valid headers.
|
742 |
+
$installed = $this->get_modules();
|
743 |
+
if ( ! array_key_exists( $module_data['ModuleID'], $installed ) ) {
|
744 |
+
return new WP_Error( 'invalid_module_header', 'Invalid module header' );
|
745 |
+
}
|
746 |
+
} else {
|
747 |
+
//Virtual modules need to be currently registered
|
748 |
+
if ( ! array_key_exists( $module_data['ModuleID'], $this->_virtual_modules ) ) {
|
749 |
+
return new WP_Error( 'module_not_registered', 'The virtual module is not registered' );
|
750 |
+
}
|
751 |
+
}
|
752 |
+
|
753 |
+
return true;
|
754 |
+
}
|
755 |
+
|
756 |
+
/**
|
757 |
+
* Add BLC-module specific headers to the list of allowed plugin headers. This
|
758 |
+
* lets us use get_plugins() to retrieve the list of BLC modules.
|
759 |
+
*
|
760 |
+
* @param array $headers Currently known plugin headers.
|
761 |
+
* @return array New plugin headers.
|
762 |
+
*/
|
763 |
+
function inject_module_headers( $headers ) {
|
764 |
+
$module_headers = array(
|
765 |
+
'ModuleID',
|
766 |
+
'ModuleCategory',
|
767 |
+
'ModuleContext',
|
768 |
+
'ModuleLazyInit',
|
769 |
+
'ModuleClassName',
|
770 |
+
'ModulePriority',
|
771 |
+
'ModuleCheckerUrlPattern',
|
772 |
+
'ModuleHidden', //Don't show the module in the Settings page
|
773 |
+
'ModuleAlwaysActive', //Module can't be deactivated.
|
774 |
+
'ModuleRequiresPro', //Can only be activated in the Pro version
|
775 |
+
);
|
776 |
+
|
777 |
+
return array_merge( $headers, $module_headers );
|
778 |
+
}
|
779 |
+
|
780 |
+
/**
|
781 |
+
* Normalize a module header, using defaults where necessary.
|
782 |
+
*
|
783 |
+
* @param array $module_header Module header, as read from the module's .php file.
|
784 |
+
* @param string $module_id Module ID.
|
785 |
+
* @param string $module_filename Module filename. Optional.
|
786 |
+
* @return array Normalized module header.
|
787 |
+
*/
|
788 |
+
function normalize_module_header( $module_header, $module_id, $module_filename = '' ) {
|
789 |
+
//Default values for optional module header fields
|
790 |
+
$defaults = array(
|
791 |
+
'ModuleContext' => 'all',
|
792 |
+
'ModuleCategory' => 'other',
|
793 |
+
'ModuleLazyInit' => 'false',
|
794 |
+
'ModulePriority' => '0',
|
795 |
+
'ModuleHidden' => 'false',
|
796 |
+
'ModuleAlwaysActive' => 'false',
|
797 |
+
'ModuleRequiresPro' => 'false',
|
798 |
+
'TextDomain' => 'broken-link-checker', //For translating module headers
|
799 |
+
);
|
800 |
+
|
801 |
+
$module_header['ModuleID'] = $module_id; //Just for consistency
|
802 |
+
$module_header['file'] = $module_filename; //Used later to load the module
|
803 |
+
|
804 |
+
//Apply defaults
|
805 |
+
foreach ( $defaults as $field => $default_value ) {
|
806 |
+
if ( empty( $module_header[ $field ] ) ) {
|
807 |
+
$module_header[ $field ] = $default_value;
|
808 |
+
}
|
809 |
+
}
|
810 |
+
|
811 |
+
//Convert bool/int fields from strings to native datatypes
|
812 |
+
$module_header['ModuleLazyInit'] = $this->str_to_bool( $module_header['ModuleLazyInit'] );
|
813 |
+
$module_header['ModuleHidden'] = $this->str_to_bool( $module_header['ModuleHidden'] );
|
814 |
+
$module_header['ModuleAlwaysActive'] = $this->str_to_bool( $module_header['ModuleAlwaysActive'] );
|
815 |
+
$module_header['ModuleRequiresPro'] = $this->str_to_bool( $module_header['ModuleRequiresPro'] );
|
816 |
+
$module_header['ModulePriority'] = intval( $module_header['ModulePriority'] );
|
817 |
+
|
818 |
+
return $module_header;
|
819 |
+
}
|
820 |
+
|
821 |
+
/**
|
822 |
+
* Converts the strings "true" and "false" to boolean TRUE and FALSE, respectively.
|
823 |
+
* Any other string will yield FALSE.
|
824 |
+
*
|
825 |
+
* @param string $value "true" or "false", case-insensitive.
|
826 |
+
* @return bool
|
827 |
+
*/
|
828 |
+
function str_to_bool( $value ) {
|
829 |
+
$value = trim( strtolower( $value ) );
|
830 |
+
return 'true' == $value;
|
831 |
+
}
|
832 |
+
|
833 |
+
/**
|
834 |
+
* Generates a PHP script that calls the __() i18n function with
|
835 |
+
* the name and description of each available module. The generated
|
836 |
+
* script is used to make module headers show up in the .POT file.
|
837 |
+
*
|
838 |
+
* @access private
|
839 |
+
*
|
840 |
+
* @return string
|
841 |
+
*/
|
842 |
+
function _build_header_translation_code() {
|
843 |
+
$this->_module_cache = null; //Clear the cache
|
844 |
+
$modules = $this->get_modules();
|
845 |
+
|
846 |
+
$strings = array();
|
847 |
+
foreach ( $modules as $module_id => $module_header ) {
|
848 |
+
if ( $module_header['ModuleHidden'] || ( 'write-module-placeholders' == $module_id ) ) {
|
849 |
+
continue;
|
850 |
+
}
|
851 |
+
if ( ! empty( $module_header['Name'] ) ) {
|
852 |
+
$strings[] = sprintf(
|
853 |
+
'_x("%s", "module name", "broken-link-checker");',
|
854 |
+
str_replace( '"', '\"', $module_header['Name'] )
|
855 |
+
);
|
856 |
+
}
|
857 |
+
}
|
858 |
+
|
859 |
+
return "<?php\n" . implode( "\n", $strings ) . "\n";
|
860 |
+
}
|
861 |
+
}
|
includes/modules.php
CHANGED
@@ -1,35 +1,37 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Load all files pertaining to BLC's module subsystem
|
5 |
-
*/
|
6 |
-
|
7 |
-
require 'module-manager.php';
|
8 |
-
require 'module-base.php';
|
9 |
-
|
10 |
-
require 'containers.php';
|
11 |
-
require 'checkers.php';
|
12 |
-
require 'parsers.php';
|
13 |
-
|
14 |
-
$blc_module_manager = blcModuleManager::getInstance(
|
15 |
-
|
16 |
-
|
17 |
-
'
|
18 |
-
'
|
19 |
-
'
|
20 |
-
'
|
21 |
-
'
|
22 |
-
'
|
23 |
-
'
|
24 |
-
'
|
25 |
-
'
|
26 |
-
'
|
27 |
-
'
|
28 |
-
'youtube-
|
29 |
-
'
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Load all files pertaining to BLC's module subsystem
|
5 |
+
*/
|
6 |
+
|
7 |
+
require 'module-manager.php';
|
8 |
+
require 'module-base.php';
|
9 |
+
|
10 |
+
require 'containers.php';
|
11 |
+
require 'checkers.php';
|
12 |
+
require 'parsers.php';
|
13 |
+
|
14 |
+
$blc_module_manager = blcModuleManager::getInstance(
|
15 |
+
array(
|
16 |
+
//List of modules active by default
|
17 |
+
'http', //Link checker for the HTTP(s) protocol
|
18 |
+
'link', //HTML link parser
|
19 |
+
'image', //HTML image parser
|
20 |
+
'metadata', //Metadata (custom field) parser
|
21 |
+
'url_field', //URL field parser
|
22 |
+
'comment', //Comment container
|
23 |
+
'custom_field', //Post metadata container (aka custom fields)
|
24 |
+
'acf_field', //Post acf container (aka advanced custom fields)
|
25 |
+
'acf', //acf parser
|
26 |
+
'post', //Post content container
|
27 |
+
'page', //Page content container
|
28 |
+
'youtube-checker', //Video checker using the YouTube API
|
29 |
+
'youtube-iframe', //Embedded YouTube video container
|
30 |
+
'dummy', //Dummy container used as a fallback
|
31 |
+
)
|
32 |
+
);
|
33 |
+
|
34 |
+
require 'any-post.php';
|
35 |
+
|
36 |
+
//Let other plugins register virtual modules.
|
37 |
+
do_action( 'blc_register_modules', $blc_module_manager );
|
includes/parsers.php
CHANGED
@@ -1,365 +1,367 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* A base class for parsers.
|
5 |
-
*
|
6 |
-
* In the context of this plugin, a "parser" is a class that knows how to extract or modify
|
7 |
-
* a specific type of links from a given piece of text. For example, there could be a "HTML Link"
|
8 |
-
* parser that knows how to find and modify standard HTML links such as this one :
|
9 |
-
* <a href="http://example.com/">Example</a>
|
10 |
-
*
|
11 |
-
* Other parsers could extract plaintext URLs or handle metadata fields.
|
12 |
-
*
|
13 |
-
* Each parser has a list of supported formats (e.g. "html", "plaintext", etc) and container types
|
14 |
-
* (e.g. "post", "comment", "blogroll", etc). When something needs to be parsed, the involved
|
15 |
-
* container class will look up the parsers that support the relevant format or the container's type,
|
16 |
-
* and apply them to the to-be-parsed string.
|
17 |
-
*
|
18 |
-
* All sub-classes of blcParser should override at least the blcParser::parse() method.
|
19 |
-
*
|
20 |
-
* @see blcContainer::$fields
|
21 |
-
*
|
22 |
-
* @package Broken Link Checker
|
23 |
-
* @access public
|
24 |
-
*/
|
25 |
-
class blcParser extends blcModule {
|
26 |
-
|
27 |
-
var $parser_type;
|
28 |
-
var $supported_formats
|
29 |
-
var $supported_containers = array();
|
30 |
-
|
31 |
-
/**
|
32 |
-
* Initialize the parser. Nothing much here.
|
33 |
-
*
|
34 |
-
* @return void
|
35 |
-
*/
|
36 |
-
function init(){
|
37 |
-
parent::init();
|
38 |
-
$this->parser_type = $this->module_id;
|
39 |
-
}
|
40 |
-
|
41 |
-
/**
|
42 |
-
* Called when the parser is activated.
|
43 |
-
*
|
44 |
-
* @return void
|
45 |
-
*/
|
46 |
-
function activated(){
|
47 |
-
parent::activated();
|
48 |
-
$this->resynch_relevant_containers();
|
49 |
-
}
|
50 |
-
|
51 |
-
/**
|
52 |
-
* Called when BLC is activated.
|
53 |
-
*/
|
54 |
-
function plugin_activated() {
|
55 |
-
//Intentionally do nothing. BLC can not parse containers while it's inactive, so we can be
|
56 |
-
//pretty sure that there are no already-parsed containers that need to be resynchronized.
|
57 |
-
}
|
58 |
-
|
59 |
-
/**
|
60 |
-
* Mark containers that this parser might be interested in as unparsed.
|
61 |
-
*
|
62 |
-
* @uses blcContainerHelper::mark_as_unsynched_where()
|
63 |
-
*
|
64 |
-
* @param bool $only_return If true, just return the list of formats and container types without actually modifying any synch. records.
|
65 |
-
* @return void|array Either nothing or an array in the form [ [format1=>timestamp1, ...], [container_type1=>timestamp1, ...] ]
|
66 |
-
*/
|
67 |
-
function resynch_relevant_containers($only_return = false){
|
68 |
-
global $blclog;
|
69 |
-
$blclog->log(sprintf('...... Parser "%s" is marking relevant items as unsynched', $this->module_id));
|
70 |
-
|
71 |
-
$last_deactivated = $this->module_manager->get_last_deactivation_time($this->module_id);
|
72 |
-
|
73 |
-
$formats = array();
|
74 |
-
foreach($this->supported_formats as $format){
|
75 |
-
$formats[$format] = $last_deactivated;
|
76 |
-
}
|
77 |
-
|
78 |
-
$container_types = array();
|
79 |
-
foreach($this->supported_containers as $container_type){
|
80 |
-
$container_types[$container_type] = $last_deactivated;
|
81 |
-
}
|
82 |
-
|
83 |
-
if ( $only_return ){
|
84 |
-
return array($formats, $container_types);
|
85 |
-
} else {
|
86 |
-
blcContainerHelper::mark_as_unsynched_where($formats, $container_types);
|
87 |
-
}
|
88 |
-
}
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
function parse($content, $base_url = '', $default_link_text = ''){
|
99 |
-
return array();
|
100 |
-
}
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
function edit($content, $new_url, $old_url, $old_raw_url){
|
115 |
-
return new WP_Error(
|
116 |
-
'not_implemented',
|
117 |
-
sprintf(__("Editing is not implemented in the '%s' parser", 'broken-link-checker'), $this->parser_type)
|
118 |
-
);
|
119 |
-
}
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
function unlink($content, $url, $raw_url){
|
130 |
-
return new WP_Error(
|
131 |
-
'not_implemented',
|
132 |
-
sprintf(__("Unlinking is not implemented in the '%s' parser", 'broken-link-checker'), $this->parser_type)
|
133 |
-
);
|
134 |
-
}
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
function ui_get_link_text($instance, $context = 'display'){
|
145 |
-
return $instance->link_text;
|
146 |
-
}
|
147 |
-
|
148 |
-
/**
|
149 |
-
* Check if the parser supports editing the link text.
|
150 |
-
*
|
151 |
-
* @return bool
|
152 |
-
*/
|
153 |
-
public function is_link_text_editable() {
|
154 |
-
return false;
|
155 |
-
}
|
156 |
-
|
157 |
-
/**
|
158 |
-
* Check if the parser supports editing the link URL.
|
159 |
-
*
|
160 |
-
* @return bool
|
161 |
-
*/
|
162 |
-
public function is_url_editable() {
|
163 |
-
return true;
|
164 |
-
}
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
function relative2absolute($url, $base_url = ''){
|
178 |
-
if ( empty($base_url) ){
|
179 |
-
$base_url = home_url();
|
180 |
-
}
|
181 |
-
|
182 |
-
$p = @parse_url($url);
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
}
|
227 |
-
|
228 |
-
}
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
*
|
316 |
-
*
|
317 |
-
* @
|
318 |
-
*
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* A base class for parsers.
|
5 |
+
*
|
6 |
+
* In the context of this plugin, a "parser" is a class that knows how to extract or modify
|
7 |
+
* a specific type of links from a given piece of text. For example, there could be a "HTML Link"
|
8 |
+
* parser that knows how to find and modify standard HTML links such as this one :
|
9 |
+
* <a href="http://example.com/">Example</a>
|
10 |
+
*
|
11 |
+
* Other parsers could extract plaintext URLs or handle metadata fields.
|
12 |
+
*
|
13 |
+
* Each parser has a list of supported formats (e.g. "html", "plaintext", etc) and container types
|
14 |
+
* (e.g. "post", "comment", "blogroll", etc). When something needs to be parsed, the involved
|
15 |
+
* container class will look up the parsers that support the relevant format or the container's type,
|
16 |
+
* and apply them to the to-be-parsed string.
|
17 |
+
*
|
18 |
+
* All sub-classes of blcParser should override at least the blcParser::parse() method.
|
19 |
+
*
|
20 |
+
* @see blcContainer::$fields
|
21 |
+
*
|
22 |
+
* @package Broken Link Checker
|
23 |
+
* @access public
|
24 |
+
*/
|
25 |
+
class blcParser extends blcModule {
|
26 |
+
|
27 |
+
var $parser_type;
|
28 |
+
var $supported_formats = array();
|
29 |
+
var $supported_containers = array();
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Initialize the parser. Nothing much here.
|
33 |
+
*
|
34 |
+
* @return void
|
35 |
+
*/
|
36 |
+
function init() {
|
37 |
+
parent::init();
|
38 |
+
$this->parser_type = $this->module_id;
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Called when the parser is activated.
|
43 |
+
*
|
44 |
+
* @return void
|
45 |
+
*/
|
46 |
+
function activated() {
|
47 |
+
parent::activated();
|
48 |
+
$this->resynch_relevant_containers();
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Called when BLC is activated.
|
53 |
+
*/
|
54 |
+
function plugin_activated() {
|
55 |
+
//Intentionally do nothing. BLC can not parse containers while it's inactive, so we can be
|
56 |
+
//pretty sure that there are no already-parsed containers that need to be resynchronized.
|
57 |
+
}
|
58 |
+
|
59 |
+
/**
|
60 |
+
* Mark containers that this parser might be interested in as unparsed.
|
61 |
+
*
|
62 |
+
* @uses blcContainerHelper::mark_as_unsynched_where()
|
63 |
+
*
|
64 |
+
* @param bool $only_return If true, just return the list of formats and container types without actually modifying any synch. records.
|
65 |
+
* @return void|array Either nothing or an array in the form [ [format1=>timestamp1, ...], [container_type1=>timestamp1, ...] ]
|
66 |
+
*/
|
67 |
+
function resynch_relevant_containers( $only_return = false ) {
|
68 |
+
global $blclog;
|
69 |
+
$blclog->log( sprintf( '...... Parser "%s" is marking relevant items as unsynched', $this->module_id ) );
|
70 |
+
|
71 |
+
$last_deactivated = $this->module_manager->get_last_deactivation_time( $this->module_id );
|
72 |
+
|
73 |
+
$formats = array();
|
74 |
+
foreach ( $this->supported_formats as $format ) {
|
75 |
+
$formats[ $format ] = $last_deactivated;
|
76 |
+
}
|
77 |
+
|
78 |
+
$container_types = array();
|
79 |
+
foreach ( $this->supported_containers as $container_type ) {
|
80 |
+
$container_types[ $container_type ] = $last_deactivated;
|
81 |
+
}
|
82 |
+
|
83 |
+
if ( $only_return ) {
|
84 |
+
return array( $formats, $container_types );
|
85 |
+
} else {
|
86 |
+
blcContainerHelper::mark_as_unsynched_where( $formats, $container_types );
|
87 |
+
}
|
88 |
+
}
|
89 |
+
|
90 |
+
/**
|
91 |
+
* Parse a string for links.
|
92 |
+
*
|
93 |
+
* @param string $content The text to parse.
|
94 |
+
* @param string $base_url The base URL to use for normalizing relative URLs. If ommitted, the blog's root URL will be used.
|
95 |
+
* @param string $default_link_text
|
96 |
+
* @return array An array of new blcLinkInstance objects. The objects will include info about the links found, but not about the corresponding container entity.
|
97 |
+
*/
|
98 |
+
function parse( $content, $base_url = '', $default_link_text = '' ) {
|
99 |
+
return array();
|
100 |
+
}
|
101 |
+
|
102 |
+
/**
|
103 |
+
* Change all links that have a certain URL to a new URL.
|
104 |
+
*
|
105 |
+
* @param string $content Look for links in this string.
|
106 |
+
* @param string $new_url Change the links to this URL.
|
107 |
+
* @param string $old_url The URL to look for.
|
108 |
+
* @param string $old_raw_url The raw, not-normalized URL of the links to look for. Optional.
|
109 |
+
*
|
110 |
+
* @return array|WP_Error If successful, the return value will be an associative array with two
|
111 |
+
* keys : 'content' - the modified content, and 'raw_url' - the new raw, non-normalized URL used
|
112 |
+
* for the modified links. In most cases, the returned raw_url will be equal to the new_url.
|
113 |
+
*/
|
114 |
+
function edit( $content, $new_url, $old_url, $old_raw_url ) {
|
115 |
+
return new WP_Error(
|
116 |
+
'not_implemented',
|
117 |
+
sprintf( __( "Editing is not implemented in the '%s' parser", 'broken-link-checker' ), $this->parser_type )
|
118 |
+
);
|
119 |
+
}
|
120 |
+
|
121 |
+
/**
|
122 |
+
* Remove all links that have a certain URL, leaving anchor text intact.
|
123 |
+
*
|
124 |
+
* @param string $content Look for links in this string.
|
125 |
+
* @param string $url The URL to look for.
|
126 |
+
* @param string $raw_url The raw, non-normalized version of the URL to look for. Optional.
|
127 |
+
* @return string Input string with all matching links removed.
|
128 |
+
*/
|
129 |
+
function unlink( $content, $url, $raw_url ) {
|
130 |
+
return new WP_Error(
|
131 |
+
'not_implemented',
|
132 |
+
sprintf( __( "Unlinking is not implemented in the '%s' parser", 'broken-link-checker' ), $this->parser_type )
|
133 |
+
);
|
134 |
+
}
|
135 |
+
|
136 |
+
/**
|
137 |
+
* Get the link text for printing in the "Broken Links" table.
|
138 |
+
* Sub-classes should override this method and display the link text in a way appropriate for the link type.
|
139 |
+
*
|
140 |
+
* @param blcLinkInstance $instance
|
141 |
+
* @param string $context
|
142 |
+
* @return string HTML
|
143 |
+
*/
|
144 |
+
function ui_get_link_text( $instance, $context = 'display' ) {
|
145 |
+
return $instance->link_text;
|
146 |
+
}
|
147 |
+
|
148 |
+
/**
|
149 |
+
* Check if the parser supports editing the link text.
|
150 |
+
*
|
151 |
+
* @return bool
|
152 |
+
*/
|
153 |
+
public function is_link_text_editable() {
|
154 |
+
return false;
|
155 |
+
}
|
156 |
+
|
157 |
+
/**
|
158 |
+
* Check if the parser supports editing the link URL.
|
159 |
+
*
|
160 |
+
* @return bool
|
161 |
+
*/
|
162 |
+
public function is_url_editable() {
|
163 |
+
return true;
|
164 |
+
}
|
165 |
+
|
166 |
+
/**
|
167 |
+
* Turn a relative URL into an absolute one.
|
168 |
+
*
|
169 |
+
* WordPress 3.4 has WP_Http::make_absolute_url() which is well-tested but not as comprehensive
|
170 |
+
* as this implementation. For example, WP_Http doesn't properly handle directory traversal with "..",
|
171 |
+
* and it removes #anchors for no good reason. The BLC implementation deals with both pretty well.
|
172 |
+
*
|
173 |
+
* @param string $url Relative URL.
|
174 |
+
* @param string $base_url Base URL. If omitted, the blog's root URL will be used.
|
175 |
+
* @return string
|
176 |
+
*/
|
177 |
+
function relative2absolute( $url, $base_url = '' ) {
|
178 |
+
if ( empty( $base_url ) ) {
|
179 |
+
$base_url = home_url();
|
180 |
+
}
|
181 |
+
|
182 |
+
$p = @parse_url( $url );
|
183 |
+
if ( ! $p ) {
|
184 |
+
//URL is a malformed
|
185 |
+
return false;
|
186 |
+
}
|
187 |
+
if ( isset( $p['scheme'] ) ) {
|
188 |
+
return $url;
|
189 |
+
}
|
190 |
+
|
191 |
+
//If the relative URL is just a query string or anchor, simply attach it to the absolute URL and return
|
192 |
+
$first_char = substr( $url, 0, 1 );
|
193 |
+
if ( ( '?' == $first_char ) || ( '#' == $first_char ) ) {
|
194 |
+
return $base_url . $url;
|
195 |
+
}
|
196 |
+
|
197 |
+
$parts = ( parse_url( $base_url ) );
|
198 |
+
|
199 |
+
//Protocol-relative URLs start with "//". We just need to prepend the right protocol.
|
200 |
+
if ( '//' === substr( $url, 0, 2 ) ) {
|
201 |
+
$scheme = isset( $parts['scheme'] ) ? $parts['scheme'] : 'http';
|
202 |
+
return $scheme . ':' . $url;
|
203 |
+
}
|
204 |
+
|
205 |
+
if ( substr( $url, 0, 1 ) == '/' ) {
|
206 |
+
//Relative URL starts with a slash => ignore the base path and jump straight to the root.
|
207 |
+
$path_segments = explode( '/', $url );
|
208 |
+
array_shift( $path_segments );
|
209 |
+
} else {
|
210 |
+
if ( isset( $parts['path'] ) ) {
|
211 |
+
$aparts = explode( '/', $parts['path'] );
|
212 |
+
array_pop( $aparts );
|
213 |
+
$aparts = array_filter( $aparts );
|
214 |
+
} else {
|
215 |
+
$aparts = array();
|
216 |
+
}
|
217 |
+
|
218 |
+
//Merge together the base path & the relative path
|
219 |
+
$aparts = array_merge( $aparts, explode( '/', $url ) );
|
220 |
+
|
221 |
+
//Filter the merged path
|
222 |
+
$path_segments = array();
|
223 |
+
foreach ( $aparts as $part ) {
|
224 |
+
if ( '.' == $part ) {
|
225 |
+
continue; //. = "this directory". It's basically a no-op, so we skip it.
|
226 |
+
} elseif ( '..' == $part ) {
|
227 |
+
array_pop( $path_segments ); //.. = one directory up. Remove the last seen path segment.
|
228 |
+
} else {
|
229 |
+
array_push( $path_segments, $part ); //Normal directory -> add it to the path.
|
230 |
+
}
|
231 |
+
}
|
232 |
+
}
|
233 |
+
$path = implode( '/', $path_segments );
|
234 |
+
|
235 |
+
//Build the absolute URL.
|
236 |
+
$url = '';
|
237 |
+
if ( $parts['scheme'] ) {
|
238 |
+
$url = "$parts[scheme]://";
|
239 |
+
}
|
240 |
+
if ( isset( $parts['user'] ) ) {
|
241 |
+
$url .= $parts['user'];
|
242 |
+
if ( isset( $parts['pass'] ) ) {
|
243 |
+
$url .= ':' . $parts['pass'];
|
244 |
+
}
|
245 |
+
$url .= '@';
|
246 |
+
}
|
247 |
+
if ( isset( $parts['host'] ) ) {
|
248 |
+
$url .= $parts['host'];
|
249 |
+
if ( isset( $parts['port'] ) ) {
|
250 |
+
$url .= ':' . $parts['port'];
|
251 |
+
}
|
252 |
+
$url .= '/';
|
253 |
+
}
|
254 |
+
$url .= $path;
|
255 |
+
|
256 |
+
return $url;
|
257 |
+
}
|
258 |
+
|
259 |
+
/**
|
260 |
+
* Apply a callback function to all links found in a string and return the results.
|
261 |
+
*
|
262 |
+
* The first argument passed to the callback function will be an associative array
|
263 |
+
* of link data. If the optional $extra parameter is set, it will be passed as the
|
264 |
+
* second argument to the callback function.
|
265 |
+
*
|
266 |
+
* The link data array will contain at least these keys :
|
267 |
+
* 'href' - the URL of the link, as-is (i.e. without any sanitization or relative-to-absolute translation).
|
268 |
+
* '#raw' - the raw link code, e.g. the entire '<a href="...">...</a>' tag of a HTML link.
|
269 |
+
*
|
270 |
+
* Sub-classes may also set additional keys.
|
271 |
+
*
|
272 |
+
* This method is currently used only internally, so sub-classes are not required
|
273 |
+
* to implement it.
|
274 |
+
*
|
275 |
+
* @param string $content A text string to parse for links.
|
276 |
+
* @param callback $callback Callback function to apply to all found links.
|
277 |
+
* @param mixed $extra If the optional $extra param. is supplied, it will be passed as the second parameter to the function $callback.
|
278 |
+
* @return array An array of all detected links after applying $callback to each of them.
|
279 |
+
*/
|
280 |
+
function map( $content, $callback, $extra = null ) {
|
281 |
+
return array();
|
282 |
+
}
|
283 |
+
|
284 |
+
/**
|
285 |
+
* Modify all links found in a string using a callback function.
|
286 |
+
*
|
287 |
+
* The first argument passed to the callback function will be an associative array
|
288 |
+
* of link data. If the optional $extra parameter is set, it will be passed as the
|
289 |
+
* second argument to the callback function. See the map() method of this class for
|
290 |
+
* details on the first argument.
|
291 |
+
*
|
292 |
+
* The callback function should return either an associative array or a string. If
|
293 |
+
* a string is returned, the parser will replace the current link with the contents
|
294 |
+
* of that string. If an array is returned, the current link will be modified/rebuilt
|
295 |
+
* by substituting the new values for the old ones (e.g. returning array with the key
|
296 |
+
* 'href' set to 'http://example.com/' will replace the current link's URL with
|
297 |
+
* http://example.com/).
|
298 |
+
*
|
299 |
+
* This method is currently only used internally, so sub-classes are not required
|
300 |
+
* to implement it.
|
301 |
+
*
|
302 |
+
* @see blcParser::map()
|
303 |
+
*
|
304 |
+
* @param string $content A text string containing the links to edit.
|
305 |
+
* @param callback $callback Callback function used to modify the links.
|
306 |
+
* @param mixed $extra If supplied, $extra will be passed as the second parameter to the function $callback.
|
307 |
+
* @return string The modified input string.
|
308 |
+
*/
|
309 |
+
function multi_edit( $content, $callback, $extra = null ) {
|
310 |
+
return $content; //No-op
|
311 |
+
}
|
312 |
+
}
|
313 |
+
|
314 |
+
/**
|
315 |
+
* A helper class for working with parsers. All its methods should be called statically.
|
316 |
+
*
|
317 |
+
* @see blcParser
|
318 |
+
*
|
319 |
+
* @package Broken Link Checker
|
320 |
+
* @access public
|
321 |
+
*/
|
322 |
+
class blcParserHelper {
|
323 |
+
|
324 |
+
/**
|
325 |
+
* Get the parser matching a parser type ID.
|
326 |
+
*
|
327 |
+
* @uses blcModuleManager::get_module()
|
328 |
+
*
|
329 |
+
* @param string $parser_type
|
330 |
+
* @return blcParser|null
|
331 |
+
*/
|
332 |
+
static function get_parser( $parser_type ) {
|
333 |
+
$manager = blcModuleManager::getInstance();
|
334 |
+
return $manager->get_module( $parser_type, true, 'parser' );
|
335 |
+
}
|
336 |
+
|
337 |
+
/**
|
338 |
+
* Get all parsers that support either the specified format or the container type.
|
339 |
+
* If a parser supports both, it will still be included only once.
|
340 |
+
*
|
341 |
+
* @param string $format
|
342 |
+
* @param string $container_type
|
343 |
+
* @return blcParser[]
|
344 |
+
*/
|
345 |
+
static function get_parsers( $format, $container_type ) {
|
346 |
+
$found = array();
|
347 |
+
|
348 |
+
//Retrieve a list of active parsers
|
349 |
+
$manager = blcModuleManager::getInstance();
|
350 |
+
$active_parsers = $manager->get_modules_by_category( 'parser' );
|
351 |
+
|
352 |
+
//Try each one
|
353 |
+
foreach ( $active_parsers as $module_id => $module_data ) {
|
354 |
+
$parser = $manager->get_module( $module_id ); //Will autoload if necessary
|
355 |
+
if ( ! $parser ) {
|
356 |
+
continue;
|
357 |
+
}
|
358 |
+
|
359 |
+
if ( in_array( $format, $parser->supported_formats ) || in_array( $container_type, $parser->supported_containers ) ) {
|
360 |
+
array_push( $found, $parser );
|
361 |
+
}
|
362 |
+
}
|
363 |
+
|
364 |
+
return $found;
|
365 |
+
}
|
366 |
+
}
|
367 |
+
|
includes/screen-meta-links.php
CHANGED
@@ -1,312 +1,312 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* @author Janis Elsts
|
5 |
-
* @copyright 2011
|
6 |
-
*/
|
7 |
-
|
8 |
-
|
9 |
-
if ( !class_exists('wsScreenMetaLinks11') ):
|
10 |
-
|
11 |
-
//Load JSON functions for PHP < 5.2
|
12 |
-
if ( !(function_exists('json_encode') && function_exists('json_decode')) && !(class_exists('Services_JSON') || class_exists('Moxiecode_JSON')) ){
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
}
|
22 |
-
|
23 |
-
class wsScreenMetaLinks11 {
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
<script type="text/javascript">
|
95 |
-
(function($, links){
|
96 |
-
var container = $('#screen-meta-links');
|
97 |
-
if ( container.length == 0 ) {
|
98 |
-
container = $('<div />').attr('id', 'screen-meta-links').insertAfter('#screen-meta');
|
99 |
-
}
|
100 |
-
for(var i = 0; i < links.length; i++){
|
101 |
-
container.append(
|
102 |
-
$('<div/>')
|
103 |
-
.attr({
|
104 |
-
'id' : links[i].id + '-wrap',
|
105 |
-
'class' : 'hide-if-no-js custom-screen-meta-link-wrap'
|
106 |
-
})
|
107 |
-
.append( $('<a/>', links[i]) )
|
108 |
-
);
|
109 |
-
}
|
110 |
-
})(jQuery, <?php echo $this->json_encode($links); ?>);
|
111 |
-
</script>
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
<style type="text/css">
|
165 |
-
.custom-screen-meta-link-wrap {
|
166 |
-
float: right;
|
167 |
-
height: 28px;
|
168 |
-
margin: 0 0 0 6px;
|
169 |
-
|
170 |
-
border: 1px solid #ddd;
|
171 |
-
border-top: none;
|
172 |
-
background: #fff;
|
173 |
-
-webkit-box-shadow: 0 1px 1px -1px rgba(0,0,0,0.1);
|
174 |
-
box-shadow: 0 1px 1px -1px rgba(0,0,0,0.1);
|
175 |
-
}
|
176 |
-
|
177 |
-
#screen-meta .custom-screen-meta-link-wrap a.custom-screen-meta-link,
|
178 |
-
#screen-meta-links .custom-screen-meta-link-wrap a.custom-screen-meta-link
|
179 |
-
{
|
180 |
-
padding: 3px 16px 3px 16px;
|
181 |
-
}
|
182 |
-
|
183 |
-
#screen-meta-links a.custom-screen-meta-link::after {
|
184 |
-
display: none;
|
185 |
-
}
|
186 |
-
</style>
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
<style type="text/css">
|
196 |
-
.custom-screen-meta-link-wrap {
|
197 |
-
float: right;
|
198 |
-
height: 22px;
|
199 |
-
padding: 0;
|
200 |
-
margin: 0 0 0 6px;
|
201 |
-
font-family: sans-serif;
|
202 |
-
-moz-border-radius-bottomleft: 3px;
|
203 |
-
-moz-border-radius-bottomright: 3px;
|
204 |
-
-webkit-border-bottom-left-radius: 3px;
|
205 |
-
-webkit-border-bottom-right-radius: 3px;
|
206 |
-
border-bottom-left-radius: 3px;
|
207 |
-
border-bottom-right-radius: 3px;
|
208 |
-
|
209 |
-
background: #e3e3e3;
|
210 |
-
|
211 |
-
border-right: 1px solid transparent;
|
212 |
-
border-left: 1px solid transparent;
|
213 |
-
border-bottom: 1px solid transparent;
|
214 |
-
background-image: -ms-linear-gradient(bottom, #dfdfdf, #f1f1f1); /* IE10 */
|
215 |
-
background-image: -moz-linear-gradient(bottom, #dfdfdf, #f1f1f1); /* Firefox */
|
216 |
-
background-image: -o-linear-gradient(bottom, #dfdfdf, #f1f1f1); /* Opera */
|
217 |
-
background-image: -webkit-gradient(linear, left bottom, left top, from(#dfdfdf), to(#f1f1f1)); /* old Webkit */
|
218 |
-
background-image: -webkit-linear-gradient(bottom, #dfdfdf, #f1f1f1); /* new Webkit */
|
219 |
-
background-image: linear-gradient(bottom, #dfdfdf, #f1f1f1); /* proposed W3C Markup */
|
220 |
-
}
|
221 |
-
|
222 |
-
#screen-meta .custom-screen-meta-link-wrap a.custom-screen-meta-link,
|
223 |
-
#screen-meta-links .custom-screen-meta-link-wrap a.custom-screen-meta-link
|
224 |
-
{
|
225 |
-
background-image: none;
|
226 |
-
padding-right: 6px;
|
227 |
-
color: #777;
|
228 |
-
}
|
229 |
-
</style>
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
}
|
279 |
-
|
280 |
-
global $ws_screen_meta_links_versions;
|
281 |
-
if ( !isset($ws_screen_meta_links_versions) ){
|
282 |
-
|
283 |
-
}
|
284 |
-
$ws_screen_meta_links_versions['1.1'] = 'wsScreenMetaLinks11';
|
285 |
-
|
286 |
-
endif;
|
287 |
-
|
288 |
-
/**
|
289 |
-
* Add a new link to the screen meta area.
|
290 |
-
*
|
291 |
-
* @param string $id Link ID. Should be unique and a valid value for a HTML ID attribute.
|
292 |
-
* @param string $text Link text.
|
293 |
-
* @param string $href Link URL.
|
294 |
-
* @param string|array $page The page(s) where you want to add the link.
|
295 |
-
* @param array $attributes Optional. Additional attributes for the link tag.
|
296 |
-
* @return void
|
297 |
-
*/
|
298 |
-
function add_screen_meta_link($id, $text, $href, $page, $attributes = null){
|
299 |
-
global $ws_screen_meta_links_versions;
|
300 |
-
|
301 |
-
static $instance = null;
|
302 |
-
if ( is_null($instance) ){
|
303 |
-
//Instantiate the latest version of the wsScreenMetaLinks class
|
304 |
-
uksort($ws_screen_meta_links_versions, 'version_compare');
|
305 |
-
$className = end($ws_screen_meta_links_versions);
|
306 |
-
$instance
|
307 |
-
}
|
308 |
-
|
309 |
-
$instance->add_screen_meta_link($id, $text, $href, $page, $attributes);
|
310 |
-
}
|
311 |
-
|
312 |
-
?>
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @author Janis Elsts
|
5 |
+
* @copyright 2011
|
6 |
+
*/
|
7 |
+
|
8 |
+
|
9 |
+
if ( ! class_exists( 'wsScreenMetaLinks11' ) ) :
|
10 |
+
|
11 |
+
//Load JSON functions for PHP < 5.2
|
12 |
+
if ( ! ( function_exists( 'json_encode' ) && function_exists( 'json_decode' ) ) && ! ( class_exists( 'Services_JSON' ) || class_exists( 'Moxiecode_JSON' ) ) ) {
|
13 |
+
$class_json_path = ABSPATH . WPINC . '/class-json.php';
|
14 |
+
$class_moxiecode_json_path = ABSPATH . WPINC . '/js/tinymce/plugins/spellchecker/classes/utils/JSON.php';
|
15 |
+
if ( file_exists( $class_json_path ) ) {
|
16 |
+
require $class_json_path;
|
17 |
+
|
18 |
+
} elseif ( file_exists( $class_moxiecode_json_path ) ) {
|
19 |
+
require $class_moxiecode_json_path;
|
20 |
+
}
|
21 |
+
}
|
22 |
+
|
23 |
+
class wsScreenMetaLinks11 {
|
24 |
+
var $registered_links; //List of meta links registered for each page.
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Class constructor.
|
28 |
+
*
|
29 |
+
* @return void
|
30 |
+
*/
|
31 |
+
function __construct() {
|
32 |
+
$this->registered_links = array();
|
33 |
+
|
34 |
+
add_action( 'admin_notices', array( &$this, 'append_meta_links' ) );
|
35 |
+
add_action( 'admin_print_styles', array( &$this, 'add_link_styles' ) );
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Add a new link to the screen meta area.
|
40 |
+
*
|
41 |
+
* Do not call this method directly. Instead, use the global add_screen_meta_link() function.
|
42 |
+
*
|
43 |
+
* @param string $id Link ID. Should be unique and a valid value for a HTML ID attribute.
|
44 |
+
* @param string $text Link text.
|
45 |
+
* @param string $href Link URL.
|
46 |
+
* @param string|array $page The page(s) where you want to add the link.
|
47 |
+
* @param array $attributes Optional. Additional attributes for the link tag.
|
48 |
+
* @return void
|
49 |
+
*/
|
50 |
+
function add_screen_meta_link( $id, $text, $href, $page, $attributes = null ) {
|
51 |
+
if ( ! is_array( $page ) ) {
|
52 |
+
$page = array( $page );
|
53 |
+
}
|
54 |
+
if ( is_null( $attributes ) ) {
|
55 |
+
$attributes = array();
|
56 |
+
}
|
57 |
+
|
58 |
+
//Basically a list of props for a jQuery() call
|
59 |
+
$link = compact( 'id', 'text', 'href' );
|
60 |
+
$link = array_merge( $link, $attributes );
|
61 |
+
|
62 |
+
//Add the CSS classes that will make the look like a proper meta link
|
63 |
+
if ( empty( $link['class'] ) ) {
|
64 |
+
$link['class'] = '';
|
65 |
+
}
|
66 |
+
$link['class'] = 'show-settings custom-screen-meta-link ' . $link['class'];
|
67 |
+
|
68 |
+
//Save the link in each relevant page's list
|
69 |
+
foreach ( $page as $page_id ) {
|
70 |
+
if ( ! isset( $this->registered_links[ $page_id ] ) ) {
|
71 |
+
$this->registered_links[ $page_id ] = array();
|
72 |
+
}
|
73 |
+
$this->registered_links[ $page_id ][] = $link;
|
74 |
+
}
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Output the JS that appends the custom meta links to the page.
|
79 |
+
* Callback for the 'admin_notices' action.
|
80 |
+
*
|
81 |
+
* @access private
|
82 |
+
* @return void
|
83 |
+
*/
|
84 |
+
function append_meta_links() {
|
85 |
+
global $hook_suffix;
|
86 |
+
|
87 |
+
//Find links registered for this page
|
88 |
+
$links = $this->get_links_for_page( $hook_suffix );
|
89 |
+
if ( empty( $links ) ) {
|
90 |
+
return;
|
91 |
+
}
|
92 |
+
|
93 |
+
?>
|
94 |
+
<script type="text/javascript">
|
95 |
+
(function($, links){
|
96 |
+
var container = $('#screen-meta-links');
|
97 |
+
if ( container.length == 0 ) {
|
98 |
+
container = $('<div />').attr('id', 'screen-meta-links').insertAfter('#screen-meta');
|
99 |
+
}
|
100 |
+
for(var i = 0; i < links.length; i++){
|
101 |
+
container.append(
|
102 |
+
$('<div/>')
|
103 |
+
.attr({
|
104 |
+
'id' : links[i].id + '-wrap',
|
105 |
+
'class' : 'hide-if-no-js custom-screen-meta-link-wrap'
|
106 |
+
})
|
107 |
+
.append( $('<a/>', links[i]) )
|
108 |
+
);
|
109 |
+
}
|
110 |
+
})(jQuery, <?php echo $this->json_encode( $links ); ?>);
|
111 |
+
</script>
|
112 |
+
<?php
|
113 |
+
}
|
114 |
+
|
115 |
+
/**
|
116 |
+
* Get a list of custom screen meta links registered for a specific page.
|
117 |
+
*
|
118 |
+
* @param string $page
|
119 |
+
* @return array
|
120 |
+
*/
|
121 |
+
function get_links_for_page( $page ) {
|
122 |
+
$links = array();
|
123 |
+
|
124 |
+
if ( isset( $this->registered_links[ $page ] ) ) {
|
125 |
+
$links = array_merge( $links, $this->registered_links[ $page ] );
|
126 |
+
}
|
127 |
+
$page_as_screen = $this->page_to_screen_id( $page );
|
128 |
+
if ( ( $page_as_screen != $page ) && isset( $this->registered_links[ $page_as_screen ] ) ) {
|
129 |
+
$links = array_merge( $links, $this->registered_links[ $page_as_screen ] );
|
130 |
+
}
|
131 |
+
|
132 |
+
return $links;
|
133 |
+
}
|
134 |
+
|
135 |
+
/**
|
136 |
+
* Output the CSS code for custom screen meta links. Required because WP only
|
137 |
+
* has styles for specific meta links (by #id), not meta links in general.
|
138 |
+
*
|
139 |
+
* Callback for 'admin_print_styles'.
|
140 |
+
*
|
141 |
+
* @access private
|
142 |
+
* @return void
|
143 |
+
*/
|
144 |
+
function add_link_styles() {
|
145 |
+
global $hook_suffix;
|
146 |
+
//Don't output the CSS if there are no custom meta links for this page.
|
147 |
+
$links = $this->get_links_for_page( $hook_suffix );
|
148 |
+
if ( empty( $links ) ) {
|
149 |
+
return;
|
150 |
+
}
|
151 |
+
|
152 |
+
if ( ! isset( $GLOBALS['wp_version'] ) || version_compare( $GLOBALS['wp_version'], '3.8-RC1', '<' ) ) {
|
153 |
+
$this->print_old_link_styles();
|
154 |
+
} else {
|
155 |
+
$this->print_link_styles();
|
156 |
+
}
|
157 |
+
}
|
158 |
+
|
159 |
+
/**
|
160 |
+
* Print screen meta button styles (WP 3.8+).
|
161 |
+
*/
|
162 |
+
private function print_link_styles() {
|
163 |
+
?>
|
164 |
+
<style type="text/css">
|
165 |
+
.custom-screen-meta-link-wrap {
|
166 |
+
float: right;
|
167 |
+
height: 28px;
|
168 |
+
margin: 0 0 0 6px;
|
169 |
+
|
170 |
+
border: 1px solid #ddd;
|
171 |
+
border-top: none;
|
172 |
+
background: #fff;
|
173 |
+
-webkit-box-shadow: 0 1px 1px -1px rgba(0,0,0,0.1);
|
174 |
+
box-shadow: 0 1px 1px -1px rgba(0,0,0,0.1);
|
175 |
+
}
|
176 |
+
|
177 |
+
#screen-meta .custom-screen-meta-link-wrap a.custom-screen-meta-link,
|
178 |
+
#screen-meta-links .custom-screen-meta-link-wrap a.custom-screen-meta-link
|
179 |
+
{
|
180 |
+
padding: 3px 16px 3px 16px;
|
181 |
+
}
|
182 |
+
|
183 |
+
#screen-meta-links a.custom-screen-meta-link::after {
|
184 |
+
display: none;
|
185 |
+
}
|
186 |
+
</style>
|
187 |
+
<?php
|
188 |
+
}
|
189 |
+
|
190 |
+
/**
|
191 |
+
* Print old screen meta button styles (WP 3.7.x and older).
|
192 |
+
*/
|
193 |
+
private function print_old_link_styles() {
|
194 |
+
?>
|
195 |
+
<style type="text/css">
|
196 |
+
.custom-screen-meta-link-wrap {
|
197 |
+
float: right;
|
198 |
+
height: 22px;
|
199 |
+
padding: 0;
|
200 |
+
margin: 0 0 0 6px;
|
201 |
+
font-family: sans-serif;
|
202 |
+
-moz-border-radius-bottomleft: 3px;
|
203 |
+
-moz-border-radius-bottomright: 3px;
|
204 |
+
-webkit-border-bottom-left-radius: 3px;
|
205 |
+
-webkit-border-bottom-right-radius: 3px;
|
206 |
+
border-bottom-left-radius: 3px;
|
207 |
+
border-bottom-right-radius: 3px;
|
208 |
+
|
209 |
+
background: #e3e3e3;
|
210 |
+
|
211 |
+
border-right: 1px solid transparent;
|
212 |
+
border-left: 1px solid transparent;
|
213 |
+
border-bottom: 1px solid transparent;
|
214 |
+
background-image: -ms-linear-gradient(bottom, #dfdfdf, #f1f1f1); /* IE10 */
|
215 |
+
background-image: -moz-linear-gradient(bottom, #dfdfdf, #f1f1f1); /* Firefox */
|
216 |
+
background-image: -o-linear-gradient(bottom, #dfdfdf, #f1f1f1); /* Opera */
|
217 |
+
background-image: -webkit-gradient(linear, left bottom, left top, from(#dfdfdf), to(#f1f1f1)); /* old Webkit */
|
218 |
+
background-image: -webkit-linear-gradient(bottom, #dfdfdf, #f1f1f1); /* new Webkit */
|
219 |
+
background-image: linear-gradient(bottom, #dfdfdf, #f1f1f1); /* proposed W3C Markup */
|
220 |
+
}
|
221 |
+
|
222 |
+
#screen-meta .custom-screen-meta-link-wrap a.custom-screen-meta-link,
|
223 |
+
#screen-meta-links .custom-screen-meta-link-wrap a.custom-screen-meta-link
|
224 |
+
{
|
225 |
+
background-image: none;
|
226 |
+
padding-right: 6px;
|
227 |
+
color: #777;
|
228 |
+
}
|
229 |
+
</style>
|
230 |
+
<?php
|
231 |
+
}
|
232 |
+
|
233 |
+
/**
|
234 |
+
* Convert a page hook name to a screen ID.
|
235 |
+
*
|
236 |
+
* @uses convert_to_screen()
|
237 |
+
* @access private
|
238 |
+
*
|
239 |
+
* @param string $page
|
240 |
+
* @return string
|
241 |
+
*/
|
242 |
+
function page_to_screen_id( $page ) {
|
243 |
+
if ( function_exists( 'convert_to_screen' ) ) {
|
244 |
+
$screen = convert_to_screen( $page );
|
245 |
+
if ( isset( $screen->id ) ) {
|
246 |
+
return $screen->id;
|
247 |
+
} else {
|
248 |
+
return '';
|
249 |
+
}
|
250 |
+
} else {
|
251 |
+
return str_replace( array( '.php', '-new', '-add' ), '', $page );
|
252 |
+
}
|
253 |
+
}
|
254 |
+
|
255 |
+
/**
|
256 |
+
* Back-wards compatible json_encode(). Used to encode link data before
|
257 |
+
* passing it to the JavaScript that actually creates the links.
|
258 |
+
*
|
259 |
+
* @param mixed $data
|
260 |
+
* @return string
|
261 |
+
*/
|
262 |
+
function json_encode( $data ) {
|
263 |
+
if ( function_exists( 'json_encode' ) ) {
|
264 |
+
return json_encode( $data );
|
265 |
+
}
|
266 |
+
if ( class_exists( 'Services_JSON' ) ) {
|
267 |
+
$json = new Services_JSON();
|
268 |
+
return( $json->encodeUnsafe( $data ) );
|
269 |
+
} elseif ( class_exists( 'Moxiecode_JSON' ) ) {
|
270 |
+
$json = new Moxiecode_JSON();
|
271 |
+
return $json->encode( $data );
|
272 |
+
} else {
|
273 |
+
trigger_error( 'No JSON parser available', E_USER_ERROR );
|
274 |
+
return null;
|
275 |
+
}
|
276 |
+
}
|
277 |
+
|
278 |
+
}
|
279 |
+
|
280 |
+
global $ws_screen_meta_links_versions;
|
281 |
+
if ( ! isset( $ws_screen_meta_links_versions ) ) {
|
282 |
+
$ws_screen_meta_links_versions = array();
|
283 |
+
}
|
284 |
+
$ws_screen_meta_links_versions['1.1'] = 'wsScreenMetaLinks11';
|
285 |
+
|
286 |
+
endif;
|
287 |
+
|
288 |
+
/**
|
289 |
+
* Add a new link to the screen meta area.
|
290 |
+
*
|
291 |
+
* @param string $id Link ID. Should be unique and a valid value for a HTML ID attribute.
|
292 |
+
* @param string $text Link text.
|
293 |
+
* @param string $href Link URL.
|
294 |
+
* @param string|array $page The page(s) where you want to add the link.
|
295 |
+
* @param array $attributes Optional. Additional attributes for the link tag.
|
296 |
+
* @return void
|
297 |
+
*/
|
298 |
+
function add_screen_meta_link( $id, $text, $href, $page, $attributes = null ) {
|
299 |
+
global $ws_screen_meta_links_versions;
|
300 |
+
|
301 |
+
static $instance = null;
|
302 |
+
if ( is_null( $instance ) ) {
|
303 |
+
//Instantiate the latest version of the wsScreenMetaLinks class
|
304 |
+
uksort( $ws_screen_meta_links_versions, 'version_compare' );
|
305 |
+
$className = end( $ws_screen_meta_links_versions );
|
306 |
+
$instance = new $className;
|
307 |
+
}
|
308 |
+
|
309 |
+
$instance->add_screen_meta_link( $id, $text, $href, $page, $attributes );
|
310 |
+
}
|
311 |
+
|
312 |
+
?>
|
includes/screen-options/screen-options.php
CHANGED
@@ -1,297 +1,296 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
if ( !class_exists('wsScreenOptions12') ):
|
4 |
-
|
5 |
-
/**
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
class wsScreenOptions13 {
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
}
|
255 |
-
|
256 |
-
//All versions of the class are stored in a global array
|
257 |
-
//and only the latest version is actually used.
|
258 |
-
global $ws_screen_options_versions;
|
259 |
-
if ( !isset($ws_screen_options_versions) ){
|
260 |
-
|
261 |
-
}
|
262 |
-
$ws_screen_options_versions['1.3'] = 'wsScreenOptions13';
|
263 |
-
|
264 |
-
endif;
|
265 |
-
|
266 |
-
if ( !function_exists('add_screen_options_panel') ){
|
267 |
-
|
268 |
-
/**
|
269 |
-
* Add a new settings panel to the "Screen Options" box.
|
270 |
-
*
|
271 |
-
* @see wsScreenOptions10::add_screen_options_panel()
|
272 |
-
*
|
273 |
-
* @param string $id String to use in the 'id' attribute of the settings panel. Should be unique.
|
274 |
-
* @param string $title Title of the settings panel. Set to an empty string to omit title.
|
275 |
-
* @param callback $callback Function that fills the panel with the desired content. Should return its output.
|
276 |
-
* @param string|array $page The page(s) on which to show the panel (similar to add_meta_box()).
|
277 |
-
* @param callback $save_callback Optional. Function that saves the settings contained in the panel.
|
278 |
-
* @param bool $autosave Optional. If set, settings will be automatically saved (via AJAX) when the value of any input element in the panel changes. Defaults to false.
|
279 |
-
* @return void
|
280 |
-
*/
|
281 |
-
function add_screen_options_panel($id, $title, $callback, $page, $save_callback = null, $autosave = false){
|
282 |
-
global $ws_screen_options_versions;
|
283 |
-
|
284 |
-
static $instance = null; /** @var wsScreenOptions13 $instance */
|
285 |
-
if ( is_null($instance) ){
|
286 |
-
//Instantiate the latest version of the wsScreenOptions class
|
287 |
-
uksort($ws_screen_options_versions, 'version_compare');
|
288 |
-
$className = end($ws_screen_options_versions);
|
289 |
-
$instance
|
290 |
-
$instance->init();
|
291 |
-
}
|
292 |
-
|
293 |
-
$instance->add_screen_options_panel($id, $title, $callback, $page, $save_callback, $autosave);
|
294 |
-
}
|
295 |
-
|
296 |
-
|
297 |
-
|
1 |
+
<?php
|
2 |
+
|
3 |
+
if ( ! class_exists( 'wsScreenOptions12' ) ) :
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Class for adding new panels to the "Screen Options" box.
|
7 |
+
*
|
8 |
+
* Do not access this class directly. Instead, use the add_screen_options_panel() function.
|
9 |
+
*
|
10 |
+
* @author Janis Elsts
|
11 |
+
* @copyright 2014
|
12 |
+
* @version 1.3
|
13 |
+
* @access public
|
14 |
+
*/
|
15 |
+
class wsScreenOptions13 {
|
16 |
+
var $registered_panels; //List of custom "Screen Options" panels
|
17 |
+
var $page_panels; //Index of panels registered for each page ($page => array of panel ids).
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Class constructor
|
21 |
+
*
|
22 |
+
* @return void
|
23 |
+
*/
|
24 |
+
function init() {
|
25 |
+
$this->registered_panels = array();
|
26 |
+
$this->page_panels = array();
|
27 |
+
|
28 |
+
add_action( 'current_screen', array( $this, 'populate_page_panels' ) );
|
29 |
+
add_filter( 'screen_settings', array( &$this, 'append_screen_settings' ), 10, 2 );
|
30 |
+
add_action( 'admin_print_scripts', array( &$this, 'add_autosave_script' ) );
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Add a new settings panel to the "Screen Options" box.
|
35 |
+
*
|
36 |
+
* @param string $id String to use in the 'id' attribute of the settings panel. Should be unique.
|
37 |
+
* @param string $title Title of the settings panel. Set to an empty string to omit title.
|
38 |
+
* @param callback $callback Function that fills the panel with the desired content. Should return its output.
|
39 |
+
* @param string|array $page The page(s) on which to show the panel (similar to add_meta_box()).
|
40 |
+
* @param callback $save_callback Optional. Function that saves the settings.
|
41 |
+
* @param bool $autosave Optional. If set, settings will be automatically saved (via AJAX) when the value of any input element in the panel changes. Defaults to false.
|
42 |
+
* @return void
|
43 |
+
*/
|
44 |
+
function add_screen_options_panel( $id, $title, $callback, $page, $save_callback = null, $autosave = false ) {
|
45 |
+
if ( ! is_array( $page ) ) {
|
46 |
+
$page = array( $page );
|
47 |
+
}
|
48 |
+
|
49 |
+
$new_panel = array(
|
50 |
+
'title' => $title,
|
51 |
+
'callback' => $callback,
|
52 |
+
'page' => $page,
|
53 |
+
'save_callback' => $save_callback,
|
54 |
+
'autosave' => $autosave,
|
55 |
+
);
|
56 |
+
$this->registered_panels[ $id ] = $new_panel;
|
57 |
+
|
58 |
+
if ( $save_callback ) {
|
59 |
+
add_action( 'wp_ajax_save_settings-' . $id, array( $this, 'ajax_save_callback' ) );
|
60 |
+
}
|
61 |
+
}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* Populate a lookup array for screen -> panels queries.
|
65 |
+
*
|
66 |
+
* This is a callback for the "current_screen" action. We have to do it in this hook or WordPress will
|
67 |
+
* complain about "doing it wrong" and incorrectly suggest using the "add_meta_boxes" action.
|
68 |
+
*
|
69 |
+
* "add_meta_boxes" doesn't work here because it only gets called on CPT pages and we want the ability
|
70 |
+
* to add screen options to any page.
|
71 |
+
*/
|
72 |
+
function populate_page_panels() {
|
73 |
+
foreach ( $this->registered_panels as $id => $panel ) {
|
74 |
+
$page = $panel['page'];
|
75 |
+
|
76 |
+
//Convert page hooks/slugs to screen IDs
|
77 |
+
$page = array_map( array( $this, 'page_to_screen_id' ), $page );
|
78 |
+
$page = array_unique( $page );
|
79 |
+
|
80 |
+
//Store the panel ID in each relevant page's list
|
81 |
+
foreach ( $page as $page_id ) {
|
82 |
+
if ( ! isset( $this->page_panels[ $page_id ] ) ) {
|
83 |
+
$this->page_panels[ $page_id ] = array();
|
84 |
+
}
|
85 |
+
$this->page_panels[ $page_id ][] = $id;
|
86 |
+
}
|
87 |
+
}
|
88 |
+
}
|
89 |
+
|
90 |
+
/**
|
91 |
+
* Convert a page hook name to a screen ID.
|
92 |
+
*
|
93 |
+
* @uses convert_to_screen()
|
94 |
+
* @access private
|
95 |
+
*
|
96 |
+
* @param string $page
|
97 |
+
* @return string
|
98 |
+
*/
|
99 |
+
function page_to_screen_id( $page ) {
|
100 |
+
if ( function_exists( 'convert_to_screen' ) ) {
|
101 |
+
$screen = convert_to_screen( $page );
|
102 |
+
if ( isset( $screen->id ) ) {
|
103 |
+
return $screen->id;
|
104 |
+
} else {
|
105 |
+
return '';
|
106 |
+
}
|
107 |
+
} else {
|
108 |
+
return str_replace( array( '.php', '-new', '-add' ), '', $page );
|
109 |
+
}
|
110 |
+
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Append custom panel HTML to the "Screen Options" box of the current page.
|
114 |
+
* Callback for the 'screen_settings' filter (available in WP 3.0 and up).
|
115 |
+
*
|
116 |
+
* @access private
|
117 |
+
*
|
118 |
+
* @param string $current
|
119 |
+
* @param string $screen Screen object (undocumented).
|
120 |
+
* @return string The HTML code to append to "Screen Options"
|
121 |
+
*/
|
122 |
+
function append_screen_settings( $current, $screen ) {
|
123 |
+
global $hook_suffix;
|
124 |
+
|
125 |
+
//Sanity check
|
126 |
+
if ( ! isset( $screen->id ) ) {
|
127 |
+
return $current;
|
128 |
+
}
|
129 |
+
|
130 |
+
//Are there any panels that want to appear on this page?
|
131 |
+
$panels = $this->get_panels_for_screen( $screen->id, $hook_suffix );
|
132 |
+
if ( empty( $panels ) ) {
|
133 |
+
return $current;
|
134 |
+
}
|
135 |
+
|
136 |
+
//Append all panels registered for this screen
|
137 |
+
foreach ( $panels as $panel_id ) {
|
138 |
+
$panel = $this->registered_panels[ $panel_id ];
|
139 |
+
|
140 |
+
//Add panel title
|
141 |
+
if ( ! empty( $panel['title'] ) ) {
|
142 |
+
$current .= "\n<h5>" . $panel['title'] . "</h5>\n";
|
143 |
+
}
|
144 |
+
//Generate panel contents
|
145 |
+
if ( is_callable( $panel['callback'] ) ) {
|
146 |
+
$contents = call_user_func( $panel['callback'] );
|
147 |
+
$classes = array(
|
148 |
+
'custom-options-panel',
|
149 |
+
);
|
150 |
+
if ( $panel['autosave'] ) {
|
151 |
+
$classes[] = 'requires-autosave';
|
152 |
+
}
|
153 |
+
|
154 |
+
$contents = sprintf(
|
155 |
+
'<div id="%s" class="%s"><input type="hidden" name="_wpnonce-%s" value="%s" />%s</div>',
|
156 |
+
esc_attr( $panel_id ),
|
157 |
+
implode( ' ', $classes ),
|
158 |
+
esc_attr( $panel_id ),
|
159 |
+
wp_create_nonce( 'save_settings-' . $panel_id ),
|
160 |
+
$contents
|
161 |
+
);
|
162 |
+
|
163 |
+
$current .= $contents;
|
164 |
+
}
|
165 |
+
}
|
166 |
+
|
167 |
+
return $current;
|
168 |
+
}
|
169 |
+
|
170 |
+
/**
|
171 |
+
* AJAX callback for the "Screen Options" autosave.
|
172 |
+
*
|
173 |
+
* @access private
|
174 |
+
* @return void
|
175 |
+
*/
|
176 |
+
function ajax_save_callback() {
|
177 |
+
if ( empty( $_POST['action'] ) ) {
|
178 |
+
die( '0' );
|
179 |
+
}
|
180 |
+
|
181 |
+
//The 'action' argument is in the form "save_settings-panel_id"
|
182 |
+
$id = end( explode( '-', $_POST['action'], 2 ) );
|
183 |
+
|
184 |
+
//Basic security check.
|
185 |
+
check_ajax_referer( 'save_settings-' . $id, '_wpnonce-' . $id );
|
186 |
+
|
187 |
+
//Hand the request to the registered callback, if any
|
188 |
+
if ( ! isset( $this->registered_panels[ $id ] ) ) {
|
189 |
+
exit( '0' );
|
190 |
+
}
|
191 |
+
$panel = $this->registered_panels[ $id ];
|
192 |
+
if ( is_callable( $panel['save_callback'] ) ) {
|
193 |
+
call_user_func( $panel['save_callback'], $_POST );
|
194 |
+
die( '1' );
|
195 |
+
} else {
|
196 |
+
die( '0' );
|
197 |
+
}
|
198 |
+
}
|
199 |
+
|
200 |
+
/**
|
201 |
+
* Add/enqueue supporting JavaScript for the autosave function of custom "Screen Options" panels.
|
202 |
+
*
|
203 |
+
* Checks if the current page is supposed to contain any autosave-enabled
|
204 |
+
* panels and adds the script only if that's the case.
|
205 |
+
*
|
206 |
+
* @return void
|
207 |
+
*/
|
208 |
+
function add_autosave_script() {
|
209 |
+
//Get the page id/hook/slug/whatever.
|
210 |
+
global $hook_suffix;
|
211 |
+
|
212 |
+
//Check if we have some panels with autosave registered for this page.
|
213 |
+
$panels = $this->get_panels_for_screen( '', $hook_suffix );
|
214 |
+
if ( empty( $panels ) ) {
|
215 |
+
return;
|
216 |
+
}
|
217 |
+
|
218 |
+
$got_autosave = false;
|
219 |
+
foreach ( $panels as $panel_id ) {
|
220 |
+
if ( $this->registered_panels[ $panel_id ]['autosave'] ) {
|
221 |
+
$got_autosave = true;
|
222 |
+
break;
|
223 |
+
}
|
224 |
+
}
|
225 |
+
|
226 |
+
if ( $got_autosave ) {
|
227 |
+
//Enqueue the script itself
|
228 |
+
$url = plugins_url( 'screen-options.js', __FILE__ );
|
229 |
+
wp_enqueue_script( 'screen-options-custom-autosave', $url, array( 'jquery' ) );
|
230 |
+
}
|
231 |
+
}
|
232 |
+
|
233 |
+
/**
|
234 |
+
* Get custom panels registered for a particular screen and/or page.
|
235 |
+
*
|
236 |
+
* @param string $screen_id Screen ID.
|
237 |
+
* @param string $page Optional. Page filename or hook name.
|
238 |
+
* @return array Array of custom panels.
|
239 |
+
*/
|
240 |
+
function get_panels_for_screen( $screen_id, $page = '' ) {
|
241 |
+
if ( isset( $this->page_panels[ $screen_id ] ) && ! empty( $this->page_panels[ $screen_id ] ) ) {
|
242 |
+
$panels = $this->page_panels[ $screen_id ];
|
243 |
+
} else {
|
244 |
+
$panels = array();
|
245 |
+
}
|
246 |
+
if ( ! empty( $page ) ) {
|
247 |
+
$page_as_screen = $this->page_to_screen_id( $page );
|
248 |
+
if ( isset( $this->page_panels[ $page_as_screen ] ) && ! empty( $this->page_panels[ $page_as_screen ] ) ) {
|
249 |
+
$panels = array_merge( $panels, $this->page_panels[ $page_as_screen ] );
|
250 |
+
}
|
251 |
+
}
|
252 |
+
return array_unique( $panels );
|
253 |
+
}
|
254 |
+
}
|
255 |
+
|
256 |
+
//All versions of the class are stored in a global array
|
257 |
+
//and only the latest version is actually used.
|
258 |
+
global $ws_screen_options_versions;
|
259 |
+
if ( ! isset( $ws_screen_options_versions ) ) {
|
260 |
+
$ws_screen_options_versions = array();
|
261 |
+
}
|
262 |
+
$ws_screen_options_versions['1.3'] = 'wsScreenOptions13';
|
263 |
+
|
264 |
+
endif;
|
265 |
+
|
266 |
+
if ( ! function_exists( 'add_screen_options_panel' ) ) {
|
267 |
+
|
268 |
+
/**
|
269 |
+
* Add a new settings panel to the "Screen Options" box.
|
270 |
+
*
|
271 |
+
* @see wsScreenOptions10::add_screen_options_panel()
|
272 |
+
*
|
273 |
+
* @param string $id String to use in the 'id' attribute of the settings panel. Should be unique.
|
274 |
+
* @param string $title Title of the settings panel. Set to an empty string to omit title.
|
275 |
+
* @param callback $callback Function that fills the panel with the desired content. Should return its output.
|
276 |
+
* @param string|array $page The page(s) on which to show the panel (similar to add_meta_box()).
|
277 |
+
* @param callback $save_callback Optional. Function that saves the settings contained in the panel.
|
278 |
+
* @param bool $autosave Optional. If set, settings will be automatically saved (via AJAX) when the value of any input element in the panel changes. Defaults to false.
|
279 |
+
* @return void
|
280 |
+
*/
|
281 |
+
function add_screen_options_panel( $id, $title, $callback, $page, $save_callback = null, $autosave = false ) {
|
282 |
+
global $ws_screen_options_versions;
|
283 |
+
|
284 |
+
static $instance = null; /** @var wsScreenOptions13 $instance */
|
285 |
+
if ( is_null( $instance ) ) {
|
286 |
+
//Instantiate the latest version of the wsScreenOptions class
|
287 |
+
uksort( $ws_screen_options_versions, 'version_compare' );
|
288 |
+
$className = end( $ws_screen_options_versions );
|
289 |
+
$instance = new $className;
|
290 |
+
$instance->init();
|
291 |
+
}
|
292 |
+
|
293 |
+
$instance->add_screen_options_panel( $id, $title, $callback, $page, $save_callback, $autosave );
|
294 |
+
}
|
295 |
+
}
|
296 |
+
|
|
includes/token-bucket.php
CHANGED
@@ -1,121 +1,126 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* This class implements a variant of the popular token bucket algorithm.
|
5 |
-
*/
|
6 |
-
class blcTokenBucketList {
|
7 |
-
const MICROSECONDS_PER_SECOND = 1000000;
|
8 |
-
|
9 |
-
/** @var float How many tokens each bucket can hold. */
|
10 |
-
private $capacity;
|
11 |
-
|
12 |
-
/** @var float How long it takes to completely fill a bucket (in seconds). */
|
13 |
-
private $fillTime;
|
14 |
-
|
15 |
-
/** @var float Minimum interval between taking tokens from a bucket (in seconds). */
|
16 |
-
private $minTakeInterval;
|
17 |
-
|
18 |
-
/** @var int How many buckets we can manage, in total. */
|
19 |
-
private $maxBuckets = 200;
|
20 |
-
|
21 |
-
private $buckets = array();
|
22 |
-
|
23 |
-
public function __construct($capacity, $fillTime, $minInterval = 0) {
|
24 |
-
$this->capacity
|
25 |
-
$this->fillTime
|
26 |
-
$this->minTakeInterval = $minInterval;
|
27 |
-
}
|
28 |
-
|
29 |
-
/**
|
30 |
-
* Take one token from a bucket.
|
31 |
-
* This method will block until a token becomes available.
|
32 |
-
*
|
33 |
-
* @param string $bucketName
|
34 |
-
*/
|
35 |
-
|
36 |
-
|
37 |
-
$this->
|
38 |
-
|
39 |
-
|
40 |
-
$this->buckets[$bucketName]['
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
*
|
46 |
-
*
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
$
|
52 |
-
|
53 |
-
|
54 |
-
$
|
55 |
-
|
56 |
-
|
57 |
-
$
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
*
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* This class implements a variant of the popular token bucket algorithm.
|
5 |
+
*/
|
6 |
+
class blcTokenBucketList {
|
7 |
+
const MICROSECONDS_PER_SECOND = 1000000;
|
8 |
+
|
9 |
+
/** @var float How many tokens each bucket can hold. */
|
10 |
+
private $capacity;
|
11 |
+
|
12 |
+
/** @var float How long it takes to completely fill a bucket (in seconds). */
|
13 |
+
private $fillTime; //phpcs:ignore WordPress.NamingConventions.ValidVariableName.PropertyNotSnakeCase
|
14 |
+
|
15 |
+
/** @var float Minimum interval between taking tokens from a bucket (in seconds). */
|
16 |
+
private $minTakeInterval; //phpcs:ignore WordPress.NamingConventions.ValidVariableName.PropertyNotSnakeCase
|
17 |
+
|
18 |
+
/** @var int How many buckets we can manage, in total. */
|
19 |
+
private $maxBuckets = 200; //phpcs:ignore WordPress.NamingConventions.ValidVariableName.PropertyNotSnakeCase
|
20 |
+
|
21 |
+
private $buckets = array();
|
22 |
+
|
23 |
+
public function __construct( $capacity, $fillTime, $minInterval = 0 ) {
|
24 |
+
$this->capacity = $capacity;
|
25 |
+
$this->fillTime = $fillTime;
|
26 |
+
$this->minTakeInterval = $minInterval;
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Take one token from a bucket.
|
31 |
+
* This method will block until a token becomes available.
|
32 |
+
*
|
33 |
+
* @param string $bucketName
|
34 |
+
*/
|
35 |
+
//phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
|
36 |
+
public function takeToken( $bucketName ) {
|
37 |
+
$this->createIfNotExists( $bucketName );
|
38 |
+
$this->waitForToken( $bucketName );
|
39 |
+
|
40 |
+
$this->buckets[ $bucketName ]['tokens']--;
|
41 |
+
$this->buckets[ $bucketName ]['lastTokenTakenAt'] = microtime( true );
|
42 |
+
}
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Wait until at a token is available.
|
46 |
+
*
|
47 |
+
* @param string $name Bucket name.
|
48 |
+
*/
|
49 |
+
//phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
|
50 |
+
private function waitForToken( $name ) {
|
51 |
+
$now = microtime( true );
|
52 |
+
|
53 |
+
$timeSinceLastToken = $now - $this->buckets[ $name ]['lastTokenTakenAt'];
|
54 |
+
$intervalWait = max( $this->minTakeInterval - $timeSinceLastToken, 0 );
|
55 |
+
|
56 |
+
$requiredTokens = max( 1 - $this->buckets[ $name ]['tokens'], 0 );
|
57 |
+
$refillWait = $requiredTokens / $this->getFillRate();
|
58 |
+
|
59 |
+
$totalWait = max( $intervalWait, $refillWait );
|
60 |
+
if ( $totalWait > 0 ) {
|
61 |
+
usleep( $totalWait * self::MICROSECONDS_PER_SECOND );
|
62 |
+
}
|
63 |
+
|
64 |
+
$this->refillBucket( $name );
|
65 |
+
return;
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Create a bucket if it doesn't exist yet.
|
70 |
+
*
|
71 |
+
* @param $name
|
72 |
+
*/
|
73 |
+
//phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
|
74 |
+
private function createIfNotExists( $name ) {
|
75 |
+
if ( ! isset( $this->buckets[ $name ] ) ) {
|
76 |
+
$this->buckets[ $name ] = array(
|
77 |
+
'tokens' => $this->capacity,
|
78 |
+
'lastRefill' => microtime( true ),
|
79 |
+
'lastTokenTakenAt' => 0,
|
80 |
+
);
|
81 |
+
}
|
82 |
+
//Make sure we don't exceed $maxBuckets.
|
83 |
+
$this->cleanup();
|
84 |
+
}
|
85 |
+
|
86 |
+
/**
|
87 |
+
* Calculate how quickly each bucket should be refilled.
|
88 |
+
*
|
89 |
+
* @return float Fill rate in tokens per second.
|
90 |
+
*/
|
91 |
+
//phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
|
92 |
+
private function getFillRate() {
|
93 |
+
return $this->capacity / $this->fillTime;
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
* Refill a bucket with fresh tokens.
|
98 |
+
*
|
99 |
+
* @param $name
|
100 |
+
*/
|
101 |
+
//phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
|
102 |
+
private function refillBucket( $name ) {
|
103 |
+
$now = microtime( true );
|
104 |
+
|
105 |
+
$timeSinceRefill = $now - $this->buckets[ $name ]['lastRefill'];
|
106 |
+
$this->buckets[ $name ]['tokens'] += $timeSinceRefill * $this->getFillRate();
|
107 |
+
|
108 |
+
if ( $this->buckets[ $name ]['tokens'] > $this->capacity ) {
|
109 |
+
$this->buckets[ $name ]['tokens'] = $this->capacity;
|
110 |
+
}
|
111 |
+
|
112 |
+
$this->buckets[ $name ]['lastRefill'] = $now;
|
113 |
+
}
|
114 |
+
|
115 |
+
/**
|
116 |
+
* Keep the number of active buckets within the $this->maxBuckets limit.
|
117 |
+
*/
|
118 |
+
private function cleanup() {
|
119 |
+
if ( $this->maxBuckets > 0 ) {
|
120 |
+
//Very simplistic implementation - just discard the oldest buckets.
|
121 |
+
while ( count( $this->buckets ) > $this->maxBuckets ) {
|
122 |
+
array_shift( $this->buckets );
|
123 |
+
}
|
124 |
+
}
|
125 |
+
}
|
126 |
+
}
|
includes/transactions-manager.php
CHANGED
@@ -1,45 +1,43 @@
|
|
1 |
<?php
|
2 |
|
3 |
-
class TransactionManager
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
{
|
20 |
-
global $wpdb;
|
21 |
global $blclog;
|
22 |
-
$blclog->debug('Starting DB commit.');
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
$blclog->debug('Commit executed.');
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
$blclog->debug('Commit failed; rollback.');
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
}
|
1 |
<?php
|
2 |
|
3 |
+
class TransactionManager {
|
4 |
+
|
5 |
+
private $isTransactionStarted = false; //phpcs:ignore WordPress.NamingConventions.ValidVariableName.PropertyNotSnakeCase
|
6 |
+
private static $instance;
|
7 |
+
|
8 |
+
public function start() {
|
9 |
+
global $wpdb;
|
10 |
+
|
11 |
+
if ( ! $this->isTransactionStarted ) {
|
12 |
+
$wpdb->query( 'BEGIN' );
|
13 |
+
$this->isTransactionStarted = true;
|
14 |
+
}
|
15 |
+
}
|
16 |
+
|
17 |
+
public function commit() {
|
18 |
+
global $wpdb;
|
|
|
|
|
19 |
global $blclog;
|
20 |
+
$blclog->debug( 'Starting DB commit.' );
|
21 |
+
|
22 |
+
$this->start();
|
23 |
+
|
24 |
+
try {
|
25 |
+
$wpdb->query( 'COMMIT' );
|
26 |
+
$blclog->debug( 'Commit executed.' );
|
27 |
+
$this->isTransactionStarted = false;
|
28 |
+
} catch ( Exception $e ) {
|
29 |
+
$wpdb->query( 'ROLLBACK' );
|
30 |
+
$blclog->debug( 'Commit failed; rollback.' );
|
31 |
+
$this->isTransactionStarted = false;
|
32 |
+
}
|
33 |
+
}
|
34 |
+
|
35 |
+
//phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
|
36 |
+
static public function getInstance() {
|
37 |
+
if ( ! self::$instance ) {
|
38 |
+
self::$instance = new TransactionManager();
|
39 |
+
}
|
40 |
+
|
41 |
+
return self::$instance;
|
42 |
+
}
|
43 |
}
|
includes/utility-class.php
CHANGED
@@ -1,417 +1,419 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* @author W-Shadow
|
5 |
-
* @copyright 2010
|
6 |
-
*/
|
7 |
-
|
8 |
-
if ( ! function_exists( 'sys_get_temp_dir' ) ) {
|
9 |
-
function sys_get_temp_dir() {
|
10 |
-
if ( ! empty( $_ENV['TMP'] ) ) {
|
11 |
-
|
12 |
-
if ( ! empty( $_ENV['
|
13 |
-
|
14 |
-
if (
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
}
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
*
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
case '
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
return
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
*
|
107 |
-
*
|
108 |
-
*
|
109 |
-
*
|
110 |
-
*
|
111 |
-
*
|
112 |
-
*
|
113 |
-
*
|
114 |
-
*
|
115 |
-
*
|
116 |
-
*
|
117 |
-
*
|
118 |
-
*
|
119 |
-
*
|
120 |
-
*
|
121 |
-
*
|
122 |
-
* @param
|
123 |
-
* @param string $
|
124 |
-
*
|
125 |
-
* @
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
'
|
151 |
-
|
152 |
-
|
153 |
-
(?P<
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
'
|
208 |
-
'
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
*
|
224 |
-
*
|
225 |
-
* @
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
return $
|
232 |
-
}
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
$
|
275 |
-
|
276 |
-
|
277 |
-
$
|
278 |
-
|
279 |
-
|
280 |
-
$
|
281 |
-
|
282 |
-
|
283 |
-
$
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
$
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
$wpdb
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
$
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
*
|
346 |
-
*
|
347 |
-
* @
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
*
|
374 |
-
*
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
*
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
*
|
402 |
-
*
|
403 |
-
* @
|
404 |
-
* @param
|
405 |
-
* @
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
$
|
411 |
-
$
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
}//
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @author W-Shadow
|
5 |
+
* @copyright 2010
|
6 |
+
*/
|
7 |
+
|
8 |
+
if ( ! function_exists( 'sys_get_temp_dir' ) ) {
|
9 |
+
function sys_get_temp_dir() {
|
10 |
+
if ( ! empty( $_ENV['TMP'] ) ) {
|
11 |
+
return realpath( $_ENV['TMP'] ); }
|
12 |
+
if ( ! empty( $_ENV['TMPDIR'] ) ) {
|
13 |
+
return realpath( $_ENV['TMPDIR'] ); }
|
14 |
+
if ( ! empty( $_ENV['TEMP'] ) ) {
|
15 |
+
return realpath( $_ENV['TEMP'] ); }
|
16 |
+
$tempfile = tempnam( uniqid( rand(), true ), '' );
|
17 |
+
if ( @file_exists( $tempfile ) ) {
|
18 |
+
unlink( $tempfile );
|
19 |
+
return realpath( dirname( $tempfile ) );
|
20 |
+
}
|
21 |
+
return '';
|
22 |
+
}
|
23 |
+
}
|
24 |
+
|
25 |
+
//Include the internationalized domain name converter (requires PHP 5)
|
26 |
+
if ( version_compare( phpversion(), '5.0.0', '>=' ) && ! class_exists( 'idna_convert' ) ) {
|
27 |
+
include BLC_DIRECTORY . '/idn/idna_convert.class.php';
|
28 |
+
if ( ! function_exists( 'encode_utf8' ) ) {
|
29 |
+
include BLC_DIRECTORY . '/idn/transcode_wrapper.php';
|
30 |
+
}
|
31 |
+
}
|
32 |
+
|
33 |
+
|
34 |
+
if ( ! class_exists( 'blcUtility' ) ) {
|
35 |
+
class blcUtility {
|
36 |
+
/**
|
37 |
+
* Checks if PHP is running in safe mode
|
38 |
+
* blcUtility::is_safe_mode()
|
39 |
+
*
|
40 |
+
* @return bool
|
41 |
+
*/
|
42 |
+
static function is_safe_mode() {
|
43 |
+
// Check php.ini safe_mode only if PHP version is lower than 5.3.0, else set to false.
|
44 |
+
if ( version_compare( phpversion(), '5.3.0', '<' ) ) {
|
45 |
+
$safe_mode = ini_get( 'safe_mode' );
|
46 |
+
} else {
|
47 |
+
$safe_mode = false;
|
48 |
+
}
|
49 |
+
|
50 |
+
// Null, 0, '', '0' and so on count as false.
|
51 |
+
if ( ! $safe_mode ) {
|
52 |
+
return false;
|
53 |
+
}
|
54 |
+
// Test for some textual true/false variations.
|
55 |
+
switch ( strtolower( $safe_mode ) ) {
|
56 |
+
case 'on':
|
57 |
+
case 'true':
|
58 |
+
case 'yes':
|
59 |
+
return true;
|
60 |
+
|
61 |
+
case 'off':
|
62 |
+
case 'false':
|
63 |
+
case 'no':
|
64 |
+
return false;
|
65 |
+
|
66 |
+
default: // Let PHP handle anything else.
|
67 |
+
return (bool) (int) $safe_mode;
|
68 |
+
}
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* blcUtility::is_open_basedir()
|
73 |
+
* Checks if open_basedir is enabled
|
74 |
+
*
|
75 |
+
* @return bool
|
76 |
+
*/
|
77 |
+
static function is_open_basedir() {
|
78 |
+
$open_basedir = ini_get( 'open_basedir' );
|
79 |
+
return $open_basedir && ( strtolower( $open_basedir ) != 'none' );
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Truncate a string on a specified boundary character.
|
84 |
+
*
|
85 |
+
* @param string $text The text to truncate.
|
86 |
+
* @param integer $max_characters Return no more than $max_characters
|
87 |
+
* @param string $break Break on this character. Defaults to space.
|
88 |
+
* @param string $pad Pad the truncated string with this string. Defaults to an HTML ellipsis.
|
89 |
+
* @return string
|
90 |
+
*/
|
91 |
+
static function truncate( $text, $max_characters = 0, $break = ' ', $pad = '…' ) {
|
92 |
+
if ( strlen( $text ) <= $max_characters ) {
|
93 |
+
return $text;
|
94 |
+
}
|
95 |
+
|
96 |
+
$text = substr( $text, 0, $max_characters );
|
97 |
+
$break_pos = strrpos( $text, $break );
|
98 |
+
if ( false !== $break_pos ) {
|
99 |
+
$text = substr( $text, 0, $break_pos );
|
100 |
+
}
|
101 |
+
|
102 |
+
return $text . $pad;
|
103 |
+
}
|
104 |
+
|
105 |
+
/**
|
106 |
+
* extract_tags()
|
107 |
+
* Extract specific HTML tags and their attributes from a string.
|
108 |
+
*
|
109 |
+
* You can either specify one tag, an array of tag names, or a regular expression that matches the tag name(s).
|
110 |
+
* If multiple tags are specified you must also set the $selfclosing parameter and it must be the same for
|
111 |
+
* all specified tags (so you can't extract both normal and self-closing tags in one go).
|
112 |
+
*
|
113 |
+
* The function returns a numerically indexed array of extracted tags. Each entry is an associative array
|
114 |
+
* with these keys :
|
115 |
+
* tag_name - the name of the extracted tag, e.g. "a" or "img".
|
116 |
+
* offset - the numberic offset of the first character of the tag within the HTML source.
|
117 |
+
* contents - the inner HTML of the tag. This is always empty for self-closing tags.
|
118 |
+
* attributes - a name -> value array of the tag's attributes, or an empty array if the tag has none.
|
119 |
+
* full_tag - the entire matched tag, e.g. '<a href="http://example.com">example.com</a>'. This key
|
120 |
+
* will only be present if you set $return_the_entire_tag to true.
|
121 |
+
*
|
122 |
+
* @param string $html The HTML code to search for tags.
|
123 |
+
* @param string|array $tag The tag(s) to extract.
|
124 |
+
* @param bool $selfclosing Whether the tag is self-closing or not. Setting it to null will force the script to try and make an educated guess.
|
125 |
+
* @param bool $return_the_entire_tag Return the entire matched tag in 'full_tag' key of the results array.
|
126 |
+
* @param string $charset The character set of the HTML code. Defaults to ISO-8859-1.
|
127 |
+
*
|
128 |
+
* @return array An array of extracted tags, or an empty array if no matching tags were found.
|
129 |
+
*/
|
130 |
+
static function extract_tags( $html, $tag, $selfclosing = null, $return_the_entire_tag = false, $charset = 'ISO-8859-1' ) {
|
131 |
+
|
132 |
+
if ( is_array( $tag ) ) {
|
133 |
+
$tag = implode( '|', $tag );
|
134 |
+
}
|
135 |
+
|
136 |
+
//If the user didn't specify if $tag is a self-closing tag we try to auto-detect it
|
137 |
+
//by checking against a list of known self-closing tags.
|
138 |
+
$selfclosing_tags = array( 'area', 'base', 'basefont', 'br', 'hr', 'input', 'img', 'link', 'meta', 'col', 'param' );
|
139 |
+
if ( is_null( $selfclosing ) ) {
|
140 |
+
$selfclosing = in_array( $tag, $selfclosing_tags );
|
141 |
+
}
|
142 |
+
|
143 |
+
//The regexp is different for normal and self-closing tags because I can't figure out
|
144 |
+
//how to make a sufficiently robust unified one.
|
145 |
+
if ( $selfclosing ) {
|
146 |
+
$tag_pattern =
|
147 |
+
'@<(?P<tag>' . $tag . ') # <tag
|
148 |
+
(?P<attributes>\s[^>]+)? # attributes, if any
|
149 |
+
\s*/?> # /> or just >, being lenient here
|
150 |
+
@xsi';
|
151 |
+
} else {
|
152 |
+
$tag_pattern =
|
153 |
+
'@<(?P<tag>' . $tag . ') # <tag
|
154 |
+
(?P<attributes>\s[^>]+)? # attributes, if any
|
155 |
+
\s*> # >
|
156 |
+
(?P<contents>.*?) # tag contents
|
157 |
+
</(?P=tag)> # the closing </tag>
|
158 |
+
@xsi';
|
159 |
+
}
|
160 |
+
|
161 |
+
$attribute_pattern =
|
162 |
+
'@
|
163 |
+
(?P<name>\w+) # attribute name
|
164 |
+
\s*=\s*
|
165 |
+
(
|
166 |
+
(?P<quote>[\"\'])(?P<value_quoted>.*?)(?P=quote) # a quoted value
|
167 |
+
| # or
|
168 |
+
(?P<value_unquoted>[^\s"\']+?)(?:\s+|$) # an unquoted value (terminated by whitespace or EOF)
|
169 |
+
)
|
170 |
+
@xsi';
|
171 |
+
|
172 |
+
//Find all tags
|
173 |
+
if ( ! preg_match_all( $tag_pattern, $html, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE ) ) {
|
174 |
+
//Return an empty array if we didn't find anything
|
175 |
+
return array();
|
176 |
+
}
|
177 |
+
|
178 |
+
$tags = array();
|
179 |
+
foreach ( $matches as $match ) {
|
180 |
+
|
181 |
+
// Parse tag attributes, if any.
|
182 |
+
$attributes = array();
|
183 |
+
if ( ! empty( $match['attributes'][0] ) ) {
|
184 |
+
|
185 |
+
if ( preg_match_all( $attribute_pattern, $match['attributes'][0], $attribute_data, PREG_SET_ORDER ) ) {
|
186 |
+
//Turn the attribute data into a name->value array
|
187 |
+
foreach ( $attribute_data as $attr ) {
|
188 |
+
if ( ! empty( $attr['value_quoted'] ) ) {
|
189 |
+
$value = $attr['value_quoted'];
|
190 |
+
} elseif ( ! empty( $attr['value_unquoted'] ) ) {
|
191 |
+
$value = $attr['value_unquoted'];
|
192 |
+
} else {
|
193 |
+
$value = '';
|
194 |
+
}
|
195 |
+
|
196 |
+
// Passing the value through html_entity_decode is handy when you want
|
197 |
+
// to extract link URLs or something like that. You might want to remove
|
198 |
+
// or modify this call if it doesn't fit your situation.
|
199 |
+
$value = html_entity_decode( $value, ENT_QUOTES, $charset );
|
200 |
+
|
201 |
+
$attributes[ $attr['name'] ] = $value;
|
202 |
+
}
|
203 |
+
}
|
204 |
+
}
|
205 |
+
|
206 |
+
$tag = array(
|
207 |
+
'tag_name' => $match['tag'][0],
|
208 |
+
'offset' => $match[0][1],
|
209 |
+
'contents' => ! empty( $match['contents'] ) ? $match['contents'][0] : '', // Empty for self-closing tags.
|
210 |
+
'attributes' => $attributes,
|
211 |
+
);
|
212 |
+
if ( $return_the_entire_tag ) {
|
213 |
+
$tag['full_tag'] = $match[0][0];
|
214 |
+
}
|
215 |
+
|
216 |
+
$tags[] = $tag;
|
217 |
+
}
|
218 |
+
|
219 |
+
return $tags;
|
220 |
+
}
|
221 |
+
|
222 |
+
/**
|
223 |
+
* Get the value of a cookie.
|
224 |
+
*
|
225 |
+
* @param string $cookie_name The name of the cookie to return.
|
226 |
+
* @param string $default_value Optional. If the cookie is not set, this value will be returned instead. Defaults to an empty string.
|
227 |
+
* @return mixed Either the value of the requested cookie, or $default_value.
|
228 |
+
*/
|
229 |
+
static function get_cookie( $cookie_name, $default_value = '' ) {
|
230 |
+
if ( isset( $_COOKIE[ $cookie_name ] ) ) {
|
231 |
+
return $_COOKIE[ $cookie_name ];
|
232 |
+
} else {
|
233 |
+
return $default_value;
|
234 |
+
}
|
235 |
+
}
|
236 |
+
|
237 |
+
/**
|
238 |
+
* Format a time delta using a fuzzy format, e.g. '2 minutes ago', '2 days', etc.
|
239 |
+
*
|
240 |
+
* @param int $delta Time period in seconds.
|
241 |
+
* @param string $type Optional. The output template to use.
|
242 |
+
* @return string
|
243 |
+
*/
|
244 |
+
static function fuzzy_delta( $delta, $template = 'default' ) {
|
245 |
+
|
246 |
+
$templates = array(
|
247 |
+
'seconds' => array(
|
248 |
+
'default' => _n_noop( '%d second', '%d seconds' ),
|
249 |
+
'ago' => _n_noop( '%d second ago', '%d seconds ago' ),
|
250 |
+
),
|
251 |
+
'minutes' => array(
|
252 |
+
'default' => _n_noop( '%d minute', '%d minutes' ),
|
253 |
+
'ago' => _n_noop( '%d minute ago', '%d minutes ago' ),
|
254 |
+
),
|
255 |
+
'hours' => array(
|
256 |
+
'default' => _n_noop( '%d hour', '%d hours' ),
|
257 |
+
'ago' => _n_noop( '%d hour ago', '%d hours ago' ),
|
258 |
+
),
|
259 |
+
'days' => array(
|
260 |
+
'default' => _n_noop( '%d day', '%d days' ),
|
261 |
+
'ago' => _n_noop( '%d day ago', '%d days ago' ),
|
262 |
+
),
|
263 |
+
'months' => array(
|
264 |
+
'default' => _n_noop( '%d month', '%d months' ),
|
265 |
+
'ago' => _n_noop( '%d month ago', '%d months ago' ),
|
266 |
+
),
|
267 |
+
);
|
268 |
+
|
269 |
+
if ( $delta < 1 ) {
|
270 |
+
$delta = 1;
|
271 |
+
}
|
272 |
+
|
273 |
+
if ( $delta < MINUTE_IN_SECONDS ) {
|
274 |
+
$units = 'seconds';
|
275 |
+
} elseif ( $delta < HOUR_IN_SECONDS ) {
|
276 |
+
$delta = intval( $delta / MINUTE_IN_SECONDS );
|
277 |
+
$units = 'minutes';
|
278 |
+
} elseif ( $delta < DAY_IN_SECONDS ) {
|
279 |
+
$delta = intval( $delta / HOUR_IN_SECONDS );
|
280 |
+
$units = 'hours';
|
281 |
+
} elseif ( $delta < MONTH_IN_SECONDS ) {
|
282 |
+
$delta = intval( $delta / DAY_IN_SECONDS );
|
283 |
+
$units = 'days';
|
284 |
+
} else {
|
285 |
+
$delta = intval( $delta / MONTH_IN_SECONDS );
|
286 |
+
$units = 'months';
|
287 |
+
}
|
288 |
+
|
289 |
+
return sprintf(
|
290 |
+
_n(
|
291 |
+
$templates[ $units ][ $template ][0], //phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralSingle
|
292 |
+
$templates[ $units ][ $template ][1], //phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralPlural
|
293 |
+
$delta,
|
294 |
+
'broken-link-checker'
|
295 |
+
),
|
296 |
+
$delta
|
297 |
+
);
|
298 |
+
}
|
299 |
+
|
300 |
+
/**
|
301 |
+
* Optimize the plugin's tables
|
302 |
+
*
|
303 |
+
* @return void
|
304 |
+
*/
|
305 |
+
static function optimize_database() {
|
306 |
+
global $wpdb; /** @var wpdb $wpdb */
|
307 |
+
|
308 |
+
$wpdb->query( "OPTIMIZE TABLE {$wpdb->prefix}blc_links, {$wpdb->prefix}blc_instances, {$wpdb->prefix}blc_synch" );
|
309 |
+
}
|
310 |
+
|
311 |
+
/**
|
312 |
+
* Get the server's load averages.
|
313 |
+
*
|
314 |
+
* Returns an array with three samples - the 1 minute avg, the 5 minute avg, and the 15 minute avg.
|
315 |
+
*
|
316 |
+
* @param integer $cache How long the load averages may be cached, in seconds. Set to 0 to get maximally up-to-date data.
|
317 |
+
* @return array|null Array, or NULL if retrieving load data is impossible (e.g. when running on a Windows box).
|
318 |
+
*/
|
319 |
+
static function get_server_load( $cache = 5 ) {
|
320 |
+
static $cached_load = null;
|
321 |
+
static $cached_when = 0;
|
322 |
+
|
323 |
+
if ( ! empty( $cache ) && ( ( time() - $cached_when ) <= $cache ) ) {
|
324 |
+
return $cached_load;
|
325 |
+
}
|
326 |
+
|
327 |
+
$load = null;
|
328 |
+
|
329 |
+
if ( function_exists( 'sys_getloadavg' ) ) {
|
330 |
+
$load = sys_getloadavg();
|
331 |
+
} else {
|
332 |
+
$loadavg_file = '/proc/loadavg';
|
333 |
+
if ( @is_readable( $loadavg_file ) ) {
|
334 |
+
$load = explode( ' ', file_get_contents( $loadavg_file ) );
|
335 |
+
$load = array_map( 'floatval', $load );
|
336 |
+
}
|
337 |
+
}
|
338 |
+
|
339 |
+
$cached_load = $load;
|
340 |
+
$cached_when = time();
|
341 |
+
return $load;
|
342 |
+
}
|
343 |
+
|
344 |
+
/**
|
345 |
+
* Convert an internationalized domain name or URL to ASCII-compatible encoding.
|
346 |
+
*
|
347 |
+
* @param string $url Either a domain name or a complete URL.
|
348 |
+
* @param string $charset The character encoding of the $url parameter. Defaults to the encoding set in Settings -> Reading.
|
349 |
+
* @return string
|
350 |
+
*/
|
351 |
+
static function idn_to_ascii( $url, $charset = '' ) {
|
352 |
+
$idn = blcUtility::get_idna_converter();
|
353 |
+
if ( null != $idn ) {
|
354 |
+
if ( empty( $charset ) ) {
|
355 |
+
$charset = get_bloginfo( 'charset' );
|
356 |
+
}
|
357 |
+
|
358 |
+
// Encode only the host.
|
359 |
+
if ( preg_match( '@(\w+:/*)?([^/:]+)(.*$)?@s', $url, $matches ) ) {
|
360 |
+
$host = $matches[2];
|
361 |
+
if ( ( strtoupper( $charset ) != 'UTF-8' ) && ( strtoupper( $charset ) != 'UTF8' ) ) {
|
362 |
+
$host = encode_utf8( $host, $charset, true );
|
363 |
+
}
|
364 |
+
$host = $idn->encode( $host );
|
365 |
+
$url = $matches[1] . $host . $matches[3];
|
366 |
+
}
|
367 |
+
}
|
368 |
+
|
369 |
+
return $url;
|
370 |
+
}
|
371 |
+
|
372 |
+
/**
|
373 |
+
* Convert an internationalized domain name (or URL) from ASCII-compatible encoding to UTF8.
|
374 |
+
*
|
375 |
+
* @param string $url
|
376 |
+
* @return string
|
377 |
+
*/
|
378 |
+
static function idn_to_utf8( $url ) {
|
379 |
+
$idn = blcUtility::get_idna_converter();
|
380 |
+
if ( null !== $idn ) {
|
381 |
+
$url = $idn->decode( $url );
|
382 |
+
}
|
383 |
+
|
384 |
+
return $url;
|
385 |
+
}
|
386 |
+
|
387 |
+
/**
|
388 |
+
* Get an instance of idna_converter
|
389 |
+
*
|
390 |
+
* @return idna_convert|null Either an instance of IDNA converter, or NULL if the converter class is not available
|
391 |
+
*/
|
392 |
+
static function get_idna_converter() {
|
393 |
+
static $idn = null;
|
394 |
+
if ( ( null === $idn ) && class_exists( 'idna_convert' ) ) {
|
395 |
+
$idn = new idna_convert();
|
396 |
+
}
|
397 |
+
return $idn;
|
398 |
+
}
|
399 |
+
|
400 |
+
/**
|
401 |
+
* Generate a numeric hash from a string. The result will be constrained to the specified interval.
|
402 |
+
*
|
403 |
+
* @static
|
404 |
+
* @param string $input
|
405 |
+
* @param int $min
|
406 |
+
* @param int $max
|
407 |
+
* @return float
|
408 |
+
*/
|
409 |
+
public static function constrained_hash( $input, $min = 0, $max = 1 ) {
|
410 |
+
$bytes_to_use = 3;
|
411 |
+
$md5_char_count = 32;
|
412 |
+
$hash = substr( md5( $input ), $md5_char_count - $bytes_to_use * 2 );
|
413 |
+
$hash = intval( hexdec( $hash ) );
|
414 |
+
return $min + ( ( $max - $min ) * ( $hash / ( pow( 2, $bytes_to_use * 8 ) - 1 ) ) );
|
415 |
+
}
|
416 |
+
|
417 |
+
}//class
|
418 |
+
|
419 |
+
}//class_exists
|
includes/wp-mutex.php
CHANGED
@@ -1,57 +1,57 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
if ( !class_exists('WPMutex') ):
|
4 |
-
|
5 |
-
class WPMutex {
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
}
|
55 |
-
|
56 |
-
endif;
|
57 |
-
|
1 |
+
<?php
|
2 |
+
|
3 |
+
if ( ! class_exists( 'WPMutex' ) ) :
|
4 |
+
|
5 |
+
class WPMutex {
|
6 |
+
/**
|
7 |
+
* Get an exclusive named lock.
|
8 |
+
*
|
9 |
+
* @param string $name
|
10 |
+
* @param integer $timeout
|
11 |
+
* @param bool $network_wide
|
12 |
+
* @return bool
|
13 |
+
*/
|
14 |
+
static function acquire( $name, $timeout = 0, $network_wide = false ) {
|
15 |
+
global $wpdb; /* @var wpdb $wpdb */
|
16 |
+
if ( ! $network_wide ) {
|
17 |
+
$name = WPMutex::_get_private_name( $name );
|
18 |
+
}
|
19 |
+
$state = $wpdb->get_var( $wpdb->prepare( 'SELECT GET_LOCK(%s, %d)', $name, $timeout ) );
|
20 |
+
return 1 == $state;
|
21 |
+
}
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Release a named lock.
|
25 |
+
*
|
26 |
+
* @param string $name
|
27 |
+
* @param bool $network_wide
|
28 |
+
* @return bool
|
29 |
+
*/
|
30 |
+
static function release( $name, $network_wide = false ) {
|
31 |
+
global $wpdb; /* @var wpdb $wpdb */
|
32 |
+
if ( ! $network_wide ) {
|
33 |
+
$name = WPMutex::_get_private_name( $name );
|
34 |
+
}
|
35 |
+
$released = $wpdb->get_var( $wpdb->prepare( 'SELECT RELEASE_LOCK(%s)', $name ) );
|
36 |
+
return 1 == $released;
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Given a generic lock name, create a new one that's unique to the current blog.
|
41 |
+
*
|
42 |
+
* @access private
|
43 |
+
*
|
44 |
+
* @param string $name
|
45 |
+
* @return string
|
46 |
+
*/
|
47 |
+
static function _get_private_name( $name ) {
|
48 |
+
global $current_blog;
|
49 |
+
if ( function_exists( 'is_multisite' ) && is_multisite() && isset( $current_blog->blog_id ) ) {
|
50 |
+
$name .= '-blog-' . $current_blog->blog_id;
|
51 |
+
}
|
52 |
+
return $name;
|
53 |
+
}
|
54 |
+
}
|
55 |
+
|
56 |
+
endif;
|
57 |
+
|
languages/broken-link-checker.pot
CHANGED
@@ -1,8 +1,8 @@
|
|
1 |
-
# Copyright (C)
|
2 |
-
# This file is distributed under the same license as the Broken Link Checker 1.11.
|
3 |
msgid ""
|
4 |
msgstr ""
|
5 |
-
"Project-Id-Version: Broken Link Checker 1.11.
|
6 |
"MIME-Version: 1.0\n"
|
7 |
"Content-Type: text/plain; charset=UTF-8\n"
|
8 |
"Content-Transfer-Encoding: 8bit\n"
|
@@ -13,814 +13,814 @@ msgstr ""
|
|
13 |
"X-Poedit-SourceCharset: UTF-8\n"
|
14 |
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
15 |
|
16 |
-
#: core/core.php:
|
17 |
msgid "Loading..."
|
18 |
msgstr ""
|
19 |
|
20 |
-
#: core/core.php:
|
21 |
msgid "[ Network error ]"
|
22 |
msgstr ""
|
23 |
|
24 |
-
#: core/core.php:
|
25 |
msgid "Automatically expand the widget if broken links have been detected"
|
26 |
msgstr ""
|
27 |
|
28 |
-
#: core/core.php:
|
29 |
msgid "Link Checker Settings"
|
30 |
msgstr ""
|
31 |
|
32 |
-
#: core/core.php:
|
33 |
msgid "Link Checker"
|
34 |
msgstr ""
|
35 |
|
36 |
-
#: core/core.php:
|
37 |
msgid "Broken Links"
|
38 |
msgstr ""
|
39 |
|
40 |
-
#: core/core.php:
|
41 |
msgid "View Broken Links"
|
42 |
msgstr ""
|
43 |
|
44 |
-
#: core/core.php:
|
45 |
msgid "Go to Broken Links"
|
46 |
msgstr ""
|
47 |
|
48 |
-
#: core/core.php:
|
49 |
msgid "Settings"
|
50 |
msgstr ""
|
51 |
|
52 |
-
#: core/core.php:
|
53 |
msgid "Edit URL"
|
54 |
msgstr ""
|
55 |
|
56 |
-
#: core/core.php:
|
57 |
msgid "Unlink"
|
58 |
msgstr ""
|
59 |
|
60 |
-
#: core/core.php:
|
61 |
msgid "Not broken"
|
62 |
msgstr ""
|
63 |
|
64 |
-
#: core/core.php:
|
65 |
msgid "Dismiss"
|
66 |
msgstr ""
|
67 |
|
68 |
-
#: core/core.php:
|
69 |
msgid "Recheck"
|
70 |
msgstr ""
|
71 |
|
72 |
-
#: core/core.php:
|
73 |
msgctxt "link action; replace one redirect with a direct link"
|
74 |
msgid "Fix redirect"
|
75 |
msgstr ""
|
76 |
|
77 |
-
#: core/core.php:
|
78 |
msgid "Settings saved."
|
79 |
msgstr ""
|
80 |
|
81 |
-
#: core/core.php:
|
82 |
msgid "Thank you for your donation!"
|
83 |
msgstr ""
|
84 |
|
85 |
-
#: core/core.php:
|
86 |
msgid "Complete site recheck started."
|
87 |
msgstr ""
|
88 |
|
89 |
-
#: core/core.php:
|
90 |
msgid "General"
|
91 |
msgstr ""
|
92 |
|
93 |
-
#: core/core.php:
|
94 |
msgid "Look For Links In"
|
95 |
msgstr ""
|
96 |
|
97 |
-
#: core/core.php:
|
98 |
msgid "Which Links To Check"
|
99 |
msgstr ""
|
100 |
|
101 |
-
#: core/core.php:
|
102 |
msgid "Protocols & APIs"
|
103 |
msgstr ""
|
104 |
|
105 |
-
#: core/core.php:
|
106 |
msgid "Advanced"
|
107 |
msgstr ""
|
108 |
|
109 |
-
#: core/core.php:
|
110 |
msgid "Broken Link Checker Options"
|
111 |
msgstr ""
|
112 |
|
113 |
-
#: core/core.php:
|
114 |
msgid "Status"
|
115 |
msgstr ""
|
116 |
|
117 |
-
#: core/core.php:
|
118 |
msgid "Show debug info"
|
119 |
msgstr ""
|
120 |
|
121 |
-
#: core/core.php:
|
122 |
msgid "Check each link"
|
123 |
msgstr ""
|
124 |
|
125 |
-
#: core/core.php:
|
126 |
msgid "Every %s hours"
|
127 |
msgstr ""
|
128 |
|
129 |
-
#: core/core.php:
|
130 |
msgid "Existing links will be checked this often. New links will usually be checked ASAP."
|
131 |
msgstr ""
|
132 |
|
133 |
-
#: core/core.php:
|
134 |
msgid "E-mail notifications"
|
135 |
msgstr ""
|
136 |
|
137 |
-
#: core/core.php:
|
138 |
msgid "Send me e-mail notifications about newly detected broken links"
|
139 |
msgstr ""
|
140 |
|
141 |
-
#: core/core.php:
|
142 |
msgid "Send authors e-mail notifications about broken links in their posts"
|
143 |
msgstr ""
|
144 |
|
145 |
-
#: core/core.php:
|
146 |
msgid "Notification e-mail address"
|
147 |
msgstr ""
|
148 |
|
149 |
-
#: core/core.php:
|
150 |
msgid "Leave empty to use the e-mail address specified in Settings → General."
|
151 |
msgstr ""
|
152 |
|
153 |
-
#: core/core.php:
|
154 |
msgid "Link tweaks"
|
155 |
msgstr ""
|
156 |
|
157 |
-
#: core/core.php:
|
158 |
msgid "Apply custom formatting to broken links"
|
159 |
msgstr ""
|
160 |
|
161 |
-
#: core/core.php:
|
162 |
msgid "Edit CSS"
|
163 |
msgstr ""
|
164 |
|
165 |
-
#: core/core.php:
|
166 |
msgid "Example : Lorem ipsum <a %s>broken link</a>, dolor sit amet."
|
167 |
msgstr ""
|
168 |
|
169 |
-
#: core/core.php:
|
170 |
msgid "Click \"Save Changes\" to update example output."
|
171 |
msgstr ""
|
172 |
|
173 |
-
#: core/core.php:
|
174 |
msgid "Apply custom formatting to removed links"
|
175 |
msgstr ""
|
176 |
|
177 |
-
#: core/core.php:
|
178 |
msgid "Example : Lorem ipsum <span %s>removed link</span>, dolor sit amet."
|
179 |
msgstr ""
|
180 |
|
181 |
-
#: core/core.php:
|
182 |
msgid "Stop search engines from following broken links"
|
183 |
msgstr ""
|
184 |
|
185 |
-
#: core/core.php:
|
186 |
msgctxt "\"Link tweaks\" settings"
|
187 |
msgid "These settings only apply to the content of posts, not comments or custom fields."
|
188 |
msgstr ""
|
189 |
|
190 |
-
#: core/core.php:
|
191 |
msgctxt "settings page"
|
192 |
msgid "Suggestions"
|
193 |
msgstr ""
|
194 |
|
195 |
-
#: core/core.php:
|
196 |
msgid "Suggest alternatives to broken links"
|
197 |
msgstr ""
|
198 |
|
199 |
-
#: core/core.php:
|
200 |
msgctxt "settings page"
|
201 |
msgid "Warnings"
|
202 |
msgstr ""
|
203 |
|
204 |
-
#: core/core.php:
|
205 |
msgid "Show uncertain or minor problems as \"warnings\" instead of \"broken\""
|
206 |
msgstr ""
|
207 |
|
208 |
-
#: core/core.php:
|
209 |
msgid "Turning off this option will make the plugin report all problems as broken links."
|
210 |
msgstr ""
|
211 |
|
212 |
-
#: core/core.php:
|
213 |
msgid "Look for links in"
|
214 |
msgstr ""
|
215 |
|
216 |
-
#: core/core.php:
|
217 |
msgid "Post statuses"
|
218 |
msgstr ""
|
219 |
|
220 |
-
#: core/core.php:
|
221 |
msgid "Link types"
|
222 |
msgstr ""
|
223 |
|
224 |
-
#: core/core.php:
|
225 |
msgid "Error : All link parsers missing!"
|
226 |
msgstr ""
|
227 |
|
228 |
-
#: core/core.php:
|
229 |
msgid "Exclusion list"
|
230 |
msgstr ""
|
231 |
|
232 |
-
#: core/core.php:
|
233 |
msgid "Don't check links where the URL contains any of these words (one per line) :"
|
234 |
msgstr ""
|
235 |
|
236 |
-
#: core/core.php:
|
237 |
msgid "Check links using"
|
238 |
msgstr ""
|
239 |
|
240 |
-
#: core/core.php:
|
241 |
msgid "Timeout"
|
242 |
msgstr ""
|
243 |
|
244 |
-
#: core/core.php:
|
245 |
msgid "%s seconds"
|
246 |
msgstr ""
|
247 |
|
248 |
-
#: core/core.php:
|
249 |
msgid "Links that take longer than this to load will be marked as broken."
|
250 |
msgstr ""
|
251 |
|
252 |
-
#: core/core.php:
|
253 |
msgid "Link monitor"
|
254 |
msgstr ""
|
255 |
|
256 |
-
#: core/core.php:
|
257 |
msgid "Run continuously while the Dashboard is open"
|
258 |
msgstr ""
|
259 |
|
260 |
-
#: core/core.php:
|
261 |
msgid "Run hourly in the background"
|
262 |
msgstr ""
|
263 |
|
264 |
-
#: core/core.php:
|
265 |
msgid "Show the dashboard widget for"
|
266 |
msgstr ""
|
267 |
|
268 |
-
#: core/core.php:
|
269 |
msgctxt "dashboard widget visibility"
|
270 |
msgid "Administrator"
|
271 |
msgstr ""
|
272 |
|
273 |
-
#: core/core.php:
|
274 |
msgctxt "dashboard widget visibility"
|
275 |
msgid "Editor and above"
|
276 |
msgstr ""
|
277 |
|
278 |
-
#: core/core.php:
|
279 |
msgctxt "dashboard widget visibility"
|
280 |
msgid "Nobody (disables the widget)"
|
281 |
msgstr ""
|
282 |
|
283 |
-
#: core/core.php:
|
284 |
msgctxt "settings page"
|
285 |
msgid "Show link actions"
|
286 |
msgstr ""
|
287 |
|
288 |
-
#: core/core.php:
|
289 |
msgid "Max. execution time"
|
290 |
msgstr ""
|
291 |
|
292 |
-
#: core/core.php:
|
293 |
msgid "The plugin works by periodically launching a background job that parses your posts for links, checks the discovered URLs, and performs other time-consuming tasks. Here you can set for how long, at most, the link monitor may run each time before stopping."
|
294 |
msgstr ""
|
295 |
|
296 |
-
#: core/core.php:
|
297 |
msgid "Server load limit"
|
298 |
msgstr ""
|
299 |
|
300 |
-
#: core/core.php:
|
301 |
msgid "Current load : %s"
|
302 |
msgstr ""
|
303 |
|
304 |
-
#: core/core.php:
|
305 |
msgid "Link checking will be suspended if the average <a href=\"%s\">server load</a> rises above this number. Leave this field blank to disable load limiting."
|
306 |
msgstr ""
|
307 |
|
308 |
-
#: core/core.php:
|
309 |
msgid "Not available"
|
310 |
msgstr ""
|
311 |
|
312 |
-
#: core/core.php:
|
313 |
msgid "Load limiting only works on Linux-like systems where <code>/proc/loadavg</code> is present and accessible."
|
314 |
msgstr ""
|
315 |
|
316 |
-
#: core/core.php:
|
317 |
msgid "Target resource usage"
|
318 |
msgstr ""
|
319 |
|
320 |
-
#: core/core.php:
|
321 |
msgid "Logging"
|
322 |
msgstr ""
|
323 |
|
324 |
-
#: core/core.php:
|
325 |
msgid "Enable logging"
|
326 |
msgstr ""
|
327 |
|
328 |
-
#: core/core.php:
|
329 |
msgid "Log file location"
|
330 |
msgstr ""
|
331 |
|
332 |
-
#: core/core.php:
|
333 |
msgctxt "log file location"
|
334 |
msgid "Default"
|
335 |
msgstr ""
|
336 |
|
337 |
-
#: core/core.php:
|
338 |
msgctxt "log file location"
|
339 |
msgid "Custom"
|
340 |
msgstr ""
|
341 |
|
342 |
-
#: core/core.php:
|
343 |
msgid "Forced recheck"
|
344 |
msgstr ""
|
345 |
|
346 |
-
#: core/core.php:
|
347 |
msgid "Re-check all pages"
|
348 |
msgstr ""
|
349 |
|
350 |
-
#: core/core.php:
|
351 |
msgid "The \"Nuclear Option\". Click this button to make the plugin empty its link database and recheck the entire site from scratch."
|
352 |
msgstr ""
|
353 |
|
354 |
-
#: core/core.php:
|
355 |
msgid "Save Changes"
|
356 |
msgstr ""
|
357 |
|
358 |
-
#: core/core.php:
|
359 |
msgid "Configure"
|
360 |
msgstr ""
|
361 |
|
362 |
-
#: core/core.php:
|
363 |
msgid "Enter the names of custom fields you want to check (one per line). If a field contains HTML code, prefix its name with <code>html:</code>. For example, <code>html:field_name</code>."
|
364 |
msgstr ""
|
365 |
|
366 |
-
#: core/core.php:
|
367 |
msgid "Enter the keys of acf fields you want to check (one per line). If a field contains HTML code, prefix its name with <code>html:</code>. For example, <code>html:field_586a3eaa4091b</code>."
|
368 |
msgstr ""
|
369 |
|
370 |
-
#: core/core.php:
|
371 |
msgid "Database error : %s"
|
372 |
msgstr ""
|
373 |
|
374 |
-
#: core/core.php:
|
375 |
msgid "You must enter a filter name!"
|
376 |
msgstr ""
|
377 |
|
378 |
-
#: core/core.php:
|
379 |
msgid "Invalid search query."
|
380 |
msgstr ""
|
381 |
|
382 |
-
#: core/core.php:
|
383 |
msgid "Filter \"%s\" created"
|
384 |
msgstr ""
|
385 |
|
386 |
-
#: core/core.php:
|
387 |
msgid "Filter ID not specified."
|
388 |
msgstr ""
|
389 |
|
390 |
-
#: core/core.php:
|
391 |
msgid "Filter deleted"
|
392 |
msgstr ""
|
393 |
|
394 |
-
#: core/core.php:
|
395 |
msgid "Replaced %d redirect with a direct link"
|
396 |
msgid_plural "Replaced %d redirects with direct links"
|
397 |
msgstr[0] ""
|
398 |
msgstr[1] ""
|
399 |
|
400 |
-
#: core/core.php:
|
401 |
msgid "Failed to fix %d redirect"
|
402 |
msgid_plural "Failed to fix %d redirects"
|
403 |
msgstr[0] ""
|
404 |
msgstr[1] ""
|
405 |
|
406 |
-
#: core/core.php:
|
407 |
msgid "None of the selected links are redirects!"
|
408 |
msgstr ""
|
409 |
|
410 |
-
#: core/core.php:
|
411 |
msgid "%d link updated."
|
412 |
msgid_plural "%d links updated."
|
413 |
msgstr[0] ""
|
414 |
msgstr[1] ""
|
415 |
|
416 |
-
#: core/core.php:
|
417 |
msgid "Failed to update %d link."
|
418 |
msgid_plural "Failed to update %d links."
|
419 |
msgstr[0] ""
|
420 |
msgstr[1] ""
|
421 |
|
422 |
-
#: core/core.php:
|
423 |
msgid "%d link removed"
|
424 |
msgid_plural "%d links removed"
|
425 |
msgstr[0] ""
|
426 |
msgstr[1] ""
|
427 |
|
428 |
-
#: core/core.php:
|
429 |
msgid "Failed to remove %d link"
|
430 |
msgid_plural "Failed to remove %d links"
|
431 |
msgstr[0] ""
|
432 |
msgstr[1] ""
|
433 |
|
434 |
-
#: core/core.php:
|
435 |
msgid "%d item was skipped because it can't be moved to the Trash. You need to delete it manually."
|
436 |
msgid_plural "%d items were skipped because they can't be moved to the Trash. You need to delete them manually."
|
437 |
msgstr[0] ""
|
438 |
msgstr[1] ""
|
439 |
|
440 |
-
#: core/core.php:
|
441 |
msgid "Didn't find anything to delete!"
|
442 |
msgstr ""
|
443 |
|
444 |
-
#: core/core.php:
|
445 |
msgid "%d link scheduled for rechecking"
|
446 |
msgid_plural "%d links scheduled for rechecking"
|
447 |
msgstr[0] ""
|
448 |
msgstr[1] ""
|
449 |
|
450 |
-
#: core/core.php:
|
451 |
msgid "This link was manually marked as working by the user."
|
452 |
msgstr ""
|
453 |
|
454 |
-
#: core/core.php:
|
455 |
msgid "Couldn't modify link %d"
|
456 |
msgstr ""
|
457 |
|
458 |
-
#: core/core.php:
|
459 |
msgid "%d link marked as not broken"
|
460 |
msgid_plural "%d links marked as not broken"
|
461 |
msgstr[0] ""
|
462 |
msgstr[1] ""
|
463 |
|
464 |
-
#: core/core.php:
|
465 |
msgid "%d link dismissed"
|
466 |
msgid_plural "%d links dismissed"
|
467 |
msgstr[0] ""
|
468 |
msgstr[1] ""
|
469 |
|
470 |
-
#: core/core.php:
|
471 |
msgid "The \"Warnings\" page lists problems that are probably temporary or suspected to be false positives.<br> Warnings that persist for a long time will usually be reclassified as broken links."
|
472 |
msgstr ""
|
473 |
|
474 |
-
#: core/core.php:
|
475 |
msgctxt "admin notice under Tools - Broken links - Warnings"
|
476 |
msgid "Hide notice"
|
477 |
msgstr ""
|
478 |
|
479 |
-
#: core/core.php:
|
480 |
msgctxt "a link from the admin notice under Tools - Broken links - Warnings"
|
481 |
msgid "Change warning settings"
|
482 |
msgstr ""
|
483 |
|
484 |
-
#: core/core.php:
|
485 |
msgid "Table columns"
|
486 |
msgstr ""
|
487 |
|
488 |
-
#: core/core.php:
|
489 |
msgid "Show on screen"
|
490 |
msgstr ""
|
491 |
|
492 |
-
#: core/core.php:
|
493 |
msgid "links"
|
494 |
msgstr ""
|
495 |
|
496 |
-
#: core/core.php:
|
497 |
msgid "Apply"
|
498 |
msgstr ""
|
499 |
|
500 |
-
#: core/core.php:
|
501 |
msgid "Misc"
|
502 |
msgstr ""
|
503 |
|
504 |
-
#: core/core.php:
|
505 |
msgid "Highlight links broken for at least %s days"
|
506 |
msgstr ""
|
507 |
|
508 |
-
#: core/core.php:
|
509 |
msgid "Color-code status codes"
|
510 |
msgstr ""
|
511 |
|
512 |
-
#: core/core.php:
|
513 |
msgid "You're not allowed to do that!"
|
514 |
msgstr ""
|
515 |
|
516 |
-
#: core/core.php:
|
517 |
msgid "View broken links"
|
518 |
msgstr ""
|
519 |
|
520 |
-
#: core/core.php:
|
521 |
msgid "Found %d broken link"
|
522 |
msgid_plural "Found %d broken links"
|
523 |
msgstr[0] ""
|
524 |
msgstr[1] ""
|
525 |
|
526 |
-
#: core/core.php:
|
527 |
msgid "No broken links found."
|
528 |
msgstr ""
|
529 |
|
530 |
-
#: core/core.php:
|
531 |
msgid "%d URL in the work queue"
|
532 |
msgid_plural "%d URLs in the work queue"
|
533 |
msgstr[0] ""
|
534 |
msgstr[1] ""
|
535 |
|
536 |
-
#: core/core.php:
|
537 |
msgid "No URLs in the work queue."
|
538 |
msgstr ""
|
539 |
|
540 |
-
#: core/core.php:
|
541 |
msgctxt "for the \"Detected X unique URLs in Y links\" message"
|
542 |
msgid "%d unique URL"
|
543 |
msgid_plural "%d unique URLs"
|
544 |
msgstr[0] ""
|
545 |
msgstr[1] ""
|
546 |
|
547 |
-
#: core/core.php:
|
548 |
msgctxt "for the \"Detected X unique URLs in Y links\" message"
|
549 |
msgid "%d link"
|
550 |
msgid_plural "%d links"
|
551 |
msgstr[0] ""
|
552 |
msgstr[1] ""
|
553 |
|
554 |
-
#: core/core.php:
|
555 |
msgid "Detected %1$s in %2$s and still searching..."
|
556 |
msgstr ""
|
557 |
|
558 |
-
#: core/core.php:
|
559 |
msgid "Detected %1$s in %2$s."
|
560 |
msgstr ""
|
561 |
|
562 |
-
#: core/core.php:
|
563 |
msgid "Searching your blog for links..."
|
564 |
msgstr ""
|
565 |
|
566 |
-
#: core/core.php:
|
567 |
msgid "No links detected."
|
568 |
msgstr ""
|
569 |
|
570 |
-
#: core/core.php:
|
571 |
msgctxt "current load"
|
572 |
msgid "Unknown"
|
573 |
msgstr ""
|
574 |
|
575 |
-
#: core/core.php:
|
576 |
msgid "Oops, I can't find the link %d"
|
577 |
msgstr ""
|
578 |
|
579 |
-
#: core/core.php:
|
580 |
msgid "Oops, couldn't modify the link!"
|
581 |
msgstr ""
|
582 |
|
583 |
-
#: core/core.php:
|
584 |
msgid "Error : link_id not specified"
|
585 |
msgstr ""
|
586 |
|
587 |
-
#: core/core.php:
|
588 |
msgid "Error : link_id or new_url not specified"
|
589 |
msgstr ""
|
590 |
|
591 |
-
#: core/core.php:
|
592 |
msgid "Oops, the new URL is invalid!"
|
593 |
msgstr ""
|
594 |
|
595 |
-
#: core/core.php:
|
596 |
msgid "An unexpected error occurred!"
|
597 |
msgstr ""
|
598 |
|
599 |
-
#: core/core.php:
|
600 |
msgid "An unexpected error occured!"
|
601 |
msgstr ""
|
602 |
|
603 |
-
#: core/core.php:
|
604 |
msgid "You don't have sufficient privileges to access this information!"
|
605 |
msgstr ""
|
606 |
|
607 |
-
#: core/core.php:
|
608 |
msgid "Error : link ID not specified"
|
609 |
msgstr ""
|
610 |
|
611 |
-
#: core/core.php:
|
612 |
msgid "Failed to load link details (%s)"
|
613 |
msgstr ""
|
614 |
|
615 |
-
#: core/core.php:
|
616 |
msgid "Broken Link Checker"
|
617 |
msgstr ""
|
618 |
|
619 |
-
#: core/core.php:
|
620 |
msgid "You have an old version of CURL. Redirect detection may not work properly."
|
621 |
msgstr ""
|
622 |
|
623 |
-
#: core/core.php:
|
624 |
msgid "Not installed"
|
625 |
msgstr ""
|
626 |
|
627 |
-
#: core/core.php:
|
628 |
msgid "Installed"
|
629 |
msgstr ""
|
630 |
|
631 |
-
#: core/core.php:
|
632 |
msgid "You must have either CURL or Snoopy installed for the plugin to work!"
|
633 |
msgstr ""
|
634 |
|
635 |
-
#: core/core.php:
|
636 |
msgid "On"
|
637 |
msgstr ""
|
638 |
|
639 |
-
#: core/core.php:
|
640 |
msgid "Redirects may be detected as broken links when safe_mode is on."
|
641 |
msgstr ""
|
642 |
|
643 |
-
#: core/core.php:
|
644 |
msgid "Off"
|
645 |
msgstr ""
|
646 |
|
647 |
-
#: core/core.php:
|
648 |
msgid "On ( %s )"
|
649 |
msgstr ""
|
650 |
|
651 |
-
#: core/core.php:
|
652 |
msgid "Redirects may be detected as broken links when open_basedir is on."
|
653 |
msgstr ""
|
654 |
|
655 |
-
#: core/core.php:
|
656 |
msgid "If this value is zero even after several page reloads you have probably encountered a bug."
|
657 |
msgstr ""
|
658 |
|
659 |
-
#: core/core.php:
|
660 |
msgid "[%s] Broken links detected"
|
661 |
msgstr ""
|
662 |
|
663 |
-
#: core/core.php:
|
664 |
msgid "Broken Link Checker has detected %d new broken link on your site."
|
665 |
msgid_plural "Broken Link Checker has detected %d new broken links on your site."
|
666 |
msgstr[0] ""
|
667 |
msgstr[1] ""
|
668 |
|
669 |
-
#: core/core.php:
|
670 |
msgid "Here's a list of the first %d broken links:"
|
671 |
msgid_plural "Here's a list of the first %d broken links:"
|
672 |
msgstr[0] ""
|
673 |
msgstr[1] ""
|
674 |
|
675 |
-
#: core/core.php:
|
676 |
msgid "Here's a list of the new broken links: "
|
677 |
msgstr ""
|
678 |
|
679 |
-
#: core/core.php:
|
680 |
msgid "Link text : %s"
|
681 |
msgstr ""
|
682 |
|
683 |
-
#: core/core.php:
|
684 |
-
msgid "Link URL : <a href=\"%s\">%s</a>"
|
685 |
msgstr ""
|
686 |
|
687 |
-
#: core/core.php:
|
688 |
msgid "Source : %s"
|
689 |
msgstr ""
|
690 |
|
691 |
-
#: core/core.php:
|
692 |
msgid "You can see all broken links here:"
|
693 |
msgstr ""
|
694 |
|
695 |
-
#: core/core.php:
|
696 |
msgid "Broken Link Checker has detected %d new broken link in your posts."
|
697 |
msgid_plural "Broken Link Checker has detected %d new broken links in your posts."
|
698 |
msgstr[0] ""
|
699 |
msgstr[1] ""
|
700 |
|
701 |
-
#: core/init.php:
|
702 |
msgid "Every 10 minutes"
|
703 |
msgstr ""
|
704 |
|
705 |
-
#: core/init.php:
|
706 |
msgid "Once Weekly"
|
707 |
msgstr ""
|
708 |
|
709 |
-
#: core/init.php:
|
710 |
msgid "Twice a Month"
|
711 |
msgstr ""
|
712 |
|
713 |
-
#: core/init.php:
|
714 |
msgid "Broken Link Checker installation failed. Try deactivating and then reactivating the plugin."
|
715 |
msgstr ""
|
716 |
|
717 |
-
#: core/init.php:
|
718 |
msgid "Please activate the plugin separately on each site. Network activation is not supported."
|
719 |
msgstr ""
|
720 |
|
721 |
-
#: includes/any-post.php:
|
722 |
msgid "Edit"
|
723 |
msgstr ""
|
724 |
|
725 |
-
#: includes/any-post.php:
|
726 |
msgid "Move this item to the Trash"
|
727 |
msgstr ""
|
728 |
|
729 |
-
#: includes/any-post.php:
|
730 |
msgid "Trash"
|
731 |
msgstr ""
|
732 |
|
733 |
-
#: includes/any-post.php:
|
734 |
msgid "Delete this item permanently"
|
735 |
msgstr ""
|
736 |
|
737 |
-
#: includes/any-post.php:
|
738 |
msgid "Delete"
|
739 |
msgstr ""
|
740 |
|
741 |
-
#: includes/any-post.php:
|
742 |
msgid "Preview “%s”"
|
743 |
msgstr ""
|
744 |
|
745 |
-
#: includes/any-post.php:
|
746 |
msgid "Preview"
|
747 |
msgstr ""
|
748 |
|
749 |
-
#: includes/any-post.php:
|
750 |
msgid "View “%s”"
|
751 |
msgstr ""
|
752 |
|
753 |
-
#: includes/any-post.php:
|
754 |
msgid "View"
|
755 |
msgstr ""
|
756 |
|
757 |
-
#: includes/any-post.php:
|
758 |
msgid "Edit this item"
|
759 |
msgstr ""
|
760 |
|
761 |
-
#: includes/any-post.php:
|
762 |
msgid "Nothing to update"
|
763 |
msgstr ""
|
764 |
|
765 |
-
#: includes/any-post.php:
|
766 |
msgid "Updating post %d failed"
|
767 |
msgstr ""
|
768 |
|
769 |
-
#: includes/any-post.php:
|
770 |
-
msgid "Failed to delete post \"%s\" (%d)"
|
771 |
msgstr ""
|
772 |
|
773 |
-
#: includes/any-post.php:
|
774 |
-
msgid "Can't move post \"%s\" (%d) to the trash because the trash feature is disabled"
|
775 |
msgstr ""
|
776 |
|
777 |
-
#: includes/any-post.php:
|
778 |
-
msgid "Failed to move post \"%s\" (%d) to the trash"
|
779 |
msgstr ""
|
780 |
|
781 |
-
#: includes/any-post.php:
|
782 |
msgid "%d post deleted."
|
783 |
msgid_plural "%d posts deleted."
|
784 |
msgstr[0] ""
|
785 |
msgstr[1] ""
|
786 |
|
787 |
-
#: includes/any-post.php:
|
788 |
msgid "%d page deleted."
|
789 |
msgid_plural "%d pages deleted."
|
790 |
msgstr[0] ""
|
791 |
msgstr[1] ""
|
792 |
|
793 |
-
#: includes/any-post.php:
|
794 |
-
msgid "%d \"%s\" deleted."
|
795 |
-
msgid_plural "%d \"%s\" deleted."
|
796 |
msgstr[0] ""
|
797 |
msgstr[1] ""
|
798 |
|
799 |
-
#: includes/any-post.php:
|
800 |
msgid "%d post moved to the Trash."
|
801 |
msgid_plural "%d posts moved to the Trash."
|
802 |
msgstr[0] ""
|
803 |
msgstr[1] ""
|
804 |
|
805 |
-
#: includes/any-post.php:
|
806 |
msgid "%d page moved to the Trash."
|
807 |
msgid_plural "%d pages moved to the Trash."
|
808 |
msgstr[0] ""
|
809 |
msgstr[1] ""
|
810 |
|
811 |
-
#: includes/any-post.php:
|
812 |
-
msgid "%d \"%s\" moved to the Trash."
|
813 |
-
msgid_plural "%d \"%s\" moved to the Trash."
|
814 |
msgstr[0] ""
|
815 |
msgstr[1] ""
|
816 |
|
817 |
#: includes/containers.php:122
|
818 |
-
msgid "%d '%s' has been deleted"
|
819 |
-
msgid_plural "%d '%s' have been deleted"
|
820 |
msgstr[0] ""
|
821 |
msgstr[1] ""
|
822 |
|
823 |
-
#: includes/containers.php:
|
824 |
msgid "Container type '%s' not recognized"
|
825 |
msgstr ""
|
826 |
|
@@ -919,11 +919,11 @@ msgctxt "module name"
|
|
919 |
msgid "Pages"
|
920 |
msgstr ""
|
921 |
|
922 |
-
#: includes/instances.php:
|
923 |
-
msgid "Container %s[%d] not found"
|
924 |
msgstr ""
|
925 |
|
926 |
-
#: includes/instances.php:
|
927 |
msgid "Parser '%s' not found."
|
928 |
msgstr ""
|
929 |
|
@@ -953,6 +953,7 @@ msgid "Warnings"
|
|
953 |
msgstr ""
|
954 |
|
955 |
#: includes/link-query.php:47
|
|
|
956 |
msgid "Warnings"
|
957 |
msgstr ""
|
958 |
|
@@ -992,11 +993,11 @@ msgstr ""
|
|
992 |
msgid "Search Results"
|
993 |
msgstr ""
|
994 |
|
995 |
-
#: includes/link-query.php:83, includes/link-query.php:
|
996 |
msgid "No links found for your query"
|
997 |
msgstr ""
|
998 |
|
999 |
-
#: includes/links.php:
|
1000 |
msgid "The plugin script was terminated while trying to check the link."
|
1001 |
msgstr ""
|
1002 |
|
@@ -1012,44 +1013,44 @@ msgstr ""
|
|
1012 |
msgid "Link is valid."
|
1013 |
msgs
|
1 |
+
# Copyright (C) 2020 Broken Link Checker 1.11.11
|
2 |
+
# This file is distributed under the same license as the Broken Link Checker 1.11.11 package.
|
3 |
msgid ""
|
4 |
msgstr ""
|
5 |
+
"Project-Id-Version: Broken Link Checker 1.11.11\n"
|
6 |
"MIME-Version: 1.0\n"
|
7 |
"Content-Type: text/plain; charset=UTF-8\n"
|
8 |
"Content-Transfer-Encoding: 8bit\n"
|
13 |
"X-Poedit-SourceCharset: UTF-8\n"
|
14 |
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
15 |
|
16 |
+
#: core/core.php:154, includes/admin/links-page-js.php:45
|
17 |
msgid "Loading..."
|
18 |
msgstr ""
|
19 |
|
20 |
+
#: core/core.php:178, includes/admin/options-page-js.php:18
|
21 |
msgid "[ Network error ]"
|
22 |
msgstr ""
|
23 |
|
24 |
+
#: core/core.php:210
|
25 |
msgid "Automatically expand the widget if broken links have been detected"
|
26 |
msgstr ""
|
27 |
|
28 |
+
#: core/core.php:302
|
29 |
msgid "Link Checker Settings"
|
30 |
msgstr ""
|
31 |
|
32 |
+
#: core/core.php:303
|
33 |
msgid "Link Checker"
|
34 |
msgstr ""
|
35 |
|
36 |
+
#: core/core.php:309, includes/link-query.php:37
|
37 |
msgid "Broken Links"
|
38 |
msgstr ""
|
39 |
|
40 |
+
#: core/core.php:325
|
41 |
msgid "View Broken Links"
|
42 |
msgstr ""
|
43 |
|
44 |
+
#: core/core.php:341
|
45 |
msgid "Go to Broken Links"
|
46 |
msgstr ""
|
47 |
|
48 |
+
#: core/core.php:359
|
49 |
msgid "Settings"
|
50 |
msgstr ""
|
51 |
|
52 |
+
#: core/core.php:394, includes/admin/table-printer.php:292, includes/admin/table-printer.php:710
|
53 |
msgid "Edit URL"
|
54 |
msgstr ""
|
55 |
|
56 |
+
#: core/core.php:395, includes/admin/links-page-js.php:713, includes/admin/table-printer.php:297, includes/admin/table-printer.php:713
|
57 |
msgid "Unlink"
|
58 |
msgstr ""
|
59 |
|
60 |
+
#: core/core.php:396, includes/admin/links-page-js.php:111, includes/admin/table-printer.php:719
|
61 |
msgid "Not broken"
|
62 |
msgstr ""
|
63 |
|
64 |
+
#: core/core.php:397, includes/admin/table-printer.php:296, includes/admin/table-printer.php:727
|
65 |
msgid "Dismiss"
|
66 |
msgstr ""
|
67 |
|
68 |
+
#: core/core.php:398, includes/admin/table-printer.php:293, includes/admin/table-printer.php:739
|
69 |
msgid "Recheck"
|
70 |
msgstr ""
|
71 |
|
72 |
+
#: core/core.php:399, includes/admin/table-printer.php:747
|
73 |
msgctxt "link action; replace one redirect with a direct link"
|
74 |
msgid "Fix redirect"
|
75 |
msgstr ""
|
76 |
|
77 |
+
#: core/core.php:650
|
78 |
msgid "Settings saved."
|
79 |
msgstr ""
|
80 |
|
81 |
+
#: core/core.php:656
|
82 |
msgid "Thank you for your donation!"
|
83 |
msgstr ""
|
84 |
|
85 |
+
#: core/core.php:664
|
86 |
msgid "Complete site recheck started."
|
87 |
msgstr ""
|
88 |
|
89 |
+
#: core/core.php:687
|
90 |
msgid "General"
|
91 |
msgstr ""
|
92 |
|
93 |
+
#: core/core.php:688
|
94 |
msgid "Look For Links In"
|
95 |
msgstr ""
|
96 |
|
97 |
+
#: core/core.php:689
|
98 |
msgid "Which Links To Check"
|
99 |
msgstr ""
|
100 |
|
101 |
+
#: core/core.php:690
|
102 |
msgid "Protocols & APIs"
|
103 |
msgstr ""
|
104 |
|
105 |
+
#: core/core.php:691
|
106 |
msgid "Advanced"
|
107 |
msgstr ""
|
108 |
|
109 |
+
#: core/core.php:705
|
110 |
msgid "Broken Link Checker Options"
|
111 |
msgstr ""
|
112 |
|
113 |
+
#: core/core.php:749, includes/admin/table-printer.php:215
|
114 |
msgid "Status"
|
115 |
msgstr ""
|
116 |
|
117 |
+
#: core/core.php:751, includes/admin/options-page-js.php:56
|
118 |
msgid "Show debug info"
|
119 |
msgstr ""
|
120 |
|
121 |
+
#: core/core.php:779
|
122 |
msgid "Check each link"
|
123 |
msgstr ""
|
124 |
|
125 |
+
#: core/core.php:784
|
126 |
msgid "Every %s hours"
|
127 |
msgstr ""
|
128 |
|
129 |
+
#: core/core.php:793
|
130 |
msgid "Existing links will be checked this often. New links will usually be checked ASAP."
|
131 |
msgstr ""
|
132 |
|
133 |
+
#: core/core.php:800
|
134 |
msgid "E-mail notifications"
|
135 |
msgstr ""
|
136 |
|
137 |
+
#: core/core.php:810
|
138 |
msgid "Send me e-mail notifications about newly detected broken links"
|
139 |
msgstr ""
|
140 |
|
141 |
+
#: core/core.php:822
|
142 |
msgid "Send authors e-mail notifications about broken links in their posts"
|
143 |
msgstr ""
|
144 |
|
145 |
+
#: core/core.php:829
|
146 |
msgid "Notification e-mail address"
|
147 |
msgstr ""
|
148 |
|
149 |
+
#: core/core.php:841
|
150 |
msgid "Leave empty to use the e-mail address specified in Settings → General."
|
151 |
msgstr ""
|
152 |
|
153 |
+
#: core/core.php:848
|
154 |
msgid "Link tweaks"
|
155 |
msgstr ""
|
156 |
|
157 |
+
#: core/core.php:858
|
158 |
msgid "Apply custom formatting to broken links"
|
159 |
msgstr ""
|
160 |
|
161 |
+
#: core/core.php:863, core/core.php:906
|
162 |
msgid "Edit CSS"
|
163 |
msgstr ""
|
164 |
|
165 |
+
#: core/core.php:885
|
166 |
msgid "Example : Lorem ipsum <a %s>broken link</a>, dolor sit amet."
|
167 |
msgstr ""
|
168 |
|
169 |
+
#: core/core.php:888, core/core.php:932
|
170 |
msgid "Click \"Save Changes\" to update example output."
|
171 |
msgstr ""
|
172 |
|
173 |
+
#: core/core.php:901
|
174 |
msgid "Apply custom formatting to removed links"
|
175 |
msgstr ""
|
176 |
|
177 |
+
#: core/core.php:929
|
178 |
msgid "Example : Lorem ipsum <span %s>removed link</span>, dolor sit amet."
|
179 |
msgstr ""
|
180 |
|
181 |
+
#: core/core.php:946
|
182 |
msgid "Stop search engines from following broken links"
|
183 |
msgstr ""
|
184 |
|
185 |
+
#: core/core.php:952
|
186 |
msgctxt "\"Link tweaks\" settings"
|
187 |
msgid "These settings only apply to the content of posts, not comments or custom fields."
|
188 |
msgstr ""
|
189 |
|
190 |
+
#: core/core.php:963
|
191 |
msgctxt "settings page"
|
192 |
msgid "Suggestions"
|
193 |
msgstr ""
|
194 |
|
195 |
+
#: core/core.php:968
|
196 |
msgid "Suggest alternatives to broken links"
|
197 |
msgstr ""
|
198 |
|
199 |
+
#: core/core.php:974
|
200 |
msgctxt "settings page"
|
201 |
msgid "Warnings"
|
202 |
msgstr ""
|
203 |
|
204 |
+
#: core/core.php:979
|
205 |
msgid "Show uncertain or minor problems as \"warnings\" instead of \"broken\""
|
206 |
msgstr ""
|
207 |
|
208 |
+
#: core/core.php:983
|
209 |
msgid "Turning off this option will make the plugin report all problems as broken links."
|
210 |
msgstr ""
|
211 |
|
212 |
+
#: core/core.php:999
|
213 |
msgid "Look for links in"
|
214 |
msgstr ""
|
215 |
|
216 |
+
#: core/core.php:1015
|
217 |
msgid "Post statuses"
|
218 |
msgstr ""
|
219 |
|
220 |
+
#: core/core.php:1048
|
221 |
msgid "Link types"
|
222 |
msgstr ""
|
223 |
|
224 |
+
#: core/core.php:1054
|
225 |
msgid "Error : All link parsers missing!"
|
226 |
msgstr ""
|
227 |
|
228 |
+
#: core/core.php:1061
|
229 |
msgid "Exclusion list"
|
230 |
msgstr ""
|
231 |
|
232 |
+
#: core/core.php:1062
|
233 |
msgid "Don't check links where the URL contains any of these words (one per line) :"
|
234 |
msgstr ""
|
235 |
|
236 |
+
#: core/core.php:1083
|
237 |
msgid "Check links using"
|
238 |
msgstr ""
|
239 |
|
240 |
+
#: core/core.php:1102, includes/links.php:1032
|
241 |
msgid "Timeout"
|
242 |
msgstr ""
|
243 |
|
244 |
+
#: core/core.php:1108, core/core.php:1203, core/core.php:3640
|
245 |
msgid "%s seconds"
|
246 |
msgstr ""
|
247 |
|
248 |
+
#: core/core.php:1117
|
249 |
msgid "Links that take longer than this to load will be marked as broken."
|
250 |
msgstr ""
|
251 |
|
252 |
+
#: core/core.php:1124
|
253 |
msgid "Link monitor"
|
254 |
msgstr ""
|
255 |
|
256 |
+
#: core/core.php:1136
|
257 |
msgid "Run continuously while the Dashboard is open"
|
258 |
msgstr ""
|
259 |
|
260 |
+
#: core/core.php:1148
|
261 |
msgid "Run hourly in the background"
|
262 |
msgstr ""
|
263 |
|
264 |
+
#: core/core.php:1156
|
265 |
msgid "Show the dashboard widget for"
|
266 |
msgstr ""
|
267 |
|
268 |
+
#: core/core.php:1161
|
269 |
msgctxt "dashboard widget visibility"
|
270 |
msgid "Administrator"
|
271 |
msgstr ""
|
272 |
|
273 |
+
#: core/core.php:1162
|
274 |
msgctxt "dashboard widget visibility"
|
275 |
msgid "Editor and above"
|
276 |
msgstr ""
|
277 |
|
278 |
+
#: core/core.php:1163
|
279 |
msgctxt "dashboard widget visibility"
|
280 |
msgid "Nobody (disables the widget)"
|
281 |
msgstr ""
|
282 |
|
283 |
+
#: core/core.php:1179
|
284 |
msgctxt "settings page"
|
285 |
msgid "Show link actions"
|
286 |
msgstr ""
|
287 |
|
288 |
+
#: core/core.php:1197
|
289 |
msgid "Max. execution time"
|
290 |
msgstr ""
|
291 |
|
292 |
+
#: core/core.php:1214
|
293 |
msgid "The plugin works by periodically launching a background job that parses your posts for links, checks the discovered URLs, and performs other time-consuming tasks. Here you can set for how long, at most, the link monitor may run each time before stopping."
|
294 |
msgstr ""
|
295 |
|
296 |
+
#: core/core.php:1223
|
297 |
msgid "Server load limit"
|
298 |
msgstr ""
|
299 |
|
300 |
+
#: core/core.php:1238
|
301 |
msgid "Current load : %s"
|
302 |
msgstr ""
|
303 |
|
304 |
+
#: core/core.php:1243
|
305 |
msgid "Link checking will be suspended if the average <a href=\"%s\">server load</a> rises above this number. Leave this field blank to disable load limiting."
|
306 |
msgstr ""
|
307 |
|
308 |
+
#: core/core.php:1252
|
309 |
msgid "Not available"
|
310 |
msgstr ""
|
311 |
|
312 |
+
#: core/core.php:1254
|
313 |
msgid "Load limiting only works on Linux-like systems where <code>/proc/loadavg</code> is present and accessible."
|
314 |
msgstr ""
|
315 |
|
316 |
+
#: core/core.php:1262
|
317 |
msgid "Target resource usage"
|
318 |
msgstr ""
|
319 |
|
320 |
+
#: core/core.php:1282
|
321 |
msgid "Logging"
|
322 |
msgstr ""
|
323 |
|
324 |
+
#: core/core.php:1288
|
325 |
msgid "Enable logging"
|
326 |
msgstr ""
|
327 |
|
328 |
+
#: core/core.php:1295
|
329 |
msgid "Log file location"
|
330 |
msgstr ""
|
331 |
|
332 |
+
#: core/core.php:1304
|
333 |
msgctxt "log file location"
|
334 |
msgid "Default"
|
335 |
msgstr ""
|
336 |
|
337 |
+
#: core/core.php:1320
|
338 |
msgctxt "log file location"
|
339 |
msgid "Custom"
|
340 |
msgstr ""
|
341 |
|
342 |
+
#: core/core.php:1332
|
343 |
msgid "Forced recheck"
|
344 |
msgstr ""
|
345 |
|
346 |
+
#: core/core.php:1335
|
347 |
msgid "Re-check all pages"
|
348 |
msgstr ""
|
349 |
|
350 |
+
#: core/core.php:1340
|
351 |
msgid "The \"Nuclear Option\". Click this button to make the plugin empty its link database and recheck the entire site from scratch."
|
352 |
msgstr ""
|
353 |
|
354 |
+
#: core/core.php:1352
|
355 |
msgid "Save Changes"
|
356 |
msgstr ""
|
357 |
|
358 |
+
#: core/core.php:1403
|
359 |
msgid "Configure"
|
360 |
msgstr ""
|
361 |
|
362 |
+
#: core/core.php:1485
|
363 |
msgid "Enter the names of custom fields you want to check (one per line). If a field contains HTML code, prefix its name with <code>html:</code>. For example, <code>html:field_name</code>."
|
364 |
msgstr ""
|
365 |
|
366 |
+
#: core/core.php:1499
|
367 |
msgid "Enter the keys of acf fields you want to check (one per line). If a field contains HTML code, prefix its name with <code>html:</code>. For example, <code>html:field_586a3eaa4091b</code>."
|
368 |
msgstr ""
|
369 |
|
370 |
+
#: core/core.php:1630, core/core.php:1716, core/core.php:1748
|
371 |
msgid "Database error : %s"
|
372 |
msgstr ""
|
373 |
|
374 |
+
#: core/core.php:1697
|
375 |
msgid "You must enter a filter name!"
|
376 |
msgstr ""
|
377 |
|
378 |
+
#: core/core.php:1701
|
379 |
msgid "Invalid search query."
|
380 |
msgstr ""
|
381 |
|
382 |
+
#: core/core.php:1711
|
383 |
msgid "Filter \"%s\" created"
|
384 |
msgstr ""
|
385 |
|
386 |
+
#: core/core.php:1738
|
387 |
msgid "Filter ID not specified."
|
388 |
msgstr ""
|
389 |
|
390 |
+
#: core/core.php:1745
|
391 |
msgid "Filter deleted"
|
392 |
msgstr ""
|
393 |
|
394 |
+
#: core/core.php:1794
|
395 |
msgid "Replaced %d redirect with a direct link"
|
396 |
msgid_plural "Replaced %d redirects with direct links"
|
397 |
msgstr[0] ""
|
398 |
msgstr[1] ""
|
399 |
|
400 |
+
#: core/core.php:1805
|
401 |
msgid "Failed to fix %d redirect"
|
402 |
msgid_plural "Failed to fix %d redirects"
|
403 |
msgstr[0] ""
|
404 |
msgstr[1] ""
|
405 |
|
406 |
+
#: core/core.php:1816
|
407 |
msgid "None of the selected links are redirects!"
|
408 |
msgstr ""
|
409 |
|
410 |
+
#: core/core.php:1896
|
411 |
msgid "%d link updated."
|
412 |
msgid_plural "%d links updated."
|
413 |
msgstr[0] ""
|
414 |
msgstr[1] ""
|
415 |
|
416 |
+
#: core/core.php:1907
|
417 |
msgid "Failed to update %d link."
|
418 |
msgid_plural "Failed to update %d links."
|
419 |
msgstr[0] ""
|
420 |
msgstr[1] ""
|
421 |
|
422 |
+
#: core/core.php:1998
|
423 |
msgid "%d link removed"
|
424 |
msgid_plural "%d links removed"
|
425 |
msgstr[0] ""
|
426 |
msgstr[1] ""
|
427 |
|
428 |
+
#: core/core.php:2009
|
429 |
msgid "Failed to remove %d link"
|
430 |
msgid_plural "Failed to remove %d links"
|
431 |
msgstr[0] ""
|
432 |
msgstr[1] ""
|
433 |
|
434 |
+
#: core/core.php:2119
|
435 |
msgid "%d item was skipped because it can't be moved to the Trash. You need to delete it manually."
|
436 |
msgid_plural "%d items were skipped because they can't be moved to the Trash. You need to delete them manually."
|
437 |
msgstr[0] ""
|
438 |
msgstr[1] ""
|
439 |
|
440 |
+
#: core/core.php:2141
|
441 |
msgid "Didn't find anything to delete!"
|
442 |
msgstr ""
|
443 |
|
444 |
+
#: core/core.php:2176
|
445 |
msgid "%d link scheduled for rechecking"
|
446 |
msgid_plural "%d links scheduled for rechecking"
|
447 |
msgstr[0] ""
|
448 |
msgstr[1] ""
|
449 |
|
450 |
+
#: core/core.php:2225, core/core.php:3061
|
451 |
msgid "This link was manually marked as working by the user."
|
452 |
msgstr ""
|
453 |
|
454 |
+
#: core/core.php:2233, core/core.php:2295
|
455 |
msgid "Couldn't modify link %d"
|
456 |
msgstr ""
|
457 |
|
458 |
+
#: core/core.php:2244
|
459 |
msgid "%d link marked as not broken"
|
460 |
msgid_plural "%d links marked as not broken"
|
461 |
msgstr[0] ""
|
462 |
msgstr[1] ""
|
463 |
|
464 |
+
#: core/core.php:2306
|
465 |
msgid "%d link dismissed"
|
466 |
msgid_plural "%d links dismissed"
|
467 |
msgstr[0] ""
|
468 |
msgstr[1] ""
|
469 |
|
470 |
+
#: core/core.php:2363
|
471 |
msgid "The \"Warnings\" page lists problems that are probably temporary or suspected to be false positives.<br> Warnings that persist for a long time will usually be reclassified as broken links."
|
472 |
msgstr ""
|
473 |
|
474 |
+
#: core/core.php:2368
|
475 |
msgctxt "admin notice under Tools - Broken links - Warnings"
|
476 |
msgid "Hide notice"
|
477 |
msgstr ""
|
478 |
|
479 |
+
#: core/core.php:2374
|
480 |
msgctxt "a link from the admin notice under Tools - Broken links - Warnings"
|
481 |
msgid "Change warning settings"
|
482 |
msgstr ""
|
483 |
|
484 |
+
#: core/core.php:2399
|
485 |
msgid "Table columns"
|
486 |
msgstr ""
|
487 |
|
488 |
+
#: core/core.php:2418
|
489 |
msgid "Show on screen"
|
490 |
msgstr ""
|
491 |
|
492 |
+
#: core/core.php:2425
|
493 |
msgid "links"
|
494 |
msgstr ""
|
495 |
|
496 |
+
#: core/core.php:2426, includes/admin/table-printer.php:175
|
497 |
msgid "Apply"
|
498 |
msgstr ""
|
499 |
|
500 |
+
#: core/core.php:2430
|
501 |
msgid "Misc"
|
502 |
msgstr ""
|
503 |
|
504 |
+
#: core/core.php:2445
|
505 |
msgid "Highlight links broken for at least %s days"
|
506 |
msgstr ""
|
507 |
|
508 |
+
#: core/core.php:2454
|
509 |
msgid "Color-code status codes"
|
510 |
msgstr ""
|
511 |
|
512 |
+
#: core/core.php:2473, core/core.php:3045, core/core.php:3092, core/core.php:3131, core/core.php:3255, core/core.php:3314, core/core.php:3392
|
513 |
msgid "You're not allowed to do that!"
|
514 |
msgstr ""
|
515 |
|
516 |
+
#: core/core.php:2913
|
517 |
msgid "View broken links"
|
518 |
msgstr ""
|
519 |
|
520 |
+
#: core/core.php:2914
|
521 |
msgid "Found %d broken link"
|
522 |
msgid_plural "Found %d broken links"
|
523 |
msgstr[0] ""
|
524 |
msgstr[1] ""
|
525 |
|
526 |
+
#: core/core.php:2920
|
527 |
msgid "No broken links found."
|
528 |
msgstr ""
|
529 |
|
530 |
+
#: core/core.php:2927
|
531 |
msgid "%d URL in the work queue"
|
532 |
msgid_plural "%d URLs in the work queue"
|
533 |
msgstr[0] ""
|
534 |
msgstr[1] ""
|
535 |
|
536 |
+
#: core/core.php:2931
|
537 |
msgid "No URLs in the work queue."
|
538 |
msgstr ""
|
539 |
|
540 |
+
#: core/core.php:2937
|
541 |
msgctxt "for the \"Detected X unique URLs in Y links\" message"
|
542 |
msgid "%d unique URL"
|
543 |
msgid_plural "%d unique URLs"
|
544 |
msgstr[0] ""
|
545 |
msgstr[1] ""
|
546 |
|
547 |
+
#: core/core.php:2941
|
548 |
msgctxt "for the \"Detected X unique URLs in Y links\" message"
|
549 |
msgid "%d link"
|
550 |
msgid_plural "%d links"
|
551 |
msgstr[0] ""
|
552 |
msgstr[1] ""
|
553 |
|
554 |
+
#: core/core.php:2947
|
555 |
msgid "Detected %1$s in %2$s and still searching..."
|
556 |
msgstr ""
|
557 |
|
558 |
+
#: core/core.php:2953
|
559 |
msgid "Detected %1$s in %2$s."
|
560 |
msgstr ""
|
561 |
|
562 |
+
#: core/core.php:2960
|
563 |
msgid "Searching your blog for links..."
|
564 |
msgstr ""
|
565 |
|
566 |
+
#: core/core.php:2962
|
567 |
msgid "No links detected."
|
568 |
msgstr ""
|
569 |
|
570 |
+
#: core/core.php:2988
|
571 |
msgctxt "current load"
|
572 |
msgid "Unknown"
|
573 |
msgstr ""
|
574 |
|
575 |
+
#: core/core.php:3053, core/core.php:3100, core/core.php:3154, core/core.php:3269, core/core.php:3337, core/core.php:3415
|
576 |
msgid "Oops, I can't find the link %d"
|
577 |
msgstr ""
|
578 |
|
579 |
+
#: core/core.php:3073, core/core.php:3114
|
580 |
msgid "Oops, couldn't modify the link!"
|
581 |
msgstr ""
|
582 |
|
583 |
+
#: core/core.php:3076, core/core.php:3117, core/core.php:3302, core/core.php:3324, core/core.php:3402
|
584 |
msgid "Error : link_id not specified"
|
585 |
msgstr ""
|
586 |
|
587 |
+
#: core/core.php:3141
|
588 |
msgid "Error : link_id or new_url not specified"
|
589 |
msgstr ""
|
590 |
|
591 |
+
#: core/core.php:3167, core/core.php:3181
|
592 |
msgid "Oops, the new URL is invalid!"
|
593 |
msgstr ""
|
594 |
|
595 |
+
#: core/core.php:3201
|
596 |
msgid "An unexpected error occurred!"
|
597 |
msgstr ""
|
598 |
|
599 |
+
#: core/core.php:3282
|
600 |
msgid "An unexpected error occured!"
|
601 |
msgstr ""
|
602 |
|
603 |
+
#: core/core.php:3450
|
604 |
msgid "You don't have sufficient privileges to access this information!"
|
605 |
msgstr ""
|
606 |
|
607 |
+
#: core/core.php:3463
|
608 |
msgid "Error : link ID not specified"
|
609 |
msgstr ""
|
610 |
|
611 |
+
#: core/core.php:3477
|
612 |
msgid "Failed to load link details (%s)"
|
613 |
msgstr ""
|
614 |
|
615 |
+
#: core/core.php:3531
|
616 |
msgid "Broken Link Checker"
|
617 |
msgstr ""
|
618 |
|
619 |
+
#: core/core.php:3570
|
620 |
msgid "You have an old version of CURL. Redirect detection may not work properly."
|
621 |
msgstr ""
|
622 |
|
623 |
+
#: core/core.php:3581, core/core.php:3597, core/core.php:3602
|
624 |
msgid "Not installed"
|
625 |
msgstr ""
|
626 |
|
627 |
+
#: core/core.php:3590
|
628 |
msgid "Installed"
|
629 |
msgstr ""
|
630 |
|
631 |
+
#: core/core.php:3603
|
632 |
msgid "You must have either CURL or Snoopy installed for the plugin to work!"
|
633 |
msgstr ""
|
634 |
|
635 |
+
#: core/core.php:3613
|
636 |
msgid "On"
|
637 |
msgstr ""
|
638 |
|
639 |
+
#: core/core.php:3614
|
640 |
msgid "Redirects may be detected as broken links when safe_mode is on."
|
641 |
msgstr ""
|
642 |
|
643 |
+
#: core/core.php:3619, core/core.php:3633
|
644 |
msgid "Off"
|
645 |
msgstr ""
|
646 |
|
647 |
+
#: core/core.php:3627
|
648 |
msgid "On ( %s )"
|
649 |
msgstr ""
|
650 |
|
651 |
+
#: core/core.php:3628
|
652 |
msgid "Redirects may be detected as broken links when open_basedir is on."
|
653 |
msgstr ""
|
654 |
|
655 |
+
#: core/core.php:3665
|
656 |
msgid "If this value is zero even after several page reloads you have probably encountered a bug."
|
657 |
msgstr ""
|
658 |
|
659 |
+
#: core/core.php:3789, core/core.php:3908
|
660 |
msgid "[%s] Broken links detected"
|
661 |
msgstr ""
|
662 |
|
663 |
+
#: core/core.php:3794
|
664 |
msgid "Broken Link Checker has detected %d new broken link on your site."
|
665 |
msgid_plural "Broken Link Checker has detected %d new broken links on your site."
|
666 |
msgstr[0] ""
|
667 |
msgstr[1] ""
|
668 |
|
669 |
+
#: core/core.php:3825
|
670 |
msgid "Here's a list of the first %d broken links:"
|
671 |
msgid_plural "Here's a list of the first %d broken links:"
|
672 |
msgstr[0] ""
|
673 |
msgstr[1] ""
|
674 |
|
675 |
+
#: core/core.php:3834
|
676 |
msgid "Here's a list of the new broken links: "
|
677 |
msgstr ""
|
678 |
|
679 |
+
#: core/core.php:3843
|
680 |
msgid "Link text : %s"
|
681 |
msgstr ""
|
682 |
|
683 |
+
#: core/core.php:3844
|
684 |
+
msgid "Link URL : <a href=\"%1$s\">%2$s</a>"
|
685 |
msgstr ""
|
686 |
|
687 |
+
#: core/core.php:3845
|
688 |
msgid "Source : %s"
|
689 |
msgstr ""
|
690 |
|
691 |
+
#: core/core.php:3859
|
692 |
msgid "You can see all broken links here:"
|
693 |
msgstr ""
|
694 |
|
695 |
+
#: core/core.php:3913
|
696 |
msgid "Broken Link Checker has detected %d new broken link in your posts."
|
697 |
msgid_plural "Broken Link Checker has detected %d new broken links in your posts."
|
698 |
msgstr[0] ""
|
699 |
msgstr[1] ""
|
700 |
|
701 |
+
#: core/init.php:233
|
702 |
msgid "Every 10 minutes"
|
703 |
msgstr ""
|
704 |
|
705 |
+
#: core/init.php:240
|
706 |
msgid "Once Weekly"
|
707 |
msgstr ""
|
708 |
|
709 |
+
#: core/init.php:246
|
710 |
msgid "Twice a Month"
|
711 |
msgstr ""
|
712 |
|
713 |
+
#: core/init.php:324
|
714 |
msgid "Broken Link Checker installation failed. Try deactivating and then reactivating the plugin."
|
715 |
msgstr ""
|
716 |
|
717 |
+
#: core/init.php:328
|
718 |
msgid "Please activate the plugin separately on each site. Network activation is not supported."
|
719 |
msgstr ""
|
720 |
|
721 |
+
#: includes/any-post.php:462, modules/containers/acf_field.php:248, modules/containers/blogroll.php:46, modules/containers/comment.php:159, modules/containers/custom_field.php:230
|
722 |
msgid "Edit"
|
723 |
msgstr ""
|
724 |
|
725 |
+
#: includes/any-post.php:470, modules/containers/acf_field.php:252, modules/containers/custom_field.php:236
|
726 |
msgid "Move this item to the Trash"
|
727 |
msgstr ""
|
728 |
|
729 |
+
#: includes/any-post.php:472, modules/containers/acf_field.php:252, modules/containers/custom_field.php:238
|
730 |
msgid "Trash"
|
731 |
msgstr ""
|
732 |
|
733 |
+
#: includes/any-post.php:477, modules/containers/acf_field.php:254, modules/containers/custom_field.php:243
|
734 |
msgid "Delete this item permanently"
|
735 |
msgstr ""
|
736 |
|
737 |
+
#: includes/any-post.php:479, modules/containers/acf_field.php:254, modules/containers/blogroll.php:47, modules/containers/custom_field.php:245
|
738 |
msgid "Delete"
|
739 |
msgstr ""
|
740 |
|
741 |
+
#: includes/any-post.php:492
|
742 |
msgid "Preview “%s”"
|
743 |
msgstr ""
|
744 |
|
745 |
+
#: includes/any-post.php:493
|
746 |
msgid "Preview"
|
747 |
msgstr ""
|
748 |
|
749 |
+
#: includes/any-post.php:500
|
750 |
msgid "View “%s”"
|
751 |
msgstr ""
|
752 |
|
753 |
+
#: includes/any-post.php:501, modules/containers/acf_field.php:258, modules/containers/comment.php:172, modules/containers/custom_field.php:250
|
754 |
msgid "View"
|
755 |
msgstr ""
|
756 |
|
757 |
+
#: includes/any-post.php:520, modules/containers/acf_field.php:248, modules/containers/custom_field.php:230
|
758 |
msgid "Edit this item"
|
759 |
msgstr ""
|
760 |
|
761 |
+
#: includes/any-post.php:584, modules/containers/blogroll.php:83, modules/containers/comment.php:43
|
762 |
msgid "Nothing to update"
|
763 |
msgstr ""
|
764 |
|
765 |
+
#: includes/any-post.php:594
|
766 |
msgid "Updating post %d failed"
|
767 |
msgstr ""
|
768 |
|
769 |
+
#: includes/any-post.php:631, modules/containers/acf_field.php:327, modules/containers/custom_field.php:319
|
770 |
+
msgid "Failed to delete post \"%1$s\" (%2$d)"
|
771 |
msgstr ""
|
772 |
|
773 |
+
#: includes/any-post.php:650, modules/containers/acf_field.php:341, modules/containers/custom_field.php:338
|
774 |
+
msgid "Can't move post \"%1$s\" (%2$d) to the trash because the trash feature is disabled"
|
775 |
msgstr ""
|
776 |
|
777 |
+
#: includes/any-post.php:670, modules/containers/acf_field.php:353, modules/containers/custom_field.php:357
|
778 |
+
msgid "Failed to move post \"%1$s\" (%2$d) to the trash"
|
779 |
msgstr ""
|
780 |
|
781 |
+
#: includes/any-post.php:778
|
782 |
msgid "%d post deleted."
|
783 |
msgid_plural "%d posts deleted."
|
784 |
msgstr[0] ""
|
785 |
msgstr[1] ""
|
786 |
|
787 |
+
#: includes/any-post.php:780
|
788 |
msgid "%d page deleted."
|
789 |
msgid_plural "%d pages deleted."
|
790 |
msgstr[0] ""
|
791 |
msgstr[1] ""
|
792 |
|
793 |
+
#: includes/any-post.php:782
|
794 |
+
msgid "%1$d \"%2$s\" deleted."
|
795 |
+
msgid_plural "%1$d \"%2$s\" deleted."
|
796 |
msgstr[0] ""
|
797 |
msgstr[1] ""
|
798 |
|
799 |
+
#: includes/any-post.php:801
|
800 |
msgid "%d post moved to the Trash."
|
801 |
msgid_plural "%d posts moved to the Trash."
|
802 |
msgstr[0] ""
|
803 |
msgstr[1] ""
|
804 |
|
805 |
+
#: includes/any-post.php:803
|
806 |
msgid "%d page moved to the Trash."
|
807 |
msgid_plural "%d pages moved to the Trash."
|
808 |
msgstr[0] ""
|
809 |
msgstr[1] ""
|
810 |
|
811 |
+
#: includes/any-post.php:805
|
812 |
+
msgid "%1$d \"%2$s\" moved to the Trash."
|
813 |
+
msgid_plural "%1$d \"%2$s\" moved to the Trash."
|
814 |
msgstr[0] ""
|
815 |
msgstr[1] ""
|
816 |
|
817 |
#: includes/containers.php:122
|
818 |
+
msgid "%1$d '%2$s' has been deleted"
|
819 |
+
msgid_plural "%1$d '%2$s' have been deleted"
|
820 |
msgstr[0] ""
|
821 |
msgstr[1] ""
|
822 |
|
823 |
+
#: includes/containers.php:923, includes/containers.php:941
|
824 |
msgid "Container type '%s' not recognized"
|
825 |
msgstr ""
|
826 |
|
919 |
msgid "Pages"
|
920 |
msgstr ""
|
921 |
|
922 |
+
#: includes/instances.php:109, includes/instances.php:165
|
923 |
+
msgid "Container %1$s[%2$d] not found"
|
924 |
msgstr ""
|
925 |
|
926 |
+
#: includes/instances.php:118, includes/instances.php:174
|
927 |
msgid "Parser '%s' not found."
|
928 |
msgstr ""
|
929 |
|
953 |
msgstr ""
|
954 |
|
955 |
#: includes/link-query.php:47
|
956 |
+
msgctxt "filter heading"
|
957 |
msgid "Warnings"
|
958 |
msgstr ""
|
959 |
|
993 |
msgid "Search Results"
|
994 |
msgstr ""
|
995 |
|
996 |
+
#: includes/link-query.php:83, includes/link-query.php:131
|
997 |
msgid "No links found for your query"
|
998 |
msgstr ""
|
999 |
|
1000 |
+
#: includes/links.php:225
|
1001 |
msgid "The plugin script was terminated while trying to check the link."
|
1002 |
msgstr ""
|
1003 |
|
1013 |
msgid "Link is valid."
|
1014 |
msgs
|