Commit 2b37e008 authored by Daniel Kudwien's avatar Daniel Kudwien
Browse files

- #991454 by sun: Added element #type table (with tableselect and tabledrag support).

parent 77a9d4cb
Loading
Loading
Loading
Loading
+154 −3
Original line number Diff line number Diff line
@@ -12,8 +12,20 @@ function edge_element_info() {
  $types['table'] = array(
    '#header' => array(),
    '#rows' => array(),
    '#empty' => '',
    '#pre_render' => array('edge_pre_render_table'),
    '#theme' => 'table',
    // Properties for tableselect support.
    '#tableselect' => FALSE,
    '#input' => TRUE,
    '#value_callback' => 'edge_type_table_value',
    '#tree' => TRUE,
    // Note that MODULE_process_THEME() clashes with global theme system process
    // callbacks.
    '#process' => array('edge_element_process_table'),
    '#element_validate' => array('edge_element_validate_table'),
    '#multiple' => TRUE,
    '#js_select' => TRUE,
  );
  return $types;
}
@@ -83,6 +95,145 @@ function edge_css_alter(&$css) {
  }
}

/**
 * Helper function to determine the value of a table form element.
 *
 * @param $element
 *   The form element whose value is being populated.
 * @param $input
 *   The incoming input to populate the form element. If this is FALSE,
 *   the element's default value should be returned.
 *
 * @return
 *   The data that will appear in the $form_state['values'] collection
 *   for this element. Return nothing to use the default.
 */
function edge_type_table_value($element, $input = FALSE) {
  // If #multiple is FALSE, the regular default value of radio buttons is used.
  if (!empty($element['#multiple'])) {
    // Contrary to #type 'checkboxes', the default value of checkboxes in a
    // table is built from the array keys (instead of array values) of the
    // #default_value property.
    // @todo D8: Remove this weirdness.
    if ($input === FALSE) {
      $element += array('#default_value' => array());
      return drupal_map_assoc(array_keys(array_filter($element['#default_value'])));
    }
    else {
      return is_array($input) ? drupal_map_assoc($input) : array();
    }
  }
}

/**
 * #process callback for #type 'table' to add tableselect support.
 *
 * @see form_process_tableselect()
 * @see theme_tableselect()
 */
function edge_element_process_table($element, &$form_state) {
  if ($element['#tableselect']) {
    if ($element['#multiple']) {
      $value = is_array($element['#value']) ? $element['#value'] : array();
    }
    // Advanced selection behaviour makes no sense for radios.
    else {
      $element['#js_select'] = FALSE;
    }
    // Add a "Select all" checkbox column to the header.
    // @todo D8: Rename into #select_all?
    if ($element['#js_select']) {
      $element['#attached']['js'][] = 'misc/tableselect.js';
      array_unshift($element['#header'], array('class' => array('select-all')));
    }
    // Add an empty header column for radio buttons or when a "Select all"
    // checkbox is not desired.
    else {
      array_unshift($element['#header'], '');
    }

    // @todo FIXME
    if (!isset($element['#default_value']) || $element['#default_value'] === 0) {
      $element['#default_value'] = array();
    }
    // Create a checkbox or radio for each row in a way that the value of the
    // tableselect element behaves as if it had been of #type checkboxes or
    // radios.
    foreach (element_children($element) as $key) {
      // Do not overwrite manually created children.
      if (!isset($element[$key]['select'])) {
        // Determine option label; either an assumed 'title' column, or the
        // first available column containing a #title or #markup.
        // @todo Consider to add an optional $element[$key]['#title_key']
        //   defaulting to 'title'?
        $title = '';
        if (!empty($element[$key]['title']['#title'])) {
          $title = $element[$key]['title']['#title'];
        }
        else {
          // @todo Performance-optimization.
          foreach (element_children($element[$key]) as $column) {
            if (isset($element[$key][$column]['#title'])) {
              $title = $element[$key][$column]['#title'];
              break;
            }
            if (isset($element[$key][$column]['#markup'])) {
              $title = $element[$key][$column]['#markup'];
              break;
            }
          }
        }
        if ($title !== '') {
          $title = t('Update !title', array('!title' => $title));
        }

        // Prepend the select column to existing columns.
        $element[$key] = array('select' => array()) + $element[$key];
        $element[$key]['select'] += array(
          '#type' => $element['#multiple'] ? 'checkbox' : 'radio',
          '#title' => $title,
          '#title_display' => 'invisible',
          // @todo If rows happen to use numeric indexes instead of string keys,
          //   this results in a first row with $key === 0, which is always FALSE.
          '#return_value' => $key,
          '#attributes' => $element['#attributes'],
        );
        $element_parents = array_merge($element['#parents'], array($key));
        if ($element['#multiple']) {
          $element[$key]['select']['#default_value'] = isset($value[$key]) ? $key : NULL;
          $element[$key]['select']['#parents'] = $element_parents;
        }
        else {
          $element[$key]['select']['#default_value'] = ($element['#default_value'] == $key ? $key : NULL);
          $element[$key]['select']['#parents'] = $element['#parents'];
          $element[$key]['select']['#id'] = drupal_html_id('edit-' . implode('-', $element_parents));
        }
      }
    }
  }

  return $element;
}

/**
 * #element_validate callback for #type 'table'.
 */
function edge_element_validate_table($element, &$form_state) {
  $error = FALSE;
  if ($element['#multiple']) {
    if (!is_array($element['#value']) || !count(array_filter($element['#value']))) {
      $error = TRUE;
    }
  }
  elseif (!isset($element['#value']) || $element['#value'] === '') {
    $error = TRUE;
  }
  // @todo Add dependency on #required?
  if ($error) {
    form_error($element, t('No items selected.'));
  }
}

/**
 * #pre_render callback to transform children of an element into #rows suitable for theme_table().
 *
@@ -148,6 +299,8 @@ function edge_pre_render_table($element) {
      $row += $element[$first]['#attributes'];
    }
    // Turn second-level elements into table row columns.
    // @todo Do not render a cell for children of #type 'value'.
    // @see http://drupal.org/node/1248940
    foreach (element_children($element[$first]) as $second) {
      // Assign the element by reference, so any potential changes to the
      // original element are taken over.
@@ -157,9 +310,7 @@ function edge_pre_render_table($element) {
  }

  // Take over $element['#id'] as HTML ID attribute, if not already set.
  if (!isset($element['#attributes']['id']) && !empty($element['#id'])) {
    $element['#attributes']['id'] = $element['#id'];
  }
  element_set_attributes($element, array('id'));

  // If the custom #attached][tabledrag] is set and there is a HTML ID, convert
  // it into #attached][drupal_add_tabledrag] and inject the HTML ID as first