PageRenderTime 58ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-content/plugins/broken-link-checker/includes/link-query.php

https://bitbucket.org/lgorence/quickpress
PHP | 821 lines | 491 code | 125 blank | 205 comment | 95 complexity | 88e4aafbee3294aa98e8f6f6126869f6 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, AGPL-1.0
  1. <?php
  2. /**
  3. * Class for querying, sorting and filtering links.
  4. * Used as a singleton.
  5. *
  6. * @package Broken Link Checker
  7. * @access public
  8. */
  9. class blcLinkQuery {
  10. var $native_filters;
  11. var $search_filter;
  12. var $custom_filters = array();
  13. var $valid_url_params = array();
  14. function __construct(){
  15. //Init. the available native filters.
  16. $this->native_filters = array(
  17. 'broken' => array(
  18. 'params' => array(
  19. 'where_expr' => '( broken = 1 )',
  20. 's_include_dismissed' => false,
  21. ),
  22. 'name' => __('Broken', 'broken-link-checker'),
  23. 'heading' => __('Broken Links', 'broken-link-checker'),
  24. 'heading_zero' => __('No broken links found', 'broken-link-checker'),
  25. 'native' => true,
  26. ),
  27. 'redirects' => array(
  28. 'params' => array(
  29. 'where_expr' => '( redirect_count > 0 )',
  30. 's_include_dismissed' => false,
  31. ),
  32. 'name' => __('Redirects', 'broken-link-checker'),
  33. 'heading' => __('Redirected Links', 'broken-link-checker'),
  34. 'heading_zero' => __('No redirects found', 'broken-link-checker'),
  35. 'native' => true,
  36. ),
  37. 'dismissed' => array(
  38. 'params' => array(
  39. 'where_expr' => '( dismissed = 1 )',
  40. ),
  41. 'name' => __('Dismissed', 'broken-link-checker'),
  42. 'heading' => __('Dismissed Links', 'broken-link-checker'),
  43. 'heading_zero' => __('No dismissed links found', 'broken-link-checker'),
  44. 'native' => true,
  45. ),
  46. 'all' => array(
  47. 'params' => array(
  48. 'where_expr' => '1',
  49. ),
  50. 'name' => __('All', 'broken-link-checker'),
  51. 'heading' => __('Detected Links', 'broken-link-checker'),
  52. 'heading_zero' => __('No links found (yet)', 'broken-link-checker'),
  53. 'native' => true,
  54. ),
  55. );
  56. //Create the special "search" filter
  57. $this->search_filter = array(
  58. 'name' => __('Search', 'broken-link-checker'),
  59. 'heading' => __('Search Results', 'broken-link-checker'),
  60. 'heading_zero' => __('No links found for your query', 'broken-link-checker'),
  61. 'params' => array(),
  62. 'use_url_params' => true,
  63. 'hidden' => true,
  64. );
  65. //These search arguments may be passed via the URL if the filter's 'use_url_params' field is set to True.
  66. //They map to the fields of the search form on the Tools -> Broken Links page. Only these arguments
  67. //can be used in user-defined filters.
  68. $this->valid_url_params = array(
  69. 's_link_text',
  70. 's_link_url',
  71. 's_parser_type',
  72. 's_container_type',
  73. 's_link_type',
  74. 's_http_code',
  75. 's_filter',
  76. );
  77. }
  78. static function getInstance(){
  79. static $instance = null;
  80. if ( is_null($instance) ){
  81. $instance = new blcLinkQuery;
  82. }
  83. return $instance;
  84. }
  85. /**
  86. * Load and return the list of user-defined link filters.
  87. *
  88. * @return array An array of custom filter definitions. If there are no custom filters defined returns an empty array.
  89. */
  90. function load_custom_filters(){
  91. global $wpdb; /** @var wpdb $wpdb */
  92. $filter_data = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}blc_filters ORDER BY name ASC", ARRAY_A);
  93. $filters = array();
  94. if ( !empty($filter_data) ) {
  95. foreach($filter_data as $data){
  96. wp_parse_str($data['params'], $params);
  97. $filters[ 'f'.$data['id'] ] = array(
  98. 'name' => $data['name'],
  99. 'params' => $params,
  100. 'heading' => ucwords($data['name']),
  101. 'heading_zero' => __('No links found for your query', 'broken-link-checker'),
  102. 'custom' => true,
  103. );
  104. }
  105. }
  106. $this->custom_filters = $filters;
  107. return $filters;
  108. }
  109. /**
  110. * Add a custom link filter.
  111. *
  112. * @param string $name Filter name.
  113. * @param string|array $params Filter params. Either as a query string, or an array.
  114. * @return string|bool The ID of the newly added filter, or False.
  115. */
  116. function create_custom_filter($name, $params){
  117. global $wpdb; /** @var wpdb $wpdb */
  118. if ( is_array($params) ){
  119. $params = http_build_query($params, null, '&');
  120. }
  121. //Save the new filter
  122. $q = $wpdb->prepare(
  123. "INSERT INTO {$wpdb->prefix}blc_filters(name, params) VALUES (%s, %s)",
  124. $name, $params
  125. );
  126. if ( $wpdb->query($q) !== false ){
  127. $filter_id = 'f'.$wpdb->insert_id;
  128. return $filter_id;
  129. } else {
  130. return false;
  131. }
  132. }
  133. /**
  134. * Delete a custom filter
  135. *
  136. * @param string $filter_id
  137. * @return bool True on success, False if a database error occured.
  138. */
  139. function delete_custom_filter($filter_id){
  140. global $wpdb; /** @var wpdb $wpdb */
  141. //Remove the "f" character from the filter ID to get its database key
  142. $filter_id = intval(ltrim($_POST['filter_id'], 'f'));
  143. //Try to delete the filter
  144. $q = $wpdb->prepare("DELETE FROM {$wpdb->prefix}blc_filters WHERE id = %d", $filter_id);
  145. if ( $wpdb->query($q) !== false ){
  146. return true;
  147. } else {
  148. return false;
  149. }
  150. }
  151. function get_filters(){
  152. $filters = array_merge($this->native_filters, $this->custom_filters);
  153. $filters['search'] = $this->search_filter;
  154. return $filters;
  155. }
  156. /**
  157. * Get a link search filter by filter ID.
  158. *
  159. * @param string $filter_id
  160. * @return array|null
  161. */
  162. function get_filter($filter_id){
  163. $filters = $this->get_filters();
  164. if ( isset($filters[$filter_id]) ){
  165. return $filters[$filter_id];
  166. } else {
  167. return null;
  168. }
  169. }
  170. /**
  171. * Get link search parameters from the specified filter.
  172. *
  173. * @param array $filter
  174. * @return array An array of parameters suitable for use with blcLinkQuery::get_links()
  175. */
  176. function get_search_params( $filter = null ){
  177. //If present, the filter's parameters may be saved either as an array or a string.
  178. $params = array();
  179. if ( !empty($filter) && !empty($filter['params']) ){
  180. $params = $filter['params'];
  181. if ( is_string( $params ) ){
  182. wp_parse_str($params, $params);
  183. }
  184. }
  185. //Merge in the parameters from the current request, if required
  186. if ( isset($filter['use_url_params']) && $filter['use_url_params'] ){
  187. $params = array_merge($params, $this->get_url_search_params());
  188. }
  189. return $params;
  190. }
  191. /**
  192. * Extract search query parameters from the current URL
  193. *
  194. * @return array
  195. */
  196. function get_url_search_params(){
  197. $url_params = array();
  198. foreach ($_GET as $param => $value){
  199. if ( in_array($param, $this->valid_url_params) ){
  200. $url_params[$param] = $value;
  201. }
  202. }
  203. return $url_params;
  204. }
  205. /**
  206. * A helper method for parsing a list of search criteria and generating the parts of the SQL query.
  207. *
  208. * @see blcLinkQuery::get_links()
  209. *
  210. * @param array $params An array of search criteria.
  211. * @return array 'where_exprs' - an array of search expressions, 'join_instances' - whether joining the instance table is required.
  212. */
  213. function compile_search_params($params){
  214. global $wpdb; /** @var wpdb $wpdb */
  215. //Track whether we'll need to left-join the instance table to run the query.
  216. $join_instances = false;
  217. //Generate the individual clauses of the WHERE expression and store them in an array.
  218. $pieces = array();
  219. //Convert parser and container type lists to arrays of valid values
  220. $s_parser_type = array();
  221. if ( !empty($params['s_parser_type']) ){
  222. $s_parser_type = $params['s_parser_type'];
  223. if ( is_string($s_parser_type) ){
  224. $s_parser_type = preg_split('/[,\s]+/', $s_parser_type);
  225. }
  226. }
  227. $s_container_type = array();
  228. if ( !empty($params['s_container_type']) ){
  229. $s_container_type = $params['s_container_type'];
  230. if ( is_string($s_container_type) ){
  231. $s_container_type = preg_split('/[,\s]+/', $s_container_type);
  232. }
  233. }
  234. //Don't include links with instances that reference invalid (not currently loaded)
  235. //containers and parsers (unless specifically told to also include invalid links).
  236. if ( empty($params['include_invalid']) ){
  237. $join_instances = true;
  238. $module_manager = blcModuleManager::getInstance();
  239. $loaded_containers = array_keys($module_manager->get_active_by_category('container'));
  240. $loaded_parsers = array_keys($module_manager->get_active_by_category('parser'));
  241. if ( empty($s_parser_type) ){
  242. $s_parser_type = $loaded_parsers;
  243. } else {
  244. $s_parser_type = array_intersect($s_parser_type, $loaded_parsers);
  245. }
  246. if ( empty($s_container_type) ){
  247. $s_container_type = $loaded_containers;
  248. } else {
  249. $s_container_type = array_intersect($s_container_type, $loaded_containers);
  250. }
  251. }
  252. //Parser type should match the parser_type column in the instance table.
  253. if ( !empty($s_parser_type) ){
  254. $s_parser_type = array_map('trim', array_unique($s_parser_type));
  255. $s_parser_type = array_map(array(&$wpdb, 'escape'), $s_parser_type);
  256. if ( count($s_parser_type) == 1 ){
  257. $pieces[] = sprintf("instances.parser_type = '%s'", reset($s_parser_type));
  258. } else {
  259. $pieces[] = "instances.parser_type IN ('" . implode("', '", $s_parser_type) . "')";
  260. }
  261. $join_instances = true;
  262. }
  263. //Container type should match the container_type column in the instance table.
  264. if ( !empty($s_container_type) ){
  265. //Sanitize for use in SQL
  266. $s_container_type = array_map('trim', array_unique($s_container_type));
  267. $s_container_type = array_map(array(&$wpdb, 'escape'), $s_container_type);
  268. if ( count($s_container_type) == 1 ){
  269. $pieces[] = sprintf("instances.container_type = '%s'", reset($s_container_type));
  270. } else {
  271. $pieces[] = "instances.container_type IN ('" . implode("', '", $s_container_type) . "')";
  272. }
  273. $join_instances = true;
  274. }
  275. //A part of the WHERE expression can be specified explicitly
  276. if ( !empty($params['where_expr']) ){
  277. $pieces[] = $params['where_expr'];
  278. $join_instances = $join_instances || ( stripos($params['where_expr'], 'instances') !== false );
  279. }
  280. //List of allowed link ids (either an array or comma-separated)
  281. if ( !empty($params['link_ids']) ){
  282. $link_ids = $params['link_ids'];
  283. if ( is_string($link_ids) ){
  284. $link_ids = preg_split('/[,\s]+/', $link_ids);
  285. }
  286. //Only accept non-zero integers
  287. $sanitized_link_ids = array();
  288. foreach($link_ids as $id){
  289. $id = intval($id);
  290. if ( $id != 0 ){
  291. $sanitized_link_ids[] = $id;
  292. }
  293. }
  294. $pieces[] = 'links.link_id IN (' . implode(', ', $sanitized_link_ids) . ')';
  295. }
  296. //Anchor text - use LIKE search
  297. if ( !empty($params['s_link_text']) ){
  298. $s_link_text = like_escape($wpdb->escape($params['s_link_text']));
  299. $s_link_text = str_replace('*', '%', $s_link_text);
  300. $pieces[] = '(instances.link_text LIKE "%' . $s_link_text . '%")';
  301. $join_instances = true;
  302. }
  303. //URL - try to match both the initial URL and the final URL.
  304. //There is limited wildcard support, e.g. "google.*/search" will match both
  305. //"google.com/search" and "google.lv/search"
  306. if ( !empty($params['s_link_url']) ){
  307. $s_link_url = like_escape($wpdb->escape($params['s_link_url']));
  308. $s_link_url = str_replace('*', '%', $s_link_url);
  309. $pieces[] = '(links.url LIKE "%'. $s_link_url .'%") OR '.
  310. '(links.final_url LIKE "%'. $s_link_url .'%")';
  311. }
  312. //Container ID should match... you guessed it - container_id
  313. if ( !empty($params['s_container_id']) ){
  314. $s_container_id = intval($params['s_container_id']);
  315. if ( $s_container_id != 0 ){
  316. $pieces[] = "instances.container_id = $s_container_id";
  317. $join_instances = true;
  318. }
  319. }
  320. //Link type can match either the the parser_type or the container_type.
  321. if ( !empty($params['s_link_type']) ){
  322. $s_link_type = $wpdb->escape($params['s_link_type']);
  323. $pieces[] = "instances.parser_type = '$s_link_type' OR instances.container_type='$s_link_type'";
  324. $join_instances = true;
  325. }
  326. //HTTP code - the user can provide a list of HTTP response codes and code ranges.
  327. //Example : 201,400-410,500
  328. if ( !empty($params['s_http_code']) ){
  329. //Strip spaces.
  330. $params['s_http_code'] = str_replace(' ', '', $params['s_http_code']);
  331. //Split by comma
  332. $codes = explode(',', $params['s_http_code']);
  333. $individual_codes = array();
  334. $ranges = array();
  335. //Try to parse each response code or range. Invalid ones are simply ignored.
  336. foreach($codes as $code){
  337. if ( is_numeric($code) ){
  338. //It's a single number
  339. $individual_codes[] = abs(intval($code));
  340. } elseif ( strpos($code, '-') !== false ) {
  341. //Try to parse it as a range
  342. $range = explode( '-', $code, 2 );
  343. if ( (count($range) == 2) && is_numeric($range[0]) && is_numeric($range[0]) ){
  344. //Make sure the smaller code comes first
  345. $range = array( intval($range[0]), intval($range[1]) );
  346. $ranges[] = array( min($range), max($range) );
  347. }
  348. }
  349. }
  350. $piece = array();
  351. //All individual response codes get one "http_code IN (...)" clause
  352. if ( !empty($individual_codes) ){
  353. $piece[] = '(links.http_code IN ('. implode(', ', $individual_codes) .'))';
  354. }
  355. //Ranges get a "http_code BETWEEN min AND max" clause each
  356. if ( !empty($ranges) ){
  357. $range_strings = array();
  358. foreach($ranges as $range){
  359. $range_strings[] = "(links.http_code BETWEEN $range[0] AND $range[1])";
  360. }
  361. $piece[] = '( ' . implode(' OR ', $range_strings) . ' )';
  362. }
  363. //Finally, generate a composite WHERE clause for both types of response code queries
  364. if ( !empty($piece) ){
  365. $pieces[] = implode(' OR ', $piece);
  366. }
  367. }
  368. //Dismissed links are included by default, but can explicitly included
  369. //or filtered out by passing a special param.
  370. if ( isset($params['s_include_dismissed']) ) {
  371. $s_include_dismissed = !empty($params['s_include_dismissed']);
  372. $pieces['filter_dismissed'] = $s_include_dismissed ? '1' : '(dismissed = 0)';
  373. }
  374. //Optionally sorting is also possible
  375. $order_exprs = array();
  376. if ( !empty($params['orderby']) ) {
  377. $allowed_columns = array(
  378. 'url' => 'links.url',
  379. );
  380. $column = $params['orderby'];
  381. $direction = !empty($params['order']) ? strtolower($params['order']) : 'asc';
  382. if ( !in_array($direction, array('asc', 'desc')) ) {
  383. $direction = 'asc';
  384. }
  385. if ( array_key_exists($column, $allowed_columns) ) {
  386. $order_exprs[] = $allowed_columns[$column] . ' ' . $direction;
  387. }
  388. }
  389. //Custom filters can optionally call one of the native filters
  390. //to narrow down the result set.
  391. if ( !empty($params['s_filter']) && isset($this->native_filters[$params['s_filter']]) ){
  392. $the_filter = $this->native_filters[$params['s_filter']];
  393. $extra_criteria = $this->compile_search_params($the_filter['params']);
  394. $pieces = array_merge($extra_criteria['where_exprs'], $pieces);
  395. $join_instances = $join_instances || $extra_criteria['join_instances'];
  396. }
  397. return array(
  398. 'where_exprs' => $pieces,
  399. 'join_instances' => $join_instances,
  400. 'order_exprs' => $order_exprs,
  401. );
  402. }
  403. /**
  404. * blcLinkQuery::get_links()
  405. *
  406. * @see blc_get_links()
  407. *
  408. * @param array $params
  409. * @return array|int
  410. */
  411. function get_links($params = null){
  412. global $wpdb; /** @var wpdb $wpdb */
  413. if( !is_array($params) ){
  414. $params = array();
  415. }
  416. $defaults = array(
  417. 'offset' => 0,
  418. 'max_results' => 0,
  419. 'load_instances' => false,
  420. 'load_containers' => false,
  421. 'load_wrapped_objects' => false,
  422. 'count_only' => false,
  423. 'purpose' => '',
  424. 'include_invalid' => false,
  425. 'orderby' => '',
  426. 'order' => '',
  427. );
  428. $params = array_merge($defaults, $params);
  429. //Compile the search-related params into search expressions usable in a WHERE clause
  430. $criteria = $this->compile_search_params($params);
  431. //Build the WHERE clause
  432. if ( !empty($criteria['where_exprs']) ){
  433. $where_expr = "\t( " . implode(" ) AND\n\t( ", $criteria['where_exprs']) . ' ) ';
  434. } else {
  435. $where_expr = '1';
  436. }
  437. //Join the blc_instances table if it's required to perform the search.
  438. $joins = "";
  439. if ( $criteria['join_instances'] ){
  440. $joins = "JOIN {$wpdb->prefix}blc_instances AS instances ON links.link_id = instances.link_id";
  441. }
  442. //Optional sorting
  443. if ( !empty($criteria['order_exprs']) ) {
  444. $order_clause = 'ORDER BY ' . implode(', ', $criteria['order_exprs']);
  445. } else {
  446. $order_clause = '';
  447. }
  448. if ( $params['count_only'] ){
  449. //Only get the number of matching links.
  450. $q = "
  451. SELECT COUNT(*)
  452. FROM (
  453. SELECT 0
  454. FROM
  455. {$wpdb->prefix}blc_links AS links
  456. $joins
  457. WHERE
  458. $where_expr
  459. GROUP BY links.link_id) AS foo";
  460. return $wpdb->get_var($q);
  461. }
  462. //Select the required links.
  463. $q = "SELECT
  464. links.*
  465. FROM
  466. {$wpdb->prefix}blc_links AS links
  467. $joins
  468. WHERE
  469. $where_expr
  470. GROUP BY links.link_id
  471. {$order_clause}"; //Note: would be a lot faster without GROUP BY
  472. //Add the LIMIT clause
  473. if ( $params['max_results'] || $params['offset'] ){
  474. $q .= sprintf("\nLIMIT %d, %d", $params['offset'], $params['max_results']);
  475. }
  476. $results = $wpdb->get_results($q, ARRAY_A);
  477. if ( empty($results) ){
  478. return array();
  479. }
  480. //Create the link objects
  481. $links = array();
  482. foreach($results as $result){
  483. $link = new blcLink($result);
  484. $links[$link->link_id] = $link;
  485. }
  486. $purpose = $params['purpose'];
  487. /*
  488. Preload instances if :
  489. * It has been requested via the 'load_instances' argument.
  490. * The links are going to be displayed or edited, which involves instances.
  491. */
  492. $load_instances = $params['load_instances'] || in_array($purpose, array(BLC_FOR_DISPLAY, BLC_FOR_EDITING));
  493. if ( $load_instances ){
  494. $link_ids = array_keys($links);
  495. $all_instances = blc_get_instances($link_ids, $purpose, $params['load_containers'], $params['load_wrapped_objects']);
  496. //Assign each batch of instances to the right link
  497. foreach($all_instances as $link_id => $instances){
  498. foreach($instances as $instance) { /** @var blcLinkInstance $instance */
  499. $instance->_link = $links[$link_id];
  500. }
  501. $links[$link_id]->_instances = $instances;
  502. }
  503. }
  504. return $links;
  505. }
  506. /**
  507. * Calculate the number of results for all known filters
  508. *
  509. * @return void
  510. */
  511. function count_filter_results(){
  512. foreach($this->native_filters as $filter_id => $filter){
  513. $this->native_filters[$filter_id]['count'] = $this->get_filter_links(
  514. $filter, array('count_only' => true)
  515. );
  516. }
  517. foreach($this->custom_filters as $filter_id => $filter){
  518. $this->custom_filters[$filter_id]['count'] = $this->get_filter_links(
  519. $filter, array('count_only' => true)
  520. );
  521. }
  522. $this->search_filter['count'] = $this->get_filter_links($this->search_filter, array('count_only' => true));
  523. }
  524. /**
  525. * Retrieve a list of links matching a filter.
  526. *
  527. * @uses blcLinkQuery::get_links()
  528. *
  529. * @param string|array $filter Either a filter ID or an array containing filter data.
  530. * @param array $extra_params Optional extra criteria that will override those set by the filter. See blc_get_links() for details.
  531. * @return array|int Either an array of blcLink objects, or an integer indicating the number of links that match the filter.
  532. */
  533. function get_filter_links($filter, $extra_params = null){
  534. if ( is_string($filter) ){
  535. $filter = $this->get_filter($filter);
  536. }
  537. $params = $this->get_search_params($filter);
  538. if ( !empty($extra_params) ){
  539. $params = array_merge($params, $extra_params);
  540. }
  541. return $this->get_links($params);
  542. }
  543. /**
  544. * Print a menu of available filters, both native and user-created.
  545. *
  546. * @param string $current Current filter ID.
  547. * @return void
  548. */
  549. function print_filter_menu($current = ''){
  550. $filters = $this->get_filters();
  551. echo '<ul class="subsubsub">';
  552. //Construct a submenu of filter types
  553. $items = array();
  554. foreach ($filters as $filter => $data){
  555. if ( !empty($data['hidden']) ) continue; //skip hidden filters
  556. $class = $number_class = '';
  557. if ( $current == $filter ) {
  558. $class = 'class="current"';
  559. $number_class = 'current-link-count';
  560. }
  561. $items[] = "<li><a href='tools.php?page=view-broken-links&filter_id=$filter' {$class}>
  562. {$data['name']}</a> <span class='count'>(<span class='$number_class'>{$data['count']}</span>)</span>";
  563. }
  564. echo implode(' |</li>', $items);
  565. echo '</ul>';
  566. }
  567. /**
  568. * Print the appropriate heading for the given filter.
  569. *
  570. * @param array $current_filter
  571. * @return void
  572. */
  573. function print_filter_heading($current_filter){
  574. echo '<h2>';
  575. //Output a header matching the current filter
  576. if ( $current_filter['count'] > 0 ){
  577. echo $current_filter['heading'] . " (<span class='current-link-count'>{$current_filter['count']}</span>)";
  578. } else {
  579. echo $current_filter['heading_zero'] . "<span class='current-link-count'></span>";
  580. }
  581. echo '</h2>';
  582. }
  583. /**
  584. * Execute a filter.
  585. *
  586. * Gathers paging and search parameters from $_GET and executes the specified filter.
  587. * The returned array contains standard filter data plus several additional fields :
  588. * 'filter_id' - Which filter was used. May differ from the specified $filter_id due to fallback settings.
  589. * 'per_page' - How many results per page the method tried to retrieve.
  590. * 'page' - Which page of results was retrieved.
  591. * 'max_pages' - The total number of results pages, calculated using the above 'per_page' value.
  592. * 'links' - An array of retrieved links (blcLink objects).
  593. * 'search_params' - An associative array of the current search parameters as extracted either from the current URL or the filter itself.
  594. * 'is_broken_filter' - TRUE if the filter was set to retrieve only broken links, FALSE otherwise.
  595. *
  596. * @param string $filter_id Filter ID.
  597. * @param int $page Optional. Which page of results to retrieve. Defaults to returning the first page of results.
  598. * @param int $per_page Optional. The number of results per page. Defaults to 30.
  599. * @param string $fallback Optional. Which filter to use if none match the specified $filter_id. Defaults to the native broken link filter.
  600. * @param string $orderby Optional. Sort results by this column.
  601. * @param string $order Optional. Sort direction ('asc' or 'desc').
  602. * @return array Associative array of filter data and the results of its execution.
  603. */
  604. function exec_filter($filter_id, $page = 1, $per_page = 30, $fallback = 'broken', $orderby = '', $order = 'asc'){
  605. //Get the selected filter (defaults to displaying broken links)
  606. $current_filter = $this->get_filter($filter_id);
  607. if ( empty($current_filter) ){
  608. $current_filter = $this->get_filter($fallback);
  609. $filter_id = $fallback;
  610. }
  611. //Page number must be > 0
  612. if ($page < 1) $page = 1;
  613. //Links per page [1 - 500]
  614. if ($per_page < 1){
  615. $per_page = 30;
  616. } else if ($per_page > 500){
  617. $per_page = 500;
  618. }
  619. //Calculate the maximum number of pages.
  620. $max_pages = ceil($current_filter['count'] / $per_page);
  621. //Select the required links
  622. $extra_params = array(
  623. 'offset' => ( ($page-1) * $per_page ),
  624. 'max_results' => $per_page,
  625. 'purpose' => BLC_FOR_DISPLAY,
  626. 'orderby' => $orderby,
  627. 'order' => $order,
  628. );
  629. $links = $this->get_filter_links($current_filter, $extra_params);
  630. //If the current request is a user-initiated search query (either directly or
  631. //via a custom filter), save the search params. They can later be used to pre-fill
  632. //the search form or build a new/modified custom filter.
  633. $search_params = array();
  634. if ( !empty($current_filter['custom']) || ($filter_id == 'search') ){
  635. $search_params = $this->get_search_params($current_filter);
  636. }
  637. $base_filter = '';
  638. if ( array_key_exists($filter_id, $this->native_filters) ) {
  639. $base_filter = $filter_id;
  640. } else if ( isset($current_filter['params']['s_filter']) && !empty($current_filter['params']['s_filter']) ) {
  641. $base_filter = $current_filter['params']['s_filter'];
  642. } else if ( isset($_GET['s_filter']) && !empty($_GET['s_filter']) ) {
  643. $base_filter = $_GET['s_filter'];
  644. }
  645. $is_broken_filter = ($base_filter == 'broken');
  646. //Save the effective filter data in the filter array.
  647. //It can be used later to print the link table.
  648. $current_filter = array_merge(array(
  649. 'filter_id' => $filter_id,
  650. 'page' => $page,
  651. 'per_page' => $per_page,
  652. 'max_pages' => $max_pages,
  653. 'links' => $links,
  654. 'search_params' => $search_params,
  655. 'is_broken_filter' => $is_broken_filter,
  656. 'base_filter' => $base_filter,
  657. ), $current_filter);
  658. return $current_filter;
  659. }
  660. }
  661. /**
  662. * Retrieve a list of links matching some criteria.
  663. *
  664. * The function argument should be an associative array describing the criteria.
  665. * The supported keys are :
  666. * 'offset' - Skip the first X results. Default is 0.
  667. * 'max_results' - The maximum number of links to return. Defaults to returning all results.
  668. * 'link_ids' - Retrieve only links with these IDs. This should either be a comma-separated list or an array.
  669. * 's_link_text' - Link text must match this keyphrase (performs a fulltext search).
  670. * 's_link_url' - Link URL must contain this string. You can use "*" as a wildcard.
  671. * 's_parser_type' - Filter links by the type of link parser that was used to find them.
  672. * 's_container_type' - Filter links by where they were found, e.g. 'post'.
  673. * 's_container_id' - Find links that belong to a container with this ID (should be used together with s_container_type).
  674. * 's_link_type' - Either parser type or container type must match this.
  675. * 's_http_code' - Filter by HTTP code. Example : 201,400-410,500
  676. * 's_filter' - Use a built-in filter. Available filters : 'broken', 'redirects', 'all'
  677. * 'where_expr' - Advanced. Lets you directly specify a part of the WHERE clause.
  678. * 'load_instances' - Pre-load all link instance data for each link. Default is false.
  679. * 'load_containers' - Pre-load container data for each instance. Default is false.
  680. * 'load_wrapped_objects' - Pre-load wrapped object data (e.g. posts, comments, etc) for each container. Default is false.
  681. * 'count_only' - Only return the number of results (int), not the whole result set. 'offset' and 'max_results' will be ignored if this is set. Default is false.
  682. * 'purpose' - An optional code indicating how the links will be used.
  683. * 'include_invalid' - Include links that have no instances and links that only have instances that reference not-loaded containers or parsers. Defaults to false.
  684. *
  685. * All keys are optional.
  686. *
  687. * @uses blcLinkQuery::get_links();
  688. *
  689. * @param array $params
  690. * @return int|blcLink[] Either an array of blcLink objects, or the number of results for the query.
  691. */
  692. function blc_get_links($params = null){
  693. $instance = blcLinkQuery::getInstance();
  694. return $instance->get_links($params);
  695. }
  696. ?>