diff --git a/core/INSTALL.txt b/core/INSTALL.txt index 6ae05b4..79c08bd 100644 --- a/core/INSTALL.txt +++ b/core/INSTALL.txt @@ -18,7 +18,7 @@ Drupal requires: - A web server with PHP support, for example: - Apache 2.0 (or greater) (http://httpd.apache.org/). - Nginx 1.1 (or greater) (http://nginx.com/). -- PHP 5.4.5 (or greater) (http://php.net/). For better security support it is +- PHP 5.4.5 (or greater) (http://php.net/). For better security support its recommended to update to at least 5.5.21 or 5.6.5. - One of the following databases: - MySQL 5.5.3 (or greater) (http://www.mysql.com/). diff --git a/core/MAINTAINERS.txt b/core/MAINTAINERS.txt index 7e6b0c7..c72d26b 100644 --- a/core/MAINTAINERS.txt +++ b/core/MAINTAINERS.txt @@ -371,7 +371,6 @@ REST module Responsive Image module - Peter Droogmans 'attiks' http://drupal.org/user/105002 - Marc Drummond 'mdrummond' http://www.drupal.org/user/360968/ -- Jelle Sebreghts 'Jelle_S' https://www.drupal.org/user/829198 Search module - Jennifer Hodgdon 'jhodgdon' https://drupal.org/user/155601 diff --git a/core/lib/Drupal/Component/Utility/SafeMarkup.php b/core/lib/Drupal/Component/Utility/SafeMarkup.php index 15d5223..d69daa5 100644 --- a/core/lib/Drupal/Component/Utility/SafeMarkup.php +++ b/core/lib/Drupal/Component/Utility/SafeMarkup.php @@ -282,4 +282,37 @@ public static function placeholder($text) { return $string; } + /** + * Replace all occurrences of the search string with the replacement string. + * + * Functions identically to str_replace, but marks the returned output as safe + * if all the inputs and the subject have also been marked as safe. + */ + public static function replace($search, $replace, $subject) { + $output = str_replace($search, $replace, $subject); + + if (!is_array($replace)) { + if (!SafeMarkup::isSafe($replace)) { + return $output; + } + } + else { + foreach ($replace as $replacement) { + if (!SafeMarkup::isSafe($replacement)) { + return $output; + } + } + } + + // If we have reached this point, then all replacements were safe, and + // therefore if the subject was also safe, then the entire output is also + // safe, and should be marked as such. + if (SafeMarkup::isSafe($subject)) { + return SafeMarkup::set($output); + } + else { + return $output; + } + } + } diff --git a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/CompositeConstraintBase.php b/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/CompositeConstraintBase.php deleted file mode 100644 index 3c605dc..0000000 --- a/core/lib/Drupal/Core/Entity/Plugin/Validation/Constraint/CompositeConstraintBase.php +++ /dev/null @@ -1,30 +0,0 @@ -' . $markup . ''; + $element['#markup'] = SafeMarkup::format('', array('!markup' => $markup)); } else { $element['#markup'] = $markup; diff --git a/core/lib/Drupal/Core/Render/Renderer.php b/core/lib/Drupal/Core/Render/Renderer.php index b969a51..c253ae6 100644 --- a/core/lib/Drupal/Core/Render/Renderer.php +++ b/core/lib/Drupal/Core/Render/Renderer.php @@ -245,9 +245,8 @@ protected function doRender(&$elements, $is_root_call = FALSE) { $elements['#children'] = ''; } - // @todo Simplify after https://drupal.org/node/2273925 if (isset($elements['#markup'])) { - $elements['#markup'] = SafeMarkup::set($elements['#markup']); + $elements['#markup'] = SafeMarkup::checkAdminXss($elements['#markup']); } // Assume that if #theme is set it represents an implemented hook. @@ -545,7 +544,10 @@ public function generateCachePlaceholder($callback, array &$context) { 'token' => Crypt::randomBytesBase64(55), ]; - return ''; + return SafeMarkup::format('', array( + '@callback' => $callback, + '@token' => $context['token'], + )); } } diff --git a/core/modules/comment/src/CommentForm.php b/core/modules/comment/src/CommentForm.php index 12f5bb8..9f47519 100644 --- a/core/modules/comment/src/CommentForm.php +++ b/core/modules/comment/src/CommentForm.php @@ -323,12 +323,9 @@ public function validate(array $form, FormStateInterface $form_state) { foreach ($violations as $violation) { $form_state->setErrorByName('date', $violation->getMessage()); } - $violations = $comment->validate(); - // Filter out violations for the name path. + $violations = $comment->name->validate(); foreach ($violations as $violation) { - if ($violation->getPropertyPath() === 'name') { - $form_state->setErrorByName('name', $violation->getMessage()); - } + $form_state->setErrorByName('name', $violation->getMessage()); } return $comment; diff --git a/core/modules/comment/src/Entity/Comment.php b/core/modules/comment/src/Entity/Comment.php index 2023448..f83f54b 100644 --- a/core/modules/comment/src/Entity/Comment.php +++ b/core/modules/comment/src/Entity/Comment.php @@ -54,9 +54,6 @@ * }, * bundle_entity_type = "comment_type", * field_ui_base_route = "entity.comment_type.edit_form", - * constraints = { - * "CommentName" = {} - * } * ) */ class Comment extends ContentEntityBase implements CommentInterface { @@ -260,7 +257,8 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ->setDescription(t("The comment author's name.")) ->setTranslatable(TRUE) ->setSetting('max_length', 60) - ->setDefaultValue(''); + ->setDefaultValue('') + ->addConstraint('CommentName', array()); $fields['mail'] = BaseFieldDefinition::create('email') ->setLabel(t('Email')) diff --git a/core/modules/comment/src/Plugin/Validation/Constraint/CommentNameConstraint.php b/core/modules/comment/src/Plugin/Validation/Constraint/CommentNameConstraint.php index 4de6726..d387542 100644 --- a/core/modules/comment/src/Plugin/Validation/Constraint/CommentNameConstraint.php +++ b/core/modules/comment/src/Plugin/Validation/Constraint/CommentNameConstraint.php @@ -7,18 +7,17 @@ namespace Drupal\comment\Plugin\Validation\Constraint; -use Drupal\Core\Entity\Plugin\Validation\Constraint\CompositeConstraintBase; +use Symfony\Component\Validator\Constraint; /** * Supports validating comment author names. * * @Plugin( * id = "CommentName", - * label = @Translation("Comment author name", context = "Validation"), - * type = "entity:comment" + * label = @Translation("Comment author name", context = "Validation") * ) */ -class CommentNameConstraint extends CompositeConstraintBase { +class CommentNameConstraint extends Constraint { /** * Message shown when an anonymous user comments using a registered name. @@ -41,11 +40,4 @@ class CommentNameConstraint extends CompositeConstraintBase { */ public $messageMatch = 'The specified author name does not match the comment author.'; - /** - * {@inheritdoc} - */ - public function coversFields() { - return ['name', 'uid']; - } - } diff --git a/core/modules/comment/src/Plugin/Validation/Constraint/CommentNameConstraintValidator.php b/core/modules/comment/src/Plugin/Validation/Constraint/CommentNameConstraintValidator.php index 915d184..f6397e0 100644 --- a/core/modules/comment/src/Plugin/Validation/Constraint/CommentNameConstraintValidator.php +++ b/core/modules/comment/src/Plugin/Validation/Constraint/CommentNameConstraintValidator.php @@ -20,13 +20,6 @@ class CommentNameConstraintValidator extends ConstraintValidator implements ContainerInjectionInterface { /** - * Validator 2.5 and upwards compatible execution context. - * - * @var \Symfony\Component\Validator\Context\ExecutionContextInterface - */ - protected $context; - - /** * User storage handler. * * @var \Drupal\user\UserStorageInterface @@ -53,36 +46,41 @@ public static function create(ContainerInterface $container) { /** * {@inheritdoc} */ - public function validate($entity, Constraint $constraint) { - $author_name = $entity->name->value; - $owner_id = (int) $entity->uid->target_id; + public function validate($items, Constraint $constraint) { + /** @var CommentNameConstraint $constraint */ + if (!isset($items)) { + return; + } + /** @var CommentInterface $comment */ + $comment = $items->getEntity(); + + if (!isset($comment)) { + // Looks like we are validating a field not being part of a comment, + // nothing we can do then. + return; + } + $author_name = $items->first()->value; // Do not allow unauthenticated comment authors to use a name that is // taken by a registered user. - if (isset($author_name) && $author_name !== '' && $owner_id === 0) { + if (isset($author_name) && $author_name !== '' && $comment->getOwnerId() === 0) { $users = $this->userStorage->loadByProperties(array('name' => $author_name)); if (!empty($users)) { - $this->context->buildViolation($constraint->messageNameTaken, array('%name' => $author_name)) - ->atPath('name') - ->addViolation(); + $this->context->addViolation($constraint->messageNameTaken, array('%name' => $author_name)); } } // If an author name and owner are given, make sure they match. - elseif (isset($author_name) && $author_name !== '' && $owner_id) { - $owner = $this->userStorage->load($owner_id); + elseif (isset($author_name) && $author_name !== '' && $comment->getOwnerId()) { + $owner = $comment->getOwner(); if ($owner->getUsername() != $author_name) { - $this->context->buildViolation($constraint->messageMatch) - ->atPath('name') - ->addViolation(); + $this->context->addViolation($constraint->messageMatch); } } // Anonymous account might be required - depending on field settings. - if ($owner_id === 0 && empty($author_name) && - $this->getAnonymousContactDetailsSetting($entity) === COMMENT_ANONYMOUS_MUST_CONTACT) { - $this->context->buildViolation($constraint->messageRequired) - ->atPath('name') - ->addViolation(); + if ($comment->getOwnerId() === 0 && empty($author_name) && + $this->getAnonymousContactDetailsSetting($comment) === COMMENT_ANONYMOUS_MUST_CONTACT) { + $this->context->addViolation($constraint->messageRequired); } } diff --git a/core/modules/contextual/src/Element/ContextualLinksPlaceholder.php b/core/modules/contextual/src/Element/ContextualLinksPlaceholder.php index d10078b..e148a1b 100644 --- a/core/modules/contextual/src/Element/ContextualLinksPlaceholder.php +++ b/core/modules/contextual/src/Element/ContextualLinksPlaceholder.php @@ -9,6 +9,7 @@ use Drupal\Core\Template\Attribute; use Drupal\Core\Render\Element\RenderElement; +use Drupal\Component\Utility\SafeMarkup; /** * Provides a contextual_links_placeholder element. @@ -47,7 +48,11 @@ public function getInfo() { * @see _contextual_links_to_id() */ public static function preRenderPlaceholder(array $element) { - $element['#markup'] = ' $element['#id'])) . '>'; + // Because the only arguments to this markup will be instance of + // \Drupal\Core\Template\AttributeString, which is passed through + // \Drupal\Component\Utility\SafeMarkup::checkPlain() before being output + // this markup is safe, and is marked as such. + $element['#markup'] = SafeMarkup::set(' $element['#id'])) . '>'); return $element; } diff --git a/core/modules/filter/src/Element/ProcessedText.php b/core/modules/filter/src/Element/ProcessedText.php index d007b5f..4851a22 100644 --- a/core/modules/filter/src/Element/ProcessedText.php +++ b/core/modules/filter/src/Element/ProcessedText.php @@ -8,6 +8,7 @@ namespace Drupal\filter\Element; use Drupal\Component\Utility\NestedArray; +use Drupal\Component\Utility\SafeMarkup; use Drupal\Core\Cache\Cache; use Drupal\Core\Render\BubbleableMetadata; use Drupal\Core\Render\Element\RenderElement; @@ -118,9 +119,11 @@ public static function preRenderText($element) { } } - // Filtering done, store in #markup, set the updated bubbleable rendering - // metadata, and set the text format's cache tag. - $element['#markup'] = $text; + // Filtering and sanitizing has been done in + // \Drupal\filter\Plugin\FilterInterface. Store its content in #markup, + // set the updated bubbleable rendering metadata, and set the text format's + // cache tag. + $element['#markup'] = SafeMarkup::set($text); $metadata->applyTo($element); $element['#cache']['tags'] = Cache::mergeTags($element['#cache']['tags'], $format->getCacheTags()); diff --git a/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php b/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php index 14fdb0b..a3caa35 100644 --- a/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php +++ b/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php @@ -76,7 +76,7 @@ public function normalize($entity, $format = NULL, array $context = array()) { 'href' => $this->getEntityUri($entity), ), 'type' => array( - 'href' => $this->linkManager->getTypeUri($entity->getEntityTypeId(), $entity->bundle(), $context), + 'href' => $this->linkManager->getTypeUri($entity->getEntityTypeId(), $entity->bundle()), ), ), ); @@ -132,7 +132,7 @@ public function denormalize($data, $class, $format = NULL, array $context = arra } // Create the entity. - $typed_data_ids = $this->getTypedDataIds($data['_links']['type'], $context); + $typed_data_ids = $this->getTypedDataIds($data['_links']['type']); $entity_type = $this->entityManager->getDefinition($typed_data_ids['entity_type']); $langcode_key = $entity_type->getKey('langcode'); $values = array(); @@ -210,14 +210,13 @@ protected function getEntityUri($entity) { * * @param array $types * The type array(s) (value of the 'type' attribute of the incoming data). - * @param array $context - * Context from the normalizer/serializer operation. * * @return array * The typed data IDs. * + * @throws \Symfony\Component\Serializer\Exception\UnexpectedValueException */ - protected function getTypedDataIds($types, $context = array()) { + protected function getTypedDataIds($types) { // The 'type' can potentially contain an array of type objects. By default, // Drupal only uses a single type in serializing, but allows for multiple // types when deserializing. @@ -232,7 +231,7 @@ protected function getTypedDataIds($types, $context = array()) { $type_uri = $type['href']; // Check whether the URI corresponds to a known type on this site. Break // once one does. - if ($typed_data_ids = $this->linkManager->getTypeInternalIds($type['href'], $context)) { + if ($typed_data_ids = $this->linkManager->getTypeInternalIds($type['href'])) { break; } } diff --git a/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php b/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php index 5160b68..4b5344a 100644 --- a/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php +++ b/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php @@ -85,7 +85,7 @@ public function normalize($field_item, $format = NULL, array $context = array()) // objects. $field_name = $field_item->getParent()->getName(); $entity = $field_item->getEntity(); - $field_uri = $this->linkManager->getRelationUri($entity->getEntityTypeId(), $entity->bundle(), $field_name, $context); + $field_uri = $this->linkManager->getRelationUri($entity->getEntityTypeId(), $entity->bundle(), $field_name); return array( '_links' => array( $field_uri => array($link), diff --git a/core/modules/hal/src/Tests/FileNormalizeTest.php b/core/modules/hal/src/Tests/FileNormalizeTest.php index 64ffd07..2b50381 100644 --- a/core/modules/hal/src/Tests/FileNormalizeTest.php +++ b/core/modules/hal/src/Tests/FileNormalizeTest.php @@ -39,7 +39,8 @@ protected function setUp() { $this->installEntitySchema('file'); $entity_manager = \Drupal::entityManager(); - $link_manager = new LinkManager(new TypeLinkManager(new MemoryBackend('default'), \Drupal::moduleHandler(), \Drupal::service('config.factory'), \Drupal::service('request_stack')), new RelationLinkManager(new MemoryBackend('default'), $entity_manager, \Drupal::moduleHandler(), \Drupal::service('config.factory'), \Drupal::service('request_stack'))); + $url_assembler = \Drupal::service('unrouted_url_assembler'); + $link_manager = new LinkManager(new TypeLinkManager(new MemoryBackend('default'), $url_assembler), new RelationLinkManager(new MemoryBackend('default'), $entity_manager, $url_assembler)); // Set up the mock serializer. $normalizers = array( diff --git a/core/modules/hal/src/Tests/NormalizerTestBase.php b/core/modules/hal/src/Tests/NormalizerTestBase.php index aea5b25..43f10b1 100644 --- a/core/modules/hal/src/Tests/NormalizerTestBase.php +++ b/core/modules/hal/src/Tests/NormalizerTestBase.php @@ -83,7 +83,6 @@ protected function setUp() { $class = get_parent_class($class); } $this->installConfig(array('field', 'language')); - \Drupal::service('router.builder')->rebuild(); // Add German as a language. ConfigurableLanguage::create(array( @@ -135,7 +134,8 @@ protected function setUp() { ))->save(); $entity_manager = \Drupal::entityManager(); - $link_manager = new LinkManager(new TypeLinkManager(new MemoryBackend('default'), \Drupal::moduleHandler(), \Drupal::service('config.factory'), \Drupal::service('request_stack')), new RelationLinkManager(new MemoryBackend('default'), $entity_manager, \Drupal::moduleHandler(), \Drupal::service('config.factory'), \Drupal::service('request_stack'))); + $url_assembler = \Drupal::service('unrouted_url_assembler'); + $link_manager = new LinkManager(new TypeLinkManager(new MemoryBackend('default'), $url_assembler), new RelationLinkManager(new MemoryBackend('default'), $entity_manager, $url_assembler)); $chain_resolver = new ChainEntityResolver(array(new UuidResolver($entity_manager), new TargetIdResolver())); diff --git a/core/modules/rest/config/install/rest.settings.yml b/core/modules/rest/config/install/rest.settings.yml index 0c388ca..0a44346 100644 --- a/core/modules/rest/config/install/rest.settings.yml +++ b/core/modules/rest/config/install/rest.settings.yml @@ -44,7 +44,3 @@ resources: # # The full documentation is located at # https://drupal.org/documentation/modules/rest - -# Set the domain for REST type and relation links. -# If left blank, the site's domain will be used -link_domain: ~ diff --git a/core/modules/rest/config/schema/rest.schema.yml b/core/modules/rest/config/schema/rest.schema.yml index 8014b31..8421d13 100644 --- a/core/modules/rest/config/schema/rest.schema.yml +++ b/core/modules/rest/config/schema/rest.schema.yml @@ -10,9 +10,6 @@ rest.settings: sequence: type: rest_resource label: 'Resource' - link_domain: - type: string - label: 'Domain of the relation' rest_resource: type: mapping diff --git a/core/modules/rest/rest.api.php b/core/modules/rest/rest.api.php index fb8993d..2f3f6ee 100644 --- a/core/modules/rest/rest.api.php +++ b/core/modules/rest/rest.api.php @@ -29,52 +29,5 @@ function hook_rest_resource_alter(&$definitions) { } /** - * Alter the REST type URI. - * - * Modules may wish to alter the type URI generated for a resource based on the - * context of the serializer/normalizer operation. - * - * @param string $uri - * The URI to alter. - * @param array $context - * The context from the serializer/normalizer operation. - * - * @see \Symfony\Component\Serializer\SerializerInterface::serialize() - * @see \Symfony\Component\Serializer\SerializerInterface::deserialize() - * @see \Symfony\Component\Serializer\NormalizerInterface::normalize() - * @see \Symfony\Component\Serializer\DenormalizerInterface::denormalize() - */ -function hook_rest_type_uri_alter(&$uri, $context = array()) { - if ($context['mymodule'] == TRUE) { - $base = \Drupal::config('rest.settings')->get('link_domain'); - $uri = str_replace($base, 'http://mymodule.domain', $uri); - } -} - - -/** - * Alter the REST relation URI. - * - * Modules may wish to alter the relation URI generated for a resource based on - * the context of the serializer/normalizer operation. - * - * @param string $uri - * The URI to alter. - * @param array $context - * The context from the serializer/normalizer operation. - * - * @see \Symfony\Component\Serializer\SerializerInterface::serialize() - * @see \Symfony\Component\Serializer\SerializerInterface::deserialize() - * @see \Symfony\Component\Serializer\NormalizerInterface::normalize() - * @see \Symfony\Component\Serializer\DenormalizerInterface::denormalize() - */ -function hook_rest_relation_uri_alter(&$uri, $context = array()) { - if ($context['mymodule'] == TRUE) { - $base = \Drupal::config('rest.settings')->get('link_domain'); - $uri = str_replace($base, 'http://mymodule.domain', $uri); - } -} - -/** * @} End of "addtogroup hooks". */ diff --git a/core/modules/rest/rest.services.yml b/core/modules/rest/rest.services.yml index 04f5f1d..bfba7bb 100644 --- a/core/modules/rest/rest.services.yml +++ b/core/modules/rest/rest.services.yml @@ -19,10 +19,10 @@ services: arguments: ['@rest.link_manager.type', '@rest.link_manager.relation'] rest.link_manager.type: class: Drupal\rest\LinkManager\TypeLinkManager - arguments: ['@cache.default', '@module_handler', '@config.factory', '@request_stack'] + arguments: ['@cache.default', '@unrouted_url_assembler'] rest.link_manager.relation: class: Drupal\rest\LinkManager\RelationLinkManager - arguments: ['@cache.default', '@entity.manager', '@module_handler', '@config.factory', '@request_stack'] + arguments: ['@cache.default', '@entity.manager', '@unrouted_url_assembler'] rest.resource_routes: class: Drupal\rest\Routing\ResourceRoutes arguments: ['@plugin.manager.rest', '@config.factory', '@logger.channel.rest'] diff --git a/core/modules/rest/src/LinkManager/ConfigurableLinkManagerInterface.php b/core/modules/rest/src/LinkManager/ConfigurableLinkManagerInterface.php deleted file mode 100644 index 0505811..0000000 --- a/core/modules/rest/src/LinkManager/ConfigurableLinkManagerInterface.php +++ /dev/null @@ -1,25 +0,0 @@ -typeLinkManager->getTypeUri($entity_type, $bundle, $context); + public function getTypeUri($entity_type, $bundle) { + return $this->typeLinkManager->getTypeUri($entity_type, $bundle); } /** * Implements \Drupal\rest\LinkManager\TypeLinkManagerInterface::getTypeInternalIds(). */ - public function getTypeInternalIds($type_uri, $context = array()) { - return $this->typeLinkManager->getTypeInternalIds($type_uri, $context); + public function getTypeInternalIds($type_uri) { + return $this->typeLinkManager->getTypeInternalIds($type_uri); } /** * Implements \Drupal\rest\LinkManager\RelationLinkManagerInterface::getRelationUri(). */ - public function getRelationUri($entity_type, $bundle, $field_name, $context = array()) { - return $this->relationLinkManager->getRelationUri($entity_type, $bundle, $field_name, $context); + public function getRelationUri($entity_type, $bundle, $field_name) { + return $this->relationLinkManager->getRelationUri($entity_type, $bundle, $field_name); } /** @@ -63,13 +63,4 @@ public function getRelationInternalIds($relation_uri) { return $this->relationLinkManager->getRelationInternalIds($relation_uri); } - /** - * {@inheritdoc} - */ - public function setLinkDomain($domain) { - $this->relationLinkManager->setLinkDomain($domain); - $this->typeLinkManager->setLinkDomain($domain); - return $this; - } - } diff --git a/core/modules/rest/src/LinkManager/LinkManagerBase.php b/core/modules/rest/src/LinkManager/LinkManagerBase.php deleted file mode 100644 index f93f6fd..0000000 --- a/core/modules/rest/src/LinkManager/LinkManagerBase.php +++ /dev/null @@ -1,64 +0,0 @@ -linkDomain = rtrim($domain, '/'); - return $this; - } - - /** - * Gets the link domain. - * - * @return string - * The link domain. - */ - protected function getLinkDomain() { - if (empty($this->linkDomain)) { - if ($domain = $this->configFactory->get('rest.settings')->get('link_domain')) { - $this->linkDomain = rtrim($domain, '/'); - } - else { - $request = $this->requestStack->getCurrentRequest(); - $this->linkDomain = $request->getSchemeAndHttpHost() . $request->getBasePath(); - } - } - return $this->linkDomain; - } - -} diff --git a/core/modules/rest/src/LinkManager/RelationLinkManager.php b/core/modules/rest/src/LinkManager/RelationLinkManager.php index 9cdd5c4..38bf4cf 100644 --- a/core/modules/rest/src/LinkManager/RelationLinkManager.php +++ b/core/modules/rest/src/LinkManager/RelationLinkManager.php @@ -9,13 +9,11 @@ use Drupal\Core\Cache\Cache; use Drupal\Core\Cache\CacheBackendInterface; -use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Entity\ContentEntityTypeInterface; use Drupal\Core\Entity\EntityManagerInterface; -use Drupal\Core\Extension\ModuleHandlerInterface; -use Symfony\Component\HttpFoundation\RequestStack; +use Drupal\Core\Utility\UnroutedUrlAssemblerInterface; -class RelationLinkManager extends LinkManagerBase implements RelationLinkManagerInterface { +class RelationLinkManager implements RelationLinkManagerInterface { /** * @var \Drupal\Core\Cache\CacheBackendInterface; @@ -30,11 +28,11 @@ class RelationLinkManager extends LinkManagerBase implements RelationLinkManager protected $entityManager; /** - * Module handler service. + * The unrouted URL assembler. * - * @var \Drupal\Core\Extension\ModuleHandlerInterface + * @var \Drupal\Core\Utility\UnroutedUrlAssemblerInterface */ - protected $moduleHandler; + protected $urlAssembler; /** * Constructor. @@ -43,35 +41,27 @@ class RelationLinkManager extends LinkManagerBase implements RelationLinkManager * The cache of relation URIs and their associated Typed Data IDs. * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager * The entity manager. - * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler - * The module handler service. - * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory - * The config factory service. - * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack - * The request stack. + * @param \Drupal\Core\Utility\UnroutedUrlAssemblerInterface $url_assembler + * The unrouted URL assembler. */ - public function __construct(CacheBackendInterface $cache, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, ConfigFactoryInterface $config_factory, RequestStack $request_stack) { + public function __construct(CacheBackendInterface $cache, EntityManagerInterface $entity_manager, UnroutedUrlAssemblerInterface $url_assembler) { $this->cache = $cache; $this->entityManager = $entity_manager; - $this->configFactory = $config_factory; - $this->moduleHandler = $module_handler; - $this->requestStack = $request_stack; + $this->urlAssembler = $url_assembler; } /** * {@inheritdoc} */ - public function getRelationUri($entity_type, $bundle, $field_name, $context = array()) { - $uri = $this->getLinkDomain() . "/rest/relation/$entity_type/$bundle/$field_name"; - $this->moduleHandler->alter('rest_relation_uri', $uri, $context); - return $uri; + public function getRelationUri($entity_type, $bundle, $field_name) { + return $this->urlAssembler->assemble("base:rest/relation/$entity_type/$bundle/$field_name", array('absolute' => TRUE)); } /** * {@inheritdoc} */ - public function getRelationInternalIds($relation_uri, $context = array()) { - $relations = $this->getRelations($context); + public function getRelationInternalIds($relation_uri) { + $relations = $this->getRelations(); if (isset($relations[$relation_uri])) { return $relations[$relation_uri]; } @@ -87,18 +77,15 @@ public function getRelationInternalIds($relation_uri, $context = array()) { * even primitives, are given a relation URI. It is up to the caller to * determine which URIs to use. * - * @param array $context - * Context from the normalizer/serializer operation. - * * @return array * An array of typed data ids (entity_type, bundle, and field name) keyed * by corresponding relation URI. */ - protected function getRelations($context = array()) { + protected function getRelations() { $cid = 'rest:links:relations'; $cache = $this->cache->get($cid); if (!$cache) { - $this->writeCache($context); + $this->writeCache(); $cache = $this->cache->get($cid); } return $cache->data; @@ -106,18 +93,15 @@ protected function getRelations($context = array()) { /** * Writes the cache of relation links. - * - * @param array $context - * Context from the normalizer/serializer operation. */ - protected function writeCache($context = array()) { + protected function writeCache() { $data = array(); foreach ($this->entityManager->getDefinitions() as $entity_type) { if ($entity_type instanceof ContentEntityTypeInterface) { foreach ($this->entityManager->getBundleInfo($entity_type->id()) as $bundle => $bundle_info) { foreach ($this->entityManager->getFieldDefinitions($entity_type->id(), $bundle) as $field_definition) { - $relation_uri = $this->getRelationUri($entity_type->id(), $bundle, $field_definition->getName(), $context); + $relation_uri = $this->getRelationUri($entity_type->id(), $bundle, $field_definition->getName()); $data[$relation_uri] = array( 'entity_type' => $entity_type, 'bundle' => $bundle, diff --git a/core/modules/rest/src/LinkManager/RelationLinkManagerInterface.php b/core/modules/rest/src/LinkManager/RelationLinkManagerInterface.php index f99e43e..2dc175c 100644 --- a/core/modules/rest/src/LinkManager/RelationLinkManagerInterface.php +++ b/core/modules/rest/src/LinkManager/RelationLinkManagerInterface.php @@ -7,7 +7,7 @@ namespace Drupal\rest\LinkManager; -interface RelationLinkManagerInterface extends ConfigurableLinkManagerInterface { +interface RelationLinkManagerInterface { /** * Gets the URI that corresponds to a field. @@ -18,13 +18,11 @@ * The bundle name. * @param string $field_name * The field name. - * @param array $context - * (optional) Optional serializer/normalizer context. * * @return string * The corresponding URI for the field. */ - public function getRelationUri($entity_type, $bundle, $field_name, $context = array()); + public function getRelationUri($entity_type, $bundle, $field_name); /** * Translates a REST URI into internal IDs. diff --git a/core/modules/rest/src/LinkManager/TypeLinkManager.php b/core/modules/rest/src/LinkManager/TypeLinkManager.php index 3c9f665..4160b2b 100644 --- a/core/modules/rest/src/LinkManager/TypeLinkManager.php +++ b/core/modules/rest/src/LinkManager/TypeLinkManager.php @@ -9,11 +9,9 @@ use Drupal\Core\Cache\Cache; use Drupal\Core\Cache\CacheBackendInterface; -use Drupal\Core\Config\ConfigFactoryInterface; -use Drupal\Core\Extension\ModuleHandlerInterface; -use Symfony\Component\HttpFoundation\RequestStack; +use Drupal\Core\Utility\UnroutedUrlAssemblerInterface; -class TypeLinkManager extends LinkManagerBase implements TypeLinkManagerInterface { +class TypeLinkManager implements TypeLinkManagerInterface { /** * Injected cache backend. @@ -23,29 +21,23 @@ class TypeLinkManager extends LinkManagerBase implements TypeLinkManagerInterfac protected $cache; /** - * Module handler service. + * The unrouted URL assembler. * - * @var \Drupal\Core\Extension\ModuleHandlerInterface + * @var \Drupal\Core\Utility\UnroutedUrlAssemblerInterface */ - protected $moduleHandler; + protected $urlAssembler; /** * Constructor. * * @param \Drupal\Core\Cache\CacheBackendInterface $cache * The injected cache backend for caching type URIs. - * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler - * The module handler service. - * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory - * The config factory service. - * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack - * The request stack. + * @param \Drupal\Core\Utility\UnroutedUrlAssemblerInterface $url_assembler + * The unrouted URL assembler. */ - public function __construct(CacheBackendInterface $cache, ModuleHandlerInterface $module_handler, ConfigFactoryInterface $config_factory, RequestStack $request_stack) { + public function __construct(CacheBackendInterface $cache, UnroutedUrlAssemblerInterface $url_assembler) { $this->cache = $cache; - $this->configFactory = $config_factory; - $this->moduleHandler = $module_handler; - $this->requestStack = $request_stack; + $this->urlAssembler = $url_assembler; } /** @@ -55,23 +47,20 @@ public function __construct(CacheBackendInterface $cache, ModuleHandlerInterface * The bundle's entity type. * @param string $bundle * The name of the bundle. - * @param array $context - * Context of normalizer/serializer. * - * @return string + * @return array * The URI that identifies this bundle. */ - public function getTypeUri($entity_type, $bundle, $context = array()) { - $uri = $this->getLinkDomain() . "/rest/type/$entity_type/$bundle"; - $this->moduleHandler->alter('rest_type_uri', $uri, $context); - return $uri; + public function getTypeUri($entity_type, $bundle) { + // @todo Make the base path configurable. + return $this->urlAssembler->assemble("base:rest/type/$entity_type/$bundle", array('absolute' => TRUE)); } /** * Implements \Drupal\rest\LinkManager\TypeLinkManagerInterface::getTypeInternalIds(). */ - public function getTypeInternalIds($type_uri, $context = array()) { - $types = $this->getTypes($context); + public function getTypeInternalIds($type_uri) { + $types = $this->getTypes(); if (isset($types[$type_uri])) { return $types[$type_uri]; } @@ -81,18 +70,15 @@ public function getTypeInternalIds($type_uri, $context = array()) { /** * Get the array of type links. * - * @param array $context - * Context from the normalizer/serializer operation. - * * @return array * An array of typed data ids (entity_type and bundle) keyed by * corresponding type URI. */ - protected function getTypes($context = array()) { + protected function getTypes() { $cid = 'rest:links:types'; $cache = $this->cache->get($cid); if (!$cache) { - $this->writeCache($context); + $this->writeCache(); $cache = $this->cache->get($cid); } return $cache->data; @@ -100,11 +86,8 @@ protected function getTypes($context = array()) { /** * Writes the cache of type links. - * - * @param array $context - * Context from the normalizer/serializer operation. */ - protected function writeCache($context = array()) { + protected function writeCache() { $data = array(); // Type URIs correspond to bundles. Iterate through the bundles to get the @@ -118,7 +101,7 @@ protected function writeCache($context = array()) { } foreach ($bundles as $bundle => $bundle_info) { // Get a type URI for the bundle. - $bundle_uri = $this->getTypeUri($entity_type_id, $bundle, $context); + $bundle_uri = $this->getTypeUri($entity_type_id, $bundle); $data[$bundle_uri] = array( 'entity_type' => $entity_type_id, 'bundle' => $bundle, @@ -129,5 +112,4 @@ protected function writeCache($context = array()) { // and only clear it when entity_info is cleared. $this->cache->set('rest:links:types', $data, Cache::PERMANENT, array('entity_types')); } - } diff --git a/core/modules/rest/src/LinkManager/TypeLinkManagerInterface.php b/core/modules/rest/src/LinkManager/TypeLinkManagerInterface.php index 3459d06..9c2d942 100644 --- a/core/modules/rest/src/LinkManager/TypeLinkManagerInterface.php +++ b/core/modules/rest/src/LinkManager/TypeLinkManagerInterface.php @@ -7,7 +7,7 @@ namespace Drupal\rest\LinkManager; -interface TypeLinkManagerInterface extends ConfigurableLinkManagerInterface { +interface TypeLinkManagerInterface { /** * Gets the URI that corresponds to a bundle. @@ -20,25 +20,21 @@ * The bundle's entity type. * @param $bundle * The bundle name. - * @param array $context - * (optional) Optional serializer/normalizer context. * * @return string * The corresponding URI for the bundle. */ - public function getTypeUri($entity_type, $bundle, $context = array()); + public function getTypeUri($entity_type, $bundle); /** * Get a bundle's Typed Data IDs based on a URI. * * @param string $type_uri * The type URI. - * @param array $context - * Context from the normalizer/serializer operation. * * @return array | boolean * If the URI matches a bundle, returns an array containing entity_type and * bundle. Otherwise, returns false. */ - public function getTypeInternalIds($type_uri, $context = array()); + public function getTypeInternalIds($type_uri); } diff --git a/core/modules/rest/src/Plugin/views/display/RestExport.php b/core/modules/rest/src/Plugin/views/display/RestExport.php index 8af1a75..0cd43e9 100644 --- a/core/modules/rest/src/Plugin/views/display/RestExport.php +++ b/core/modules/rest/src/Plugin/views/display/RestExport.php @@ -278,7 +278,7 @@ public function execute() { $header = []; $header['Content-type'] = $this->getMimeType(); - $response = new CacheableResponse($this->renderer->renderRoot($output), 200); + $response = new CacheableResponse($output['#markup'], 200); $cache_metadata = CacheableMetadata::createFromRenderArray($output); $response->addCacheableDependency($cache_metadata); diff --git a/core/modules/rest/src/Plugin/views/style/Serializer.php b/core/modules/rest/src/Plugin/views/style/Serializer.php index 09e94e6..6078c9f 100644 --- a/core/modules/rest/src/Plugin/views/style/Serializer.php +++ b/core/modules/rest/src/Plugin/views/style/Serializer.php @@ -7,7 +7,9 @@ namespace Drupal\rest\Plugin\views\style; +use Drupal\Component\Utility\SafeMarkup; use Drupal\Core\Form\FormStateInterface; +use Drupal\rest\Plugin\views\row\DataFieldRow; use Drupal\views\ViewExecutable; use Drupal\views\Plugin\views\display\DisplayPluginBase; use Drupal\views\Plugin\views\style\StylePluginBase; diff --git a/core/modules/rest/src/Tests/RestLinkManagerTest.php b/core/modules/rest/src/Tests/RestLinkManagerTest.php deleted file mode 100644 index 4adee7c..0000000 --- a/core/modules/rest/src/Tests/RestLinkManagerTest.php +++ /dev/null @@ -1,90 +0,0 @@ -installSchema('system', ['router']); - \Drupal::service('router.builder')->rebuild(); - } - - /** - * Tests that type hooks work as expected. - */ - public function testRestLinkManagers() { - \Drupal::moduleHandler()->invoke('rest', 'install'); - /* @var \Drupal\rest\LinkManager\TypeLinkManagerInterface $type_manager */ - $type_manager = \Drupal::service('rest.link_manager.type'); - $base = Url::fromRoute('', [], ['absolute' => TRUE])->toString(); - $link = $type_manager->getTypeUri('node', 'page'); - $this->assertEqual($link, $base . 'rest/type/node/page'); - // Now with optional context. - $link = $type_manager->getTypeUri('node', 'page', ['rest_test' => TRUE]); - $this->assertEqual($link, 'rest_test_type'); - - /* @var \Drupal\rest\LinkManager\RelationLinkManagerInterface $relation_manager */ - $relation_manager = \Drupal::service('rest.link_manager.relation'); - $link = $relation_manager->getRelationUri('node', 'page', 'field_ref'); - $this->assertEqual($link, $base . 'rest/relation/node/page/field_ref'); - // Now with optional context. - $link = $relation_manager->getRelationUri('node', 'page', 'foobar', ['rest_test' => TRUE]); - $this->assertEqual($link, 'rest_test_relation'); - } - - /** - * Tests that type hooks work as expected even without install hook. - */ - public function testRestLinkManagersNoInstallHook() { - /* @var \Drupal\rest\LinkManager\TypeLinkManagerInterface $type_manager */ - $type_manager = \Drupal::service('rest.link_manager.type'); - $base = Url::fromRoute('', [], ['absolute' => TRUE])->toString(); - $link = $type_manager->getTypeUri('node', 'page'); - $this->assertEqual($link, $base . 'rest/type/node/page'); - // Now with optional context. - $link = $type_manager->getTypeUri('node', 'page', ['rest_test' => TRUE]); - $this->assertEqual($link, 'rest_test_type'); - - /* @var \Drupal\rest\LinkManager\RelationLinkManagerInterface $relation_manager */ - $relation_manager = \Drupal::service('rest.link_manager.relation'); - $link = $relation_manager->getRelationUri('node', 'page', 'field_ref'); - $this->assertEqual($link, $base . 'rest/relation/node/page/field_ref'); - // Now with optional context. - $link = $relation_manager->getRelationUri('node', 'page', 'foobar', ['rest_test' => TRUE]); - $this->assertEqual($link, 'rest_test_relation'); - } - - /** - * Tests \Drupal\rest\LinkManager\LinkManager::setLinkDomain(). - */ - public function testRestLinkManagersSetLinkDomain() { - /* @var \Drupal\rest\LinkManager\LinkManager $link_manager */ - $link_manager = \Drupal::service('rest.link_manager'); - $link_manager->setLinkDomain('http://example.com/'); - $link = $link_manager->getTypeUri('node', 'page'); - $this->assertEqual($link, 'http://example.com/rest/type/node/page'); - $link = $link_manager->getRelationUri('node', 'page', 'field_ref'); - $this->assertEqual($link, 'http://example.com/rest/relation/node/page/field_ref'); - } - -} diff --git a/core/modules/rest/src/Tests/Views/StyleSerializerTest.php b/core/modules/rest/src/Tests/Views/StyleSerializerTest.php index 137e8f0..e16556c 100644 --- a/core/modules/rest/src/Tests/Views/StyleSerializerTest.php +++ b/core/modules/rest/src/Tests/Views/StyleSerializerTest.php @@ -335,6 +335,16 @@ public function testFieldapiField() { $result = $this->drupalGetJSON('test/serialize/node-field'); $this->assertEqual($result[0]['nid'], $node->id()); $this->assertEqual($result[0]['body'], $node->body->processed); - } + // Make sure that serialized fields are not exposed to XSS. + $node = $this->drupalCreateNode(); + $node->body = array( + 'value' => '' . $this->randomMachineName(32), + 'format' => filter_default_format(), + ); + $node->save(); + $result = $this->drupalGetJSON('test/serialize/node-field'); + $this->assertEqual($result[1]['nid'], $node->id()); + $this->assertTrue(strpos($this->getRawContent(), "installSchema('system', 'router'); \Drupal::service('router.builder')->rebuild(); // Create the test field storage. diff --git a/core/modules/serialization/src/Tests/NormalizerTestBase.php b/core/modules/serialization/src/Tests/NormalizerTestBase.php index 8ac4485..3078f84 100644 --- a/core/modules/serialization/src/Tests/NormalizerTestBase.php +++ b/core/modules/serialization/src/Tests/NormalizerTestBase.php @@ -23,10 +23,8 @@ protected function setUp() { $this->installEntitySchema('entity_test_mulrev'); $this->installEntitySchema('user'); - $this->installSchema('system', array('url_alias', 'router')); + $this->installSchema('system', array('url_alias')); $this->installConfig(array('field')); - \Drupal::service('router.builder')->rebuild(); - \Drupal::moduleHandler()->invoke('rest', 'install'); // Auto-create a field for testing. entity_create('field_storage_config', array( diff --git a/core/modules/system/src/Tests/Entity/EntityValidationTest.php b/core/modules/system/src/Tests/Entity/EntityValidationTest.php index d4358fc..07823fb 100644 --- a/core/modules/system/src/Tests/Entity/EntityValidationTest.php +++ b/core/modules/system/src/Tests/Entity/EntityValidationTest.php @@ -7,8 +7,6 @@ namespace Drupal\system\Tests\Entity; -use Drupal\Core\Entity\Plugin\Validation\Constraint\CompositeConstraintBase; - /** * Tests the Entity Validation API. * @@ -63,6 +61,7 @@ protected function setUp() { protected function createTestEntity($entity_type) { $this->entityName = $this->randomMachineName(); $this->entityUser = $this->createUser(); + $this->entityFieldText = $this->randomMachineName(); // Pass in the value of the name field when creating. With the user // field we test setting a field after creation. @@ -71,10 +70,7 @@ protected function createTestEntity($entity_type) { $entity->name->value = $this->entityName; // Set a value for the test field. - if ($entity->hasField('field_test_text')) { - $this->entityFieldText = $this->randomMachineName(); - $entity->field_test_text->value = $this->entityFieldText; - } + $entity->field_test_text->value = $this->entityFieldText; return $entity; } @@ -172,27 +168,4 @@ protected function checkValidation($entity_type) { $this->assertEqual($violation->getInvalidValue(), $test_entity->field_test_text->format, 'Violation contains invalid value.'); } - /** - * Tests composite constraints. - */ - public function testCompositeConstraintValidation() { - $entity = $this->createTestEntity('entity_test_composite_constraint'); - $violations = $entity->validate(); - $this->assertEqual($violations->count(), 0); - - // Trigger violation condition. - $entity->name->value = 'test'; - $entity->type->value = 'test2'; - $violations = $entity->validate(); - $this->assertEqual($violations->count(), 1); - - // Make sure we can determine this is composite constraint. - $constraint = $violations[0]->getConstraint(); - $this->assertTrue($constraint instanceof CompositeConstraintBase, 'Constraint is composite constraint.'); - $this->assertEqual('type', $violations[0]->getPropertyPath()); - - /** @var CompositeConstraintBase $constraint */ - $this->assertEqual($constraint->coversFields(), ['name', 'type'], 'Information about covered fields can be retrieved.'); - } - } diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestCompositeConstraint.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestCompositeConstraint.php deleted file mode 100644 index c387b93..0000000 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestCompositeConstraint.php +++ /dev/null @@ -1,31 +0,0 @@ -name->value === 'test' && $entity->type->value === 'test2') { - $this->context->buildViolation($constraint->message) - ->atPath('type') - ->addViolation(); - } - - } - -} diff --git a/core/modules/toolbar/src/Element/ToolbarItem.php b/core/modules/toolbar/src/Element/ToolbarItem.php index b772193..929dac4 100644 --- a/core/modules/toolbar/src/Element/ToolbarItem.php +++ b/core/modules/toolbar/src/Element/ToolbarItem.php @@ -61,7 +61,7 @@ public static function preRenderToolbarItem($element) { // Provide attributes necessary for trays. $attributes += array( 'data-toolbar-tray' => $id . '-tray', - 'aria-owns' => $id . '-tray', + 'aria-owns' => $id, 'role' => 'button', 'aria-pressed' => 'false', ); @@ -75,6 +75,7 @@ public static function preRenderToolbarItem($element) { $attributes = array( 'id' => $id . '-tray', 'data-toolbar-tray' => $id . '-tray', + 'aria-owned-by' => $id, ); // Merge in module-provided attributes. if (!isset($element['tray']['#wrapper_attributes'])) { diff --git a/core/modules/views/src/Tests/Handler/AreaTest.php b/core/modules/views/src/Tests/Handler/AreaTest.php index 03b8b57..8bcb173 100644 --- a/core/modules/views/src/Tests/Handler/AreaTest.php +++ b/core/modules/views/src/Tests/Handler/AreaTest.php @@ -7,6 +7,7 @@ namespace Drupal\views\Tests\Handler; +use Drupal\Component\Utility\SafeMarkup; use Drupal\views\Views; /** @@ -107,9 +108,9 @@ public function testRenderArea() { // Check whether the strings exists in the output. $output = $view->preview(); $output = drupal_render($output); - $this->assertTrue(strpos($output, $header_string) !== FALSE); - $this->assertTrue(strpos($output, $footer_string) !== FALSE); - $this->assertTrue(strpos($output, $empty_string) !== FALSE); + $this->assertTrue(strpos($output, SafeMarkup::checkPlain($header_string)) !== FALSE); + $this->assertTrue(strpos($output, SafeMarkup::checkPlain($footer_string)) !== FALSE); + $this->assertTrue(strpos($output, SafeMarkup::checkPlain($empty_string)) !== FALSE); } /** diff --git a/core/modules/views/views.module b/core/modules/views/views.module index 41ff298..bae7e06 100644 --- a/core/modules/views/views.module +++ b/core/modules/views/views.module @@ -679,7 +679,7 @@ function views_pre_render_views_form_views_form($element) { } // Apply substitutions to the rendered output. - $element['output'] = array('#markup' => str_replace($search, $replace, drupal_render($element['output']))); + $element['output'] = array('#markup' => SafeMarkup::replace($search, $replace, drupal_render($element['output']))); // Sort, render and add remaining form fields. $children = Element::children($element, TRUE); diff --git a/core/modules/views_ui/views_ui.theme.inc b/core/modules/views_ui/views_ui.theme.inc index f2f2b67..4aafde4 100644 --- a/core/modules/views_ui/views_ui.theme.inc +++ b/core/modules/views_ui/views_ui.theme.inc @@ -147,21 +147,35 @@ function theme_views_ui_build_group_filter_form($variables) { foreach (Element::children($form['group_items']) as $group_id) { $form['group_items'][$group_id]['value']['#title'] = ''; + $default = array( + $form['default_group'][$group_id], + $form['default_group_multiple'][$group_id], + ); + $link = [ + '#type' => 'link', + '#url' => Url::fromRoute('', [], [ + 'attributes' => [ + 'id' => 'views-remove-link-' . $group_id, + 'class' => [ + 'views-hidden', + 'views-button-remove', + 'views-groups-remove-link', + 'views-remove-link', + ], + 'alt' => t('Remove this item'), + 'title' => t('Remove this item'), + ], + ]), + '#title' => SafeMarkup::format('@text', ['@text' => t('Remove')]), + ]; + $remove = [$form['group_items'][$group_id]['remove'], $link]; $data = array( - 'default' => array( - 'data' => array( - '#markup' => drupal_render($form['default_group'][$group_id]) . drupal_render($form['default_group_multiple'][$group_id]), - ), - ), + 'default' => ['data' => $default], 'weight' => drupal_render($form['group_items'][$group_id]['weight']), 'title' => drupal_render($form['group_items'][$group_id]['title']), 'operator' => drupal_render($form['group_items'][$group_id]['operator']), 'value' => drupal_render($form['group_items'][$group_id]['value']), - 'remove' => array( - 'data' => array( - '#markup' => drupal_render($form['group_items'][$group_id]['remove']) . \Drupal::l(SafeMarkup::format('@text', array('@text' => t('Remove'))), Url::fromRoute('', [], array('attributes' => array('id' => 'views-remove-link-' . $group_id, 'class' => array('views-hidden', 'views-button-remove', 'views-groups-remove-link', 'views-remove-link'), 'alt' => t('Remove this item'), 'title' => t('Remove this item'))))), - ), - ), + 'remove' => ['data' => $remove], ); $rows[] = array('data' => $data, 'id' => 'views-row-' . $group_id, 'class' => array('draggable')); } diff --git a/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php b/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php index 1e7d355..acba34a 100644 --- a/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php +++ b/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php @@ -188,4 +188,73 @@ function testPlaceholder() { $this->assertEquals('Some text', SafeMarkup::placeholder('Some text')); } + /** + * Tests SafeMarkup::replace(). + * + * @dataProvider providerReplace + * @covers ::replace + */ + public function testReplaces($search, $replace, $subject, $expected, $is_safe) { + $result = SafeMarkup::replace($search, $replace, $subject); + $this->assertEquals($expected, $result); + $this->assertEquals($is_safe, SafeMarkup::isSafe($result)); + } + + /** + * Data provider for testReplace(). + * + * @see testReplace() + */ + public function providerReplace() { + $tests = []; + + // Subject unsafe. + $tests[] = [ + '', + SafeMarkup::set('foo'), + 'bazqux', + 'foobazqux', + FALSE, + ]; + + // All safe. + $tests[] = [ + '', + SafeMarkup::set('foo'), + SafeMarkup::set('barbaz'), + 'foobarbaz', + TRUE, + ]; + + // Safe subject, but should result in unsafe string because replacement is + // unsafe. + $tests[] = [ + '', + 'fubar', + SafeMarkup::set('barbaz'), + 'fubarbarbaz', + FALSE, + ]; + + // Array with all safe. + $tests[] = [ + ['', '', ''], + [SafeMarkup::set('foo'), SafeMarkup::set('bar'), SafeMarkup::set('baz')], + SafeMarkup::set(''), + 'foobarbaz', + TRUE, + ]; + + // Array with unsafe replacement. + $tests[] = [ + ['', '', '',], + [SafeMarkup::set('bar'), SafeMarkup::set('baz'), 'qux'], + SafeMarkup::set(''), + 'barbazqux', + FALSE, + ]; + + return $tests; + } + } diff --git a/core/tests/Drupal/Tests/Core/Render/RendererPostRenderCacheTest.php b/core/tests/Drupal/Tests/Core/Render/RendererPostRenderCacheTest.php index 50ccc2e..d96e4fd 100644 --- a/core/tests/Drupal/Tests/Core/Render/RendererPostRenderCacheTest.php +++ b/core/tests/Drupal/Tests/Core/Render/RendererPostRenderCacheTest.php @@ -432,7 +432,7 @@ public function testPlaceholder() { '#prefix' => '
',
       '#suffix' => '
', ]; - $expected_output = '
' . $context['bar'] . '
'; + $expected_output = '
' . $context['bar'] . '
'; // #cache disabled. $element = $test_element; @@ -530,7 +530,7 @@ public function testChildElementPlaceholder() { '#suffix' => '' ], ]; - $expected_output = '
' . $context['bar'] . '
' . "\n"; + $expected_output = '
' . $context['bar'] . '
' . "\n"; // #cache disabled. $element = $test_element; diff --git a/core/tests/Drupal/Tests/Core/Render/RendererTest.php b/core/tests/Drupal/Tests/Core/Render/RendererTest.php index e8368b2..addfa4b 100644 --- a/core/tests/Drupal/Tests/Core/Render/RendererTest.php +++ b/core/tests/Drupal/Tests/Core/Render/RendererTest.php @@ -81,6 +81,10 @@ public function providerTestRenderBasic() { $data[] = [[ 'child' => ['#markup' => 'bar'], ], 'bar']; + // XSS filtering test. + $data[] = [[ + 'child' => ['#markup' => "This is test"], + ], "This is alert('XSS') test"]; // #children set but empty, and renderable children. $data[] = [[ '#children' => '', diff --git a/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php b/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php index 903adce..fb0aeb9 100644 --- a/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php +++ b/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php @@ -255,7 +255,7 @@ public static function callback(array $element, array $context) { public static function placeholder(array $element, array $context) { $placeholder = \Drupal::service('renderer')->generateCachePlaceholder(__NAMESPACE__ . '\\PostRenderCache::placeholder', $context); $replace_element = array( - '#markup' => '' . $context['bar'] . '', + '#markup' => '' . $context['bar'] . '', '#attached' => array( 'drupalSettings' => [ 'common_test' => $context,