PageRenderTime 31ms CodeModel.GetById 8ms RepoModel.GetById 0ms app.codeStats 0ms

/mod/search/search_hooks.php

https://github.com/fragilbert/Elgg
PHP | 472 lines | 266 code | 88 blank | 118 comment | 24 complexity | 96521f6577190666f8495039b02784df MD5 | raw file
Possible License(s): MIT, BSD-3-Clause, LGPL-2.1, GPL-2.0
  1. <?php
  2. /**
  3. * Elgg core search.
  4. *
  5. * @package Elgg
  6. * @subpackage Core
  7. */
  8. /**
  9. * Return default results for searches on objects.
  10. *
  11. * @param unknown_type $hook
  12. * @param unknown_type $type
  13. * @param unknown_type $value
  14. * @param unknown_type $params
  15. * @return unknown_type
  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, FALSE);
  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. $entities = elgg_get_entities($params);
  33. // add the volatile data for why these entities have been returned.
  34. foreach ($entities as $entity) {
  35. $title = search_get_highlighted_relevant_substrings($entity->title, $params['query']);
  36. $entity->setVolatileData('search_matched_title', $title);
  37. $desc = search_get_highlighted_relevant_substrings($entity->description, $params['query']);
  38. $entity->setVolatileData('search_matched_description', $desc);
  39. }
  40. return array(
  41. 'entities' => $entities,
  42. 'count' => $count,
  43. );
  44. }
  45. /**
  46. * Return default results for searches on groups.
  47. *
  48. * @param unknown_type $hook
  49. * @param unknown_type $type
  50. * @param unknown_type $value
  51. * @param unknown_type $params
  52. * @return unknown_type
  53. */
  54. function search_groups_hook($hook, $type, $value, $params) {
  55. $db_prefix = elgg_get_config('dbprefix');
  56. $query = sanitise_string($params['query']);
  57. $join = "JOIN {$db_prefix}groups_entity ge ON e.guid = ge.guid";
  58. $params['joins'] = array($join);
  59. $fields = array('name', 'description');
  60. // force into boolean mode because we've having problems with the
  61. // "if > 50% match 0 sets are returns" problem.
  62. $where = search_get_where_sql('ge', $fields, $params, FALSE);
  63. $params['wheres'] = array($where);
  64. // override subtype -- All groups should be returned regardless of subtype.
  65. $params['subtype'] = ELGG_ENTITIES_ANY_VALUE;
  66. $params['count'] = TRUE;
  67. $count = elgg_get_entities($params);
  68. // no need to continue if nothing here.
  69. if (!$count) {
  70. return array('entities' => array(), 'count' => $count);
  71. }
  72. $params['count'] = FALSE;
  73. $params['order_by'] = search_get_order_by_sql('e', 'ge', $params['sort'], $params['order']);
  74. $entities = elgg_get_entities($params);
  75. // add the volatile data for why these entities have been returned.
  76. foreach ($entities as $entity) {
  77. $name = search_get_highlighted_relevant_substrings($entity->name, $query);
  78. $entity->setVolatileData('search_matched_title', $name);
  79. $description = search_get_highlighted_relevant_substrings($entity->description, $query);
  80. $entity->setVolatileData('search_matched_description', $description);
  81. }
  82. return array(
  83. 'entities' => $entities,
  84. 'count' => $count,
  85. );
  86. }
  87. /**
  88. * Return default results for searches on users.
  89. *
  90. * @todo add profile field MD searching
  91. *
  92. * @param unknown_type $hook
  93. * @param unknown_type $type
  94. * @param unknown_type $value
  95. * @param unknown_type $params
  96. * @return unknown_type
  97. */
  98. function search_users_hook($hook, $type, $value, $params) {
  99. $db_prefix = elgg_get_config('dbprefix');
  100. $query = sanitise_string($params['query']);
  101. $params['joins'] = array(
  102. "JOIN {$db_prefix}users_entity ue ON e.guid = ue.guid",
  103. "JOIN {$db_prefix}metadata md on e.guid = md.entity_guid",
  104. "JOIN {$db_prefix}metastrings msv ON n_table.value_id = msv.id"
  105. );
  106. // username and display name
  107. $fields = array('username', 'name');
  108. $where = search_get_where_sql('ue', $fields, $params, FALSE);
  109. // profile fields
  110. $profile_fields = array_keys(elgg_get_config('profile_fields'));
  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. ));
  116. $params['joins'] = array_merge($clauses['joins'], $params['joins']);
  117. // no fulltext index, can't disable fulltext search in this function.
  118. // $md_where .= " AND " . search_get_where_sql('msv', array('string'), $params, FALSE);
  119. $md_where = "(({$clauses['wheres'][0]}) AND msv.string LIKE '%$query%')";
  120. $params['wheres'] = array("(($where) OR ($md_where))");
  121. // override subtype -- All users should be returned regardless of subtype.
  122. $params['subtype'] = ELGG_ENTITIES_ANY_VALUE;
  123. $params['count'] = true;
  124. $count = elgg_get_entities($params);
  125. // no need to continue if nothing here.
  126. if (!$count) {
  127. return array('entities' => array(), 'count' => $count);
  128. }
  129. $params['count'] = FALSE;
  130. $params['order_by'] = search_get_order_by_sql('e', 'ue', $params['sort'], $params['order']);
  131. $entities = elgg_get_entities($params);
  132. // add the volatile data for why these entities have been returned.
  133. foreach ($entities as $entity) {
  134. $title = search_get_highlighted_relevant_substrings($entity->name, $query);
  135. // include the username if it matches but the display name doesn't.
  136. if (false !== strpos($entity->username, $query)) {
  137. $username = search_get_highlighted_relevant_substrings($entity->username, $query);
  138. $title .= " ($username)";
  139. }
  140. $entity->setVolatileData('search_matched_title', $title);
  141. $matched = '';
  142. foreach ($profile_fields as $md) {
  143. $text = $entity->$md;
  144. if (stristr($text, $query)) {
  145. $matched .= elgg_echo("profile:{$md}") . ': '
  146. . search_get_highlighted_relevant_substrings($text, $query);
  147. }
  148. }
  149. $entity->setVolatileData('search_matched_description', $matched);
  150. }
  151. return array(
  152. 'entities' => $entities,
  153. 'count' => $count,
  154. );
  155. }
  156. /**
  157. * Return default results for searches on tags.
  158. *
  159. * @param unknown_type $hook
  160. * @param unknown_type $type
  161. * @param unknown_type $value
  162. * @param unknown_type $params
  163. * @return unknown_type
  164. */
  165. function search_tags_hook($hook, $type, $value, $params) {
  166. $db_prefix = elgg_get_config('dbprefix');
  167. $valid_tag_names = elgg_get_registered_tag_metadata_names();
  168. // @todo will need to split this up to support searching multiple tags at once.
  169. $query = sanitise_string($params['query']);
  170. // if passed a tag metadata name, only search on that tag name.
  171. // tag_name isn't included in the params because it's specific to
  172. // tag searches.
  173. if ($tag_names = get_input('tag_names')) {
  174. if (is_array($tag_names)) {
  175. $search_tag_names = $tag_names;
  176. } else {
  177. $search_tag_names = array($tag_names);
  178. }
  179. // check these are valid to avoid arbitrary metadata searches.
  180. foreach ($search_tag_names as $i => $tag_name) {
  181. if (!in_array($tag_name, $valid_tag_names)) {
  182. unset($search_tag_names[$i]);
  183. }
  184. }
  185. } else {
  186. $search_tag_names = $valid_tag_names;
  187. }
  188. if (!$search_tag_names) {
  189. return array('entities' => array(), 'count' => $count);
  190. }
  191. // don't use elgg_get_entities_from_metadata() here because of
  192. // performance issues. since we don't care what matches at this point
  193. // use an IN clause to grab everything that matches at once and sort
  194. // out the matches later.
  195. $params['joins'][] = "JOIN {$db_prefix}metadata md on e.guid = md.entity_guid";
  196. $params['joins'][] = "JOIN {$db_prefix}metastrings msn on md.name_id = msn.id";
  197. $params['joins'][] = "JOIN {$db_prefix}metastrings msv on md.value_id = msv.id";
  198. $access = get_access_sql_suffix('md');
  199. $sanitised_tags = array();
  200. foreach ($search_tag_names as $tag) {
  201. $sanitised_tags[] = '"' . sanitise_string($tag) . '"';
  202. }
  203. $tags_in = implode(',', $sanitised_tags);
  204. $params['wheres'][] = "(msn.string IN ($tags_in) AND msv.string = '$query' AND $access)";
  205. $params['count'] = TRUE;
  206. $count = elgg_get_entities($params);
  207. // no need to continue if nothing here.
  208. if (!$count) {
  209. return array('entities' => array(), 'count' => $count);
  210. }
  211. $params['count'] = FALSE;
  212. $params['order_by'] = search_get_order_by_sql('e', null, $params['sort'], $params['order']);
  213. $entities = elgg_get_entities($params);
  214. // add the volatile data for why these entities have been returned.
  215. foreach ($entities as $entity) {
  216. $matched_tags_strs = array();
  217. // get tags for each tag name requested to find which ones matched.
  218. foreach ($search_tag_names as $tag_name) {
  219. $tags = $entity->getTags($tag_name);
  220. // @todo make one long tag string and run this through the highlight
  221. // function. This might be confusing as it could chop off
  222. // the tag labels.
  223. if (in_array(strtolower($query), array_map('strtolower', $tags))) {
  224. if (is_array($tags)) {
  225. $tag_name_str = elgg_echo("tag_names:$tag_name");
  226. $matched_tags_strs[] = "$tag_name_str: " . implode(', ', $tags);
  227. }
  228. }
  229. }
  230. // different entities have different titles
  231. switch($entity->type) {
  232. case 'site':
  233. case 'user':
  234. case 'group':
  235. $title_tmp = $entity->name;
  236. break;
  237. case 'object':
  238. $title_tmp = $entity->title;
  239. break;
  240. }
  241. // Nick told me my idea was dirty, so I'm hard coding the numbers.
  242. $title_tmp = strip_tags($title_tmp);
  243. if (elgg_strlen($title_tmp) > 297) {
  244. $title_str = elgg_substr($title_tmp, 0, 297) . '...';
  245. } else {
  246. $title_str = $title_tmp;
  247. }
  248. $desc_tmp = strip_tags($entity->description);
  249. if (elgg_strlen($desc_tmp) > 297) {
  250. $desc_str = elgg_substr($desc_tmp, 0, 297) . '...';
  251. } else {
  252. $desc_str = $desc_tmp;
  253. }
  254. $tags_str = implode('. ', $matched_tags_strs);
  255. $tags_str = search_get_highlighted_relevant_substrings($tags_str, $params['query'], 30, 300, true);
  256. $entity->setVolatileData('search_matched_title', $title_str);
  257. $entity->setVolatileData('search_matched_description', $desc_str);
  258. $entity->setVolatileData('search_matched_extra', $tags_str);
  259. }
  260. return array(
  261. 'entities' => $entities,
  262. 'count' => $count,
  263. );
  264. }
  265. /**
  266. * Register tags as a custom search type.
  267. *
  268. * @param unknown_type $hook
  269. * @param unknown_type $type
  270. * @param unknown_type $value
  271. * @param unknown_type $params
  272. * @return unknown_type
  273. */
  274. function search_custom_types_tags_hook($hook, $type, $value, $params) {
  275. $value[] = 'tags';
  276. return $value;
  277. }
  278. /**
  279. * Return default results for searches on comments.
  280. *
  281. * @param unknown_type $hook
  282. * @param unknown_type $type
  283. * @param unknown_type $value
  284. * @param unknown_type $params
  285. * @return unknown_type
  286. */
  287. function search_comments_hook($hook, $type, $value, $params) {
  288. $db_prefix = elgg_get_config('dbprefix');
  289. $query = sanitise_string($params['query']);
  290. $limit = sanitise_int($params['limit']);
  291. $offset = sanitise_int($params['offset']);
  292. $params['annotation_names'] = array('generic_comment', 'group_topic_post');
  293. $params['joins'] = array(
  294. "JOIN {$db_prefix}annotations a on e.guid = a.entity_guid",
  295. "JOIN {$db_prefix}metastrings msn on a.name_id = msn.id",
  296. "JOIN {$db_prefix}metastrings msv on a.value_id = msv.id"
  297. );
  298. $fields = array('string');
  299. // force IN BOOLEAN MODE since fulltext isn't
  300. // available on metastrings (and boolean mode doesn't need it)
  301. $search_where = search_get_where_sql('msv', $fields, $params, FALSE);
  302. $container_and = '';
  303. if ($params['container_guid'] && $params['container_guid'] !== ELGG_ENTITIES_ANY_VALUE) {
  304. $container_and = 'AND e.container_guid = ' . sanitise_int($params['container_guid']);
  305. }
  306. $e_access = get_access_sql_suffix('e');
  307. $a_access = get_access_sql_suffix('a');
  308. // @todo this can probably be done through the api..
  309. $q = "SELECT count(DISTINCT a.id) as total FROM {$db_prefix}annotations a
  310. JOIN {$db_prefix}metastrings msn ON a.name_id = msn.id
  311. JOIN {$db_prefix}metastrings msv ON a.value_id = msv.id
  312. JOIN {$db_prefix}entities e ON a.entity_guid = e.guid
  313. WHERE msn.string IN ('generic_comment', 'group_topic_post')
  314. AND ($search_where)
  315. AND $e_access
  316. AND $a_access
  317. $container_and
  318. ";
  319. if (!$result = get_data($q)) {
  320. return FALSE;
  321. }
  322. $count = $result[0]->total;
  323. // don't continue if nothing there...
  324. if (!$count) {
  325. return array ('entities' => array(), 'count' => 0);
  326. }
  327. $order_by = search_get_order_by_sql('e', null, $params['sort'], $params['order']);
  328. if ($order_by) {
  329. $order_by = "ORDER BY $order_by";
  330. }
  331. $q = "SELECT DISTINCT a.*, msv.string as comment FROM {$db_prefix}annotations a
  332. JOIN {$db_prefix}metastrings msn ON a.name_id = msn.id
  333. JOIN {$db_prefix}metastrings msv ON a.value_id = msv.id
  334. JOIN {$db_prefix}entities e ON a.entity_guid = e.guid
  335. WHERE msn.string IN ('generic_comment', 'group_topic_post')
  336. AND ($search_where)
  337. AND $e_access
  338. AND $a_access
  339. $container_and
  340. $order_by
  341. LIMIT $offset, $limit
  342. ";
  343. $comments = get_data($q);
  344. // @todo if plugins are disabled causing subtypes
  345. // to be invalid and there are comments on entities of those subtypes,
  346. // the counts will be wrong here and results might not show up correctly,
  347. // especially on the search landing page, which only pulls out two results.
  348. // probably better to check against valid subtypes than to do what I'm doing.
  349. // need to return actual entities
  350. // add the volatile data for why these entities have been returned.
  351. $entities = array();
  352. foreach ($comments as $comment) {
  353. $entity = get_entity($comment->entity_guid);
  354. // hic sunt dracones
  355. if (!$entity) {
  356. //continue;
  357. $entity = new ElggObject();
  358. $entity->setVolatileData('search_unavailable_entity', TRUE);
  359. }
  360. $comment_str = search_get_highlighted_relevant_substrings($comment->comment, $query);
  361. $entity->setVolatileData('search_match_annotation_id', $comment->id);
  362. $entity->setVolatileData('search_matched_comment', $comment_str);
  363. $entity->setVolatileData('search_matched_comment_owner_guid', $comment->owner_guid);
  364. $entity->setVolatileData('search_matched_comment_time_created', $comment->time_created);
  365. $entities[] = $entity;
  366. }
  367. return array(
  368. 'entities' => $entities,
  369. 'count' => $count,
  370. );
  371. }
  372. /**
  373. * Register comments as a custom search type.
  374. *
  375. * @param unknown_type $hook
  376. * @param unknown_type $type
  377. * @param unknown_type $value
  378. * @param unknown_type $params
  379. * @return unknown_type
  380. */
  381. function search_custom_types_comments_hook($hook, $type, $value, $params) {
  382. $value[] = 'comments';
  383. return $value;
  384. }