array( 'render element' => 'form', ), // Fieldset types. 'views_fieldsets_fieldset' => array( 'views_fieldsets_label' => '
', 'variables' => array('fieldset_fields' => array(), 'legend' => ''), 'template' => 'views-fieldsets-fieldset', 'path' => $path, ), 'views_fieldsets_div' => array( 'views_fieldsets_label' => '
', 'variables' => array('fieldset_fields' => array()), 'template' => 'views-fieldsets-div', 'path' => $path, ), 'views_fieldsets_details' => array( 'views_fieldsets_label' => '
', 'variables' => array('fieldset_fields' => array(), 'legend' => ''), 'template' => 'views-fieldsets-details', 'path' => $path, ), ); } /** * Implements hook_theme_registry_alter(). */ function views_fieldsets_theme_registry_alter(&$registry) { foreach ($registry as $hook => &$info) { // Splice in our generic preprocessor for all fieldset types. if (strpos($hook, 'views_fieldsets_') === 0 && isset($info['views_fieldsets_label'])) { // It must come directly after 'template_preprocess', or first if that doesn't exist. $index = array_search('template_preprocess', $info['preprocess functions']); if ($index !== FALSE) { array_splice($info['preprocess functions'], $index + 1, 0, '_template_preprocess_views_fieldsets'); } else { array_unshift($info['preprocess functions'], '_template_preprocess_views_fieldsets'); } } unset($info); } } /** * Find all eligible fieldset theme hooks. */ function _views_fieldsets_types() { $registry = theme_get_registry(); $types = array(); foreach ($registry as $hook => $info) { if (strpos($hook, 'views_fieldsets_') === 0 && isset($info['views_fieldsets_label'])) { $types[substr($hook, 16)] = $info['views_fieldsets_label']; } } return $types; } /** * Implements template_preprocess_views_view_table(). * * Preprocessor for Views' row style template. */ function views_fieldsets_preprocess_views_view_table(&$vars) { $view = $vars['view']; $display_handler = $view->display_handler; $fields = $display_handler->get_option('fields'); $tmp_vars = $vars; $tmp_vars['options']['default_field_elements'] = TRUE; foreach ($view->result as $index => $row) { $view->row_index = $index; template_preprocess_views_view_fields($tmp_vars); $vars['field_fields'][$index] = $tmp_vars['fields']; } $fieldsets = views_fieldsets_field_options_to_fieldsets_1d($fields); if ($fieldsets && array_filter($fieldsets)) { // Fix separators. $last_in_fieldset = FALSE; foreach ($vars['fields'] as $id => $object) { $in_fieldset = views_fieldsets_field_is_in_fieldset($id, $display_handler); if (!empty($object->separator) && !$in_fieldset && $last_in_fieldset) { $object->separator = ''; } $last_in_fieldset = $in_fieldset; } // Add collapse.js for actual fieldsets. drupal_add_js('misc/collapse.js'); drupal_add_js('misc/form.js'); $hierarchy = views_fieldsets_hierarchy_info($display_handler, $fieldsets); $vars['hierarchy'] = $hierarchy; // Remove fieldsetted fields' header. foreach (array_filter($fieldsets) as $field_name => $fieldset_name) { unset($vars['header'][$field_name]); } foreach ($vars['rows'] as $index => $row) { $view->row_index = $index; $new_row = array(); foreach ($hierarchy['tree'] as $field_name => $info_field) { if (isset($vars['fields'][$field_name])) { if (views_fieldsets_field_is_fieldset($field_name, $display_handler)) { $sub_vars = $vars; // Fake vars. $sub_vars['fields'][$field_name] = (object) array( 'handler' => $view->display_handler->handlers['field'][$field_name], 'label' => $view->display_handler->handlers['field'][$field_name]->options['label'], ); $sub_vars['fieldset_field'] = $field_name; // Saving current row index. $sub_vars['row_index'] = $index; // Recursive render of fieldsets. $new_row[$field_name] = _views_fieldsets_render__table($info_field, $sub_vars); } else { // Render single field. if (isset($row[$field_name])) { $new_row[$field_name] = $row[$field_name]; } } } } // Save new, rendered, flat row into old row. $vars['rows'][$index] = $new_row; } } } /** * Helper function to render fieldsets recursively for table row style. */ function _views_fieldsets_render__table($fields, $vars) { // Set default values. $view = $vars['view']; $display_handler = $view->display_handler; $views_field = $view->field[$vars['fieldset_field']]; $views_field_options = $views_field->options; $hide_empty = !empty($display_handler->default_display->options['style_options']['info'][$vars['fieldset_field']]['empty_column']); $content = array(); foreach ($fields->children as $child_field_name => $child_info) { if ($views_field = $vars['field_fields'][$vars['row_index']][$child_field_name]) { if (views_fieldsets_field_is_fieldset($child_field_name, $display_handler)) { $sub_vars = $vars; // Fake vars. $sub_vars['fields'][$child_field_name] = (object) array( 'handler' => $view->display_handler->handlers['field'][$child_field_name], 'label' => $view->display_handler->handlers['field'][$child_field_name]->options['label'], ); $sub_vars['fieldset_field'] = $child_field_name; $content[$child_field_name] = $views_field; $content[$child_field_name]->content = _views_fieldsets_render__table($child_info, $sub_vars); $views_field->label_html = $views_field->wrapper_prefix = $views_field->wrapper_suffix = ''; } else { // Default behaviour to render field. $content[$child_field_name] = $views_field; } } } // Checking if we need to hide fieldset. $is_empty = TRUE; foreach ($content as $field) { if (!empty($field->content)) { $is_empty = FALSE; break; } } if (!$is_empty || !$hide_empty) { $vars['fieldset_fields'] = $content; $html_tag = $views_field_options['fieldset']['type']; unset($vars['theme_hook_suggestion'], $vars['theme_hook_suggestions']); $theme_hook_suggestions = views_theme_functions('views_fieldsets_' . $html_tag, $vars['view'], $display_handler->display); $output = theme($theme_hook_suggestions, $vars); $output .= "\n"; } else { $output = ''; } return $output; } /** * Implements template_preprocess_views_view_fields(). * * Preprocessor for Views' row style template. */ function views_fieldsets_preprocess_views_view_fields(&$vars) { $view = $vars['view']; $display_handler = $view->display_handler; $fields = $display_handler->get_option('fields'); $fieldsets = views_fieldsets_field_options_to_fieldsets_1d($fields); if ($fieldsets && array_filter($fieldsets)) { // Fix separators. $last_in_fieldset = FALSE; foreach ($vars['fields'] as $id => $object) { $in_fieldset = views_fieldsets_field_is_in_fieldset($id, $display_handler); if (!empty($object->separator) && !$in_fieldset && $last_in_fieldset) { $object->separator = ''; } $last_in_fieldset = $in_fieldset; } // Add collapse.js for actual fieldsets. drupal_add_js('misc/collapse.js'); drupal_add_js('misc/form.js'); $hierarchy = views_fieldsets_hierarchy_info($display_handler, $fieldsets); $vars['hierarchy'] = $hierarchy; $new_fields = array(); foreach ($hierarchy['tree'] as $field_name => $info_field) { if (isset($vars['fields'][$field_name])) { $views_field = $vars['fields'][$field_name]; if (views_fieldsets_field_is_fieldset($field_name, $display_handler)) { // Save fieldset field name for next recursion. $sub_vars = $vars; $sub_vars['fieldset_field'] = $field_name; // Render fieldset + children. $content = views_fieldsets_render__simple($info_field->children, $sub_vars); $views_field->content = $content; $views_field->label_html = $views_field->wrapper_prefix = $views_field->wrapper_suffix = ''; $new_fields[$field_name] = $views_field; } else { // Render single field. $new_fields[$field_name] = $views_field; } } } $vars['fields'] = $new_fields; } } /** * Helper to render fieldsets. * * @see views_fieldsets_preprocess_views_view_fields__2(). */ function views_fieldsets_render__simple($fields, $vars) { $display_handler = $vars['view']->display_handler; $views_field = $vars['view']->field[$vars['fieldset_field']]; $views_field_options = $views_field->options; $hide_empty = !empty($views_field->options['hide_empty']); $content = array(); foreach ($fields as $field_name => $info_field) { if (isset($vars['fields'][$field_name])) { $views_field = $vars['fields'][$field_name]; if (views_fieldsets_field_is_fieldset($field_name, $display_handler)) { // Prep children & fieldset field options/settings. $sub_fields = !empty($info_field->children) ? $info_field->children : array(); $sub_vars = $vars; $sub_vars['fieldset_field'] = $field_name; // Render fieldset + children. $views_field->is_fieldset = TRUE; $views_field->wrapper_prefix = ''; $views_field->label_html = ''; $views_field->content = views_fieldsets_render__simple($sub_fields, $sub_vars); $views_field->wrapper_suffix = ''; } else { // Render single field. $views_field->is_fieldset = FALSE; } $content[$field_name] = $views_field; } } $is_empty = TRUE; foreach ($content as $field) { if (!$field->is_fieldset || !empty($field->content)) { $is_empty = FALSE; break; } } if (!$is_empty || !$hide_empty) { $vars['fieldset_fields'] = $content; $html_tag = $views_field_options['fieldset']['type']; unset($vars['theme_hook_suggestion'], $vars['theme_hook_suggestions']); $theme_hook_suggestions = views_theme_functions('views_fieldsets_' . $html_tag, $vars['view'], $display_handler->display); $output = theme($theme_hook_suggestions, $vars); $output .= "\n"; } else { $output = ''; } return $output; } /** * Default preprocessor for all views_fieldsets_TYPE tpls. */ function _template_preprocess_views_fieldsets(&$vars) { $view = $vars['view']; $hierarchy = $vars['hierarchy']; $fieldset_field = $vars['fieldset_field']; $views_field = $vars['fields'][$fieldset_field]; $views_field_options = $views_field->handler->options; // CSS classes. $classes = $views_field->handler->tokenize_value($views_field_options['fieldset']['classes'], $view->row_index); $classes_array = array_filter(explode(' ', $classes)); if (!$classes_array) { $classes_array[] = drupal_strtolower(drupal_clean_css_identifier($views_field->label)); } $classes_array[] = 'views-fieldset'; if (!empty($views_field_options['fieldset']['collapsible'])) { $classes_array[] = 'collapsible'; } if (!empty($views_field_options['fieldset']['collapsed'])) { $classes_array[] = 'collapsed'; } $vars['classes_array'] = $classes_array; // Fieldset legend. if (!empty($views_field_options['alter']['alter_text']) && !empty($views_field_options['alter']['text'])) { $vars['legend'] = $views_field->content; } else { $vars['legend'] = $views_field->label; } $vars['legend'] = t($vars['legend'], array(), array('context' => 'views_fieldsets')); // Attributes? $vars['attributes_array'] = array( 'data-module' => 'views_fieldsets', ); if (!empty($views_field_options['fieldset']['id'])) { $id = $views_field_options['fieldset']['id']; $id = $views_field->handler->tokenize_value($id, $view->row_index); $vars['attributes_array']['id'] = drupal_html_id($id); } } /** * Implements hook_form_FORM_ID_alter() for views_ui_edit_form(). */ function views_fieldsets_form_views_ui_edit_form_alter($form, $form_state) { drupal_add_css(drupal_get_path('module', 'views_fieldsets') . '/css/views_fieldsets.css'); } /** * Implements hook_form_views_ui_edit_display_form_alter(). */ function views_fieldsets_form_views_ui_edit_display_form_alter(&$form, &$form_state) { $view = $form_state['view']; if ($form_state['section'] == 'analyze-theme') { $funcs = array(); foreach (array('fieldset', 'div', 'details') as $tag) { $theme_hook_suggestions = views_theme_functions('views_fieldsets_' . $tag, $view, $view->display_handler->display); $funcs[] = $view->display_handler->option_link(t('Views fieldsets @tag', array('@tag' => '<' . $tag . '>')), 'analyze-theme-fieldsets-' . $tag) . ': ' . $view->display_handler->format_themes($theme_hook_suggestions); } $html = theme('item_list', array('items' => $funcs)); $form['options']['analysis']['#markup'] .= $html; } } /** * Helper to make a nice 1D fieldsets array from fields' settings. */ function views_fieldsets_field_options_to_fieldsets_1d($fields) { // Create empty 1D array with `field` => `parent`. $fieldsets = $fields ? array_combine(array_keys($fields), array_fill(0, count($fields), '')) : array(); // Fill `parent` values by iterating through all fields (only the Views Fieldset fields matter). foreach ($fields as $field_name => $field) { if (isset($field['children'])) { foreach ($field['children'] as $child_field_name) { $fieldsets[$child_field_name] = $field_name; } } } return $fieldsets; } /** * Implements hook_views_ui_display_tab_alter(). */ function views_fieldsets_views_ui_display_tab_alter(&$build, $view, $display_id) { $display_handler = &$view->display[$display_id]->handler; $fields = $display_handler->get_option('fields'); $fieldsets = views_fieldsets_field_options_to_fieldsets_1d($fields); $hierarchy = views_fieldsets_hierarchy_info($display_handler, $fieldsets); foreach (element_children($build['details']['columns']['first']['fields']['fields']) as $field_name) { $field = &$build['details']['columns']['first']['fields']['fields'][$field_name]; $field_is_fieldset = views_fieldsets_field_is_fieldset($field_name, $display_handler); if ($field_is_fieldset) { $field['#class'][] = 'views-fieldset'; } if (isset($hierarchy['fields'][$field_name])) { $field['#class'][] = 'views-fieldset-depth-' . $hierarchy['fields'][$field_name]->info->depth; } unset($field); } } /** * Helper to ... * * Establishes whether a field is a Views Fieldsets field BY ITS NAME. * * Deprecated since 2012-06-27. */ function views_fieldsets_name_is_fieldset($field_name) { return 0 < preg_match('/^fieldset(_\d+)?$/', $field_name); } /** * Helper to ... * * Establishes whether a field is a Views Fieldsets field BY ITS TYPE. */ function views_fieldsets_field_is_fieldset($field_name, $display_handler) { $handler = $display_handler->get_handler('field', $field_name); return $handler instanceof views_fieldsets_fieldset_field_handler; } /** * Helper to ... * * Establishes whether a field is IN a Views Fieldsets field. */ function views_fieldsets_field_is_in_fieldset($field_name, $display_handler) { if (empty($display_handler->_fieldsets)) { $display_handler->_fieldsets = array_filter($display_handler->handlers['field'], function ($handler) { return $handler instanceof views_fieldsets_fieldset_field_handler; }); } foreach ($display_handler->_fieldsets as $fieldset) { if (in_array($field_name, $fieldset->options['children'])) { return TRUE; } } return FALSE; } /** * Helper to ... */ function views_fieldsets_hierarchy_total_weights_set(&$hierarchy, &$fields, &$weight) { foreach ($hierarchy as $field_name => $field) { $field->info->total_weight = $weight++; $fields[$field_name] = $field; if (isset($field->children)) { views_fieldsets_hierarchy_total_weights_set($field->children, $fields, $weight); } } } /** * Helper to ... */ function views_fieldsets_hierarchy_total_weights(&$hierarchy, &$fields = array(), &$weight = 0) { views_fieldsets_hierarchy_total_weights_set($hierarchy, $fields, $weight); } /** * Helper to ... */ function views_fieldsets_hierarchy_info($display_handler, $fieldsets, $get_parent = '', $depth = 0) { $hierarchy = array(); foreach ($fieldsets as $field_name => $parent) { if ($get_parent == $parent) { $hierarchy[$field_name] = (object) array( 'info' => (object) array( 'fieldset_weight' => count($hierarchy), 'total_weight' => 0, 'depth' => $depth, 'parent' => $parent ? $parent : '', ), ); if (views_fieldsets_field_is_fieldset($field_name, $display_handler)) { $hierarchy[$field_name]->children = views_fieldsets_hierarchy_info($display_handler, $fieldsets, $field_name, $depth + 1); } } } if (!$depth) { $fields = array(); $weight = 0; views_fieldsets_hierarchy_total_weights($hierarchy, $fields, $weight); return array( 'total_weights' => array_keys($fields), 'fields' => $fields, 'tree' => $hierarchy, ); } return $hierarchy; } /** * Implements hook_form_FORM_ID_alter() for views_ui_rearrange_form(). */ function views_fieldsets_form_views_ui_rearrange_form_alter(&$form, &$form_state, $form_id = 'views_ui_rearrange_form') { $actions = array_filter(explode('/', $form['#action'])); if (!isset($form['fields']) || 'field' != end($actions)) { return; } $view = $form_state['view']; $display_handler = $view->display_handler; $fields = $display_handler->get_option('fields'); $fieldsets = views_fieldsets_field_options_to_fieldsets_1d($fields); $hierarchy = views_fieldsets_hierarchy_info($display_handler, $fieldsets); $fields = $hierarchy['fields']; // Add tabledrag related elements foreach (element_children($form['fields']) as $field_name) { if (views_fieldsets_field_is_fieldset($field_name, $display_handler)) { $form['fields'][$field_name]['name']['#markup'] = '' . $form['fields'][$field_name]['name']['#markup'] . ''; } $form['fields'][$field_name]['weight']['#size'] = 3; $form['fields'][$field_name]['field_name'] = array( '#type' => 'hidden', '#value' => $field_name, '#attributes' => array('class' => array('field-name')), ); $form['fields'][$field_name]['hierarchy'] = array( '#type' => 'hidden', '#default_value' => !empty($fields[$field_name]->info->parent) ? $fields[$field_name]->info->parent : '', '#attributes' => array('class' => array('hierarchy')), '#size' => 5, ); $form['fields'][$field_name]['depth'] = array( '#type' => 'hidden', '#default_value' => !empty($fields[$field_name]->info->depth) ? $fields[$field_name]->info->depth : '', '#attributes' => array('class' => array('depth')), '#size' => 5, ); } // To add TableDrag and visual specialties. $form['#theme'] = 'views_fieldsets_rearrange_form'; // To be used in the theme function (for views_fieldsets_field_is_fieldset()). $form['#display_handler'] = $display_handler; array_unshift($form['buttons']['submit']['#submit'], 'views_fieldsets_rearrange_form_submit'); } /** * Submit handler for views_ui_rearrange_form(). */ function views_fieldsets_rearrange_form_submit($form, &$form_state) { $values = &$form_state['values']; $view = $form_state['view']; $display_handler = $view->display_handler; $fields = $display_handler->get_option('fields'); // Sort input by (hierarchical) weight. uasort($values, 'drupal_sort_weight'); // Create simple hierarchy (field => parent) from sorted input. $fieldsets = array(); foreach ($values as $field_name => $stuff) { if (isset($fields[$field_name])) { $fieldsets[$field_name] = $stuff['hierarchy']; } } // Create advanced hierarchy from sorted simple hierarchy. $fieldsets = views_fieldsets_hierarchy_info($display_handler, $fieldsets); // Sort fields the Views way (no indentations/parents/relationships). // Why? For Views' actual submit handler (which is the one after this one). foreach ($fieldsets['total_weights'] as $weight => $field_name) { $values[$field_name]['weight'] = $weight; } // Create simple hierarchy from new sort (sorted the Views way, with parents). foreach ($fieldsets['fields'] as $field_name => $field) { // $hierarchy[$field_name] = $fields[$field_name]['views_fieldsets_parent'] = $field->info->parent; // And create hierarchy per Views Fieldset field. if (views_fieldsets_field_is_fieldset($field_name, $display_handler)) { $fields[$field_name]['children'] = !empty($field->children) ? array_keys($field->children) : array(); } } $display_handler->set_option('fields', $fields); } /** * Theme function for views_fieldsets_rearrange_form. */ function theme_views_fieldsets_rearrange_form($variables) { $form = $variables['form']; $display_handler = $form['#display_handler']; $rows = array(); foreach (element_children($form['fields']) as $id) { if (isset($form['fields'][$id]['name'])) { $field_name = $form['fields'][$id]['field_name']['#value']; $depth = $form['fields'][$id]['depth']['#default_value']; $indentation = $depth > 0 ? theme('indentation', array('size' => $depth)) : ''; $row = array(); $row[] = $indentation . drupal_render($form['fields'][$id]['name']) . drupal_render($form['fields'][$id]['hierarchy']) . drupal_render($form['fields'][$id]['depth']) . drupal_render($form['fields'][$id]['field_name']); $form['fields'][$id]['weight']['#attributes']['class'] = array('weight'); $row[] = drupal_render($form['fields'][$id]['weight']); $row[] = drupal_render($form['fields'][$id]['removed']) . l('' . t('Remove') . '', 'javascript:void()', array( 'attributes' => array( 'id' => 'views-remove-link-' . $id, 'class' => array( 'views-hidden', 'views-button-remove', 'views-remove-link', ), 'alt' => t('Remove this item'), 'title' => t('Remove this item'), ), 'html' => TRUE, )); $classes = array('draggable'); if (!views_fieldsets_field_is_fieldset($field_name, $display_handler)) { $classes[] = 'tabledrag-leaf'; } $rows[] = array( 'data' => $row, 'class' => $classes, 'id' => 'views-row-' . $id, ); } } if (empty($rows)) { $rows[] = array( array( 'data' => t('No fields available.'), 'colspan' => '2', ), ); } $header = array('', t('Weight'), t('Remove')); $output = drupal_render($form['override']); $output .= '
'; $output .= theme('table', array( 'header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'arrange'), )); $output .= '
'; $output .= drupal_render_children($form); drupal_add_tabledrag('arrange', 'match', 'parent', 'hierarchy', 'hierarchy', 'field-name', FALSE); drupal_add_tabledrag('arrange', 'depth', 'group', 'depth', NULL, NULL, FALSE); drupal_add_tabledrag('arrange', 'order', 'sibling', 'weight'); return $output; } /** * Implements hook_views_api(). */ function views_fieldsets_views_api() { return array( 'version' => 3, 'path' => drupal_get_path('module', 'views_fieldsets') . '/includes', ); }