You are here

fieldset_helper.module in Fieldset helper 6.2

Same filename and directory in other branches
  1. 6 fieldset_helper.module
  2. 7.2 fieldset_helper.module

File

fieldset_helper.module
View source
<?php

/**
 *
 * @file
 * Saves the collapsed state of a Drupal collapsible fieldset.
 *
 * Besides saving the state of collapsible fieldsets this module improves
 * the usability of Drupal's main module page (admin/build/modules) by adding
 * expand and collapse all links to top of the page.
 *
 *
 * Notes:
 *
 * - Fieldset ids are generated based on the FAPI form's associated array
 *   keys or the id is generated from the fieldset's title.
 *
 * - All generated fieldset ids will be pre-pended with 'fieldset-'.
 *
 * - All collapsible fieldsets should be generated using theme('fieldset', $element);
 *   but you can also use static HTML. (See README.txt)
 *
 *
 * Fieldset helper state manager:
 *
 * - The 'state manager' stores the state of all collapsible fieldsets in a
 *   cookie.
 *
 * - The state manager dramatically reduces the cookie's size, by converting the
 *   fieldset's element #id and its related path to an auto incremented numeric id.
 *
 * - The state management is controlled by the fieldset_helper_state_manager
 *   PHP functions and the FieldsetHelperStateManager JavaScript object which
 *   isolates the API so that it can copied, renamed, and re-used.
 *
 *
 * Related discussions
 * - @link http://drupal.org/node/114130 Is it possible to get Fieldset Collapsed/Collapsible to remember settings? @endlink
 * - @link http://drupal.org/node/209006 would be nice to save/show fieldset states @endlink
 * - @link http://drupal.org/node/198529 In modules listing: collapse fieldsets @endlink
 * - @link http://drupal.org/node/49103 Give fieldsets an id @endlink
 * - @link http://drupal.org/node/118343 Adding a collapsible fieldset to your nodes @endlink
 * - @link http://drupal.org/node/321779 Use Drupal JS Libraries : Your own collapsible fieldset @endlink
 *
 * Similar modules
 * - @link http://drupal.org/project/autosave Autosave @endlink
 * - @link http://drupal.org/project/util Utility @endlink
 */
module_load_include('inc', 'fieldset_helper', 'fieldset_helper.theme');

////////////////////////////////////////////////////////////////////////////////

// Hooks

////////////////////////////////////////////////////////////////////////////////

/**
 * Implementation of hook_perm().
 */
function fieldset_helper_perm() {
  return array(
    'save fieldset state',
  );
}

/**
 * Implementation of hook_menu().
 */
function fieldset_helper_menu() {
  $items['admin/settings/fieldset_helper'] = array(
    'title' => 'Fieldset helper',
    'description' => 'Settings to save FAPI collapsible fieldset state',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'fieldset_helper_admin_settings',
    ),
    'file' => 'fieldset_helper.admin.inc',
    'access arguments' => array(
      'administer site configuration',
    ),
    'type' => MENU_NORMAL_ITEM,
  );
  $items['admin/settings/fieldset_helper/view'] = array(
    'title' => 'Settings',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => 0,
  ) + $items['admin/settings/fieldset_helper'];
  $items['admin/settings/fieldset_helper/test'] = array(
    'title' => 'Test',
    'description' => 'Test page for saving FAPI collapsible fieldset state',
    'page callback' => 'fieldset_helper_test',
    'file' => 'fieldset_helper.admin.inc',
    'access arguments' => array(
      'administer site configuration',
    ),
    'type' => MENU_LOCAL_TASK,
    'weight' => 1,
  );
  return $items;
}

/**
 * Implementation of hook_fieldset_helper_path_alter().
 */
function fieldset_helper_fieldset_helper_path_alter(&$path) {

  // Force all node form (add and edit) fieldsets to share their state.
  if (variable_get('fieldset_helper_node_form_state', 1) == 1) {
    if (preg_match('#^(node/[^/]+/edit|node/add/[^/]+)$#', $path)) {
      $path = 'node/form';
    }
  }
}

////////////////////////////////////////////////////////////////////////////////

// Form alter functions

////////////////////////////////////////////////////////////////////////////////

/**
 * Implementation of hook_form_alter().
 */
function fieldset_helper_form_alter(&$form, $form_state, $form_id) {

  // If the $form object has an id, which will be used in the <form> tag,
  // then replace the $form_id variable.
  $form_id = isset($form['#id']) ? $form['#id'] : $form_id;
  _fieldset_helper_set_fieldset_ids_recursive($form, $form_id);
}

/**
 * Recursively set a fieldset ids based on the associated array keys.
 *
 * All fieldset id's will begin with 'fieldset-' to insure their uniqueness.
 *
 * @param &$form
 *   Nested array of form elements that comprise the form.
 * @param $form_id
 *   String representing the id of the form.
 * @param $id
 *   Based id for fieldsets.
 */
