/** * Class "constructor". Can't use an actual constructor due to how PHP4 handles object references. * * Specifically, this class is a singleton. The function needs to pass $this to several other * functions (to set up hooks), which will store the reference for later use. However, it appears * that in PHP4 the actual value of $this is thrown away right after the constructor finishes, and * `new` returns a *copy* of $this. The result is that getInstance() won't be returning a ref. * to the same object as is used for hook callbacks. And that's horrible. * * Sets up hooks that monitor added/modified/deleted posts and registers * virtual modules for all post types. * * @return void */ function init() { $this->plugin_conf = blc_get_configuration(); if (isset($this->plugin_conf->options['enabled_post_statuses'])) { $this->enabled_post_statuses = $this->plugin_conf->options['enabled_post_statuses']; } //Register a virtual container module for each enabled post type $module_manager = blcModuleManager::getInstance(); $post_types = get_post_types(array(), 'objects'); $exceptions = array('revision', 'nav_menu_item', 'attachment'); foreach ($post_types as $data) { $post_type = $data->name; if (in_array($post_type, $exceptions)) { continue; } $module_manager->register_virtual_module($post_type, array('Name' => $data->labels->name, 'ModuleCategory' => 'container', 'ModuleContext' => 'all', 'ModuleClassName' => 'blcAnyPostContainerManager')); } //These hooks update the synch & instance records when posts are added, deleted or modified. add_action('delete_post', array(&$this, 'post_deleted')); add_action('save_post', array(&$this, 'post_saved')); //We also treat post trashing/untrashing as delete/save. add_action('trash_post', array(&$this, 'post_deleted')); add_action('untrash_post', array(&$this, 'post_saved')); //Highlight and nofollow broken links in posts & pages if ($this->plugin_conf->options['mark_broken_links'] || $this->plugin_conf->options['nofollow_broken_links']) { add_filter('the_content', array(&$this, 'hook_the_content')); if ($this->plugin_conf->options['mark_broken_links'] && !empty($this->plugin_conf->options['broken_link_css'])) { add_action('wp_head', array(&$this, 'hook_wp_head')); } } }
/** * A helper method for parsing a list of search criteria and generating the parts of the SQL query. * * @see blcLinkQuery::get_links() * * @param array $params An array of search criteria. * @return array 'where_exprs' - an array of search expressions, 'join_instances' - whether joining the instance table is required. */ function compile_search_params($params) { global $wpdb; /** @var wpdb $wpdb */ //Track whether we'll need to left-join the instance table to run the query. $join_instances = false; //Generate the individual clauses of the WHERE expression and store them in an array. $pieces = array(); //Convert parser and container type lists to arrays of valid values $s_parser_type = array(); if (!empty($params['s_parser_type'])) { $s_parser_type = $params['s_parser_type']; if (is_string($s_parser_type)) { $s_parser_type = preg_split('/[,\\s]+/', $s_parser_type); } } $s_container_type = array(); if (!empty($params['s_container_type'])) { $s_container_type = $params['s_container_type']; if (is_string($s_container_type)) { $s_container_type = preg_split('/[,\\s]+/', $s_container_type); } } //Don't include links with instances that reference invalid (not currently loaded) //containers and parsers (unless specifically told to also include invalid links). if (empty($params['include_invalid'])) { $join_instances = true; $module_manager = blcModuleManager::getInstance(); $loaded_containers = array_keys($module_manager->get_active_by_category('container')); $loaded_parsers = array_keys($module_manager->get_active_by_category('parser')); if (empty($s_parser_type)) { $s_parser_type = $loaded_parsers; } else { $s_parser_type = array_intersect($s_parser_type, $loaded_parsers); } if (empty($s_container_type)) { $s_container_type = $loaded_containers; } else { $s_container_type = array_intersect($s_container_type, $loaded_containers); } } //Parser type should match the parser_type column in the instance table. if (!empty($s_parser_type)) { $s_parser_type = array_map('trim', array_unique($s_parser_type)); $s_parser_type = array_map('esc_sql', $s_parser_type); if (count($s_parser_type) == 1) { $pieces[] = sprintf("instances.parser_type = '%s'", reset($s_parser_type)); } else { $pieces[] = "instances.parser_type IN ('" . implode("', '", $s_parser_type) . "')"; } $join_instances = true; } //Container type should match the container_type column in the instance table. if (!empty($s_container_type)) { //Sanitize for use in SQL $s_container_type = array_map('trim', array_unique($s_container_type)); $s_container_type = array_map('esc_sql', $s_container_type); if (count($s_container_type) == 1) { $pieces[] = sprintf("instances.container_type = '%s'", reset($s_container_type)); } else { $pieces[] = "instances.container_type IN ('" . implode("', '", $s_container_type) . "')"; } $join_instances = true; } //A part of the WHERE expression can be specified explicitly if (!empty($params['where_expr'])) { $pieces[] = $params['where_expr']; $join_instances = $join_instances || stripos($params['where_expr'], 'instances') !== false; } //List of allowed link ids (either an array or comma-separated) if (!empty($params['link_ids'])) { $link_ids = $params['link_ids']; if (is_string($link_ids)) { $link_ids = preg_split('/[,\\s]+/', $link_ids); } //Only accept non-zero integers $sanitized_link_ids = array(); foreach ($link_ids as $id) { $id = intval($id); if ($id != 0) { $sanitized_link_ids[] = $id; } } $pieces[] = 'links.link_id IN (' . implode(', ', $sanitized_link_ids) . ')'; } //Anchor text - use LIKE search if (!empty($params['s_link_text'])) { $s_link_text = esc_sql($this->esc_like($params['s_link_text'])); $s_link_text = str_replace('*', '%', $s_link_text); $pieces[] = '(instances.link_text LIKE "%' . $s_link_text . '%")'; $join_instances = true; } //URL - try to match both the initial URL and the final URL. //There is limited wildcard support, e.g. "google.*/search" will match both //"google.com/search" and "google.lv/search" if (!empty($params['s_link_url'])) { $s_link_url = esc_sql($this->esc_like($params['s_link_url'])); $s_link_url = str_replace('*', '%', $s_link_url); $pieces[] = '(links.url LIKE "%' . $s_link_url . '%") OR ' . '(links.final_url LIKE "%' . $s_link_url . '%")'; } //Container ID should match... you guessed it - container_id if (!empty($params['s_container_id'])) { $s_container_id = intval($params['s_container_id']); if ($s_container_id != 0) { $pieces[] = "instances.container_id = {$s_container_id}"; $join_instances = true; } } //Link type can match either the the parser_type or the container_type. if (!empty($params['s_link_type'])) { $s_link_type = esc_sql($params['s_link_type']); $pieces[] = "instances.parser_type = '{$s_link_type}' OR instances.container_type='{$s_link_type}'"; $join_instances = true; } //HTTP code - the user can provide a list of HTTP response codes and code ranges. //Example : 201,400-410,500 if (!empty($params['s_http_code'])) { //Strip spaces. $params['s_http_code'] = str_replace(' ', '', $params['s_http_code']); //Split by comma $codes = explode(',', $params['s_http_code']); $individual_codes = array(); $ranges = array(); //Try to parse each response code or range. Invalid ones are simply ignored. foreach ($codes as $code) { if (is_numeric($code)) { //It's a single number $individual_codes[] = abs(intval($code)); } elseif (strpos($code, '-') !== false) { //Try to parse it as a range $range = explode('-', $code, 2); if (count($range) == 2 && is_numeric($range[0]) && is_numeric($range[0])) { //Make sure the smaller code comes first $range = array(intval($range[0]), intval($range[1])); $ranges[] = array(min($range), max($range)); } } } $piece = array(); //All individual response codes get one "http_code IN (...)" clause if (!empty($individual_codes)) { $piece[] = '(links.http_code IN (' . implode(', ', $individual_codes) . '))'; } //Ranges get a "http_code BETWEEN min AND max" clause each if (!empty($ranges)) { $range_strings = array(); foreach ($ranges as $range) { $range_strings[] = "(links.http_code BETWEEN {$range['0']} AND {$range['1']})"; } $piece[] = '( ' . implode(' OR ', $range_strings) . ' )'; } //Finally, generate a composite WHERE clause for both types of response code queries if (!empty($piece)) { $pieces[] = implode(' OR ', $piece); } } //Dismissed links are included by default, but can explicitly included //or filtered out by passing a special param. if (isset($params['s_include_dismissed'])) { $s_include_dismissed = !empty($params['s_include_dismissed']); $pieces['filter_dismissed'] = $s_include_dismissed ? '1' : '(dismissed = 0)'; } //Optionally sorting is also possible $order_exprs = array(); if (!empty($params['orderby'])) { $allowed_columns = array('url' => 'links.url', 'link_text' => 'instances.link_text'); $column = $params['orderby']; $direction = !empty($params['order']) ? strtolower($params['order']) : 'asc'; if (!in_array($direction, array('asc', 'desc'))) { $direction = 'asc'; } if (array_key_exists($column, $allowed_columns)) { $order_exprs[] = $allowed_columns[$column] . ' ' . $direction; } } //Custom filters can optionally call one of the native filters //to narrow down the result set. if (!empty($params['s_filter']) && isset($this->native_filters[$params['s_filter']])) { $the_filter = $this->native_filters[$params['s_filter']]; $extra_criteria = $this->compile_search_params($the_filter['params']); $pieces = array_merge($extra_criteria['where_exprs'], $pieces); $join_instances = $join_instances || $extra_criteria['join_instances']; } return array('where_exprs' => $pieces, 'join_instances' => $join_instances, 'order_exprs' => $order_exprs); }
/** * Remove instances that reference invalid containers or containers/parsers that are not currently loaded * * @return bool */ function blc_cleanup_instances() { global $wpdb; global $blclog; //Delete all instances that reference non-existent containers $q = "DELETE instances.*\n\t\t FROM \n \t\t\t{$wpdb->prefix}blc_instances AS instances LEFT JOIN {$wpdb->prefix}blc_synch AS synch\n \t\t\tON instances.container_type = synch.container_type AND instances.container_id = synch.container_id\n\t\t WHERE\n \t\t\tsynch.container_id IS NULL"; $rez = $wpdb->query($q); $blclog->log(sprintf('... %d instances deleted', $wpdb->rows_affected)); //Delete instances that reference containers and parsers that are no longer active $manager =& blcModuleManager::getInstance(); $active_containers = $manager->get_escaped_ids('container'); $active_parsers = $manager->get_escaped_ids('parser'); $q = "DELETE instances.*\n\t FROM {$wpdb->prefix}blc_instances AS instances\n\t WHERE\n\t instances.container_type NOT IN ({$active_containers}) OR\n\t instances.parser_type NOT IN ({$active_parsers})"; $rez2 = $wpdb->query($q); $blclog->log(sprintf('... %d more instances deleted', $wpdb->rows_affected)); return $rez !== false && $rez2 !== false; }
$blc_config_manager->options['installation_complete'] = false; $blc_config_manager->options['installation_flag_cleared_on'] = date('c') . ' (' . microtime(true) . ')'; //Note the time of the first installation (not very accurate, but still useful) if (empty($blc_config_manager->options['first_installation_timestamp'])) { $blc_config_manager->options['first_installation_timestamp'] = time(); } $blc_config_manager->save_options(); $blclog->info('Installation/update begins.'); //Load the base classes and utilities require_once BLC_DIRECTORY . '/includes/links.php'; require_once BLC_DIRECTORY . '/includes/link-query.php'; require_once BLC_DIRECTORY . '/includes/instances.php'; require_once BLC_DIRECTORY . '/includes/utility-class.php'; //Load the module subsystem require_once BLC_DIRECTORY . '/includes/modules.php'; $moduleManager = blcModuleManager::getInstance(); //If upgrading, activate/deactivate custom field and comment containers based on old ver. settings if (isset($blc_config_manager->options['check_comment_links'])) { if (!$blc_config_manager->options['check_comment_links']) { $moduleManager->deactivate('comment'); } unset($blc_config_manager->options['check_comment_links']); } if (empty($blc_config_manager->options['custom_fields'])) { $moduleManager->deactivate('custom_field'); } //Prepare the database. $blclog->info('Upgrading the database...'); $upgrade_start = microtime(true); require_once BLC_DIRECTORY . '/includes/admin/db-upgrade.php'; blcDatabaseUpgrader::upgrade_database();
/** * 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\r\n\t\t WHERE \r\n\t\t \t(\r\n\t\t\t\t \t( last_check_attempt < %s ) \r\n\t\t\t\t\tOR \r\n\t\t\t \t \t( \r\n\t\t\t\t\t\t(broken = 1 OR being_checked = 1) \r\n\t\t\t\t\t\tAND may_recheck = 1\r\n\t\t\t\t\t\tAND check_count < %d \r\n\t\t\t\t\t\tAND last_check_attempt < %s \r\n\t\t\t\t\t)\r\n\t\t\t\t)\r\n\t\t\t\tAND EXISTS (\r\n\t\t\t\t\tSELECT 1 FROM {$wpdb->prefix}blc_instances AS instances\r\n\t\t\t\t\tWHERE \r\n\t\t\t\t\t\tinstances.link_id = links.link_id\r\n\t\t\t\t\t\tAND ( instances.container_type IN ({$loaded_containers}) )\r\n\t\t\t\t\t\tAND ( instances.parser_type IN ({$loaded_parsers}) )\r\n\t\t\t\t)\r\n\t\t\t"; 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; }
/** * Get all parsers that support either the specified format or the container type. * If a parser supports both, it will still be included only once. * * @param string $format * @param string $container_type * @return array of blcParser */ static function get_parsers($format, $container_type) { $found = array(); //Retrieve a list of active parsers $manager = blcModuleManager::getInstance(); $active_parsers = $manager->get_modules_by_category('parser'); //Try each one foreach ($active_parsers as $module_id => $module_data) { $parser = $manager->get_module($module_id); //Will autoload if necessary if (!$parser) { continue; } if (in_array($format, $parser->supported_formats) || in_array($container_type, $parser->supported_containers)) { array_push($found, $parser); } } return $found; }
/** * Remove synch. records that reference container types not currently loaded * * @return bool */ static function cleanup_containers() { global $wpdb; /* @var wpdb $wpdb */ global $blclog; $module_manager = blcModuleManager::getInstance(); $start = microtime(true); $active_containers = $module_manager->get_escaped_ids('container'); $q = "DELETE synch.*\r\n\t\t FROM {$wpdb->prefix}blc_synch AS synch\r\n\t\t WHERE\r\n\t \t synch.container_type NOT IN ({$active_containers})"; $rez = $wpdb->query($q); $elapsed = microtime(true) - $start; $blclog->log(sprintf('... %d synch records deleted in %.3f seconds', $wpdb->rows_affected, $elapsed)); return $rez !== false; }
/** * Remove synch. records that reference container types not currently loaded * * @return bool */ function cleanup_containers() { global $wpdb; global $blclog; $module_manager =& blcModuleManager::getInstance(); $active_containers = $module_manager->get_escaped_ids('container'); $q = "DELETE synch.*\n\t\t FROM {$wpdb->prefix}blc_synch AS synch\n\t\t WHERE\n\t \t synch.container_type NOT IN ({$active_containers})"; $rez = $wpdb->query($q); $blclog->log(sprintf('... %d synch records deleted', $wpdb->rows_affected)); return $rez !== false; }
/** * Get a checker object that can check the specified URL. * * @param string $url * @return blcChecker|null */ static function get_checker_for($url) { $parsed = @parse_url($url); $manager = blcModuleManager::getInstance(); $active_checkers = $manager->get_active_by_category('checker'); foreach ($active_checkers as $module_id => $module_data) { //Try the URL pattern in the header first. If it doesn't match, //we can avoid loading the module altogether. if (!empty($module_data['ModuleCheckerUrlPattern'])) { if (!preg_match($module_data['ModuleCheckerUrlPattern'], $url)) { continue; } } $checker = $manager->get_module($module_id); if (!$checker) { continue; } //The can_check() method can perform more sophisticated filtering, //or just return true if the checker thinks matching the URL regex //is sufficient. if ($checker->can_check($url, $parsed)) { return $checker; } } $checker = null; return $checker; }
<?php /** * Load all files pertaining to BLC's module subsystem */ require 'module-manager.php'; require 'module-base.php'; require 'containers.php'; require 'checkers.php'; require 'parsers.php'; $blc_module_manager = blcModuleManager::getInstance(array('http', 'link', 'image', 'metadata', 'url_field', 'comment', 'custom_field', 'post', 'page', 'youtube-checker', 'youtube-iframe', 'dummy')); require 'any-post.php'; //Let other plugins register virtual modules. do_action('blc_register_modules', $blc_module_manager);
<?php /** * Load all files pertaining to BLC's module subsystem */ require 'module-manager.php'; require 'module-base.php'; require 'containers.php'; require 'checkers.php'; require 'parsers.php'; $blc_module_manager =& blcModuleManager::getInstance(array('http', 'link', 'image', 'metadata', 'url_field', 'blogroll', 'comment', 'custom_field', 'post', 'page', 'dummy')); require 'any-post.php';