hs_content_taxonomy_views.module

Tracking 5.x-3.x branch
  1. drupal
    1. 5 contributions/hierarchical_select/modules/hs_content_taxonomy_views.module

Implementation of the Hierarchical Select API for the Content Taxonomy Views module.

Functions & methods

NameDescription
hs_content_taxonomy_views_common_config_form_submitAdditional submit callback to redirect the user to the "Edit view" form.
hs_content_taxonomy_views_config_formForm definition; configuration form for Hierarchical Select as the widget for a Taxonomy exposed filter.
hs_content_taxonomy_views_form_alterImplementation of hook_form_alter().
hs_content_taxonomy_views_hierarchical_select_childrenImplementation of hook_hierarchical_select_children().
hs_content_taxonomy_views_hierarchical_select_config_infoImplementation of hook_hierarchical_select_config_info().
hs_content_taxonomy_views_hierarchical_select_entity_countImplementation of hook_hierarchical_select_entity_count().
hs_content_taxonomy_views_hierarchical_select_implementation_infoImplementation of hook_hierarchical_select_implementation_info().
hs_content_taxonomy_views_hierarchical_select_item_get_labelImplementation of hook_hierarchical_select_item_get_label().
hs_content_taxonomy_views_hierarchical_select_lineageImplementation of hook_hierarchical_select_lineage().
hs_content_taxonomy_views_hierarchical_select_paramsImplementation of hook_hierarchical_select_params().
hs_content_taxonomy_views_hierarchical_select_root_levelImplementation of hook_hierarchical_select_root_level().
hs_content_taxonomy_views_hierarchical_select_valid_itemImplementation of hook_hierarchical_select_valid_item().
hs_content_taxonomy_views_menuImplementation of hook_menu().
hs_content_taxonomy_views_requirementsImplementation of hook_requirements().
_hs_content_taxonomy_views_parse_fieldname_from_idGiven an id of the form "content_taxonomy_<field name>.tid", get the field name.

File

