/** * #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( * 'action' => 'order', * 'relationship' => 'sibling', * 'group' => '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'), * '#url' => Url::fromRoute('entity.test_entity.edit_form', ['test_entity' => $row]), * ); * } * @endcode * * @param array $element * A structured array containing two sub-levels of elements. Properties used: * - #tabledrag: The value is a list of $options arrays that are passed to * drupal_attach_tabledrag(). The HTML ID of the table is added to each * $options array. * * @return array * * @see theme_table() * @see drupal_process_attached() * @see drupal_attach_tabledrag() */ public static function preRenderTable($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. $column = array('data' => &$element[$first][$second]); // Apply wrapper attributes of second-level elements as table cell // attributes. if (isset($element[$first][$second]['#wrapper_attributes'])) { $column += $element[$first][$second]['#wrapper_attributes']; } $row['data'][] = $column; } $element['#rows'][] = $row; } // Take over $element['#id'] as HTML ID attribute, if not already set. Element::setAttributes($element, array('id')); // Add sticky headers, if applicable. if (count($element['#header']) && $element['#sticky']) { $element['#attached']['library'][] = 'core/drupal.tableheader'; // Add 'sticky-enabled' class to the table to identify it for JS. // This is needed to target tables constructed by this function. $element['#attributes']['class'][] = 'sticky-enabled'; } // If the table has headers and it should react responsively to columns hidden // with the classes represented by the constants RESPONSIVE_PRIORITY_MEDIUM // and RESPONSIVE_PRIORITY_LOW, add the tableresponsive behaviors. if (count($element['#header']) && $element['#responsive']) { $element['#attached']['library'][] = 'core/drupal.tableresponsive'; // Add 'responsive-enabled' class to the table to identify it for JS. // This is needed to target tables constructed by this function. $element['#attributes']['class'][] = 'responsive-enabled'; } // If the custom #tabledrag is set and there is a HTML ID, add the table's // HTML ID to the options and attach the behavior. if (!empty($element['#tabledrag']) && isset($element['#attributes']['id'])) { foreach ($element['#tabledrag'] as $options) { $options['table_id'] = $element['#attributes']['id']; drupal_attach_tabledrag($element, $options); } } return $element; }
/** * Performs pre-render tasks on field_ui_table elements. * * This function is assigned as a #pre_render callback in * field_ui_element_info(). * * @param array $element * A structured array containing two sub-levels of elements. Properties * used: * - #tabledrag: The value is a list of $options arrays that are passed to * drupal_attach_tabledrag(). The HTML ID of the table is added to each * $options array. * * @see drupal_render() * @see drupal_pre_render_table() */ public function tablePreRender($elements) { $js_settings = array(); // For each region, build the tree structure from the weight and parenting // data contained in the flat form structure, to determine row order and // indentation. $regions = $elements['#regions']; $tree = array('' => array('name' => '', 'children' => array())); $trees = array_fill_keys(array_keys($regions), $tree); $parents = array(); $children = Element::children($elements); $list = array_combine($children, $children); // Iterate on rows until we can build a known tree path for all of them. while ($list) { foreach ($list as $name) { $row =& $elements[$name]; $parent = $row['parent_wrapper']['parent']['#value']; // Proceed if parent is known. if (empty($parent) || isset($parents[$parent])) { // Grab parent, and remove the row from the next iteration. $parents[$name] = $parent ? array_merge($parents[$parent], array($parent)) : array(); unset($list[$name]); // Determine the region for the row. $region_name = call_user_func($row['#region_callback'], $row); // Add the element in the tree. $target =& $trees[$region_name]['']; foreach ($parents[$name] as $key) { $target =& $target['children'][$key]; } $target['children'][$name] = array('name' => $name, 'weight' => $row['weight']['#value']); // Add tabledrag indentation to the first row cell. if ($depth = count($parents[$name])) { $children = Element::children($row); $cell = current($children); $indentation = array('#theme' => 'indentation', '#size' => $depth); $row[$cell]['#prefix'] = drupal_render($indentation) . (isset($row[$cell]['#prefix']) ? $row[$cell]['#prefix'] : ''); } // Add row id and associate JS settings. $id = drupal_html_class($name); $row['#attributes']['id'] = $id; if (isset($row['#js_settings'])) { $row['#js_settings'] += array('rowHandler' => $row['#row_type'], 'name' => $name, 'region' => $region_name); $js_settings[$id] = $row['#js_settings']; } } } } // Determine rendering order from the tree structure. foreach ($regions as $region_name => $region) { $elements['#regions'][$region_name]['rows_order'] = array_reduce($trees[$region_name], array($this, 'reduceOrder')); } $elements['#attached']['js'][] = array('type' => 'setting', 'data' => array('fieldUIRowsData' => $js_settings)); // If the custom #tabledrag is set and there is a HTML ID, add the table's // HTML ID to the options and attach the behavior. // @see drupal_pre_render_table() if (!empty($elements['#tabledrag']) && isset($elements['#attributes']['id'])) { foreach ($elements['#tabledrag'] as $options) { $options['table_id'] = $elements['#attributes']['id']; drupal_attach_tabledrag($elements, $options); } } return $elements; }