Issue #2966327 by alexpott, mcdruid, dww, dsnopek, catch, pwolanin, larowlan, tim.plunkett, Berdir, Sam152: Limit what can be called by a callback in render arrays to reduce the risk of RCE
parent
7da933bafc
commit
5a42b47b6e
|
@ -16,7 +16,6 @@ use Drupal\Component\Utility\Html;
|
|||
use Drupal\Component\Utility\SortArray;
|
||||
use Drupal\Component\Utility\UrlHelper;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Render\BubbleableMetadata;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\Core\Render\Element\Link;
|
||||
use Drupal\Core\Render\HtmlResponseAttachmentsProcessor;
|
||||
|
@ -777,96 +776,14 @@ function drupal_pre_render_link($element) {
|
|||
/**
|
||||
* Pre-render callback: Collects child links into a single array.
|
||||
*
|
||||
* This function can be added as a pre_render callback for a renderable array,
|
||||
* usually one which will be themed by links.html.twig. It iterates through all
|
||||
* unrendered children of the element, collects any #links properties it finds,
|
||||
* merges them into the parent element's #links array, and prevents those
|
||||
* children from being rendered separately.
|
||||
* @deprecated in Drupal 8.8.0 and will be removed before Drupal 9.0.0. Use
|
||||
* \Drupal\Core\Render\Element\Link::preRenderLinks() instead.
|
||||
*
|
||||
* The purpose of this is to allow links to be logically grouped into related
|
||||
* categories, so that each child group can be rendered as its own list of
|
||||
* links if drupal_render() is called on it, but calling drupal_render() on the
|
||||
* parent element will still produce a single list containing all the remaining
|
||||
* links, regardless of what group they were in.
|
||||
*
|
||||
* A typical example comes from node links, which are stored in a renderable
|
||||
* array similar to this:
|
||||
* @code
|
||||
* $build['links'] = array(
|
||||
* '#theme' => 'links__node',
|
||||
* '#pre_render' => array('drupal_pre_render_links'),
|
||||
* 'comment' => array(
|
||||
* '#theme' => 'links__node__comment',
|
||||
* '#links' => array(
|
||||
* // An array of links associated with node comments, suitable for
|
||||
* // passing in to links.html.twig.
|
||||
* ),
|
||||
* ),
|
||||
* 'statistics' => array(
|
||||
* '#theme' => 'links__node__statistics',
|
||||
* '#links' => array(
|
||||
* // An array of links associated with node statistics, suitable for
|
||||
* // passing in to links.html.twig.
|
||||
* ),
|
||||
* ),
|
||||
* 'translation' => array(
|
||||
* '#theme' => 'links__node__translation',
|
||||
* '#links' => array(
|
||||
* // An array of links associated with node translation, suitable for
|
||||
* // passing in to links.html.twig.
|
||||
* ),
|
||||
* ),
|
||||
* );
|
||||
* @endcode
|
||||
*
|
||||
* In this example, the links are grouped by functionality, which can be
|
||||
* helpful to themers who want to display certain kinds of links independently.
|
||||
* For example, adding this code to node.html.twig will result in the comment
|
||||
* links being rendered as a single list:
|
||||
* @code
|
||||
* {{ content.links.comment }}
|
||||
* @endcode
|
||||
*
|
||||
* (where a node's content has been transformed into $content before handing
|
||||
* control to the node.html.twig template).
|
||||
*
|
||||
* The pre_render function defined here allows the above flexibility, but also
|
||||
* allows the following code to be used to render all remaining links into a
|
||||
* single list, regardless of their group:
|
||||
* @code
|
||||
* {{ content.links }}
|
||||
* @endcode
|
||||
*
|
||||
* In the above example, this will result in the statistics and translation
|
||||
* links being rendered together in a single list (but not the comment links,
|
||||
* which were rendered previously on their own).
|
||||
*
|
||||
* Because of the way this function works, the individual properties of each
|
||||
* group (for example, a group-specific #theme property such as
|
||||
* 'links__node__comment' in the example above, or any other property such as
|
||||
* #attributes or #pre_render that is attached to it) are only used when that
|
||||
* group is rendered on its own. When the group is rendered together with other
|
||||
* children, these child-specific properties are ignored, and only the overall
|
||||
* properties of the parent are used.
|
||||
* @see https://www.drupal.org/node/2966725
|
||||
*/
|
||||
function drupal_pre_render_links($element) {
|
||||
$element += ['#links' => [], '#attached' => []];
|
||||
foreach (Element::children($element) as $key) {
|
||||
$child = &$element[$key];
|
||||
// If the child has links which have not been printed yet and the user has
|
||||
// access to it, merge its links in to the parent.
|
||||
if (isset($child['#links']) && empty($child['#printed']) && Element::isVisibleElement($child)) {
|
||||
$element['#links'] += $child['#links'];
|
||||
// Mark the child as having been printed already (so that its links
|
||||
// cannot be mistakenly rendered twice).
|
||||
$child['#printed'] = TRUE;
|
||||
}
|
||||
// Merge attachments.
|
||||
if (isset($child['#attached'])) {
|
||||
$element['#attached'] = BubbleableMetadata::mergeAttachments($element['#attached'], $child['#attached']);
|
||||
}
|
||||
}
|
||||
return $element;
|
||||
@trigger_error('drupal_pre_render_links() is deprecated in Drupal 8.8.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Render\Element\Link::preRenderLinks() instead. See https://www.drupal.org/node/2966725', E_USER_DEPRECATED);
|
||||
return Link::preRenderLinks($element);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,13 +4,14 @@ namespace Drupal\Core\Access;
|
|||
|
||||
use Drupal\Component\Utility\Crypt;
|
||||
use Drupal\Core\Render\BubbleableMetadata;
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
use Drupal\Core\RouteProcessor\OutboundRouteProcessorInterface;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* Processes the outbound route to handle the CSRF token.
|
||||
*/
|
||||
class RouteProcessorCsrf implements OutboundRouteProcessorInterface {
|
||||
class RouteProcessorCsrf implements OutboundRouteProcessorInterface, TrustedCallbackInterface {
|
||||
|
||||
/**
|
||||
* The CSRF token generator.
|
||||
|
@ -81,4 +82,11 @@ class RouteProcessorCsrf implements OutboundRouteProcessorInterface {
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
return ['renderPlaceholderCsrfToken'];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,13 +7,14 @@ use Drupal\Core\DependencyInjection\DeprecatedServicePropertyTrait;
|
|||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
use Drupal\Core\Render\RendererInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Defines a generic controller to render a single entity.
|
||||
*/
|
||||
class EntityViewController implements ContainerInjectionInterface {
|
||||
class EntityViewController implements ContainerInjectionInterface, TrustedCallbackInterface {
|
||||
use DeprecatedServicePropertyTrait;
|
||||
|
||||
/**
|
||||
|
@ -110,6 +111,13 @@ class EntityViewController implements ContainerInjectionInterface {
|
|||
return $page;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
return ['buildTitle'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a page to render a single entity revision.
|
||||
*
|
||||
|
|
|
@ -11,6 +11,7 @@ use Drupal\Core\Field\FieldItemInterface;
|
|||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
use Drupal\Core\Theme\Registry;
|
||||
use Drupal\Core\TypedData\TranslatableInterface as TranslatableDataInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
@ -20,7 +21,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
|||
*
|
||||
* @ingroup entity_api
|
||||
*/
|
||||
class EntityViewBuilder extends EntityHandlerBase implements EntityHandlerInterface, EntityViewBuilderInterface {
|
||||
class EntityViewBuilder extends EntityHandlerBase implements EntityHandlerInterface, EntityViewBuilderInterface, TrustedCallbackInterface {
|
||||
use DeprecatedServicePropertyTrait;
|
||||
|
||||
/**
|
||||
|
@ -142,6 +143,13 @@ class EntityViewBuilder extends EntityHandlerBase implements EntityHandlerInterf
|
|||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
return ['build', 'buildMultiple'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
|
@ -1530,7 +1530,8 @@ function hook_entity_view_alter(array &$build, Drupal\Core\Entity\EntityInterfac
|
|||
$build['an_additional_field']['#weight'] = -10;
|
||||
|
||||
// Add a #post_render callback to act on the rendered HTML of the entity.
|
||||
$build['#post_render'][] = 'my_module_node_post_render';
|
||||
// The object must implement \Drupal\Core\Security\TrustedCallbackInterface.
|
||||
$build['#post_render'][] = '\Drupal\my_module\NodeCallback::postRender';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ use Drupal\Core\Extension\ModuleHandlerInterface;
|
|||
use Drupal\Core\Form\Exception\BrokenPostRequestException;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\Core\Render\ElementInfoManagerInterface;
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
use Drupal\Core\Theme\ThemeManagerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\FileBag;
|
||||
|
@ -26,7 +27,7 @@ use Symfony\Component\HttpFoundation\Response;
|
|||
*
|
||||
* @ingroup form_api
|
||||
*/
|
||||
class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormSubmitterInterface, FormCacheInterface {
|
||||
class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormSubmitterInterface, FormCacheInterface, TrustedCallbackInterface {
|
||||
|
||||
/**
|
||||
* The module handler.
|
||||
|
@ -1406,4 +1407,11 @@ class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormS
|
|||
return $this->currentUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
return ['renderPlaceholderFormAction', 'renderFormTokenPlaceholder'];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,6 +17,9 @@ use Drupal\Component\Plugin\PluginInspectionInterface;
|
|||
* Some render elements are specifically form input elements; see
|
||||
* \Drupal\Core\Render\Element\FormElementInterface for more information.
|
||||
*
|
||||
* The public API of these objects must be designed with security in mind as
|
||||
* render elements process raw user input.
|
||||
*
|
||||
* @see \Drupal\Core\Render\ElementInfoManager
|
||||
* @see \Drupal\Core\Render\Annotation\RenderElement
|
||||
* @see \Drupal\Core\Render\Element\RenderElement
|
||||
|
@ -24,7 +27,7 @@ use Drupal\Component\Plugin\PluginInspectionInterface;
|
|||
*
|
||||
* @ingroup theme_render
|
||||
*/
|
||||
interface ElementInterface extends PluginInspectionInterface {
|
||||
interface ElementInterface extends PluginInspectionInterface, RenderCallbackInterface {
|
||||
|
||||
/**
|
||||
* Returns the element properties for this element.
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace Drupal\Core\Render\Element;
|
|||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Component\Utility\Html as HtmlUtility;
|
||||
use Drupal\Core\Render\BubbleableMetadata;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\Core\Url as CoreUrl;
|
||||
|
||||
/**
|
||||
|
@ -99,4 +100,105 @@ class Link extends RenderElement {
|
|||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-render callback: Collects child links into a single array.
|
||||
*
|
||||
* This method can be added as a pre_render callback for a renderable array,
|
||||
* usually one which will be themed by links.html.twig. It iterates through
|
||||
* all unrendered children of the element, collects any #links properties it
|
||||
* finds, merges them into the parent element's #links array, and prevents
|
||||
* those children from being rendered separately.
|
||||
*
|
||||
* The purpose of this is to allow links to be logically grouped into related
|
||||
* categories, so that each child group can be rendered as its own list of
|
||||
* links if drupal_render() is called on it, but calling drupal_render() on
|
||||
* the parent element will still produce a single list containing all the
|
||||
* remaining links, regardless of what group they were in.
|
||||
*
|
||||
* A typical example comes from node links, which are stored in a renderable
|
||||
* array similar to this:
|
||||
* @code
|
||||
* $build['links'] = array(
|
||||
* '#theme' => 'links__node',
|
||||
* '#pre_render' => array(Link::class, 'preRenderLinks'),
|
||||
* 'comment' => array(
|
||||
* '#theme' => 'links__node__comment',
|
||||
* '#links' => array(
|
||||
* // An array of links associated with node comments, suitable for
|
||||
* // passing in to links.html.twig.
|
||||
* ),
|
||||
* ),
|
||||
* 'statistics' => array(
|
||||
* '#theme' => 'links__node__statistics',
|
||||
* '#links' => array(
|
||||
* // An array of links associated with node statistics, suitable for
|
||||
* // passing in to links.html.twig.
|
||||
* ),
|
||||
* ),
|
||||
* 'translation' => array(
|
||||
* '#theme' => 'links__node__translation',
|
||||
* '#links' => array(
|
||||
* // An array of links associated with node translation, suitable for
|
||||
* // passing in to links.html.twig.
|
||||
* ),
|
||||
* ),
|
||||
* );
|
||||
* @endcode
|
||||
*
|
||||
* In this example, the links are grouped by functionality, which can be
|
||||
* helpful to themers who want to display certain kinds of links
|
||||
* independently. For example, adding this code to node.html.twig will result
|
||||
* in the comment links being rendered as a single list:
|
||||
* @code
|
||||
* {{ content.links.comment }}
|
||||
* @endcode
|
||||
*
|
||||
* (where a node's content has been transformed into $content before handing
|
||||
* control to the node.html.twig template).
|
||||
*
|
||||
* The preRenderLinks method defined here allows the above flexibility, but
|
||||
* also allows the following code to be used to render all remaining links
|
||||
* into a single list, regardless of their group:
|
||||
* @code
|
||||
* {{ content.links }}
|
||||
* @endcode
|
||||
*
|
||||
* In the above example, this will result in the statistics and translation
|
||||
* links being rendered together in a single list (but not the comment links,
|
||||
* which were rendered previously on their own).
|
||||
*
|
||||
* Because of the way this method works, the individual properties of each
|
||||
* group (for example, a group-specific #theme property such as
|
||||
* 'links__node__comment' in the example above, or any other property such as
|
||||
* #attributes or #pre_render that is attached to it) are only used when that
|
||||
* group is rendered on its own. When the group is rendered together with
|
||||
* other children, these child-specific properties are ignored, and only the
|
||||
* overall properties of the parent are used.
|
||||
*
|
||||
* @param array $element
|
||||
* Render array containing child links to group.
|
||||
*
|
||||
* @return array
|
||||
* Render array containing child links grouped into a single array.
|
||||
*/
|
||||
public static function preRenderLinks($element) {
|
||||
$element += ['#links' => [], '#attached' => []];
|
||||
foreach (Element::children($element) as $key) {
|
||||
$child = &$element[$key];
|
||||
// If the child has links which have not been printed yet and the user has
|
||||
// access to it, merge its links in to the parent.
|
||||
if (isset($child['#links']) && empty($child['#printed']) && Element::isVisibleElement($child)) {
|
||||
$element['#links'] += $child['#links'];
|
||||
// Mark the child as having been printed already (so that its links
|
||||
// cannot be mistakenly rendered twice).
|
||||
$child['#printed'] = TRUE;
|
||||
}
|
||||
// Merge attachments.
|
||||
if (isset($child['#attached'])) {
|
||||
$element['#attached'] = BubbleableMetadata::mergeAttachments($element['#attached'], $child['#attached']);
|
||||
}
|
||||
}
|
||||
return $element;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Core\Render\Element;
|
||||
|
||||
/**
|
||||
* Indicates all public methods are safe to use in render callbacks.
|
||||
*
|
||||
* This should only be used when all public methods on the class are supposed to
|
||||
* used as render callbacks or the class implements ElementInterface. If this is
|
||||
* not the case then use TrustedCallbackInterface instead.
|
||||
*
|
||||
* @see \Drupal\Core\Render\Element\ElementInterface
|
||||
* @see \Drupal\Core\Security\TrustedCallbackInterface
|
||||
* @see \Drupal\Core\Render\Renderer::doCallback()
|
||||
*/
|
||||
interface RenderCallbackInterface {
|
||||
}
|
|
@ -9,6 +9,9 @@ use Drupal\Core\Access\AccessResultInterface;
|
|||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Controller\ControllerResolverInterface;
|
||||
use Drupal\Core\Render\Element\RenderCallbackInterface;
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
use Drupal\Core\Security\DoTrustedCallbackTrait;
|
||||
use Drupal\Core\Theme\ThemeManagerInterface;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
|
@ -16,6 +19,7 @@ use Symfony\Component\HttpFoundation\RequestStack;
|
|||
* Turns a render array into a HTML string.
|
||||
*/
|
||||
class Renderer implements RendererInterface {
|
||||
use DoTrustedCallbackTrait;
|
||||
|
||||
/**
|
||||
* The theme manager.
|
||||
|
@ -210,10 +214,7 @@ class Renderer implements RendererInterface {
|
|||
}
|
||||
|
||||
if (!isset($elements['#access']) && isset($elements['#access_callback'])) {
|
||||
if (is_string($elements['#access_callback']) && strpos($elements['#access_callback'], '::') === FALSE) {
|
||||
$elements['#access_callback'] = $this->controllerResolver->getControllerFromDefinition($elements['#access_callback']);
|
||||
}
|
||||
$elements['#access'] = call_user_func($elements['#access_callback'], $elements);
|
||||
$elements['#access'] = $this->doCallback('#access_callback', $elements['#access_callback'], [$elements]);
|
||||
}
|
||||
|
||||
// Early-return nothing if user does not have access.
|
||||
|
@ -350,12 +351,7 @@ class Renderer implements RendererInterface {
|
|||
}
|
||||
// Build the element if it is still empty.
|
||||
if (isset($elements['#lazy_builder'])) {
|
||||
$callable = $elements['#lazy_builder'][0];
|
||||
$args = $elements['#lazy_builder'][1];
|
||||
if (is_string($callable) && strpos($callable, '::') === FALSE) {
|
||||
$callable = $this->controllerResolver->getControllerFromDefinition($callable);
|
||||
}
|
||||
$new_elements = call_user_func_array($callable, $args);
|
||||
$new_elements = $this->doCallback('#lazy_builder', $elements['#lazy_builder'][0], $elements['#lazy_builder'][1]);
|
||||
// Retain the original cacheability metadata, plus cache keys.
|
||||
CacheableMetadata::createFromRenderArray($elements)
|
||||
->merge(CacheableMetadata::createFromRenderArray($new_elements))
|
||||
|
@ -372,10 +368,7 @@ class Renderer implements RendererInterface {
|
|||
// element is rendered into the final text.
|
||||
if (isset($elements['#pre_render'])) {
|
||||
foreach ($elements['#pre_render'] as $callable) {
|
||||
if (is_string($callable) && strpos($callable, '::') === FALSE) {
|
||||
$callable = $this->controllerResolver->getControllerFromDefinition($callable);
|
||||
}
|
||||
$elements = call_user_func($callable, $elements);
|
||||
$elements = $this->doCallback('#pre_render', $callable, [$elements]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -499,10 +492,7 @@ class Renderer implements RendererInterface {
|
|||
// outputted text to be filtered.
|
||||
if (isset($elements['#post_render'])) {
|
||||
foreach ($elements['#post_render'] as $callable) {
|
||||
if (is_string($callable) && strpos($callable, '::') === FALSE) {
|
||||
$callable = $this->controllerResolver->getControllerFromDefinition($callable);
|
||||
}
|
||||
$elements['#children'] = call_user_func($callable, $elements['#children'], $elements);
|
||||
$elements['#children'] = $this->doCallback('#post_render', $callable, [$elements['#children'], $elements]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -756,4 +746,38 @@ class Renderer implements RendererInterface {
|
|||
return $elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a callback.
|
||||
*
|
||||
* @param string $callback_type
|
||||
* The type of the callback. For example, '#post_render'.
|
||||
* @param string|callable $callback
|
||||
* The callback to perform.
|
||||
* @param array $args
|
||||
* The arguments to pass to the callback.
|
||||
*
|
||||
* @return mixed
|
||||
* The callback's return value.
|
||||
*
|
||||
* @see \Drupal\Core\Security\TrustedCallbackInterface
|
||||
*/
|
||||
protected function doCallback($callback_type, $callback, array $args) {
|
||||
if (is_string($callback)) {
|
||||
$double_colon = strpos($callback, '::');
|
||||
if ($double_colon === FALSE) {
|
||||
$callback = $this->controllerResolver->getControllerFromDefinition($callback);
|
||||
}
|
||||
elseif ($double_colon > 0) {
|
||||
$callback = explode('::', $callback, 2);
|
||||
}
|
||||
}
|
||||
$message = sprintf('Render %s callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was %s. Support for this callback implementation is deprecated in 8.8.0 and will be removed in Drupal 9.0.0. See https://www.drupal.org/node/2966725', $callback_type, '%s');
|
||||
// Add \Drupal\Core\Render\Element\RenderCallbackInterface as an extra
|
||||
// trusted interface so that:
|
||||
// - All public methods on Render elements are considered trusted.
|
||||
// - Helper classes that contain only callback methods can implement this
|
||||
// instead of TrustedCallbackInterface.
|
||||
return $this->doTrustedCallback($callback, $args, $message, TrustedCallbackInterface::TRIGGER_SILENCED_DEPRECATION, RenderCallbackInterface::class);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Core\Security;
|
||||
|
||||
/**
|
||||
* Ensures that TrustedCallbackInterface can be enforced for callback methods.
|
||||
*
|
||||
* @see \Drupal\Core\Security\TrustedCallbackInterface
|
||||
*/
|
||||
trait DoTrustedCallbackTrait {
|
||||
|
||||
/**
|
||||
* Performs a callback.
|
||||
*
|
||||
* If the callback is trusted the callback will occur. Trusted callbacks must
|
||||
* be methods of a class that implements
|
||||
* \Drupal\Core\Security\TrustedCallbackInterface or $extra_trusted_interface
|
||||
* or be an anonymous function. If the callback is not trusted then whether or
|
||||
* not the callback is called and what type of error is thrown depends on
|
||||
* $error_type. To provide time for dependent code to use trusted callbacks
|
||||
* use TrustedCallbackInterface::TRIGGER_SILENCED_DEPRECATION and then at a
|
||||
* later date change this to TrustedCallbackInterface::THROW_EXCEPTION.
|
||||
*
|
||||
* @param callable $callback
|
||||
* The callback to call. Note that callbacks which are objects and use the
|
||||
* magic method __invoke() are not supported.
|
||||
* @param array $args
|
||||
* The arguments to pass the callback.
|
||||
* @param $message
|
||||
* The error message if the callback is not trusted. If the message contains
|
||||
* "%s" it will be replaced in with the resolved callback.
|
||||
* @param string $error_type
|
||||
* (optional) The type of error to trigger. One of:
|
||||
* - TrustedCallbackInterface::THROW_EXCEPTION
|
||||
* - TrustedCallbackInterface::TRIGGER_DEPRECATION
|
||||
* - TrustedCallbackInterface::TRIGGER_SILENCED_DEPRECATION
|
||||
* Defaults to TrustedCallbackInterface::THROW_EXCEPTION.
|
||||
* @param string $extra_trusted_interface
|
||||
* (optional) An additional interface that if implemented by the callback
|
||||
* object means any public methods on that object are trusted.
|
||||
*
|
||||
* @return mixed
|
||||
* The callback's return value.
|
||||
*
|
||||
* @throws \Drupal\Core\Security\UntrustedCallbackException
|
||||
* Exception thrown if the callback is not trusted and $error_type equals
|
||||
* TrustedCallbackInterface::THROW_EXCEPTION.
|
||||
*
|
||||
* @see \Drupal\Core\Security\TrustedCallbackInterface
|
||||
*/
|
||||
public function doTrustedCallback(callable $callback, array $args, $message, $error_type = TrustedCallbackInterface::THROW_EXCEPTION, $extra_trusted_interface = NULL) {
|
||||
$object_or_classname = $callback;
|
||||
$safe_callback = FALSE;
|
||||
|
||||
if (is_array($callback)) {
|
||||
list($object_or_classname, $method_name) = $callback;
|
||||
}
|
||||
elseif (is_string($callback) && strpos($callback, '::') !== FALSE) {
|
||||
list($object_or_classname, $method_name) = explode('::', $callback, 2);
|
||||
}
|
||||
|
||||
if (isset($method_name)) {
|
||||
if ($extra_trusted_interface && is_subclass_of($object_or_classname, $extra_trusted_interface)) {
|
||||
$safe_callback = TRUE;
|
||||
}
|
||||
elseif (is_subclass_of($object_or_classname, TrustedCallbackInterface::class)) {
|
||||
if (is_object($object_or_classname)) {
|
||||
$methods = $object_or_classname->trustedCallbacks();
|
||||
}
|
||||
else {
|
||||
$methods = call_user_func($object_or_classname . '::trustedCallbacks');
|
||||
}
|
||||
$safe_callback = in_array($method_name, $methods, TRUE);
|
||||
}
|
||||
}
|
||||
elseif ($callback instanceof \Closure) {
|
||||
$safe_callback = TRUE;
|
||||
}
|
||||
|
||||
if (!$safe_callback) {
|
||||
$description = $object_or_classname;
|
||||
if (is_object($description)) {
|
||||
$description = get_class($description);
|
||||
}
|
||||
if (isset($method_name)) {
|
||||
$description .= '::' . $method_name;
|
||||
}
|
||||
$message = sprintf($message, $description);
|
||||
if ($error_type === TrustedCallbackInterface::TRIGGER_SILENCED_DEPRECATION) {
|
||||
@trigger_error($message, E_USER_DEPRECATED);
|
||||
}
|
||||
elseif ($error_type === TrustedCallbackInterface::TRIGGER_WARNING) {
|
||||
trigger_error($message, E_USER_WARNING);
|
||||
}
|
||||
else {
|
||||
throw new UntrustedCallbackException($message);
|
||||
}
|
||||
}
|
||||
|
||||
return call_user_func_array($callback, $args);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Core\Security;
|
||||
|
||||
/**
|
||||
* Interface to declare trusted callbacks.
|
||||
*
|
||||
* @see \Drupal\Core\Security\DoTrustedCallbackTrait
|
||||
*/
|
||||
interface TrustedCallbackInterface {
|
||||
|
||||
/**
|
||||
* Untrusted callbacks throw exceptions.
|
||||
*/
|
||||
const THROW_EXCEPTION = 'exception';
|
||||
|
||||
/**
|
||||
* Untrusted callbacks trigger E_USER_WARNING errors.
|
||||
*/
|
||||
const TRIGGER_WARNING = 'warning';
|
||||
|
||||
/**
|
||||
* Untrusted callbacks trigger silenced E_USER_DEPRECATION errors.
|
||||
*/
|
||||
const TRIGGER_SILENCED_DEPRECATION = 'silenced_deprecation';
|
||||
|
||||
/**
|
||||
* Lists the trusted callbacks provided by the implementing class.
|
||||
*
|
||||
* Trusted callbacks are public methods on the implementing class and can be
|
||||
* invoked via
|
||||
* \Drupal\Core\Security\DoTrustedCallbackTrait::doTrustedCallback().
|
||||
*
|
||||
* @return string[]
|
||||
* List of method names implemented by the class that can be used as trusted
|
||||
* callbacks.
|
||||
*
|
||||
* @see \Drupal\Core\Security\DoTrustedCallbackTrait::doTrustedCallback()
|
||||
*/
|
||||
public static function trustedCallbacks();
|
||||
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Core\Security;
|
||||
|
||||
/**
|
||||
* Exception thrown if a callback is untrusted.
|
||||
*/
|
||||
class UntrustedCallbackException extends \RuntimeException {
|
||||
}
|
|
@ -5,6 +5,7 @@ namespace Drupal\Core;
|
|||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Component\Utility\UrlHelper;
|
||||
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Routing\UrlGeneratorInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
@ -15,7 +16,7 @@ use Symfony\Component\HttpFoundation\Request;
|
|||
/**
|
||||
* Defines an object that holds information about a URL.
|
||||
*/
|
||||
class Url {
|
||||
class Url implements TrustedCallbackInterface {
|
||||
use DependencySerializationTrait;
|
||||
|
||||
/**
|
||||
|
@ -887,4 +888,11 @@ class Url {
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
return ['renderAccess'];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
namespace Drupal\big_pipe_regression_test;
|
||||
|
||||
use Drupal\big_pipe\Render\BigPipeMarkup;
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
|
||||
class BigPipeRegressionTestController {
|
||||
class BigPipeRegressionTestController implements TrustedCallbackInterface {
|
||||
|
||||
const MARKER_2678662 = '<script>var hitsTheFloor = "</body>";</script>';
|
||||
|
||||
|
@ -43,4 +44,11 @@ class BigPipeRegressionTestController {
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
return ['currentTime'];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,8 +4,9 @@ namespace Drupal\big_pipe_test;
|
|||
|
||||
use Drupal\big_pipe\Render\BigPipeMarkup;
|
||||
use Drupal\big_pipe_test\EventSubscriber\BigPipeTestSubscriber;
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
|
||||
class BigPipeTestController {
|
||||
class BigPipeTestController implements TrustedCallbackInterface {
|
||||
|
||||
/**
|
||||
* Returns a all BigPipe placeholder test case render arrays.
|
||||
|
@ -157,4 +158,11 @@ class BigPipeTestController {
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
return ['currentTime', 'counter'];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,11 +12,12 @@ use Drupal\Core\Extension\ModuleHandlerInterface;
|
|||
use Drupal\Core\Plugin\ContextAwarePluginInterface;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\block\Entity\Block;
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
|
||||
/**
|
||||
* Provides a Block view builder.
|
||||
*/
|
||||
class BlockViewBuilder extends EntityViewBuilder {
|
||||
class BlockViewBuilder extends EntityViewBuilder implements TrustedCallbackInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
@ -135,6 +136,13 @@ class BlockViewBuilder extends EntityViewBuilder {
|
|||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
return ['preRender', 'lazyBuilder'];
|
||||
}
|
||||
|
||||
/**
|
||||
* #lazy_builder callback; builds a #pre_render-able block.
|
||||
*
|
||||
|
|
|
@ -25,7 +25,7 @@ function block_test_block_view_test_cache_alter(array &$build, BlockPluginInterf
|
|||
$build['#attributes']['foo'] = 'bar';
|
||||
}
|
||||
if (\Drupal::state()->get('block_test_view_alter_append_pre_render_prefix') !== NULL) {
|
||||
$build['#pre_render'][] = 'block_test_pre_render_alter_content';
|
||||
$build['#pre_render'][] = '\Drupal\block_test\BlockRenderAlterContent::preRender';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,11 +52,3 @@ function block_test_block_build_test_cache_alter(array &$build, BlockPluginInter
|
|||
$build['#create_placeholder'] = \Drupal::state()->get('block_test_block_alter_create_placeholder');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* #pre_render callback for a block to alter its content.
|
||||
*/
|
||||
function block_test_pre_render_alter_content($build) {
|
||||
$build['#prefix'] = 'Hiya!<br>';
|
||||
return $build;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\block_test;
|
||||
|
||||
use Drupal\Core\Render\Element\RenderCallbackInterface;
|
||||
|
||||
/**
|
||||
* Implements a trusted preRender callback.
|
||||
*/
|
||||
class BlockRenderAlterContent implements RenderCallbackInterface {
|
||||
|
||||
/**
|
||||
* #pre_render callback for a block to alter its content.
|
||||
*/
|
||||
public static function preRender(array $build) {
|
||||
$build['#prefix'] = 'Hiya!<br>';
|
||||
return $build;
|
||||
}
|
||||
|
||||
}
|
|
@ -11,13 +11,13 @@ use Drupal\Component\Utility\Color;
|
|||
use Drupal\Component\Utility\Environment;
|
||||
use Drupal\Core\Asset\CssOptimizer;
|
||||
use Drupal\Core\Block\BlockPluginInterface;
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\File\Exception\FileException;
|
||||
use Drupal\Core\File\FileSystemInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Render\Element\Textfield;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\color\ColorSystemBrandingBlockAlter;
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
|
@ -116,26 +116,20 @@ function color_library_info_alter(&$libraries, $extension) {
|
|||
* Implements hook_block_view_BASE_BLOCK_ID_alter().
|
||||
*/
|
||||
function color_block_view_system_branding_block_alter(array &$build, BlockPluginInterface $block) {
|
||||
$build['#pre_render'][] = 'color_block_view_pre_render';
|
||||
$build['#pre_render'][] = [ColorSystemBrandingBlockAlter::class, 'preRender'];
|
||||
}
|
||||
|
||||
/**
|
||||
* #pre_render callback: Sets color preset logo.
|
||||
*
|
||||
* @deprecated in Drupal 8.8.0 and will be removed before Drupal 9.0.0. Use
|
||||
* \Drupal\color\ColorSystemBrandingBlockAlter::preRender() instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2966725
|
||||
*/
|
||||
function color_block_view_pre_render(array $build) {
|
||||
$theme_key = \Drupal::theme()->getActiveTheme()->getName();
|
||||
$config = \Drupal::config('color.theme.' . $theme_key);
|
||||
CacheableMetadata::createFromRenderArray($build)
|
||||
->addCacheableDependency($config)
|
||||
->applyTo($build);
|
||||
|
||||
// Override logo.
|
||||
$logo = $config->get('logo');
|
||||
if ($logo && $build['content']['site_logo'] && preg_match('!' . $theme_key . '/logo.svg$!', $build['content']['site_logo']['#uri'])) {
|
||||
$build['content']['site_logo']['#uri'] = file_url_transform_relative(file_create_url($logo));
|
||||
}
|
||||
|
||||
return $build;
|
||||
@trigger_error('color_block_view_pre_render() is deprecated in Drupal 8.8.0 and will be removed before Drupal 9.0.0. Use \Drupal\color\ColorSystemBrandingBlockAlter::preRender() instead. See https://www.drupal.org/node/2966725', E_USER_DEPRECATED);
|
||||
return ColorSystemBrandingBlockAlter::preRender($build);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\color;
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Render\Element\RenderCallbackInterface;
|
||||
|
||||
/**
|
||||
* Provides a trusted callback to alter the system branding block.
|
||||
*
|
||||
* @see color_block_view_system_branding_block_alter()
|
||||
*/
|
||||
class ColorSystemBrandingBlockAlter implements RenderCallbackInterface {
|
||||
|
||||
/**
|
||||
* #pre_render callback: Sets color preset logo.
|
||||
*/
|
||||
public static function preRender($build) {
|
||||
$theme_key = \Drupal::theme()->getActiveTheme()->getName();
|
||||
$config = \Drupal::config('color.theme.' . $theme_key);
|
||||
CacheableMetadata::createFromRenderArray($build)
|
||||
->addCacheableDependency($config)
|
||||
->applyTo($build);
|
||||
|
||||
// Override logo.
|
||||
$logo = $config->get('logo');
|
||||
if ($logo && $build['content']['site_logo'] && preg_match('!' . $theme_key . '/logo.svg$!', $build['content']['site_logo']['#uri'])) {
|
||||
$build['content']['site_logo']['#uri'] = file_url_transform_relative(file_create_url($logo));
|
||||
}
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\color\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests Color module's legacy code.
|
||||
*
|
||||
* @group color
|
||||
* @group legacy
|
||||
*/
|
||||
class ColorLegacyTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['system', 'color'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->container->get('theme_installer')->install(['bartik']);
|
||||
$this->config('system.theme')
|
||||
->set('default', 'bartik')
|
||||
->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests color_block_view_pre_render() deprecation.
|
||||
*
|
||||
* @expectedDeprecation color_block_view_pre_render() is deprecated in Drupal 8.8.0 and will be removed before Drupal 9.0.0. Use \Drupal\color\ColorSystemBrandingBlockAlter::preRender() instead. See https://www.drupal.org/node/2966725
|
||||
*/
|
||||
public function testColorSystemBrandingBlockAlterPreRender() {
|
||||
$render = color_block_view_pre_render([]);
|
||||
$this->assertEquals(['config:color.theme.bartik'], $render['#cache']['tags']);
|
||||
}
|
||||
|
||||
}
|
|
@ -8,6 +8,8 @@ use Drupal\Core\Entity\EntityFormBuilderInterface;
|
|||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Render\Element\Link;
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
use Drupal\Core\Render\RendererInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\Url;
|
||||
|
@ -15,7 +17,7 @@ use Drupal\Core\Url;
|
|||
/**
|
||||
* Defines a service for comment #lazy_builder callbacks.
|
||||
*/
|
||||
class CommentLazyBuilders {
|
||||
class CommentLazyBuilders implements TrustedCallbackInterface {
|
||||
use DeprecatedServicePropertyTrait;
|
||||
|
||||
/**
|
||||
|
@ -135,7 +137,7 @@ class CommentLazyBuilders {
|
|||
public function renderLinks($comment_entity_id, $view_mode, $langcode, $is_in_preview) {
|
||||
$links = [
|
||||
'#theme' => 'links__comment',
|
||||
'#pre_render' => ['drupal_pre_render_links'],
|
||||
'#pre_render' => [[Link::class, 'preRenderLinks']],
|
||||
'#attributes' => ['class' => ['links', 'inline']],
|
||||
];
|
||||
|
||||
|
@ -231,4 +233,11 @@ class CommentLazyBuilders {
|
|||
return content_translation_translate_access($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
return ['renderLinks', 'renderForm'];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -169,7 +169,7 @@ class CommentViewBuilder extends EntityViewBuilder {
|
|||
|
||||
// Embed the metadata for the comment "new" indicators on this node.
|
||||
$build[$id]['history'] = [
|
||||
'#lazy_builder' => ['history_attach_timestamp', [$commented_entity->id()]],
|
||||
'#lazy_builder' => ['\Drupal\history\HistoryRenderCallback::lazyBuilder', [$commented_entity->id()]],
|
||||
'#create_placeholder' => TRUE,
|
||||
];
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Drupal\editor;
|
||||
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
use Drupal\editor\Entity\Editor;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
use Drupal\Component\Plugin\PluginManagerInterface;
|
||||
|
@ -10,7 +11,7 @@ use Drupal\Core\Render\BubbleableMetadata;
|
|||
/**
|
||||
* Defines a service for Text Editor's render elements.
|
||||
*/
|
||||
class Element {
|
||||
class Element implements TrustedCallbackInterface {
|
||||
|
||||
/**
|
||||
* The Text Editor plugin manager service.
|
||||
|
@ -29,6 +30,13 @@ class Element {
|
|||
$this->pluginManager = $plugin_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
return ['preRenderTextFormat'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional #pre_render callback for 'text_format' elements.
|
||||
*/
|
||||
|
|
|
@ -12,6 +12,7 @@ use Drupal\Core\Cache\Cache;
|
|||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\Template\Attribute;
|
||||
use Drupal\filter\Element\TextFormat;
|
||||
use Drupal\filter\FilterFormatInterface;
|
||||
|
||||
/**
|
||||
|
@ -306,17 +307,14 @@ function check_markup($text, $format_id = NULL, $langcode = '', $filter_types_to
|
|||
/**
|
||||
* Render API callback: Hides the field value of 'text_format' elements.
|
||||
*
|
||||
* To not break form processing and previews if a user does not have access to
|
||||
* a stored text format, the expanded form elements in filter_process_format()
|
||||
* are forced to take over the stored #default_values for 'value' and 'format'.
|
||||
* However, to prevent the unfiltered, original #value from being displayed to
|
||||
* the user, we replace it with a friendly notice here.
|
||||
* @deprecated in Drupal 8.8.0 and will be removed before Drupal 9.0.0. Use
|
||||
* \Drupal\filter\Element\TextFormat::accessDeniedCallback() instead.
|
||||
*
|
||||
* @see filter_process_format()
|
||||
* @see https://www.drupal.org/node/2966725
|
||||
*/
|
||||
function filter_form_access_denied($element) {
|
||||
$element['#value'] = t('This field has been disabled because you do not have sufficient permissions to edit it.');
|
||||
return $element;
|
||||
@trigger_error('filter_form_access_denied() is deprecated in Drupal 8.8.0 and will be removed before Drupal 9.0.0. Use \Drupal\filter\Element\TextFormat::accessDeniedCallback() instead. See https://www.drupal.org/node/2966725', E_USER_DEPRECATED);
|
||||
return TextFormat::accessDeniedCallback($element);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -226,7 +226,7 @@ class TextFormat extends RenderElement {
|
|||
// Prepend #pre_render callback to replace field value with user notice
|
||||
// prior to rendering.
|
||||
$element['value'] += ['#pre_render' => []];
|
||||
array_unshift($element['value']['#pre_render'], 'filter_form_access_denied');
|
||||
array_unshift($element['value']['#pre_render'], [static::class, 'accessDeniedCallback']);
|
||||
|
||||
// Cosmetic adjustments.
|
||||
if (isset($element['value']['#rows'])) {
|
||||
|
@ -247,6 +247,26 @@ class TextFormat extends RenderElement {
|
|||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render API callback: Hides the field value of 'text_format' elements.
|
||||
*
|
||||
* To not break form processing and previews if a user does not have access to
|
||||
* a stored text format, the expanded form elements in filter_process_format()
|
||||
* are forced to take over the stored #default_values for 'value' and
|
||||
* 'format'. However, to prevent the unfiltered, original #value from being
|
||||
* displayed to the user, we replace it with a friendly notice here.
|
||||
*
|
||||
* @param array $element
|
||||
* The render array to add the access denied message to.
|
||||
*
|
||||
* @return array
|
||||
* The updated render array.
|
||||
*/
|
||||
public static function accessDeniedCallback(array $element) {
|
||||
$element['#value'] = t('This field has been disabled because you do not have sufficient permissions to edit it.');
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the current user.
|
||||
*
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Drupal\filter_test\Plugin\Filter;
|
||||
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
use Drupal\filter\FilterProcessResult;
|
||||
use Drupal\filter\Plugin\FilterBase;
|
||||
|
||||
|
@ -15,7 +16,7 @@ use Drupal\filter\Plugin\FilterBase;
|
|||
* type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_REVERSIBLE
|
||||
* )
|
||||
*/
|
||||
class FilterTestPlaceholders extends FilterBase {
|
||||
class FilterTestPlaceholders extends FilterBase implements TrustedCallbackInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
@ -42,4 +43,11 @@ class FilterTestPlaceholders extends FilterBase {
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
return ['renderDynamicThing'];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\filter\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests Filter module's legacy code.
|
||||
*
|
||||
* @group filter
|
||||
* @group legacy
|
||||
*/
|
||||
class FilterLegacyTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['filter'];
|
||||
|
||||
/**
|
||||
* Tests filter_form_access_denied() deprecation.
|
||||
*
|
||||
* @expectedDeprecation filter_form_access_denied() is deprecated in Drupal 8.8.0 and will be removed before Drupal 9.0.0. Use \Drupal\filter\Element\TextFormat::accessDeniedCallback() instead. See https://www.drupal.org/node/2966725
|
||||
*/
|
||||
public function testFilterFormAccessDenied() {
|
||||
$element = filter_form_access_denied([]);
|
||||
$this->assertEquals('This field has been disabled because you do not have sufficient permissions to edit it.', $element['#value']);
|
||||
}
|
||||
|
||||
}
|
|
@ -13,6 +13,7 @@ use Drupal\Core\Url;
|
|||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\history\HistoryRenderCallback;
|
||||
use Drupal\user\UserInterface;
|
||||
|
||||
/**
|
||||
|
@ -188,9 +189,13 @@ function history_user_delete($account) {
|
|||
*
|
||||
* @return array
|
||||
* A renderable array containing the last read timestamp.
|
||||
*
|
||||
* @deprecated in Drupal 8.8.0 and will be removed before Drupal 9.0.0. Use
|
||||
* \Drupal\history\HistoryRenderCallback::lazyBuilder() instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2966725
|
||||
*/
|
||||
function history_attach_timestamp($node_id) {
|
||||
$element = [];
|
||||
$element['#attached']['drupalSettings']['history']['lastReadTimestamps'][$node_id] = (int) history_read($node_id);
|
||||
return $element;
|
||||
@trigger_error('history_attach_timestamp() is deprecated in Drupal 8.8.0 and will be removed before Drupal 9.0.0. Use \Drupal\history\HistoryRenderCallback::lazyBuilder() instead. See https://www.drupal.org/node/2966725', E_USER_DEPRECATED);
|
||||
return HistoryRenderCallback::lazyBuilder($node_id);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\history;
|
||||
|
||||
use Drupal\Core\Render\Element\RenderCallbackInterface;
|
||||
|
||||
/**
|
||||
* Render callback object.
|
||||
*/
|
||||
class HistoryRenderCallback implements RenderCallbackInterface {
|
||||
|
||||
/**
|
||||
* #lazy_builder callback; attaches the last read timestamp for a node.
|
||||
*
|
||||
* @param int $node_id
|
||||
* The node ID for which to attach the last read timestamp.
|
||||
*
|
||||
* @return array
|
||||
* A renderable array containing the last read timestamp.
|
||||
*/
|
||||
public static function lazyBuilder($node_id) {
|
||||
$element = [];
|
||||
$element['#attached']['drupalSettings']['history']['lastReadTimestamps'][$node_id] = (int) history_read($node_id);
|
||||
return $element;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\history\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Tests History module's legacy code.
|
||||
*
|
||||
* @group history
|
||||
* @group legacy
|
||||
*/
|
||||
class HistoryLegacyTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['history', 'node', 'user', 'system'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('node');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installSchema('history', ['history']);
|
||||
$this->installSchema('system', ['sequences']);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests history_attach_timestamp() deprecation.
|
||||
*
|
||||
* @expectedDeprecation history_attach_timestamp() is deprecated in Drupal 8.8.0 and will be removed before Drupal 9.0.0. Use \Drupal\history\HistoryRenderCallback::lazyBuilder() instead. See https://www.drupal.org/node/2966725
|
||||
*/
|
||||
public function testHistoryAttachTimestamp() {
|
||||
$node = Node::create([
|
||||
'title' => 'n1',
|
||||
'type' => 'default',
|
||||
]);
|
||||
$node->save();
|
||||
|
||||
$user1 = User::create([
|
||||
'name' => 'user1',
|
||||
'mail' => 'user1@example.com',
|
||||
]);
|
||||
$user1->save();
|
||||
|
||||
\Drupal::currentUser()->setAccount($user1);
|
||||
history_write(1);
|
||||
|
||||
$render = history_attach_timestamp(1);
|
||||
$this->assertEquals(REQUEST_TIME, $render['#attached']['drupalSettings']['history']['lastReadTimestamps'][1]);
|
||||
}
|
||||
|
||||
}
|
|
@ -4,11 +4,13 @@ namespace Drupal\node;
|
|||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityViewBuilder;
|
||||
use Drupal\Core\Render\Element\Link;
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
|
||||
/**
|
||||
* View builder handler for nodes.
|
||||
*/
|
||||
class NodeViewBuilder extends EntityViewBuilder {
|
||||
class NodeViewBuilder extends EntityViewBuilder implements TrustedCallbackInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
@ -87,7 +89,7 @@ class NodeViewBuilder extends EntityViewBuilder {
|
|||
public static function renderLinks($node_entity_id, $view_mode, $langcode, $is_in_preview, $revision_id = NULL) {
|
||||
$links = [
|
||||
'#theme' => 'links__node',
|
||||
'#pre_render' => ['drupal_pre_render_links'],
|
||||
'#pre_render' => [[Link::class, 'preRenderLinks']],
|
||||
'#attributes' => ['class' => ['links', 'inline']],
|
||||
];
|
||||
|
||||
|
@ -146,4 +148,13 @@ class NodeViewBuilder extends EntityViewBuilder {
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
$callbacks = parent::trustedCallbacks();
|
||||
$callbacks[] = 'renderLinks';
|
||||
return $callbacks;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ use Drupal\Core\Form\FormStateInterface;
|
|||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\Messenger\MessengerInterface;
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\Access\AccessibleInterface;
|
||||
use Drupal\Core\Database\Query\Condition;
|
||||
|
@ -33,7 +34,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
|||
* title = @Translation("Content")
|
||||
* )
|
||||
*/
|
||||
class NodeSearch extends ConfigurableSearchPluginBase implements AccessibleInterface, SearchIndexingInterface {
|
||||
class NodeSearch extends ConfigurableSearchPluginBase implements AccessibleInterface, SearchIndexingInterface, TrustedCallbackInterface {
|
||||
use DeprecatedServicePropertyTrait;
|
||||
|
||||
/**
|
||||
|
@ -850,4 +851,11 @@ class NodeSearch extends ConfigurableSearchPluginBase implements AccessibleInter
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
return ['removeSubmittedInfo'];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace Drupal\form_test\Form;
|
|||
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
|
||||
/**
|
||||
|
@ -11,7 +12,7 @@ use Symfony\Component\HttpFoundation\JsonResponse;
|
|||
*
|
||||
* @internal
|
||||
*/
|
||||
class FormTestInputForgeryForm extends FormBase {
|
||||
class FormTestInputForgeryForm extends FormBase implements TrustedCallbackInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
@ -68,4 +69,11 @@ class FormTestInputForgeryForm extends FormBase {
|
|||
return new JsonResponse($form_state->getValues());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
return ['postRender'];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,11 +5,12 @@ namespace Drupal\pager_test\Controller;
|
|||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Database\Query\PagerSelectExtender;
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
|
||||
/**
|
||||
* Controller routine for testing the pager.
|
||||
*/
|
||||
class PagerTestController extends ControllerBase {
|
||||
class PagerTestController extends ControllerBase implements TrustedCallbackInterface {
|
||||
|
||||
/**
|
||||
* Builds a render array for a pageable test table.
|
||||
|
@ -124,4 +125,11 @@ class PagerTestController extends ControllerBase {
|
|||
return $pager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
return ['showPagerCacheContext'];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,11 +2,12 @@
|
|||
|
||||
namespace Drupal\render_placeholder_message_test;
|
||||
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
use Drupal\Core\Render\RenderContext;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
|
||||
|
||||
class RenderPlaceholderMessageTestController implements ContainerAwareInterface {
|
||||
class RenderPlaceholderMessageTestController implements ContainerAwareInterface, TrustedCallbackInterface {
|
||||
|
||||
use ContainerAwareTrait;
|
||||
|
||||
|
@ -97,4 +98,11 @@ class RenderPlaceholderMessageTestController implements ContainerAwareInterface
|
|||
return ['#markup' => '<p class="logged-message">Message: ' . $message . '</p>'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
return ['setAndLogMessage'];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ use Drupal\Core\Access\AccessResult;
|
|||
use Drupal\Core\Cache\CacheableResponse;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Messenger\MessengerInterface;
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
use Drupal\Core\Render\RendererInterface;
|
||||
use Drupal\Core\Render\Markup;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
@ -19,7 +20,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
|||
/**
|
||||
* Controller routines for system_test routes.
|
||||
*/
|
||||
class SystemTestController extends ControllerBase {
|
||||
class SystemTestController extends ControllerBase implements TrustedCallbackInterface {
|
||||
|
||||
/**
|
||||
* The lock service.
|
||||
|
@ -398,4 +399,11 @@ class SystemTestController extends ControllerBase {
|
|||
return new CacheableResponse('Foo', 200, ['Cache-Control' => 'bar']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
return ['preRenderCacheTags'];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace Drupal\Tests\system\Kernel\Theme;
|
|||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\Core\Render\Element\Link;
|
||||
use Drupal\Core\Session\UserSession;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
@ -417,9 +418,114 @@ class FunctionsTest extends KernelTestBase {
|
|||
}
|
||||
|
||||
/**
|
||||
* Test the use of drupal_pre_render_links() on a nested array of links.
|
||||
* Test the use of Link::preRenderLinks() on a nested array of links.
|
||||
*
|
||||
* @see \Drupal\Core\Render\Element\Link::preRenderLinks()
|
||||
*/
|
||||
public function testDrupalPreRenderLinks() {
|
||||
// Define the base array to be rendered, containing a variety of different
|
||||
// kinds of links.
|
||||
$base_array = [
|
||||
'#theme' => 'links',
|
||||
'#pre_render' => [[Link::class, 'preRenderLinks']],
|
||||
'#links' => [
|
||||
'parent_link' => [
|
||||
'title' => 'Parent link original',
|
||||
'url' => Url::fromRoute('router_test.1'),
|
||||
],
|
||||
],
|
||||
'first_child' => [
|
||||
'#theme' => 'links',
|
||||
'#links' => [
|
||||
// This should be rendered if 'first_child' is rendered separately,
|
||||
// but ignored if the parent is being rendered (since it duplicates
|
||||
// one of the parent's links).
|
||||
'parent_link' => [
|
||||
'title' => 'Parent link copy',
|
||||
'url' => Url::fromRoute('router_test.6'),
|
||||
],
|
||||
// This should always be rendered.
|
||||
'first_child_link' => [
|
||||
'title' => 'First child link',
|
||||
'url' => Url::fromRoute('router_test.7'),
|
||||
],
|
||||
],
|
||||
],
|
||||
// This should always be rendered as part of the parent.
|
||||
'second_child' => [
|
||||
'#theme' => 'links',
|
||||
'#links' => [
|
||||
'second_child_link' => [
|
||||
'title' => 'Second child link',
|
||||
'url' => Url::fromRoute('router_test.8'),
|
||||
],
|
||||
],
|
||||
],
|
||||
// This should never be rendered, since the user does not have access to
|
||||
// it.
|
||||
'third_child' => [
|
||||
'#theme' => 'links',
|
||||
'#links' => [
|
||||
'third_child_link' => [
|
||||
'title' => 'Third child link',
|
||||
'url' => Url::fromRoute('router_test.9'),
|
||||
],
|
||||
],
|
||||
'#access' => FALSE,
|
||||
],
|
||||
];
|
||||
|
||||
// Start with a fresh copy of the base array, and try rendering the entire
|
||||
// thing. We expect a single <ul> with appropriate links contained within
|
||||
// it.
|
||||
$render_array = $base_array;
|
||||
$html = \Drupal::service('renderer')->renderRoot($render_array);
|
||||
$dom = new \DOMDocument();
|
||||
$dom->loadHTML($html);
|
||||
$this->assertEqual($dom->getElementsByTagName('ul')->length, 1, 'One "ul" tag found in the rendered HTML.');
|
||||
$list_elements = $dom->getElementsByTagName('li');
|
||||
$this->assertEqual($list_elements->length, 3, 'Three "li" tags found in the rendered HTML.');
|
||||
$this->assertEqual($list_elements->item(0)->nodeValue, 'Parent link original', 'First expected link found.');
|
||||
$this->assertEqual($list_elements->item(1)->nodeValue, 'First child link', 'Second expected link found.');
|
||||
$this->assertEqual($list_elements->item(2)->nodeValue, 'Second child link', 'Third expected link found.');
|
||||
$this->assertIdentical(strpos($html, 'Parent link copy'), FALSE, '"Parent link copy" link not found.');
|
||||
$this->assertIdentical(strpos($html, 'Third child link'), FALSE, '"Third child link" link not found.');
|
||||
|
||||
// Now render 'first_child', followed by the rest of the links, and make
|
||||
// sure we get two separate <ul>'s with the appropriate links contained
|
||||
// within each.
|
||||
$render_array = $base_array;
|
||||
$child_html = \Drupal::service('renderer')->renderRoot($render_array['first_child']);
|
||||
$parent_html = \Drupal::service('renderer')->renderRoot($render_array);
|
||||
// First check the child HTML.
|
||||
$dom = new \DOMDocument();
|
||||
$dom->loadHTML($child_html);
|
||||
$this->assertEqual($dom->getElementsByTagName('ul')->length, 1, 'One "ul" tag found in the rendered child HTML.');
|
||||
$list_elements = $dom->getElementsByTagName('li');
|
||||
$this->assertEqual($list_elements->length, 2, 'Two "li" tags found in the rendered child HTML.');
|
||||
$this->assertEqual($list_elements->item(0)->nodeValue, 'Parent link copy', 'First expected link found.');
|
||||
$this->assertEqual($list_elements->item(1)->nodeValue, 'First child link', 'Second expected link found.');
|
||||
// Then check the parent HTML.
|
||||
$dom = new \DOMDocument();
|
||||
$dom->loadHTML($parent_html);
|
||||
$this->assertEqual($dom->getElementsByTagName('ul')->length, 1, 'One "ul" tag found in the rendered parent HTML.');
|
||||
$list_elements = $dom->getElementsByTagName('li');
|
||||
$this->assertEqual($list_elements->length, 2, 'Two "li" tags found in the rendered parent HTML.');
|
||||
$this->assertEqual($list_elements->item(0)->nodeValue, 'Parent link original', 'First expected link found.');
|
||||
$this->assertEqual($list_elements->item(1)->nodeValue, 'Second child link', 'Second expected link found.');
|
||||
$this->assertIdentical(strpos($parent_html, 'First child link'), FALSE, '"First child link" link not found.');
|
||||
$this->assertIdentical(strpos($parent_html, 'Third child link'), FALSE, '"Third child link" link not found.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the use of drupal_pre_render_links() on a nested array of links.
|
||||
*
|
||||
* @group legacy
|
||||
*
|
||||
* @expectedDeprecation Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was drupal_pre_render_links. Support for this callback implementation is deprecated in 8.8.0 and will be removed in Drupal 9.0.0. See https://www.drupal.org/node/2966725
|
||||
* @expectedDeprecation drupal_pre_render_links() is deprecated in Drupal 8.8.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Render\Element\Link::preRenderLinks() instead. See https://www.drupal.org/node/2966725
|
||||
*/
|
||||
public function testDrupalPreRenderLinksLegacy() {
|
||||
// Define the base array to be rendered, containing a variety of different
|
||||
// kinds of links.
|
||||
$base_array = [
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace Drupal\text\Plugin\Field\FieldFormatter;
|
|||
use Drupal\Core\Field\FormatterBase;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
|
||||
/**
|
||||
* Plugin implementation of the 'text_trimmed' formatter.
|
||||
|
@ -27,7 +28,7 @@ use Drupal\Core\Form\FormStateInterface;
|
|||
* }
|
||||
* )
|
||||
*/
|
||||
class TextTrimmedFormatter extends FormatterBase {
|
||||
class TextTrimmedFormatter extends FormatterBase implements TrustedCallbackInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
@ -124,4 +125,11 @@ class TextTrimmedFormatter extends FormatterBase {
|
|||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
return ['preRenderSummary'];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,13 +4,16 @@ namespace Drupal\toolbar\Controller;
|
|||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Ajax\AjaxResponse;
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\Core\Menu\MenuTreeParameters;
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
use Drupal\toolbar\Ajax\SetSubtreesCommand;
|
||||
|
||||
/**
|
||||
* Defines a controller for the toolbar module.
|
||||
*/
|
||||
class ToolbarController extends ControllerBase {
|
||||
class ToolbarController extends ControllerBase implements TrustedCallbackInterface {
|
||||
|
||||
/**
|
||||
* Returns an AJAX response to render the toolbar subtrees.
|
||||
|
@ -51,4 +54,90 @@ class ToolbarController extends ControllerBase {
|
|||
return AccessResult::allowedIf($this->currentUser()->hasPermission('access toolbar') && hash_equals($expected_hash, $hash))->cachePerPermissions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the toolbar's administration tray.
|
||||
*
|
||||
* @param array $element
|
||||
* A renderable array.
|
||||
*
|
||||
* @return array
|
||||
* The updated renderable array.
|
||||
*
|
||||
* @see drupal_render()
|
||||
*/
|
||||
public static function preRenderAdministrationTray(array $element) {
|
||||
$menu_tree = \Drupal::service('toolbar.menu_tree');
|
||||
// Load the administrative menu. The first level is the "Administration"
|
||||
// link. In order to load the children of that link, start and end on the
|
||||
// second level.
|
||||
$parameters = new MenuTreeParameters();
|
||||
$parameters->setMinDepth(2)->setMaxDepth(2)->onlyEnabledLinks();
|
||||
// @todo Make the menu configurable in https://www.drupal.org/node/1869638.
|
||||
$tree = $menu_tree->load('admin', $parameters);
|
||||
$manipulators = [
|
||||
['callable' => 'menu.default_tree_manipulators:checkAccess'],
|
||||
['callable' => 'menu.default_tree_manipulators:generateIndexAndSort'],
|
||||
['callable' => 'toolbar_menu_navigation_links'],
|
||||
];
|
||||
$tree = $menu_tree->transform($tree, $manipulators);
|
||||
$element['administration_menu'] = $menu_tree->build($tree);
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* #pre_render callback for toolbar_get_rendered_subtrees().
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public static function preRenderGetRenderedSubtrees(array $data) {
|
||||
$menu_tree = \Drupal::service('toolbar.menu_tree');
|
||||
// Load the administration menu. The first level is the "Administration"
|
||||
// link. In order to load the children of that link and the subsequent two
|
||||
// levels, start at the second level and end at the fourth.
|
||||
$parameters = new MenuTreeParameters();
|
||||
$parameters->setMinDepth(2)->setMaxDepth(4)->onlyEnabledLinks();
|
||||
// @todo Make the menu configurable in https://www.drupal.org/node/1869638.
|
||||
$tree = $menu_tree->load('admin', $parameters);
|
||||
$manipulators = [
|
||||
['callable' => 'menu.default_tree_manipulators:checkAccess'],
|
||||
['callable' => 'menu.default_tree_manipulators:generateIndexAndSort'],
|
||||
['callable' => 'toolbar_menu_navigation_links'],
|
||||
];
|
||||
$tree = $menu_tree->transform($tree, $manipulators);
|
||||
$subtrees = [];
|
||||
// Calculated the combined cacheability of all subtrees.
|
||||
$cacheability = new CacheableMetadata();
|
||||
foreach ($tree as $element) {
|
||||
/** @var \Drupal\Core\Menu\MenuLinkInterface $link */
|
||||
$link = $element->link;
|
||||
if ($element->subtree) {
|
||||
$subtree = $menu_tree->build($element->subtree);
|
||||
$output = \Drupal::service('renderer')->renderPlain($subtree);
|
||||
$cacheability = $cacheability->merge(CacheableMetadata::createFromRenderArray($subtree));
|
||||
}
|
||||
else {
|
||||
$output = '';
|
||||
}
|
||||
// Many routes have dots as route name, while some special ones like
|
||||
// <front> have <> characters in them.
|
||||
$url = $link->getUrlObject();
|
||||
$id = str_replace(['.', '<', '>'], ['-', '', ''], $url->isRouted() ? $url->getRouteName() : $url->getUri());
|
||||
|
||||
$subtrees[$id] = $output;
|
||||
}
|
||||
|
||||
// Store the subtrees, along with the cacheability metadata.
|
||||
$cacheability->applyTo($data);
|
||||
$data['#subtrees'] = $subtrees;
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
return ['preRenderAdministrationTray', 'preRenderGetRenderedSubtrees'];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\toolbar\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests Toolbar module's legacy code.
|
||||
*
|
||||
* @group toolbar
|
||||
* @group legacy
|
||||
*/
|
||||
class ToolbarLegacyTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['toolbar', 'system', 'user'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// Generate menu links from system.links.menu.yml.
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests toolbar_prerender_toolbar_administration_tray() deprecation.
|
||||
*
|
||||
* @expectedDeprecation toolbar_prerender_toolbar_administration_tray() is deprecated in Drupal 8.8.0 and will be removed before Drupal 9.0.0. Use \Drupal\toolbar\Controller\ToolbarController::preRenderAdministrationTray() instead. See https://www.drupal.org/node/2966725
|
||||
*/
|
||||
public function testPreRenderToolbarAdministrationTray() {
|
||||
$render = toolbar_prerender_toolbar_administration_tray([]);
|
||||
$this->assertEquals('admin', $render['administration_menu']['#menu_name']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests _toolbar_do_get_rendered_subtrees() deprecation.
|
||||
*
|
||||
* @expectedDeprecation _toolbar_do_get_rendered_subtrees() is deprecated in Drupal 8.8.0 and will be removed before Drupal 9.0.0. Use Drupal\toolbar\Controller\ToolbarController::preRenderGetRenderedSubtrees() instead. See https://www.drupal.org/node/2966725
|
||||
*/
|
||||
public function testDoGetRenderedSubtrees() {
|
||||
$render = _toolbar_do_get_rendered_subtrees([]);
|
||||
$this->assertEquals(['front' => ''], $render['#subtrees']);
|
||||
}
|
||||
|
||||
}
|
|
@ -6,12 +6,12 @@
|
|||
*/
|
||||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Menu\MenuTreeParameters;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Template\Attribute;
|
||||
use Drupal\Component\Utility\Crypt;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\toolbar\Controller\ToolbarController;
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
|
@ -192,9 +192,7 @@ function toolbar_toolbar() {
|
|||
'#heading' => t('Administration menu'),
|
||||
'#attached' => $subtrees_attached,
|
||||
'toolbar_administration' => [
|
||||
'#pre_render' => [
|
||||
'toolbar_prerender_toolbar_administration_tray',
|
||||
],
|
||||
'#pre_render' => [[ToolbarController::class, 'preRenderAdministrationTray']],
|
||||
'#type' => 'container',
|
||||
'#attributes' => [
|
||||
'class' => ['toolbar-menu-administration'],
|
||||
|
@ -217,25 +215,16 @@ function toolbar_toolbar() {
|
|||
* @return array
|
||||
* The updated renderable array.
|
||||
*
|
||||
* @deprecated in Drupal 8.8.0 and will be removed before Drupal 9.0.0. Use
|
||||
* \Drupal\toolbar\Controller\ToolbarController::preRenderAdministrationTray()
|
||||
* instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2966725
|
||||
* @see \Drupal\Core\Render\RendererInterface::render()
|
||||
*/
|
||||
function toolbar_prerender_toolbar_administration_tray(array $element) {
|
||||
$menu_tree = \Drupal::service('toolbar.menu_tree');
|
||||
// Load the administrative menu. The first level is the "Administration" link.
|
||||
// In order to load the children of that link, start and end on the second
|
||||
// level.
|
||||
$parameters = new MenuTreeParameters();
|
||||
$parameters->setMinDepth(2)->setMaxDepth(2)->onlyEnabledLinks();
|
||||
// @todo Make the menu configurable in https://www.drupal.org/node/1869638.
|
||||
$tree = $menu_tree->load('admin', $parameters);
|
||||
$manipulators = [
|
||||
['callable' => 'menu.default_tree_manipulators:checkAccess'],
|
||||
['callable' => 'menu.default_tree_manipulators:generateIndexAndSort'],
|
||||
['callable' => 'toolbar_menu_navigation_links'],
|
||||
];
|
||||
$tree = $menu_tree->transform($tree, $manipulators);
|
||||
$element['administration_menu'] = $menu_tree->build($tree);
|
||||
return $element;
|
||||
@trigger_error('toolbar_prerender_toolbar_administration_tray() is deprecated in Drupal 8.8.0 and will be removed before Drupal 9.0.0. Use \Drupal\toolbar\Controller\ToolbarController::preRenderAdministrationTray() instead. See https://www.drupal.org/node/2966725', E_USER_DEPRECATED);
|
||||
return ToolbarController::preRenderAdministrationTray($element);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -299,7 +288,7 @@ function toolbar_preprocess_html(&$variables) {
|
|||
*/
|
||||
function toolbar_get_rendered_subtrees() {
|
||||
$data = [
|
||||
'#pre_render' => ['_toolbar_do_get_rendered_subtrees'],
|
||||
'#pre_render' => [[ToolbarController::class, 'preRenderGetRenderedSubtrees']],
|
||||
'#cache' => [
|
||||
'keys' => [
|
||||
'toolbar_rendered_subtrees',
|
||||
|
@ -313,49 +302,16 @@ function toolbar_get_rendered_subtrees() {
|
|||
|
||||
/**
|
||||
* #pre_render callback for toolbar_get_rendered_subtrees().
|
||||
*
|
||||
* @deprecated in Drupal 8.8.0 and will be removed before Drupal 9.0.0. Use
|
||||
* \Drupal\toolbar\Controller\ToolbarController::preRenderGetRenderedSubtrees()
|
||||
* instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2966725
|
||||
*/
|
||||
function _toolbar_do_get_rendered_subtrees(array $data) {
|
||||
$menu_tree = \Drupal::service('toolbar.menu_tree');
|
||||
// Load the administration menu. The first level is the "Administration" link.
|
||||
// In order to load the children of that link and the subsequent two levels,
|
||||
// start at the second level and end at the fourth.
|
||||
$parameters = new MenuTreeParameters();
|
||||
$parameters->setMinDepth(2)->setMaxDepth(4)->onlyEnabledLinks();
|
||||
// @todo Make the menu configurable in https://www.drupal.org/node/1869638.
|
||||
$tree = $menu_tree->load('admin', $parameters);
|
||||
$manipulators = [
|
||||
['callable' => 'menu.default_tree_manipulators:checkAccess'],
|
||||
['callable' => 'menu.default_tree_manipulators:generateIndexAndSort'],
|
||||
['callable' => 'toolbar_menu_navigation_links'],
|
||||
];
|
||||
$tree = $menu_tree->transform($tree, $manipulators);
|
||||
$subtrees = [];
|
||||
// Calculated the combined cacheability of all subtrees.
|
||||
$cacheability = new CacheableMetadata();
|
||||
foreach ($tree as $element) {
|
||||
/** @var \Drupal\Core\Menu\MenuLinkInterface $link */
|
||||
$link = $element->link;
|
||||
if ($element->subtree) {
|
||||
$subtree = $menu_tree->build($element->subtree);
|
||||
$output = \Drupal::service('renderer')->renderPlain($subtree);
|
||||
$cacheability = $cacheability->merge(CacheableMetadata::createFromRenderArray($subtree));
|
||||
}
|
||||
else {
|
||||
$output = '';
|
||||
}
|
||||
// Many routes have dots as route name, while some special ones like <front>
|
||||
// have <> characters in them.
|
||||
$url = $link->getUrlObject();
|
||||
$id = str_replace(['.', '<', '>'], ['-', '', ''], $url->isRouted() ? $url->getRouteName() : $url->getUri());
|
||||
|
||||
$subtrees[$id] = $output;
|
||||
}
|
||||
|
||||
// Store the subtrees, along with the cacheability metadata.
|
||||
$cacheability->applyTo($data);
|
||||
$data['#subtrees'] = $subtrees;
|
||||
|
||||
return $data;
|
||||
@trigger_error('_toolbar_do_get_rendered_subtrees() is deprecated in Drupal 8.8.0 and will be removed before Drupal 9.0.0. Use Drupal\toolbar\Controller\ToolbarController::preRenderGetRenderedSubtrees() instead. See https://www.drupal.org/node/2966725', E_USER_DEPRECATED);
|
||||
return ToolbarController::preRenderGetRenderedSubtrees($data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,6 +10,7 @@ use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
|
|||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
use Drupal\language\ConfigurableLanguageManagerInterface;
|
||||
use Drupal\user\Plugin\LanguageNegotiation\LanguageNegotiationUser;
|
||||
use Drupal\user\Plugin\LanguageNegotiation\LanguageNegotiationUserAdmin;
|
||||
|
@ -18,7 +19,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
|||
/**
|
||||
* Form controller for the user account forms.
|
||||
*/
|
||||
abstract class AccountForm extends ContentEntityForm {
|
||||
abstract class AccountForm extends ContentEntityForm implements TrustedCallbackInterface {
|
||||
|
||||
/**
|
||||
* The language manager.
|
||||
|
@ -276,6 +277,13 @@ abstract class AccountForm extends ContentEntityForm {
|
|||
return parent::form($form, $form_state, $account);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
return ['alterPreferredLangcodeDescription'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Alters the preferred language widget description.
|
||||
*
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace Drupal\user\Plugin\Block;
|
|||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
use Drupal\Core\Routing\RedirectDestinationTrait;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Url;
|
||||
|
@ -21,7 +22,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
|||
* category = @Translation("Forms")
|
||||
* )
|
||||
*/
|
||||
class UserLoginBlock extends BlockBase implements ContainerFactoryPluginInterface {
|
||||
class UserLoginBlock extends BlockBase implements ContainerFactoryPluginInterface, TrustedCallbackInterface {
|
||||
|
||||
use RedirectDestinationTrait;
|
||||
|
||||
|
@ -159,4 +160,11 @@ class UserLoginBlock extends BlockBase implements ContainerFactoryPluginInterfac
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
return ['renderPlaceholderFormAction'];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Drupal\user;
|
||||
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
use Drupal\Core\Session\AccountProxyInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Core\Url;
|
||||
|
@ -9,7 +10,7 @@ use Drupal\Core\Url;
|
|||
/**
|
||||
* ToolbarLinkBuilder fills out the placeholders generated in user_toolbar().
|
||||
*/
|
||||
class ToolbarLinkBuilder {
|
||||
class ToolbarLinkBuilder implements TrustedCallbackInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
|
@ -83,4 +84,11 @@ class ToolbarLinkBuilder {
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
return ['renderToolbarLinks', 'renderDisplayName'];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,11 +2,15 @@
|
|||
|
||||
namespace Drupal\views\Form;
|
||||
|
||||
use Drupal\Component\Render\MarkupInterface;
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Form\FormInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
use Drupal\views\Render\ViewsRenderPipelineMarkup;
|
||||
use Drupal\views\ViewExecutable;
|
||||
|
||||
class ViewsFormMainForm implements FormInterface {
|
||||
class ViewsFormMainForm implements FormInterface, TrustedCallbackInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
@ -14,6 +18,55 @@ class ViewsFormMainForm implements FormInterface {
|
|||
public function getFormId() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces views substitution placeholders.
|
||||
*
|
||||
* @param array $element
|
||||
* An associative array containing the properties of the element.
|
||||
* Properties used: #substitutions, #children.
|
||||
*
|
||||
* @return array
|
||||
* The $element with prepared variables ready for #theme 'form'
|
||||
* in views_form_views_form.
|
||||
*/
|
||||
public static function preRenderViewsForm(array $element) {
|
||||
// Placeholders and their substitutions (usually rendered form elements).
|
||||
$search = [];
|
||||
$replace = [];
|
||||
|
||||
// Add in substitutions provided by the form.
|
||||
foreach ($element['#substitutions']['#value'] as $substitution) {
|
||||
$field_name = $substitution['field_name'];
|
||||
$row_id = $substitution['row_id'];
|
||||
|
||||
$search[] = $substitution['placeholder'];
|
||||
$replace[] = isset($element[$field_name][$row_id]) ? \Drupal::service('renderer')->render($element[$field_name][$row_id]) : '';
|
||||
}
|
||||
// Add in substitutions from hook_views_form_substitutions().
|
||||
$substitutions = \Drupal::moduleHandler()->invokeAll('views_form_substitutions');
|
||||
foreach ($substitutions as $placeholder => $substitution) {
|
||||
$search[] = Html::escape($placeholder);
|
||||
// Ensure that any replacements made are safe to make.
|
||||
if (!($substitution instanceof MarkupInterface)) {
|
||||
$substitution = Html::escape($substitution);
|
||||
}
|
||||
$replace[] = $substitution;
|
||||
}
|
||||
|
||||
// Apply substitutions to the rendered output.
|
||||
$output = str_replace($search, $replace, \Drupal::service('renderer')->render($element['output']));
|
||||
$element['output'] = ['#markup' => ViewsRenderPipelineMarkup::create($output)];
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
return ['preRenderViewsForm'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -21,7 +74,7 @@ class ViewsFormMainForm implements FormInterface {
|
|||
$form['#prefix'] = '<div class="views-form">';
|
||||
$form['#suffix'] = '</div>';
|
||||
|
||||
$form['#pre_render'][] = 'views_pre_render_views_form_views_form';
|
||||
$form['#pre_render'][] = [static::class, 'preRenderViewsForm'];
|
||||
|
||||
// Add the output markup to the form array so that it's included when the form
|
||||
// array is passed to the theme function.
|
||||
|
|
|
@ -9,6 +9,7 @@ use Drupal\Core\Language\LanguageInterface;
|
|||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\Core\Plugin\PluginBase as ComponentPluginBase;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
||||
use Drupal\views\Plugin\views\display\DisplayPluginBase;
|
||||
use Drupal\views\ViewExecutable;
|
||||
|
@ -38,7 +39,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
|||
*
|
||||
* @ingroup views_plugins
|
||||
*/
|
||||
abstract class PluginBase extends ComponentPluginBase implements ContainerFactoryPluginInterface, ViewsPluginInterface, DependentPluginInterface {
|
||||
abstract class PluginBase extends ComponentPluginBase implements ContainerFactoryPluginInterface, ViewsPluginInterface, DependentPluginInterface, TrustedCallbackInterface {
|
||||
|
||||
/**
|
||||
* Include negotiated languages when listing languages.
|
||||
|
@ -270,6 +271,13 @@ abstract class PluginBase extends ComponentPluginBase implements ContainerFactor
|
|||
$form['#pre_render'][] = [get_class($this), 'preRenderAddFieldsetMarkup'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
return ['preRenderAddFieldsetMarkup'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
|
@ -149,6 +149,15 @@ abstract class ArgumentPluginBase extends HandlerBase implements CacheableDepend
|
|||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
$callbacks = parent::trustedCallbacks();
|
||||
$callbacks[] = 'preRenderMoveArgumentOptions';
|
||||
return $callbacks;
|
||||
}
|
||||
|
||||
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
|
||||
parent::buildOptionsForm($form, $form_state);
|
||||
|
||||
|
|
|
@ -2126,6 +2126,15 @@ abstract class DisplayPluginBase extends PluginBase implements DisplayPluginInte
|
|||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
$callbacks = parent::trustedCallbacks();
|
||||
$callbacks[] = 'elementPreRender';
|
||||
return $callbacks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the cacheability of the current display to the given render array.
|
||||
*
|
||||
|
|
|
@ -55,6 +55,15 @@ class Custom extends FieldPluginBase {
|
|||
$form['#pre_render'][] = [$this, 'preRenderCustomForm'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
$callbacks = parent::trustedCallbacks();
|
||||
$callbacks[] = 'preRenderCustomForm';
|
||||
return $callbacks;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
|
@ -649,6 +649,15 @@ abstract class FilterPluginBase extends HandlerBase implements CacheableDependen
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
$callbacks = parent::trustedCallbacks();
|
||||
$callbacks[] = 'preRenderFlattenData';
|
||||
return $callbacks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the options form.
|
||||
*/
|
||||
|
|
|
@ -210,6 +210,15 @@ abstract class SortPluginBase extends HandlerBase implements CacheableDependency
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
$callbacks = parent::trustedCallbacks();
|
||||
$callbacks[] = 'preRenderFlattenData';
|
||||
return $callbacks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide default options for exposed sorts.
|
||||
*/
|
||||
|
|
|
@ -744,6 +744,15 @@ abstract class StylePluginBase extends PluginBase {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
$callbacks = parent::trustedCallbacks();
|
||||
$callbacks[] = 'elementPreRenderRow';
|
||||
return $callbacks;
|
||||
}
|
||||
|
||||
/**
|
||||
* #pre_render callback for view row field rendering.
|
||||
*
|
||||
|
|
|
@ -2,10 +2,12 @@
|
|||
|
||||
namespace Drupal\views_test_data\Controller;
|
||||
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
|
||||
/**
|
||||
* Controller class for views_test_data callbacks.
|
||||
*/
|
||||
class ViewsTestDataController {
|
||||
class ViewsTestDataController implements TrustedCallbackInterface {
|
||||
|
||||
/**
|
||||
* Renders an error form page.
|
||||
|
@ -24,4 +26,33 @@ class ViewsTestDataController {
|
|||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* #lazy_builder callback; for testing purposes only.
|
||||
*/
|
||||
public static function placeholderLazyBuilder() {
|
||||
// No-op.
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test pre_render function.
|
||||
*
|
||||
* @param array $element
|
||||
* A render array.
|
||||
*
|
||||
* @return array
|
||||
* The changed render array.
|
||||
*/
|
||||
public static function preRender($element) {
|
||||
$element['#markup'] = '\Drupal\views_test_data\Controller\ViewsTestDataController::preRender executed';
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
return ['placeholderLazyBuilder', 'preRender'];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -112,20 +112,6 @@ function template_preprocess_views_view_mapping_test(&$variables) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test pre_render function.
|
||||
*
|
||||
* @param array $element
|
||||
* A render array
|
||||
*
|
||||
* @return array
|
||||
* The changed render array.
|
||||
*/
|
||||
function views_test_data_test_pre_render_function($element) {
|
||||
$element['#markup'] = 'views_test_data_test_pre_render_function executed';
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_BASE_FORM_ID_alter().
|
||||
*/
|
||||
|
|
|
@ -54,21 +54,13 @@ function views_test_data_views_pre_render(ViewExecutable $view) {
|
|||
if (isset($view) && ($view->storage->id() == 'test_cache_header_storage')) {
|
||||
$view->element['#attached']['library'][] = 'views_test_data/test';
|
||||
$view->element['#attached']['drupalSettings']['foo'] = 'bar';
|
||||
$view->element['#attached']['placeholders']['non-existing-placeholder-just-for-testing-purposes']['#lazy_builder'] = ['views_test_data_placeholders', ['bar']];
|
||||
$view->element['#attached']['placeholders']['non-existing-placeholder-just-for-testing-purposes']['#lazy_builder'] = ['Drupal\views_test_data\Controller\ViewsTestDataController::placeholderLazyBuilder', ['bar']];
|
||||
$view->element['#cache']['tags'][] = 'views_test_data:1';
|
||||
$view->build_info['pre_render_called'] = TRUE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* #lazy_builder callback; for testing purposes only.
|
||||
*/
|
||||
function views_test_data_placeholders() {
|
||||
// No-op.
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_views_post_render().
|
||||
*/
|
||||
|
|
|
@ -237,7 +237,7 @@ class FieldKernelTest extends ViewsKernelTestBase {
|
|||
$renderer = \Drupal::service('renderer');
|
||||
|
||||
$view = Views::getView('test_field_argument_tokens');
|
||||
$this->executeView($view, ['{{ { "#pre_render": ["views_test_data_test_pre_render_function"]} }}']);
|
||||
$this->executeView($view, ['{{ { "#pre_render": ["\Drupal\views_test_data\Controller\ViewsTestDataController::preRender"]} }}']);
|
||||
|
||||
$name_field_0 = $view->field['name'];
|
||||
|
||||
|
@ -250,7 +250,7 @@ class FieldKernelTest extends ViewsKernelTestBase {
|
|||
return $name_field_0->advancedRender($row);
|
||||
});
|
||||
|
||||
$this->assertFalse(strpos((string) $output, 'views_test_data_test_pre_render_function executed') !== FALSE, 'Ensure that the pre_render function was not executed');
|
||||
$this->assertFalse(strpos((string) $output, '\Drupal\views_test_data\Controller\ViewsTestDataController::preRender executed') !== FALSE, 'Ensure that the pre_render function was not executed');
|
||||
$this->assertEqual('%1 !1', (string) $output, "Ensure that old style placeholders aren't replaced");
|
||||
|
||||
// This time use new style tokens but ensure that we still don't allow
|
||||
|
@ -263,8 +263,8 @@ class FieldKernelTest extends ViewsKernelTestBase {
|
|||
return $name_field_0->advancedRender($row);
|
||||
});
|
||||
|
||||
$this->assertFalse(strpos((string) $output, 'views_test_data_test_pre_render_function executed') !== FALSE, 'Ensure that the pre_render function was not executed');
|
||||
$this->assertEqual('{{ { "#pre_render": ["views_test_data_test_pre_render_function"]} }} {{ { "#pre_render": ["views_test_data_test_pre_render_function"]} }}', (string) $output, 'Ensure that new style placeholders are replaced');
|
||||
$this->assertFalse(strpos((string) $output, '\Drupal\views_test_data\Controller\ViewsTestDataController::preRender executed') !== FALSE, 'Ensure that the pre_render function was not executed');
|
||||
$this->assertEqual('{{ { "#pre_render": ["\Drupal\views_test_data\Controller\ViewsTestDataController::preRender"]} }} {{ { "#pre_render": ["\Drupal\views_test_data\Controller\ViewsTestDataController::preRender"]} }}', (string) $output, 'Ensure that new style placeholders are replaced');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -295,7 +295,7 @@ class CacheTest extends ViewsKernelTestBase {
|
|||
$this->assertEqual(['foo' => 'bar'], $output['#attached']['drupalSettings'], 'Make sure drupalSettings are added for cached views.');
|
||||
// Note: views_test_data_views_pre_render() adds some cache tags.
|
||||
$this->assertEqual(['config:views.view.test_cache_header_storage', 'views_test_data:1'], $output['#cache']['tags']);
|
||||
$this->assertEqual(['non-existing-placeholder-just-for-testing-purposes' => ['#lazy_builder' => ['views_test_data_placeholders', ['bar']]]], $output['#attached']['placeholders']);
|
||||
$this->assertEqual(['non-existing-placeholder-just-for-testing-purposes' => ['#lazy_builder' => ['Drupal\views_test_data\Controller\ViewsTestDataController::placeholderLazyBuilder', ['bar']]]], $output['#attached']['placeholders']);
|
||||
$this->assertFalse(!empty($view->build_info['pre_render_called']), 'Make sure hook_views_pre_render is not called for the cached view.');
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Drupal\Tests\views\Kernel;
|
||||
|
||||
use Drupal\Core\Render\RenderContext;
|
||||
use Drupal\views\Form\ViewsFormMainForm;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
|
@ -97,7 +98,29 @@ class ViewsHooksTest extends ViewsKernelTestBase {
|
|||
* Tests how hook_views_form_substitutions() makes substitutions.
|
||||
*
|
||||
* @see views_test_data_views_form_substitutions()
|
||||
* @see views_pre_render_views_form_views_form()
|
||||
* @see \Drupal\views\Form\ViewsFormMainForm::preRenderViewsForm()
|
||||
*/
|
||||
public function testViewsFormMainFormPreRender() {
|
||||
$element = [
|
||||
'output' => [
|
||||
'#plain_text' => '<!--will-be-escaped--><!--will-be-not-escaped-->',
|
||||
],
|
||||
'#substitutions' => ['#value' => []],
|
||||
];
|
||||
$element = \Drupal::service('renderer')->executeInRenderContext(new RenderContext(), function () use ($element) {
|
||||
return ViewsFormMainForm::preRenderViewsForm($element);
|
||||
});
|
||||
$this->setRawContent((string) $element['output']['#markup']);
|
||||
$this->assertEscaped('<em>escaped</em>');
|
||||
$this->assertRaw('<em>unescaped</em>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests views_pre_render_views_form_views_form() deprecation.
|
||||
*
|
||||
* @group legacy
|
||||
*
|
||||
* @expectedDeprecation views_pre_render_views_form_views_form() is deprecated in Drupal 8.8.0 and will be removed before Drupal 9.0.0. Use \Drupal\views\Form\ViewsFormMainForm::preRenderViewsForm() instead. See https://www.drupal.org/node/2966725
|
||||
*/
|
||||
public function testViewsPreRenderViewsFormViewsForm() {
|
||||
$element = [
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
* Primarily Drupal hooks and global API functions to manipulate views.
|
||||
*/
|
||||
|
||||
use Drupal\Component\Render\MarkupInterface;
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Database\Query\AlterableInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
|
@ -16,7 +15,7 @@ use Drupal\views\Plugin\Derivative\ViewsLocalTask;
|
|||
use Drupal\views\ViewEntityInterface;
|
||||
use Drupal\views\ViewExecutable;
|
||||
use Drupal\views\Entity\View;
|
||||
use Drupal\views\Render\ViewsRenderPipelineMarkup;
|
||||
use Drupal\views\Form\ViewsFormMainForm;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
|
@ -627,36 +626,15 @@ function views_disable_view(View $view) {
|
|||
* @return array
|
||||
* The $element with prepared variables ready for #theme 'form'
|
||||
* in views_form_views_form.
|
||||
*
|
||||
* @deprecated in Drupal 8.8.0 and will be removed before Drupal 9.0.0. Use
|
||||
* \Drupal\views\Form\ViewsFormMainForm::preRenderViewsForm() instead.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2966725
|
||||
*/
|
||||
function views_pre_render_views_form_views_form($element) {
|
||||
// Placeholders and their substitutions (usually rendered form elements).
|
||||
$search = [];
|
||||
$replace = [];
|
||||
|
||||
// Add in substitutions provided by the form.
|
||||
foreach ($element['#substitutions']['#value'] as $substitution) {
|
||||
$field_name = $substitution['field_name'];
|
||||
$row_id = $substitution['row_id'];
|
||||
|
||||
$search[] = $substitution['placeholder'];
|
||||
$replace[] = isset($element[$field_name][$row_id]) ? \Drupal::service('renderer')->render($element[$field_name][$row_id]) : '';
|
||||
}
|
||||
// Add in substitutions from hook_views_form_substitutions().
|
||||
$substitutions = \Drupal::moduleHandler()->invokeAll('views_form_substitutions');
|
||||
foreach ($substitutions as $placeholder => $substitution) {
|
||||
$search[] = Html::escape($placeholder);
|
||||
// Ensure that any replacements made are safe to make.
|
||||
if (!($substitution instanceof MarkupInterface)) {
|
||||
$substitution = Html::escape($substitution);
|
||||
}
|
||||
$replace[] = $substitution;
|
||||
}
|
||||
|
||||
// Apply substitutions to the rendered output.
|
||||
$output = str_replace($search, $replace, \Drupal::service('renderer')->render($element['output']));
|
||||
$element['output'] = ['#markup' => ViewsRenderPipelineMarkup::create($output)];
|
||||
|
||||
return $element;
|
||||
@trigger_error('views_pre_render_views_form_views_form() is deprecated in Drupal 8.8.0 and will be removed before Drupal 9.0.0. Use \Drupal\views\Form\ViewsFormMainForm::preRenderViewsForm() instead. See https://www.drupal.org/node/2966725', E_USER_DEPRECATED);
|
||||
return ViewsFormMainForm::preRenderViewsForm($element);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -9,6 +9,7 @@ namespace Drupal\Tests\Core\Render;
|
|||
|
||||
use Drupal\Core\Cache\MemoryBackend;
|
||||
use Drupal\Core\KeyValueStore\KeyValueMemoryFactory;
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
use Drupal\Core\State\State;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
|
||||
|
@ -632,7 +633,7 @@ class RendererBubblingTest extends RendererTestBase {
|
|||
}
|
||||
|
||||
|
||||
class BubblingTest {
|
||||
class BubblingTest implements TrustedCallbackInterface {
|
||||
|
||||
/**
|
||||
* #pre_render callback for testBubblingWithPrerender().
|
||||
|
@ -711,4 +712,11 @@ class BubblingTest {
|
|||
return $elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
return ['bubblingPreRender', 'bubblingNestedPreRenderUncached', 'bubblingNestedPreRenderCached', 'bubblingPlaceholder', 'bubblingCacheOverwritePrerender'];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Core\Render;
|
||||
|
||||
use Drupal\Tests\Traits\ExpectDeprecationTrait;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Core\Render\Renderer
|
||||
* @group Render
|
||||
* @group legacy
|
||||
* Once Renderer::doCallback() throws exceptions this will no longer be a legacy
|
||||
* test.
|
||||
*/
|
||||
class RendererCallbackTest extends RendererTestBase {
|
||||
|
||||
use ExpectDeprecationTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->controllerResolver->expects($this->any())
|
||||
->method('getControllerFromDefinition')
|
||||
->willReturnArgument(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the expected deprecations are triggered by Renderer::doCallback().
|
||||
*
|
||||
* @param array $render_array
|
||||
* The render array with a callback.
|
||||
* @param $expected_deprecation
|
||||
* The expected deprecation message triggered whilst rendering.
|
||||
*
|
||||
* @dataProvider providerTestCallback
|
||||
*/
|
||||
public function testCallback(array $render_array, $expected_deprecation) {
|
||||
$this->expectedDeprecations([$expected_deprecation]);
|
||||
$this->renderer->renderRoot($render_array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testCallback().
|
||||
*/
|
||||
public function providerTestCallback() {
|
||||
return [
|
||||
'Procedural function pre render' => [
|
||||
['#pre_render' => ['\Drupal\Tests\Core\Render\callback'], '#type' => 'container'],
|
||||
'Render #pre_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was \Drupal\Tests\Core\Render\callback. Support for this callback implementation is deprecated in 8.8.0 and will be removed in Drupal 9.0.0. See https://www.drupal.org/node/2966725',
|
||||
],
|
||||
'Static object method post render' => [
|
||||
['#post_render' => ['\Drupal\Tests\Core\Render\RendererCallbackTest::renderCallback'], '#type' => 'container'],
|
||||
'Render #post_render callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was \Drupal\Tests\Core\Render\RendererCallbackTest::renderCallback. Support for this callback implementation is deprecated in 8.8.0 and will be removed in Drupal 9.0.0. See https://www.drupal.org/node/2966725',
|
||||
],
|
||||
'Object method access callback' => [
|
||||
['#access_callback' => [$this, 'renderCallback'], '#type' => 'container'],
|
||||
'Render #access_callback callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was Drupal\Tests\Core\Render\RendererCallbackTest::renderCallback. Support for this callback implementation is deprecated in 8.8.0 and will be removed in Drupal 9.0.0. See https://www.drupal.org/node/2966725',
|
||||
],
|
||||
'Procedural function lazy builder' => [
|
||||
['#lazy_builder' => ['\Drupal\Tests\Core\Render\callback', []]],
|
||||
'Render #lazy_builder callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was \Drupal\Tests\Core\Render\callback. Support for this callback implementation is deprecated in 8.8.0 and will be removed in Drupal 9.0.0. See https://www.drupal.org/node/2966725',
|
||||
],
|
||||
'Invokable object access callback' => [
|
||||
['#access_callback' => $this, '#type' => 'container'],
|
||||
'Render #access_callback callbacks must be methods of a class that implements \Drupal\Core\Security\TrustedCallbackInterface or be an anonymous function. The callback was Drupal\Tests\Core\Render\RendererCallbackTest. Support for this callback implementation is deprecated in 8.8.0 and will be removed in Drupal 9.0.0. See https://www.drupal.org/node/2966725',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* A test render callback.
|
||||
*/
|
||||
public static function renderCallback($element = []) {
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements magic method as a render callback.
|
||||
*/
|
||||
public function __invoke($element = []) {
|
||||
return $element;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A test render callback.
|
||||
*/
|
||||
function callback($element = []) {
|
||||
return $element;
|
||||
}
|
|
@ -11,6 +11,7 @@ use Drupal\Component\Utility\Crypt;
|
|||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Render\Markup;
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
use Drupal\Core\Render\RenderContext;
|
||||
|
||||
/**
|
||||
|
@ -1142,7 +1143,7 @@ EOS;
|
|||
/**
|
||||
* @see \Drupal\Tests\Core\Render\RendererPlaceholdersTest::testRecursivePlaceholder()
|
||||
*/
|
||||
class RecursivePlaceholdersTest {
|
||||
class RecursivePlaceholdersTest implements TrustedCallbackInterface {
|
||||
|
||||
/**
|
||||
* #lazy_builder callback; bubbles another placeholder.
|
||||
|
@ -1162,4 +1163,11 @@ class RecursivePlaceholdersTest {
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
return ['callback'];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ use Drupal\Component\Render\MarkupInterface;
|
|||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Access\AccessResultInterface;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\Core\Render\Markup;
|
||||
use Drupal\Core\Template\Attribute;
|
||||
|
@ -1035,7 +1036,7 @@ class RendererTest extends RendererTestBase {
|
|||
|
||||
}
|
||||
|
||||
class TestAccessClass {
|
||||
class TestAccessClass implements TrustedCallbackInterface {
|
||||
|
||||
public static function accessTrue() {
|
||||
return TRUE;
|
||||
|
@ -1053,13 +1054,27 @@ class TestAccessClass {
|
|||
return AccessResult::forbidden();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
return ['accessTrue', 'accessFalse', 'accessResultAllowed', 'accessResultForbidden'];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TestCallables {
|
||||
class TestCallables implements TrustedCallbackInterface {
|
||||
|
||||
public function preRenderPrinted($elements) {
|
||||
$elements['#printed'] = TRUE;
|
||||
return $elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
return ['preRenderPrinted'];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ use Drupal\Core\Cache\Cache;
|
|||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Cache\Context\ContextCacheKeys;
|
||||
use Drupal\Core\Cache\MemoryBackend;
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
use Drupal\Core\Render\PlaceholderGenerator;
|
||||
use Drupal\Core\Render\PlaceholderingRenderCache;
|
||||
use Drupal\Core\Render\Renderer;
|
||||
|
@ -252,7 +253,7 @@ abstract class RendererTestBase extends UnitTestCase {
|
|||
}
|
||||
|
||||
|
||||
class PlaceholdersTest {
|
||||
class PlaceholdersTest implements TrustedCallbackInterface {
|
||||
|
||||
/**
|
||||
* #lazy_builder callback; attaches setting, generates markup.
|
||||
|
@ -308,4 +309,11 @@ class PlaceholdersTest {
|
|||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function trustedCallbacks() {
|
||||
return ['callbackTagCurrentTemperature', 'callbackPerUser', 'callback'];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\Core\Security;
|
||||
|
||||
use Drupal\Core\Security\TrustedCallbackInterface;
|
||||
use Drupal\Core\Security\DoTrustedCallbackTrait;
|
||||
use Drupal\Core\Security\UntrustedCallbackException;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use PHPUnit\Framework\Error\Warning;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\Core\Security\DoTrustedCallbackTrait
|
||||
* @group Security
|
||||
*/
|
||||
class DoTrustedCallbackTraitTest extends UnitTestCase {
|
||||
use DoTrustedCallbackTrait;
|
||||
|
||||
/**
|
||||
* @covers ::doTrustedCallback
|
||||
* @dataProvider providerTestTrustedCallbacks
|
||||
*/
|
||||
public function testTrustedCallbacks(callable $callback, $extra_trusted_interface = NULL) {
|
||||
$return = $this->doTrustedCallback($callback, [], '%s is not trusted', TrustedCallbackInterface::THROW_EXCEPTION, $extra_trusted_interface);
|
||||
$this->assertSame('test', $return);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for ::testTrustedCallbacks().
|
||||
*/
|
||||
public function providerTestTrustedCallbacks() {
|
||||
$closure = function () {
|
||||
return 'test';
|
||||
};
|
||||
|
||||
$tests['closure'] = [$closure];
|
||||
$tests['TrustedCallbackInterface_object'] = [[new TrustedMethods(), 'callback'], TrustedInterface::class];
|
||||
$tests['TrustedCallbackInterface_static_string'] = ['\Drupal\Tests\Core\Security\TrustedMethods::callback', TrustedInterface::class];
|
||||
$tests['TrustedCallbackInterface_static_array'] = [[TrustedMethods::class, 'callback'], TrustedInterface::class];
|
||||
$tests['extra_trusted_interface_object'] = [[new TrustedObject(), 'callback'], TrustedInterface::class];
|
||||
$tests['extra_trusted_interface_static_string'] = ['\Drupal\Tests\Core\Security\TrustedObject::callback', TrustedInterface::class];
|
||||
$tests['extra_trusted_interface_static_array'] = [[TrustedObject::class, 'callback'], TrustedInterface::class];
|
||||
return $tests;
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::doTrustedCallback
|
||||
* @dataProvider providerTestUntrustedCallbacks
|
||||
*/
|
||||
public function testUntrustedCallbacks(callable $callback, $extra_trusted_interface = NULL) {
|
||||
$this->expectException(UntrustedCallbackException::class);
|
||||
$this->doTrustedCallback($callback, [], '%s is not trusted', TrustedCallbackInterface::THROW_EXCEPTION, $extra_trusted_interface);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for ::testUntrustedCallbacks().
|
||||
*/
|
||||
public function providerTestUntrustedCallbacks() {
|
||||
$tests['TrustedCallbackInterface_object'] = [[new TrustedMethods(), 'unTrustedCallback'], TrustedInterface::class];
|
||||
$tests['TrustedCallbackInterface_static_string'] = ['\Drupal\Tests\Core\Security\TrustedMethods::unTrustedCallback', TrustedInterface::class];
|
||||
$tests['TrustedCallbackInterface_static_array'] = [[TrustedMethods::class, 'unTrustedCallback'], TrustedInterface::class];
|
||||
$tests['untrusted_object'] = [[new UntrustedObject(), 'callback'], TrustedInterface::class];
|
||||
$tests['untrusted_object_static_string'] = ['\Drupal\Tests\Core\Security\UntrustedObject::callback', TrustedInterface::class];
|
||||
$tests['untrusted_object_static_array'] = [[UntrustedObject::class, 'callback'], TrustedInterface::class];
|
||||
$tests['invokable_untrusted_object_static_array'] = [new InvokableUntrustedObject(), TrustedInterface::class];
|
||||
return $tests;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider errorTypeProvider
|
||||
*/
|
||||
public function testException($callback) {
|
||||
$this->expectException(UntrustedCallbackException::class);
|
||||
$this->expectExceptionMessage('Drupal\Tests\Core\Security\UntrustedObject::callback is not trusted');
|
||||
$this->doTrustedCallback($callback, [], '%s is not trusted');
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider errorTypeProvider
|
||||
* @group legacy
|
||||
* @expectedDeprecation Drupal\Tests\Core\Security\UntrustedObject::callback is not trusted
|
||||
*/
|
||||
public function testSilencedDeprecation($callback) {
|
||||
$this->doTrustedCallback($callback, [], '%s is not trusted', TrustedCallbackInterface::TRIGGER_SILENCED_DEPRECATION);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider errorTypeProvider
|
||||
*/
|
||||
public function testWarning($callback) {
|
||||
$this->expectException(Warning::class, 'Drupal\Tests\Core\Security\UntrustedObject::callback is not trusted');
|
||||
$this->doTrustedCallback($callback, [], '%s is not trusted', TrustedCallbackInterface::TRIGGER_WARNING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for tests of ::doTrustedCallback $error_type argument.
|
||||
*/
|
||||
public function errorTypeProvider() {
|
||||
$tests['untrusted_object'] = [[new UntrustedObject(), 'callback']];
|
||||
$tests['untrusted_object_static_string'] = ['Drupal\Tests\Core\Security\UntrustedObject::callback'];
|
||||
$tests['untrusted_object_static_array'] = [[UntrustedObject::class, 'callback']];
|
||||
return $tests;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface TrustedInterface {
|
||||
}
|
||||
|
||||
class TrustedObject implements TrustedInterface {
|
||||
|
||||
public static function callback() {
|
||||
return 'test';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class UntrustedObject {
|
||||
|
||||
public static function callback() {
|
||||
return 'test';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class InvokableUntrustedObject {
|
||||
|
||||
public function __invoke() {
|
||||
return 'test';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TrustedMethods implements TrustedCallbackInterface {
|
||||
|
||||
public static function trustedCallbacks() {
|
||||
return ['callback'];
|
||||
}
|
||||
|
||||
public static function callback() {
|
||||
return 'test';
|
||||
}
|
||||
|
||||
public static function unTrustedCallback() {
|
||||
return 'test';
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue