search_api_sorts.module 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. <?php
  2. /**
  3. * Implements hook_help().
  4. */
  5. function search_api_sorts_help($path, array $arg) {
  6. if ($path == 'admin/config/search/search_api/index/%/sorts') {
  7. return t('Select the indexed fields for which you want to enable sorting. ' . 'Before the sort blocks is actually displayed, you will have to enable and configure it at the <a href="!url">block administration page</a>.', array('!url' => url('admin/structure/block')));
  8. }
  9. }
  10. /**
  11. * Implements hook_menu().
  12. */
  13. function search_api_sorts_menu() {
  14. $items['admin/config/search/search_api/index/%search_api_index/sorts'] = array(
  15. 'title' => 'Sorts',
  16. 'description' => 'Select the sort fields to display.',
  17. 'page callback' => 'drupal_get_form',
  18. 'page arguments' => array(
  19. 'search_api_sorts_index_select',
  20. 5
  21. ),
  22. 'access arguments' => array('administer search_api'),
  23. 'weight' => -1,
  24. 'type' => MENU_LOCAL_TASK,
  25. 'context' => MENU_CONTEXT_INLINE | MENU_CONTEXT_PAGE,
  26. 'file' => 'search_api_sorts.admin.inc',
  27. );
  28. return $items;
  29. }
  30. /**
  31. * Implements hook_theme().
  32. */
  33. function search_api_sorts_theme() {
  34. $themes['search_api_sorts_form_table'] = array(
  35. 'render element' => 'element',
  36. 'file' => 'search_api_sorts.admin.inc',
  37. );
  38. $themes['search_api_sorts_list'] = array(
  39. 'variables' => array(
  40. 'items' => array(),
  41. 'options' => array(),
  42. ),
  43. 'file' => 'search_api_sorts.theme.inc',
  44. );
  45. $themes['search_api_sorts_sort'] = array(
  46. 'variables' => array(
  47. 'name' => '',
  48. 'path' => NULL,
  49. 'options' => array(),
  50. 'order_options' => array(),
  51. 'active' => FALSE,
  52. 'default_sort' => FALSE,
  53. ),
  54. 'file' => 'search_api_sorts.theme.inc',
  55. );
  56. return $themes;
  57. }
  58. /**
  59. * Implements hook_entity_info().
  60. */
  61. function search_api_sorts_entity_info() {
  62. $info['search_api_sort'] = array(
  63. 'label' => t('Search sort'),
  64. 'controller class' => 'EntityAPIControllerExportable',
  65. 'entity class' => 'SearchApiSort',
  66. 'base table' => 'search_api_sort',
  67. 'uri callback' => 'search_api_sort_url',
  68. 'label callback' => 'search_api_sort_label',
  69. 'module' => 'search_api_sorts',
  70. 'exportable' => TRUE,
  71. 'entity keys' => array(
  72. 'id' => 'id',
  73. 'name' => 'identifier',
  74. 'label' => 'name',
  75. ),
  76. );
  77. return $info;
  78. }
  79. /**
  80. * Implements hook_permission().
  81. */
  82. function search_api_sorts_permission() {
  83. return array('use search_api_sorts' => array('title' => t('Use search sorts'), ), );
  84. }
  85. /**
  86. * Implements hook_block_info().
  87. */
  88. function search_api_sorts_block_info() {
  89. $blocks = array('search-sorts' => array(
  90. 'info' => t('Search sorts'),
  91. 'cache' => DRUPAL_NO_CACHE,
  92. 'weight' => 4,
  93. ), );
  94. return $blocks;
  95. }
  96. /**
  97. * Implements hook_ctools_block_info().
  98. *
  99. * @see http://drupal.org/node/1763954
  100. */
  101. function search_api_sorts_ctools_block_info($module, $delta, &$info) {
  102. // Give the Search API Sorts block it's own category.
  103. $info['category'] = t('Search API Sorts');
  104. // Allow blocks to be used before the search results in Panels.
  105. $info['render last'] = TRUE;
  106. }
  107. /**
  108. * Implements hook_block_view().
  109. */
  110. function search_api_sorts_block_view($delta = '') {
  111. if (!user_access('use search_api_sorts')) {
  112. return;
  113. }
  114. if ($delta == 'search-sorts') {
  115. return search_api_sorts_block_search_sorts_view();
  116. }
  117. }
  118. /**
  119. * Get a list of sorts field names for the current search index id
  120. *
  121. * @return a cached query object
  122. */
  123. function search_api_sorts_search_sorts($index_id, $enabled = 1, $reset = FALSE) {
  124. $cache = &drupal_static(__FUNCTION__, array());
  125. if (!isset($cache[$index_id . '_' . $enabled]) || $reset) {
  126. $query = db_select('search_api_sort', 's')->fields('s', array(
  127. 'field',
  128. 'name',
  129. 'default_sort',
  130. 'default_sort_no_terms',
  131. 'default_order',
  132. 'weight'
  133. ))->condition('index_id', $index_id)->condition('enabled', $enabled)->orderby('weight', 'ASC')->execute();
  134. $data = array();
  135. while ($row = $query->fetch()) {
  136. $data[] = $row;
  137. }
  138. $cache[$index_id . '_' . $enabled] = $data;
  139. }
  140. return $cache[$index_id . '_' . $enabled];
  141. }
  142. /**
  143. * View the "Search sorts" block.
  144. */
  145. function search_api_sorts_block_search_sorts_view() {
  146. $query = drupal_static('search_api_sorts_search_api_query_alter', array());
  147. if (!$query) {
  148. return;
  149. }
  150. $search_sorts = search_api_sorts_search_sorts($query->getIndex()->machine_name);
  151. if (empty($search_sorts)) {
  152. return;
  153. }
  154. $item = menu_get_item();
  155. $path = $item['href'];
  156. $current_sort = $query->getSort();
  157. $params = drupal_get_query_parameters($_GET, array(
  158. 'q',
  159. 'page'
  160. ));
  161. //Override the path if facetapi_pretty_paths is enabled
  162. if (module_exists('facetapi_pretty_paths')) {
  163. // Get the facet api adapter by the machine readable name of searcher.
  164. $adapter = facetapi_adapter_load('search_api@' . $query->getIndex()->machine_name);
  165. // Get the url processor and check if it uses pretty paths.
  166. $urlProcessor = $adapter->getUrlProcessor();
  167. if ($urlProcessor instanceof FacetapiUrlProcessorPrettyPaths) {
  168. // Retrieve the full path from the url processor.
  169. $path = $urlProcessor->getFullPath();
  170. }
  171. }
  172. if (isset($params['search_api_views_fulltext'])) {
  173. $default_sort = _search_api_sorts_get_default_sort($search_sorts, $params['search_api_views_fulltext']);
  174. }
  175. else {
  176. $default_sort = _search_api_sorts_get_default_sort($search_sorts);
  177. }
  178. $tmp = array_keys($query->getSort());
  179. if (!$sort = strtolower(reset($tmp))) {
  180. $sort = $default_sort->field;
  181. }
  182. $tmp = reset($query->getSort());
  183. if (!$order = strtolower($tmp)) {
  184. $order = $default_sort->default_order;
  185. }
  186. $items = array();
  187. foreach ($search_sorts as $key => $data) {
  188. $options = array(
  189. 'query' => array(
  190. 'sort' => $data->field,
  191. 'order' => $data->default_order,
  192. ) + $params,
  193. 'attributes' => array('class' => array('sort-item'), ),
  194. );
  195. // active sort field logic
  196. if ($sort == $data->field) {
  197. // add some extra classes
  198. $options['attributes']['class'] += array(
  199. 'active',
  200. 'sort-' . $order
  201. );
  202. // create a copy of the options, for use in the order-link
  203. $order_options = $options;
  204. // with sort order flipped
  205. $order_options['query']['order'] = ($order == 'asc') ? 'desc' : 'asc';
  206. // remove sort and order for the link, because clicking on the
  207. // active should remove the sort.
  208. unset($options['query']['sort']);
  209. unset($options['query']['order']);
  210. $items[] = array(
  211. '#theme' => 'search_api_sorts_sort',
  212. '#name' => t($data->name),
  213. '#path' => $path,
  214. '#options' => $options,
  215. '#order_options' => $order_options,
  216. '#active' => TRUE,
  217. '#default_sort' => $default_sort->field,
  218. );
  219. }
  220. else {
  221. // regular sort field logic
  222. $items[] = array(
  223. '#theme' => 'search_api_sorts_sort',
  224. '#name' => t($data->name),
  225. '#path' => $path,
  226. '#options' => $options,
  227. '#active' => FALSE,
  228. '#default_sort' => $default_sort->field,
  229. );
  230. }
  231. }
  232. return array(
  233. 'subject' => t('Sort by'),
  234. 'content' => array(
  235. '#theme' => 'search_api_sorts_list',
  236. '#items' => $items,
  237. '#options' => array('attributes' => array('class' => array('search-api-sorts')))
  238. ),
  239. );
  240. }
  241. /**
  242. * entity_label callback.
  243. */
  244. function search_api_sort_label($sort) {
  245. static $indices = NULL;
  246. if (empty($indices)) {
  247. $indices = search_api_index_load_multiple(FALSE);
  248. }
  249. return $indices[$sort->index_id]->name . ' - ' . $sort->name;
  250. }
  251. /**
  252. * Load multiple sorts at once, determined by IDs or deltas, or by other
  253. * conditions.
  254. *
  255. * @see entity_load()
  256. *
  257. * @param $ids
  258. * An array of sort IDs or machine names.
  259. * @param $conditions
  260. * An array of conditions on the {search_api_sort} table in the form
  261. * 'field' => $value.
  262. * @param $reset
  263. * Whether to reset the internal entity_load cache.
  264. *
  265. * @return array
  266. * An array of SearchApiSort objects keyed by machine name.
  267. */
  268. function search_api_sorts_load_multiple($ids = array(), $conditions = array(), $reset = FALSE) {
  269. return entity_load('search_api_sort', $ids, $conditions, $reset);
  270. }
  271. /**
  272. * Implements hook_search_api_query_alter().
  273. */
  274. function search_api_sorts_search_api_query_alter(SearchApiQueryInterface $query) {
  275. if (!user_access('use search_api_sorts')) {
  276. return;
  277. }
  278. // Static save current search query
  279. $_query = &drupal_static(__FUNCTION__, array());
  280. $_query = $query;
  281. // There's already an existing sort, so abort!
  282. $existing = $query->getSort();
  283. if (!empty($existing)) {
  284. return;
  285. }
  286. $search_sorts = search_api_sorts_search_sorts($query->getIndex()->machine_name);
  287. if (empty($search_sorts)) {
  288. return;
  289. }
  290. $default_sort = _search_api_sorts_get_default_sort($search_sorts, $query->getKeys());
  291. // alter sort field and sort order
  292. $sort = $default_sort->field;
  293. $params = drupal_get_query_parameters($_GET, array(
  294. 'q',
  295. 'page'
  296. ));
  297. if (isset($params['sort']) && !empty($params['sort'])) {
  298. $sort = $params['sort'];
  299. }
  300. $order = $default_sort->default_order;
  301. if (isset($params['order']) && !empty($params['order'])) {
  302. $order = $params['order'];
  303. }
  304. if (!empty($order) && !empty($sort)) {
  305. $query->sort($sort, $order);
  306. }
  307. }
  308. /*
  309. * Helper function to get the default sort field
  310. */
  311. function _search_api_sorts_get_default_sort($search_sorts, $keys = NULL) {
  312. // If there are no keys set, return the default for no keys if set
  313. if (empty($keys)) {
  314. foreach ($search_sorts as $sort) {
  315. if ($sort->default_sort_no_terms) {
  316. $sort->active = TRUE;
  317. return $sort;
  318. }
  319. }
  320. }
  321. // Return the default if set
  322. foreach ($search_sorts as $sort) {
  323. if ($sort->default_sort) {
  324. $sort->active = TRUE;
  325. return $sort;
  326. }
  327. }
  328. // Return relevance if no defaults are set
  329. return (object) array(
  330. 'field' => 'search_api_relevance',
  331. 'name' => t('Relevence'),
  332. 'default_order' => 'desc',
  333. 'active' => TRUE,
  334. );
  335. }