summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--includes/common.inc48
-rw-r--r--modules/simpletest/tests/common.test50
2 files changed, 96 insertions, 2 deletions
diff --git a/includes/common.inc b/includes/common.inc
index 62a5037f0..2ab991433 100644
--- a/includes/common.inc
+++ b/includes/common.inc
@@ -5827,8 +5827,9 @@ function drupal_render_cache_set(&$markup, $elements) {
// be retrieved and used.
$data['#markup'] = &$markup;
// Persist attached data associated with this element.
- if (isset($elements['#attached'])) {
- $data['#attached'] = $elements['#attached'];
+ $attached = drupal_render_collect_attached($elements, TRUE);
+ if ($attached) {
+ $data['#attached'] = $attached;
}
$bin = isset($elements['#cache']['bin']) ? $elements['#cache']['bin'] : 'cache';
$expire = isset($elements['#cache']['expire']) ? $elements['#cache']['expire'] : CACHE_PERMANENT;
@@ -5836,6 +5837,49 @@ function drupal_render_cache_set(&$markup, $elements) {
}
/**
+ * Collect #attached for an element and all child elements into a single array.
+ *
+ * When caching elements, it is necessary to collect all libraries, javascript
+ * and CSS into a single array, from both the element itself and all child
+ * elements. This allows drupal_render() to add these back to the page when the
+ * element is returned from cache.
+ *
+ * @param $elements
+ * The element to collect #attached from.
+ * @param $return
+ * Whether to return the attached elements and reset the internal static.
+ *
+ * @return
+ * The #attached array for this element and its descendants.
+ */
+function drupal_render_collect_attached($elements, $return = FALSE) {
+ $attached = &drupal_static(__FUNCTION__, array());
+
+ // Collect all #attached for this element.
+ if (isset($elements['#attached'])) {
+ foreach ($elements['#attached'] as $key => $value) {
+ if (!isset($attached[$key])) {
+ $attached[$key] = array();
+ }
+ $attached[$key] = array_merge($attached[$key], $value);
+ }
+ }
+ if ($children = element_children($elements)) {
+ foreach ($children as $child) {
+ drupal_render_collect_attached($elements[$child]);
+ }
+ }
+
+ // If this was the first call to the function, return all attached elements
+ // and reset the static cache.
+ if ($return) {
+ $return = $attached;
+ $attached = array();
+ return $return;
+ }
+}
+
+/**
* Prepare an element for caching based on a query. This smart caching strategy
* saves Drupal from querying and rendering to HTML when the underlying query is
* unchanged.
diff --git a/modules/simpletest/tests/common.test b/modules/simpletest/tests/common.test
index ca29e11f8..f48497559 100644
--- a/modules/simpletest/tests/common.test
+++ b/modules/simpletest/tests/common.test
@@ -1522,6 +1522,56 @@ class DrupalRenderTestCase extends DrupalWebTestCase {
}
/**
+ * Test #attached functionality in children elements.
+ */
+ function testDrupalRenderChildrenAttached() {
+ // The cache system is turned off for POST requests.
+ $request_method = $_SERVER['REQUEST_METHOD'];
+ $_SERVER['REQUEST_METHOD'] = 'GET';
+
+ // Create an element with a child and subchild. Each element loads a
+ // different JavaScript file using #attached.
+ $parent_js = drupal_get_path('module', 'user') . '/user.js';
+ $child_js = drupal_get_path('module', 'forum') . '/forum.js';
+ $subchild_js = drupal_get_path('module', 'book') . '/book.js';
+ $element = array(
+ '#type' => 'fieldset',
+ '#cache' => array(
+ 'keys' => array('simpletest', 'drupal_render', 'children_attached'),
+ ),
+ '#attached' => array('js' => array($parent_js)),
+ '#title' => 'Parent',
+ );
+ $element['child'] = array(
+ '#type' => 'fieldset',
+ '#attached' => array('js' => array($child_js)),
+ '#title' => 'Child',
+ );
+ $element['child']['subchild'] = array(
+ '#attached' => array('js' => array($subchild_js)),
+ '#markup' => 'Subchild',
+ );
+
+ // Render the element and verify the presence of #attached JavaScript.
+ drupal_render($element);
+ $scripts = drupal_get_js();
+ $this->assertTrue(strpos($scripts, $parent_js), t('The element #attached JavaScript was included.'));
+ $this->assertTrue(strpos($scripts, $child_js), t('The child #attached JavaScript was included.'));
+ $this->assertTrue(strpos($scripts, $subchild_js), t('The subchild #attached JavaScript was included.'));
+
+ // Load the element from cache and verify the presence of the #attached
+ // JavaScript.
+ drupal_static_reset('drupal_add_js');
+ $this->assertTrue(drupal_render_cache_get($element), t('The element was retrieved from cache.'));
+ $scripts = drupal_get_js();
+ $this->assertTrue(strpos($scripts, $parent_js), t('The element #attached JavaScript was included when loading from cache.'));
+ $this->assertTrue(strpos($scripts, $child_js), t('The child #attached JavaScript was included when loading from cache.'));
+ $this->assertTrue(strpos($scripts, $subchild_js), t('The subchild #attached JavaScript was included when loading from cache.'));
+
+ $_SERVER['REQUEST_METHOD'] = $request_method;
+ }
+
+ /**
* Test passing arguments to the theme function.
*/
function testDrupalRenderThemeArguments() {