Issue #2273277 by Wim Leers, effulgentsia, Fabianx: Fixed Figure out a solution for the problematic interaction between the render system and the theme system when using #pre_render.
parent
dcc880bc10
commit
8c68491896
|
@ -22,6 +22,7 @@ use Drupal\Component\Utility\Tags;
|
|||
use Drupal\Component\Utility\UrlHelper;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Render\RenderStackFrame;
|
||||
use Drupal\Core\Site\Settings;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
@ -1169,20 +1170,6 @@ function _drupal_add_css($data = NULL, $options = NULL) {
|
|||
function drupal_get_css($css = NULL, $skip_alter = FALSE, $theme_add_css = TRUE) {
|
||||
$theme_info = \Drupal::theme()->getActiveTheme();
|
||||
|
||||
// @todo There is probably a better place to add the CSS from themes,
|
||||
// see https://www.drupal.org/node/2322617.
|
||||
if ($theme_add_css) {
|
||||
foreach ($theme_info->getStyleSheets() as $media => $stylesheets) {
|
||||
foreach ($stylesheets as $stylesheet) {
|
||||
_drupal_add_css($stylesheet, array(
|
||||
'group' => CSS_AGGREGATE_THEME,
|
||||
'every_page' => TRUE,
|
||||
'media' => $media
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($css)) {
|
||||
$css = _drupal_add_css();
|
||||
}
|
||||
|
@ -1685,16 +1672,6 @@ function drupal_js_defaults($data = NULL) {
|
|||
* @see drupal_js_defaults()
|
||||
*/
|
||||
function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALSE, $is_ajax = FALSE, $theme_add_js = TRUE) {
|
||||
// @todo There is probably a better place to add the JS from themes,
|
||||
// see https://www.drupal.org/node/2322617.
|
||||
$active_theme = \Drupal::theme()->getActiveTheme();
|
||||
if ($theme_add_js) {
|
||||
// Add libraries used by this theme.
|
||||
foreach ($active_theme->getLibraries() as $library) {
|
||||
_drupal_add_library($library);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($javascript)) {
|
||||
$javascript = _drupal_add_js();
|
||||
}
|
||||
|
@ -1722,7 +1699,7 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS
|
|||
// Don't add settings if there is no other JavaScript on the page, unless
|
||||
// this is an AJAX request.
|
||||
if (!empty($items['settings']) || $is_ajax) {
|
||||
$theme_key = $active_theme->getName();
|
||||
$theme_key = \Drupal::theme()->getActiveTheme()->getName();
|
||||
// Provide the page with information about the theme that's used, so that
|
||||
// a later AJAX request can be rendered using the same theme.
|
||||
// @see \Drupal\Core\Theme\AjaxBasePageNegotiator
|
||||
|
@ -1847,6 +1824,41 @@ function drupal_merge_attached(array $a, array $b) {
|
|||
return NestedArray::mergeDeep($a, $b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges sets of cache tags.
|
||||
*
|
||||
* The cache tags array is returned in a format that is valid for
|
||||
* \Drupal\Core\Cache\CacheBackendInterface::set().
|
||||
*
|
||||
* When caching elements, it is necessary to collect all cache tags into a
|
||||
* single array, from both the element itself and all child elements. This
|
||||
* allows items to be invalidated based on all tags attached to the content
|
||||
* they're constituted from.
|
||||
*
|
||||
* @param array $tags
|
||||
* The first set of cache tags.
|
||||
* @param array $other
|
||||
* The other set of cache tags.
|
||||
*
|
||||
* @return array
|
||||
* The merged set of cache tags.
|
||||
*/
|
||||
function drupal_merge_cache_tags(array $tags, array $other) {
|
||||
foreach ($other as $namespace => $values) {
|
||||
if (is_array($values)) {
|
||||
foreach ($values as $value) {
|
||||
$tags[$namespace][$value] = $value;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!isset($tags[$namespace])) {
|
||||
$tags[$namespace] = $values;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds attachments to a render() structure.
|
||||
*
|
||||
|
@ -2593,6 +2605,14 @@ function drupal_render_page($page) {
|
|||
* the children is typically inserted into the markup generated by the parent
|
||||
* array.
|
||||
*
|
||||
* An important aspect of rendering is the bubbling of rendering metadata: cache
|
||||
* tags, attached assets and #post_render_cache metadata all need to be bubbled
|
||||
* up. That information is needed once the rendering to a HTML string is
|
||||
* completed: the resulting HTML for the page must know by which cache tags it
|
||||
* should be invalidated, which (CSS and JavaScript) assets must be loaded, and
|
||||
* which #post_render_cache callbacks should be executed. A stack data structure
|
||||
* is used to perform this bubbling.
|
||||
*
|
||||
* The process of rendering an element is recursive unless the element defines
|
||||
* an implemented theme hook in #theme. During each call to drupal_render(), the
|
||||
* outermost renderable array (also known as an "element") is processed using
|
||||
|
@ -2600,6 +2620,8 @@ function drupal_render_page($page) {
|
|||
* - If this element has already been printed (#printed = TRUE) or the user
|
||||
* does not have access to it (#access = FALSE), then an empty string is
|
||||
* returned.
|
||||
* - If no stack data structure has been created yet, it is done now. Next,
|
||||
* an empty \Drupal\Core\Render\RenderStackFrame is pushed onto the stack.
|
||||
* - If this element has #cache defined then the cached markup for this
|
||||
* element will be returned if it exists in drupal_render()'s cache. To use
|
||||
* drupal_render() caching, set the element's #cache property to an
|
||||
|
@ -2615,6 +2637,12 @@ function drupal_render_page($page) {
|
|||
* - 'expire': Set to one of the cache lifetime constants.
|
||||
* - 'bin': Specify a cache bin to cache the element in. Default is
|
||||
* 'default'.
|
||||
* When there is a render cache hit, there is no rendering work left to be
|
||||
* done, so the stack must be updated. The empty (and topmost) frame that
|
||||
* was just pushed onto the stack is updated with all bubbleable rendering
|
||||
* metadata from the element retrieved from render cache. Then, this stack
|
||||
* frame is bubbled: the two topmost frames are popped from the stack, they
|
||||
* are merged, and the result is pushed back onto the stack.
|
||||
* - If this element has #type defined and the default attributes for this
|
||||
* element have not already been merged in (#defaults_loaded = TRUE) then
|
||||
* the defaults for this type of element, defined in hook_element_info(),
|
||||
|
@ -2625,6 +2653,12 @@ function drupal_render_page($page) {
|
|||
* called sequentially to modify the element before rendering. After all the
|
||||
* #pre_render functions have been called, #printed is checked a second time
|
||||
* in case a #pre_render function flags the element as printed.
|
||||
* If #printed is set, we return early and hence no rendering work is left
|
||||
* to be done, similarly to a render cache hit. Once again, the empty (and
|
||||
* topmost) frame that was just pushed onto the stack is updated with all
|
||||
* bubbleable rendering metadata from the element whose #printed = TRUE.
|
||||
* Then, this stack frame is bubbled: the two topmost frames are popped from
|
||||
* the stack, they are merged, and the result is pushed back onto the stack.
|
||||
* - The child elements of this element are sorted by weight using uasort() in
|
||||
* \Drupal\Core\Render\Element::children(). Since this is expensive, when
|
||||
* passing already sorted elements to drupal_render(), for example from a
|
||||
|
@ -2684,20 +2718,50 @@ function drupal_render_page($page) {
|
|||
* attribute as a string and the element itself.
|
||||
* - If this element has #prefix and/or #suffix defined, they are concatenated
|
||||
* to #children.
|
||||
* - The rendering of this element is now complete. The next step will be
|
||||
* render caching. So this is the perfect time to update the the stack. At
|
||||
* this point, children of this element (if any), have been rendered also,
|
||||
* and if there were any, their bubbleable rendering metadata will have been
|
||||
* bubbled up into the stack frame for the element that is currently being
|
||||
* rendered. The render cache item for this element must contain the
|
||||
* bubbleable rendering metadata for this element and all of its children.
|
||||
* However, right now, the topmost stack frame (the one for this element)
|
||||
* currently only contains the metadata for the children. Therefore, the
|
||||
* topmost stack frame is updated with this element's metadata, and then the
|
||||
* element's metadata is replaced with the metadata in the topmost stack
|
||||
* frame. This element now contains all bubbleable rendering metadata for
|
||||
* this element and all its children, so it's now ready for render caching.
|
||||
* - If this element has #cache defined, the rendered output of this element
|
||||
* is saved to drupal_render()'s internal cache. This includes the changes
|
||||
* made by #post_render.
|
||||
* - If this element (or any of its children) has an array of
|
||||
* #post_render_cache functions defined, they are called sequentially to
|
||||
* replace placeholders in the final #markup and extend #attached.
|
||||
* Placeholders must contain a unique token, to guarantee that e.g. samples
|
||||
* of placeholders are not replaced also. For this, a special element named
|
||||
* 'render_cache_placeholder' is provided.
|
||||
* - If this element has an array of #post_render_cache functions defined, or
|
||||
* any of its children has (which we would know thanks to the stack having
|
||||
* been updated just before the render caching step), they are called
|
||||
* sequentially to replace placeholders in the final #markup and extend
|
||||
* #attached. Placeholders must contain a unique token, to guarantee that
|
||||
* e.g. samples of placeholders are not replaced also.
|
||||
* But, since #post_render_cache callbacks add attach additional assets, the
|
||||
* correct bubbling of those must once again be taken into account. This
|
||||
* final stage of rendering should be considered as if it were the parent of
|
||||
* the current element, because it takes that as its input, and then alters
|
||||
* its #markup. Hence, just before calling the #post_render_cache callbacks,
|
||||
* a new empty frame is pushed onto the stack, where all assets #attached
|
||||
* during the execution of those callbacks will end up in. Then, after the
|
||||
* execution of those callbacks, we merge that back into the element.
|
||||
* Note that these callbacks run always: when hitting the render cache, when
|
||||
* missing, or when render caching is not used at all. This is done to allow
|
||||
* any Drupal module to customize other render arrays without breaking the
|
||||
* render cache if it is enabled, and to not require it to use other logic
|
||||
* when render caching is disabled.
|
||||
* - Just before finishing the rendering of this element, this element's stack
|
||||
* frame (the topmost one) is bubbled: the two topmost frames are popped
|
||||
* from the stack, they are merged and the result is pushed back onto the
|
||||
* stack.
|
||||
* So if this element e.g. was a child element, then a new frame was pushed
|
||||
* onto the stack element at the beginning of rendering this element, it was
|
||||
* updated when the rendering was completed, and now we merge it with the
|
||||
* frame for the parent, so that the parent now has the bubbleable rendering
|
||||
* metadata for its child.
|
||||
* - #printed is set to TRUE for this element to ensure that it is only
|
||||
* rendered once.
|
||||
* - The final value of #children for this element is returned as the rendered
|
||||
|
@ -2717,6 +2781,35 @@ function drupal_render_page($page) {
|
|||
* @see drupal_process_attached()
|
||||
*/
|
||||
function drupal_render(&$elements, $is_recursive_call = FALSE) {
|
||||
static $stack;
|
||||
|
||||
$update_stack = function(&$element) use (&$stack) {
|
||||
// The latest frame represents the bubbleable data for the subtree.
|
||||
$frame = $stack->top();
|
||||
// Update the frame, but also update the current element, to ensure it
|
||||
// contains up-to-date information in case it gets render cached.
|
||||
$frame->tags = $element['#cache']['tags'] = drupal_merge_cache_tags($element['#cache']['tags'], $frame->tags);
|
||||
$frame->attached = $element['#attached'] = drupal_merge_attached($element['#attached'], $frame->attached);
|
||||
$frame->postRenderCache = $element['#post_render_cache'] = NestedArray::mergeDeep($element['#post_render_cache'], $frame->postRenderCache);
|
||||
};
|
||||
|
||||
$bubble_stack = function() use (&$stack) {
|
||||
// If there's only one frame on the stack, then this is the root call, and
|
||||
// we can't bubble up further.
|
||||
if ($stack->count() === 1) {
|
||||
$stack->pop();
|
||||
return;
|
||||
}
|
||||
|
||||
// Merge the current and the parent stack frame.
|
||||
$current = $stack->pop();
|
||||
$parent = $stack->pop();
|
||||
$current->tags = drupal_merge_cache_tags($current->tags, $parent->tags);
|
||||
$current->attached = drupal_merge_attached($current->attached, $parent->attached);
|
||||
$current->postRenderCache = NestedArray::mergeDeep($current->postRenderCache, $parent->postRenderCache);
|
||||
$stack->push($current);
|
||||
};
|
||||
|
||||
// Early-return nothing if user does not have access.
|
||||
if (empty($elements) || (isset($elements['#access']) && !$elements['#access'])) {
|
||||
return '';
|
||||
|
@ -2727,6 +2820,11 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
|
|||
return '';
|
||||
}
|
||||
|
||||
if (!isset($stack)) {
|
||||
$stack = new \SplStack();
|
||||
}
|
||||
$stack->push(new RenderStackFrame());
|
||||
|
||||
// Try to fetch the prerendered element from cache, run any #post_render_cache
|
||||
// callbacks and return the final markup.
|
||||
if (isset($elements['#cache'])) {
|
||||
|
@ -2740,6 +2838,11 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
|
|||
_drupal_render_process_post_render_cache($elements);
|
||||
}
|
||||
$elements['#markup'] = SafeMarkup::set($elements['#markup']);
|
||||
// The render cache item contains all the bubbleable rendering metadata for
|
||||
// the subtree.
|
||||
$update_stack($elements);
|
||||
// Render cache hit, so rendering is finished, all necessary info collected!
|
||||
$bubble_stack();
|
||||
return $elements['#markup'];
|
||||
}
|
||||
}
|
||||
|
@ -2767,8 +2870,18 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
|
|||
}
|
||||
}
|
||||
|
||||
// Defaults for bubbleable rendering metadata.
|
||||
$elements['#cache']['tags'] = isset($elements['#cache']['tags']) ? $elements['#cache']['tags'] : array();
|
||||
$elements['#attached'] = isset($elements['#attached']) ? $elements['#attached'] : array();
|
||||
$elements['#post_render_cache'] = isset($elements['#post_render_cache']) ? $elements['#post_render_cache'] : array();
|
||||
|
||||
// Allow #pre_render to abort rendering.
|
||||
if (!empty($elements['#printed'])) {
|
||||
// The #printed element contains all the bubbleable rendering metadata for
|
||||
// the subtree.
|
||||
$update_stack($elements);
|
||||
// #printed, so rendering is finished, all necessary info collected!
|
||||
$bubble_stack();
|
||||
return '';
|
||||
}
|
||||
|
||||
|
@ -2777,12 +2890,6 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
|
|||
drupal_process_states($elements);
|
||||
}
|
||||
|
||||
// Add additional libraries, CSS, JavaScript and other custom
|
||||
// attached data associated with this element.
|
||||
if (!empty($elements['#attached'])) {
|
||||
drupal_process_attached($elements);
|
||||
}
|
||||
|
||||
// Get the children of the element, sorted by weight.
|
||||
$children = Element::children($elements, TRUE);
|
||||
|
||||
|
@ -2872,9 +2979,6 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
|
|||
if (is_string($callable) && strpos($callable, '::') === FALSE) {
|
||||
$callable = $controller_resolver->getControllerFromDefinition($callable);
|
||||
}
|
||||
else {
|
||||
$callable = $callable;
|
||||
}
|
||||
$elements['#children'] = call_user_func($callable, $elements['#children'], $elements);
|
||||
}
|
||||
}
|
||||
|
@ -2887,20 +2991,8 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
|
|||
$suffix = isset($elements['#suffix']) ? $elements['#suffix'] : '';
|
||||
$elements['#markup'] = $prefix . $elements['#children'] . $suffix;
|
||||
|
||||
// Collect all #post_render_cache callbacks associated with this element when:
|
||||
// - about to store this element in the render cache, or when;
|
||||
// - about to apply #post_render_cache callbacks.
|
||||
if (!$is_recursive_call || isset($elements['#cache'])) {
|
||||
$post_render_cache = drupal_render_collect_post_render_cache($elements);
|
||||
if ($post_render_cache) {
|
||||
$elements['#post_render_cache'] = $post_render_cache;
|
||||
}
|
||||
}
|
||||
// Collect all cache tags. This allows the caller of drupal_render() to also
|
||||
// access the complete list of cache tags.
|
||||
if (!$is_recursive_call || isset($elements['#cache'])) {
|
||||
$elements['#cache']['tags'] = drupal_render_collect_cache_tags($elements);
|
||||
}
|
||||
// We've rendered this element (and its subtree!), now update the stack.
|
||||
$update_stack($elements);
|
||||
|
||||
// Cache the processed element if #cache is set.
|
||||
if (isset($elements['#cache'])) {
|
||||
|
@ -2917,9 +3009,23 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
|
|||
// Only the case of a cache hit when #cache is enabled, is not handled here,
|
||||
// that is handled earlier in drupal_render().
|
||||
if (!$is_recursive_call) {
|
||||
// We've already called $update_stack() earlier, which updated both the
|
||||
// element and current stack frame. However,
|
||||
// _drupal_render_process_post_render_cache() can both change the element
|
||||
// further and create and render new child elements, so provide a fresh
|
||||
// stack frame to collect those additions, merge them back to the element,
|
||||
// and then update the current frame to match the modified element state.
|
||||
$stack->push(new RenderStackFrame());
|
||||
_drupal_render_process_post_render_cache($elements);
|
||||
$post_render_additions = $stack->pop();
|
||||
$elements['#cache']['tags'] = drupal_merge_cache_tags($elements['#cache']['tags'], $post_render_additions->tags);
|
||||
$elements['#attached'] = drupal_merge_attached($elements['#attached'], $post_render_additions->attached);
|
||||
$elements['#post_render_cache'] = NestedArray::mergeDeep($elements['#post_render_cache'], $post_render_additions->postRenderCache);
|
||||
}
|
||||
|
||||
// Rendering is finished, all necessary info collected!
|
||||
$bubble_stack();
|
||||
|
||||
$elements['#printed'] = TRUE;
|
||||
$elements['#markup'] = SafeMarkup::set($elements['#markup']);
|
||||
return $elements['#markup'];
|
||||
|
@ -3064,11 +3170,6 @@ function drupal_render_cache_get(array $elements) {
|
|||
|
||||
if (!empty($cid) && $cache = \Drupal::cache($bin)->get($cid)) {
|
||||
$cached_element = $cache->data;
|
||||
// Add additional libraries, JavaScript, CSS and other data attached
|
||||
// to this element.
|
||||
if (isset($cached_element['#attached'])) {
|
||||
drupal_process_attached($cached_element);
|
||||
}
|
||||
// Return the cached element.
|
||||
return $cached_element;
|
||||
}
|
||||
|
@ -3103,10 +3204,7 @@ function drupal_render_cache_set(&$markup, array $elements) {
|
|||
$data['#markup'] = $markup;
|
||||
|
||||
// Persist attached data associated with this element.
|
||||
$attached = drupal_render_collect_attached($elements, TRUE);
|
||||
if ($attached) {
|
||||
$data['#attached'] = $attached;
|
||||
}
|
||||
$data['#attached'] = $elements['#attached'];
|
||||
|
||||
// Persist #post_render_cache callbacks associated with this element.
|
||||
if (isset($elements['#post_render_cache'])) {
|
||||
|
@ -3204,161 +3302,9 @@ function _drupal_render_process_post_render_cache(array &$elements) {
|
|||
$elements = call_user_func_array($callable, array($elements, $context));
|
||||
}
|
||||
}
|
||||
// Make sure that any attachments added in #post_render_cache callbacks are
|
||||
// also executed.
|
||||
if (isset($elements['#attached'])) {
|
||||
drupal_process_attached($elements);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects #post_render_cache for an element and its children into a single
|
||||
* array.
|
||||
*
|
||||
* When caching elements, it is necessary to collect all #post_render_cache
|
||||
* callbacks into a single array, from both the element itself and all child
|
||||
* elements. This allows drupal_render() to execute all of them when the element
|
||||
* is retrieved from the render cache.
|
||||
*
|
||||
* Note: the theme system may render child elements directly (e.g. rendering a
|
||||
* node causes its template to be rendered, which causes the node links to be
|
||||
* drupal_render()ed). On top of that, the theme system transforms render arrays
|
||||
* into HTML strings. These two facts combined imply that it is impossible for
|
||||
* #post_render_cache callbacks to bubble up to the root of the render array.
|
||||
* Therefore, drupal_render_collect_post_render_cache() must be called *before*
|
||||
* #theme callbacks, so that it has a chance to examine the full render array.
|
||||
* In short: in order to examine the full render array for #post_render_cache
|
||||
* callbacks, it must use post-order tree traversal, whereas drupal_render()
|
||||
* itself uses pre-order tree traversal.
|
||||
*
|
||||
* @param array &$elements
|
||||
* The element to collect #post_render_cache callbacks for.
|
||||
* @param array $callbacks
|
||||
* Internal use only. The #post_render_callbacks array so far.
|
||||
* @param bool $is_root_element
|
||||
* Internal use only. Whether the element being processed is the root or not.
|
||||
*
|
||||
* @return
|
||||
* The #post_render_cache array for this element and its descendants.
|
||||
*
|
||||
* @see drupal_render()
|
||||
* @see _drupal_render_process_post_render_cache()
|
||||
*/
|
||||
function drupal_render_collect_post_render_cache(array &$elements, array $callbacks = array(), $is_root_element = TRUE) {
|
||||
// Try to fetch the prerendered element from cache, to determine
|
||||
// #post_render_cache callbacks for this element and all its children. If we
|
||||
// don't do this, then the #post_render_cache tokens will be re-generated, but
|
||||
// they would no longer match the tokens in the render cached markup, causing
|
||||
// the render cache placeholder markup to be sent to the end user!
|
||||
$retrieved_from_cache = FALSE;
|
||||
if (!$is_root_element && isset($elements['#cache'])) {
|
||||
$cached_element = drupal_render_cache_get($elements);
|
||||
if ($cached_element !== FALSE && isset($cached_element['#post_render_cache'])) {
|
||||
$elements['#post_render_cache'] = $cached_element['#post_render_cache'];
|
||||
$retrieved_from_cache = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// Collect all #post_render_cache callbacks for this element.
|
||||
if (isset($elements['#post_render_cache'])) {
|
||||
$callbacks = NestedArray::mergeDeep($callbacks, $elements['#post_render_cache']);
|
||||
}
|
||||
|
||||
// Collect the #post_render_cache callbacks for all child elements, unless
|
||||
// we've already collected them above by retrieving this element (and its
|
||||
// children) from the render cache.
|
||||
if (!$retrieved_from_cache && $children = Element::children($elements)) {
|
||||
foreach ($children as $child) {
|
||||
$callbacks = drupal_render_collect_post_render_cache($elements[$child], $callbacks, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
return $callbacks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects #attached for an element and its children 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'])) {
|
||||
$attached = drupal_merge_attached($attached, $elements['#attached']);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects cache tags for an element and its children into a single array.
|
||||
*
|
||||
* The cache tags array is returned in a format that is valid for
|
||||
* \Drupal\Core\Cache\CacheBackendInterface::set().
|
||||
*
|
||||
* When caching elements, it is necessary to collect all cache tags into a
|
||||
* single array, from both the element itself and all child elements. This
|
||||
* allows items to be invalidated based on all tags attached to the content
|
||||
* they're constituted from.
|
||||
*
|
||||
* @param array $element
|
||||
* The element to collect cache tags from.
|
||||
* @param array $tags
|
||||
* (optional) An array of already collected cache tags (i.e. from a parent
|
||||
* element). Defaults to an empty array.
|
||||
*
|
||||
* @return array
|
||||
* The cache tags array for this element and its descendants.
|
||||
*/
|
||||
function drupal_render_collect_cache_tags($element, $tags = array()) {
|
||||
if (isset($element['#cache']['tags'])) {
|
||||
foreach ($element['#cache']['tags'] as $namespace => $values) {
|
||||
if (is_array($values)) {
|
||||
foreach ($values as $value) {
|
||||
$tags[$namespace][$value] = $value;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!isset($tags[$namespace])) {
|
||||
$tags[$namespace] = $values;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($children = Element::children($element)) {
|
||||
foreach ($children as $child) {
|
||||
$tags = drupal_render_collect_cache_tags($element[$child], $tags);
|
||||
}
|
||||
}
|
||||
|
||||
return $tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the cache ID for a renderable element.
|
||||
*
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Component\Utility\String;
|
||||
use Drupal\Component\Utility\UrlHelper;
|
||||
use Drupal\Component\Utility\Xss;
|
||||
use Drupal\Core\Config\Config;
|
||||
use Drupal\Core\Config\StorageException;
|
||||
|
@ -1750,6 +1751,14 @@ function template_preprocess_html(&$variables) {
|
|||
$page->addMetaElement($metatag);
|
||||
}
|
||||
}
|
||||
|
||||
// Add favicon.
|
||||
if (theme_get_setting('features.favicon')) {
|
||||
$url = UrlHelper::stripDangerousProtocols(theme_get_setting('favicon.url'));
|
||||
$link = new LinkElement($url, 'shortcut icon', ['type' => theme_get_setting('favicon.mimetype')]);
|
||||
$page->addLinkElement($link);
|
||||
}
|
||||
|
||||
$variables['page_top'][] = array('#markup' => $page->getBodyTop());
|
||||
$variables['page_bottom'][] = array('#markup' => $page->getBodyBottom());
|
||||
}
|
||||
|
|
|
@ -78,7 +78,9 @@ class AjaxResponseRenderer {
|
|||
* @todo: Remove as part of https://drupal.org/node/2182149
|
||||
*/
|
||||
protected function drupalRender(&$elements, $is_recursive_call = FALSE) {
|
||||
return drupal_render($elements, $is_recursive_call);
|
||||
$output = drupal_render($elements, $is_recursive_call);
|
||||
drupal_process_attached($elements);
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -150,7 +150,7 @@ class OpenDialogCommand implements CommandInterface {
|
|||
*/
|
||||
protected function drupalAttachLibrary($name) {
|
||||
$attached['#attached']['library'][] = $name;
|
||||
drupal_render($attached);
|
||||
drupal_process_attached($attached);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -93,6 +93,7 @@ class DialogController {
|
|||
}
|
||||
|
||||
$content = drupal_render($page_content);
|
||||
drupal_process_attached($page_content);
|
||||
$title = isset($page_content['#title']) ? $page_content['#title'] : $this->titleResolver->getTitle($request, $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT));
|
||||
$response = new AjaxResponse();
|
||||
// Fetch any modal options passed in from data-dialog-options.
|
||||
|
|
|
@ -73,6 +73,9 @@ class HtmlControllerBase {
|
|||
}
|
||||
|
||||
$content = $this->drupalRender($page_content);
|
||||
if (!empty($page_content)) {
|
||||
drupal_process_attached($page_content);
|
||||
}
|
||||
$cache = !empty($page_content['#cache']['tags']) ? array('tags' => $page_content['#cache']['tags']) : array();
|
||||
$fragment = new HtmlFragment($content, $cache);
|
||||
|
||||
|
@ -85,7 +88,7 @@ class HtmlControllerBase {
|
|||
}
|
||||
|
||||
// Add feed links from the page content.
|
||||
$attached = drupal_render_collect_attached($page_content, TRUE);
|
||||
$attached = isset($page_content['#attached']) ? $page_content['#attached'] : array();
|
||||
if (!empty($attached['drupal_add_feed'])) {
|
||||
foreach ($attached['drupal_add_feed'] as $feed) {
|
||||
$feed_link = new FeedLinkElement($feed[1], $this->urlGenerator->generateFromPath($feed[0]));
|
||||
|
|
|
@ -59,6 +59,14 @@ class DefaultHtmlFragmentRenderer implements HtmlFragmentRendererInterface {
|
|||
$page->setContent(drupal_render($page_array));
|
||||
$page->setStatusCode($status_code);
|
||||
|
||||
drupal_process_attached($page_array);
|
||||
if (isset($page_array['page_top'])) {
|
||||
drupal_process_attached($page_array['page_top']);
|
||||
}
|
||||
if (isset($page_array['page_bottom'])) {
|
||||
drupal_process_attached($page_array['page_bottom']);
|
||||
}
|
||||
|
||||
if ($fragment instanceof CacheableInterface) {
|
||||
// Collect cache tags for all the content in all the regions on the page.
|
||||
$tags = $page_array['#cache']['tags'];
|
||||
|
@ -102,6 +110,21 @@ class DefaultHtmlFragmentRenderer implements HtmlFragmentRendererInterface {
|
|||
$page->addLinkElement($link);
|
||||
}
|
||||
|
||||
// Add libraries and CSS used by this theme.
|
||||
$active_theme = \Drupal::theme()->getActiveTheme();
|
||||
foreach ($active_theme->getLibraries() as $library) {
|
||||
$page_array['#attached']['library'][] = $library;
|
||||
}
|
||||
foreach ($active_theme->getStyleSheets() as $media => $stylesheets) {
|
||||
foreach ($stylesheets as $stylesheet) {
|
||||
$page_array['#attached']['css'][$stylesheet] = array(
|
||||
'group' => CSS_AGGREGATE_THEME,
|
||||
'every_page' => TRUE,
|
||||
'media' => $media
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $page;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,15 @@ class DefaultHtmlPageRenderer implements HtmlPageRendererInterface {
|
|||
'#type' => 'html',
|
||||
'#page_object' => $page,
|
||||
);
|
||||
// drupal_render() will render the 'html' template, which will call
|
||||
// HtmlPage::getScripts(). But normally we can only run
|
||||
// drupal_process_attached() after drupal_render(). Hence any assets
|
||||
// attached to '#type' => 'html' will be lost. This is a work-around for
|
||||
// that limitation, until the HtmlPage object contains its assets — this is
|
||||
// an unfortunate intermediate consequence of the way HtmlPage dictates page
|
||||
// rendering and how that differs from how drupal_render() works.
|
||||
$render += element_info($render['#type']);
|
||||
drupal_process_attached($render);
|
||||
return drupal_render($render);
|
||||
}
|
||||
|
||||
|
@ -102,6 +111,13 @@ class DefaultHtmlPageRenderer implements HtmlPageRendererInterface {
|
|||
$page->setBodyTop(drupal_render($page_array['page_top']));
|
||||
$page->setBodyBottom(drupal_render($page_array['page_bottom']));
|
||||
$page->setContent(drupal_render($page_array));
|
||||
drupal_process_attached($page_array);
|
||||
if (isset($page_array['page_top'])) {
|
||||
drupal_process_attached($page_array['page_top']);
|
||||
}
|
||||
if (isset($page_array['page_bottom'])) {
|
||||
drupal_process_attached($page_array['page_bottom']);
|
||||
}
|
||||
|
||||
return \Drupal::service('html_page_renderer')->render($page);
|
||||
}
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
|
||||
namespace Drupal\Core\Render\Element;
|
||||
|
||||
use Drupal\Component\Utility\UrlHelper;
|
||||
|
||||
/**
|
||||
* Provides a render element for <html>.
|
||||
*
|
||||
|
@ -20,12 +18,8 @@ class Html extends RenderElement {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInfo() {
|
||||
$class = get_class($this);
|
||||
return array(
|
||||
'#theme' => 'html',
|
||||
'#pre_render' => array(
|
||||
array($class, 'preRenderHtml'),
|
||||
),
|
||||
// HTML5 Shiv
|
||||
'#attached' => array(
|
||||
'library' => array('core/html5shiv'),
|
||||
|
@ -33,35 +27,4 @@ class Html extends RenderElement {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* #pre_render callback for the html element type.
|
||||
*
|
||||
* @param array $element
|
||||
* A structured array containing the html element type build properties.
|
||||
*
|
||||
* @return array
|
||||
* The processed element.
|
||||
*/
|
||||
public static function preRenderHtml($element) {
|
||||
// Add favicon.
|
||||
if (static::themeGetSetting('features.favicon')) {
|
||||
$favicon = static::themeGetSetting('favicon.url');
|
||||
$type = static::themeGetSetting('favicon.mimetype');
|
||||
$element['#attached']['drupal_add_html_head_link'][][] = array(
|
||||
'rel' => 'shortcut icon',
|
||||
'href' => UrlHelper::stripDangerousProtocols($favicon),
|
||||
'type' => $type,
|
||||
);
|
||||
}
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps theme_get_setting().
|
||||
*/
|
||||
protected static function themeGetSetting($setting_name) {
|
||||
return theme_get_setting($setting_name);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -56,7 +56,9 @@ class ElementInfoManager extends DefaultPluginManager implements ElementInfoMana
|
|||
if (!isset($this->elementInfo)) {
|
||||
$this->elementInfo = $this->buildInfo();
|
||||
}
|
||||
return isset($this->elementInfo[$type]) ? $this->elementInfo[$type] : array();
|
||||
$info = isset($this->elementInfo[$type]) ? $this->elementInfo[$type] : array();
|
||||
$info['#defaults_loaded'] = TRUE;
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Render\RenderStackFrame.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Render;
|
||||
|
||||
/**
|
||||
* Value object used for bubbleable rendering metadata.
|
||||
*
|
||||
* @see drupal_render()
|
||||
*/
|
||||
class RenderStackFrame {
|
||||
|
||||
/**
|
||||
* Cache tags.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $tags = [];
|
||||
|
||||
/**
|
||||
* Attached assets.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $attached = [];
|
||||
|
||||
/**
|
||||
* #post_render_cache metadata.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $postRenderCache = [];
|
||||
|
||||
}
|
|
@ -741,7 +741,7 @@ function template_preprocess_comment(&$variables) {
|
|||
}
|
||||
|
||||
if (isset($variables['elements']['signature'])) {
|
||||
$variables['signature'] = $variables['elements']['signature']['#markup'];
|
||||
$variables['signature'] = $variables['elements']['signature'];
|
||||
unset($variables['elements']['signature']);
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -72,7 +72,7 @@ class CommentPostRenderCache {
|
|||
$form = $this->entityFormBuilder->getForm($comment);
|
||||
// @todo: This only works as long as assets are still tracked in a global
|
||||
// static variable, see https://drupal.org/node/2238835
|
||||
$markup = drupal_render($form, TRUE);
|
||||
$markup = drupal_render($form);
|
||||
|
||||
$callback = 'comment.post_render_cache:renderForm';
|
||||
$placeholder = drupal_render_cache_generate_placeholder($callback, $context);
|
||||
|
|
|
@ -138,11 +138,6 @@ class CommentViewBuilder extends EntityViewBuilder {
|
|||
'#format' => $account->getSignatureFormat(),
|
||||
'#langcode' => $entity->language()->getId(),
|
||||
);
|
||||
// The signature will only be rendered in the theme layer, which means
|
||||
// its associated cache tags will not bubble up. Work around this for
|
||||
// now by already rendering the signature here.
|
||||
// @todo remove this work-around, see https://drupal.org/node/2273277
|
||||
drupal_render($build[$id]['signature'], TRUE);
|
||||
}
|
||||
|
||||
if (!isset($build[$id]['#attached'])) {
|
||||
|
|
|
@ -162,20 +162,6 @@ class CommentDefaultFormatter extends FormatterBase implements ContainerFactoryP
|
|||
if ($this->getSetting('pager_id')) {
|
||||
$build['pager']['#element'] = $this->getSetting('pager_id');
|
||||
}
|
||||
// The viewElements() method of entity field formatters is run
|
||||
// during the #pre_render phase of rendering an entity. A formatter
|
||||
// builds the content of the field in preparation for theming.
|
||||
// All entity cache tags must be available after the #pre_render phase.
|
||||
// This field formatter is highly exceptional: it renders *another*
|
||||
// entity and this referenced entity has its own #pre_render
|
||||
// callbacks. In order collect the cache tags associated with the
|
||||
// referenced entity it must be passed to drupal_render() so that its
|
||||
// #pre_render callbacks are invoked and its full build array is
|
||||
// assembled. Rendering the referenced entity in place here will allow
|
||||
// its cache tags to be bubbled up and included with those of the
|
||||
// main entity when cache tags are collected for a renderable array
|
||||
// in drupal_render().
|
||||
drupal_render($build, TRUE);
|
||||
$output['comments'] = $build;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,7 +76,8 @@ class CommentCacheTagsTest extends EntityWithUriCacheTagsTestBase {
|
|||
* Each comment must have a comment body, which always has a text format.
|
||||
*/
|
||||
protected function getAdditionalCacheTagsForEntity(EntityInterface $entity) {
|
||||
return array('filter_format:plain_text');
|
||||
/** @var \Drupal\comment\CommentInterface $entity */
|
||||
return array('filter_format:plain_text', 'user:' . $entity->getOwnerId(), 'user_view:1');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ class CommentDefaultFormatterCacheTagsTest extends EntityUnitTestBase {
|
|||
drupal_render($build);
|
||||
$expected_cache_tags = array(
|
||||
'entity_test_view' => TRUE,
|
||||
'entity_test' => array(1 => $commented_entity->id()),
|
||||
'entity_test' => array($commented_entity->id()),
|
||||
);
|
||||
$this->assertEqual($build['#cache']['tags'], $expected_cache_tags, 'The test entity has the expected cache tags before it has comments.');
|
||||
|
||||
|
@ -102,12 +102,14 @@ class CommentDefaultFormatterCacheTagsTest extends EntityUnitTestBase {
|
|||
drupal_render($build);
|
||||
$expected_cache_tags = array(
|
||||
'entity_test_view' => TRUE,
|
||||
'entity_test' => array(1 => $commented_entity->id()),
|
||||
'entity_test' => array($commented_entity->id()),
|
||||
'comment_view' => TRUE,
|
||||
'comment' => array(1 => $comment->id()),
|
||||
'filter_format' => array(
|
||||
'plain_text' => 'plain_text',
|
||||
),
|
||||
'user_view' => TRUE,
|
||||
'user' => array(2 => 2),
|
||||
);
|
||||
$this->assertEqual($build['#cache']['tags'], $expected_cache_tags, 'The test entity has the expected cache tags when it has comments.');
|
||||
}
|
||||
|
|
|
@ -95,23 +95,7 @@ class EntityReferenceEntityFormatter extends EntityReferenceFormatterBase {
|
|||
}
|
||||
|
||||
if (!empty($item->target_id)) {
|
||||
// The viewElements() method of entity field formatters is run
|
||||
// during the #pre_render phase of rendering an entity. A formatter
|
||||
// builds the content of the field in preparation for theming.
|
||||
// All entity cache tags must be available after the #pre_render phase.
|
||||
// This field formatter is highly exceptional: it renders *another*
|
||||
// entity and this referenced entity has its own #pre_render
|
||||
// callbacks. In order collect the cache tags associated with the
|
||||
// referenced entity it must be passed to drupal_render() so that its
|
||||
// #pre_render callbacks are invoked and its full build array is
|
||||
// assembled. Rendering the referenced entity in place here will allow
|
||||
// its cache tags to be bubbled up and included with those of the
|
||||
// main entity when cache tags are collected for a renderable array
|
||||
// in drupal_render().
|
||||
// @todo remove this work-around, see https://drupal.org/node/2273277
|
||||
$referenced_entity_build = entity_view($item->entity, $view_mode, $item->getLangcode());
|
||||
drupal_render($referenced_entity_build, TRUE);
|
||||
$elements[$delta] = $referenced_entity_build;
|
||||
$elements[$delta] = entity_view($item->entity, $view_mode, $item->getLangcode());
|
||||
|
||||
if (empty($links) && isset($result[$delta][$target_type][$item->target_id]['links'])) {
|
||||
// Hide the element links.
|
||||
|
|
|
@ -181,10 +181,11 @@ class EntityReferenceFormatterTest extends EntityUnitTestBase {
|
|||
</div>
|
||||
</div>
|
||||
';
|
||||
drupal_render($build[0]);
|
||||
$this->assertEqual($build[0]['#markup'], 'default | ' . $this->referencedEntity->label() . $expected_rendered_name_field . $expected_rendered_body_field, format_string('The markup returned by the @formatter formatter is correct.', array('@formatter' => $formatter)));
|
||||
$expected_cache_tags = array(
|
||||
$this->entityType . '_view' => TRUE,
|
||||
$this->entityType => array($this->referencedEntity->id() => $this->referencedEntity->id()),
|
||||
$this->entityType => array($this->referencedEntity->id()),
|
||||
'filter_format' => array('full_html' => 'full_html'),
|
||||
);
|
||||
$this->assertEqual($build[0]['#cache']['tags'], $expected_cache_tags, format_string('The @formatter formatter has the expected cache tags.', array('@formatter' => $formatter)));
|
||||
|
|
|
@ -118,7 +118,8 @@ class DisplayApiTest extends FieldUnitTestBase {
|
|||
$items = $this->entity->get($this->field_name);
|
||||
|
||||
// No display settings: check that default display settings are used.
|
||||
$this->render($items->view());
|
||||
$build = $items->view();
|
||||
$this->render($build);
|
||||
$settings = \Drupal::service('plugin.manager.field.formatter')->getDefaultSettings('field_test_default');
|
||||
$setting = $settings['test_formatter_setting'];
|
||||
$this->assertText($this->label, 'Label was displayed.');
|
||||
|
@ -135,7 +136,8 @@ class DisplayApiTest extends FieldUnitTestBase {
|
|||
'alter' => TRUE,
|
||||
),
|
||||
);
|
||||
$this->render($items->view($display));
|
||||
$build = $items->view($display);
|
||||
$this->render($build);
|
||||
$setting = $display['settings']['test_formatter_setting_multiple'];
|
||||
$this->assertNoText($this->label, 'Label was not displayed.');
|
||||
$this->assertText('field_test_entity_display_build_alter', 'Alter fired, display passed.');
|
||||
|
@ -155,7 +157,8 @@ class DisplayApiTest extends FieldUnitTestBase {
|
|||
'alter' => TRUE,
|
||||
),
|
||||
);
|
||||
$this->render($items->view($display));
|
||||
$build = $items->view($display);
|
||||
$this->render($build);
|
||||
$setting = $display['settings']['test_formatter_setting_multiple'];
|
||||
$this->assertRaw('visually-hidden', 'Label was visually hidden.');
|
||||
$this->assertText('field_test_entity_display_build_alter', 'Alter fired, display passed.');
|
||||
|
@ -174,7 +177,8 @@ class DisplayApiTest extends FieldUnitTestBase {
|
|||
'test_formatter_setting_additional' => $this->randomMachineName(),
|
||||
),
|
||||
);
|
||||
$this->render($items->view($display));
|
||||
$build = $items->view($display);
|
||||
$this->render($build);
|
||||
$setting = $display['settings']['test_formatter_setting_additional'];
|
||||
$this->assertNoText($this->label, 'Label was not displayed.');
|
||||
$this->assertNoText('field_test_entity_display_build_alter', 'Alter not fired.');
|
||||
|
@ -184,7 +188,8 @@ class DisplayApiTest extends FieldUnitTestBase {
|
|||
|
||||
// View mode: check that display settings specified in the display object
|
||||
// are used.
|
||||
$this->render($items->view('teaser'));
|
||||
$build = $items->view('teaser');
|
||||
$this->render($build);
|
||||
$setting = $this->display_options['teaser']['settings']['test_formatter_setting'];
|
||||
$this->assertText($this->label, 'Label was displayed.');
|
||||
foreach ($this->values as $delta => $value) {
|
||||
|
@ -193,7 +198,8 @@ class DisplayApiTest extends FieldUnitTestBase {
|
|||
|
||||
// Unknown view mode: check that display settings for 'default' view mode
|
||||
// are used.
|
||||
$this->render($items->view('unknown_view_mode'));
|
||||
$build = $items->view('unknown_view_mode');
|
||||
$this->render($build);
|
||||
$setting = $this->display_options['default']['settings']['test_formatter_setting'];
|
||||
$this->assertText($this->label, 'Label was displayed.');
|
||||
foreach ($this->values as $delta => $value) {
|
||||
|
@ -210,7 +216,8 @@ class DisplayApiTest extends FieldUnitTestBase {
|
|||
$setting = $settings['test_formatter_setting'];
|
||||
foreach ($this->values as $delta => $value) {
|
||||
$item = $this->entity->{$this->field_name}[$delta];
|
||||
$this->render($item->view());
|
||||
$build = $item->view();
|
||||
$this->render($build);
|
||||
$this->assertText($setting . '|' . $value['value'], format_string('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
|
||||
}
|
||||
|
||||
|
@ -224,7 +231,8 @@ class DisplayApiTest extends FieldUnitTestBase {
|
|||
$setting = $display['settings']['test_formatter_setting_multiple'];
|
||||
foreach ($this->values as $delta => $value) {
|
||||
$item = $this->entity->{$this->field_name}[$delta];
|
||||
$this->render($item->view($display));
|
||||
$build = $item->view($display);
|
||||
$this->render($build);
|
||||
$this->assertText($setting . '|0:' . $value['value'], format_string('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
|
||||
}
|
||||
|
||||
|
@ -238,7 +246,8 @@ class DisplayApiTest extends FieldUnitTestBase {
|
|||
$setting = $display['settings']['test_formatter_setting_additional'];
|
||||
foreach ($this->values as $delta => $value) {
|
||||
$item = $this->entity->{$this->field_name}[$delta];
|
||||
$this->render($item->view($display));
|
||||
$build = $item->view($display);
|
||||
$this->render($build);
|
||||
$this->assertText($setting . '|' . $value['value'] . '|' . ($value['value'] + 1), format_string('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
|
||||
}
|
||||
|
||||
|
@ -247,7 +256,8 @@ class DisplayApiTest extends FieldUnitTestBase {
|
|||
$setting = $this->display_options['teaser']['settings']['test_formatter_setting'];
|
||||
foreach ($this->values as $delta => $value) {
|
||||
$item = $this->entity->{$this->field_name}[$delta];
|
||||
$this->render($item->view('teaser'));
|
||||
$build = $item->view('teaser');
|
||||
$this->render($build);
|
||||
$this->assertText($setting . '|' . $value['value'], format_string('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
|
||||
}
|
||||
|
||||
|
@ -256,7 +266,8 @@ class DisplayApiTest extends FieldUnitTestBase {
|
|||
$setting = $this->display_options['default']['settings']['test_formatter_setting'];
|
||||
foreach ($this->values as $delta => $value) {
|
||||
$item = $this->entity->{$this->field_name}[$delta];
|
||||
$this->render($item->view('unknown_view_mode'));
|
||||
$build = $item->view('unknown_view_mode');
|
||||
$this->render($build);
|
||||
$this->assertText($setting . '|' . $value['value'], format_string('Value @delta was displayed with expected setting.', array('@delta' => $delta)));
|
||||
}
|
||||
}
|
||||
|
@ -275,7 +286,8 @@ class DisplayApiTest extends FieldUnitTestBase {
|
|||
);
|
||||
// $this->entity is set by the setUp() method and by default contains 4
|
||||
// numeric values. We only want to test the display of this one field.
|
||||
$this->render($this->entity->get($this->field_name)->view($display));
|
||||
$build = $this->entity->get($this->field_name)->view($display);
|
||||
$this->render($build);
|
||||
// The test field by default contains values, so should not display the
|
||||
// default "empty" text.
|
||||
$this->assertNoText($display['settings']['test_empty_string']);
|
||||
|
@ -283,7 +295,8 @@ class DisplayApiTest extends FieldUnitTestBase {
|
|||
// Now remove the values from the test field and retest.
|
||||
$this->entity->{$this->field_name} = array();
|
||||
$this->entity->save();
|
||||
$this->render($this->entity->get($this->field_name)->view($display));
|
||||
$build = $this->entity->get($this->field_name)->view($display);
|
||||
$this->render($build);
|
||||
// This time, as the field values have been removed, we *should* show the
|
||||
// default "empty" text.
|
||||
$this->assertText($display['settings']['test_empty_string']);
|
||||
|
|
|
@ -75,6 +75,7 @@ class FileWidgetAjaxController extends FormAjaxController {
|
|||
$status_messages = array('#theme' => 'status_messages');
|
||||
$form['#prefix'] .= drupal_render($status_messages);
|
||||
$output = drupal_render($form);
|
||||
drupal_process_attached($form);
|
||||
$js = _drupal_add_js();
|
||||
$settings = drupal_merge_js_settings($js['settings']['data']);
|
||||
|
||||
|
|
|
@ -247,12 +247,12 @@ class FilterAPITest extends EntityUnitTestBase {
|
|||
$expected_cache_tags = array(
|
||||
// The cache tag set by the processed_text element itself.
|
||||
'filter_format' => array(
|
||||
'element_test' => 'element_test',
|
||||
'element_test',
|
||||
),
|
||||
// The cache tags set by the filter_test_cache_tags filter.
|
||||
'foo' => array(
|
||||
'bar' => 'bar',
|
||||
'baz' => 'baz',
|
||||
'bar',
|
||||
'baz',
|
||||
),
|
||||
);
|
||||
$this->assertEqual($expected_cache_tags, $build['#cache']['tags'], 'Expected cache tags present.');
|
||||
|
|
|
@ -32,6 +32,7 @@ class LocaleLibraryInfoAlterTest extends WebTestBase {
|
|||
public function testLibraryInfoAlter() {
|
||||
$attached['#attached']['library'][] = 'core/jquery.ui.datepicker';
|
||||
drupal_render($attached);
|
||||
drupal_process_attached($attached);
|
||||
$scripts = drupal_get_js();
|
||||
$this->assertTrue(strpos($scripts, 'locale.datepicker.js'), 'locale.datepicker.js added to scripts.');
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ class NodeCacheTagsTest extends EntityWithUriCacheTagsTestBase {
|
|||
* Each node must have an author.
|
||||
*/
|
||||
protected function getAdditionalCacheTagsForEntity(EntityInterface $node) {
|
||||
return array('user:' . $node->getOwnerId());
|
||||
return array('user:' . $node->getOwnerId(), 'user_view:1');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -547,8 +547,9 @@ abstract class KernelTestBase extends UnitTestBase {
|
|||
* @return string
|
||||
* The rendered string output (typically HTML).
|
||||
*/
|
||||
protected function render(array $elements) {
|
||||
protected function render(array &$elements) {
|
||||
$content = drupal_render($elements);
|
||||
drupal_process_attached($elements);
|
||||
$this->setRawContent($content);
|
||||
$this->verbose('<pre style="white-space: pre-wrap">' . String::checkPlain($content));
|
||||
return $content;
|
||||
|
|
|
@ -18,6 +18,7 @@ use Drupal\Core\Database\Database;
|
|||
use Drupal\Core\Database\ConnectionNotDefinedException;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\Session\AnonymousUserSession;
|
||||
use Drupal\Core\Session\UserSession;
|
||||
|
@ -341,26 +342,37 @@ abstract class WebTestBase extends TestBase {
|
|||
* @see drupal_render()
|
||||
*/
|
||||
protected function drupalBuildEntityView(EntityInterface $entity, $view_mode = 'full', $langcode = NULL, $reset = FALSE) {
|
||||
$ensure_fully_built = function(&$elements) use (&$ensure_fully_built) {
|
||||
// If the default values for this element have not been loaded yet, populate
|
||||
// them.
|
||||
if (isset($elements['#type']) && empty($elements['#defaults_loaded'])) {
|
||||
$elements += element_info($elements['#type']);
|
||||
}
|
||||
|
||||
// Make any final changes to the element before it is rendered. This means
|
||||
// that the $element or the children can be altered or corrected before the
|
||||
// element is rendered into the final text.
|
||||
if (isset($elements['#pre_render'])) {
|
||||
foreach ($elements['#pre_render'] as $callable) {
|
||||
$elements = call_user_func($callable, $elements);
|
||||
}
|
||||
}
|
||||
|
||||
// And recurse.
|
||||
$children = Element::children($elements, TRUE);
|
||||
foreach ($children as $key) {
|
||||
$ensure_fully_built($elements[$key]);
|
||||
}
|
||||
};
|
||||
|
||||
$render_controller = $this->container->get('entity.manager')->getViewBuilder($entity->getEntityTypeId());
|
||||
if ($reset) {
|
||||
$render_controller->resetCache(array($entity->id()));
|
||||
}
|
||||
$elements = $render_controller->view($entity, $view_mode, $langcode);
|
||||
// If the default values for this element have not been loaded yet, populate
|
||||
// them.
|
||||
if (isset($elements['#type']) && empty($elements['#defaults_loaded'])) {
|
||||
$elements += element_info($elements['#type']);
|
||||
}
|
||||
$build = $render_controller->view($entity, $view_mode, $langcode);
|
||||
$ensure_fully_built($build);
|
||||
|
||||
// Make any final changes to the element before it is rendered. This means
|
||||
// that the $element or the children can be altered or corrected before the
|
||||
// element is rendered into the final text.
|
||||
if (isset($elements['#pre_render'])) {
|
||||
foreach ($elements['#pre_render'] as $callable) {
|
||||
$elements = call_user_func($callable, $elements);
|
||||
}
|
||||
}
|
||||
return $elements;
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -110,6 +110,14 @@ class BatchController implements ContainerInjectionInterface {
|
|||
$page->setBodyBottom(drupal_render($page_array['page_bottom']));
|
||||
$page->setContent(drupal_render($page_array));
|
||||
|
||||
drupal_process_attached($page_array);
|
||||
if (isset($page_array['page_top'])) {
|
||||
drupal_process_attached($page_array['page_top']);
|
||||
}
|
||||
if (isset($page_array['page_bottom'])) {
|
||||
drupal_process_attached($page_array['page_bottom']);
|
||||
}
|
||||
|
||||
$page->setStatusCode($status_code);
|
||||
|
||||
return $page;
|
||||
|
|
|
@ -50,6 +50,7 @@ class FrameworkTest extends AjaxTestBase {
|
|||
),
|
||||
);
|
||||
drupal_render($attached);
|
||||
drupal_process_attached($attached);
|
||||
$expected_commands[1] = new AddCssCommand(drupal_get_css(_drupal_add_css(), TRUE));
|
||||
drupal_static_reset('_drupal_add_js');
|
||||
$attached = array(
|
||||
|
@ -60,6 +61,7 @@ class FrameworkTest extends AjaxTestBase {
|
|||
),
|
||||
);
|
||||
drupal_render($attached);
|
||||
drupal_process_attached($attached);
|
||||
$expected_commands[2] = new PrependCommand('head', drupal_get_js('header', _drupal_add_js(), TRUE));
|
||||
drupal_static_reset('_drupal_add_js');
|
||||
$attached = array(
|
||||
|
@ -70,6 +72,7 @@ class FrameworkTest extends AjaxTestBase {
|
|||
),
|
||||
);
|
||||
drupal_render($attached);
|
||||
drupal_process_attached($attached);
|
||||
$expected_commands[3] = new AppendCommand('body', drupal_get_js('footer', _drupal_add_js(), TRUE));
|
||||
$expected_commands[4] = new HtmlCommand('body', 'Hello, world!');
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ class JavaScriptTest extends DrupalUnitTestBase {
|
|||
*/
|
||||
function testAddFile() {
|
||||
$attached['#attached']['js']['core/misc/collapse.js'] = array();
|
||||
drupal_render($attached);
|
||||
$this->render($attached);
|
||||
$javascript = _drupal_add_js();
|
||||
$this->assertTrue(array_key_exists('core/misc/collapse.js', $javascript), 'JavaScript files are correctly added.');
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ class JavaScriptTest extends DrupalUnitTestBase {
|
|||
function testAddSetting() {
|
||||
// Add a file in order to test default settings.
|
||||
$attached['#attached']['library'][] = 'core/drupalSettings';
|
||||
drupal_render($attached);
|
||||
$this->render($attached);
|
||||
$javascript = _drupal_add_js();
|
||||
$last_settings = reset($javascript['settings']['data']);
|
||||
$this->assertTrue(array_key_exists('currentPath', $last_settings['path']), 'The current path JavaScript setting is set correctly.');
|
||||
|
@ -90,7 +90,7 @@ class JavaScriptTest extends DrupalUnitTestBase {
|
|||
*/
|
||||
function testAddExternal() {
|
||||
$attached['#attached']['js']['http://example.com/script.js'] = array('type' => 'external');
|
||||
drupal_render($attached);
|
||||
$this->render($attached);
|
||||
$javascript = _drupal_add_js();
|
||||
$this->assertTrue(array_key_exists('http://example.com/script.js', $javascript), 'Added an external JavaScript file.');
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ class JavaScriptTest extends DrupalUnitTestBase {
|
|||
$attached['#attached']['js']['core/misc/collapse.js'] = array(
|
||||
'attributes' => array('defer' => 'defer'),
|
||||
);
|
||||
drupal_render($attached);
|
||||
$this->render($attached);
|
||||
$javascript = drupal_get_js();
|
||||
|
||||
$expected_1 = '<script src="http://example.com/script.js" defer="defer"></script>';
|
||||
|
@ -136,7 +136,7 @@ class JavaScriptTest extends DrupalUnitTestBase {
|
|||
$attached['#attached']['js']['core/misc/collapse.js'] = array(
|
||||
'attributes' => array('defer' => 'defer'),
|
||||
);
|
||||
drupal_render($attached);
|
||||
$this->render($attached);
|
||||
$javascript = drupal_get_js();
|
||||
|
||||
$expected_1 = '<script src="http://example.com/script.js" defer="defer"></script>';
|
||||
|
@ -152,7 +152,7 @@ class JavaScriptTest extends DrupalUnitTestBase {
|
|||
function testHeaderSetting() {
|
||||
$attached = array();
|
||||
$attached['#attached']['library'][] = 'core/drupalSettings';
|
||||
drupal_render($attached);
|
||||
$this->render($attached);
|
||||
|
||||
$javascript = drupal_get_js('header');
|
||||
$this->assertTrue(strpos($javascript, 'basePath') > 0, 'Rendered JavaScript header returns basePath setting.');
|
||||
|
@ -211,7 +211,7 @@ class JavaScriptTest extends DrupalUnitTestBase {
|
|||
'data' => array('commonTestRealWorldAlmostIdentical' => $settings_two),
|
||||
);
|
||||
|
||||
drupal_render($attached);
|
||||
$this->render($attached);
|
||||
$javascript = drupal_get_js('header');
|
||||
|
||||
// Test whether _drupal_add_js can be used to override a previous setting.
|
||||
|
@ -248,7 +248,7 @@ class JavaScriptTest extends DrupalUnitTestBase {
|
|||
function testReset() {
|
||||
$attached['#attached']['library'][] = 'core/drupal';
|
||||
$attached['#attached']['js']['core/misc/collapse.js'] = array();
|
||||
drupal_render($attached);
|
||||
$this->render($attached);
|
||||
drupal_static_reset('_drupal_add_js');
|
||||
$this->assertEqual(array(), _drupal_add_js(), 'Resetting the JavaScript correctly empties the cache.');
|
||||
}
|
||||
|
@ -264,7 +264,7 @@ class JavaScriptTest extends DrupalUnitTestBase {
|
|||
'data' => $inline,
|
||||
'attributes' => array('defer' => 'defer'),
|
||||
);
|
||||
drupal_render($attached);
|
||||
$this->render($attached);
|
||||
$javascript = _drupal_add_js();
|
||||
$this->assertTrue(array_key_exists('core/assets/vendor/jquery/jquery.js', $javascript), 'jQuery is added when inline scripts are added.');
|
||||
$data = end($javascript);
|
||||
|
@ -281,7 +281,7 @@ class JavaScriptTest extends DrupalUnitTestBase {
|
|||
'type' => 'external',
|
||||
'data' => $external,
|
||||
);
|
||||
drupal_render($attached);
|
||||
$this->render($attached);
|
||||
|
||||
$javascript = drupal_get_js();
|
||||
// Local files have a base_path() prefix, external files should not.
|
||||
|
@ -300,7 +300,7 @@ class JavaScriptTest extends DrupalUnitTestBase {
|
|||
'scope' => 'footer',
|
||||
'attributes' => array('defer' => 'defer'),
|
||||
);
|
||||
drupal_render($attached);
|
||||
$this->render($attached);
|
||||
|
||||
$javascript = drupal_get_js('footer');
|
||||
$this->assertTrue(strpos($javascript, $inline) > 0, 'Rendered JavaScript footer returns the inline code.');
|
||||
|
@ -312,7 +312,7 @@ class JavaScriptTest extends DrupalUnitTestBase {
|
|||
function testNoCache() {
|
||||
$attached['#attached']['library'][] = 'core/drupal';
|
||||
$attached['#attached']['js']['core/misc/collapse.js'] = array('cache' => FALSE);
|
||||
drupal_render($attached);
|
||||
$this->render($attached);
|
||||
$javascript = _drupal_add_js();
|
||||
$this->assertFalse($javascript['core/misc/collapse.js']['preprocess'], 'Setting cache to FALSE sets proprocess to FALSE when adding JavaScript.');
|
||||
}
|
||||
|
@ -323,7 +323,7 @@ class JavaScriptTest extends DrupalUnitTestBase {
|
|||
function testDifferentGroup() {
|
||||
$attached['#attached']['library'][] = 'core/drupal';
|
||||
$attached['#attached']['js']['core/misc/collapse.js'] = array('group' => JS_THEME);
|
||||
drupal_render($attached);
|
||||
$this->render($attached);
|
||||
$javascript = _drupal_add_js();
|
||||
$this->assertEqual($javascript['core/misc/collapse.js']['group'], JS_THEME, 'Adding a JavaScript file with a different group caches the given group.');
|
||||
}
|
||||
|
@ -333,7 +333,7 @@ class JavaScriptTest extends DrupalUnitTestBase {
|
|||
*/
|
||||
function testDifferentWeight() {
|
||||
$attached['#attached']['js']['core/misc/collapse.js'] = array('weight' => 2);
|
||||
drupal_render($attached);
|
||||
$this->render($attached);
|
||||
$javascript = _drupal_add_js();
|
||||
$this->assertEqual($javascript['core/misc/collapse.js']['weight'], 2, 'Adding a JavaScript file with a different weight caches the given weight.');
|
||||
}
|
||||
|
@ -355,7 +355,7 @@ class JavaScriptTest extends DrupalUnitTestBase {
|
|||
'data' => 'jQuery(function () { });',
|
||||
'browsers' => array('IE' => FALSE),
|
||||
);
|
||||
drupal_render($attached);
|
||||
$this->render($attached);
|
||||
$javascript = drupal_get_js();
|
||||
|
||||
$expected_1 = "<!--[if lte IE 8]>\n" . '<script src="' . file_create_url('core/misc/collapse.js') . '?' . $default_query_string . '"></script>' . "\n<![endif]-->";
|
||||
|
@ -372,7 +372,7 @@ class JavaScriptTest extends DrupalUnitTestBase {
|
|||
$attached['#attached']['library'][] = 'core/drupal';
|
||||
$attached['#attached']['js']['core/misc/collapse.js'] = array('version' => 'foo');
|
||||
$attached['#attached']['js']['core/misc/ajax.js'] = array('version' => 'bar');
|
||||
drupal_render($attached);
|
||||
$this->render($attached);
|
||||
$javascript = drupal_get_js();
|
||||
$this->assertTrue(strpos($javascript, 'core/misc/collapse.js?v=foo') > 0 && strpos($javascript, 'core/misc/ajax.js?v=bar') > 0 , 'JavaScript version identifiers correctly appended to URLs');
|
||||
}
|
||||
|
@ -393,7 +393,7 @@ class JavaScriptTest extends DrupalUnitTestBase {
|
|||
$attached['#attached']['js']['core/misc/collapse.js'] = array('every_page' => TRUE);
|
||||
$attached['#attached']['js']['core/misc/autocomplete.js'] = array();
|
||||
$attached['#attached']['js']['core/misc/batch.js'] = array('every_page' => TRUE);
|
||||
drupal_render($attached);
|
||||
$this->render($attached);
|
||||
$javascript = drupal_get_js();
|
||||
$expected = implode("\n", array(
|
||||
'<script src="' . file_create_url('core/misc/collapse.js') . '?' . $default_query_string . '"></script>',
|
||||
|
@ -415,7 +415,7 @@ class JavaScriptTest extends DrupalUnitTestBase {
|
|||
$attached['#attached']['js']['core/misc/collapse.js'] = array('every_page' => TRUE);
|
||||
$attached['#attached']['js']['core/misc/autocomplete.js'] = array();
|
||||
$attached['#attached']['js']['core/misc/batch.js'] = array('every_page' => TRUE);
|
||||
drupal_render($attached);
|
||||
$this->render($attached);
|
||||
$js_items = _drupal_add_js();
|
||||
$javascript = drupal_get_js();
|
||||
$expected = implode("\n", array(
|
||||
|
@ -438,7 +438,7 @@ class JavaScriptTest extends DrupalUnitTestBase {
|
|||
$attached['#attached']['library'][] = 'core/drupal';
|
||||
$attached['#attached']['js']['core/misc/ajax.js'] = array();
|
||||
$attached['#attached']['js']['core/misc/autocomplete.js'] = array();
|
||||
drupal_render($attached);
|
||||
$this->render($attached);
|
||||
|
||||
$js_items = _drupal_add_js();
|
||||
$scripts_html = array(
|
||||
|
@ -448,7 +448,7 @@ class JavaScriptTest extends DrupalUnitTestBase {
|
|||
'core/misc/autocomplete.js' => $js_items['core/misc/autocomplete.js']
|
||||
)
|
||||
);
|
||||
drupal_render($scripts_html);
|
||||
$this->render($scripts_html);
|
||||
|
||||
// Store the expected key for the first item in the cache.
|
||||
$cache = array_keys(\Drupal::state()->get('system.js_cache_files') ?: array());
|
||||
|
@ -462,7 +462,7 @@ class JavaScriptTest extends DrupalUnitTestBase {
|
|||
$attached['#attached']['js']['some/custom/javascript_file.js'] = array('scope' => 'footer');
|
||||
$attached['#attached']['js']['core/misc/ajax.js'] = array();
|
||||
$attached['#attached']['js']['core/misc/autocomplete.js'] = array();
|
||||
drupal_render($attached);
|
||||
$this->render($attached);
|
||||
|
||||
// Rebuild the cache.
|
||||
$js_items = _drupal_add_js();
|
||||
|
@ -473,7 +473,7 @@ class JavaScriptTest extends DrupalUnitTestBase {
|
|||
'core/misc/autocomplete.js' => $js_items['core/misc/autocomplete.js']
|
||||
)
|
||||
);
|
||||
drupal_render($scripts_html);
|
||||
$this->render($scripts_html);
|
||||
|
||||
// Compare the expected key for the first file to the current one.
|
||||
$cache = array_keys(\Drupal::state()->get('system.js_cache_files') ?: array());
|
||||
|
@ -528,7 +528,7 @@ class JavaScriptTest extends DrupalUnitTestBase {
|
|||
$attached['#attached']['js'][] = $shared_options + array(
|
||||
'data' => '(function($){alert("Weight 0 #3");})(jQuery);',
|
||||
);
|
||||
drupal_render($attached);
|
||||
$this->render($attached);
|
||||
|
||||
// Construct the expected result from the regex.
|
||||
$expected = array(
|
||||
|
@ -569,7 +569,7 @@ class JavaScriptTest extends DrupalUnitTestBase {
|
|||
'every_page' => TRUE,
|
||||
'weight' => -21,
|
||||
);
|
||||
drupal_render($attached);
|
||||
$this->render($attached);
|
||||
$javascript = drupal_get_js();
|
||||
$this->assertTrue(strpos($javascript, 'core/misc/collapse.js') < strpos($javascript, 'core/assets/vendor/jquery/jquery.js'), 'Rendering a JavaScript file above jQuery.');
|
||||
}
|
||||
|
@ -583,7 +583,7 @@ class JavaScriptTest extends DrupalUnitTestBase {
|
|||
// Add both tableselect.js and simpletest.js, with a larger weight on SimpleTest.
|
||||
$attached['#attached']['js']['core/misc/tableselect.js'] = array();
|
||||
$attached['#attached']['js'][drupal_get_path('module', 'simpletest') . '/simpletest.js'] = array('weight' => 9999);
|
||||
drupal_render($attached);
|
||||
$this->render($attached);
|
||||
|
||||
// Render the JavaScript, testing if simpletest.js was altered to be before
|
||||
// tableselect.js. See simpletest_js_alter() to see where this alteration
|
||||
|
@ -598,7 +598,7 @@ class JavaScriptTest extends DrupalUnitTestBase {
|
|||
function testLibraryRender() {
|
||||
$attached = array();
|
||||
$attached['#attached']['library'][] = 'core/jquery.farbtastic';
|
||||
drupal_render($attached);
|
||||
$this->render($attached);
|
||||
$scripts = drupal_get_js();
|
||||
$styles = drupal_get_css();
|
||||
$this->assertTrue(strpos($scripts, 'core/assets/vendor/farbtastic/farbtastic.js'), 'JavaScript of library was added to the page.');
|
||||
|
@ -619,7 +619,7 @@ class JavaScriptTest extends DrupalUnitTestBase {
|
|||
|
||||
// common_test_library_info_alter() also added a dependency on jQuery Form.
|
||||
$attached['#attached']['library'][] = 'core/jquery.farbtastic';
|
||||
drupal_render($attached);
|
||||
$this->render($attached);
|
||||
$scripts = drupal_get_js();
|
||||
$this->assertTrue(strpos($scripts, 'core/assets/vendor/jquery-form/jquery.form.js'), 'Altered library dependencies are added to the page.');
|
||||
}
|
||||
|
@ -647,7 +647,7 @@ class JavaScriptTest extends DrupalUnitTestBase {
|
|||
drupal_static_reset('drupal_get_library');
|
||||
|
||||
$attached['#attached']['library'][] = 'unknown/unknown';
|
||||
drupal_render($attached);
|
||||
$this->render($attached);
|
||||
$scripts = drupal_get_js();
|
||||
$this->assertTrue(strpos($scripts, 'unknown') === FALSE, 'Unknown library was not added to the page.');
|
||||
}
|
||||
|
@ -657,7 +657,7 @@ class JavaScriptTest extends DrupalUnitTestBase {
|
|||
*/
|
||||
function testAttachedLibrary() {
|
||||
$element['#attached']['library'][] = 'core/jquery.farbtastic';
|
||||
drupal_render($element);
|
||||
$this->render($element);
|
||||
$scripts = drupal_get_js();
|
||||
$this->assertTrue(strpos($scripts, 'core/assets/vendor/farbtastic/farbtastic.js'), 'The attached_library property adds the additional libraries.');
|
||||
}
|
||||
|
|
|
@ -354,20 +354,14 @@ class RenderTest extends DrupalUnitTestBase {
|
|||
|
||||
// Render the element and verify the presence of #attached JavaScript.
|
||||
drupal_render($element);
|
||||
$scripts = drupal_get_js();
|
||||
$this->assertTrue(strpos($scripts, $parent_js), 'The element #attached JavaScript was included.');
|
||||
$this->assertTrue(strpos($scripts, $child_js), 'The child #attached JavaScript was included.');
|
||||
$this->assertTrue(strpos($scripts, $subchild_js), 'The subchild #attached JavaScript was included.');
|
||||
$expected_js = [$parent_js, $child_js, $subchild_js];
|
||||
$this->assertEqual($element['#attached']['js'], $expected_js, 'The element, child and subchild #attached JavaScript are included.');
|
||||
|
||||
// Load the element from cache and verify the presence of the #attached
|
||||
// JavaScript.
|
||||
drupal_static_reset('_drupal_add_js');
|
||||
$element = array('#cache' => array('keys' => array('simpletest', 'drupal_render', 'children_attached')));
|
||||
$this->assertTrue(strlen(drupal_render($element)) > 0, 'The element was retrieved from cache.');
|
||||
$scripts = drupal_get_js();
|
||||
$this->assertTrue(strpos($scripts, $parent_js), 'The element #attached JavaScript was included when loading from cache.');
|
||||
$this->assertTrue(strpos($scripts, $child_js), 'The child #attached JavaScript was included when loading from cache.');
|
||||
$this->assertTrue(strpos($scripts, $subchild_js), 'The subchild #attached JavaScript was included when loading from cache.');
|
||||
$this->assertEqual($element['#attached']['js'], $expected_js, 'The element, child and subchild #attached JavaScript are included.');
|
||||
|
||||
// Restore the previous request method.
|
||||
\Drupal::request()->setMethod($request_method);
|
||||
|
@ -430,11 +424,11 @@ class RenderTest extends DrupalUnitTestBase {
|
|||
// Test that cache tags are correctly collected from the render element,
|
||||
// including the ones from its subchild.
|
||||
$expected_tags = array(
|
||||
'rendered' => TRUE,
|
||||
'render_cache_tag' => TRUE,
|
||||
'render_cache_tag_child' => array(1 => 1, 2 => 2),
|
||||
);
|
||||
$actual_tags = drupal_render_collect_cache_tags($test_element);
|
||||
$this->assertEqual($expected_tags, $actual_tags, 'Cache tags were collected from the element and its subchild.');
|
||||
$this->assertEqual($expected_tags, $element['#cache']['tags'], 'Cache tags were collected from the element and its subchild.');
|
||||
|
||||
// Restore the previous request method.
|
||||
\Drupal::request()->setMethod($request_method);
|
||||
|
@ -453,22 +447,22 @@ class RenderTest extends DrupalUnitTestBase {
|
|||
);
|
||||
|
||||
// #cache disabled.
|
||||
drupal_static_reset('_drupal_add_js');
|
||||
$element = $test_element;
|
||||
$element['#markup'] = '<p>#cache disabled</p>';
|
||||
$output = drupal_render($element);
|
||||
$this->assertIdentical($output, '<p>overridden</p>', 'Output is overridden.');
|
||||
$this->assertIdentical($element['#markup'], '<p>overridden</p>', '#markup is overridden.');
|
||||
$settings = $this->parseDrupalSettings(drupal_get_js());
|
||||
$this->assertIdentical($settings['foo'], 'bar', 'Original JavaScript setting is added to the page.');
|
||||
$this->assertIdentical($settings['common_test'], $context, '#attached is modified; JavaScript setting is added to page.');
|
||||
$expected_js = [
|
||||
['type' => 'setting', 'data' => ['foo' => 'bar']],
|
||||
['type' => 'setting', 'data' => ['common_test' => $context]],
|
||||
];
|
||||
$this->assertIdentical($element['#attached']['js'], $expected_js, '#attached is modified; both the original JavaScript setting and the one added by the #post_render_cache callback exist.');
|
||||
|
||||
// The cache system is turned off for POST requests.
|
||||
$request_method = \Drupal::request()->getMethod();
|
||||
\Drupal::request()->setMethod('GET');
|
||||
|
||||
// GET request: #cache enabled, cache miss.
|
||||
drupal_static_reset('_drupal_add_js');
|
||||
$element = $test_element;
|
||||
$element['#cache'] = array('cid' => 'post_render_cache_test_GET');
|
||||
$element['#markup'] = '<p>#cache enabled, GET</p>';
|
||||
|
@ -476,9 +470,11 @@ class RenderTest extends DrupalUnitTestBase {
|
|||
$this->assertIdentical($output, '<p>overridden</p>', 'Output is overridden.');
|
||||
$this->assertTrue(isset($element['#printed']), 'No cache hit');
|
||||
$this->assertIdentical($element['#markup'], '<p>overridden</p>', '#markup is overridden.');
|
||||
$settings = $this->parseDrupalSettings(drupal_get_js());
|
||||
$this->assertIdentical($settings['foo'], 'bar', 'Original JavaScript setting is added to the page.');
|
||||
$this->assertIdentical($settings['common_test'], $context, '#attached is modified; JavaScript setting is added to page.');
|
||||
$expected_js = [
|
||||
['type' => 'setting', 'data' => ['foo' => 'bar']],
|
||||
['type' => 'setting', 'data' => ['common_test' => $context]],
|
||||
];
|
||||
$this->assertIdentical($element['#attached']['js'], $expected_js, '#attached is modified; both the original JavaScript setting and the one added by the #post_render_cache callback exist.');
|
||||
|
||||
// GET request: validate cached data.
|
||||
$element = array('#cache' => array('cid' => 'post_render_cache_test_GET'));
|
||||
|
@ -492,23 +488,23 @@ class RenderTest extends DrupalUnitTestBase {
|
|||
$this->assertIdentical($cached_element, $expected_element, 'The correct data is cached: the stored #markup and #attached properties are not affected by #post_render_cache callbacks.');
|
||||
|
||||
// GET request: #cache enabled, cache hit.
|
||||
drupal_static_reset('_drupal_add_js');
|
||||
$element['#cache'] = array('cid' => 'post_render_cache_test_GET');
|
||||
$element['#markup'] = '<p>#cache enabled, GET</p>';
|
||||
$output = drupal_render($element);
|
||||
$this->assertIdentical($output, '<p>overridden</p>', 'Output is overridden.');
|
||||
$this->assertFalse(isset($element['#printed']), 'Cache hit');
|
||||
$this->assertIdentical($element['#markup'], '<p>overridden</p>', '#markup is overridden.');
|
||||
$settings = $this->parseDrupalSettings(drupal_get_js());
|
||||
$this->assertIdentical($settings['foo'], 'bar', 'Original JavaScript setting is added to the page.');
|
||||
$this->assertIdentical($settings['common_test'], $context, '#attached is modified; JavaScript setting is added to page.');
|
||||
$expected_js = [
|
||||
['type' => 'setting', 'data' => ['foo' => 'bar']],
|
||||
['type' => 'setting', 'data' => ['common_test' => $context]],
|
||||
];
|
||||
$this->assertIdentical($element['#attached']['js'], $expected_js, '#attached is modified; both the original JavaScript setting and the one added by the #post_render_cache callback exist.');
|
||||
|
||||
// Verify behavior when handling a non-GET request, e.g. a POST request:
|
||||
// also in that case, #post_render_cache callbacks must be called.
|
||||
\Drupal::request()->setMethod('POST');
|
||||
|
||||
// POST request: #cache enabled, cache miss.
|
||||
drupal_static_reset('_drupal_add_js');
|
||||
$element = $test_element;
|
||||
$element['#cache'] = array('cid' => 'post_render_cache_test_POST');
|
||||
$element['#markup'] = '<p>#cache enabled, POST</p>';
|
||||
|
@ -516,9 +512,11 @@ class RenderTest extends DrupalUnitTestBase {
|
|||
$this->assertIdentical($output, '<p>overridden</p>', 'Output is overridden.');
|
||||
$this->assertTrue(isset($element['#printed']), 'No cache hit');
|
||||
$this->assertIdentical($element['#markup'], '<p>overridden</p>', '#markup is overridden.');
|
||||
$settings = $this->parseDrupalSettings(drupal_get_js());
|
||||
$this->assertIdentical($settings['foo'], 'bar', 'Original JavaScript setting is added to the page.');
|
||||
$this->assertIdentical($settings['common_test'], $context, '#attached is modified; JavaScript setting is added to page.');
|
||||
$expected_js = [
|
||||
['type' => 'setting', 'data' => ['foo' => 'bar']],
|
||||
['type' => 'setting', 'data' => ['common_test' => $context]],
|
||||
];
|
||||
$this->assertIdentical($element['#attached']['js'], $expected_js, '#attached is modified; both the original JavaScript setting and the one added by the #post_render_cache callback exist.');
|
||||
|
||||
// POST request: Ensure no data was cached.
|
||||
$element = array('#cache' => array('cid' => 'post_render_cache_test_POST'));
|
||||
|
@ -540,7 +538,6 @@ class RenderTest extends DrupalUnitTestBase {
|
|||
// Test case 1.
|
||||
// Create an element with a child and subchild. Each element has the same
|
||||
// #post_render_cache callback, but with different contexts.
|
||||
drupal_static_reset('_drupal_add_js');
|
||||
$context_1 = array('foo' => $this->randomContextValue());
|
||||
$context_2 = array('bar' => $this->randomContextValue());
|
||||
$context_3 = array('baz' => $this->randomContextValue());
|
||||
|
@ -579,10 +576,13 @@ class RenderTest extends DrupalUnitTestBase {
|
|||
$this->assertIdentical($output, '<p>overridden</p>', 'Output is overridden.');
|
||||
$this->assertTrue(isset($element['#printed']), 'No cache hit');
|
||||
$this->assertIdentical($element['#markup'], '<p>overridden</p>', '#markup is overridden.');
|
||||
$settings = $this->parseDrupalSettings(drupal_get_js());
|
||||
$expected_settings = $context_1 + $context_2 + $context_3;
|
||||
$this->assertIdentical($settings['foo'], 'bar', 'Original JavaScript setting is added to the page.');
|
||||
$this->assertIdentical($settings['common_test'], $expected_settings, '#attached is modified; JavaScript settings for each #post_render_cache callback are added to page.');
|
||||
$expected_js = [
|
||||
['type' => 'setting', 'data' => ['foo' => 'bar']],
|
||||
['type' => 'setting', 'data' => ['common_test' => $context_1 ]],
|
||||
['type' => 'setting', 'data' => ['common_test' => $context_2 ]],
|
||||
['type' => 'setting', 'data' => ['common_test' => $context_3 ]],
|
||||
];
|
||||
$this->assertIdentical($element['#attached']['js'], $expected_js, '#attached is modified; both the original JavaScript setting and the ones added by each #post_render_cache callback exist.');
|
||||
|
||||
// GET request: validate cached data.
|
||||
$element = array('#cache' => $element['#cache']);
|
||||
|
@ -619,27 +619,20 @@ class RenderTest extends DrupalUnitTestBase {
|
|||
$this->assertIdentical($cached_element, $expected_element, 'The correct data is cached: the stored #attached properties are not affected by #post_render_cache callbacks.');
|
||||
|
||||
// GET request: #cache enabled, cache hit.
|
||||
drupal_static_reset('_drupal_add_js');
|
||||
$element = $test_element;
|
||||
$output = drupal_render($element);
|
||||
$this->assertIdentical($output, '<p>overridden</p>', 'Output is overridden.');
|
||||
$this->assertFalse(isset($element['#printed']), 'Cache hit');
|
||||
$settings = $this->parseDrupalSettings(drupal_get_js());
|
||||
$this->assertIdentical($settings['foo'], 'bar', 'Original JavaScript setting is added to the page.');
|
||||
$this->assertIdentical($settings['common_test'], $expected_settings, '#attached is modified; JavaScript settings for each #post_render_cache callback are added to page.');
|
||||
$this->assertIdentical($element['#attached']['js'], $expected_js, '#attached is modified; both the original JavaScript setting and the ones added by each #post_render_cache callback exist.');
|
||||
|
||||
// Test case 2.
|
||||
// Use the exact same element, but now unset #cache.
|
||||
drupal_static_reset('drupal_add_js');
|
||||
unset($test_element['#cache']);
|
||||
$element = $test_element;
|
||||
$output = drupal_render($element);
|
||||
$this->assertIdentical($output, '<p>overridden</p>', 'Output is overridden.');
|
||||
$this->assertIdentical($element['#markup'], '<p>overridden</p>', '#markup is overridden.');
|
||||
$settings = $this->parseDrupalSettings(drupal_get_js());
|
||||
$expected_settings = $context_1 + $context_2 + $context_3;
|
||||
$this->assertIdentical($settings['foo'], 'bar', 'Original JavaScript setting is added to the page.');
|
||||
$this->assertIdentical($settings['common_test'], $expected_settings, '#attached is modified; JavaScript settings for each #post_render_cache callback are added to page.');
|
||||
$this->assertIdentical($element['#attached']['js'], $expected_js, '#attached is modified; both the original JavaScript setting and the ones added by each #post_render_cache callback exist.');
|
||||
|
||||
// Test case 3.
|
||||
// Create an element with a child and subchild. Each element has the same
|
||||
|
@ -649,7 +642,6 @@ class RenderTest extends DrupalUnitTestBase {
|
|||
// #post_render_cache callbacks. I.e. the #post_render_cache callbacks may
|
||||
// not yet have run, or otherwise the cached parent element would contain
|
||||
// personalized data, thereby breaking the render cache.
|
||||
drupal_static_reset('_drupal_add_js');
|
||||
$element = $test_element;
|
||||
$element['#cache']['keys'] = array('simpletest', 'drupal_render', 'children_post_render_cache', 'nested_cache_parent');
|
||||
$element['child']['#cache']['keys'] = array('simpletest', 'drupal_render', 'children_post_render_cache', 'nested_cache_child');
|
||||
|
@ -657,10 +649,7 @@ class RenderTest extends DrupalUnitTestBase {
|
|||
$this->assertIdentical($output, '<p>overridden</p>', 'Output is overridden.');
|
||||
$this->assertTrue(isset($element['#printed']), 'No cache hit');
|
||||
$this->assertIdentical($element['#markup'], '<p>overridden</p>', '#markup is overridden.');
|
||||
$settings = $this->parseDrupalSettings(drupal_get_js());
|
||||
$expected_settings = $context_1 + $context_2 + $context_3;
|
||||
$this->assertIdentical($settings['foo'], 'bar', 'Original JavaScript setting is added to the page.');
|
||||
$this->assertIdentical($settings['common_test'], $expected_settings, '#attached is modified; JavaScript settings for each #post_render_cache callback are added to page.');
|
||||
$this->assertIdentical($element['#attached']['js'], $expected_js, '#attached is modified; both the original JavaScript setting and the ones added by each #post_render_cache callback exist.');
|
||||
|
||||
// GET request: validate cached data for both the parent and child.
|
||||
$element = $test_element;
|
||||
|
@ -725,28 +714,25 @@ class RenderTest extends DrupalUnitTestBase {
|
|||
$this->assertIdentical($cached_child_element, $expected_child_element, 'The correct data is cached for the child: the stored #attached properties are not affected by #post_render_cache callbacks.');
|
||||
|
||||
// GET request: #cache enabled, cache hit, parent element.
|
||||
drupal_static_reset('_drupal_add_js');
|
||||
$element = $test_element;
|
||||
$element['#cache']['keys'] = array('simpletest', 'drupal_render', 'children_post_render_cache', 'nested_cache_parent');
|
||||
$output = drupal_render($element);
|
||||
$this->assertIdentical($output, '<p>overridden</p>', 'Output is overridden.');
|
||||
$this->assertFalse(isset($element['#printed']), 'Cache hit');
|
||||
$settings = $this->parseDrupalSettings(drupal_get_js());
|
||||
$this->assertIdentical($settings['foo'], 'bar', 'Original JavaScript setting is added to the page.');
|
||||
$this->assertIdentical($settings['common_test'], $expected_settings, '#attached is modified; JavaScript settings for each #post_render_cache callback are added to page.');
|
||||
$this->assertIdentical($element['#attached']['js'], $expected_js, '#attached is modified; both the original JavaScript setting and the ones added by each #post_render_cache callback exist.');
|
||||
|
||||
// GET request: #cache enabled, cache hit, child element.
|
||||
drupal_static_reset('_drupal_add_js');
|
||||
$element = $test_element;
|
||||
$element['child']['#cache']['keys'] = array('simpletest', 'drupal_render', 'children_post_render_cache', 'nested_cache_child');
|
||||
$element = $element['child'];
|
||||
$output = drupal_render($element);
|
||||
$this->assertIdentical($output, '<p>overridden</p>', 'Output is overridden.');
|
||||
$this->assertFalse(isset($element['#printed']), 'Cache hit');
|
||||
$settings = $this->parseDrupalSettings(drupal_get_js());
|
||||
$expected_settings = $context_2 + $context_3;
|
||||
$this->assertTrue(!isset($settings['foo']), 'Parent JavaScript setting is not added to the page.');
|
||||
$this->assertIdentical($settings['common_test'], $expected_settings, '#attached is modified; JavaScript settings for each #post_render_cache callback are added to page.');
|
||||
$expected_js = [
|
||||
['type' => 'setting', 'data' => ['common_test' => $context_2 ]],
|
||||
['type' => 'setting', 'data' => ['common_test' => $context_3 ]],
|
||||
];
|
||||
$this->assertIdentical($element['#attached']['js'], $expected_js, '#attached is modified; both the original JavaScript setting and the ones added by each #post_render_cache callback exist.');
|
||||
|
||||
// Restore the previous request method.
|
||||
\Drupal::request()->setMethod($request_method);
|
||||
|
@ -776,27 +762,27 @@ class RenderTest extends DrupalUnitTestBase {
|
|||
$expected_output = '<foo><bar>' . $context['bar'] . '</bar></foo>';
|
||||
|
||||
// #cache disabled.
|
||||
drupal_static_reset('_drupal_add_js');
|
||||
$element = $test_element;
|
||||
$output = drupal_render($element);
|
||||
$this->assertIdentical($output, $expected_output, 'Placeholder was replaced in output');
|
||||
$settings = $this->parseDrupalSettings(drupal_get_js());
|
||||
$this->assertIdentical($settings['common_test'], $context, '#attached is modified; JavaScript setting is added to page.');
|
||||
$expected_js = [
|
||||
['type' => 'setting', 'data' => ['common_test' => $context]],
|
||||
];
|
||||
$this->assertIdentical($element['#attached']['js'], $expected_js, '#attached is modified; JavaScript setting is added to page.');
|
||||
|
||||
// The cache system is turned off for POST requests.
|
||||
$request_method = \Drupal::request()->getMethod();
|
||||
\Drupal::request()->setMethod('GET');
|
||||
|
||||
// GET request: #cache enabled, cache miss.
|
||||
drupal_static_reset('_drupal_add_js');
|
||||
$element = $test_element;
|
||||
$element['#cache'] = array('cid' => 'render_cache_placeholder_test_GET');
|
||||
$output = drupal_render($element);
|
||||
$this->assertIdentical($output, $expected_output, 'Placeholder was replaced in output');
|
||||
$this->assertTrue(isset($element['#printed']), 'No cache hit');
|
||||
$this->assertIdentical($element['#markup'], $expected_output, 'Placeholder was replaced in #markup.');
|
||||
$settings = $this->parseDrupalSettings(drupal_get_js());
|
||||
$this->assertIdentical($settings['common_test'], $context, '#attached is modified; JavaScript setting is added to page.');
|
||||
$this->assertIdentical($output, $expected_output, 'Placeholder was replaced in output');
|
||||
$this->assertIdentical($element['#attached']['js'], $expected_js, '#attached is modified; JavaScript setting is added to page.');
|
||||
|
||||
// GET request: validate cached data.
|
||||
$expected_token = $element['#post_render_cache']['common_test_post_render_cache_placeholder'][0]['token'];
|
||||
|
@ -815,6 +801,7 @@ class RenderTest extends DrupalUnitTestBase {
|
|||
// Verify the token is in the cached element.
|
||||
$expected_element = array(
|
||||
'#markup' => '<foo><drupal-render-cache-placeholder callback="common_test_post_render_cache_placeholder" token="'. $expected_token . '"></drupal-render-cache-placeholder></foo>',
|
||||
'#attached' => array(),
|
||||
'#post_render_cache' => array(
|
||||
'common_test_post_render_cache_placeholder' => array(
|
||||
$context
|
||||
|
@ -825,77 +812,69 @@ class RenderTest extends DrupalUnitTestBase {
|
|||
$this->assertIdentical($cached_element, $expected_element, 'The correct data is cached: the stored #markup and #attached properties are not affected by #post_render_cache callbacks.');
|
||||
|
||||
// GET request: #cache enabled, cache hit.
|
||||
drupal_static_reset('_drupal_add_js');
|
||||
$element = $test_element;
|
||||
$element['#cache'] = array('cid' => 'render_cache_placeholder_test_GET');
|
||||
$output = drupal_render($element);
|
||||
$this->assertIdentical($output, $expected_output, 'Placeholder was replaced in output');
|
||||
$this->assertFalse(isset($element['#printed']), 'Cache hit');
|
||||
$this->assertIdentical($element['#markup'], $expected_output, 'Placeholder was replaced in #markup.');
|
||||
$settings = $this->parseDrupalSettings(drupal_get_js());
|
||||
$this->assertIdentical($settings['common_test'], $context, '#attached is modified; JavaScript setting is added to page.');
|
||||
$this->assertIdentical($element['#attached']['js'], $expected_js, '#attached is modified; JavaScript setting is added to page.');
|
||||
|
||||
// Restore the previous request method.
|
||||
\Drupal::request()->setMethod($request_method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests post-render cache-integrated 'render_cache_placeholder' child
|
||||
* element.
|
||||
* Tests child element that uses #post_render_cache but that is rendered via a
|
||||
* template.
|
||||
*/
|
||||
function testDrupalRenderChildElementRenderCachePlaceholder() {
|
||||
$container = array(
|
||||
'#type' => 'container',
|
||||
);
|
||||
$context = array(
|
||||
'bar' => $this->randomContextValue(),
|
||||
);
|
||||
$callback = 'common_test_post_render_cache_placeholder';
|
||||
$placeholder = drupal_render_cache_generate_placeholder($callback, $context);
|
||||
$test_element = array(
|
||||
'#post_render_cache' => array(
|
||||
$callback => array(
|
||||
$context
|
||||
),
|
||||
),
|
||||
'#markup' => $placeholder,
|
||||
'#prefix' => '<foo>',
|
||||
'#suffix' => '</foo>'
|
||||
);
|
||||
$container['test_element'] = $test_element;
|
||||
$expected_output = '<div><foo><bar>' . $context['bar'] . '</bar></foo></div>' . "\n";
|
||||
$test_element = [
|
||||
'#theme' => 'common_test_render_element',
|
||||
'foo' => [
|
||||
'#post_render_cache' => [
|
||||
$callback => [
|
||||
$context
|
||||
],
|
||||
],
|
||||
'#markup' => $placeholder,
|
||||
'#prefix' => '<foo>',
|
||||
'#suffix' => '</foo>'
|
||||
],
|
||||
];
|
||||
$expected_output = '<foo><bar>' . $context['bar'] . '</bar></foo>' . "\n";
|
||||
|
||||
// #cache disabled.
|
||||
drupal_static_reset('_drupal_add_js');
|
||||
$element = $container;
|
||||
$element = $test_element;
|
||||
$output = drupal_render($element);
|
||||
$this->assertIdentical($output, $expected_output, 'Placeholder was replaced in output');
|
||||
$settings = $this->parseDrupalSettings(drupal_get_js());
|
||||
$this->assertIdentical($settings['common_test'], $context, '#attached is modified; JavaScript setting is added to page.');
|
||||
$expected_js = [
|
||||
['type' => 'setting', 'data' => ['common_test' => $context]],
|
||||
];
|
||||
$this->assertIdentical($element['#attached']['js'], $expected_js, '#attached is modified; JavaScript setting is added to page.');
|
||||
|
||||
// The cache system is turned off for POST requests.
|
||||
$request_method = \Drupal::request()->getMethod();
|
||||
\Drupal::request()->setMethod('GET');
|
||||
|
||||
// GET request: #cache enabled, cache miss.
|
||||
drupal_static_reset('_drupal_add_js');
|
||||
$element = $container;
|
||||
$element = $test_element;
|
||||
$element['#cache'] = array('cid' => 'render_cache_placeholder_test_GET');
|
||||
$element['test_element']['#cache'] = array('cid' => 'render_cache_placeholder_test_child_GET');
|
||||
// Simulate element rendering in a template, where sub-items of a renderable
|
||||
// can be sent to drupal_render() before the parent.
|
||||
$child = &$element['test_element'];
|
||||
$element['#children'] = drupal_render($child, TRUE);
|
||||
// Eventually, drupal_render() gets called on the root element.
|
||||
$element['foo']['#cache'] = array('cid' => 'render_cache_placeholder_test_child_GET');
|
||||
// Render, which will use the common-test-render-element.html.twig template.
|
||||
$output = drupal_render($element);
|
||||
$this->assertIdentical($output, $expected_output, 'Placeholder was replaced in output');
|
||||
$this->assertIdentical($output, $expected_output); //, 'Placeholder was replaced in output');
|
||||
$this->assertTrue(isset($element['#printed']), 'No cache hit');
|
||||
$this->assertIdentical($element['#markup'], $expected_output, 'Placeholder was replaced in #markup.');
|
||||
$settings = $this->parseDrupalSettings(drupal_get_js());
|
||||
$this->assertIdentical($settings['common_test'], $context, '#attached is modified; JavaScript setting is added to page.');
|
||||
$this->assertIdentical($element['#attached']['js'], $expected_js, '#attached is modified; JavaScript setting is added to page.');
|
||||
|
||||
// GET request: validate cached data for child element.
|
||||
$child_tokens = $element['test_element']['#post_render_cache']['common_test_post_render_cache_placeholder'][0]['token'];
|
||||
$child_tokens = $element['foo']['#post_render_cache']['common_test_post_render_cache_placeholder'][0]['token'];
|
||||
$parent_tokens = $element['#post_render_cache']['common_test_post_render_cache_placeholder'][0]['token'];
|
||||
$expected_token = $child_tokens;
|
||||
$element = array('#cache' => array('cid' => 'render_cache_placeholder_test_child_GET'));
|
||||
|
@ -913,6 +892,7 @@ class RenderTest extends DrupalUnitTestBase {
|
|||
// Verify the token is in the cached element.
|
||||
$expected_element = array(
|
||||
'#markup' => '<foo><drupal-render-cache-placeholder callback="common_test_post_render_cache_placeholder" token="'. $expected_token . '"></drupal-render-cache-placeholder></foo>',
|
||||
'#attached' => array(),
|
||||
'#post_render_cache' => array(
|
||||
'common_test_post_render_cache_placeholder' => array(
|
||||
$context,
|
||||
|
@ -937,7 +917,8 @@ class RenderTest extends DrupalUnitTestBase {
|
|||
$this->assertIdentical($token, $expected_token, 'The tokens are identical for the parent element');
|
||||
// Verify the token is in the cached element.
|
||||
$expected_element = array(
|
||||
'#markup' => '<div><foo><drupal-render-cache-placeholder callback="common_test_post_render_cache_placeholder" token="'. $expected_token . '"></drupal-render-cache-placeholder></foo></div>' . "\n",
|
||||
'#markup' => '<foo><drupal-render-cache-placeholder callback="common_test_post_render_cache_placeholder" token="'. $expected_token . '"></drupal-render-cache-placeholder></foo>' . "\n",
|
||||
'#attached' => array(),
|
||||
'#post_render_cache' => array(
|
||||
'common_test_post_render_cache_placeholder' => array(
|
||||
$context,
|
||||
|
@ -945,7 +926,7 @@ class RenderTest extends DrupalUnitTestBase {
|
|||
),
|
||||
'#cache' => array('tags' => array('rendered' => TRUE)),
|
||||
);
|
||||
$this->assertIdentical($cached_element, $expected_element, 'The correct data is cached for the parent element: the stored #markup and #attached properties are not affected by #post_render_cache callbacks.');
|
||||
$this->assertIdentical($cached_element, $expected_element); //, 'The correct data is cached for the parent element: the stored #markup and #attached properties are not affected by #post_render_cache callbacks.');
|
||||
|
||||
// GET request: validate cached data.
|
||||
// Check the cache of the child element again after the parent has been
|
||||
|
@ -967,6 +948,7 @@ class RenderTest extends DrupalUnitTestBase {
|
|||
// Verify the token is in the cached element.
|
||||
$expected_element = array(
|
||||
'#markup' => '<foo><drupal-render-cache-placeholder callback="common_test_post_render_cache_placeholder" token="'. $expected_token . '"></drupal-render-cache-placeholder></foo>',
|
||||
'#attached' => array(),
|
||||
'#post_render_cache' => array(
|
||||
'common_test_post_render_cache_placeholder' => array(
|
||||
$context,
|
||||
|
@ -977,32 +959,153 @@ class RenderTest extends DrupalUnitTestBase {
|
|||
$this->assertIdentical($cached_element, $expected_element, 'The correct data is cached for the child element: the stored #markup and #attached properties are not affected by #post_render_cache callbacks.');
|
||||
|
||||
// GET request: #cache enabled, cache hit.
|
||||
drupal_static_reset('_drupal_add_js');
|
||||
$element = $container;
|
||||
$element = $test_element;
|
||||
$element['#cache'] = array('cid' => 'render_cache_placeholder_test_GET');
|
||||
// Simulate element rendering in a template, where sub-items of a renderable
|
||||
// can be sent to drupal_render before the parent.
|
||||
$child = &$element['test_element'];
|
||||
$element['#children'] = drupal_render($child, TRUE);
|
||||
// Render, which will use the common-test-render-element.html.twig template.
|
||||
$output = drupal_render($element);
|
||||
$this->assertIdentical($output, $expected_output, 'Placeholder was replaced in output');
|
||||
$this->assertFalse(isset($element['#printed']), 'Cache hit');
|
||||
$this->assertIdentical($element['#markup'], $expected_output, 'Placeholder was replaced in #markup.');
|
||||
$settings = $this->parseDrupalSettings(drupal_get_js());
|
||||
$this->assertIdentical($settings['common_test'], $context, '#attached is modified; JavaScript setting is added to page.');
|
||||
$this->assertIdentical($element['#attached']['js'], $expected_js, '#attached is modified; JavaScript setting is added to page.');
|
||||
|
||||
// Restore the previous request method.
|
||||
\Drupal::request()->setMethod($request_method);
|
||||
}
|
||||
|
||||
protected function parseDrupalSettings($html) {
|
||||
$startToken = 'drupalSettings = ';
|
||||
$endToken = '}';
|
||||
$start = strpos($html, $startToken) + strlen($startToken);
|
||||
$end = strrpos($html, $endToken);
|
||||
$json = drupal_substr($html, $start, $end - $start + 1);
|
||||
$parsed_settings = Json::decode($json);
|
||||
return $parsed_settings;
|
||||
/**
|
||||
* #pre_render callback for testDrupalRenderBubbling().
|
||||
*/
|
||||
public static function bubblingPreRender($elements) {
|
||||
$callback = 'Drupal\system\Tests\Common\RenderTest::bubblingPostRenderCache';
|
||||
$context = array(
|
||||
'foo' => 'bar',
|
||||
'baz' => 'qux',
|
||||
);
|
||||
$placeholder = drupal_render_cache_generate_placeholder($callback, $context);
|
||||
$elements += array(
|
||||
'child_cache_tag' => array(
|
||||
'#cache' => array(
|
||||
'tags' => array('child' => 'cache_tag'),
|
||||
),
|
||||
'#markup' => 'Cache tag!',
|
||||
),
|
||||
'child_asset' => array(
|
||||
'#attached' => array(
|
||||
'js' => array(
|
||||
array(
|
||||
'type' => 'setting',
|
||||
'data' => array('foo' => 'bar'),
|
||||
)
|
||||
),
|
||||
),
|
||||
'#markup' => 'Asset!',
|
||||
),
|
||||
'child_post_render_cache' => array(
|
||||
'#post_render_cache' => array(
|
||||
$callback => array(
|
||||
$context,
|
||||
),
|
||||
),
|
||||
'#markup' => $placeholder,
|
||||
),
|
||||
'child_nested_pre_render_uncached' => array(
|
||||
'#cache' => array('cid' => 'uncached_nested'),
|
||||
'#pre_render' => array('Drupal\system\Tests\Common\RenderTest::bubblingNestedPreRenderUncached'),
|
||||
),
|
||||
'child_nested_pre_render_cached' => array(
|
||||
'#cache' => array('cid' => 'cached_nested'),
|
||||
'#pre_render' => array('Drupal\system\Tests\Common\RenderTest::bubblingNestedPreRenderCached'),
|
||||
),
|
||||
);
|
||||
return $elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* #pre_render callback for testDrupalRenderBubbling().
|
||||
*/
|
||||
public static function bubblingNestedPreRenderUncached($elements) {
|
||||
\Drupal::state()->set('bubbling_nested_pre_render_uncached', TRUE);
|
||||
$elements['#markup'] = 'Nested!';
|
||||
return $elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* #pre_render callback for testDrupalRenderBubbling().
|
||||
*/
|
||||
public static function bubblingNestedPreRenderCached($elements) {
|
||||
\Drupal::state()->set('bubbling_nested_pre_render_cached', TRUE);
|
||||
return $elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* #post_render_cache callback for testDrupalRenderBubbling().
|
||||
*/
|
||||
public static function bubblingPostRenderCache(array $element, array $context) {
|
||||
$callback = 'Drupal\system\Tests\Common\RenderTest::bubblingPostRenderCache';
|
||||
$placeholder = drupal_render_cache_generate_placeholder($callback, $context);
|
||||
$element['#markup'] = str_replace($placeholder, 'Post-render cache!' . $context['foo'] . $context['baz'], $element['#markup']);
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests bubbling of assets, cache tags and post-render cache callbacks when
|
||||
* they are added by #pre_render callbacks.
|
||||
*/
|
||||
function testDrupalRenderBubbling() {
|
||||
$verify_result= function ($test_element) {
|
||||
\Drupal::state()->set('bubbling_nested_pre_render_uncached', FALSE);
|
||||
\Drupal::state()->set('bubbling_nested_pre_render_cached', FALSE);
|
||||
\Drupal::cache('render')->set('cached_nested', array('#markup' => 'Cached nested!', '#attached' => array(), '#cache' => array('tags' => array()), '#post_render_cache' => array()));
|
||||
\Drupal::cache('render')->delete('uncached_nested');
|
||||
|
||||
$output = drupal_render($test_element);
|
||||
// Assert top-level.
|
||||
$this->assertEqual('Cache tag!Asset!Post-render cache!barquxNested!Cached nested!', trim($output), 'Expected HTML generated.');
|
||||
$this->assertEqual(array('child' => 'cache_tag'), $test_element['#cache']['tags'], 'Expected cache tags found.');
|
||||
$expected_attached = array(
|
||||
'js' => array(
|
||||
0 => array(
|
||||
'type' => 'setting',
|
||||
'data' => array('foo' => 'bar'),
|
||||
),
|
||||
),
|
||||
);
|
||||
$this->assertEqual($expected_attached, $test_element['#attached'], 'Expected assets found.');
|
||||
$expected_post_render_cache = array(
|
||||
'Drupal\\system\\Tests\\Common\\RenderTest::bubblingPostRenderCache' => array(
|
||||
0 => array (
|
||||
'foo' => 'bar',
|
||||
'baz' => 'qux',
|
||||
),
|
||||
),
|
||||
);
|
||||
$post_render_cache = $test_element['#post_render_cache'];
|
||||
// We don't care about the exact token.
|
||||
unset($post_render_cache['Drupal\\system\\Tests\\Common\\RenderTest::bubblingPostRenderCache'][0]['token']);
|
||||
$this->assertEqual($expected_post_render_cache, $post_render_cache, 'Expected post-render cache data found.');
|
||||
|
||||
// Ensure that #pre_render callbacks are only executed if they don't have
|
||||
// a render cache hit.
|
||||
$this->assertTrue(\Drupal::state()->get('bubbling_nested_pre_render_uncached'));
|
||||
$this->assertFalse(\Drupal::state()->get('bubbling_nested_pre_render_cached'));
|
||||
};
|
||||
|
||||
$this->pass('Test <strong>without</strong> theming/Twig.');
|
||||
$test_element_without_theme = array(
|
||||
'foo' => array(
|
||||
'#pre_render' => array(array(get_class($this), 'bubblingPreRender')),
|
||||
),
|
||||
);
|
||||
$verify_result($test_element_without_theme);
|
||||
|
||||
$this->pass('Test <strong>with</strong> theming/Twig.');
|
||||
$test_element_with_theme = array(
|
||||
'#theme' => 'common_test_render_element',
|
||||
'foo' => array(
|
||||
'#pre_render' => array(array(get_class($this), 'bubblingPreRender')),
|
||||
),
|
||||
);
|
||||
$verify_result($test_element_with_theme);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -46,7 +46,7 @@ abstract class EntityWithUriCacheTagsTestBase extends EntityCacheTagsTestBase {
|
|||
$cid = 'entity_view:' . $entity_type . ':' . $this->entity->id() . ':' . $view_mode . ':stark:r.anonymous:' . date_default_timezone_get();
|
||||
$cache_entry = \Drupal::cache('render')->get($cid);
|
||||
$expected_cache_tags = array_merge(array($view_cache_tag, $cache_tag), $this->getAdditionalCacheTagsForEntity($this->entity), array($render_cache_tag));
|
||||
$this->assertIdentical($cache_entry->tags, $expected_cache_tags);
|
||||
$this->verifyRenderCache($cid, $expected_cache_tags);
|
||||
}
|
||||
|
||||
// Verify that after modifying the entity, there is a cache miss.
|
||||
|
|
|
@ -180,7 +180,9 @@ function ajax_forms_test_advanced_commands_settings_with_merging_callback($form,
|
|||
),
|
||||
),
|
||||
);
|
||||
// @todo Why is this being tested via an explicit drupal_render() call?
|
||||
drupal_render($attached);
|
||||
drupal_process_attached($attached);
|
||||
|
||||
$response = new AjaxResponse();
|
||||
return $response;
|
||||
|
|
|
@ -73,6 +73,7 @@ class AjaxFormsTestLazyLoadForm extends FormBase {
|
|||
),
|
||||
);
|
||||
drupal_render($attached);
|
||||
drupal_process_attached($attached);
|
||||
}
|
||||
$form_state['rebuild'] = TRUE;
|
||||
}
|
||||
|
|
|
@ -33,7 +33,9 @@ function ajax_test_render() {
|
|||
),
|
||||
),
|
||||
);
|
||||
// @todo Why is this being tested via an explicit drupal_render() call?
|
||||
drupal_render($attached);
|
||||
drupal_process_attached($attached);
|
||||
$response = new AjaxResponse();
|
||||
return $response;
|
||||
}
|
||||
|
@ -69,7 +71,11 @@ function ajax_test_order() {
|
|||
),
|
||||
),
|
||||
);
|
||||
|
||||
// @todo Why is this being tested via an explicit drupal_render() call?
|
||||
drupal_render($attached);
|
||||
drupal_process_attached($attached);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
|
|
|
@ -117,6 +117,10 @@ function common_test_theme() {
|
|||
'variables' => array('foo' => 'foo', 'bar' => 'bar'),
|
||||
'template' => 'common-test-foo',
|
||||
),
|
||||
'common_test_render_element' => array(
|
||||
'render element' => 'foo',
|
||||
'template' => 'common-test-render-element',
|
||||
),
|
||||
'common_test_empty' => array(
|
||||
'variables' => array('foo' => 'foo'),
|
||||
),
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
{#
|
||||
/**
|
||||
* @file
|
||||
* Default theme implementation for the common test render element.
|
||||
*
|
||||
* Available variables:
|
||||
* - foo: a render array
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
#}
|
||||
{{ foo }}
|
|
@ -60,18 +60,6 @@ class TextDefaultFormatter extends FormatterBase {
|
|||
'#format' => $item->format,
|
||||
'#langcode' => $item->getLangcode(),
|
||||
);
|
||||
// The viewElements() method of entity field formatters is run
|
||||
// during the #pre_render phase of rendering an entity. A formatter
|
||||
// builds the content of the field in preparation for theming.
|
||||
// All cache tags must be available after the #pre_render phase. In order
|
||||
// to collect the cache tags associated with the processed text, it must
|
||||
// be passed to drupal_render() so that its #pre_render callback is
|
||||
// invoked and its full build array is assembled. Rendering the processed
|
||||
// text in place here will allow its cache tags to be bubbled up and
|
||||
// included with those of the main entity when cache tags are collected
|
||||
// for a renderable array in drupal_render().
|
||||
// @todo remove this work-around, see https://drupal.org/node/2273277
|
||||
drupal_render($elements[$delta], TRUE);
|
||||
}
|
||||
|
||||
return $elements;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\text\Plugin\field\formatter\TextTrimmedFormatter.
|
||||
* Contains \Drupal\text\Plugin\field\FieldFormatter\TextTrimmedFormatter.
|
||||
*/
|
||||
namespace Drupal\text\Plugin\Field\FieldFormatter;
|
||||
|
||||
|
@ -90,6 +90,16 @@ class TextTrimmedFormatter extends FormatterBase {
|
|||
protected function viewElementsWithTextProcessing(FieldItemListInterface $items) {
|
||||
$elements = array();
|
||||
|
||||
$render_as_summary = function (&$element) {
|
||||
// Make sure any default #pre_render callbacks are set on the element,
|
||||
// because text_pre_render_summary() must run last.
|
||||
$element += \Drupal::service('element_info')->getInfo($element['#type']);
|
||||
// Add the #pre_render callback that renders the text into a summary.
|
||||
$element['#pre_render'][] = '\Drupal\text\Plugin\field\FieldFormatter\TextTrimmedFormatter::preRenderSummary';
|
||||
// Pass on the trim length to the #pre_render callback via a property.
|
||||
$element['#text_summary_trim_length'] = $this->getSetting('trim_length');
|
||||
};
|
||||
|
||||
foreach ($items as $delta => $item) {
|
||||
$elements[$delta] = array(
|
||||
'#type' => 'processed_text',
|
||||
|
@ -98,26 +108,12 @@ class TextTrimmedFormatter extends FormatterBase {
|
|||
'#langcode' => $item->getLangcode(),
|
||||
);
|
||||
|
||||
// The viewElements() method of entity field formatters is run
|
||||
// during the #pre_render phase of rendering an entity. A formatter
|
||||
// builds the content of the field in preparation for theming.
|
||||
// All cache tags must be available after the #pre_render phase. In order
|
||||
// to collect the cache tags associated with the processed text, it must
|
||||
// be passed to drupal_render() so that its #pre_render callback is
|
||||
// invoked and its full build array is assembled. Rendering the processed
|
||||
// text in place here will allow its cache tags to be bubbled up and
|
||||
// included with those of the main entity when cache tags are collected
|
||||
// for a renderable array in drupal_render().
|
||||
if ($this->getPluginId() == 'text_summary_or_trimmed' && !empty($item->summary)) {
|
||||
$elements[$delta]['#text'] = $item->summary;
|
||||
// @todo remove this work-around, see https://drupal.org/node/2273277
|
||||
drupal_render($elements[$delta], TRUE);
|
||||
}
|
||||
else {
|
||||
$elements[$delta]['#text'] = $item->value;
|
||||
// @todo remove this work-around, see https://drupal.org/node/2273277
|
||||
drupal_render($elements[$delta], TRUE);
|
||||
$elements[$delta]['#markup'] = text_summary($elements[$delta]['#markup'], $item->format, $this->getSetting('trim_length'));
|
||||
$render_as_summary($elements[$delta]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -153,4 +149,27 @@ class TextTrimmedFormatter extends FormatterBase {
|
|||
return $elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-render callback: Renders a processed text element's #markup as a summary.
|
||||
*
|
||||
* @param array $element
|
||||
* A structured array with the following key-value pairs:
|
||||
* - #markup: the filtered text (as filtered by filter_pre_render_text())
|
||||
* - #format: containing the machine name of the filter format to be used to
|
||||
* filter the text. Defaults to the fallback format. See
|
||||
* filter_fallback_format().
|
||||
* - #text_summary_trim_length: the desired character length of the summary
|
||||
* (used by text_summary())
|
||||
*
|
||||
* @return array
|
||||
* The passed-in element with the filtered text in '#markup' trimmed.
|
||||
*
|
||||
* @see filter_pre_render_text()
|
||||
* @see text_summary()
|
||||
*/
|
||||
public static function preRenderSummary(array $element) {
|
||||
$element['#markup'] = text_summary($element['#markup'], $element['#format'], $element['#text_summary_trim_length']);
|
||||
return $element;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -111,9 +111,10 @@ class TextFormatterTest extends EntityUnitTestBase {
|
|||
foreach ($formatters as $formatter) {
|
||||
// Verify the processed text field formatter's render array.
|
||||
$build = $entity->get('processed_text')->view(array('type' => $formatter));
|
||||
drupal_render($build[0]);
|
||||
$this->assertEqual($build[0]['#markup'], "<p>Hello, world!</p>\n");
|
||||
$expected_cache_tags = array(
|
||||
'filter_format' => array('my_text_format' => 'my_text_format'),
|
||||
'filter_format' => array('my_text_format'),
|
||||
);
|
||||
$this->assertEqual($build[0]['#cache']['tags'], $expected_cache_tags, format_string('The @formatter formatter has the expected cache tags when formatting a processed text field.', array('@formatter' => $formatter)));
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ function views_ajax_form_wrapper($form_class, FormStateInterface &$form_state) {
|
|||
|
||||
$form = \Drupal::formBuilder()->buildForm($form_class, $form_state);
|
||||
$output = drupal_render($form);
|
||||
drupal_process_attached($form);
|
||||
|
||||
// These forms have the title built in, so set the title here:
|
||||
if (empty($form_state['ajax']) && !empty($form_state['title'])) {
|
||||
|
|
|
@ -156,7 +156,9 @@ class ViewAjaxController implements ContainerInjectionInterface {
|
|||
* @see https://drupal.org/node/2171071
|
||||
*/
|
||||
protected function drupalRender(array $elements) {
|
||||
return drupal_render($elements);
|
||||
$output = drupal_render($elements);
|
||||
drupal_process_attached($elements);
|
||||
return $output;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -244,7 +244,7 @@ abstract class CachePluginBase extends PluginBase {
|
|||
$this->storage['head'] = '';
|
||||
}
|
||||
|
||||
$attached = drupal_render_collect_attached($this->storage['output']);
|
||||
$attached = $this->storage['output']['#attached'];
|
||||
$this->storage['css'] = $attached['css'];
|
||||
$this->storage['js'] = $attached['js'];
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ class Rss extends StylePluginBase {
|
|||
if ($display->hasPath()) {
|
||||
if (empty($this->preview)) {
|
||||
$build['#attached']['drupal_add_feed'][] = array($url, $title);
|
||||
drupal_render($build);
|
||||
drupal_process_attached($build);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -64,6 +64,7 @@ class Rss extends StylePluginBase {
|
|||
'href' => $url,
|
||||
);
|
||||
$this->view->feed_icon .= drupal_render($feed_icon);
|
||||
drupal_process_attached($feed_icon);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -135,20 +135,15 @@ class CacheTest extends PluginTestBase {
|
|||
$output = $view->preview();
|
||||
drupal_render($output);
|
||||
unset($view->pre_render_called);
|
||||
drupal_static_reset('_drupal_add_css');
|
||||
drupal_static_reset('_drupal_add_js');
|
||||
$view->destroy();
|
||||
|
||||
$view->setDisplay();
|
||||
$output = $view->preview();
|
||||
drupal_render($output);
|
||||
$css = _drupal_add_css();
|
||||
$css_path = drupal_get_path('module', 'views_test_data') . '/views_cache.test.css';
|
||||
$js_path = drupal_get_path('module', 'views_test_data') . '/views_cache.test.js';
|
||||
$js = _drupal_add_js();
|
||||
|
||||
$this->assertTrue(isset($css[basename($css_path)]), 'Make sure the css is added for cached views.');
|
||||
$this->assertTrue(isset($js[$js_path]), 'Make sure the js is added for cached views.');
|
||||
$this->assertTrue(in_array($css_path, $output['#attached']['css']), 'Make sure the css is added for cached views.');
|
||||
$this->assertTrue(in_array($js_path, $output['#attached']['js']), 'Make sure the js is added for cached views.');
|
||||
$this->assertFalse(!empty($view->build_info['pre_render_called']), 'Make sure hook_views_pre_render is not called for the cached view.');
|
||||
|
||||
// Now add some css/jss before running the view.
|
||||
|
@ -165,22 +160,19 @@ class CacheTest extends PluginTestBase {
|
|||
),
|
||||
);
|
||||
drupal_render($attached);
|
||||
drupal_process_attached($attached);
|
||||
$view->destroy();
|
||||
|
||||
$output = $view->preview();
|
||||
drupal_render($output);
|
||||
drupal_static_reset('_drupal_add_css');
|
||||
drupal_static_reset('_drupal_add_js');
|
||||
$this->assertTrue(empty($output['#attached']['css']), 'The view does not have attached CSS.');
|
||||
$this->assertTrue(empty($output['#attached']['js']), 'The view does not have attached JS.');
|
||||
$view->destroy();
|
||||
|
||||
$output = $view->preview();
|
||||
drupal_render($output);
|
||||
|
||||
$css = _drupal_add_css();
|
||||
$js = _drupal_add_js();
|
||||
|
||||
$this->assertFalse(isset($css['system.maintenance.css']), 'Make sure that unrelated css is not added.');
|
||||
$this->assertFalse(isset($js[drupal_get_path('module', 'user') . '/user.permissions.js']), 'Make sure that unrelated js is not added.');
|
||||
$this->assertTrue(empty($output['#attached']['css']), 'The cached view does not have attached CSS.');
|
||||
$this->assertTrue(empty($output['#attached']['js']), 'The cached view does not have attached JS.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -54,7 +54,8 @@ class RowEntityTest extends ViewUnitTestBase {
|
|||
$term->save();
|
||||
|
||||
$view = Views::getView('test_entity_row');
|
||||
$this->render($view->preview());
|
||||
$build = $view->preview();
|
||||
$this->render($build);
|
||||
|
||||
$this->assertText($term->getName(), 'The rendered entity appears as row in the view.');
|
||||
|
||||
|
|
|
@ -86,6 +86,7 @@ class ElementInfoManagerTest extends UnitTestCase {
|
|||
'#type' => 'page',
|
||||
'#show_messages' => TRUE,
|
||||
'#theme' => 'page',
|
||||
'#defaults_loaded' => TRUE,
|
||||
),
|
||||
array('page' => array(
|
||||
'#show_messages' => TRUE,
|
||||
|
@ -96,6 +97,7 @@ class ElementInfoManagerTest extends UnitTestCase {
|
|||
$data[] = array(
|
||||
'form',
|
||||
array(
|
||||
'#defaults_loaded' => TRUE,
|
||||
),
|
||||
array('page' => array(
|
||||
'#show_messages' => TRUE,
|
||||
|
@ -110,6 +112,7 @@ class ElementInfoManagerTest extends UnitTestCase {
|
|||
'#show_messages' => TRUE,
|
||||
'#theme' => 'page',
|
||||
'#number' => 597219,
|
||||
'#defaults_loaded' => TRUE,
|
||||
),
|
||||
array('page' => array(
|
||||
'#show_messages' => TRUE,
|
||||
|
@ -178,6 +181,7 @@ class ElementInfoManagerTest extends UnitTestCase {
|
|||
'#type' => 'page',
|
||||
'#show_messages' => TRUE,
|
||||
'#theme' => 'page',
|
||||
'#defaults_loaded' => TRUE,
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -189,6 +193,7 @@ class ElementInfoManagerTest extends UnitTestCase {
|
|||
'#theme' => 'page',
|
||||
'#input' => TRUE,
|
||||
'#value_callback' => array('TestElementPlugin', 'valueCallback'),
|
||||
'#defaults_loaded' => TRUE,
|
||||
),
|
||||
);
|
||||
return $data;
|
||||
|
|
Loading…
Reference in New Issue