facetapi.block.inc 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. <?php
  2. /**
  3. * @file
  4. * Block realm code and hook implementations.
  5. */
  6. /**
  7. * Implements hook_block_info().
  8. *
  9. * @see facetapi_get_block_info()
  10. */
  11. function facetapi_block_info() {
  12. return facetapi_get_block_info(array('block'));
  13. }
  14. /**
  15. * Helper function to get block info for all block-like realms.
  16. *
  17. * @param array $realm_names
  18. * An array of machine readable realm names blocks are being collected for.
  19. *
  20. * @return array
  21. * An array of block information.
  22. *
  23. * @see http://api.drupal.org/api/drupal/modules%21block%21block.api.php/function/hook_block_info/7
  24. */
  25. function facetapi_get_block_info(array $realm_names) {
  26. $blocks = array();
  27. foreach ($realm_names as $realm_name) {
  28. // Gets delta map, iterates over all enabled facets.
  29. $map = facetapi_get_delta_map();
  30. foreach (facetapi_get_searcher_info() as $searcher => $info) {
  31. // Gets cache settings for the searcher.
  32. $cache = facetapi_get_block_cache_setting($searcher, $realm_name);
  33. // Adds blocks for facets that are enabled or whose delta mapping is forced.
  34. foreach (facetapi_get_delta_map_queue($searcher, $realm_name) as $facet_name) {
  35. if ($facet = facetapi_facet_load($facet_name, $searcher)) {
  36. // Gets the delta from the delta map.
  37. $string = facetapi_build_delta($searcher, $realm_name, $facet_name);
  38. $delta = array_search($string, $map);
  39. // Builds the info. For non-block realm, append the realm name.
  40. $subject = 'Facet API: ' . $info['label'] . ' : ' . $facet['label'];
  41. if ('block' != $realm_name && ($realm = facetapi_realm_load($realm_name))) {
  42. $subject .= ' (' . $realm['label'] . ')';
  43. }
  44. // Defines the block.
  45. $blocks[$delta] = array(
  46. 'info' => $subject,
  47. 'cache' => $cache,
  48. );
  49. }
  50. }
  51. }
  52. }
  53. // Returns available blocks.
  54. return $blocks;
  55. }
  56. /**
  57. * Implements hook_ctools_block_info().
  58. *
  59. * @see http://drupal.org/node/1669918
  60. */
  61. function facetapi_ctools_block_info($module, $delta, &$info) {
  62. // Give the Facet API blocks their own category.
  63. $info['category'] = t('Facet API');
  64. // Allow blocks to be used before the search results in Panels.
  65. $info['render last'] = TRUE;
  66. }
  67. /**
  68. * Returns block cache settings.
  69. *
  70. * @param $searcher
  71. * The machine readable name of searcher.
  72. * @param string $realm_name
  73. * The machine readable name of the realm.
  74. *
  75. * @return int
  76. * The constands defined in includes/common.inc, defaults to DRUPAL_NO_CACHE.
  77. *
  78. * @see includes/common.inc
  79. */
  80. function facetapi_get_block_cache_setting($searcher, $realm_name) {
  81. $name = 'facetapi:block_cache:' . $searcher;
  82. if ('block' != $realm_name) {
  83. $name .= ':' . $realm_name;
  84. }
  85. return variable_get($name, DRUPAL_NO_CACHE);
  86. }
  87. /**
  88. * Implements hook_block_view().
  89. */
  90. function facetapi_block_view($delta = '') {
  91. $builds = &drupal_static(__FUNCTION__, array());
  92. $parsed = &drupal_static('facetapi_parsed_deltas', array());
  93. // Test block visibility if not already tested.
  94. if (!isset($parsed[$delta]) && !facetapi_check_block_visibility($delta)) {
  95. return;
  96. }
  97. list($searcher, $realm_name, $facet_name) = $parsed[$delta];
  98. // Builds and caches the entire realm per searcher / realm combination.
  99. $group = $searcher . ':' . $realm_name;
  100. if (!isset($builds[$group])) {
  101. $builds[$group] = facetapi_build_realm($searcher, $realm_name);
  102. }
  103. // Returns the individual block.
  104. if (isset($builds[$group][$facet_name])) {
  105. // Adds contextual links.
  106. $builds[$group][$facet_name]['#contextual_links'] = array(
  107. 'facetapi' => array('admin/config/search/facetapi', array($searcher, $realm_name, $facet_name)),
  108. );
  109. // Returns the subject and content of the block.
  110. $variables = array('title' => $builds[$group][$facet_name]['#title'], 'facet' => $builds[$group][$facet_name]);
  111. return array(
  112. 'subject' => theme('facetapi_title', $variables),
  113. 'content' => $builds[$group][$facet_name]
  114. );
  115. }
  116. }
  117. /**
  118. * Checks whether the block should be displayed.
  119. *
  120. * In cases where modules like Context are being used, hook_block_list_alter()
  121. * is not invoked and we get fatal errors. We have to test whether or not the
  122. * hook has been invoked and call this function manually otherwise.
  123. *
  124. * @param $delta
  125. * The block delta.
  126. *
  127. * @return
  128. * A boolean flagging whether to display this block or not.
  129. */
  130. function facetapi_check_block_visibility($delta) {
  131. $map = facetapi_get_delta_map();
  132. // Store parsed deltas so we only calculate once. This also lets us know
  133. // whether hook_block_list_alter() was called or not.
  134. $parsed = &drupal_static('facetapi_parsed_deltas', array());
  135. // Ensures the delta is mapped.
  136. if (!isset($map[$delta])) {
  137. $parsed[$delta] = FALSE;
  138. return FALSE;
  139. }
  140. // Parses the raw delta, extracts variables for code readability.
  141. $parsed[$delta] = facetapi_parse_delta($map[$delta]);
  142. list($searcher, $realm_name, $facet_name) = $parsed[$delta];
  143. // Checks whether block should be displayed.
  144. if (!facetapi_is_active_searcher($searcher)) {
  145. return FALSE;
  146. }
  147. if (!facetapi_facet_enabled($searcher, $realm_name, $facet_name)) {
  148. return FALSE;
  149. }
  150. if (!$adapter = facetapi_adapter_load($searcher)) {
  151. return FALSE;
  152. }
  153. if ($adapter->suppressOutput($realm_name)) {
  154. return FALSE;
  155. }
  156. // We have facets!
  157. return TRUE;
  158. }
  159. /**
  160. * Returns a cached delta map of hashes to names.
  161. *
  162. * Sometimes deltas are longer than 32 chars and need to be passed to hash().
  163. * Due to the block table's schema, deltas cannot be longer than 32 characters.
  164. * However, hashes are nasty as CSS IDs, so we can use the map to convert
  165. * the hashes back to a nicer value in facetapi_preprocess_block().
  166. *
  167. * @return
  168. * An array containing the delta map.
  169. */
  170. function facetapi_get_delta_map() {
  171. $map = &drupal_static(__FUNCTION__);
  172. if (NULL === $map) {
  173. if ($data = cache_get('facetapi:delta_map')) {
  174. $map = $data->data;
  175. }
  176. else {
  177. $map = array();
  178. // Maps deltas for all realms. This is a hack since not all realms display
  179. // facets in blocks, but it doesn't hurt to store the extra data. The map
  180. // is cached anyways, so it shouldn't cause too much additional resource
  181. // consumption. It is also an edge-case to have non-block facets.
  182. $searcher_info = facetapi_get_searcher_info();
  183. foreach (facetapi_get_realm_info() as $realm_name => $realm_info) {
  184. foreach ($searcher_info as $searcher => $info) {
  185. foreach (facetapi_get_delta_map_queue($searcher, $realm_name) as $facet_name) {
  186. $delta = facetapi_build_delta($searcher, $realm_name, $facet_name);
  187. $map[facetapi_hash_delta($delta)] = $delta;
  188. }
  189. }
  190. }
  191. // Caches the map so we don't have to do this repeatedly.
  192. cache_set('facetapi:delta_map', $map, 'cache', CACHE_TEMPORARY);
  193. }
  194. }
  195. return $map;
  196. }
  197. /**
  198. * Build a delta from the searcher, realm name, and facet name.
  199. *
  200. * @param $searcher
  201. * The machine readable name of the searcher.
  202. * @param $realm_name
  203. * The machine readable name of the realm.
  204. * @param $facet_name
  205. * The machine readable name of the facet.
  206. *
  207. * @return
  208. * A string containing the raw delta.
  209. */
  210. function facetapi_build_delta($searcher, $realm_name, $facet_name) {
  211. return $searcher . ':' . $realm_name . ':' . urlencode($facet_name);
  212. }
  213. /**
  214. * Parses a raw delta into parts.
  215. *
  216. * @param $raw_delta
  217. * A string containing the raw delta prior to being hashed.
  218. *
  219. * @return
  220. * An array containing the searcher, realm_name, and facet name in that order.
  221. */
  222. function facetapi_parse_delta($raw_delta) {
  223. $parsed = array();
  224. // Splits by ":", finds each part.
  225. $parts = explode(':', $raw_delta);
  226. $facet_name = array_pop($parts);
  227. $facet_name = rawurldecode($facet_name);
  228. $realm_name = array_pop($parts);
  229. $searcher = implode(':', $parts);
  230. // Returns array with parsed info.
  231. return array($searcher, $realm_name, $facet_name);
  232. }
  233. /**
  234. * Hashing code for deltas.
  235. *
  236. * @param $delta
  237. * A string containing the delta.
  238. *
  239. * @return
  240. * The hashed delta value.
  241. */
  242. function facetapi_hash_delta($delta) {
  243. // Ensure hashes are URL safe and alpha-numeric.
  244. // @see http://drupal.org/node/1355270
  245. $hash = substr(drupal_hash_base64($delta), 0, 32);
  246. $hash = strtr($hash, array('-' => '0', '_' => '1'));
  247. drupal_alter('facetapi_hash', $hash, $delta);
  248. return $hash;
  249. }
  250. /**
  251. * Returns facets that are enabled or whose delta mapping is forced.
  252. *
  253. * @param $searcher
  254. * The machine readable name of the searcher.
  255. * @param $realm_name
  256. * The machine readable name of the realm.
  257. *
  258. * @return array
  259. * A list of machine readable facet names.
  260. */
  261. function facetapi_get_delta_map_queue($searcher, $realm_name) {
  262. static $forced;
  263. if (NULL === $forced) {
  264. $forced = module_invoke_all('facetapi_force_delta_mapping');
  265. }
  266. $enabled = array_keys(facetapi_get_enabled_facets($searcher, $realm_name));
  267. if (!isset($forced[$searcher][$realm_name])) {
  268. $forced[$searcher][$realm_name] = array();
  269. }
  270. // Merges enabled facets and facets whose mapping is forced.
  271. return array_unique(array_merge($enabled, $forced[$searcher][$realm_name]));
  272. }