View source
  1. <?php
  2. /**
  3. * @file
  4. * Implementation of the Hierarchical Select API for the Content Taxonomy
  5. * Views module.
  6. */
  7. //----------------------------------------------------------------------------
  8. // Core hooks.
  9. /**
  10. * Implementation of hook_menu().
  11. */
  12. function hs_content_taxonomy_views_menu($may_cache) {
  13. $items = array();
  14. if (!$may_cache && arg(0) == 'admin' && arg(1) == 'build' && arg(2) == 'views' && is_string(arg(3)) && arg(4) == 'hs_config' && is_string(arg(5))) {
  15. $view_name = arg(3);
  16. $field_name = arg(5);
  17. $widget_type = db_result(db_query("SELECT widget_type FROM {node_field_instance} WHERE field_name = '%s'", $field_name));
  18. if ($widget_type == 'content_taxonomy_hs') {
  19. $items[] = array(
  20. 'path' => "admin/build/views/$view_name/hs_config/$field_name",
  21. 'title' => t('Hierarchical Select configuration for !view', array('!view' => $view_name)),
  22. 'callback' => 'drupal_get_form',
  23. 'callback arguments' => array('hs_content_taxonomy_views_config_form', $view_name, $field_name),
  24. 'access' => user_access('administer views'),
  25. 'type' => MENU_NORMAL_ITEM,
  26. );
  27. }
  28. }
  29. return $items;
  30. }
  31. /**
  32. * Implementation of hook_form_alter().
  33. */
  34. function hs_content_taxonomy_views_form_alter($form_id, &$form) {
  35. // Change the exposed filters of Views. Only affects hierarchical vocabulary
  36. // filters.
  37. if (in_array($form_id, array('views_filters', 'views_filterblock'))) {
  38. $hs_exposed_filters_found = 0;
  39. // Find the ids and vocabulary ids of the exposed filters.
  40. foreach ($form['view']['#value']->exposed_filter as $id => $filter) {
  41. $field_name = _hs_content_taxonomy_views_parse_fieldname_from_id($filter['id']);
  42. if ($field_name !== FALSE) {
  43. $widget_type = db_result(db_query("SELECT widget_type FROM {node_field_instance} WHERE field_name = '%s'", $field_name));
  44. // Only apply Hierarchical Select if it's enabled for this field.
  45. if ($widget_type == 'content_taxonomy_hs') {
  46. $hs_exposed_filters_found++;
  47. $field = content_fields($field_name);
  48. $vid = $field['vid']; // This is the vocabulary id, not the view id!
  49. $tid = $field['tid'];
  50. $view = $form['view']['#value'];
  51. // Make it use a hierarchical select.
  52. require_once(drupal_get_path('module', 'hierarchical_select') .'/includes/common.inc');
  53. unset($form["filter$id"]['#size']);
  54. unset($form["filter$id"]['#options']);
  55. unset($form["filter$id"]['#theme']);
  56. $form["filter$id"]['#type'] = 'hierarchical_select';
  57. $defaults_override = array(
  58. 'module' => 'hs_content_taxonomy_views',
  59. 'params' => array(
  60. 'optional' => (bool) $view->exposed_filter[$id]['optional'],
  61. 'vid' => $vid,
  62. 'tid' => $tid,
  63. 'depth' => min($field['depth'], _hs_taxonomy_hierarchical_select_get_depth($vid)),
  64. ),
  65. // When the **ALL** option is selected, nothing else should be.
  66. 'exclusive_lineages' => array('**ALL**'),
  67. // This is a GET form, so also render the flat select.
  68. 'render_flat_select' => 1,
  69. );
  70. hierarchical_select_common_config_apply($form["filter$id"], "content-taxonomy-views-$view->name-$field_name", $defaults_override);
  71. // Inherit #required from the exposed filter settings.
  72. $form["filter$id"]['#required'] = !((bool) $view->exposed_filter[$id]['optional']);
  73. // Put the altered exposed filters in a separate table row.
  74. hierarchical_select_common_views_exposed_filters_reposition();
  75. }
  76. }
  77. }
  78. if ($hs_exposed_filters_found > 0) {
  79. // Views will remove the form_id in views_filters_process(), but we need
  80. // it for Hierarchical Select to work, so put it back.
  81. $form['copy_of_form_id'] = $form['form_id'] + array('#parents' => array('form_id'));
  82. }
  83. }
  84. // Alter the edit view form: add a link to the Hierarchical Select
  85. // configuration when appropriate and to mark which settings are now managed
  86. // by the Hierarchical Select configuration.
  87. if ($form_id == 'views_edit_view') {
  88. foreach ($form['exposed_filter'] as $filter_id => $filter) {
  89. // If $filter['field'] is not set, the exposed filter is being deleted.
  90. if (is_numeric($filter_id) && isset($filter['field'])) {
  91. $id = $form['exposed_filter'][$filter_id]['id']['#default_value'];
  92. $field_name = _hs_content_taxonomy_views_parse_fieldname_from_id($id);
  93. if ($field_name !== FALSE) {
  94. $widget_type = db_result(db_query("SELECT widget_type FROM {node_field_instance} WHERE field_name = '%s'", $field_name));
  95. if ($widget_type == 'content_taxonomy_hs') {
  96. $view = $form['#parameters'][1];
  97. $link = l(t('Configure Hierarchical Select'), "admin/build/views/$view->name/hs_config/$field_name");
  98. $form['exposed_filter'][$filter_id]['name']['#value'] .= '<br />'. $link;
  99. // Alter the form to support the current Hierarchical Select
  100. // config when …
  101. require_once(drupal_get_path('module', 'hierarchical_select') .'/includes/common.inc');
  102. $config_id = "taxonomy-views-$view->name-$vid";
  103. $config = hierarchical_select_common_config_get($config_id);
  104. $text = t('This setting is now managed by the<br />Hierarchical Select configuration!');
  105. // Exposed filter's "Force single" setting.
  106. $form['exposed_filter'][$filter_id]['single']['#description'] = $text;
  107. // Additional settings when save_lineage is enabled.
  108. if ($config['save_lineage']) {
  109. // Filter's "Operator" setting.
  110. $form['filter'][$filter_id]['operator']['#description'] = $text;
  111. // Exposed filter's "Lock Operator" setting.
  112. $form['exposed_filter'][$filter_id]['operator']['#description'] = $text;
  113. }
  114. }
  115. }
  116. }
  117. }
  118. }
  119. }
  120. /**
  121. * Implementation of hook_requirements().
  122. */
  123. function hs_content_taxonomy_views_requirements($phase) {
  124. $requirements = array();
  125. if ($phase == 'runtime') {
  126. $pattern = <<<EOT
  127. function _views_build_filters_form(\$view) {
  128. // When the form is retrieved through an AJAX callback, the cache hasn't
  129. // been loaded yet. The cache is necesssary for _views_get_filters().
  130. views_load_cache();
  131. EOT;
  132. $views_with_patch_257004 = preg_match('#'. preg_quote($pattern) .'#m', file_get_contents(drupal_get_path('module', 'views') .'/views.module'));
  133. if ($views_with_patch_257004) {
  134. $value = t('The Views module is new enough.');
  135. $description = '';
  136. $severity = REQUIREMENT_OK;
  137. }
  138. else {
  139. $value = t('The Views module is outdated.');
  140. $description = t("The version of Views that you have installed is either
  141. older than May 11, 2008, or doesn't have the obligatory patch applied.
  142. Please apply the <a href=\"!patch_url\">patch</a> or update to a newer
  143. version of the Views module!",
  144. array('!patch_url' => 'http://drupal.org/files/issues/hs_compatibility.patch')
  145. );
  146. $severity = REQUIREMENT_ERROR;
  147. }
  148. $requirements['hs_content_taxonomy_views'] = array(
  149. 'title' => t('Hierarchical Select Content Taxonomy Views'),
  150. 'value' => $value,
  151. 'description' => $description,
  152. 'severity' => $severity,
  153. );
  154. }
  155. return $requirements;
  156. }
  157. //----------------------------------------------------------------------------
  158. // Forms API callbacks.
  159. /**
  160. * Form definition; configuration form for Hierarchical Select as the widget
  161. * for a Taxonomy exposed filter.
  162. *
  163. * @param $view_name
  164. * Name of a view. Provides necessary context.
  165. * @param $field_name
  166. * Name of a field. Provides necessary context.
  167. */
  168. function hs_content_taxonomy_views_config_form($view_name, $field_name) {
  169. require_once(drupal_get_path('module', 'hierarchical_select') .'/includes/common.inc');
  170. // Find the exposed filter, we need this to set the default value of
  171. // $config['dropbox']['status'].
  172. $view = views_get_view($view_name);
  173. foreach ($view->exposed_filter as $filter) {
  174. if ($filter['id'] == "content_taxonomy_$field_name.tid") {
  175. $exposed_filter = $filter;
  176. break;
  177. }
  178. }
  179. $field = content_fields($field_name);
  180. // Extract the necessary context from the $field array.
  181. $vid = $field['vid'];
  182. $tid = $field['tid'];
  183. $depth = (empty($field['depth'])) ? 0 : $field['depth'];
  184. // Add the Hierarchical Select config form.
  185. $module = 'hs_content_taxonomy_views';
  186. $params = array(
  187. 'vid' => $vid,
  188. 'tid' => $tid,
  189. 'depth' => $depth,
  190. 'optional' => (bool) $exposed_filter['optional'],
  191. );
  192. $config_id = "content-taxonomy-views-$view_name-$field_name";
  193. $vocabulary = taxonomy_get_vocabulary($vid);
  194. $defaults = array(
  195. // Enable the save_lineage setting by default if the multiple parents
  196. // vocabulary option is enabled.
  197. 'save_lineage' => (int) ($vocabulary->hierarchy == 2),
  198. 'dropbox' => array(
  199. 'status' => !$exposed_filter['single'],
  200. ),
  201. 'editability' => array(
  202. 'max_levels' => min($depth, _hs_taxonomy_hierarchical_select_get_depth($vid)),
  203. ),
  204. );
  205. $strings = array(
  206. 'hierarchy' => t('vocabulary'),
  207. 'hierarchies' => t('vocabularies'),
  208. 'item' => t('term'),
  209. 'items' => t('terms'),
  210. 'item_type' => t('term type'),
  211. 'entity' => t('node'),
  212. 'entities' => t('nodes'),
  213. );
  214. $max_hierarchy_depth = min(($depth == 0) ? 9 : $depth, _hs_taxonomy_hierarchical_select_get_depth($vid));
  215. $preview_is_required = !(bool)$exposed_filter['optional'];
  216. $form['hierarchical_select_config'] = hierarchical_select_common_config_form($module, $params, $config_id, $defaults, $strings, $max_hierarchy_depth, $preview_is_required);
  217. $form['link'] = array(
  218. '#value' => l('Back to the view configuration', "admin/build/views/$view_name/edit"),
  219. '#prefix' => '<div class="hierarchical-select-config-back-link">',
  220. '#suffix' => '</div>',
  221. '#weight' => -5,
  222. );
  223. $form['save'] = array(
  224. '#type' => 'submit',
  225. '#value' => t('Save'),
  226. );
  227. // Add the the submit handler for the Hierarchical Select config form.
  228. $parents = array('hierarchical_select_config');
  229. $form['#submit']['hierarchical_select_common_config_form_submit'] = array($parents);
  230. $form['#submit']['hs_content_taxonomy_views_common_config_form_submit'] = array($view_name, $field_name);
  231. return $form;
  232. }
  233. /**
  234. * Additional submit callback to redirect the user to the "Edit view" form.
  235. * @param $form_id
  236. * @param $form_values
  237. * @param $view_name
  238. * Name of a view. Provides necessary context.
  239. * @param $field_name
  240. * Name of a field. Provides necessary context.
  241. */
  242. function hs_content_taxonomy_views_common_config_form_submit($form_id, $form_values, $view_name, $field_name) {
  243. $view_id = db_result(db_query("SELECT vid FROM {view_view} WHERE name = '%s'", $view_name));
  244. $field = 'content_'. $field_name .'.'. $field_name .'_value';
  245. $filter = db_fetch_object(db_query("SELECT operator FROM {view_filter} WHERE vid = %d AND field = '%s'", $view_id, $field));
  246. $exposed_filter = db_fetch_object(db_query("SELECT operator, single FROM {view_exposed_filter} WHERE vid = %d AND field = '%s'", $view_id, $field));
  247. // Overrides when save_lineage is enabled.
  248. if ($form_values['hierarchical_select_config']['save_lineage']) {
  249. // "Operator" must always be 'AND'.
  250. $filter->operator = 'AND';
  251. // The exposed filter must be locked to 'AND'.
  252. $exposed_filter->operator = 1;
  253. // "Force single" must be disabled.
  254. $exposed_filter->single = 0;
  255. }
  256. else {
  257. // "Force single" must be enabled.
  258. $exposed_filter->single = 1;
  259. }
  260. // Overrides when the dropbox is enabled.
  261. if ($form_values['hierarchical_select_config']['dropbox']['status']) {
  262. // "Force single" must be disabled.
  263. $exposed_filter->single = 0;
  264. }
  265. if ($view_id === FALSE) {
  266. drupal_set_message(t("Could not update the view because it doesn't live in the database."), 'error');
  267. }
  268. else {
  269. db_query("UPDATE {view_filter} SET operator = '%s' WHERE vid = %d AND field = '%s'", $filter->operator, $view_id, $field);
  270. db_query("UPDATE {view_exposed_filter} SET operator = %d, single = %d WHERE vid = %d AND field = '%s'", $exposed_filter->operator, $exposed_filter->single, $view_id, $field);
  271. cache_clear_all('views_urls', 'cache_views');
  272. drupal_set_message(t("Updated the View's exposed filter according to the settings you made."));
  273. }
  274. }
  275. //----------------------------------------------------------------------------
  276. // Hierarchical Select hooks.
  277. /**
  278. * Implementation of hook_hierarchical_select_params().
  279. */
  280. function hs_content_taxonomy_views_hierarchical_select_params() {
  281. return array('optional') + hs_content_taxonomy_hierarchical_select_params();
  282. }
  283. /**
  284. * Implementation of hook_hierarchical_select_root_level().
  285. */
  286. function hs_content_taxonomy_views_hierarchical_select_root_level($params) {
  287. $root_level = ($params['optional']) ? array('**ALL**' => '<'. t('all') .'>') : array();
  288. $root_level += hs_content_taxonomy_hierarchical_select_root_level($params);
  289. return $root_level;
  290. }
  291. /**
  292. * Implementation of hook_hierarchical_select_children().
  293. */
  294. function hs_content_taxonomy_views_hierarchical_select_children($parent, $params) {
  295. return ($parent == '**ALL**') ? array() : hs_content_taxonomy_hierarchical_select_children($parent, $params);
  296. }
  297. /**
  298. * Implementation of hook_hierarchical_select_lineage().
  299. */
  300. function hs_content_taxonomy_views_hierarchical_select_lineage($item, $params) {
  301. return ($item == '**ALL**') ? array($item) : hs_content_taxonomy_hierarchical_select_lineage($item, $params);
  302. }
  303. /**
  304. * Implementation of hook_hierarchical_select_valid_item().
  305. */
  306. function hs_content_taxonomy_views_hierarchical_select_valid_item($item, $params) {
  307. return ($item == '**ALL**' || hs_content_taxonomy_hierarchical_select_valid_item($item, $params));
  308. }
  309. /**
  310. * Implementation of hook_hierarchical_select_item_get_label().
  311. */
  312. function hs_content_taxonomy_views_hierarchical_select_item_get_label($item, $params) {
  313. return ($item == '**ALL**') ? '<'. t('all') .'>' : hs_content_taxonomy_hierarchical_select_item_get_label($item, $params);
  314. }
  315. /**
  316. * Implementation of hook_hierarchical_select_create_item().
  317. */
  318. // No implementation. This doesn't make sense for exposed filters: if you were
  319. // able to create new items in the hierarchy, how could you then possibly find
  320. // anything for that item?
  321. /**
  322. * Implementation of hook_hierarchical_select_entity_count().
  323. */
  324. function hs_content_taxonomy_views_hierarchical_select_entity_count($item, $params) {
  325. if ($item == '**ALL**') {
  326. // Unlike in the hs_taxonomy_views implementation, we can't use a simple
  327. // SQL query here, because no depth information is stored in the database.
  328. return count(_hs_taxonomy_hierarchical_select_get_tree($params['vid'], 0, -1, $params['depth']));
  329. }
  330. else {
  331. return hs_content_taxonomy_hierarchical_select_entity_count($item, $params);
  332. }
  333. }
  334. /**
  335. * Implementation of hook_hierarchical_select_implementation_info().
  336. */
  337. function hs_content_taxonomy_views_hierarchical_select_implementation_info() {
  338. return array(
  339. 'hierarchy type' => t('Content Taxonomy'),
  340. 'entity type' => t('Node'),
  341. );
  342. }
  343. /**
  344. * Implementation of hook_hierarchical_select_config_info().
  345. */
  346. function hs_content_taxonomy_views_hierarchical_select_config_info() {
  347. static $config_info;
  348. if (!isset($config_info)) {
  349. $config_info = array();
  350. views_load_cache();
  351. $result = db_query("SELECT vid, name FROM {view_view} ORDER BY name");
  352. while ($view = db_fetch_object($result)) {
  353. $view = views_get_view($view->name);
  354. foreach ($view->exposed_filter as $filter_id => $filter) {
  355. $field_name = _hs_content_taxonomy_views_parse_fieldname_from_id($filter['id']);
  356. if ($field_name) {
  357. $field = content_fields($field_name);
  358. $vocabulary = taxonomy_get_vocabulary($field['vid']);
  359. $config_id = "content-taxonomy-views-$view->name-$field_name";
  360. $config_info[$config_id] = array(
  361. 'config_id' => $config_id,
  362. 'hierarchy type' => t('Content Taxonomy'),
  363. 'hierarchy' => t($vocabulary->name) ." ($field_name)",
  364. 'entity type' => t('Node'),
  365. 'entity' => '',
  366. 'context type' => t('Views exposed filter'),
  367. 'context' => t($view->name),
  368. 'edit link' => "admin/build/views/$view->name/hs_config/$field_name",
  369. );
  370. }
  371. }
  372. }
  373. }
  374. return $config_info;
  375. }
  376. /**
  377. * Given an id of the form "content_taxonomy_<field name>.tid", get the
  378. * field name.
  379. *
  380. * @return
  381. * When no valid id was given, FALSE, otherwise the field name.
  382. */
  383. function _hs_content_taxonomy_views_parse_fieldname_from_id($id) {
  384. $field_name = FALSE;
  385. // When "save as tag": content_taxonomy_field_<field_name>.tid
  386. // Other save options: content_field_<field_name>.<field_name>_value
  387. if (preg_match("/(content|content_taxonomy)_(field_[A-Za-z0-9_]+)\.(\\2_value|tid)/", $id, $matches)) {
  388. $field_name = $matches[2];
  389. }
  390. return $field_name;
  391. }