function _fieldset_helper_set_fieldset_ids_recursive(&$form, $form_id, $id = 'fieldset') {
  foreach ($form as $key => $value) {

    // If $key is a property (begins with a hash (#) then continue.
    if (strpos($key, '#') === 0) {
      continue;
    }

    // If this element has no type or it is not a fieldset then continue.
    if (!isset($form[$key]['#type']) || $form[$key]['#type'] != 'fieldset') {
      continue;
    }

    // Add key, as valid DOM id, to fieldset id.
    $fieldset_id = _fieldset_helper_format_id($id . '-' . $key);

    // Add id to the collapsible fieldset if an id is not defined.
    if (!isset($form[$key]['#attributes']['id'])) {
      $form[$key]['#attributes']['id'] = $fieldset_id;
    }

    // Recurse downward
    _fieldset_helper_set_fieldset_ids_recursive($form[$key], $form_id, $fieldset_id);
  }
}

////////////////////////////////////////////////////////////////////////////////

// General and utility functions

////////////////////////////////////////////////////////////////////////////////

/**
 * Get default collapsible attribute for the current path.
 */
function fieldset_helper_default_collapsible() {
  static $default_collapsible;
  if (isset($default_collapsible)) {
    return $default_collapsible;
  }
  $pages = variable_get('fieldset_helper_default_collapsible_pages', '*');
  if (empty($pages)) {
    $default_collapsible = FALSE;
  }
  elseif (trim($pages) == '*') {
    $default_collapsible = TRUE;
  }
  else {
    $default_collapsible = drupal_match_path($_GET['q'], $pages);
  }
  return $default_collapsible;
}

/**
 * Get default collapsed state for the current page.
 */
function fieldset_helper_default_collapsed() {
  static $default_collapsed;
  if (isset($default_collapsed)) {
    return $default_collapsed;
  }
  $pages = variable_get('fieldset_helper_default_collapsed_pages', 'admin/build/modules');
  if (empty($pages)) {
    $default_collapsed = FALSE;
  }
  elseif (trim($pages) == '*') {
    $default_collapsed = TRUE;
  }
  else {
    $default_collapsed = drupal_match_path($_GET['q'], $pages);
  }
  return $default_collapsed;
}

/**
 * Formats any string as a valid fieldset id.
 *
 * @param $text
 *   A string to be converted to a valid fieldset id.
 *
 * @return
 *   The string format as a fieldset id.
 */
function _fieldset_helper_format_id($text) {
  return form_clean_id(preg_replace('/[^a-z0-9]+/', '-', drupal_strtolower($text)));
}

////////////////////////////////////////////////////////////////////////////////

// State manager functions.

////////////////////////////////////////////////////////////////////////////////

/**
 * Check if a path matches any pattern in a set of patterns.
 *
 * @return
 *   The path to save current page's element to.
 */
function fieldset_helper_state_manager_get_path() {
  static $path;
  if (isset($path)) {
    return $path;
  }
  $path = $_GET['q'];
  if ($pages = variable_get('fieldset_helper_global_pages', '')) {
    $pages = preg_split('/\\s+/', $pages);
    foreach ($pages as $page) {
      $pattern = '/^' . str_replace('\\*', '.*', preg_quote($page, '/')) . '$/';
      if (preg_match($pattern, $path)) {
        $path = $page;
        break;
      }
    }
  }

  // Run all hook implementations for hook_fieldset_helper_path_alter().
  foreach (module_implements('fieldset_helper_path_alter') as $module) {
    $function = $module . '_fieldset_helper_path_alter';
    $function($path);
  }
  return $path;
}

/**
 * Check if a fieldset id matches any global id patterns.
 *
 * @param $element_id
 *   The DOM element id.
 * @param $path
 *   Default path if no match is found.
 * @return
 *   The elements path (*, $_GET['q'], or custom).
 */
function fieldset_helper_state_manager_get_element_path($element_id, $path) {
  static $global_patterns;
  if (!isset($global_patterns)) {
    $global_ids = variable_get('fieldset_helper_global_ids', '');
    $global_patterns = '/^(' . preg_replace(array(
      '/(\\r\\n?|\\n)/',
      '/\\\\\\*/',
    ), array(
      '|',
      '.*',
    ), preg_quote($global_ids, '/')) . ')$/';
  }
  $path = preg_match($global_patterns, $element_id) ? '*' : $path;

  // Run all hook implementations for hook_fieldset_helper_element_path_alter().
  foreach (module_implements('fieldset_helper_element_path_alter') as $module) {
    $function = $module . '_fieldset_helper_element_path_alter';
    $function($element_id, $path);
  }
  return $path;
}

