BBoss_Global_Search_Helper

BuddyPress Global Search Plugin Main Controller **************************************.

Defined (1)

The class is defined in the following location(s).

/includes/class.BBoss_Global_Search_Helper.php  
  1. class BBoss_Global_Search_Helper { 
  2. /** 
  3. * The variable to hold the helper class objects for each type of searches. 
  4. * E.g  
  5. * [ 
  6. * 'posts' => an object of BBoss_Global_Search_Posts,  
  7. * 'groups' => an object of BBoss_Global_Search_Groups 
  8. * ] 
  9. * @var array  
  10. * @since 1.0.0 
  11. */ 
  12. private $search_helpers = array(); 
  13.  
  14. /** 
  15. * The variable to hold arguments used for search. 
  16. * It will be used by other methods later on. 
  17. * @var array  
  18. */ 
  19. private $search_args = array(); 
  20.  
  21. /** 
  22. * The variable to hold search results. 
  23. * The results will be grouped into different types(e.g: posts, members, etc..) 
  24. *  
  25. * The structure of array after being populated should be: 
  26. * 'posts' => [ 
  27. * 'total_match_count' => 34,  
  28. * 'items' =>  
  29. * ],  
  30. * 'members' => 
  31. * @var array 
  32. */ 
  33. private $search_results = array(); 
  34.  
  35. /** 
  36. * Insures that only one instance of Class exists in memory at any 
  37. * one time. Also prevents needing to define globals all over the place. 
  38. * @since 1.0.0 
  39. * @see buddyboss_global_search() 
  40. * @return object BuddyBoss_Global_Search_Plugin 
  41. */ 
  42. public static function instance() { 
  43. // Store the instance locally to avoid private static replication 
  44. static $instance = null; 
  45.  
  46. // Only run these methods if they haven't been run previously 
  47. if (null === $instance) { 
  48. $instance = new BBoss_Global_Search_Helper(); 
  49.  
  50. //create instances of helpers and associate them with types 
  51. add_action( 'init', array( $instance, 'load_search_helpers' ), 80 ); 
  52.  
  53. add_action( 'wp_ajax_bboss_global_search_ajax', array( $instance, 'ajax_search' ) ); 
  54. add_action( 'wp_ajax_nopriv_bboss_global_search_ajax', array( $instance, 'ajax_search' ) ); 
  55.  
  56. // Always return the instance 
  57. return $instance; 
  58.  
  59. /** Magic Methods 
  60. * =================================================================== 
  61. */ 
  62.  
  63. /** 
  64. * A dummy constructor to prevent this class from being loaded more than once. 
  65. * @since 1.0.0 
  66. */ 
  67. private function __construct() { /** Do nothing here */ 
  68.  
  69. /** 
  70. * A dummy magic method to prevent this class from being cloned. 
  71. * @since 1.0.0 
  72. */ 
  73. public function __clone() { 
  74. _doing_it_wrong(__FUNCTION__, __('Cheatin’ huh?', 'buddypress-global-search'), '1.7'); 
  75.  
  76. /** 
  77. * A dummy magic method to prevent this class being unserialized. 
  78. * @since 1.0.0 
  79. */ 
  80. public function __wakeup() { 
  81. _doing_it_wrong(__FUNCTION__, __('Cheatin’ huh?', 'buddypress-global-search'), '1.7'); 
  82.  
  83. /** 
  84. *  
  85. */ 
  86. public function load_search_helpers() { 
  87. $searchable_types = buddyboss_global_search()->option('items-to-search'); 
  88.  
  89. if( !empty( $searchable_types ) ) { 
  90. //load the helper type parent class 
  91. require_once( BUDDYBOSS_GLOBAL_SEARCH_PLUGIN_DIR . 'includes/search-types/class.BBoss_Global_Search_Type.php' ); 
  92.  
  93. //load and associate helpers one by one 
  94. if( in_array( 'posts', $searchable_types ) ) { 
  95. require_once( BUDDYBOSS_GLOBAL_SEARCH_PLUGIN_DIR . 'includes/search-types/class.BBoss_Global_Search_Posts.php' ); 
  96. $this->search_helpers['posts'] = BBoss_Global_Search_Posts::instance(); 
  97.  
  98. if ( in_array( 'posts_comments', $searchable_types ) ) { 
  99. require_once( BUDDYBOSS_GLOBAL_SEARCH_PLUGIN_DIR . 'includes/search-types/class.BBoss_Global_Search_Posts_Comments.php' ); 
  100. $this->search_helpers['posts_comments'] = BBoss_Global_Search_Posts_Comments::instance(); 
  101.  
  102. if ( is_plugin_active( 'bbpress/bbpress.php' ) ) { 
  103.  
  104. require_once( BUDDYBOSS_GLOBAL_SEARCH_PLUGIN_DIR . 'includes/abstracts/class.BBoss_Global_Search_bbPress.php' ); 
  105.  
  106. if ( in_array( 'forum', $searchable_types ) ) { 
  107. require_once( BUDDYBOSS_GLOBAL_SEARCH_PLUGIN_DIR . 'includes/search-types/class.BBoss_Global_Search_bbPress_Forums.php' ); 
  108. $this->search_helpers['forum'] = BBoss_Global_Search_bbPress_Forums::instance(); 
  109.  
  110. if ( in_array( 'topic', $searchable_types ) ) { 
  111. require_once( BUDDYBOSS_GLOBAL_SEARCH_PLUGIN_DIR . 'includes/search-types/class.BBoss_Global_Search_bbPress_Topics.php' ); 
  112. $this->search_helpers['topic'] = BBoss_Global_Search_bbPress_Topics::instance(); 
  113.  
  114.  
  115. if ( in_array( 'reply', $searchable_types ) ) { 
  116. require_once( BUDDYBOSS_GLOBAL_SEARCH_PLUGIN_DIR . 'includes/search-types/class.BBoss_Global_Search_bbPress_Replies.php' ); 
  117. $this->search_helpers['reply'] = BBoss_Global_Search_bbPress_Replies::instance(); 
  118.  
  119. //Check BuddyPress is active 
  120. if ( function_exists( 'bp_is_active' ) ) { 
  121.  
  122. if ( in_array( 'members', $searchable_types ) ) { 
  123. require_once( BUDDYBOSS_GLOBAL_SEARCH_PLUGIN_DIR . 'includes/search-types/class.BBoss_Global_Search_Members.php' ); 
  124. $this->search_helpers['members'] = BBoss_Global_Search_Members::instance(); 
  125.  
  126. if ( bp_is_active( 'groups' ) && in_array( 'groups', $searchable_types ) ) { 
  127. require_once( BUDDYBOSS_GLOBAL_SEARCH_PLUGIN_DIR . 'includes/search-types/class.BBoss_Global_Search_Groups.php' ); 
  128. $this->search_helpers['groups'] = BBoss_Global_Search_Groups::instance(); 
  129.  
  130. if ( bp_is_active( 'activity' ) && in_array( 'activity', $searchable_types ) ) { 
  131. require_once( BUDDYBOSS_GLOBAL_SEARCH_PLUGIN_DIR . 'includes/search-types/class.BBoss_Global_Search_Activities.php' ); 
  132. $this->search_helpers['activity'] = BBoss_Global_Search_Activities::instance(); 
  133.  
  134. if ( in_array( 'activity_comment', $searchable_types ) ) { 
  135. require_once( BUDDYBOSS_GLOBAL_SEARCH_PLUGIN_DIR . 'includes/search-types/class.BBoss_Global_Search_Activity_Comments.php' ); 
  136. $this->search_helpers['activity_comment'] = BBoss_Global_Search_Activity_Comment::instance(); 
  137.  
  138.  
  139. if ( bp_is_active( 'messages' ) && in_array( 'messages', $searchable_types ) ) { 
  140. require_once( BUDDYBOSS_GLOBAL_SEARCH_PLUGIN_DIR . 'includes/search-types/class.BBoss_Global_Search_Messages.php' ); 
  141. $this->search_helpers['messages'] = BBoss_Global_Search_Messages::instance(); 
  142.  
  143. /** 
  144. * Hook to load helper classes for additional search types. 
  145. */ 
  146. $additional_search_helpers = apply_filters( 'bboss_global_search_additional_search_helpers', array() ); 
  147. if( !empty( $additional_search_helpers ) ) { 
  148. foreach( $additional_search_helpers as $search_type=>$helper_object ) { 
  149. /** 
  150. * All helper classes must inherit from BBoss_Global_Search_Type 
  151. */ 
  152. if( !isset( $this->search_helpers[$search_type] ) && is_a( $helper_object, 'BBoss_Global_Search_Type' ) ) { 
  153. $this->search_helpers[$search_type] = $helper_object; 
  154.  
  155. public function ajax_search() { 
  156. check_ajax_referer( 'bboss_global_search_ajax', 'nonce' ); 
  157.  
  158. if( isset($_POST["view"]) && $_POST["view"] == "content") { 
  159.  
  160. $_GET["s"] = $_POST["s"]; 
  161. if(!empty($_POST["subset"])) { 
  162. $_GET["subset"] = $_POST["subset"]; 
  163.  
  164. if(!empty($_POST["list"])) { 
  165. $_GET["list"] = $_POST["list"]; 
  166.  
  167. $content = ""; 
  168.  
  169. buddyboss_global_search()->search->prepare_search_page(); 
  170. $content = buddyboss_global_search_buffer_template_part( 'results-page-content', '', false ); 
  171.  
  172. echo $content; 
  173.  
  174. die(); 
  175.  
  176. $args = array( 
  177. 'search_term' => esc_attr( $_REQUEST['search_term'] ),  
  178. //How many results should be displyed in autosuggest? 
  179. //@todo: give a settings field for this value 
  180. 'per_page' => $_REQUEST['per_page'] ,  
  181. 'count_total' => false,  
  182. 'template_type' => 'ajax',  
  183. ); 
  184.  
  185. if( isset( $_REQUEST['forum_search_term']) ) { 
  186. $args['forum_search'] = true; 
  187.  
  188. $this->do_search( $args ); 
  189.  
  190. $search_results = array(); 
  191. if( isset( $this->search_results['all']['items'] ) && !empty( $this->search_results['all']['items'] ) ) { 
  192. /** ++++++++++++++++++++++++++++++++++ 
  193. group items of same type together 
  194. ++++++++++++++++++++++++++++++++++ */ 
  195. $types = array(); 
  196. foreach( $this->search_results['all']['items'] as $item_id=>$item ) { 
  197. $type = $item['type']; 
  198. if( empty( $types ) || !in_array( $type, $types ) ) { 
  199. $types[] = $type; 
  200.  
  201. $new_items = array(); 
  202. foreach( $types as $type ) { 
  203. $first_html_changed = false; 
  204. foreach( $this->search_results['all']['items'] as $item_id=>$item ) { 
  205. if( $item['type']!= $type ) 
  206. continue; 
  207.  
  208. //add group/type title in first one 
  209. /** 
  210. if( !$first_html_changed ) { 
  211. //this filter can be used to change display of 'posts' to 'Blog Posts' etc.. 
  212. $label = apply_filters( 'bboss_global_search_label_search_type', $type ); 
  213.  
  214. //$item['html'] = "<div class='results-group results-group-{$type}'><span class='results-group-title'>{$label}</span></div>" . $item['html']; 
  215. $first_html_changed = true; 
  216.   
  217. */  
  218.  
  219. $new_items[$item_id] = $item; 
  220.  
  221. $this->search_results['all']['items'] = $new_items; 
  222.  
  223. /** _______________________________ */ 
  224. $url = $this->search_page_search_url(); 
  225. $url = esc_url(add_query_arg( array( 'no_frame' => '1' ), $url )); 
  226. $type_mem = ""; 
  227. foreach( $this->search_results['all']['items'] as $item_id=>$item ) { 
  228. $new_row = array( 'value'=>$item['html'] ); 
  229. $type_label = apply_filters( 'bboss_global_search_label_search_type', $item['type'] ); 
  230. $new_row['type'] = $item['type']; 
  231. $new_row['type_label'] = ""; 
  232. $new_row['value'] = $item['html']; 
  233. if( isset( $item['title'] ) ) { 
  234. $new_row['label'] = $item['title']; 
  235.  
  236. if($type_mem != $new_row['type']) { 
  237. $type_mem = $new_row['type']; 
  238. $cat_row = $new_row; 
  239. $cat_row["type"] = $item['type']; 
  240. $cat_row['type_label'] = $type_label; 
  241. $category_search_url = esc_url(add_query_arg( array( 'subset' => $item['type'] ), $url )); 
  242. $html = "<span><a href='" . esc_url( $category_search_url ) . "'>" . $type_label . "</a></span>"; 
  243. $cat_row["value"] = apply_filters('buddypress_gs_autocomplete_category', $html, $item['type'], $url, $type_label); 
  244. $search_results[] = $cat_row; 
  245.  
  246. $search_results[] = $new_row; 
  247.  
  248. $all_results_row = array( 
  249. "value" => "<div class='bboss_ajax_search_item allresults'><a href='" . esc_url( $url ) . "'>" . sprintf( __( "View all results for '%s'", "buddypress-global-search" ), $this->search_args['search_term'] ) . "</a></div>",  
  250. "type" => 'view_all_type',  
  251. "type_label" => '' 
  252. ); 
  253. $search_results[] = $all_results_row; 
  254. } else { 
  255. //@todo give a settings screen for this field 
  256. $search_results[] = array( 
  257. 'value' => '<div class="bboss_ajax_search_item noresult">' . sprintf( __( "Nothing found for '%s'", "buddypress-global-search" ), $this->search_args['search_term'] ) . '</div>',  
  258. 'label' => $this->search_args['search_term'] 
  259. ); 
  260.  
  261. die( json_encode( $search_results ) ); 
  262.  
  263. /** 
  264. * Perform search and generate search results 
  265. * @param mixed $args 
  266. * @since 1.0.0 
  267. */ 
  268. public function do_search( $args='' ) { 
  269. global $wpdb; 
  270.  
  271. $args = $this->sanitize_args( $args ); 
  272.  
  273. $defaults = array( 
  274. //the search term 
  275. 'search_term' => '',  
  276. //Restrict search results to only this subset. eg: posts, members, groups, etc. 
  277. //See Setting > what to search? 
  278. 'search_subset' => 'all', // 
  279. //What all to search for. e.g: members. 
  280. //See Setting > what to search? 
  281. //The options passed here must be a subset of all options available on Setting > what to search, nothing extra can be passed here. 
  282. // 
  283. //This is different from search_subset. 
  284. //If search_subset is 'all', then search is performed for all searchable items. 
  285. //If search_subset is 'members' then only total match count for other searchable_items is calculated( so that it can be displayed in tabs) 
  286. //members(23) | posts(201) | groups(2) and so on. 
  287. 'searchable_items' => buddyboss_global_search()->option('items-to-search'),  
  288. //how many search results to display per page 
  289. 'per_page' => 10,  
  290. //current page 
  291. 'current_page' => 1,  
  292. //should we calculate total match count for all different types? 
  293. //it should be set to false while calling this function for ajax search 
  294. 'count_total' => true,  
  295. //template type to load for each item 
  296. //search results will be styled differently(minimal) while in ajax search 
  297. //options ''|'minimal'  
  298. 'template_type' => '',  
  299. 'forum_search' => false,  
  300. ); 
  301.  
  302. $args = wp_parse_args( $args, $defaults ); 
  303.  
  304. if ( true === $args['forum_search'] ) { 
  305. $args['searchable_items'] = array( 'forum', 'topic', 'reply' ); 
  306.  
  307. $this->search_args = $args;//save it for using in other methods 
  308.  
  309. //bail out if nothing to search for 
  310. if( !$args['search_term'] ) 
  311. return; 
  312.  
  313. if( 'all' == $args['search_subset'] ) { 
  314.  
  315. /** 
  316. * 1. Generate a 'UNION' sql query for all searchable items with only ID, RANK, TYPE(posts|members|..) as columns, order by RANK DESC. 
  317. * 3. Generate html for each of them 
  318. */ 
  319. /** an example UNION query :-  
  320. ----------------------------------------------------- 
  321. SELECT  
  322. wp_posts.id , 'posts' as type, wp_posts.post_title LIKE '%ho%' AS relevance, wp_posts.post_date as entry_date  
  323. FROM  
  324. wp_posts  
  325. WHERE  
  326. 1=1  
  327. AND ( 
  328. (wp_posts.post_title LIKE '%ho%')  
  329. OR (wp_posts.post_content LIKE '%ho%') 
  330. )  
  331. AND wp_posts.post_type IN ('post', 'page', 'attachment')  
  332. AND ( 
  333. wp_posts.post_status = 'publish'  
  334. OR wp_posts.post_author = 1  
  335. AND wp_posts.post_status = 'private' 
  336. )  
  337. UNION 
  338. SELECT  
  339. DISTINCT g.id, 'groups' as type, g.name LIKE '%ho%' AS relevance, gm2.meta_value as entry_date  
  340. FROM  
  341. wp_bp_groups_groupmeta gm1, wp_bp_groups_groupmeta gm2, wp_bp_groups g  
  342. WHERE  
  343. 1=1  
  344. AND g.id = gm1.group_id  
  345. AND g.id = gm2.group_id  
  346. AND gm2.meta_key = 'last_activity'  
  347. AND gm1.meta_key = 'total_member_count'  
  348. AND ( g.name LIKE '%ho%' OR g.description LIKE '%ho%' ) 
  349.   
  350. ORDER BY  
  351. relevance DESC, entry_date DESC LIMIT 0, 10 
  352. ---------------------------------------------------- 
  353. */ 
  354.  
  355. $sql_queries = array(); 
  356. foreach( $args['searchable_items'] as $search_type ) { 
  357. if( !isset($this->search_helpers[$search_type])) 
  358. continue; 
  359.  
  360. /** 
  361. * the following variable will be an object of current search type helper class 
  362. * e.g: an object of BBoss_Global_Search_Groups or BBoss_Global_Search_Posts etc. 
  363. * so we can safely call the public methods defined in those classes. 
  364. * This also means that all such classes must have a common set of methods. 
  365. */ 
  366. $obj = $this->search_helpers[$search_type]; 
  367. $sql_queries[] = "( " . $obj->union_sql( $args['search_term'] ) . " ) "; 
  368.  
  369. if( empty( $sql_queries ) ) { 
  370. //thigs will get messy if program reaches here!! 
  371. return; 
  372.  
  373. $pre_search_query = implode( ' UNION ', $sql_queries) . " ORDER BY relevance, type DESC, entry_date DESC "; 
  374.  
  375. if( $args['per_page']> 0 ) { 
  376. $offset = ( $args['current_page'] * $args['per_page'] ) - $args['per_page']; 
  377. $pre_search_query .= " LIMIT {$offset}, {$args['per_page']} "; 
  378.  
  379.  
  380. $results = $wpdb->get_results( $pre_search_query ); 
  381. /** $results will have a structure like below */ 
  382. /** 
  383. id | type | relevance | entry_date 
  384. 45 | groups | 1 | 2014-10-28 17:05:18 
  385. 40 | posts | 1 | 2014-10-26 13:52:06 
  386. 4 | groups | 0 | 2014-10-21 15:15:36 
  387. */ 
  388. if( !empty( $results ) ) { 
  389. $this->search_results['all'] = array( 'total_match_count' => 0, 'items' => array(), 'items_title'=> array() ); 
  390. //segregate items of a type together and pass it to corresponsing search handler, so that an aggregate query can be done 
  391. //e.g one single wordpress loop can be done for all posts 
  392.  
  393. foreach( $results as $item ) { 
  394. $obj = $this->search_helpers[$item->type]; 
  395. $obj->add_search_item( $item->id ); 
  396.  
  397. //now get html for each item 
  398. foreach( $results as $item ) { 
  399.  
  400. $obj = $this->search_helpers[$item->type]; 
  401.  
  402. $result = array( 
  403. 'id' => $item->id,  
  404. 'type' => $item->type,  
  405. 'html' => $obj->get_html( $item->id, $args['template_type'] ),  
  406. 'title' => $obj->get_title( $item->id ) 
  407. ); 
  408.  
  409. $this->search_results['all']['items'][$item->id] = $result; 
  410. //now we've html saved for search results 
  411.  
  412. if( !empty( $this->search_results['all']['items'] ) && $args['template_type']!='ajax' ) { 
  413. /** ++++++++++++++++++++++++++++++++++ 
  414. group items of same type together 
  415. ++++++++++++++++++++++++++++++++++ */ 
  416. //create another copy, of items, this time, items of same type grouped together 
  417. $ordered_items_group = array(); 
  418. foreach( $this->search_results['all']['items'] as $item_id=>$item ) { 
  419. $type = $item['type']; 
  420. if( !isset( $ordered_items_group[$type] ) ) { 
  421. $ordered_items_group[$type] = array(); 
  422.  
  423. $ordered_items_group[$type][$item_id] = $item; 
  424.  
  425. $search_items = buddyboos_global_search_items(); 
  426.  
  427. foreach( $ordered_items_group as $type=>&$items ) { 
  428. //now prepend html (opening tags) to first item of each 
  429.  
  430. $label = isset( $search_items[$type] ) ? $search_items[$type] : $type; 
  431. $first_item = reset($items); 
  432. $start_html = "<div class='results-group results-group-{$type} ". apply_filters( 'bboss_global_search_class_search_wrap', 'bboss-results-wrap', $label ) ."'>" 
  433. . "<h2 class='results-group-title'><span>" . apply_filters( 'bboss_global_search_label_search_type', $label ) . "</span></h2>" 
  434. . "<ul id='{$type}-stream' class='item-list {$type}-list ". apply_filters( 'bboss_global_search_class_search_list', 'bboss-results-list', $label ) ."'>"; 
  435.  
  436. $group_start_html = apply_filters( "bboss_global_search_results_group_start_html", $start_html, $type ); 
  437.  
  438. $first_item['html'] = $group_start_html . $first_item['html']; 
  439. $items[$first_item['id']] = $first_item; 
  440.  
  441. //and append html (closing tags) to last item of each type 
  442. $last_item = end($items); 
  443. $end_html = "</ul></div>"; 
  444.  
  445. $group_end_html = apply_filters( "bboss_global_search_results_group_end_html", $end_html, $type ); 
  446.  
  447. $last_item['html'] = $last_item['html'] . $group_end_html; 
  448. $items[$last_item['id']] = $last_item; 
  449.  
  450. //replace orginal items with this new, grouped set of items 
  451. $this->search_results['all']['items'] = array(); 
  452. foreach( $ordered_items_group as $type=>$grouped_items ) { 
  453. foreach( $grouped_items as $item_id=>$item ) { 
  454. $this->search_results['all']['items'][$item_id] = $item; 
  455. /** ________________________________ */ 
  456. } else { 
  457. //if subset not in searchable items, bail out. 
  458. if( !in_array( $args['search_subset'], $args['searchable_items'] ) ) 
  459. return; 
  460.  
  461. if( !isset($this->search_helpers[$args['search_subset']])) 
  462. return; 
  463.  
  464. /** 
  465. * 1. Search top top 20( $args['per_page'] ) item( posts|members|..) 
  466. * 2. Generate html for each of them 
  467. */ 
  468.  
  469. $obj = $this->search_helpers[$args['search_subset']]; 
  470. $pre_search_query = $obj->union_sql( $args['search_term'] ) . " ORDER BY relevance DESC, entry_date DESC "; 
  471.  
  472. if( $args['per_page']> 0 ) { 
  473. $offset = ( $args['current_page'] * $args['per_page'] ) - $args['per_page']; 
  474. $pre_search_query .= " LIMIT {$offset}, {$args['per_page']} "; 
  475.  
  476. $results = $wpdb->get_results( $pre_search_query ); 
  477.  
  478. /** $results will have a structure like below */ 
  479. /** 
  480. id | type | relevance | entry_date 
  481. 45 | groups | 1 | 2014-10-28 17:05:18 
  482. 40 | posts | 1 | 2014-10-26 13:52:06 
  483. 4 | groups | 0 | 2014-10-21 15:15:36 
  484. */ 
  485. if( !empty( $results ) ) { 
  486. $obj = $this->search_helpers[$args['search_subset']]; 
  487. $this->search_results[$args['search_subset']] = array( 'total_match_count' => 0, 'items' => array() ); 
  488. //segregate items of a type together and pass it to corresponsing search handler, so that an aggregate query can be done 
  489. //e.g one single wordpress loop can be done for all posts 
  490. foreach( $results as $item ) { 
  491. $obj->add_search_item( $item->id ); 
  492.  
  493. //now get html for each item 
  494. foreach( $results as $item ) { 
  495. $html = $obj->get_html( $item->id, $args['template_type'] ); 
  496.  
  497. $result = array( 
  498. 'id' => $item->id,  
  499. 'type' => $args['search_subset'],  
  500. 'html' => $obj->get_html( $item->id, $args['template_type'] ),  
  501. 'title' => $obj->get_title( $item->id ),  
  502. ); 
  503.  
  504. $this->search_results[$args['search_subset']]['items'][$item->id] = $result; 
  505.  
  506. //now prepend html (opening tags) to first item of each type 
  507. $first_item = reset($this->search_results[$args['search_subset']]['items']); 
  508. $start_html = "<div class='results-group results-group-{$args['search_subset']} ". apply_filters( 'bboss_global_search_class_search_wrap', 'bboss-results-wrap', $args['search_subset'] ) ."'>" 
  509. . "<ul id='{$args['search_subset']}-stream' class='item-list {$args['search_subset']}-list ". apply_filters( 'bboss_global_search_class_search_list', 'bboss-results-list', $args['search_subset'] ) ."'>"; 
  510.  
  511. $group_start_html = apply_filters( "bboss_global_search_results_group_start_html", $start_html, $args['search_subset'] ); 
  512.  
  513. $first_item['html'] = $group_start_html . $first_item['html']; 
  514. $this->search_results[$args['search_subset']]['items'][$first_item['id']] = $first_item; 
  515.  
  516. //and append html (closing tags) to last item of each type 
  517. $last_item = end($this->search_results[$args['search_subset']]['items']); 
  518. $end_html = "</ul></div>"; 
  519.  
  520. $group_end_html = apply_filters( "bboss_global_search_results_group_end_html", $end_html, $args['search_subset'] ); 
  521.  
  522. $last_item['html'] = $last_item['html'] . $group_end_html; 
  523. $this->search_results[$args['search_subset']]['items'][$last_item['id']] = $last_item; 
  524.  
  525. //html for search results is generated. 
  526. //now, lets calculate the total number of search results, for all different types 
  527. if( $args['count_total'] ) { 
  528. $all_items_count = 0; 
  529. foreach( $args['searchable_items'] as $search_type ) { 
  530. if( !isset($this->search_helpers[$search_type])) 
  531. continue; 
  532.  
  533. $obj = $this->search_helpers[$search_type]; 
  534. $total_match_count = $obj->get_total_match_count( $this->search_args['search_term'] ); 
  535. $this->search_results[$search_type]['total_match_count'] = $total_match_count; 
  536.  
  537. $all_items_count += $total_match_count; 
  538.  
  539. $this->search_results['all']['total_match_count'] = $all_items_count; 
  540.  
  541. /** 
  542. * setup everything before starting to display content for search page. 
  543. */ 
  544. public function prepare_search_page() { 
  545. $args = array(); 
  546. if( isset( $_GET['subset'] ) && !empty( $_GET['subset'] ) ) { 
  547. $args['search_subset'] = $_GET['subset']; 
  548.  
  549. if( isset( $_GET['s'] ) && !empty( $_GET['s'] ) ) { 
  550. $args['search_term'] = $_GET['s']; 
  551.  
  552. if( isset( $_GET['list'] ) && !empty( $_GET['list'] ) ) { 
  553. $current_page = (int)$_GET['list']; 
  554. if( $current_page > 0 ) { 
  555. $args['current_page'] = $current_page; 
  556.  
  557. $args = apply_filters( 'bboss_global_search_search_page_args', $args ); 
  558. $this->do_search( $args ); 
  559.  
  560. /** 
  561. * Sanitize user inputs before performing search. 
  562. *  
  563. * @param mixed $args 
  564. * @return array 
  565. */ 
  566. public function sanitize_args( $args='' ) { 
  567. $args = wp_parse_args( $args, array() ); 
  568.  
  569. if( isset( $args['search_term'] ) ) { 
  570. $args['search_term'] = sanitize_text_field( $args['search_term'] ); 
  571.  
  572. if( isset( $args['search_subset'] ) ) { 
  573. $args['search_subset'] = sanitize_text_field( $args['search_subset'] ); 
  574.  
  575. if( isset( $args['per_page'] ) ) { 
  576. $args['per_page'] = absint( $args['per_page'] ); 
  577.  
  578. if( isset( $args['current_page'] ) ) { 
  579. $args['current_page'] = absint( $args['current_page'] ); 
  580.  
  581. return $args; 
  582.  
  583. /** 
  584. * Returns the url of the page which is selected to display search results. 
  585. * @since 1.0.0 
  586. * @return string url of the serach results page 
  587. */ 
  588. public function search_page_url($value="") { 
  589. $url = home_url( '/' ); 
  590.  
  591. if(!empty($value)) { 
  592. $url = esc_url(add_query_arg( 's', urlencode($value), $url )); 
  593.  
  594. return $url; 
  595.  
  596. /** 
  597. * function to return full search url, added with search terms and other filters 
  598. */ 
  599. private function search_page_search_url() { 
  600.  
  601. if ( true == $this->search_args['forum_search'] ) { 
  602. //Full search url for bbpress forum search 
  603. $base_url = bbp_get_search_url(); 
  604. $full_url = esc_url( add_query_arg( 'bbp_search' , urlencode( $this->search_args['search_term'] ), $base_url ) ); 
  605. } else { 
  606. $base_url = $this->search_page_url(); 
  607. $full_url = esc_url(add_query_arg( 's', urlencode( $this->search_args['search_term'] ), $base_url )); 
  608. //for now we only have one filter in url 
  609.  
  610. return $full_url; 
  611.  
  612. public function print_tabs() { 
  613. $search_url = $this->search_page_search_url(); 
  614.  
  615. //first print the 'all results' tab 
  616. $class = 'all'==$this->search_args['search_subset'] ? 'active current' : ''; 
  617. //this filter can be used to change display of 'all' to 'Everything' etc.. 
  618. $all_label = __( 'All', 'buddypress-global-search' ); 
  619. $label = apply_filters( 'bboss_global_search_label_search_type', $all_label ); 
  620.  
  621. if( $this->search_args['count_total'] && isset( $this->search_results['all'] ) ) { 
  622. $label .= "<span class='count'>" . $this->search_results['all']['total_match_count'] . "</span>"; 
  623.  
  624. $tab_url = $search_url; 
  625. echo "<li class='{$class}'><a href='" . esc_url($tab_url) . "'>{$label}</a></li>"; 
  626.  
  627. //then other tabs 
  628. $search_items = buddyboos_global_search_items(); 
  629. foreach( $this->search_args['searchable_items'] as $item ) { 
  630. $class = $item==$this->search_args['search_subset'] ? 'active current' : ''; 
  631. //this filter can be used to change display of 'posts' to 'Blog Posts' etc.. 
  632.  
  633. $label = isset ( $search_items[$item] ) ? $search_items[$item] : $item; 
  634.  
  635. $label = apply_filters( 'bboss_global_search_label_search_type', $label); 
  636.  
  637. if(empty($this->search_results[$item]['total_match_count'])) { 
  638. continue; //skip tab 
  639.  
  640. if( $this->search_args['count_total'] ) { 
  641. $label .= "<span class='count'>" . (int)$this->search_results[$item]['total_match_count'] . "</span>"; 
  642.  
  643. $tab_url = esc_url(add_query_arg( 'subset', $item, $search_url )); 
  644. echo "<li class='{$class} {$item}' data-item='{$item}'><a href='" . esc_url($tab_url) . "'>{$label}</a></li>"; 
  645.  
  646. public function print_results() { 
  647. $current_tab = $this->search_args['search_subset']; 
  648. if( isset( $this->search_results[$current_tab]['items'] ) && !empty( $this->search_results[$current_tab]['items'] ) ) { 
  649. foreach( $this->search_results[$current_tab]['items'] as $item_id=>$item ) { 
  650. echo $item['html']; 
  651.  
  652. if( function_exists( 'emi_generate_paging_param' ) ) { 
  653. $page_slug = untrailingslashit( str_replace( home_url(), '', $this->search_page_url() ) ); 
  654. emi_generate_paging_param(  
  655. $this->search_results[$current_tab]['total_match_count'],  
  656. $this->search_args['per_page'],  
  657. $this->search_args['current_page'],  
  658. $page_slug 
  659. ); 
  660. } else { 
  661. buddyboss_global_search_buffer_template_part( 'no-results', $current_tab ); 
  662.  
  663. public function get_search_term() { 
  664. return isset( $this->search_args['search_term'] ) ? $this->search_args['search_term'] : '';