Commit 568fef4c authored by webchick's avatar webchick

Issue #2109793 by damiankloip, dawehner, tim.plunkett: Convert element_*...

Issue #2109793 by damiankloip, dawehner, tim.plunkett: Convert element_* methods in common.inc to a class.
parent 7db0d8b8
<?php <?php
use Drupal\Component\Utility\Crypt;
use Drupal\Component\Utility\Json; use Drupal\Component\Utility\Json;
use Drupal\Component\Utility\Number; use Drupal\Component\Utility\Number;
use Drupal\Component\Utility\Settings; use Drupal\Component\Utility\Settings;
...@@ -21,6 +20,7 @@ ...@@ -21,6 +20,7 @@
use Drupal\Core\Routing\GeneratorNotInitializedException; use Drupal\Core\Routing\GeneratorNotInitializedException;
use Drupal\Core\SystemListingInfo; use Drupal\Core\SystemListingInfo;
use Drupal\Core\Template\Attribute; use Drupal\Core\Template\Attribute;
use Drupal\Core\Render\Element;
/** /**
* @file * @file
...@@ -3821,7 +3821,7 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) { ...@@ -3821,7 +3821,7 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
} }
// Get the children of the element, sorted by weight. // Get the children of the element, sorted by weight.
$children = element_children($elements, TRUE); $children = Element::children($elements, TRUE);
// Initialize this element's #children, unless a #pre_render callback already // Initialize this element's #children, unless a #pre_render callback already
// preset #children. // preset #children.
...@@ -4668,23 +4668,35 @@ function drupal_sort_title($a, $b) { ...@@ -4668,23 +4668,35 @@ function drupal_sort_title($a, $b) {
/** /**
* Checks if the key is a property. * Checks if the key is a property.
*
* @see \Drupal\Core\Render\Element::property()
*
* @deprecated as of Drupal 8.0. Use Element::property() instead.
*/ */
function element_property($key) { function element_property($key) {
return $key[0] == '#'; return Element::property($key);
} }
/** /**
* Gets properties of a structured array element (keys beginning with '#'). * Gets properties of a structured array element (keys beginning with '#').
*
* @see \Drupal\Core\Render\Element::properties()
*
* @deprecated as of Drupal 8.0. Use Element::properties() instead.
*/ */
function element_properties($element) { function element_properties($element) {
return array_filter(array_keys((array) $element), 'element_property'); return Element::properties($element);
} }
/** /**
* Checks if the key is a child. * Checks if the key is a child.
*
* @see \Drupal\Core\Render\Element::child()
*
* @deprecated as of Drupal 8.0. Use Element::child() instead.
*/ */
function element_child($key) { function element_child($key) {
return !isset($key[0]) || $key[0] != '#'; return Element::child($key);
} }
/** /**
...@@ -4700,43 +4712,13 @@ function element_child($key) { ...@@ -4700,43 +4712,13 @@ function element_child($key) {
* *
* @return * @return
* The array keys of the element's children. * The array keys of the element's children.
*
* @see \Drupal\Core\Render\Element::children()
*
* @deprecated as of Drupal 8.0. Use Element::children() instead.
*/ */
function element_children(&$elements, $sort = FALSE) { function element_children(&$elements, $sort = FALSE) {
// Do not attempt to sort elements which have already been sorted. return Element::children($elements, $sort);
$sort = isset($elements['#sorted']) ? !$elements['#sorted'] : $sort;
// Filter out properties from the element, leaving only children.
$children = array();
$sortable = FALSE;
foreach ($elements as $key => $value) {
if ($key === '' || $key[0] !== '#') {
if (is_array($value)) {
$children[$key] = $value;
if (isset($value['#weight'])) {
$sortable = TRUE;
}
}
// Only trigger an error if the value is not null.
// @see http://drupal.org/node/1283892
elseif (isset($value)) {
trigger_error(t('"@key" is an invalid render array key', array('@key' => $key)), E_USER_ERROR);
}
}
}
// Sort the children if necessary.
if ($sort && $sortable) {
uasort($children, 'element_sort');
// Put the sorted children back into $elements in the correct order, to
// preserve sorting if the same element is passed through
// element_children() twice.
foreach ($children as $key => $child) {
unset($elements[$key]);
$elements[$key] = $child;
}
$elements['#sorted'] = TRUE;
}
return array_keys($children);
} }
/** /**
...@@ -4747,27 +4729,13 @@ function element_children(&$elements, $sort = FALSE) { ...@@ -4747,27 +4729,13 @@ function element_children(&$elements, $sort = FALSE) {
* *
* @return * @return
* The array keys of the element's visible children. * The array keys of the element's visible children.
*
* @see \Drupal\Core\Render\Element::getVisibleChildren()
*
* @deprecated as of Drupal 8.0. Use Element::getVisibleChildren() instead.
*/ */
function element_get_visible_children(array $elements) { function element_get_visible_children(array $elements) {
$visible_children = array(); return Element::getVisibleChildren($elements);
foreach (element_children($elements) as $key) {
$child = $elements[$key];
// Skip un-accessible children.
if (isset($child['#access']) && !$child['#access']) {
continue;
}
// Skip value and hidden elements, since they are not rendered.
if (isset($child['#type']) && in_array($child['#type'], array('value', 'hidden'))) {
continue;
}
$visible_children[$key] = $child;
}
return array_keys($visible_children);
} }
/** /**
...@@ -4781,18 +4749,13 @@ function element_get_visible_children(array $elements) { ...@@ -4781,18 +4749,13 @@ function element_get_visible_children(array $elements) {
* array('#propertyname' => 'attributename'). If both names are identical * array('#propertyname' => 'attributename'). If both names are identical
* except for the leading '#', then an attribute name value is sufficient and * except for the leading '#', then an attribute name value is sufficient and
* no property name needs to be specified. * no property name needs to be specified.
*
* @see \Drupal\Core\Render\Element::setAttributes()
*
* @deprecated as of Drupal 8.0. Use Element::setAttributes() instead.
*/ */
function element_set_attributes(array &$element, array $map) { function element_set_attributes(array &$element, array $map) {
foreach ($map as $property => $attribute) { Element::setAttributes($element, $map);
// If the key is numeric, the attribute name needs to be taken over.
if (is_int($property)) {
$property = '#' . $attribute;
}
// Do not overwrite already existing attributes.
if (isset($element[$property]) && !isset($element['#attributes'][$attribute])) {
$element['#attributes'][$attribute] = $element[$property];
}
}
} }
/** /**
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\HttpKernel; use Drupal\Core\HttpKernel;
use Drupal\Core\KeyValueStore\KeyValueExpirableFactory; use Drupal\Core\KeyValueStore\KeyValueExpirableFactory;
use Drupal\Core\Render\Element;
use Drupal\Core\Routing\UrlGeneratorInterface; use Drupal\Core\Routing\UrlGeneratorInterface;
use Drupal\Core\StringTranslation\TranslationInterface; use Drupal\Core\StringTranslation\TranslationInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface;
...@@ -1003,7 +1004,7 @@ public function redirectForm($form_state) { ...@@ -1003,7 +1004,7 @@ public function redirectForm($form_state) {
*/ */
protected function doValidateForm(&$elements, &$form_state, $form_id = NULL) { protected function doValidateForm(&$elements, &$form_state, $form_id = NULL) {
// Recurse through all children. // Recurse through all children.
foreach ($this->elementChildren($elements) as $key) { foreach (Element::children($elements) as $key) {
if (isset($elements[$key]) && $elements[$key]) { if (isset($elements[$key]) && $elements[$key]) {
$this->doValidateForm($elements[$key], $form_state); $this->doValidateForm($elements[$key], $form_state);
} }
...@@ -1162,7 +1163,7 @@ protected function doValidateForm(&$elements, &$form_state, $form_id = NULL) { ...@@ -1162,7 +1163,7 @@ protected function doValidateForm(&$elements, &$form_state, $form_id = NULL) {
*/ */
protected function setElementErrorsFromFormState(array &$elements, array &$form_state) { protected function setElementErrorsFromFormState(array &$elements, array &$form_state) {
// Recurse through all children. // Recurse through all children.
foreach ($this->elementChildren($elements) as $key) { foreach (Element::children($elements) as $key) {
if (isset($elements[$key]) && $elements[$key]) { if (isset($elements[$key]) && $elements[$key]) {
$this->setElementErrorsFromFormState($elements[$key], $form_state); $this->setElementErrorsFromFormState($elements[$key], $form_state);
} }
...@@ -1367,7 +1368,7 @@ public function doBuildForm($form_id, &$element, &$form_state) { ...@@ -1367,7 +1368,7 @@ public function doBuildForm($form_id, &$element, &$form_state) {
// Recurse through all child elements. // Recurse through all child elements.
$count = 0; $count = 0;
foreach ($this->elementChildren($element) as $key) { foreach (Element::children($element) as $key) {
// Prior to checking properties of child elements, their default // Prior to checking properties of child elements, their default
// properties need to be loaded. // properties need to be loaded.
if (isset($element[$key]['#type']) && empty($element[$key]['#defaults_loaded']) && ($info = $this->getElementInfo($element[$key]['#type']))) { if (isset($element[$key]['#type']) && empty($element[$key]['#defaults_loaded']) && ($info = $this->getElementInfo($element[$key]['#type']))) {
...@@ -1774,15 +1775,6 @@ protected function drupalSetMessage($message = NULL, $type = 'status', $repeat = ...@@ -1774,15 +1775,6 @@ protected function drupalSetMessage($message = NULL, $type = 'status', $repeat =
return drupal_set_message($message, $type, $repeat); return drupal_set_message($message, $type, $repeat);
} }
/**
* Wraps element_children().
*
* @return array
*/
protected function elementChildren(&$elements, $sort = FALSE) {
return element_children($elements, $sort);
}
/** /**
* Wraps drupal_html_class(). * Wraps drupal_html_class().
* *
......
<?php
/**
* @file
* Contains \Drupal\Core\Render\Element.
*/
namespace Drupal\Core\Render;
use Drupal\Component\Utility\String;
/**
* Deals with drupal render elements.
*/
class Element {
/**
* Checks if the key is a property.
*
* @param string $key
* The key to check.
*
* @return bool
* TRUE of the key is a property, FALSE otherwise.
*/
public static function property($key) {
return $key[0] == '#';
}
/**
* Gets properties of a structured array element (keys beginning with '#').
*
* @param array $element
* An element array to return properties for.
*
* @return array
* An array of property keys for the element.
*/
public static function properties(array $element) {
return array_filter(array_keys($element), 'static::property');
}
/**
* Checks if the key is a child.
*
* @param string $key
* The key to check.
*
* @return bool
* TRUE if the element is a child, FALSE otherwise.
*/
public static function child($key) {
return !isset($key[0]) || $key[0] != '#';
}
/**
* Identifies the children of an element array, optionally sorted by weight.
*
* The children of a element array are those key/value pairs whose key does
* not start with a '#'. See drupal_render() for details.
*
* @param array $elements
* The element array whose children are to be identified. Passed by
* reference.
* @param bool $sort
* Boolean to indicate whether the children should be sorted by weight.
*
* @return array
* The array keys of the element's children.
*/
public static function children(array &$elements, $sort = FALSE) {
// Do not attempt to sort elements which have already been sorted.
$sort = isset($elements['#sorted']) ? !$elements['#sorted'] : $sort;
// Filter out properties from the element, leaving only children.
$children = array();
$sortable = FALSE;
foreach ($elements as $key => $value) {
if ($key === '' || $key[0] !== '#') {
if (is_array($value)) {
$children[$key] = $value;
if (isset($value['#weight'])) {
$sortable = TRUE;
}
}
// Only trigger an error if the value is not null.
// @see http://drupal.org/node/1283892
elseif (isset($value)) {
trigger_error(String::format('"@key" is an invalid render array key', array('@key' => $key)), E_USER_ERROR);
}
}
}
// Sort the children if necessary.
if ($sort && $sortable) {
uasort($children, 'Drupal\Component\Utility\SortArray::sortByWeightProperty');
// Put the sorted children back into $elements in the correct order, to
// preserve sorting if the same element is passed through
// element_children() twice.
foreach ($children as $key => $child) {
unset($elements[$key]);
$elements[$key] = $child;
}
$elements['#sorted'] = TRUE;
}
return array_keys($children);
}
/**
* Returns the visible children of an element.
*
* @param array $elements
* The parent element.
*
* @return array
* The array keys of the element's visible children.
*/
public static function getVisibleChildren(array $elements) {
$visible_children = array();
foreach (static::children($elements) as $key) {
$child = $elements[$key];
// Skip un-accessible children.
if (isset($child['#access']) && !$child['#access']) {
continue;
}
// Skip value and hidden elements, since they are not rendered.
if (isset($child['#type']) && in_array($child['#type'], array('value', 'hidden'))) {
continue;
}
$visible_children[$key] = $child;
}
return array_keys($visible_children);
}
/**
* Sets HTML attributes based on element properties.
*
* @param array $element
* The renderable element to process. Passed by reference.
* @param array $map
* An associative array whose keys are element property names and whose values
* are the HTML attribute names to set for corresponding the property; e.g.,
* array('#propertyname' => 'attributename'). If both names are identical
* except for the leading '#', then an attribute name value is sufficient and
* no property name needs to be specified.
*/
public static function setAttributes(array &$element, array $map) {
foreach ($map as $property => $attribute) {
// If the key is numeric, the attribute name needs to be taken over.
if (is_int($property)) {
$property = '#' . $attribute;
}
// Do not overwrite already existing attributes.
if (isset($element[$property]) && !isset($element['#attributes'][$attribute])) {
$element['#attributes'][$attribute] = $element[$property];
}
}
}
}
...@@ -145,23 +145,6 @@ function testDrupalRenderFormElements() { ...@@ -145,23 +145,6 @@ function testDrupalRenderFormElements() {
)); ));
} }
/**
* Tests rendering elements with invalid keys.
*/
function testDrupalRenderInvalidKeys() {
$error = array(
'%type' => 'User error',
'!message' => '"child" is an invalid render array key',
'%function' => 'element_children()',
);
$message = t('%type: !message in %function (line ', $error);
\Drupal::config('system.logging')->set('error_level', ERROR_REPORTING_DISPLAY_ALL)->save();
$this->drupalGet('common-test/drupal-render-invalid-keys');
$this->assertResponse(200, 'Received expected HTTP status code.');
$this->assertRaw($message, format_string('Found error message: !message.', array('!message' => $message)));
}
/** /**
* Tests that elements are rendered properly. * Tests that elements are rendered properly.
*/ */
......
...@@ -12,14 +12,6 @@ common_test.destination: ...@@ -12,14 +12,6 @@ common_test.destination:
requirements: requirements:
_permission: 'access content' _permission: 'access content'
common_test.drupal_render_invalid_keys:
path: '/common-test/drupal-render-invalid-keys'
defaults:
_title: 'Drupal Render'
_content: '\Drupal\common_test\Controller\CommonTestController::drupalRenderInvalidKeys'
requirements:
_permission: 'access content'
common_test.js_and_css_querystring: common_test.js_and_css_querystring:
path: '/common-test/query-string' path: '/common-test/query-string'
defaults: defaults:
......
...@@ -61,20 +61,6 @@ public function typeLinkActiveClass() { ...@@ -61,20 +61,6 @@ public function typeLinkActiveClass() {
); );
} }
/**
* Renders an element with an invalid render array key.
*
* @return array
* A render array.
*/
public function drupalRenderInvalidKeys() {
define('SIMPLETEST_COLLECT_ERRORS', FALSE);
// Keys that begin with # may contain a value of any type, otherwise they must
// contain arrays.
$element = array('child' => 'This should be an array.');
return drupal_render($element);
}
/** /**
* Adds a JavaScript file and a CSS file with a query string appended. * Adds a JavaScript file and a CSS file with a query string appended.
* *
......
...@@ -813,21 +813,6 @@ protected function drupalSetMessage($message = NULL, $type = 'status', $repeat = ...@@ -813,21 +813,6 @@ protected function drupalSetMessage($message = NULL, $type = 'status', $repeat =
protected function watchdog($type, $message, array $variables = NULL, $severity = WATCHDOG_NOTICE, $link = NULL) { protected function watchdog($type, $message, array $variables = NULL, $severity = WATCHDOG_NOTICE, $link = NULL) {
} }
/**
* {@inheritdoc}
*/
protected function elementChildren(&$elements, $sort = FALSE) {
$children = array();
foreach ($elements as $key => $value) {
if ($key === '' || $key[0] !== '#') {
if (is_array($value)) {
$children[] = $key;
}
}
}
return $children;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
......
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Render\ElementTest.
*/
namespace Drupal\Tests\Core\Render;
use Drupal\Tests\UnitTestCase;
use Drupal\Core\Render\Element;
/**
* Tests the Element class
*
* @see \Drupal\Core\Render\Element
*
* @group Drupal
* @group Render
*/
class ElementTest extends UnitTestCase {
/**
* {@inheritdoc}
*/
public static function getInfo() {
return array(
'name' => 'Element test',
'description' => 'Tests \Drupal\Core\Render\Element helper class.',
'group' => 'Render',
);
}
/**
* Tests the property() method.
*/
public function testProperty() {
$this->assertTrue(Element::property('#property'));
$this->assertFalse(Element::property('property'));
$this->assertFalse(Element::property('property#'));
}
/**
* Tests the properties() method.
*/
public function testProperties() {
$element = array(
'#property1' => 'property1',
'#property2' => 'property2',
'property3' => 'property3'
);
$properties = Element::properties($element);
$this->assertContains('#property1', $properties);
$this->assertContains('#property2', $properties);
$this->assertNotContains('property3', $properties);
}
/**
* Tests the child() method.
*/
public function testChild() {
$this->assertFalse(Element::child('#property'));
$this->assertTrue(Element::child('property'));
$this->assertTrue(Element::child('property#'));
}
/**
* Tests the children() method.
*/
public function testChildren() {
$element = array(
'child2' => array('#weight' => 10),
'child1' => array('#weight' => 0),
'child3' => array('#weight' => 20),
'#property' => 'property',
);
$expected = array('child2', 'child1', 'child3');
$element_copy = $element;
$this->assertSame($expected, Element::children($element_copy));