/**
 * Get the lookup id for the $element_id in the current path.
 *
 * @param $element_id
 *   The DOM element id.
 * @return
 *   The numeric auto generated look up id for the $element_id. If $element_id
 *   is not set then the entire lookup id table for the current page will returned.
 */
function fieldset_helper_state_manager_get_lookup_id($element_id = NULL) {
  static $lookup_id_table;
  $path = fieldset_helper_state_manager_get_path();

  // Load existing lookup ids for the current path from the database.
  if (!isset($lookup_id_table)) {

    // Fetch lookup records for the current path. Use sorting to make sure global path (*) are last.
    $query = "SELECT id, element_id FROM {fieldset_helper_state_manager} WHERE path='%s' OR path='*' ORDER BY path DESC ";
    $result = db_query($query, $path);
    while ($data = db_fetch_array($result)) {
      $lookup_id_table[$data['element_id']] = $data['id'];
    }
  }

  // Create a new lookup id for element_id's not associated with the lookup id table.
  if ($element_id != NULL && !isset($lookup_id_table[$element_id])) {

    // Get element path.
    $element_path = fieldset_helper_state_manager_get_element_path($element_id, $path);

    // Get id for path and element_id combination.
    $sql = "INSERT INTO {fieldset_helper_state_manager} (path, element_id) VALUES ('%s', '%s')";
    db_query($sql, $element_path, $element_id);
    $lookup_id = db_last_insert_id('fieldset_helper_state_manager', 'id');
    $lookup_id_table[$element_id] = $lookup_id;
  }

  // Return the look up id for the element id.
  return $element_id == NULL ? $lookup_id_table : $lookup_id_table[$element_id];
}

/**
 * Clear all the store lookup id for every form.
 */
function fieldset_helper_state_manager_clear_lookup_ids() {
  drupal_set_message(t('Fieldset lookup ids cleared.'));
  db_query("DELETE FROM {fieldset_helper_state_manager}");
}

/**
 * Get an associated array for lookup id and the element's state (1 or 0) from $_COOKIE['fieldset_helper_state_manager'].
 *
 * @param $clear
 *   Optional boolean when set to TRUE will clear any cached cookie states.
 */
function fieldset_helper_state_manager_get_cookie_states($clear = FALSE) {
  static $states;
  if (isset($states) && $clear == FALSE) {
    return $states;
  }
  $states = array();
  if (!isset($_COOKIE['fieldset_helper_state_manager'])) {
    return $states;
  }
  else {
    $values = explode('_', $_COOKIE['fieldset_helper_state_manager']);
    foreach ($values as $value) {
      $params = explode('.', $value);
      $states[$params[0]] = $params[1] == '1' ? TRUE : FALSE;
    }
    return $states;
  }
}

/**
 * Get fieldset's collapsed state.
 *
 * @param $element_id
 *   The DOM element id.
 * @param $default_value
 *   Boolean for default state value
 */
function fieldset_helper_state_manager_get_state($element_id, $default_value = FALSE) {

  // Always return the default value is state management is disabled for anonymous.
  if (user_is_anonymous() && variable_get('fieldset_helper_disable_state_anonymous', 0) == 1) {
    return $default_value;
  }

  // Get fieldset states and lookup ids
  $states = fieldset_helper_state_manager_get_cookie_states();
  $lookup_id = fieldset_helper_state_manager_get_lookup_id($element_id);

  // Return collapsed boolean value.
  if (isset($states[$lookup_id])) {
    return $states[$lookup_id] ? TRUE : FALSE;
  }
  else {
    return $default_value ? TRUE : FALSE;
  }
}

Functions

Namesort descending Description
fieldset_helper_default_collapsed Get default collapsed state for the current page.
fieldset_helper_default_collapsible Get default collapsible attribute for the current path.
fieldset_helper_fieldset_helper_path_alter Implementation of hook_fieldset_helper_path_alter().
fieldset_helper_form_alter Implementation of hook_form_alter().
fieldset_helper_menu Implementation of hook_menu().
fieldset_helper_perm Implementation of hook_perm().
fieldset_helper_state_manager_clear_lookup_ids Clear all the store lookup id for every form.
fieldset_helper_state_manager_get_cookie_states Get an associated array for lookup id and the element's state (1 or 0) from $_COOKIE['fieldset_helper_state_manager'].
fieldset_helper_state_manager_get_element_path Check if a fieldset id matches any global id patterns.
fieldset_helper_state_manager_get_lookup_id Get the lookup id for the $element_id in the current path.
fieldset_helper_state_manager_get_path Check if a path matches any pattern in a set of patterns.
fieldset_helper_state_manager_get_state Get fieldset's collapsed state.
_fieldset_helper_format_id Formats any string as a valid fieldset id.
_fieldset_helper_set_fieldset_ids_recursive Recursively set a fieldset ids based on the associated array keys.