/** * blcUtility::normalize_url() * * @param string $url * @params string $base_url (Optional) The base URL is used to convert a relative URL to a fully-qualified one * @return string A normalized URL or FALSE if the URL is invalid */ function normalize_url($url, $base_url = '') { //Sometimes links may contain shortcodes. Parse them. $url = do_shortcode($url); $parts = @parse_url($url); if (!$parts) { return false; } //Invalid URL if (isset($parts['scheme'])) { //Only HTTP(S) links are checked. Other protocols are not supported. if ($parts['scheme'] != 'http' && $parts['scheme'] != 'https') { return false; } } $url = html_entity_decode($url); $url = preg_replace(array('/([\\?&]PHPSESSID=\\w+)$/i', '/(#[^\\/]*)$/', '/&/', '/^(javascript:.*)/i', '/([\\?&]sid=\\w+)$/i'), array('', '', '&', '', ''), $url); $url = trim($url); if ($url == '') { return false; } // turn relative URLs into absolute URLs if (empty($base_url)) { $base_url = get_option('siteurl'); } $url = blcUtility::relative2absolute($base_url, $url); return $url; }
/** * Extract embedded elements from a HTML string. * * Returns an array of IFrame elements found in the input string. * Elements without a 'src' attribute are skipped. * * Each array item has the same basic structure as the array items * returned by blcUtility::extract_tags(), plus an additional 'embed_code' key * that contains the full HTML code for the entire <ifram> tag. * * @uses blcUtility::extract_tags() This function is a simple wrapper around extract_tags() * * @param string $html * @return array */ function extract_embeds($html) { $results = array(); //remove all <code></code> blocks first $html = preg_replace('/<code[^>]*>.+?<\\/code>/si', ' ', $html); //Find likely-looking <object> elements $iframes = blcUtility::extract_tags($html, 'iframe', false, true); foreach ($iframes as $embed) { if (empty($embed['attributes']['src'])) { continue; } $embed['embed_code'] = $embed['full_tag']; $results[] = $embed; } return $results; }
$moduleManager->plugin_activated(); blc_got_unsynched_items(); $blclog->info(sprintf('--- Total: %.3f seconds', microtime(true) - $notification_start)); //Turn off load limiting if it's not available on this server. $blclog->info('Updating server load limit settings...'); $load = blcUtility::get_server_load(); if (empty($load)) { $blc_config_manager->options['enable_load_limit'] = false; $blclog->info('Disable load limit. Cannot retrieve current load average.'); } elseif ($blc_config_manager->options['enable_load_limit'] && !isset($blc_config_manager->options['server_load_limit'])) { $fifteen_minutes = floatval(end($load)); $default_load_limit = round(max(min($fifteen_minutes * 2, $fifteen_minutes + 2), 4)); $blc_config_manager->options['server_load_limit'] = $default_load_limit; $blclog->info(sprintf('Set server load limit to %.2f. Current load average is %.2f', $default_load_limit, $fifteen_minutes)); } //And optimize my DB tables, too (for good measure) $blclog->info('Optimizing the database...'); $optimize_start = microtime(true); blcUtility::optimize_database(); $blclog->info(sprintf('--- Total: %.3f seconds', microtime(true) - $optimize_start)); $blclog->info('Completing installation...'); $blc_config_manager->options['installation_complete'] = true; $blc_config_manager->options['installation_flag_set_on'] = date('c') . ' (' . microtime(true) . ')'; if ($blc_config_manager->save_options()) { $blclog->info('Configuration saved.'); } else { $blclog->error('Error saving plugin configuration!'); } $blclog->info(sprintf('Installation/update completed at %s with %d queries executed.', date_i18n('Y-m-d H:i:s'), $wpdb->num_queries - $queryCnt)); $blclog->info(sprintf('Total time: %.3f seconds', microtime(true) - $activation_start)); $blclog->save();
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; }
/** * Get the link URL in ASCII-compatible encoding. * * @return string */ function get_ascii_url() { return blcUtility::idn_to_ascii($this->url); }
function ui_get_source($container_field = '', $context = 'display') { //Display a comment icon. if ($container_field == 'comment_author_url') { $image = 'font-awesome/font-awesome-user.png'; } else { $image = 'font-awesome/font-awesome-comment-alt.png'; } $image = sprintf('<img src="%s/broken-link-checker/images/%s" class="blc-small-image" title="%3$s" alt="%3$s"> ', WP_PLUGIN_URL, $image, __('Comment', 'broken-link-checker')); $comment = $this->get_wrapped_object(); //Display a small text sample from the comment $text_sample = strip_tags($comment->comment_content); $text_sample = blcUtility::truncate($text_sample, 65); $html = sprintf('<a href="%s" title="%s"><b>%s</b> — %s</a>', $this->get_edit_url(), esc_attr__('Edit comment'), esc_attr($comment->comment_author), $text_sample); //Don't show the image in email notifications. if ($context != 'email') { $html = $image . $html; } return $html; }
function check($url, $use_get = false) { $this->last_headers = ''; $url = $this->clean_url($url); $result = array('broken' => false); $log = ''; //Get the BLC configuration. It's used below to set the right timeout values and such. $conf = blc_get_configuration(); //Init curl. $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $this->urlencodefix($url)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); //Masquerade as Internet explorer //$ua = 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)'; $ua = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30)'; curl_setopt($ch, CURLOPT_USERAGENT, $ua); //Add a semi-plausible referer header to avoid tripping up some bot traps curl_setopt($ch, CURLOPT_REFERER, home_url()); //Redirects don't work when safe mode or open_basedir is enabled. if (!blcUtility::is_safe_mode() && !blcUtility::is_open_basedir()) { curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); } //Set maximum redirects curl_setopt($ch, CURLOPT_MAXREDIRS, 10); //Set the timeout curl_setopt($ch, CURLOPT_TIMEOUT, $conf->options['timeout']); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $conf->options['timeout']); //Set the proxy configuration. The user can provide this in wp-config.php if (defined('WP_PROXY_HOST')) { curl_setopt($ch, CURLOPT_PROXY, WP_PROXY_HOST); } if (defined('WP_PROXY_PORT')) { curl_setopt($ch, CURLOPT_PROXYPORT, WP_PROXY_PORT); } if (defined('WP_PROXY_USERNAME')) { $auth = WP_PROXY_USERNAME; if (defined('WP_PROXY_PASSWORD')) { $auth .= ':' . WP_PROXY_PASSWORD; } curl_setopt($ch, CURLOPT_PROXYUSERPWD, $auth); } //Make CURL return a valid result even if it gets a 404 or other error. curl_setopt($ch, CURLOPT_FAILONERROR, false); $nobody = !$use_get; //Whether to send a HEAD request (the default) or a GET request $parts = @parse_url($url); if ($parts['scheme'] == 'https') { curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //Required to make HTTPS URLs work. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); $nobody = false; //Can't use HEAD with HTTPS. } if ($nobody) { //If possible, use HEAD requests for speed. curl_setopt($ch, CURLOPT_NOBODY, true); } else { //If we must use GET at least limit the amount of downloaded data. curl_setopt($ch, CURLOPT_HTTPHEADER, array('Range: bytes=0-2048')); //2 KB } //Register a callback function which will process the HTTP header(s). //It can be called multiple times if the remote server performs a redirect. curl_setopt($ch, CURLOPT_HEADERFUNCTION, array(&$this, 'read_header')); //Execute the request $start_time = microtime_float(); curl_exec($ch); $measured_request_duration = microtime_float() - $start_time; $info = curl_getinfo($ch); //Store the results $result['http_code'] = intval($info['http_code']); $result['final_url'] = $info['url']; $result['request_duration'] = $info['total_time']; $result['redirect_count'] = $info['redirect_count']; //CURL doesn't return a request duration when a timeout happens, so we measure it ourselves. //It is useful to see how long the plugin waited for the server to respond before assuming it timed out. if (empty($result['request_duration'])) { $result['request_duration'] = $measured_request_duration; } //Determine if the link counts as "broken" if ($result['http_code'] == 0) { $result['broken'] = true; $error_code = curl_errno($ch); $log .= sprintf("%s [Error #%d]\n", curl_error($ch), $error_code); //We only handle a couple of CURL error codes; most are highly esoteric. //libcurl "CURLE_" constants can't be used here because some of them have //different names or values in PHP. switch ($error_code) { case 6: //CURLE_COULDNT_RESOLVE_HOST $result['status_code'] = BLC_LINK_STATUS_WARNING; $result['status_text'] = __('Server Not Found', 'broken-link-checker'); break; case 28: //CURLE_OPERATION_TIMEDOUT $result['timeout'] = true; break; case 7: //CURLE_COULDNT_CONNECT //More often than not, this error code indicates that the connection attempt //timed out. This heuristic tries to distinguish between connections that fail //due to timeouts and those that fail due to other causes. if ($result['request_duration'] >= 0.9 * $conf->options['timeout']) { $result['timeout'] = true; } else { $result['status_code'] = BLC_LINK_STATUS_WARNING; $result['status_text'] = __('Connection Failed', 'broken-link-checker'); } break; default: $result['status_code'] = BLC_LINK_STATUS_WARNING; $result['status_text'] = __('Unknown Error', 'broken-link-checker'); } } else { $result['broken'] = $this->is_error_code($result['http_code']); } curl_close($ch); if ($nobody && $result['broken']) { //The site in question might be expecting GET instead of HEAD, so lets retry the request //using the GET verb. return $this->check($url, true); //Note : normally a server that doesn't allow HEAD requests on a specific resource *should* //return "405 Method Not Allowed". Unfortunately, there are sites that return 404 or //another, even more general, error code instead. So just checking for 405 wouldn't be enough. } //When safe_mode or open_basedir is enabled CURL will be forbidden from following redirects, //so redirect_count will be 0 for all URLs. As a workaround, set it to 1 when the HTTP //response codes indicates a redirect but redirect_count is zero. //Note to self : Extracting the Location header might also be helpful. if ($result['redirect_count'] == 0 && in_array($result['http_code'], array(301, 302, 303, 307))) { $result['redirect_count'] = 1; } //Build the log from HTTP code and headers. $log .= '=== '; if ($result['http_code']) { $log .= sprintf(__('HTTP code : %d', 'broken-link-checker'), $result['http_code']); } else { $log .= __('(No response)', 'broken-link-checker'); } $log .= " ===\n\n"; $log .= $this->last_headers; if (!empty($result['broken']) && !empty($result['timeout'])) { $log .= "\n(" . __("Most likely the connection timed out or the domain doesn't exist.", 'broken-link-checker') . ')'; } $result['log'] = $log; //The hash should contain info about all pieces of data that pertain to determining if the //link is working. $result['result_hash'] = implode('|', array($result['http_code'], !empty($result['broken']) ? 'broken' : '0', !empty($result['timeout']) ? 'timeout' : '0', md5($result['final_url']))); return $result; }
/** * Apply a callback function to all HTML links found in a string and return the results. * * The link data array will contain at least these keys : * 'href' - the URL of the link (with htmlentitydecode() already applied). * '#raw' - the raw link code, e.g. the entire '<a href="...">...</a>' tag of a HTML link. * '#offset' - the offset within $content at which the first character of the link tag was found. * '#link_text' - the link's anchor text, if any. May contain HTML tags. * * Any attributes of the link tag will also be included in the returned array as attr_name => attr_value * pairs. This function will also automatically decode any HTML entities found in attribute values. * * @see blcParser::map() * * @param string $content A text string to parse for links. * @param callback $callback Callback function to apply to all found links. * @param mixed $extra If the optional $extra param. is supplied, it will be passed as the second parameter to the function $callback. * @return array An array of all detected links after applying $callback to each of them. */ function map($content, $callback, $extra = null) { $results = array(); //Find all links $links = blcUtility::extract_tags($content, 'a', false, true); //Iterate over the links and apply $callback to each foreach ($links as $link) { //Massage the found link into a form required for the callback function $param = $link['attributes']; $param = array_merge($param, array('#raw' => $link['full_tag'], '#offset' => $link['offset'], '#link_text' => $link['contents'], 'href' => isset($link['attributes']['href']) ? $link['attributes']['href'] : '')); //Prepare arguments for the callback $params = array($param); if (isset($extra)) { $params[] = $extra; } //Execute & store :) $results[] = call_user_func_array($callback, $params); } return $results; }
/** * Convert an internationalized domain name (or URL) from ASCII-compatible encoding to UTF8. * * @param string $url * @return string */ static function idn_to_utf8($url) { $idn = blcUtility::get_idna_converter(); if ($idn != null) { $url = $idn->decode($url); } return $url; }
function check($url, $use_get = false) { $this->last_headers = ''; $url = $this->clean_url($url); $result = array('broken' => false); $log = ''; //Get the BLC configuration. It's used below to set the right timeout values and such. $conf = blc_get_configuration(); //Init curl. $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $this->urlencodefix($url)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); //Masquerade as Internet explorer //$ua = 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)'; $ua = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30)'; curl_setopt($ch, CURLOPT_USERAGENT, $ua); //Add a semi-plausible referer header to avoid tripping up some bot traps curl_setopt($ch, CURLOPT_REFERER, get_option('home')); //Redirects don't work when safe mode or open_basedir is enabled. if (!blcUtility::is_safe_mode() && !blcUtility::is_open_basedir()) { curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); } //Set maximum redirects curl_setopt($ch, CURLOPT_MAXREDIRS, 10); //Set the timeout curl_setopt($ch, CURLOPT_TIMEOUT, $conf->options['timeout']); //Set the proxy configuration. The user can provide this in wp-config.php if (defined('WP_PROXY_HOST')) { curl_setopt($ch, CURLOPT_PROXY, WP_PROXY_HOST); } if (defined('WP_PROXY_PORT')) { curl_setopt($ch, CURLOPT_PROXYPORT, WP_PROXY_PORT); } if (defined('WP_PROXY_USERNAME')) { $auth = WP_PROXY_USERNAME; if (defined('WP_PROXY_PASSWORD')) { $auth .= ':' . WP_PROXY_PASSWORD; } curl_setopt($ch, CURLOPT_PROXYUSERPWD, $auth); } //Make CURL return a valid result even if it gets a 404 or other error. curl_setopt($ch, CURLOPT_FAILONERROR, false); $nobody = !$use_get; //Whether to send a HEAD request (the default) or a GET request $parts = @parse_url($url); if ($parts['scheme'] == 'https') { curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); //Required to make HTTPS URLs work. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); //Likewise. $nobody = false; //Can't use HEAD with HTTPS. } if ($nobody) { //If possible, use HEAD requests for speed. curl_setopt($ch, CURLOPT_NOBODY, true); } else { //If we must use GET at least limit the amount of downloaded data. curl_setopt($ch, CURLOPT_HTTPHEADER, array('Range: bytes=0-2048')); //2 KB } //Register a callback function which will process the HTTP header(s). //It can be called multiple times if the remote server performs a redirect. curl_setopt($ch, CURLOPT_HEADERFUNCTION, array(&$this, 'read_header')); //Execute the request curl_exec($ch); $info = curl_getinfo($ch); curl_close($ch); //Store the results $result['http_code'] = intval($info['http_code']); $result['timeout'] = $result['http_code'] == 0; //If the code is 0 then it's probably a timeout $result['final_url'] = $info['url']; $result['request_duration'] = $info['total_time']; $result['redirect_count'] = $info['redirect_count']; //Determine if the link counts as "broken" $result['broken'] = $this->is_error_code($result['http_code']) || $result['timeout']; if ($nobody && $result['broken']) { //The site in question might be expecting GET instead of HEAD, so lets retry the request //using the GET verb. return $this->check($url, true); //Note : normally a server that doesn't allow HEAD requests on a specific resource *should* //return "405 Method Not Allowed". Unfortunately, there are sites that return 404 or //another, even more general, error code instead. So just checking for 405 wouldn't be enough. } //When safe_mode or open_basedir is enabled CURL will be forbidden from following redirects, //so redirect_count will be 0 for all URLs. As a workaround, set it to 1 when the HTTP //response codes indicates a redirect but redirect_count is zero. //Note to self : Extracting the Location header might also be helpful. if ($result['redirect_count'] == 0 && in_array($result['http_code'], array(301, 302, 303, 307))) { $result['redirect_count'] = 1; } //Build the log from HTTP code and headers. //TODO: Put some kind of a color-coded error explanation at the top of the log, not a cryptic HTTP code. $log .= '=== '; if ($result['http_code']) { $log .= sprintf(__('HTTP code : %d', 'broken-link-checker'), $result['http_code']); } else { $log .= __('(No response)', 'broken-link-checker'); } $log .= " ===\n\n"; $log .= $this->last_headers; if ($result['broken'] && $result['timeout']) { $log .= "\n(" . __("Most likely the connection timed out or the domain doesn't exist.", 'broken-link-checker') . ')'; } $result['log'] = $log; //The hash should contain info about all pieces of data that pertain to determining if the //link is working. $result['result_hash'] = implode('|', array($result['http_code'], $result['broken'] ? 'broken' : '0', $result['timeout'] ? 'timeout' : '0', md5($result['final_url']))); return $result; }
function ui_get_source_comment($container, $container_field = '') { //Display a comment icon. if ($container_field == 'comment_author_url') { $image = 'font-awesome/font-awesome-user.png'; } else { $image = 'font-awesome/font-awesome-comment-alt.png'; } $comment = $container->get_wrapped_object(); //Display a small text sample from the comment $text_sample = strip_tags($comment->comment_content); $text_sample = blcUtility::truncate($text_sample, 65); return array('image' => $image, 'text_sample' => $text_sample, 'comment_author' => esc_attr($comment->comment_author), 'comment_id' => esc_attr($comment->comment_ID), 'comment_status' => wp_get_comment_status($comment->comment_ID), 'container_post_title' => get_the_title($comment->comment_post_ID), 'container_post_status' => get_post_status($comment->comment_post_ID), 'container_post_ID' => $comment->comment_post_ID); }
function check($url) { $result = array('final_url' => $url, 'redirect_count' => 0, 'timeout' => false, 'broken' => false, 'log' => "<em>(Using YouTube API)</em>\n\n", 'result_hash' => ''); //Extract the video ID from the URL $components = @parse_url($url); parse_str($components['query'], $query); $video_id = $query['v']; //Fetch video data from the YouTube API $api_url = 'http://gdata.youtube.com/feeds/api/videos/' . $video_id; $conf =& blc_get_configuration(); $args = array('timeout' => $conf->options['timeout']); $start = microtime_float(); $response = wp_remote_get($api_url, $args); $result['request_duration'] = microtime_float() - $start; //Placeholders for video restriction data $state_name = $state_reason = ''; //Got anything? if (is_wp_error($response)) { $result['log'] .= "Error.\n" . $response->get_error_message(); //WP doesn't make it easy to distinguish between different internal errors. $result['broken'] = true; $result['http_code'] = 0; } else { $result['http_code'] = intval($response['response']['code']); switch ($result['http_code']) { case 404: //Not found $result['log'] .= __('Video Not Found', 'broken-link-checker'); $result['broken'] = true; $result['http_code'] = 0; $result['status_text'] = __('Video Not Found', 'broken-link-checker'); $result['status_code'] = BLC_LINK_STATUS_ERROR; break; case 403: //Forbidden. Usually means that the video has been removed. Body contains details. $result['log'] .= $response['body']; $result['broken'] = true; $result['http_code'] = 0; $result['status_text'] = __('Video Removed', 'broken-link-checker'); $result['status_code'] = BLC_LINK_STATUS_ERROR; break; case 400: //Bad request. Usually means that the video ID is incorrect. Body contains details. $result['log'] .= $response['body']; $result['broken'] = true; $result['http_code'] = 0; $result['status_text'] = __('Invalid Video ID', 'broken-link-checker'); $result['status_code'] = BLC_LINK_STATUS_WARNING; break; case 200: //Video exists, but may be restricted. Check for <yt:state> tags. //See http://code.google.com/apis/youtube/2.0/reference.html#youtube_data_api_tag_yt:state //Can we count on an XML parser being installed? No, probably not. //Back to our makeshift tag "parser" we go. $state = blcUtility::extract_tags($response['body'], 'yt:state', false); if (empty($state)) { //Phew, no restrictions. $result['log'] .= __("Video OK", 'broken-link-checker'); $result['status_text'] = __('OK', 'link status', 'broken-link-checker'); $result['status_code'] = BLC_LINK_STATUS_OK; $result['http_code'] = 0; } else { //Get the state name and code and append them to the log $state = reset($state); $state_name = $state['attributes']['name']; $state_reason = isset($state['attributes']['reasonCode']) ? $state['attributes']['reasonCode'] : ''; $result['result_hash'] = 'youtube_api|' . $state_name . '|' . $state_reason; $result['log'] .= sprintf(__('Video status : %s%s', 'broken-link-checker'), $state_name, $state_reason ? ' [' . $state_reason . ']' : ''); //A couple of restricted states are not that bad $state_ok = $state_name == 'processing' || $state_name == 'restricted' && $state_reason == 'limitedSyndication'; if ($state_ok) { $result['broken'] = false; $result['status_text'] = __('OK', 'link status', 'broken-link-checker'); $result['status_code'] = BLC_LINK_STATUS_OK; $result['http_code'] = 0; } else { $result['broken'] = true; $result['status_text'] = __('Video Restricted', 'broken-link-checker'); $result['status_code'] = BLC_LINK_STATUS_WARNING; $result['http_code'] = 0; } } //Add the video title to the log, purely for information. //http://code.google.com/apis/youtube/2.0/reference.html#youtube_data_api_tag_media:title $title = blcUtility::extract_tags($response['body'], 'media:title', false); if (!empty($title)) { $result['log'] .= "\n\nTitle : \"" . $title[0]['contents'] . '"'; } break; default: $result['log'] .= $result['http_code'] . $response['response']['message']; $result['log'] .= "\n" . __('Unknown YouTube API response received.'); break; } } //The hash should contain info about all pieces of data that pertain to determining if the //link is working. $result['result_hash'] = implode('|', array('youtube', $result['http_code'], $result['broken'] ? 'broken' : '0', $result['timeout'] ? 'timeout' : '0', $state_name, $state_reason)); return $result; }
/** * Check a YouTube API response that contains a single playlist. * * @param array $response * @param array $result * @return array */ protected function check_playlist($response, $result) { switch ($result['http_code']) { case 404: //Not found $result['log'] .= __('Playlist Not Found', 'broken-link-checker'); $result['broken'] = true; $result['http_code'] = 0; $result['status_text'] = __('Playlist Not Found', 'broken-link-checker'); $result['status_code'] = BLC_LINK_STATUS_ERROR; break; case 403: //Forbidden. We're unlikely to see this code for playlists, but lets allow it. $result['log'] .= $response['body']; $result['broken'] = true; $result['status_text'] = __('Playlist Restricted', 'broken-link-checker'); $result['status_code'] = BLC_LINK_STATUS_ERROR; break; case 400: //Bad request. Probably indicates a client error (invalid API request). Body contains details. $result['log'] .= $response['body']; $result['broken'] = true; $result['status_text'] = __('Invalid Playlist', 'broken-link-checker'); $result['status_code'] = BLC_LINK_STATUS_WARNING; break; case 200: //The playlist exists, but some of the videos may be restricted. //Check for <yt:state> tags. $video_states = blcUtility::extract_tags($response['body'], 'yt:state', false); if (empty($video_states)) { //No restrictions. Does the playlist have any entries? $entries = blcUtility::extract_tags($response['body'], 'entry', false); if (!empty($entries)) { //All is well. $result['log'] .= __("Playlist OK", 'broken-link-checker'); $result['status_text'] = __('OK', 'link status', 'broken-link-checker'); $result['status_code'] = BLC_LINK_STATUS_OK; $result['http_code'] = 0; } else { //An empty playlist. It is possible that all of the videos //have been deleted. Treat it as a warning. $result['log'] .= __("This playlist has no entries or all entries have been deleted.", 'broken-link-checker'); $result['status_text'] = __('Empty Playlist', 'link status', 'broken-link-checker'); $result['status_code'] = BLC_LINK_STATUS_WARNING; $result['http_code'] = 0; $result['broken'] = true; } } else { //Treat the playlist as broken if at least one video is inaccessible. foreach ($video_states as $state) { $state_name = $state['attributes']['name']; $state_reason = isset($state['attributes']['reasonCode']) ? $state['attributes']['reasonCode'] : ''; if (!$this->is_state_ok($state_name, $state_reason)) { $result['log'] .= sprintf(__('Video status : %s%s', 'broken-link-checker'), $state_name, $state_reason ? ' [' . $state_reason . ']' : ''); $result['state_name'] = $state_name; $result['state_reason'] = $state_reason; $result['broken'] = true; $result['status_text'] = __('Video Restricted', 'broken-link-checker'); $result['status_code'] = BLC_LINK_STATUS_WARNING; $result['http_code'] = 0; break; } } if (!$result['broken']) { $result['status_text'] = __('OK', 'link status', 'broken-link-checker'); $result['status_code'] = BLC_LINK_STATUS_OK; $result['http_code'] = 0; } } //Add the playlist title to the log, purely for information. $title = blcUtility::extract_tags($response['body'], 'title', false); if (!empty($title)) { $result['log'] .= "\n\nPlaylist title : \"" . $title[0]['contents'] . '"'; } break; default: $result['log'] .= $result['http_code'] . $response['response']['message']; $result['log'] .= "\n" . __('Unknown YouTube API response received.'); break; } return $result; }
function send_email_notifications() { global $wpdb; //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, 'max_results' => 0)); if (empty($links)) { return; } $cnt = count($links); //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.", $cnt, 'broken-link-checker'), $cnt); $body .= "<br>"; $max_displayed_links = 5; if ($cnt > $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'); } $body .= "<p>{$line}</p>"; //Show up to $max_displayed_links broken link instances right in the email. $displayed = 0; foreach ($links as $link) { $instances = $link->get_instances(); foreach ($instances as $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($link->url), blcUtility::truncate($link->url, 70, '')), sprintf(__('Source : %s', 'broken-link-checker'), $instance->ui_get_source('email'))); $link_entry = implode("<br>", $pieces); $body .= "{$link_entry}<br><br>"; $displayed++; if ($displayed >= $max_displayed_links) { break 2; //Exit both foreach loops } } } //Add a link to the "Broken Links" tab. $body .= __("You can see all broken links here:", 'broken-link-checker') . "<br>"; $link_page = admin_url('tools.php?page=view-broken-links'); $body .= sprintf('<a href="%1$s">%1$s</a>', $link_page); //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')); //Send the notification $rez = wp_mail(get_option('admin_email'), $subject, $body); if ($rez) { $this->conf->options['last_notification_sent'] = time(); $this->conf->save_options(); } //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')); }
?> </ul> </div> </div> <?php } } ?> <!-- Admin Menu Editor Pro ad --> <?php if ($show_ame_ad) { //Display an ad for Admin Menu Editor. //We're A/B testing a bunch of different ad copies. $ame_copy_variants = array(array('a', "Add, delete, hide, or move any admin menu item."), array('b', "Organize your admin menu the way you want it."), array('c', "Hide, move or customize admin menus. Perfect for client sites.")); $ad_copy_index = intval(blcUtility::constrained_hash(get_site_url(), 0, count($ame_copy_variants))); $ad_copy = $ame_copy_variants[$ad_copy_index]; $ad_url = sprintf('http://w-shadow.com/admin-menu-editor-pro/?utm_source=broken_link_checker&utm_medium=text_link&utm_campaign=Plugins&utm_content=%s', urlencode('ad_copy_') . $ad_copy[0]); ?> <div class="postbox" id="advertising"> <h3 class="hndle"><?php _e('More plugins by Janis Elsts', 'broken-link-checker'); ?> </h3> <div class="inside"> <p class="ws-ame-ad-copy"><?php echo $ad_copy[1]; ?> </p> <p class="ws-ame-ad-link"> <a href="<?php
/** * Extract embedded elements from a HTML string. * * This function returns an array of <embed> elements found in the input * string. Embeds without a 'src' attribute are skipped. * * Each array item has the same basic structure as the array items * returned by blcUtility::extract_tags(), plus an additional 'embed_code' key * that contains the HTML code for the element. If the embed element is wrapped * in an <object>, the 'embed_code' key contains the full HTML for the entire * <object> + <embed> structure. * * @uses blcUtility::extract_tags() This function is a simple wrapper around extract_tags() * * @param string $html * @return array */ function extract_embeds($html) { $results = array(); //remove all <code></code> blocks first $html = preg_replace('/<code[^>]*>.+?<\\/code>/si', ' ', $html); //Find likely-looking <object> elements $objects = blcUtility::extract_tags($html, 'object', false, true); foreach ($objects as $candidate) { //Find the <embed> tag $embed = blcUtility::extract_tags($candidate['full_tag'], 'embed', true); if (empty($embed)) { continue; } $embed = reset($embed); //Take the first (and only) found <embed> element if (empty($embed['attributes']['src'])) { continue; } $embed['embed_code'] = $candidate['full_tag']; $results[] = $embed; //Remove the element so it doesn't come up when we search for plain <embed> elements. $html = str_replace($candidate['full_tag'], ' ', $html); } //Find <embed> elements not wrapped in an <object> element. $embeds = blcUtility::extract_tags($html, 'embed', false, true); foreach ($embeds as $embed) { if (!empty($embed['attributes']['src'])) { $embed['embed_code'] = $embed['full_tag']; $results[] = $embed; } } return $results; }
/** * @param blcLink $link * @param blcLinkInstance[] $instances */ function column_status($link, $instances) { printf('<table class="mini-status" title="%s">', esc_attr(__('Show more info about this link', 'broken-link-checker'))); $status = $link->analyse_status(); printf('<tr class="link-status-row link-status-%s"> <td> <span class="http-code">%s</span> <span class="status-text">%s</span> </td> </tr>', $status['code'], empty($link->http_code) ? '' : $link->http_code, $status['text']); //Last checked... if ($link->last_check != 0) { $last_check = _x('Checked', 'checked how long ago', 'broken-link-checker') . ' '; $last_check .= blcUtility::fuzzy_delta(time() - $link->last_check, 'ago'); printf('<tr class="link-last-checked"><td>%s</td></tr>', $last_check); } //Broken for... if ($link->broken) { $delta = time() - $link->first_failure; $broken_for = blcUtility::fuzzy_delta($delta); printf('<tr class="link-broken-for"><td>%s %s</td></tr>', __('Broken for', 'broken-link-checker'), $broken_for); } echo '</table>'; }