Commit 27545d65 authored by webchick's avatar webchick

Issue #80855 by sun, vangorra, dman, Damien Tournoud, kkaefer: Add element...

Issue #80855 by sun, vangorra, dman, Damien Tournoud, kkaefer: Add element #type table and merge tableselect/tabledrag into it.
parent 27637ac5
......@@ -2347,6 +2347,36 @@ function form_type_checkboxes_value($element, $input = FALSE) {
}
}
/**
* Determines the value of a table form element.
*
* @param array $element
* The form element whose value is being populated.
* @param array|false $input
* The incoming input to populate the form element. If this is FALSE,
* the element's default value should be returned.
*
* @return array
* The data that will appear in the $form_state['values'] collection
* for this element. Return nothing to use the default.
*/
function form_type_table_value(array $element, $input = FALSE) {
// If #multiple is FALSE, the regular default value of radio buttons is used.
if (!empty($element['#tableselect']) && !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 inconsistency.
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();
}
}
}
/**
* Form value callback: Determines the value for a #type radios form element.
*
......@@ -3566,6 +3596,128 @@ function form_process_tableselect($element) {
return $element;
}
/**
* #process callback for #type 'table' to add tableselect support.
*
* @param array $element
* An associative array containing the properties and children of the
* table element.
* @param array $form_state
* The current state of the form.
*
* @return array
* The processed element.
*
* @see form_process_tableselect()
* @see theme_tableselect()
*/
function form_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']['library'][] = array('system', 'drupal.tableselect');
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'], '');
}
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 {
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'.
*
* @param array $element
* An associative array containing the properties and children of the
* table element.
* @param array $form_state
* The current state of the form.
*/
function form_validate_table($element, &$form_state) {
// Skip this validation if the button to submit the form does not require
// selected table row data.
if (empty($form_state['triggering_element']['#tableselect'])) {
return;
}
if ($element['#multiple']) {
if (!is_array($element['#value']) || !count(array_filter($element['#value']))) {
form_error($element, t('No items selected.'));
}
}
elseif (!isset($element['#value']) || $element['#value'] === '') {
form_error($element, t('No item selected.'));
}
}
/**
* Processes a machine-readable name form element.
*
......
......@@ -1852,6 +1852,99 @@ function theme_breadcrumb($variables) {
return $output;
}
/**
* #pre_render callback to transform children of an element into #rows suitable for theme_table().
*
* This function converts sub-elements of an element of #type 'table' to be
* suitable for theme_table():
* - The first level of sub-elements are table rows. Only the #attributes
* property is taken into account.
* - The second level of sub-elements is converted into columns for the
* corresponding first-level table row.
*
* Simple example usage:
* @code
* $form['table'] = array(
* '#type' => 'table',
* '#header' => array(t('Title'), array('data' => t('Operations'), 'colspan' => '1')),
* // Optionally, to add tableDrag support:
* '#tabledrag' => array(
* array('order', 'sibling', 'thing-weight'),
* ),
* );
* foreach ($things as $row => $thing) {
* $form['table'][$row]['#weight'] = $thing['weight'];
*
* $form['table'][$row]['title'] = array(
* '#type' => 'textfield',
* '#default_value' => $thing['title'],
* );
*
* // Optionally, to add tableDrag support:
* $form['table'][$row]['#attributes']['class'][] = 'draggable';
* $form['table'][$row]['weight'] = array(
* '#type' => 'textfield',
* '#title' => t('Weight for @title', array('@title' => $thing['title'])),
* '#title_display' => 'invisible',
* '#size' => 4,
* '#default_value' => $thing['weight'],
* '#attributes' => array('class' => array('thing-weight')),
* );
*
* // The amount of link columns should be identical to the 'colspan'
* // attribute in #header above.
* $form['table'][$row]['edit'] = array(
* '#type' => 'link',
* '#title' => t('Edit'),
* '#href' => 'thing/' . $row . '/edit',
* );
* }
* @endcode
*
* @param array $element
* A structured array containing two sub-levels of elements. Properties used:
* - #tabledrag: The value is a list of arrays that are passed to
* drupal_add_tabledrag(). The HTML ID of the table is prepended to each set
* of arguments.
*
* @see system_element_info()
* @see theme_table()
* @see drupal_process_attached()
* @see drupal_add_tabledrag()
*/
function drupal_pre_render_table(array $element) {
foreach (element_children($element) as $first) {
$row = array('data' => array());
// Apply attributes of first-level elements as table row attributes.
if (isset($element[$first]['#attributes'])) {
$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.
$row['data'][] = array('data' => &$element[$first][$second]);
}
$element['#rows'][] = $row;
}
// Take over $element['#id'] as HTML ID attribute, if not already set.
element_set_attributes($element, array('id'));
// If the custom #tabledrag is set and there is a HTML ID, inject the table's
// HTML ID as first callback argument and attach the behavior.
if (!empty($element['#tabledrag']) && isset($element['#attributes']['id'])) {
foreach ($element['#tabledrag'] as &$args) {
array_unshift($args, $element['#attributes']['id']);
}
$element['#attached']['drupal_add_tabledrag'] = $element['#tabledrag'];
}
return $element;
}
/**
* Returns HTML for a table.
*
......
......@@ -18,7 +18,17 @@ function filter_admin_overview($form) {
$fallback_format = filter_fallback_format();
$form['#tree'] = TRUE;
$form['formats'] = array(
'#type' => 'table',
'#header' => array(t('Name'), t('Roles'), t('Weight'), t('Operations')),
'#tabledrag' => array(
array('order', 'sibling', 'text-format-order-weight'),
),
);
foreach ($formats as $id => $format) {
$form['formats'][$id]['#attributes']['class'][] = 'draggable';
$form['formats'][$id]['#weight'] = $format->weight;
$links = array();
$links['configure'] = array(
'title' => t('configure'),
......@@ -40,16 +50,20 @@ function filter_admin_overview($form) {
'href' => "admin/config/content/formats/$id/disable",
);
}
$form['formats'][$id]['roles'] = array('#markup' => $roles_markup);
$form['formats'][$id]['operations'] = array(
'#type' => 'operations',
'#links' => $links,
);
$form['formats'][$id]['weight'] = array(
'#type' => 'weight',
'#title' => t('Weight for @title', array('@title' => $format->name)),
'#title_display' => 'invisible',
'#default_value' => $format->weight,
'#attributes' => array('class' => array('text-format-order-weight')),
);
$form['formats'][$id]['operations'] = array(
'#type' => 'operations',
'#links' => $links,
);
}
$form['actions'] = array('#type' => 'actions');
......@@ -74,41 +88,6 @@ function filter_admin_overview_submit($form, &$form_state) {
drupal_set_message(t('The text format ordering has been saved.'));
}
/**
* Returns HTML for the text format administration overview form.
*
* @param $variables
* An associative array containing:
* - form: A render element representing the form.
*
* @ingroup themeable
*/
function theme_filter_admin_overview($variables) {
$form = $variables['form'];
$rows = array();
foreach (element_children($form['formats']) as $id) {
$form['formats'][$id]['weight']['#attributes']['class'] = array('text-format-order-weight');
$row = array(
'data' => array(
drupal_render($form['formats'][$id]['name']),
drupal_render($form['formats'][$id]['roles']),
drupal_render($form['formats'][$id]['weight']),
drupal_render($form['formats'][$id]['operations']),
),
'class' => array('draggable'),
);
$rows[] = $row;
}
$header = array(t('Name'), t('Roles'), t('Weight'), t('Operations'));
$output = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'text-format-order')));
$output .= drupal_render_children($form);
drupal_add_tabledrag('text-format-order', 'order', 'sibling', 'text-format-order-weight');
return $output;
}
/**
* Page callback: Displays the text format add/edit form.
*
......
......@@ -73,10 +73,6 @@ function filter_help($path, $arg) {
*/
function filter_theme() {
return array(
'filter_admin_overview' => array(
'render element' => 'form',
'file' => 'filter.admin.inc',
),
'filter_admin_format_filter_order' => array(
'render element' => 'element',
'file' => 'filter.admin.inc',
......
......@@ -418,7 +418,6 @@ function node_admin_content($form, $form_state) {
* Returns the admin form object to node_admin_content().
*
* @see node_admin_nodes_submit()
* @see node_admin_nodes_validate()
* @see node_filter_form()
* @see node_filter_form_submit()
* @see node_multiple_delete_confirm()
......@@ -453,7 +452,7 @@ function node_admin_nodes() {
$form['options']['submit'] = array(
'#type' => 'submit',
'#value' => t('Update'),
'#validate' => array('node_admin_nodes_validate'),
'#tableselect' => TRUE,
'#submit' => array('node_admin_nodes_submit'),
);
......@@ -523,27 +522,39 @@ function node_admin_nodes() {
// Prepare the list of nodes.
$languages = language_list(LANGUAGE_ALL);
$destination = drupal_get_destination();
$options = array();
$form['nodes'] = array(
'#type' => 'table',
'#header' => $header,
'#empty' => t('No content available.'),
);
foreach ($nodes as $node) {
$l_options = $node->langcode != LANGUAGE_NOT_SPECIFIED && isset($languages[$node->langcode]) ? array('language' => $languages[$node->langcode]) : array();
$options[$node->nid] = array(
'title' => array(
'data' => array(
'#type' => 'link',
'#title' => $node->label(),
'#href' => 'node/' . $node->nid,
'#options' => $l_options,
'#suffix' => ' ' . theme('mark', array('type' => node_mark($node->nid, $node->changed))),
),
),
'type' => check_plain(node_get_type_label($node)),
'author' => theme('username', array('account' => $node)),
'status' => $node->status ? t('published') : t('not published'),
'changed' => format_date($node->changed, 'short'),
$form['nodes'][$node->nid]['title'] = array(
'#type' => 'link',
'#title' => $node->label(),
'#href' => 'node/' . $node->nid,
'#options' => $l_options,
'#suffix' => ' ' . theme('mark', array('type' => node_mark($node->nid, $node->changed))),
);
$form['nodes'][$node->nid]['type'] = array(
'#markup' => check_plain(node_get_type_label($node)),
);
$form['nodes'][$node->nid]['author'] = array(
'#theme' => 'username',
'#account' => $node,
);
$form['nodes'][$node->nid]['status'] = array(
'#markup' => $node->status ? t('published') : t('not published'),
);
$form['nodes'][$node->nid]['changed'] = array(
'#markup' => format_date($node->changed, 'short'),
);
if ($multilingual) {
$options[$node->nid]['language_name'] = language_name($node->langcode);
$form['nodes'][$node->nid]['language_name'] = array(
'#markup' => language_name($node->langcode),
);
}
// Build a list of all the accessible operations for the current node.
$operations = array();
if (node_access('update', $node)) {
......@@ -567,27 +578,23 @@ function node_admin_nodes() {
'query' => $destination,
);
}
$options[$node->nid]['operations'] = array();
$form['nodes'][$node->nid]['operations'] = array();
if (count($operations) > 1) {
// Render an unordered list of operations links.
$options[$node->nid]['operations'] = array(
'data' => array(
'#type' => 'operations',
'#subtype' => 'node',
'#links' => $operations,
),
$form['nodes'][$node->nid]['operations'] = array(
'#type' => 'operations',
'#subtype' => 'node',
'#links' => $operations,
);
}
elseif (!empty($operations)) {
// Render the first and only operation as a link.
$link = reset($operations);
$options[$node->nid]['operations'] = array(
'data' => array(
'#type' => 'link',
'#title' => $link['title'],
'#href' => $link['href'],
'#options' => array('query' => $link['query']),
),
$form['nodes'][$node->nid]['operations'] = array(
'#type' => 'link',
'#title' => $link['title'],
'#href' => $link['href'],
'#options' => array('query' => $link['query']),
);
}
}
......@@ -595,47 +602,13 @@ function node_admin_nodes() {
// Only use a tableselect when the current user is able to perform any
// operations.
if ($admin_access) {
$form['nodes'] = array(
'#type' => 'tableselect',
'#header' => $header,
'#options' => $options,
'#empty' => t('No content available.'),
);
}
// Otherwise, use a simple table.
else {
$form['nodes'] = array(
'#theme' => 'table',
'#header' => $header,
'#rows' => $options,
'#empty' => t('No content available.'),
);
$form['nodes']['#tableselect'] = TRUE;
}
$form['pager'] = array('#markup' => theme('pager'));
$form['pager'] = array('#theme' => 'pager');
return $form;
}
/**
* Form validation handler for node_admin_nodes().
*
* Checks whether any nodes have been selected to perform the chosen 'Update
* option' on.
*
* @see node_admin_nodes()
* @see node_admin_nodes_submit()
* @see node_filter_form()
* @see node_filter_form_submit()
* @see node_multiple_delete_confirm()
* @see node_multiple_delete_confirm_submit()
*/
function node_admin_nodes_validate($form, &$form_state) {
// Error if there are no items to select.
if (!is_array($form_state['values']['nodes']) || !count(array_filter($form_state['values']['nodes']))) {
form_set_error('', t('No items selected.'));
}
}
/**
* Form submission handler for node_admin_nodes().
*
......
......@@ -557,6 +557,30 @@ function system_element_info() {
'#theme' => 'hidden',
);
$types['table'] = array(
'#header' => array(),
'#rows' => array(),
'#empty' => '',
// Properties for tableselect support.
'#input' => TRUE,
'#tree' => TRUE,
'#tableselect' => FALSE,
'#multiple' => TRUE,
'#js_select' => TRUE,
'#value_callback' => 'form_type_table_value',
'#process' => array('form_process_table'),
'#element_validate' => array('form_validate_table'),
// Properties for tabledrag support.
// The value is a list of arrays that are passed to drupal_add_tabledrag().
// drupal_pre_render_table() prepends the HTML ID of the table to each set
// of arguments.
// @see drupal_add_tabledrag()
'#tabledrag' => array(),
// Render properties.
'#pre_render' => array('drupal_pre_render_table'),
'#theme' => 'table',
);
return $types;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment