search_hooks.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. <?php
  2. /**
  3. * Elgg core search.
  4. *
  5. * @package Elgg
  6. * @subpackage Search
  7. */
  8. /**
  9. * Get objects that match the search parameters.
  10. *
  11. * @param string $hook Hook name
  12. * @param string $type Hook type
  13. * @param array $value Empty array
  14. * @param array $params Search parameters
  15. * @return array
  16. */
  17. function search_objects_hook($hook, $type, $value, $params) {
  18. $db_prefix = elgg_get_config('dbprefix');
  19. $join = "JOIN {$db_prefix}objects_entity oe ON e.guid = oe.guid";
  20. $params['joins'] = array($join);
  21. $fields = array('title', 'description');
  22. $where = search_get_where_sql('oe', $fields, $params);
  23. $params['wheres'] = array($where);
  24. $params['count'] = TRUE;
  25. $count = elgg_get_entities($params);
  26. // no need to continue if nothing here.
  27. if (!$count) {
  28. return array('entities' => array(), 'count' => $count);
  29. }
  30. $params['count'] = FALSE;
  31. $params['order_by'] = search_get_order_by_sql('e', 'oe', $params['sort'], $params['order']);
  32. $params['preload_owners'] = true;
  33. $entities = elgg_get_entities($params);
  34. // add the volatile data for why these entities have been returned.
  35. foreach ($entities as $entity) {
  36. $title = search_get_highlighted_relevant_substrings($entity->title, $params['query']);
  37. $entity->setVolatileData('search_matched_title', $title);
  38. $desc = search_get_highlighted_relevant_substrings($entity->description, $params['query']);
  39. $entity->setVolatileData('search_matched_description', $desc);
  40. }
  41. return array(
  42. 'entities' => $entities,
  43. 'count' => $count,
  44. );
  45. }
  46. /**
  47. * Get groups that match the search parameters.
  48. *
  49. * @param string $hook Hook name
  50. * @param string $type Hook type
  51. * @param array $value Empty array
  52. * @param array $params Search parameters
  53. * @return array
  54. */
  55. function search_groups_hook($hook, $type, $value, $params) {
  56. $db_prefix = elgg_get_config('dbprefix');
  57. $query = sanitise_string($params['query']);
  58. $join = "JOIN {$db_prefix}groups_entity ge ON e.guid = ge.guid";
  59. $params['joins'] = array($join);
  60. $fields = array('name', 'description');
  61. $where = search_get_where_sql('ge', $fields, $params);
  62. $params['wheres'] = array($where);
  63. // override subtype -- All groups should be returned regardless of subtype.
  64. $params['subtype'] = ELGG_ENTITIES_ANY_VALUE;
  65. $params['count'] = TRUE;
  66. $count = elgg_get_entities($params);
  67. // no need to continue if nothing here.
  68. if (!$count) {
  69. return array('entities' => array(), 'count' => $count);
  70. }
  71. $params['count'] = FALSE;
  72. $params['order_by'] = search_get_order_by_sql('e', 'ge', $params['sort'], $params['order']);
  73. $entities = elgg_get_entities($params);
  74. // add the volatile data for why these entities have been returned.
  75. foreach ($entities as $entity) {
  76. $name = search_get_highlighted_relevant_substrings($entity->name, $query);
  77. $entity->setVolatileData('search_matched_title', $name);
  78. $description = search_get_highlighted_relevant_substrings($entity->description, $query);
  79. $entity->setVolatileData('search_matched_description', $description);
  80. }
  81. return array(
  82. 'entities' => $entities,
  83. 'count' => $count,
  84. );
  85. }
  86. /**
  87. * Get users that match the search parameters.
  88. *
  89. * Searches on username, display name, and profile fields
  90. *
  91. * @param string $hook Hook name
  92. * @param string $type Hook type
  93. * @param array $value Empty array
  94. * @param array $params Search parameters
  95. * @return array
  96. */
  97. function search_users_hook($hook, $type, $value, $params) {
  98. $db_prefix = elgg_get_config('dbprefix');
  99. $query = sanitise_string($params['query']);
  100. $params['joins'] = array(
  101. "JOIN {$db_prefix}users_entity ue ON e.guid = ue.guid",
  102. );
  103. // username and display name
  104. $fields = array('username', 'name');
  105. $where = search_get_where_sql('ue', $fields, $params, FALSE);
  106. // profile fields
  107. $profile_fields = array_keys(elgg_get_config('profile_fields'));
  108. if (!empty($profile_fields)) {
  109. $params['joins'][] = "JOIN {$db_prefix}metadata md on e.guid = md.entity_guid";
  110. $params['joins'][] = "JOIN {$db_prefix}metastrings msv ON n_table.value_id = msv.id";
  111. // get the where clauses for the md names
  112. // can't use egef_metadata() because the n_table join comes too late.
  113. $clauses = _elgg_entities_get_metastrings_options('metadata', array(
  114. 'metadata_names' => $profile_fields,
  115. // avoid notices
  116. 'metadata_values' => null,
  117. 'metadata_name_value_pairs' => null,
  118. 'metadata_name_value_pairs_operator' => null,
  119. 'metadata_case_sensitive' => null,
  120. 'order_by_metadata' => null,
  121. 'metadata_owner_guids' => null,
  122. ));
  123. $params['joins'] = array_merge($clauses['joins'], $params['joins']);
  124. // no fulltext index, can't disable fulltext search in this function.
  125. // $md_where .= " AND " . search_get_where_sql('msv', array('string'), $params, FALSE);
  126. $md_where = "(({$clauses['wheres'][0]}) AND msv.string LIKE '%$query%')";
  127. $params['wheres'] = array("(($where) OR ($md_where))");
  128. } else {
  129. $params['wheres'] = array("$where");
  130. }
  131. // override subtype -- All users should be returned regardless of subtype.
  132. $params['subtype'] = ELGG_ENTITIES_ANY_VALUE;
  133. $params['count'] = true;
  134. $count = elgg_get_entities($params);
  135. // no need to continue if nothing here.
  136. if (!$count) {
  137. return array('entities' => array(), 'count' => $count);
  138. }
  139. $params['count'] = FALSE;
  140. $params['order_by'] = search_get_order_by_sql('e', 'ue', $params['sort'], $params['order']);
  141. $entities = elgg_get_entities($params);
  142. // add the volatile data for why these entities have been returned.
  143. foreach ($entities as $entity) {
  144. $title = search_get_highlighted_relevant_substrings($entity->name, $query);
  145. // include the username if it matches but the display name doesn't.
  146. if (false !== strpos($entity->username, $query)) {
  147. $username = search_get_highlighted_relevant_substrings($entity->username, $query);
  148. $title .= " ($username)";
  149. }
  150. $entity->setVolatileData('search_matched_title', $title);
  151. if (!empty($profile_fields)) {
  152. $matched = '';
  153. foreach ($profile_fields as $md_name) {
  154. $metadata = $entity->$md_name;
  155. if (is_array($metadata)) {
  156. foreach ($metadata as $text) {
  157. if (stristr($text, $query)) {
  158. $matched .= elgg_echo("profile:{$md_name}") . ': '
  159. . search_get_highlighted_relevant_substrings($text, $query);
  160. }
  161. }
  162. } else {
  163. if (stristr($metadata, $query)) {
  164. $matched .= elgg_echo("profile:{$md_name}") . ': '
  165. . search_get_highlighted_relevant_substrings($metadata, $query);
  166. }
  167. }
  168. }
  169. $entity->setVolatileData('search_matched_description', $matched);
  170. }
  171. }
  172. return array(
  173. 'entities' => $entities,
  174. 'count' => $count,
  175. );
  176. }
  177. /**
  178. * Get entities with tags that match the search parameters.
  179. *
  180. * @param string $hook Hook name
  181. * @param string $type Hook type
  182. * @param array $value Empty array
  183. * @param array $params Search parameters
  184. * @return array
  185. */
  186. function search_tags_hook($hook, $type, $value, $params) {
  187. $db_prefix = elgg_get_config('dbprefix');
  188. $valid_tag_names = elgg_get_registered_tag_metadata_names();
  189. // @todo will need to split this up to support searching multiple tags at once.
  190. $query = sanitise_string($params['query']);
  191. // if passed a tag metadata name, only search on that tag name.
  192. // tag_name isn't included in the params because it's specific to
  193. // tag searches.
  194. if ($tag_names = get_input('tag_names')) {
  195. if (is_array($tag_names)) {
  196. $search_tag_names = $tag_names;
  197. } else {
  198. $search_tag_names = array($tag_names);
  199. }
  200. // check these are valid to avoid arbitrary metadata searches.
  201. foreach ($search_tag_names as $i => $tag_name) {
  202. if (!in_array($tag_name, $valid_tag_names)) {
  203. unset($search_tag_names[$i]);
  204. }
  205. }
  206. } else {
  207. $search_tag_names = $valid_tag_names;
  208. }
  209. if (!$search_tag_names) {
  210. return array('entities' => array(), 'count' => $count);
  211. }
  212. // don't use elgg_get_entities_from_metadata() here because of
  213. // performance issues. since we don't care what matches at this point
  214. // use an IN clause to grab everything that matches at once and sort
  215. // out the matches later.
  216. $params['joins'][] = "JOIN {$db_prefix}metadata md on e.guid = md.entity_guid";
  217. $params['joins'][] = "JOIN {$db_prefix}metastrings msn on md.name_id = msn.id";
  218. $params['joins'][] = "JOIN {$db_prefix}metastrings msv on md.value_id = msv.id";
  219. $access = _elgg_get_access_where_sql(array('table_alias' => 'md'));
  220. $sanitised_tags = array();
  221. foreach ($search_tag_names as $tag) {
  222. $sanitised_tags[] = '"' . sanitise_string($tag) . '"';
  223. }
  224. $tags_in = implode(',', $sanitised_tags);
  225. $params['wheres'][] = "(msn.string IN ($tags_in) AND msv.string = '$query' AND $access)";
  226. $params['count'] = TRUE;
  227. $count = elgg_get_entities($params);
  228. // no need to continue if nothing here.
  229. if (!$count) {
  230. return array('entities' => array(), 'count' => $count);
  231. }
  232. $params['count'] = FALSE;
  233. $params['order_by'] = search_get_order_by_sql('e', null, $params['sort'], $params['order']);
  234. $entities = elgg_get_entities($params);
  235. // add the volatile data for why these entities have been returned.
  236. foreach ($entities as $entity) {
  237. $matched_tags_strs = array();
  238. // get tags for each tag name requested to find which ones matched.
  239. foreach ($search_tag_names as $tag_name) {
  240. $tags = $entity->getTags($tag_name);
  241. // @todo make one long tag string and run this through the highlight
  242. // function. This might be confusing as it could chop off
  243. // the tag labels.
  244. if (in_array(strtolower($query), array_map('strtolower', $tags))) {
  245. if (is_array($tags)) {
  246. $tag_name_str = elgg_echo("tag_names:$tag_name");
  247. $matched_tags_strs[] = "$tag_name_str: " . implode(', ', $tags);
  248. }
  249. }
  250. }
  251. // different entities have different titles
  252. switch($entity->type) {
  253. case 'site':
  254. case 'user':
  255. case 'group':
  256. $title_tmp = $entity->name;
  257. break;
  258. case 'object':
  259. $title_tmp = $entity->title;
  260. break;
  261. }
  262. // Nick told me my idea was dirty, so I'm hard coding the numbers.
  263. $title_tmp = strip_tags($title_tmp);
  264. if (elgg_strlen($title_tmp) > 297) {
  265. $title_str = elgg_substr($title_tmp, 0, 297) . '...';
  266. } else {
  267. $title_str = $title_tmp;
  268. }
  269. $desc_tmp = strip_tags($entity->description);
  270. if (elgg_strlen($desc_tmp) > 297) {
  271. $desc_str = elgg_substr($desc_tmp, 0, 297) . '...';
  272. } else {
  273. $desc_str = $desc_tmp;
  274. }
  275. $tags_str = implode('. ', $matched_tags_strs);
  276. $tags_str = search_get_highlighted_relevant_substrings($tags_str, $params['query'], 30, 300, true);
  277. $entity->setVolatileData('search_matched_title', $title_str);
  278. $entity->setVolatileData('search_matched_description', $desc_str);
  279. $entity->setVolatileData('search_matched_extra', $tags_str);
  280. }
  281. return array(
  282. 'entities' => $entities,
  283. 'count' => $count,
  284. );
  285. }
  286. /**
  287. * Register tags as a custom search type.
  288. *
  289. * @param string $hook Hook name
  290. * @param string $type Hook type
  291. * @param array $value Array of custom search types
  292. * @param array $params Search parameters
  293. * @return array
  294. */
  295. function search_custom_types_tags_hook($hook, $type, $value, $params) {
  296. $value[] = 'tags';
  297. return $value;
  298. }