/wp-content/plugins/broken-link-checker/core/core.php
PHP | 3100 lines | 2270 code | 374 blank | 456 comment | 238 complexity | 41a237c91dd4ed7ae616689aa36ee5cb MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, AGPL-1.0
- <?php
- /**
- * Simple function to replicate PHP 5 behaviour
- */
- if ( !function_exists( 'microtime_float' ) ) {
- function microtime_float()
- {
- list($usec, $sec) = explode(" ", microtime());
- return ((float)$usec + (float)$sec);
- }
- }
- require BLC_DIRECTORY . '/includes/screen-options/screen-options.php';
- require BLC_DIRECTORY . '/includes/screen-meta-links.php';
- require BLC_DIRECTORY . '/includes/wp-mutex.php';
- if (!class_exists('wsBrokenLinkChecker')) {
- class wsBrokenLinkChecker {
- var $conf;
-
- var $loader;
- var $my_basename = '';
-
- var $db_version; //The required version of the plugin's DB schema.
-
- var $execution_start_time; //Used for a simple internal execution timer in start_timer()/execution_time()
-
- /**
- * wsBrokenLinkChecker::wsBrokenLinkChecker()
- * Class constructor
- *
- * @param string $loader The fully qualified filename of the loader script that WP identifies as the "main" plugin file.
- * @param blcConfigurationManager $conf An instance of the configuration manager
- * @return void
- */
- function wsBrokenLinkChecker ( $loader, &$conf ) {
- global $wpdb;
- $this->db_version = BLC_DATABASE_VERSION;
-
- $this->conf = &$conf;
- $this->loader = $loader;
- $this->my_basename = plugin_basename( $this->loader );
- $this->load_language();
-
- //Unlike the activation hook, the deactivation callback *can* be registered in this file
- //because deactivation happens after this class has already been instantiated (durinng the
- //'init' action).
- register_deactivation_hook($loader, array(&$this, 'deactivation'));
-
- add_action('admin_menu', array(&$this,'admin_menu'));
- //Load jQuery on Dashboard pages (probably redundant as WP already does that)
- add_action('admin_print_scripts', array(&$this,'admin_print_scripts'));
-
- //The dashboard widget
- add_action('wp_dashboard_setup', array(&$this, 'hook_wp_dashboard_setup'));
-
- //AJAXy hooks
- add_action( 'wp_ajax_blc_full_status', array(&$this,'ajax_full_status') );
- add_action( 'wp_ajax_blc_dashboard_status', array(&$this,'ajax_dashboard_status') );
- add_action( 'wp_ajax_blc_work', array(&$this,'ajax_work') );
- add_action( 'wp_ajax_blc_discard', array(&$this,'ajax_discard') );
- add_action( 'wp_ajax_blc_edit', array(&$this,'ajax_edit') );
- add_action( 'wp_ajax_blc_link_details', array(&$this,'ajax_link_details') );
- add_action( 'wp_ajax_blc_unlink', array(&$this,'ajax_unlink') );
- add_action( 'wp_ajax_blc_current_load', array(&$this,'ajax_current_load') );
- add_action( 'wp_ajax_blc_dismiss', array($this, 'ajax_dismiss') );
- add_action( 'wp_ajax_blc_undismiss', array($this, 'ajax_undismiss') );
-
- //Add/remove Cron events
- $this->setup_cron_events();
-
- //Set hooks that listen for our Cron actions
- add_action('blc_cron_email_notifications', array( &$this, 'maybe_send_email_notifications' ));
- add_action('blc_cron_check_links', array(&$this, 'cron_check_links'));
- add_action('blc_cron_database_maintenance', array(&$this, 'database_maintenance'));
- add_action('blc_cron_check_news', array(&$this, 'check_news'));
-
- //Set the footer hook that will call the worker function via AJAX.
- add_action('admin_footer', array(&$this,'admin_footer'));
-
- //Add a "Screen Options" panel to the "Broken Links" page
- add_screen_options_panel(
- 'blc-screen-options',
- '',
- array(&$this, 'screen_options_html'),
- 'tools_page_view-broken-links',
- array(&$this, 'ajax_save_screen_options'),
- true
- );
- }
- /**
- * Output the script that runs the link monitor while the Dashboard is open.
- *
- * @return void
- */
- function admin_footer(){
- if ( !$this->conf->options['run_in_dashboard'] ){
- return;
- }
- ?>
- <!-- wsblc admin footer -->
- <script type='text/javascript'>
- (function($){
-
- //(Re)starts the background worker thread
- function blcDoWork(){
- $.post(
- "<?php echo admin_url('admin-ajax.php'); ?>",
- {
- 'action' : 'blc_work'
- }
- );
- }
- //Call it the first time
- blcDoWork();
-
- //Then call it periodically every X seconds
- setInterval(blcDoWork, <?php echo (intval($this->conf->options['max_execution_time']) + 1 )*1000; ?>);
-
- })(jQuery);
- </script>
- <!-- /wsblc admin footer -->
- <?php
- }
-
- /**
- * Check if an URL matches the exclusion list.
- *
- * @param string $url
- * @return bool
- */
- function is_excluded($url){
- if (!is_array($this->conf->options['exclusion_list'])) return false;
- foreach($this->conf->options['exclusion_list'] as $excluded_word){
- if (stristr($url, $excluded_word)){
- return true;
- }
- }
- return false;
- }
- function dashboard_widget(){
- ?>
- <p id='wsblc_activity_box'><?php _e('Loading...', 'broken-link-checker'); ?></p>
- <script type='text/javascript'>
- jQuery( function($){
- var blc_was_autoexpanded = false;
-
- function blcDashboardStatus(){
- $.getJSON(
- "<?php echo admin_url('admin-ajax.php'); ?>",
- {
- 'action' : 'blc_dashboard_status',
- 'random' : Math.random()
- },
- function (data, textStatus){
- if ( data && ( typeof(data.text) != 'undefined' ) ) {
- $('#wsblc_activity_box').html(data.text);
- <?php if ( $this->conf->options['autoexpand_widget'] ) { ?>
- //Expand the widget if there are broken links.
- //Do this only once per pageload so as not to annoy the user.
- if ( !blc_was_autoexpanded && ( data.status.broken_links > 0 ) ){
- $('#blc_dashboard_widget.postbox').removeClass('closed');
- blc_was_autoexpanded = true;
- }
- <?php } ?>
- } else {
- $('#wsblc_activity_box').html('<?php _e('[ Network error ]', 'broken-link-checker'); ?>');
- }
-
- setTimeout( blcDashboardStatus, 120*1000 ); //...update every two minutes
- }
- );
- }
-
- blcDashboardStatus();//Call it the first time
-
- } );
- </script>
- <?php
- }
- function dashboard_widget_control(
- /** @noinspection PhpUnusedParameterInspection */ $widget_id, $form_inputs = array()
- ){
- if ( 'POST' == $_SERVER['REQUEST_METHOD'] && 'blc_dashboard_widget' == $_POST['widget_id'] ) {
- //It appears $form_inputs isn't used in the current WP version, so lets just use $_POST
- $this->conf->options['autoexpand_widget'] = !empty($_POST['blc-autoexpand']);
- $this->conf->save_options();
- }
-
- ?>
- <p><label for="blc-autoexpand">
- <input id="blc-autoexpand" name="blc-autoexpand" type="checkbox" value="1" <?php if ( $this->conf->options['autoexpand_widget'] ) echo 'checked="checked"'; ?> />
- <?php _e('Automatically expand the widget if broken links have been detected', 'broken-link-checker'); ?>
- </label></p>
- <?php
- }
- function admin_print_scripts(){
- //jQuery is used for triggering the link monitor via AJAX when any admin page is open.
- wp_enqueue_script('jquery');
- }
-
- function enqueue_settings_scripts(){
- //jQuery UI is used on the settings page
- wp_enqueue_script('jquery-ui-core'); //Used for background color animation
- wp_enqueue_script('jquery-ui-dialog');
- wp_enqueue_script('jquery-ui-tabs');
- wp_enqueue_script('jquery-cookie', plugins_url('js/jquery.cookie.js', BLC_PLUGIN_FILE)); //Used for storing last widget states, etc
- }
-
- function enqueue_link_page_scripts(){
- wp_enqueue_script('jquery-ui-core'); //Used for background color animation
- wp_enqueue_script('jquery-ui-dialog'); //Used for the search form
- wp_enqueue_script('sprintf', plugins_url('js/sprintf.js', BLC_PLUGIN_FILE)); //Used in error messages
- }
-
- /**
- * Initiate a full recheck - reparse everything and check all links anew.
- *
- * @return void
- */
- function initiate_recheck(){
- global $wpdb; /** @var wpdb $wpdb */
- //Delete all discovered instances
- $wpdb->query("TRUNCATE {$wpdb->prefix}blc_instances");
-
- //Delete all discovered links
- $wpdb->query("TRUNCATE {$wpdb->prefix}blc_links");
-
- //Mark all posts, custom fields and bookmarks for processing.
- blc_resynch(true);
- }
- /**
- * A hook executed when the plugin is deactivated.
- *
- * @return void
- */
- function deactivation(){
- //Remove our Cron events
- wp_clear_scheduled_hook('blc_cron_check_links');
- wp_clear_scheduled_hook('blc_cron_email_notifications');
- wp_clear_scheduled_hook('blc_cron_database_maintenance');
- wp_clear_scheduled_hook('blc_cron_check_news');
- //Note the deactivation time for each module. This will help them
- //synch up propely if/when the plugin is reactivated.
- $moduleManager = blcModuleManager::getInstance();
- $the_time = current_time('timestamp');
- foreach($moduleManager->get_active_modules() as $module_id => $module){
- $this->conf->options['module_deactivated_when'][$module_id] = $the_time;
- }
- $this->conf->save_options();
- }
-
- /**
- * Perform various database maintenance tasks on the plugin's tables.
- *
- * Removes records that reference disabled containers and parsers,
- * deletes invalid instances and links, optimizes tables, etc.
- *
- * @return void
- */
- function database_maintenance(){
- blcContainerHelper::cleanup_containers();
- blc_cleanup_instances();
- blc_cleanup_links();
-
- blcUtility::optimize_database();
- }
- /**
- * Create the plugin's menu items and enqueue their scripts and CSS.
- * Callback for the 'admin_menu' action.
- *
- * @return void
- */
- function admin_menu(){
- if (current_user_can('manage_options'))
- add_filter('plugin_action_links', array(&$this, 'plugin_action_links'), 10, 2);
-
- $options_page_hook = add_options_page(
- __('Link Checker Settings', 'broken-link-checker'),
- __('Link Checker', 'broken-link-checker'),
- 'manage_options',
- 'link-checker-settings',array(&$this, 'options_page')
- );
-
- $menu_title = __('Broken Links', 'broken-link-checker');
- if ( $this->conf->options['show_link_count_bubble'] ){
- //To make it easier to notice when broken links appear, display the current number of
- //broken links in a little bubble notification in the "Broken Links" menu.
- //(Similar to how the number of plugin updates and unmoderated comments is displayed).
- $blc_link_query = blcLinkQuery::getInstance();
- $broken_links = $blc_link_query->get_filter_links('broken', array('count_only' => true));
- if ( $broken_links > 0 ){
- //TODO: Appropriating existing CSS classes for my own purposes is hacky. Fix eventually.
- $menu_title .= sprintf(
- ' <span class="update-plugins"><span class="update-count blc-menu-bubble">%d</span></span>',
- $broken_links
- );
- }
- }
- $links_page_hook = add_management_page(
- __('View Broken Links', 'broken-link-checker'),
- $menu_title,
- 'edit_others_posts',
- 'view-broken-links',array(&$this, 'links_page')
- );
-
- //Add plugin-specific scripts and CSS only to the it's own pages
- add_action( 'admin_print_styles-' . $options_page_hook, array(&$this, 'options_page_css') );
- add_action( 'admin_print_styles-' . $links_page_hook, array(&$this, 'links_page_css') );
- add_action( 'admin_print_scripts-' . $options_page_hook, array(&$this, 'enqueue_settings_scripts') );
- add_action( 'admin_print_scripts-' . $links_page_hook, array(&$this, 'enqueue_link_page_scripts') );
-
- //Add a "Feedback" button that links to the plugin's UserVoice forum
- add_screen_meta_link(
- 'blc-feedback-widget',
- __('Feedback', 'broken-link-checker'),
- 'http://whiteshadow.uservoice.com/forums/58400-broken-link-checker',
- array($options_page_hook, $links_page_hook)
- );
- //Add a link to the Admin Menu Editor site to the "Broken Links" page.
- if ( !$this->conf->get('user_has_donated') ) {
- //Choose anchor text randomly.
- $possible_anchor_texts = array(
- 'Organize WordPress admin menu',
- 'Simplify WordPress Admin Menu',
- 'Customize WP Admin Menu',
- 'Organize WP Admin: use Admin Menu Editor',
- 'Web Developer? Check out Admin Menu Editor',
- 'Admin Menu Editor for WP',
- 'Organize, Hide And Customize Admin Menus',
- );
- $index = $this->conf->get('view-broken-links-meta-ad', null);
- if ( $index === null ) {
- $index = rand(0, count($possible_anchor_texts) - 1);
- $this->conf->set('view-broken-links-meta-ad', $index);
- $this->conf->save_options();
- }
- add_screen_meta_link(
- 'blc-more-plugins-link',
- $possible_anchor_texts[$index],
- sprintf(
- 'http://w-shadow.com/admin-menu-editor-pro/?utm_source=broken_link_checker&utm_medium=Broken_Links_meta_link&utm_campaign=Plugins&utm_content=copy-a%s',
- urlencode($index)
- ),
- $links_page_hook,
- array('style' => 'font-weight: bold;')
- );
- }
- //Make the Settings page link to the link list
- add_screen_meta_link(
- 'blc-links-page-link',
- __('Go to Broken Links', 'broken-link-checker'),
- admin_url('tools.php?page=view-broken-links'),
- $options_page_hook,
- array('style' => 'font-weight: bold;')
- );
-
- //Add a link to the latest blog post/whatever about this plugin, if any.
- if ( isset($this->conf->options['plugin_news']) && !empty($this->conf->options['plugin_news']) ){
- $news = $this->conf->options['plugin_news'];
- add_screen_meta_link(
- 'blc-plugin-news-link',
- $news[0],
- $news[1],
- array($options_page_hook, $links_page_hook)
- );
- }
- }
-
- /**
- * plugin_action_links()
- * Handler for the 'plugin_action_links' hook. Adds a "Settings" link to this plugin's entry
- * on the plugin list.
- *
- * @param array $links
- * @param string $file
- * @return array
- */
- function plugin_action_links($links, $file) {
- if ($file == $this->my_basename)
- $links[] = "<a href='options-general.php?page=link-checker-settings'>" . __('Settings') . "</a>";
- return $links;
- }
- function options_page(){
- global $blclog;
- $moduleManager = blcModuleManager::getInstance();
- //Prior to 1.5.2 (released 2012-05-27), there was a bug that would cause the donation flag to be
- //set incorrectly. So we'll unset the flag in that case.
- $reset_donation_flag =
- ($this->conf->get('first_installation_timestamp', 0) < strtotime('2012-05-27 00:00')) &&
- !$this->conf->get('donation_flag_fixed', false);
- if ( $reset_donation_flag) {
- $this->conf->set('user_has_donated', false);
- $this->conf->set('donation_flag_fixed', true);
- $this->conf->save_options();
- }
-
- if (isset($_POST['recheck']) && !empty($_POST['recheck']) ){
- $this->initiate_recheck();
-
- //Redirect back to the settings page
- $base_url = remove_query_arg( array('_wpnonce', 'noheader', 'updated', 'error', 'action', 'message') );
- wp_redirect( add_query_arg( array( 'recheck-initiated' => true), $base_url ) );
- die();
- }
-
- if(isset($_POST['submit'])) {
- check_admin_referer('link-checker-options');
-
- //Activate/deactivate modules
- if ( !empty($_POST['module']) ){
- $active = array_keys($_POST['module']);
- $moduleManager->set_active_modules($active);
- }
-
- //Only post statuses that actually exist can be selected
- if ( isset($_POST['enabled_post_statuses']) && is_array($_POST['enabled_post_statuses']) ){
- $available_statuses = get_post_stati();
- $enabled_post_statuses = array_intersect($_POST['enabled_post_statuses'], $available_statuses);
- } else {
- $enabled_post_statuses = array();
- }
- //At least one status must be enabled; defaults to "Published".
- if ( empty($enabled_post_statuses) ){
- $enabled_post_statuses = array('publish');
- }
- $this->conf->options['enabled_post_statuses'] = $enabled_post_statuses;
-
- //The execution time limit must be above zero
- $new_execution_time = intval($_POST['max_execution_time']);
- if( $new_execution_time > 0 ){
- $this->conf->options['max_execution_time'] = $new_execution_time;
- }
- //The check threshold also must be > 0
- $new_check_threshold=intval($_POST['check_threshold']);
- if( $new_check_threshold > 0 ){
- $this->conf->options['check_threshold'] = $new_check_threshold;
- }
-
- $this->conf->options['mark_broken_links'] = !empty($_POST['mark_broken_links']);
- $new_broken_link_css = trim($_POST['broken_link_css']);
- $this->conf->options['broken_link_css'] = $new_broken_link_css;
-
- $this->conf->options['mark_removed_links'] = !empty($_POST['mark_removed_links']);
- $new_removed_link_css = trim($_POST['removed_link_css']);
- $this->conf->options['removed_link_css'] = $new_removed_link_css;
-
- $this->conf->options['nofollow_broken_links'] = !empty($_POST['nofollow_broken_links']);
- $this->conf->options['exclusion_list'] = array_filter(
- preg_split(
- '/[\s\r\n]+/', //split on newlines and whitespace
- $_POST['exclusion_list'],
- -1,
- PREG_SPLIT_NO_EMPTY //skip empty values
- )
- );
-
- //Parse the custom field list
- $new_custom_fields = array_filter(
- preg_split( '/[\r\n]+/', $_POST['blc_custom_fields'], -1, PREG_SPLIT_NO_EMPTY )
- );
-
- //Calculate the difference between the old custom field list and the new one (used later)
- $diff1 = array_diff( $new_custom_fields, $this->conf->options['custom_fields'] );
- $diff2 = array_diff( $this->conf->options['custom_fields'], $new_custom_fields );
- $this->conf->options['custom_fields'] = $new_custom_fields;
-
- //HTTP timeout
- $new_timeout = intval($_POST['timeout']);
- if( $new_timeout > 0 ){
- $this->conf->options['timeout'] = $new_timeout ;
- }
-
- //Server load limit
- if ( isset($_POST['server_load_limit']) ){
- $this->conf->options['server_load_limit'] = floatval($_POST['server_load_limit']);
- if ( $this->conf->options['server_load_limit'] < 0 ){
- $this->conf->options['server_load_limit'] = 0;
- }
-
- $this->conf->options['enable_load_limit'] = $this->conf->options['server_load_limit'] > 0;
- }
-
- //When to run the checker
- $this->conf->options['run_in_dashboard'] = !empty($_POST['run_in_dashboard']);
- $this->conf->options['run_via_cron'] = !empty($_POST['run_via_cron']);
-
- //Email notifications on/off
- $email_notifications = !empty($_POST['send_email_notifications']);
- $send_authors_email_notifications = !empty($_POST['send_authors_email_notifications']);
- if (
- ($email_notifications && !$this->conf->options['send_email_notifications'])
- || ($send_authors_email_notifications && !$this->conf->options['send_authors_email_notifications'])
- ){
- /*
- The plugin should only send notifications about links that have become broken
- since the time when email notifications were turned on. If we don't do this,
- the first email notification will be sent nigh-immediately and list *all* broken
- links that the plugin currently knows about.
- */
- $this->conf->options['last_notification_sent'] = time();
- }
- $this->conf->options['send_email_notifications'] = $email_notifications;
- $this->conf->options['send_authors_email_notifications'] = $send_authors_email_notifications;
- //Make settings that affect our Cron events take effect immediately
- $this->setup_cron_events();
-
- $this->conf->save_options();
-
- /*
- If the list of custom fields was modified then we MUST resynchronize or
- custom fields linked with existing posts may not be detected. This is somewhat
- inefficient.
- */
- if ( ( count($diff1) > 0 ) || ( count($diff2) > 0 ) ){
- $manager = blcContainerHelper::get_manager('custom_field');
- if ( !is_null($manager) ){
- $manager->resynch();
- blc_got_unsynched_items();
- }
- }
-
- //Redirect back to the settings page
- $base_url = remove_query_arg( array('_wpnonce', 'noheader', 'updated', 'error', 'action', 'message') );
- wp_redirect( add_query_arg( array( 'settings-updated' => true), $base_url ) );
- }
-
- //Show a confirmation message when settings are saved.
- if ( !empty($_GET['settings-updated']) ){
- echo '<div id="message" class="updated fade"><p><strong>',__('Settings saved.', 'broken-link-checker'), '</strong></p></div>';
-
- }
-
- //Show a thank-you message when a donation is made.
- if ( !empty($_GET['donated']) ){
- echo '<div id="message" class="updated fade"><p><strong>',__('Thank you for your donation!', 'broken-link-checker'), '</strong></p></div>';
- $this->conf->set('user_has_donated', true);
- $this->conf->save_options();
- }
-
- //Show one when recheck is started, too.
- if ( !empty($_GET['recheck-initiated']) ){
- echo '<div id="message" class="updated fade"><p><strong>',
- __('Complete site recheck started.', 'broken-link-checker'), // -- Yoda
- '</strong></p></div>';
- }
-
- //Cull invalid and missing modules
- $moduleManager->validate_active_modules();
-
- $debug = $this->get_debug_info();
-
- $details_text = __('Details', 'broken-link-checker');
- add_filter('blc-module-settings-custom_field', array(&$this, 'make_custom_field_input'), 10, 2);
-
- //Translate and markup-ify module headers for display
- $modules = $moduleManager->get_modules_by_category('', true, true);
-
- //Output the custom broken link/removed link styles for example links
- printf(
- '<style type="text/css">%s %s</style>',
- $this->conf->options['broken_link_css'],
- $this->conf->options['removed_link_css']
- );
-
- $section_names = array(
- 'general' => __('General', 'broken-link-checker'),
- 'where' => __('Look For Links In', 'broken-link-checker'),
- 'which' => __('Which Links To Check', 'broken-link-checker'),
- 'how' => __('Protocols & APIs', 'broken-link-checker'),
- 'advanced' => __('Advanced', 'broken-link-checker'),
- );
- ?>
-
- <!--[if lte IE 7]>
- <style type="text/css">
- /* Simulate inline-block in IE7 */
- ul.ui-tabs-nav li {
- display: inline;
- zoom: 1;
- }
- </style>
- <![endif]-->
-
- <div class="wrap" id="blc-settings-wrap">
- <?php screen_icon(); ?><h2><?php _e('Broken Link Checker Options', 'broken-link-checker'); ?></h2>
-
-
- <div id="blc-sidebar">
- <div class="metabox-holder">
- <?php include BLC_DIRECTORY . '/includes/admin/sidebar.php'; ?>
- </div>
- </div>
-
-
- <div id="blc-admin-content">
-
- <form name="link_checker_options" id="link_checker_options" method="post" action="<?php
- echo admin_url('options-general.php?page=link-checker-settings&noheader=1');
- ?>">
- <?php
- wp_nonce_field('link-checker-options');
- ?>
-
- <div id="blc-tabs">
-
- <ul class="hide-if-no-js">
- <?php
- foreach($section_names as $section_id => $section_name){
- printf(
- '<li id="tab-button-%s"><a href="#section-%s" title="%s">%s</a></li>',
- esc_attr($section_id),
- esc_attr($section_id),
- esc_attr($section_name),
- $section_name
- );
- }
- ?>
- </ul>
- <div id="section-general" class="blc-section">
- <h3 class="hide-if-js"><?php echo $section_names['general']; ?></h3>
-
- <table class="form-table">
- <tr valign="top">
- <th scope="row">
- <?php _e('Status','broken-link-checker'); ?>
- <br>
- <a href="javascript:void(0)" id="blc-debug-info-toggle"><?php _e('Show debug info', 'broken-link-checker'); ?></a>
- </th>
- <td>
- <div id='wsblc_full_status'>
- <br/><br/><br/>
- </div>
-
- <table id="blc-debug-info">
- <?php
-
- //Output the debug info in a table
- foreach( $debug as $key => $value ){
- printf (
- '<tr valign="top" class="blc-debug-item-%s"><th scope="row">%s</th><td>%s<div class="blc-debug-message">%s</div></td></tr>',
- $value['state'],
- $key,
- $value['value'],
- ( array_key_exists('message', $value)?$value['message']:'')
- );
- }
- ?>
- </table>
-
- </td>
- </tr>
- <tr valign="top">
- <th scope="row"><?php _e('Check each link','broken-link-checker'); ?></th>
- <td>
- <?php
- printf(
- __('Every %s hours','broken-link-checker'),
- sprintf(
- '<input type="text" name="check_threshold" id="check_threshold" value="%d" size="5" maxlength="5" />',
- $this->conf->options['check_threshold']
- )
- );
- ?>
- <br/>
- <span class="description">
- <?php _e('Existing links will be checked this often. New links will usually be checked ASAP.', 'broken-link-checker'); ?>
- </span>
- </td>
- </tr>
-
- <tr valign="top">
- <th scope="row"><?php _e('E-mail notifications', 'broken-link-checker'); ?></th>
- <td>
- <p style="margin-top: 0;">
- <label for='send_email_notifications'>
- <input type="checkbox" name="send_email_notifications" id="send_email_notifications"
- <?php if ($this->conf->options['send_email_notifications']) echo ' checked="checked"'; ?>/>
- <?php _e('Send me e-mail notifications about newly detected broken links', 'broken-link-checker'); ?>
- </label><br />
- </p>
-
- <p>
- <label for='send_authors_email_notifications'>
- <input type="checkbox" name="send_authors_email_notifications" id="send_authors_email_notifications"
- <?php if ($this->conf->options['send_authors_email_notifications']) echo ' checked="checked"'; ?>/>
- <?php _e('Send authors e-mail notifications about broken links in their posts', 'broken-link-checker'); ?>
- </label><br />
- </p>
- </td>
- </tr>
- <tr valign="top">
- <th scope="row"><?php _e('Link tweaks','broken-link-checker'); ?></th>
- <td>
- <p style="margin-top: 0; margin-bottom: 0.5em;">
- <label for='mark_broken_links'>
- <input type="checkbox" name="mark_broken_links" id="mark_broken_links"
- <?php if ($this->conf->options['mark_broken_links']) echo ' checked="checked"'; ?>/>
- <?php _e('Apply custom formatting to broken links', 'broken-link-checker'); ?>
- </label>
- |
- <a id="toggle-broken-link-css-editor" href="#" class="blc-toggle-link"><?php
- _e('Edit CSS', 'broken-link-checker');
- ?></a>
- </p>
-
- <div id="broken-link-css-wrap"<?php
- if ( !blcUtility::get_cookie('broken-link-css-wrap', false) ){
- echo ' class="hidden"';
- }
- ?>>
- <textarea name="broken_link_css" id="broken_link_css" cols='45' rows='4'/><?php
- if( isset($this->conf->options['broken_link_css']) ) {
- echo $this->conf->options['broken_link_css'];
- }
- ?></textarea>
- <p class="description"><?php
- printf(
- __('Example : Lorem ipsum <a %s>broken link</a>, dolor sit amet.', 'broken-link-checker'),
- ' href="#" class="broken_link" onclick="return false;"'
- );
- echo ' ', __('Click "Save Changes" to update example output.', 'broken-link-checker');
- ?></p>
- </div>
-
- <p style="margin-bottom: 0.5em;">
- <label for='mark_removed_links'>
- <input type="checkbox" name="mark_removed_links" id="mark_removed_links"
- <?php if ($this->conf->options['mark_removed_links']) echo ' checked="checked"'; ?>/>
- <?php _e('Apply custom formatting to removed links', 'broken-link-checker'); ?>
- </label>
- |
- <a id="toggle-removed-link-css-editor" href="#" class="blc-toggle-link"><?php
- _e('Edit CSS', 'broken-link-checker');
- ?></a>
- </p>
-
- <div id="removed-link-css-wrap" <?php
- if ( !blcUtility::get_cookie('removed-link-css-wrap', false) ){
- echo ' class="hidden"';
- }
- ?>>
- <textarea name="removed_link_css" id="removed_link_css" cols='45' rows='4'/><?php
- if( isset($this->conf->options['removed_link_css']) )
- echo $this->conf->options['removed_link_css'];
- ?></textarea>
-
- <p class="description"><?php
- printf(
- __('Example : Lorem ipsum <span %s>removed link</span>, dolor sit amet.', 'broken-link-checker'),
- ' class="removed_link"'
- );
- echo ' ', __('Click "Save Changes" to update example output.', 'broken-link-checker');
- ?>
- </p>
- </div>
-
- <p>
- <label for='nofollow_broken_links'>
- <input type="checkbox" name="nofollow_broken_links" id="nofollow_broken_links"
- <?php if ($this->conf->options['nofollow_broken_links']) echo ' checked="checked"'; ?>/>
- <?php _e('Stop search engines from following broken links', 'broken-link-checker'); ?>
- </label>
- </p>
- </td>
- </tr>
-
- </table>
-
- </div>
-
- <div id="section-where" class="blc-section">
- <h3 class="hide-if-js"><?php echo $section_names['where']; ?></h3>
-
- <table class="form-table">
-
- <tr valign="top">
- <th scope="row"><?php _e('Look for links in', 'broken-link-checker'); ?></th>
- <td>
- <?php
- if ( !empty($modules['container']) ){
- uasort($modules['container'], create_function('$a, $b', 'return strcasecmp($a["Name"], $b["Name"]);'));
- $this->print_module_list($modules['container'], $this->conf->options);
- }
- ?>
- </td></tr>
-
- <tr valign="top">
- <th scope="row"><?php _e('Post statuses', 'broken-link-checker'); ?></th>
- <td>
- <?php
- $available_statuses = get_post_stati(array('internal' => false), 'objects');
-
- if ( isset($this->conf->options['enabled_post_statuses']) ){
- $enabled_post_statuses = $this->conf->options['enabled_post_statuses'];
- } else {
- $enabled_post_statuses = array();
- }
-
- foreach($available_statuses as $status => $status_object){
- printf(
- '<p><label><input type="checkbox" name="enabled_post_statuses[]" value="%s"%s> %s</label></p>',
- esc_attr($status),
- in_array($status, $enabled_post_statuses)?' checked="checked"':'',
- $status_object->label
- );
- }
- ?>
- </td></tr>
-
- </table>
-
- </div>
-
-
- <div id="section-which" class="blc-section">
- <h3 class="hide-if-js"><?php echo $section_names['which']; ?></h3>
-
- <table class="form-table">
-
- <tr valign="top">
- <th scope="row"><?php _e('Link types', 'broken-link-checker'); ?></th>
- <td>
- <?php
- if ( !empty($modules['parser']) ){
- $this->print_module_list($modules['parser'], $this->conf->options);
- } else {
- echo __('Error : All link parsers missing!', 'broken-link-checker');
- }
- ?>
- </td>
- </tr>
-
- <tr valign="top">
- <th scope="row"><?php _e('Exclusion list', 'broken-link-checker'); ?></th>
- <td><?php _e("Don't check links where the URL contains any of these words (one per line) :", 'broken-link-checker'); ?><br/>
- <textarea name="exclusion_list" id="exclusion_list" cols='45' rows='4' wrap='off'/><?php
- if( isset($this->conf->options['exclusion_list']) )
- echo implode("\n", $this->conf->options['exclusion_list']);
- ?></textarea>
- </td>
- </tr>
-
- </table>
- </div>
-
- <div id="section-how" class="blc-section">
- <h3 class="hide-if-js"><?php echo $section_names['how']; ?></h3>
-
- <table class="form-table">
-
- <tr valign="top">
- <th scope="row"><?php _e('Check links using', 'broken-link-checker'); ?></th>
- <td>
- <?php
- if ( !empty($modules['checker']) ){
- $modules['checker'] = array_reverse($modules['checker']);
- $this->print_module_list($modules['checker'], $this->conf->options);
- }
- ?>
- </td></tr>
-
- </table>
- </div>
-
- <div id="section-advanced" class="blc-section">
- <h3 class="hide-if-js"><?php echo $section_names['advanced']; ?></h3>
-
- <table class="form-table">
-
- <tr valign="top">
- <th scope="row"><?php _e('Timeout', 'broken-link-checker'); ?></th>
- <td>
- <?php
-
- printf(
- __('%s seconds', 'broken-link-checker'),
- sprintf(
- '<input type="text" name="timeout" id="blc_timeout" value="%d" size="5" maxlength="3" />',
- $this->conf->options['timeout']
- )
- );
-
- ?>
- <br/><span class="description">
- <?php _e('Links that take longer than this to load will be marked as broken.','broken-link-checker'); ?>
- </span>
- </td>
- </tr>
-
- <tr valign="top">
- <th scope="row"><?php _e('Link monitor', 'broken-link-checker'); ?></th>
- <td>
-
- <p>
- <label for='run_in_dashboard'>
-
- <input type="checkbox" name="run_in_dashboard" id="run_in_dashboard"
- <?php if ($this->conf->options['run_in_dashboard']) echo ' checked="checked"'; ?>/>
- <?php _e('Run continuously while the Dashboard is open', 'broken-link-checker'); ?>
- </label>
- </p>
-
- <p>
- <label for='run_via_cron'>
- <input type="checkbox" name="run_via_cron" id="run_via_cron"
- <?php if ($this->conf->options['run_via_cron']) echo ' checked="checked"'; ?>/>
- <?php _e('Run hourly in the background', 'broken-link-checker'); ?>
- </label>
- </p>
- </td>
- </tr>
-
- <tr valign="top">
- <th scope="row"><?php _e('Max. execution time', 'broken-link-checker'); ?></th>
- <td>
- <?php
-
- printf(
- __('%s seconds', 'broken-link-checker'),
- sprintf(
- '<input type="text" name="max_execution_time" id="max_execution_time" value="%d" size="5" maxlength="5" />',
- $this->conf->options['max_execution_time']
- )
- );
-
- ?>
- <br/><span class="description">
- <?php
-
- _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');
-
- ?>
- </span>
- </td>
- </tr>
-
- <tr valign="top">
- <th scope="row"><?php _e('Server load limit', 'broken-link-checker'); ?></th>
- <td>
- <?php
-
- $load = blcUtility::get_server_load();
- $available = !empty($load);
-
- if ( $available ){
- $value = !empty($this->conf->options['server_load_limit'])?sprintf('%.2f', $this->conf->options['server_load_limit']):'';
- printf(
- '<input type="text" name="server_load_limit" id="server_load_limit" value="%s" size="5" maxlength="5"/> ',
- $value
- );
-
- printf(
- __('Current load : %s', 'broken-link-checker'),
- '<span id="wsblc_current_load">...</span>'
- );
- echo '<br/><span class="description">';
- printf(
- __(
- '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.',
- 'broken-link-checker'
- ),
- 'http://en.wikipedia.org/wiki/Load_(computing)'
- );
- echo '</span>';
-
- } else {
- echo '<input type="text" disabled="disabled" value="', esc_attr(__('Not available', 'broken-link-checker')), '" size="13"/><br>';
- echo '<span class="description">';
- _e('Load limiting only works on Linux-like systems where <code>/proc/loadavg</code> is present and accessible.', 'broken-link-checker');
- echo '</span>';
- }
- ?>
- </td>
- </tr>
-
- <tr valign="top">
- <th scope="row"><?php _e('Forced recheck', 'broken-link-checker'); ?></th>
- <td>
- <input class="button" type="button" name="start-recheck" id="start-recheck"
- value="<?php _e('Re-check all pages', 'broken-link-checker'); ?>" />
- <input type="hidden" name="recheck" value="" id="recheck" />
- <br />
- <span class="description"><?php
- _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');
-
- ?></span>
- </td>
- </tr>
-
- </table>
- </div>
-
- </div>
-
- <p class="submit"><input type="submit" name="submit" class='button-primary' value="<?php _e('Save Changes') ?>" /></p>
- </form>
-
- </div> <!-- First postbox-container -->
-
-
- </div>
-
-
-
- <?php
- //The various JS for this page is stored in a separate file for the purposes readability.
- include dirname($this->loader) . '/includes/admin/options-page-js.php';
- }
-
- /**
- * Output a list of modules and their settings.
- *
- * Each list entry will contain a checkbox that is checked if the module is
- * currently active.
- *
- * @param array $modules Array of modules to display
- * @param array $current_settings
- * @return void
- */
- function print_module_list($modules, $current_settings){
- $moduleManager = blcModuleManager::getInstance();
-
- foreach($modules as $module_id => $module_data){
- $module_id = $module_data['ModuleID'];
-
- $style = $module_data['ModuleHidden']?' style="display:none;"':'';
-
- printf(
- '<div class="module-container" id="module-container-%s"%s>',
- $module_id,
- $style
- );
- $this->print_module_checkbox($module_id, $module_data, $moduleManager->is_active($module_id));
-
- $extra_settings = apply_filters(
- 'blc-module-settings-'.$module_id,
- '',
- $current_settings
- );
-
- if ( !empty($extra_settings) ){
-
- printf(
- ' | <a class="blc-toggle-link toggle-module-settings" id="toggle-module-settings-%s" href="#">%s</a>',
- $module_id,
- __('Configure', 'broken-link-checker')
- );
-
- //The plugin remembers the last open/closed state of module configuration boxes
- $box_id = 'module-extra-settings-' . $module_id;
- $show = blcUtility::get_cookie(
- $box_id,
- $moduleManager->is_active($module_id)
- );
-
- printf(
- '<div class="module-extra-settings%s" id="%s">%s</div>',
- $show?'':' hidden',
- $box_id,
- $extra_settings
- );
- }
-
- echo '</div>';
- }
- }
-
- /**
- * Output a checkbox for a module.
- *
- * Generates a simple checkbox that can be used to mark a module as active/inactive.
- * If the specified module can't be deactivated (ModuleAlwaysActive = true), the checkbox
- * will be displayed in a disabled state and a hidden field will be created to make
- * form submissions work correctly.
- *
- * @param string $module_id Module ID.
- * @param array $module_data Associative array of module data.
- * @param bool $active If true, the newly created checkbox will start out checked.
- * @return void
- */
- function print_module_checkbox($module_id, $module_data, $active = false){
- $disabled = false;
- $name_prefix = 'module';
- $label_class = '';
- $active = $active || $module_data['ModuleAlwaysActive'];
-
- if ( $module_data['ModuleAlwaysActive'] ){
- $disabled = true;
- $name_prefix = 'module-always-active';
- }
-
- $checked = $active ? ' checked="checked"':'';
- if ( $disabled ){
- $checked .= ' disabled="disabled"';
- }
-
- printf(
- '<label class="%s">
- <input type="checkbox" name="%s[%s]" id="module-checkbox-%s"%s /> %s
- </label>',
- esc_attr($label_class),
- $name_prefix,
- esc_attr($module_id),
- esc_attr($module_id),
- $checked,
- $module_data['Name']
- );
-
- if ( $module_data['ModuleAlwaysActive'] ){
- printf(
- '<input type="hidden" name="module[%s]" value="on">',
- esc_attr($module_id)
- );
- }
- }
-
- /**
- * Add extra settings to the "Custom fields" entry on the plugin's config. page.
- *
- * Callback for the 'blc-module-settings-custom_field' filter.
- *
- * @param string $html Current extra HTML
- * @param array $current_settings The current plugin configuration.
- * @return string New extra HTML.
- */
- function make_custom_field_input($html, $current_settings){
- $html .= '<span class="description">' .
- __('Check URLs entered in these custom fields (one per line) :', 'broken-link-checker') .
- '</span>';
- $html .= '<br><textarea name="blc_custom_fields" id="blc_custom_fields" cols="45" rows="4" />';
- if( isset($current_settings['custom_fields']) )
- $html .= implode("\n", $current_settings['custom_fields']);
- $html .= '</textarea>';
-
- return $html;
- }
-
- /**
- * Enqueue CSS file for the plugin's Settings page.
- *
- * @return void
- */
- function options_page_css(){
- wp_enqueue_style('blc-options-page', plugins_url('css/options-page.css', BLC_PLUGIN_FILE), array(), '20120527' );
- wp_enqueue_style('dashboard');
- }
-
- /**
- * Display the "Broken Links" page, listing links detected by the plugin and their status.
- *
- * @return void
- */
- function links_page(){
- global $wpdb, $blclog; /* @var wpdb $wpdb */
-
- $blc_link_query = blcLinkQuery::getInstance();
- //Cull invalid and missing modules so that we don't get dummy links/instances showing up.
- $moduleManager = blcModuleManager::getInstance();
- $moduleManager->validate_active_modules();
-
- if ( defined('BLC_DEBUG') && constant('BLC_DEBUG') ){
- //Make module headers translatable. They need to be formatted corrrectly and
- //placed in a .php file to be visible to the script(s) that generate .pot files.
- $code = $moduleManager->_build_header_translation_code();
- file_put_contents( dirname($this->loader) . '/includes/extra-strings.php', $code );
- }
-
- $action = !empty($_POST['action'])?$_POST['action']:'';
- if ( intval($action) == -1 ){
- //Try the second bulk actions box
- $action = !empty($_POST['action2'])?$_POST['action2']:'';
- }
-
- //Get the list of link IDs selected via checkboxes
- $selected_links = array();
- if ( isset($_POST['selected_links']) && is_array($_POST['selected_links']) ){
- //Convert all link IDs to integers (non-numeric entries are converted to zero)
- $selected_links = array_map('intval', $_POST['selected_links']);
- //Remove all zeroes
- $selected_links = array_filter($selected_links);
- }
-
- $message = '';
- $msg_class = 'updated';
-
- //Run the selected bulk action, if any
- $force_delete = false;
- switch ( $action ){
- case 'create-custom-filter':
- list($message, $msg_class) = $this->do_create_custom_filter();
- break;
-
- case 'delete-custom-filter':
- list($message, $msg_class) = $this->do_delete_custom_filter();
- break;
- /** @noinspection PhpMissingBreakStatementInspection Deliberate fall-through. */
- case 'bulk-delete-sources':
- $force_delete = true;
- case 'bulk-trash-sources':
- list($message, $msg_class) = $this->do_bulk_delete_sources($selected_links, $force_delete);
- break;
-
- case 'bulk-unlink':
- list($message, $msg_class) = $this->do_bulk_unlink($selected_links);
- break;
-
- case 'bulk-deredirect':
- list($message, $msg_class) = $this->do_bulk_deredirect($selected_links);
- break;
-
- case 'bulk-recheck':
- list($message, $msg_class) = $this->do_bulk_recheck($selected_links);
- break;
-
- case 'bulk-not-broken':
- list($message, $msg_class) = $this->do_bulk_discard($selected_links);
- break;
- case 'bulk-edit':
- list($message, $msg_class) = $this->do_bulk_edit($selected_links);
- break;
- }
-
-
- if ( !empty($message) ){
- echo '<div id="message" class="'.$msg_class.' fade"><p>'.$message.'</p></div>';
- }
-
- $start_time = microtime_float();
-
- //Load custom filters, if any
- $blc_link_query->load_custom_filters();
-
- //Calculate the number of links matching each filter
- $blc_link_query->count_filter_results();
-
- //Run the selected filter (defaults to displaying broken links)
- $selected_filter_id = isset($_GET['filter_id'])?$_GET['filter_id']:'broken';
- $current_filter = $blc_link_query->exec_filter(
- $selected_filter_id,
- isset($_GET['paged']) ? intval($_GET['paged']) : 1,
- $this->conf->options['table_links_per_page'],
- 'broken',
- isset($_GET['orderby']) ? $_GET['orderby'] : '',
- isset($_GET['order']) ? $_GET['order'] : ''
- );
- //exec_filter() returns an array with filter data, including the actual filter ID that was used.
- $filter_id = $current_filter['filter_id'];
- //Error?
- if ( empty($current_filter['links']) && !empty($wpdb->last_error) ){
- printf( __('Database error : %s', 'broken-link-checker'), $wpdb->last_error);
- }
- ?>
-
- <script type='text/javascript'>
- var blc_current_filter = '<?php echo $filter_id; ?>';
- var blc_is_broken_filter = <?php echo $current_filter['is_broken_filter'] ? 'true' : 'false'; ?>;
- var blc_current_base_filter = '<?php echo esc_js($current_filter['base_filter']); ?>';
- </script>
-
- <div class="wrap"><?php screen_icon(); ?>
- <?php
- $blc_link_query->print_filter_heading($current_filter);
- $blc_link_query->print_filter_menu($filter_id);
-
- //Display the "Search" form and associated buttons.
- //The form requires the $filter_id and $current_filter variables to be set.
- include dirname($this->loader) . '/includes/admin/search-form.php';
-
- //If the user has decided to switch the table to a different mode (compact/full),
- //save the new setting.
- if ( isset($_GET['compact']) ){
- $this->conf->options['table_compact'] = (bool)$_GET['compact'];
- $this->conf->save_options();
- }
- //Display the links, if any
- if( $current_filter['links'] && ( count($current_filter['links']) > 0 ) ) {
-
- include dirname($this->loader) . '/includes/admin/table-printer.php';
- $table = new blcTablePrinter($this);
- $table->print_table(
- $current_filter,
- $this->conf->options['table_layout'],
- $this->conf->options['table_visible_columns'],
- $this->conf->options['table_compact']
- );
- };
- printf('<!-- Total elapsed : %.4f seconds -->', microtime_float() - $start_time);
-
- //Load assorted JS event handlers and other shinies
- include dirname($this->loader) . '/includes/admin/links-page-js.php';
-
- ?></div><?php
- }
-
- /**
- * Create a custom link filter using params passed in $_POST.
- *
- * @uses $_POST
- * @uses $_GET to replace the current filter ID (if any) with that of the newly created filter.
- *
- * @return array Message and the CSS class to apply to the message.
- */
- function do_create_custom_filter(){
- global $wpdb;
- //Create a custom filter!
- check_admin_referer( 'create-custom-filter' );
- $msg_class = 'updated';
-
- //Filter name must be set
- if ( empty($_POST['name']) ){
- $message = __("You must enter a filter name!", 'broken-link-checker');
- $msg_class = 'error';
- //Filter parameters (a search query) must also be set
- } elseif ( empty($_POST['params']) ){
- $message = __("Invalid search query.", 'broken-link-checker');
- $msg_class = 'error';
- } else {
- //Save the new filter
- $blc_link_query = blcLinkQuery::getInstance();
- $filter_id = $blc_link_query->create_custom_filter($_POST['name'], $_POST['params']);
-
- if ( $filter_id ){
- //Saved
- $message = sprintf( __('Filter "%s" created', 'broken-link-checker'), $_POST['name']);
- //A little hack to make the filter active immediately
- $_GET['filter_id'] = $filter_id;
- } else {
- //Error
- $message = sprintf( __("Database error : %s", 'broken-link-checker'), $wpdb->last_error);
- $msg_class = 'error';
- }
- }
-
- return array($message, $msg_class);
- }
-
- /**
- * Delete a custom link filter.
- *
- * @uses $_POST
- *
- * @return array Message and a CSS class to apply to the message.
- */
- function do_delete_custom_filter(){
- //Delete an existing custom filter!
- check_admin_referer( 'delete-custom-filter' );
- $msg_class = 'updated';
-
- //Filter ID must be set
- if ( empty($_POST['filter_id']) ){
- $message = __("Filter ID not specified.", 'broken-link-checker');
- $msg_class = 'error';
- } else {
- //Try to delete the filter
- $blc_link_query = blcLinkQuery::getInstance();
- if ( $blc_link_query->delete_custom_filter($_POST['filter_id']) ){
- //Success
- $message = __('Filter deleted', 'broken-link-checker');
- } else {
- //Either the ID is wrong or there was some other error
- $message = __('Database error : %s', 'broken-link-checker');
- $msg_class = 'error';
- }
- }
-
- return array($message, $msg_class);
- }
-
- /**
- * Modify multiple links to point to their target URLs.
- *
- * @param array $selected_links
- * @return array The message to display and its CSS class.
- */
- function do_bulk_deredirect($selected_links){
- //For all selected links, replace the URL with the final URL that it redirects to.
-
- $message = '';
- $msg_class = 'updated';
-
- check_admin_referer( 'bulk-action' );
-
- if ( count($selected_links) > 0 ) {
- //Fetch all the selected links
- $links = blc_get_links(array(
- 'link_ids' => $selected_links,
- 'purpose' => BLC_FOR_EDITING,
- ));
-
- if ( count($links) > 0 ) {
- $processed_links = 0;
- $failed_links = 0;
-
- //Deredirect all selected links
- foreach($links as $link){
- $rez = $link->deredirect();
- if ( !is_wp_error($rez) && empty($rez['errors'] )){
- $processed_links++;
- } else {
- $failed_links++;
- }
- }
-
- $message = sprintf(
- _n(
- 'Replaced %d redirect with a direct link',
- 'Replaced %d redirects with direct links',
- $processed_links,
- 'broken-link-checker'
- ),
- $processed_links
- );
-
- if ( $failed_links > 0 ) {
- $message .= '<br>' . sprintf(
- _n(
- 'Failed to fix %d redirect',
- 'Failed to fix %d redirects',
- $failed_links,
- 'broken-link-checker'
- ),
- $failed_links
- );
- $msg_class = 'error';
- }
- } else {
- $message = __('None of the selected links are redirects!', 'broken-link-checker');
- }
- }
-
- return array($message, $msg_class);
- }
-
- /**
- * Edit multiple links in one go.
- *
- * @param array $selected_links
- * @return array The message to display and its CSS class.
- */
- function do_bulk_edit($selected_links){
- $message = '';
- $msg_class = 'updated';
-
- check_admin_referer( 'bulk-action' );
-
- $post = $_POST;
- if ( function_exists('wp_magic_quotes') ){
- $post = stripslashes_deep($post); //Ceterum censeo, WP shouldn't mangle superglobals.
- }
-
- $search = isset($post['search']) ? $post['search'] : '';
- $replace = isset($post['replace']) ? $post['replace'] : '';
- $use_regex = !empty($post['regex']);
- $case_sensitive = !empty($post['case_sensitive']);
-
- $delimiter = '`'; //Pick a char that's uncommon in URLs so that escaping won't usually be a problem
- if ( $use_regex ){
- $search = $delimiter . str_replace($delimiter, '\\' . $delimiter, $search) . $delimiter;
- if ( !$case_sensitive ){
- $search .= 'i';
- }
- } elseif ( !$case_sensitive ) {
- //str_ireplace() would be more appropriate for case-insensitive, non-regexp replacement,
- //but that's only available in PHP5.
- $search = $delimiter . str_replace($delimiter, '\\' . $delimiter, preg_quote($search)) . $delimiter . 'i';
- $use_regex = true;
- }
-
- if ( count($selected_links) > 0 ) {
- set_time_limit(300); //In case the user decides to edit hundreds of links at once
-
- //Fetch all the selected links
- $links = blc_get_links(array(
- 'link_ids' => $selected_links,
- 'purpose' => BLC_FOR_EDITING,
- ));
-
- if ( count($links) > 0 ) {
- $processed_links = 0;
- $failed_links = 0;
- $skipped_links = 0;
-
- //Edit the links
- foreach($links as $link){
- if ( $use_regex ){
- $new_url = preg_replace($search, $replace, $link->url);
- } else {
- $new_url = str_replace($search, $replace, $link->url);
- }
-
- if ( $new_url == $link->url ){
- $skipped_links++;
- continue;
- }
-
- $rez = $link->edit($new_url);
- if ( !is_wp_error($rez) && empty($rez['errors'] )){
- $processed_links++;
- } else {
- $failed_links++;
- }
- }
-
- $message .= sprintf(
- _n(
- '%d link updated.',
- '%d links updated.',
- $processed_links,
- 'broken-link-checker'
- ),
- $processed_links
- );
-
- if ( $failed_links > 0 ) {
- $message .= '<br>' . sprintf(
- _n(
- 'Failed to update %d link.',
- 'Failed to update %d links.',
- $failed_links,
- 'broken-link-checker'
- ),
- $failed_links
- );
- $msg_class = 'error';
- }
- }
- }
-
- return array($message, $msg_class);
- }
-
- /**
- * Unlink multiple links.
- *
- * @param array $selected_links
- * @return array Message and a CSS classname.
- */
- function do_bulk_unlink($selected_links){
- //Unlink all selected links.
- $message = '';
- $msg_class = 'updated';
-
- check_admin_referer( 'bulk-action' );
-
- if ( count($selected_links) > 0 ) {
-
- //Fetch all the selected links
- $links = blc_get_links(array(
- 'link_ids' => $selected_links,
- 'purpose' => BLC_FOR_EDITING,
- ));
-
- if ( count($links) > 0 ) {
- $processed_links = 0;
- $failed_links = 0;
-
- //Unlink (delete) each one
- foreach($links as $link){
- $rez = $link->unlink();
- if ( ($rez == false) || is_wp_error($rez) ){
- $failed_links++;
- } else {
- $processed_links++;
- }
- }
-
- //This message is slightly misleading - it doesn't account for the fact that
- //a link can be present in more than one post.
- $message = sprintf(
- _n(
- '%d link removed',
- '%d links removed',
- $processed_links,
- 'broken-link-checker'
- ),
- $processed_links
- );
-
- if ( $failed_links > 0 ) {
- $message .= '<br>' . sprintf(
- _n(
- 'Failed to remove %d link',
- 'Failed to remove %d links',
- $failed_links,
- 'broken-link-checker'
- ),
- $failed_links
- );
- $msg_class = 'error';
- }
- }
- }
-
- return array($message, $msg_class);
- }
-
- /**
- * Delete or trash posts, bookmarks and other items that contain any of the specified links.
- *
- * Will prefer moving stuff to trash to permanent deletion. If it encounters an item that
- * can't be moved to the trash, it will skip that item by default.
- *
- * @param array $selected_links An array of link IDs
- * @param bool $force_delete Whether to bypass trash and force deletion. Defaults to false.
- * @return array Confirmation message and its CSS class.
- */
- function do_bulk_delete_sources($selected_links, $force_delete = false){
- $message = '';
- $msg_class = 'updated';
-
- //Delete posts, blogroll entries and any other link containers that contain any of the selected links.
- //
- //Note that once all containers containing a particular link have been deleted,
- //there is no need to explicitly delete the link record itself. The hooks attached to
- //the actions that execute when something is deleted (e.g. "post_deleted") will
- //take care of that.
-
- check_admin_referer( 'bulk-action' );
-
- if ( count($selected_links) > 0 ) {
- $messages = array();
-
- //Fetch all the selected links
- $links = blc_get_links(array(
- 'link_ids' => $selected_links,
- 'load_instances' => true,
- ));
-
- //Make a list of all containers associated with these links, with each container
- //listed only once.
- $containers = array();
- foreach($links as $link){ /* @var blcLink $link */
- $instances = $link->get_instances();
- foreach($instances as $instance){ /* @var blcLinkInstance $instance */
- $key = $instance->container_type . '|' . $instance->container_id;
- $containers[$key] = array($instance->container_type, $instance->container_id);
- }
- }
-
- //Instantiate the containers
- $containers = blcContainerHelper::get_containers($containers);
- //Delete/trash their associated entities
- $deleted = array();
- $skipped = array();
- foreach($containers as $container){ /* @var blcContainer $container */
- if ( !$container->current_user_can_delete() ){
- continue;
- }
-
- if ( $force_delete ){
- $rez = $container->delete_wrapped_object();
- } else {
- if ( $container->can_be_trashed() ){
- $rez = $container->trash_wrapped_object();
- } else {
- $skipped[] = $container;
- continue;
- }
- }
-
- if ( is_wp_error($rez) ){ /* @var WP_Error $rez */
- //Record error messages for later display
- $messages[] = $rez->get_error_message();
- $msg_class = 'error';
- } else {
- //Keep track of how many of each type were deleted.
- $container_type = $container->container_type;
- if ( isset($deleted[$container_type]) ){
- $deleted[$container_type]++;
- } else {
- $deleted[$container_type] = 1;
- }
- }
- }
-
- //Generate delete confirmation messages
- foreach($deleted as $container_type => $number){
- if ( $force_delete ){
- $messages[] = blcContainerHelper::ui_bulk_delete_message($container_type, $number);
- } else {
- $messages[] = blcContainerHelper::ui_bulk_trash_message($container_type, $number);
- }
-
- }
-
- //If some items couldn't be trashed, let the user know
- if ( count($skipped) > 0 ){
- $message = sprintf(
- _n(
- "%d item was skipped because it can't be moved to the Trash. You need to delete it manually.",
- "%d items were skipped because they can't be moved to the Trash. You need to delete them manually.",
- count($skipped)
- ),
- count($skipped)
- );
- $message .= '<br><ul>';
- foreach($skipped as $container){
- $message .= sprintf(
- '<li>%s</li>',
- $container->ui_get_source('')
- );
- }
- $message .= '</ul>';
-
- $messages[] = $message;
- }
-
- if ( count($messages) > 0 ){
- $message = implode('<p>', $messages);
- } else {
- $message = __("Didn't find anything to delete!", 'broken-link-checker');
- $msg_class = 'error';
- }
- }
-
- return array($message, $msg_class);
- }
-
- /**
- * Mark multiple links as unchecked.
- *
- * @param array $selected_links An array of link IDs
- * @return array Confirmation nessage and the CSS class to use with that message.
- */
- function do_bulk_recheck($selected_links){
- /** @var wpdb $wpdb */
- global $wpdb;
-
- $message = '';
- $msg_class = 'updated';
-
- if ( count($selected_links) > 0 ){
- $q = "UPDATE {$wpdb->prefix}blc_links
- SET last_check_attempt = '0000-00-00 00:00:00'
- WHERE link_id IN (".implode(', ', $selected_links).")";
- $changes = $wpdb->query($q);
-
- $message = sprintf(
- _n(
- "%d link scheduled for rechecking",
- "%d links scheduled for rechecking",
- $changes,
- 'broken-link-checker'
- ),
- $changes
- );
- }
-
- return array($message, $msg_class);
- }
-
-
- /**
- * Mark multiple links as not broken.
- *
- * @param array $selected_links An array of link IDs
- * @return array Confirmation nessage and the CSS class to use with that message.
- */
- function do_bulk_discard($selected_links){
- check_admin_referer( 'bulk-action' );
-
- $messages = array();
- $msg_class = 'updated';
- $processed_links = 0;
-
- if ( count($selected_links) > 0 ){
- foreach($selected_links as $link_id){
- //Load the link
- $link = new blcLink( intval($link_id) );
-
- //Skip links that don't actually exist
- if ( !$link->valid() ){
- continue;
- }
-
- //Skip links that weren't actually detected as broken
- if ( !$link->broken ){
- continue;
- }
-
- //Make it appear "not broken"
- $link->broken = false;
- $link->false_positive = true;
- $link->last_check_attempt = time();
- $link->log = __("This link was manually marked as working by the user.", 'broken-link-checker');
-
- //Save the changes
- if ( $link->save() ){
- $processed_links++;
- } else {
- $messages[] = sprintf(
- __("Couldn't modify link %d", 'broken-link-checker'),
- $link_id
- );
- $msg_class = 'error';
- }
- }
- }
-
- if ( $processed_links > 0 ){
- $messages[] = sprintf(
- _n(
- '%d link marked as not broken',
- '%d links marked as not broken',
- $processed_links,
- 'broken-link-checker'
- ),
- $processed_links
- );
- }
-
- return array(implode('<br>', $messages), $msg_class);
- }
-
-
- /**
- * Enqueue CSS files for the "Broken Links" page
- *
- * @return void
- */
- function links_page_css(){
- wp_enqueue_style('blc-links-page', plugins_url('css/links-page.css', $this->loader), array(), '20120702');
- }
-
- /**
- * Generate the HTML for the plugin's Screen Options panel.
- *
- * @return string
- */
- function screen_options_html(){
- //Update the links-per-page setting when "Apply" is clicked
- if ( isset($_POST['per_page']) && is_numeric($_POST['per_page']) ) {
- check_admin_referer( 'screen-options-nonce', 'screenoptionnonce' );
- $per_page = intval($_POST['per_page']);
- if ( ($per_page >= 1) && ($per_page <= 500) ){
- $this->conf->options['table_links_per_page'] = $per_page;
- $this->conf->save_options();
- }
- }
-
- //Let the user show/hide individual table columns
- $html = '<h5>' . __('Table columns', 'broken-link-checker') . '</h5>';
-
- include dirname($this->loader) . '/includes/admin/table-printer.php';
- $table = new blcTablePrinter($this);
- $available_columns = $table->get_layout_columns($this->conf->options['table_layout']);
-
- $html .= '<div id="blc-column-selector" class="metabox-prefs">';
-
- foreach( $available_columns as $column_id => $data ){
- $html .= sprintf(
- '<label><input type="checkbox" name="visible_columns[%s]"%s>%s</label>',
- esc_attr($column_id),
- in_array($column_id, $this->conf->options['table_visible_columns']) ? ' checked="checked"' : '',
- $data['heading']
- );
- }
-
- $html .= '</div>';
-
- $html .= '<h5>' . __('Show on screen') . '</h5>';
- $html .= '<div class="screen-options">';
- $html .= sprintf(
- '<input type="text" name="per_page" maxlength="3" value="%d" class="screen-per-page" id="blc_links_per_page" />
- <label for="blc_links_per_page">%s</label>
- <input type="button" class="button" value="%s" id="blc-per-page-apply-button" /><br />',
- $this->conf->options['table_links_per_page'],
- __('links', 'broken-link-checker'),
- __('Apply')
- );
- $html .= '</div>';
-
- $html .= '<h5>' . __('Misc', 'broken-link-checker') . '</h5>';
- $html .= '<div class="screen-options">';
- /*
- Display a checkbox in "Screen Options" that lets the user highlight links that
- have been broken for at least X days.
- */
- $html .= sprintf(
- '<label><input type="checkbox" id="highlight_permanent_failures" name="highlight_permanent_failures"%s> ',
- $this->conf->options['highlight_permanent_failures'] ? ' checked="checked"' : ''
- );
- $input_box = sprintf(
- '</label><input type="text" name="failure_duration_threshold" id="failure_duration_threshold" value="%d" size="2"><label for="highlight_permanent_failures">',
- $this->conf->options['failure_duration_threshold']
- );
- $html .= sprintf(
- __('Highlight links broken for at least %s days', 'broken-link-checker'),
- $input_box
- );
- $html .= '</label>';
-
- //Display a checkbox for turning colourful link status messages on/off
- $html .= sprintf(
- '<br/><label><input type="checkbox" id="table_color_code_status" name="table_color_code_status"%s> %s</label>',
- $this->conf->options['table_color_code_status'] ? ' checked="checked"' : '',
- __('Color-code status codes', 'broken-link-checker')
- );
-
- $html .= '</div>';
-
- return $html;
- }
-
- /**
- * AJAX callback for saving the "Screen Options" panel settings
- *
- * @param array $form
- * @return void
- */
- function ajax_save_screen_options($form){
- if ( !current_user_can('edit_others_posts') ){
- die( json_encode( array(
- 'error' => __("You're not allowed to do that!", 'broken-link-checker')
- )));
- }
-
- $this->conf->options['highlight_permanent_failures'] = !empty($form['highlight_permanent_failures']);
- $this->conf->options['table_color_code_status'] = !empty($form['table_color_code_status']);
-
- $failure_duration_threshold = intval($form['failure_duration_threshold']);
- if ( $failure_duration_threshold >=1 ){
- $this->conf->options['failure_duration_threshold'] = $failure_duration_threshold;
- }
-
- if ( isset($form['visible_columns']) && is_array($form['visible_columns']) ){
- $this->conf->options['table_visible_columns'] = array_keys($form['visible_columns']);
- }
-
- $this->conf->save_options();
- die('1');
- }
-
- function start_timer(){
- $this->execution_start_time = microtime_float();
- }
-
- function execution_time(){
- return microtime_float() - $this->execution_start_time;
- }
-
- /**
- * The main worker function that does all kinds of things.
- *
- * @return void
- */
- function work(){
- global $wpdb;
-
- if ( !$this->acquire_lock() ){
- //FB::warn("Another instance of BLC is already working. Stop.");
- return;
- }
-
- if ( $this->server_too_busy() ){
- //FB::warn("Server is too busy. Stop.");
- return;
- }
-
- $this->start_timer();
-
- $max_execution_time = $this->conf->options['max_execution_time'];
-
- /*****************************************
- Preparation
- ******************************************/
- // Check for safe mode
- if( blcUtility::is_safe_mode() ){
- // Do it the safe mode way - obey the existing max_execution_time setting
- $t = ini_get('max_execution_time');
- if ($t && ($t < $max_execution_time))
- $max_execution_time = $t-1;
- } else {
- // Do it the regular way
- @set_time_limit( $max_execution_time * 2 ); //x2 should be plenty, running any longer would mean a glitch.
- }
-
- //Don't stop the script when the connection is closed
- ignore_user_abort( true );
-
- //Close the connection as per http://www.php.net/manual/en/features.connection-handling.php#71172
- //This reduces resource usage and may solve the mysterious slowdowns certain users have
- //encountered when activating the plugin.
- //(Disable when debugging or you won't get the FirePHP output)
- if ( !headers_sent() && (!defined('BLC_DEBUG') || !constant('BLC_DEBUG')) ){
- @ob_end_clean(); //Discard the existing buffer, if any
- header("Connection: close");
- ob_start();
- echo ('Connection closed'); //This could be anything
- $size = ob_get_length();
- header("Content-Length: $size");
- ob_end_flush(); // Strange behaviour, will not work
- flush(); // Unless both are called !
- }
-
- //Load modules for this context
- $moduleManager = blcModuleManager::getInstance();
- $moduleManager->load_modules('work');
-
-
- /*****************************************
- Parse posts and bookmarks
- ******************************************/
-
- $orphans_possible = false;
- $still_need_resynch = $this->conf->options['need_resynch'];
-
- if ( $still_need_resynch ) {
-
- //FB::log("Looking for containers that need parsing...");
-
- while( $containers = blcContainerHelper::get_unsynched_containers(50) ){
- //FB::log($containers, 'Found containers');
-
- foreach($containers as $container){
- //FB::log($container, "Parsing container");
- $container->synch();
-
- //Check if we still have some execution time left
- if( $this->execution_time() > $max_execution_time ){
- //FB::log('The alloted execution time has run out');
- blc_cleanup_links();
- $this->release_lock();
- return;
- }
-
- //Check if the server isn't overloaded
- if ( $this->server_too_busy() ){
- //FB::log('Server overloaded, bailing out.');
- blc_cleanup_links();
- $this->release_lock();
- return;
- }
- }
- $orphans_possible = true;
- }
-
- //FB::log('No unparsed items found.');
- $still_need_resynch = false;
-
- } else {
- //FB::log('Resynch not required.');
- }
-
- /******************************************
- Resynch done?
- *******************************************/
- if ( $this->conf->options['need_resynch'] && !$still_need_resynch ){
- $this->conf->options['need_resynch'] = $still_need_resynch;
- $this->conf->save_options();
- }
-
- /******************************************
- Remove orphaned links
- *******************************************/
-
- if ( $orphans_possible ) {
- //FB::log('Cleaning up the link table.');
- blc_cleanup_links();
- }
-
- //Check if we still have some execution time left
- if( $this->execution_time() > $max_execution_time ){
- //FB::log('The alloted execution time has run out');
- $this->release_lock();
- return;
- }
-
- if ( $this->server_too_busy() ){
- //FB::log('Server overloaded, bailing out.');
- $this->release_lock();
- return;
- }
-
- /*****************************************
- Check links
- ******************************************/
- while ( $links = $this->get_links_to_check(30) ){
-
- //Some unchecked links found
- //FB::log("Checking ".count($links)." link(s)");
-
- foreach ($links as $link) {
- //Does this link need to be checked? Excluded links aren't checked, but their URLs are still
- //tested periodically to see if they're still on the exlusion list.
- if ( !$this->is_excluded( $link->url ) ) {
- //Check the link.
- //FB::log($link->url, "Checking link {$link->link_id}");
- $link->check( true );
- } else {
- //FB::info("The URL {$link->url} is excluded, skipping link {$link->link_id}.");
- $link->last_check_attempt = time();
- $link->save();
- }
-
- //Check if we still have some execution time left
- if( $this->execution_time() > $max_execution_time ){
- //FB::log('The alloted execution time has run out');
- $this->release_lock();
- return;
- }
-
- //Check if the server isn't overloaded
- if ( $this->server_too_busy() ){
- //FB::log('Server overloaded, bailing out.');
- $this->release_lock();
- return;
- }
- }
-
- }
- //FB::log('No links need to be checked right now.');
-
- $this->release_lock();
- //FB::log('All done.');
- }
-
- /**
- * This function is called when the plugin's cron hook executes.
- * Its only purpose is to invoke the worker function.
- *
- * @uses wsBrokenLinkChecker::work()
- *
- * @return void
- */
- function cron_check_links(){
- $this->work();
- }
-
- /**
- * Retrieve links that need to be checked or re-checked.
- *
- * @param integer $max_results The maximum number of links to return. Defaults to 0 = no limit.
- * @param bool $count_only If true, only the number of found links will be returned, not the links themselves.
- * @return int|blcLink[]
- */
- function get_links_to_check($max_results = 0, $count_only = false){
- global $wpdb; /* @var wpdb $wpdb */
-
- $check_threshold = date('Y-m-d H:i:s', strtotime('-'.$this->conf->options['check_threshold'].' hours'));
- $recheck_threshold = date('Y-m-d H:i:s', time() - $this->conf->options['recheck_threshold']);
-
- //FB::log('Looking for links to check (threshold : '.$check_threshold.', recheck_threshold : '.$recheck_threshold.')...');
-
- //Select some links that haven't been checked for a long time or
- //that are broken and need to be re-checked again. Links that are
- //marked as "being checked" and have been that way for several minutes
- //can also be considered broken/buggy, so those will be selected
- //as well.
-
- //Only check links that have at least one valid instance (i.e. an instance exists and
- //it corresponds to one of the currently loaded container/parser types).
- $manager = blcModuleManager::getInstance();
- $loaded_containers = $manager->get_escaped_ids('container');
- $loaded_parsers = $manager->get_escaped_ids('parser');
-
- //Note : This is a slow query, but AFAIK there is no way to speed it up.
- //I could put an index on last_check_attempt, but that value is almost
- //certainly unique for each row so it wouldn't be much better than a full table scan.
- if ( $count_only ){
- $q = "SELECT COUNT(links.link_id)\n";
- } else {
- $q = "SELECT links.*\n";
- }
- $q .= "FROM {$wpdb->prefix}blc_links AS links
- WHERE
- (
- ( last_check_attempt < %s )
- OR
- (
- (broken = 1 OR being_checked = 1)
- AND may_recheck = 1
- AND check_count < %d
- AND last_check_attempt < %s
- )
- )
- AND EXISTS (
- SELECT 1 FROM {$wpdb->prefix}blc_instances AS instances
- WHERE
- instances.link_id = links.link_id
- AND ( instances.container_type IN ({$loaded_containers}) )
- AND ( instances.parser_type IN ({$loaded_parsers}) )
- )
- ";
- if ( !$count_only ){
- $q .= "\nORDER BY last_check_attempt ASC\n";
- if ( !empty($max_results) ){
- $q .= "LIMIT " . intval($max_results);
- }
- }
-
- $link_q = $wpdb->prepare(
- $q,
- $check_threshold,
- $this->conf->options['recheck_count'],
- $recheck_threshold
- );
- //FB::log($link_q, "Find links to check");
-
- //If we just need the number of links, retrieve it and return
- if ( $count_only ){
- return $wpdb->get_var($link_q);
- }
-
- //Fetch the link data
- $link_data = $wpdb->get_results($link_q, ARRAY_A);
- if ( empty($link_data) ){
- return array();
- }
-
- //Instantiate blcLink objects for all fetched links
- $links = array();
- foreach($link_data as $data){
- $links[] = new blcLink($data);
- }
-
- return $links;
- }
-
- /**
- * Output the current link checker status in JSON format.
- * Ajax hook for the 'blc_full_status' action.
- *
- * @return void
- */
- function ajax_full_status( ){
- $status = $this->get_status();
- $text = $this->status_text( $status );
-
- echo json_encode( array(
- 'text' => $text,
- 'status' => $status,
- ) );
-
- die();
- }
-
- /**
- * Generates a status message based on the status info in $status
- *
- * @param array $status
- * @return string
- */
- function status_text( $status ){
- $text = '';
-
- if( $status['broken_links'] > 0 ){
- $text .= sprintf(
- "<a href='%s' title='" . __('View broken links', 'broken-link-checker') . "'><strong>".
- _n('Found %d broken link', 'Found %d broken links', $status['broken_links'], 'broken-link-checker') .
- "</strong></a>",
- admin_url('tools.php?page=view-broken-links'),
- $status['broken_links']
- );
- } else {
- $text .= __("No broken links found.", 'broken-link-checker');
- }
-
- $text .= "<br/>";
-
- if( $status['unchecked_links'] > 0) {
- $text .= sprintf(
- _n('%d URL in the work queue', '%d URLs in the work queue', $status['unchecked_links'], 'broken-link-checker'),
- $status['unchecked_links'] );
- } else {
- $text .= __("No URLs in the work queue.", 'broken-link-checker');
- }
-
- $text .= "<br/>";
- if ( $status['known_links'] > 0 ){
- $text .= sprintf(
- _n('Detected %d unique URL', 'Detected %d unique URLs', $status['known_links'], 'broken-link-checker') .
- ' ' . _n('in %d link', 'in %d links', $status['known_instances'], 'broken-link-checker'),
- $status['known_links'],
- $status['known_instances']
- );
- if ($this->conf->options['need_resynch']){
- $text .= ' ' . __('and still searching...', 'broken-link-checker');
- } else {
- $text .= '.';
- }
- } else {
- if ($this->conf->options['need_resynch']){
- $text .= __('Searching your blog for links...', 'broken-link-checker');
- } else {
- $text .= __('No links detected.', 'broken-link-checker');
- }
- }
-
- return $text;
- }
-
- /**
- * @uses wsBrokenLinkChecker::ajax_full_status()
- *
- * @return void
- */
- function ajax_dashboard_status(){
- //Just display the full status.
- $this->ajax_full_status();
- }
-
- /**
- * Output the current average server load (over the last one-minute period).
- * Called via AJAX.
- *
- * @return void
- */
- function ajax_current_load(){
- $load = blcUtility::get_server_load();
- if ( empty($load) ){
- die( _x('Unknown', 'current load', 'broken-link-checker') );
- }
-
- $one_minute = reset($load);
- printf('%.2f', $one_minute);
- die();
- }
-
- /**
- * Returns an array with various status information about the plugin. Array key reference:
- * check_threshold - date/time; links checked before this threshold should be checked again.
- * recheck_threshold - date/time; broken links checked before this threshold should be re-checked.
- * known_links - the number of detected unique URLs (a misleading name, yes).
- * known_instances - the number of detected link instances, i.e. actual link elements in posts and other places.
- * broken_links - the number of detected broken links.
- * unchecked_links - the number of URLs that need to be checked ASAP; based on check_threshold and recheck_threshold.
- *
- * @return array
- */
- function get_status(){
- global $wpdb;
- $blc_link_query = blcLinkQuery::getInstance();
-
- $check_threshold=date('Y-m-d H:i:s', strtotime('-'.$this->conf->options['check_threshold'].' hours'));
- $recheck_threshold=date('Y-m-d H:i:s', time() - $this->conf->options['recheck_threshold']);
-
- $known_links = blc_get_links(array('count_only' => true));
- $known_instances = blc_get_usable_instance_count();
-
- $broken_links = $blc_link_query->get_filter_links('broken', array('count_only' => true));
-
- $unchecked_links = $this->get_links_to_check(0, true);
-
- return array(
- 'check_threshold' => $check_threshold,
- 'recheck_threshold' => $recheck_threshold,
- 'known_links' => $known_links,
- 'known_instances' => $known_instances,
- 'broken_links' => $broken_links,
- 'unchecked_links' => $unchecked_links,
- );
- }
-
- function ajax_work(){
- //Run the worker function
- $this->work();
- die();
- }
-
- /**
- * AJAX hook for the "Not broken" button. Marks a link as broken and as a likely false positive.
- *
- * @return void
- */
- function ajax_discard(){
- if (!current_user_can('edit_others_posts') || !check_ajax_referer('blc_discard', false, false)){
- die( __("You're not allowed to do that!", 'broken-link-checker') );
- }
-
- if ( isset($_POST['link_id']) ){
- //Load the link
- $link = new blcLink( intval($_POST['link_id']) );
-
- if ( !$link->valid() ){
- printf( __("Oops, I can't find the link %d", 'broken-link-checker'), intval($_POST['link_id']) );
- die();
- }
- //Make it appear "not broken"
- $link->broken = false;
- $link->false_positive = true;
- $link->last_check_attempt = time();
- $link->log = __("This link was manually marked as working by the user.", 'broken-link-checker');
-
- //Save the changes
- if ( $link->save() ){
- die( "OK" );
- } else {
- die( __("Oops, couldn't modify the link!", 'broken-link-checker') ) ;
- }
- } else {
- die( __("Error : link_id not specified", 'broken-link-checker') );
- }
- }
- public function ajax_dismiss(){
- $this->ajax_set_link_dismissed(true);
- }
- public function ajax_undismiss(){
- $this->ajax_set_link_dismissed(false);
- }
- private function ajax_set_link_dismissed($dismiss){
- $action = $dismiss ? 'blc_dismiss' : 'blc_undismiss';
- if (!current_user_can('edit_others_posts') || !check_ajax_referer($action, false, false)){
- die( __("You're not allowed to do that!", 'broken-link-checker') );
- }
- if ( isset($_POST['link_id']) ){
- //Load the link
- $link = new blcLink( intval($_POST['link_id']) );
- if ( !$link->valid() ){
- printf( __("Oops, I can't find the link %d", 'broken-link-checker'), intval($_POST['link_id']) );
- die();
- }
- $link->dismissed = $dismiss;
- //Save the changes
- if ( $link->save() ){
- die( "OK" );
- } else {
- die( __("Oops, couldn't modify the link!", 'broken-link-checker') ) ;
- }
- } else {
- die( __("Error : link_id not specified", 'broken-link-checker') );
- }
- }
-
- /**
- * AJAX hook for the inline link editor on Tools -> Broken Links.
- *
- * @return void
- */
- function ajax_edit(){
- if (!current_user_can('edit_others_posts') || !check_ajax_referer('blc_edit', false, false)){
- die( json_encode( array(
- 'error' => __("You're not allowed to do that!", 'broken-link-checker')
- )));
- }
-
- if ( isset($_GET['link_id']) && !empty($_GET['new_url']) ){
- //Load the link
- $link = new blcLink( intval($_GET['link_id']) );
-
- if ( !$link->valid() ){
- die( json_encode( array(
- 'error' => sprintf( __("Oops, I can't find the link %d", 'broken-link-checker'), intval($_GET['link_id']) )
- )));
- }
-
- $new_url = $_GET['new_url'];
- $new_url = stripslashes($new_url);
-
- $parsed = @parse_url($new_url);
- if ( !$parsed ){
- die( json_encode( array(
- 'error' => __("Oops, the new URL is invalid!", 'broken-link-checker')
- )));
- }
-
- //Try and edit the link
- //FB::log($new_url, "Ajax edit");
- //FB::log($_GET, "Ajax edit");
- $rez = $link->edit($new_url);
-
- if ( $rez === false ){
- die( json_encode( array(
- 'error' => __("An unexpected error occured!", 'broken-link-checker')
- )));
- } else {
- $response = array(
- 'new_link_id' => $rez['new_link_id'],
- 'cnt_okay' => $rez['cnt_okay'],
- 'cnt_error' => $rez['cnt_error'],
- 'errors' => array(),
- );
- foreach($rez['errors'] as $error){ /** @var $error WP_Error */
- array_push( $response['errors'], implode(', ', $error->get_error_messages()) );
- }
-
- die( json_encode($response) );
- }
-
- } else {
- die( json_encode( array(
- 'error' => __("Error : link_id or new_url not specified", 'broken-link-checker')
- )));
- }
- }
-
- /**
- * AJAX hook for the "Unlink" action links in Tools -> Broken Links.
- * Removes the specified link from all posts and other supported items.
- *
- * @return void
- */
- function ajax_unlink(){
- if (!current_user_can('edit_others_posts') || !check_ajax_referer('blc_unlink', false, false)){
- die( json_encode( array(
- 'error' => __("You're not allowed to do that!", 'broken-link-checker')
- )));
- }
-
- if ( isset($_POST['link_id']) ){
- //Load the link
- $link = new blcLink( intval($_POST['link_id']) );
-
- if ( !$link->valid() ){
- die( json_encode( array(
- 'error' => sprintf( __("Oops, I can't find the link %d", 'broken-link-checker'), intval($_POST['link_id']) )
- )));
- }
-
- //Try and unlink it
- $rez = $link->unlink();
-
- if ( $rez === false ){
- die( json_encode( array(
- 'error' => __("An unexpected error occured!", 'broken-link-checker')
- )));
- } else {
- $response = array(
- 'cnt_okay' => $rez['cnt_okay'],
- 'cnt_error' => $rez['cnt_error'],
- 'errors' => array(),
- );
- foreach($rez['errors'] as $error){ /** @var WP_Error $error */
- array_push( $response['errors'], implode(', ', $error->get_error_messages()) );
- }
-
- die( json_encode($response) );
- }
-
- } else {
- die( json_encode( array(
- 'error' => __("Error : link_id not specified", 'broken-link-checker')
- )));
- }
- }
-
- function ajax_link_details(){
- global $wpdb; /* @var wpdb $wpdb */
-
- if (!current_user_can('edit_others_posts')){
- die( __("You don't have sufficient privileges to access this information!", 'broken-link-checker') );
- }
-
- //FB::log("Loading link details via AJAX");
-
- if ( isset($_GET['link_id']) ){
- //FB::info("Link ID found in GET");
- $link_id = intval($_GET['link_id']);
- } else if ( isset($_POST['link_id']) ){
- //FB::info("Link ID found in POST");
- $link_id = intval($_POST['link_id']);
- } else {
- //FB::error('Link ID not specified, you hacking bastard.');
- die( __('Error : link ID not specified', 'broken-link-checker') );
- }
-
- //Load the link.
- $link = new blcLink($link_id);
-
- if ( !$link->is_new ){
- //FB::info($link, 'Link loaded');
- if ( !class_exists('blcTablePrinter') ){
- require dirname($this->loader) . '/includes/admin/table-printer.php';
- }
- blcTablePrinter::details_row_contents($link);
- die();
- } else {
- printf( __('Failed to load link details (%s)', 'broken-link-checker'), $wpdb->last_error );
- die();
- }
- }
-
- /**
- * Acquire an exclusive lock.
- * If we already hold a lock, it will be released and a new one will be acquired.
- *
- * @return bool
- */
- function acquire_lock(){
- return WPMutex::acquire('blc_lock');
- }
-
- /**
- * Relese our exclusive lock.
- * Does nothing if the lock has already been released.
- *
- * @return bool
- */
- function release_lock(){
- return WPMutex::release('blc_lock');
- }
-
- /**
- * Check if server is currently too overloaded to run the link checker.
- *
- * @return bool
- */
- function server_too_busy(){
- if ( !$this->conf->options['enable_load_limit'] ){
- return false;
- }
-
- $loads = blcUtility::get_server_load();
- if ( empty($loads) ){
- return false;
- }
- $one_minute = floatval(reset($loads));
-
- return $one_minute > $this->conf->options['server_load_limit'];
- }
-
- /**
- * Register BLC's Dashboard widget
- *
- * @return void
- */
- function hook_wp_dashboard_setup(){
- if ( function_exists( 'wp_add_dashboard_widget' ) && current_user_can('edit_others_posts') ) {
- wp_add_dashboard_widget(
- 'blc_dashboard_widget',
- __('Broken Link Checker', 'broken-link-checker'),
- array( &$this, 'dashboard_widget' ),
- array( &$this, 'dashboard_widget_control' )
- );
- }
- }
-
- /**
- * Collect various debugging information and return it in an associative array
- *
- * @return array
- */
- function get_debug_info(){
- /** @var wpdb $wpdb */
- global $wpdb;
- //Collect some information that's useful for debugging
- $debug = array();
-
- //PHP version. Any one is fine as long as WP supports it.
- $debug[ __('PHP version', 'broken-link-checker') ] = array(
- 'state' => 'ok',
- 'value' => phpversion(),
- );
-
- //MySQL version
- $debug[ __('MySQL version', 'broken-link-checker') ] = array(
- 'state' => 'ok',
- 'value' => $wpdb->db_version(),
- );
-
- //CURL presence and version
- if ( function_exists('curl_version') ){
- $version = curl_version();
-
- if ( version_compare( $version['version'], '7.16.0', '<=' ) ){
- $data = array(
- 'state' => 'warning',
- 'value' => $version['version'],
- 'message' => __('You have an old version of CURL. Redirect detection may not work properly.', 'broken-link-checker'),
- );
- } else {
- $data = array(
- 'state' => 'ok',
- 'value' => $version['version'],
- );
- }
-
- } else {
- $data = array(
- 'state' => 'warning',
- 'value' => __('Not installed', 'broken-link-checker'),
- );
- }
- $debug[ __('CURL version', 'broken-link-checker') ] = $data;
-
- //Snoopy presence
- if ( class_exists('Snoopy') || file_exists(ABSPATH. WPINC . '/class-snoopy.php') ){
- $data = array(
- 'state' => 'ok',
- 'value' => __('Installed', 'broken-link-checker'),
- );
- } else {
- //No Snoopy? This should never happen, but if it does we *must* have CURL.
- if ( function_exists('curl_init') ){
- $data = array(
- 'state' => 'ok',
- 'value' => __('Not installed', 'broken-link-checker'),
- );
- } else {
- $data = array(
- 'state' => 'error',
- 'value' => __('Not installed', 'broken-link-checker'),
- 'message' => __('You must have either CURL or Snoopy installed for the plugin to work!', 'broken-link-checker'),
- );
- }
-
- }
- $debug['Snoopy'] = $data;
-
- //Safe_mode status
- if ( blcUtility::is_safe_mode() ){
- $debug['Safe mode'] = array(
- 'state' => 'warning',
- 'value' => __('On', 'broken-link-checker'),
- 'message' => __('Redirects may be detected as broken links when safe_mode is on.', 'broken-link-checker'),
- );
- } else {
- $debug['Safe mode'] = array(
- 'state' => 'ok',
- 'value' => __('Off', 'broken-link-checker'),
- );
- }
-
- //Open_basedir status
- if ( blcUtility::is_open_basedir() ){
- $debug['open_basedir'] = array(
- 'state' => 'warning',
- 'value' => sprintf( __('On ( %s )', 'broken-link-checker'), ini_get('open_basedir') ),
- 'message' => __('Redirects may be detected as broken links when open_basedir is on.', 'broken-link-checker'),
- );
- } else {
- $debug['open_basedir'] = array(
- 'state' => 'ok',
- 'value' => __('Off', 'broken-link-checker'),
- );
- }
-
- //Default PHP execution time limit
- $debug['Default PHP execution time limit'] = array(
- 'state' => 'ok',
- 'value' => sprintf(__('%s seconds'), ini_get('max_execution_time')),
- );
-
- //Resynch flag.
- $debug['Resynch. flag'] = array(
- 'state' => 'ok',
- 'value' => sprintf('%d', $this->conf->options['need_resynch'] ? '1 (resynch. required)' : '0 (resynch. not required)'),
- );
-
- //Synch records
- $synch_records = intval($wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}blc_synch"));
- $data = array(
- 'state' => 'ok',
- 'value' => sprintf('%d', $synch_records),
- );
- if ( $synch_records == 0 ){
- $data['state'] = 'warning';
- $data['message'] = __('If this value is zero even after several page reloads you have probably encountered a bug.', 'broken-link-checker');
- }
- $debug['Synch. records'] = $data;
-
- //Total links and instances (including invalid ones)
- $all_links = intval($wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}blc_links"));
- $all_instances = intval($wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}blc_instances"));
-
- //Show the number of unparsed containers. Useful for debugging. For performance,
- //this is only shown when we have no links/instances yet.
- if( ($all_links == 0) && ($all_instances == 0) ){
- $unparsed_items = intval($wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}blc_synch WHERE synched=0"));
- $debug['Unparsed items'] = array(
- 'state' => 'warning',
- 'value' => $unparsed_items,
- );
- }
-
- //Links & instances
- if ( ($all_links > 0) && ($all_instances > 0) ){
- $debug['Link records'] = array(
- 'state' => 'ok',
- 'value' => sprintf('%d (%d)', $all_links, $all_instances),
- );
- } else {
- $debug['Link records'] = array(
- 'state' => 'warning',
- 'value' => sprintf('%d (%d)', $all_links, $all_instances),
- );
- }
-
- //Installation log
- $logger = new blcCachedOptionLogger('blc_installation_log');
- $installation_log = $logger->get_messages();
- if ( !empty($installation_log) ){
- $debug['Installation log'] = array(
- 'state' => $this->conf->options['installation_complete'] ? 'ok' : 'error',
- 'value' => implode("<br>\n", $installation_log),
- );
- } else {
- $debug['Installation log'] = array(
- 'state' => 'warning',
- 'value' => 'No installation log found found.',
- );
- }
-
- return $debug;
- }
- function maybe_send_email_notifications() {
- global $wpdb; /** @var wpdb $wpdb */
- if ( !($this->conf->options['send_email_notifications'] || $this->conf->options['send_authors_email_notifications']) ){
- return;
- }
- //Find links that have been detected as broken since the last sent notification.
- $last_notification = date('Y-m-d H:i:s', $this->conf->options['last_notification_sent']);
- $where = $wpdb->prepare('( first_failure >= %s )', $last_notification);
- $links = blc_get_links(array(
- 's_filter' => 'broken',
- 'where_expr' => $where,
- 'load_instances' => true,
- 'load_containers' => true,
- 'load_wrapped_objects' => $this->conf->options['send_authors_email_notifications'],
- 'max_results' => 0,
- ));
- if ( empty($links) ){
- return;
- }
- //Send the admin notification
- $admin_email = get_option('admin_email');
- if ( $this->conf->options['send_email_notifications'] && !empty($admin_email) ) {
- $this->send_admin_notification($links, $admin_email);
- }
- //Send notifications to post authors
- if ( $this->conf->options['send_authors_email_notifications'] ) {
- $this->send_authors_notifications($links);
- }
- $this->conf->options['last_notification_sent'] = time();
- $this->conf->save_options();
- }
- function send_admin_notification($links, $email) {
- //Prepare email message
- $subject = sprintf(
- __("[%s] Broken links detected", 'broken-link-checker'),
- html_entity_decode(get_option('blogname'), ENT_QUOTES)
- );
- $body = sprintf(
- _n(
- "Broken Link Checker has detected %d new broken link on your site.",
- "Broken Link Checker has detected %d new broken links on your site.",
- count($links),
- 'broken-link-checker'
- ),
- count($links)
- );
- $body .= "<br>";
- $instances = array();
- foreach($links as $link) { /* @var blcLink $link */
- $instances = array_merge($instances, $link->get_instances());
- }
- $body .= $this->build_instance_list_for_email($instances);
- $this->send_html_email($email, $subject, $body);
- }
- function build_instance_list_for_email($instances, $max_displayed_links = 5){
- $result = '';
- if ( count($instances) > $max_displayed_links ){
- $line = sprintf(
- _n(
- "Here's a list of the first %d broken links:",
- "Here's a list of the first %d broken links:",
- $max_displayed_links,
- 'broken-link-checker'
- ),
- $max_displayed_links
- );
- } else {
- $line = __("Here's a list of the new broken links: ", 'broken-link-checker');
- }
- $result .= "<p>$line</p>";
- //Show up to $max_displayed_links broken link instances right in the email.
- $displayed = 0;
- foreach($instances as $instance){ /* @var blcLinkInstance $instance */
- $pieces = array(
- sprintf( __('Link text : %s', 'broken-link-checker'), $instance->ui_get_link_text('email') ),
- sprintf( __('Link URL : <a href="%s">%s</a>', 'broken-link-checker'), htmlentities($instance->get_url()), blcUtility::truncate($instance->get_url(), 70, '') ),
- sprintf( __('Source : %s', 'broken-link-checker'), $instance->ui_get_source('email') ),
- );
- $link_entry = implode("<br>", $pieces);
- $result .= "$link_entry<br><br>";
- $displayed++;
- if ( $displayed >= $max_displayed_links ){
- break;
- }
- }
- //Add a link to the "Broken Links" tab.
- $result .= __("You can see all broken links here:", 'broken-link-checker') . "<br>";
- $result .= sprintf('<a href="%1$s">%1$s</a>', admin_url('tools.php?page=view-broken-links'));
- return $result;
- }
- function send_html_email($email_address, $subject, $body) {
- //Need to override the default 'text/plain' content type to send a HTML email.
- add_filter('wp_mail_content_type', array(&$this, 'override_mail_content_type'));
- $success = wp_mail($email_address, $subject, $body);
- //Remove the override so that it doesn't interfere with other plugins that might
- //want to send normal plaintext emails.
- remove_filter('wp_mail_content_type', array(&$this, 'override_mail_content_type'));
-
- return $success;
- }
- function send_authors_notifications($links) {
- $authorInstances = array();
- foreach($links as $link){ /* @var blcLink $link */
- foreach($link->get_instances() as $instance){ /* @var blcLinkInstance $instance */
- $container = $instance->get_container(); /** @var blcContainer $container */
- if ( empty($container) || !($container instanceof blcAnyPostContainer) ) {
- continue;
- }
- $post = $container->get_wrapped_object(); /** @var StdClass $post */
- if ( !array_key_exists($post->post_author, $authorInstances) ) {
- $authorInstances[$post->post_author] = array();
- }
- $authorInstances[$post->post_author][] = $instance;
- }
- }
- foreach($authorInstances as $author_id => $instances) {
- $subject = sprintf(
- __("[%s] Broken links detected", 'broken-link-checker'),
- html_entity_decode(get_option('blogname'), ENT_QUOTES)
- );
- $body = sprintf(
- _n(
- "Broken Link Checker has detected %d new broken link in your posts.",
- "Broken Link Checker has detected %d new broken links in your posts.",
- count($instances),
- 'broken-link-checker'
- ),
- count($instances)
- );
- $body .= "<br>";
- $body .= $this->build_instance_list_for_email($instances);
- $author = get_user_by('id', $author_id); /** @var WP_User $author */
- $this->send_html_email($author->user_email, $subject, $body);
- }
- }
-
- function override_mail_content_type($content_type){
- return 'text/html';
- }
-
- /**
- * Install or uninstall the plugin's Cron events based on current settings.
- *
- * @uses wsBrokenLinkChecker::$conf Uses $conf->options to determine if events need to be (un)installed.
- *
- * @return void
- */
- function setup_cron_events(){
- //Link monitor
- if ( $this->conf->options['run_via_cron'] ){
- if (!wp_next_scheduled('blc_cron_check_links')) {
- wp_schedule_event( time(), 'hourly', 'blc_cron_check_links' );
- }
- } else {
- wp_clear_scheduled_hook('blc_cron_check_links');
- }
-
- //Email notifications about broken links
- if ( $this->conf->options['send_email_notifications'] || $this->conf->options['send_authors_email_notifications'] ){
- if ( !wp_next_scheduled('blc_cron_email_notifications') ){
- wp_schedule_event(time(), $this->conf->options['notification_schedule'], 'blc_cron_email_notifications');
- }
- } else {
- wp_clear_scheduled_hook('blc_cron_email_notifications');
- }
-
- //Run database maintenance every two weeks or so
- if ( !wp_next_scheduled('blc_cron_database_maintenance') ){
- wp_schedule_event(time(), 'bimonthly', 'blc_cron_database_maintenance');
- }
-
- //Check for news notices related to this plugin
- if ( !wp_next_scheduled('blc_cron_check_news') ){
- wp_schedule_event(time(), 'daily', 'blc_cron_check_news');
- }
- }
-
- /**
- * Load the plugin's textdomain
- *
- * @return void
- */
- function load_language(){
- load_plugin_textdomain( 'broken-link-checker', false, basename(dirname($this->loader)) . '/languages' );
- }
-
- /**
- * Check if there's a "news" link to display on the plugin's pages.
- *
- * @return void
- */
- function check_news(){
- $url = 'http://w-shadow.com/plugin-news/broken-link-checker-news.txt';
-
- //Retrieve the appropriate "news" file
- $res = wp_remote_get($url);
- if ( is_wp_error($res) ){
- return;
- }
-
- //Anything there?
- if ( isset($res['response']['code']) && ($res['response']['code'] == 200) && isset($res['body']) ) {
- //The file should contain two lines - a title and an URL
- $news = explode("\n", trim($res['body']));
- if ( count($news) == 2 ){
- //Save for later.
- $this->conf->options['plugin_news'] = $news;
- } else {
- $this->conf->options['plugin_news'] = null;
- }
- $this->conf->save_options();
- }
- }
-
- }//class ends here
- } // if class_exists...
- ?>