diff options
-rw-r--r-- | includes/common.inc | 48 | ||||
-rw-r--r-- | modules/simpletest/tests/common.test | 50 |
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() { |