Exemplo n.º 1
1
  /**
   * Tests that #cache_properties are properly handled.
   *
   * @param array $expected_results
   *   An associative array of expected results keyed by property name.
   *
   * @covers ::render
   * @covers ::doRender
   * @covers \Drupal\Core\Render\RenderCache::get
   * @covers \Drupal\Core\Render\RenderCache::set
   * @covers \Drupal\Core\Render\RenderCache::createCacheID
   * @covers \Drupal\Core\Render\RenderCache::getCacheableRenderArray
   *
   * @dataProvider providerTestRenderCacheProperties
   */
  public function testRenderCacheProperties(array $expected_results) {
    $this->setUpRequest();
    $this->setupMemoryCache();

    $element = $original = [
      '#cache' => [
        'keys' => ['render_cache_test'],
      ],
      // Collect expected property names.
      '#cache_properties' => array_keys(array_filter($expected_results)),
      'child1' => ['#markup' => Markup::create('1')],
      'child2' => ['#markup' => Markup::create('2')],
      // Mark the value as safe.
      '#custom_property' => Markup::create('custom_value'),
      '#custom_property_array' => ['custom value'],
    ];

    $this->renderer->renderRoot($element);

    $cache = $this->cacheFactory->get('render');
    $data = $cache->get('render_cache_test:en:stark')->data;

    // Check that parent markup is ignored when caching children's markup.
    $this->assertEquals($data['#markup'] === '', (bool) Element::children($data));

    // Check that the element properties are cached as specified.
    foreach ($expected_results as $property => $expected) {
      $cached = !empty($data[$property]);
      $this->assertEquals($cached, (bool) $expected);
      // Check that only the #markup key is preserved for children.
      if ($cached) {
        $this->assertEquals($data[$property], $original[$property]);
      }
    }
    // #custom_property_array can not be a safe_cache_property.
    $safe_cache_properties = array_diff(Element::properties(array_filter($expected_results)), ['#custom_property_array']);
    foreach ($safe_cache_properties as $cache_property) {
      $this->assertTrue(SafeMarkup::isSafe($data[$cache_property]), "$cache_property is marked as a safe string");
    }
  }
Exemplo n.º 2
0
 /**
  * Tests the rediscovering.
  */
 public function testRediscover()
 {
     \Drupal::state()->set('menu_link_content_dynamic_route.routes', ['route_name_1' => new Route('/example-path')]);
     \Drupal::service('router.builder')->rebuild();
     // Set up a custom menu link pointing to a specific path.
     MenuLinkContent::create(['title' => '<script>alert("Welcome to the discovered jungle!")</script>', 'link' => [['uri' => 'internal:/example-path']], 'menu_name' => 'tools'])->save();
     $menu_tree = \Drupal::menuTree()->load('tools', new MenuTreeParameters());
     $this->assertEqual(1, count($menu_tree));
     /** @var \Drupal\Core\Menu\MenuLinkTreeElement $tree_element */
     $tree_element = reset($menu_tree);
     $this->assertEqual('route_name_1', $tree_element->link->getRouteName());
     // Change the underlying route and trigger the rediscovering.
     \Drupal::state()->set('menu_link_content_dynamic_route.routes', ['route_name_2' => new Route('/example-path')]);
     \Drupal::service('router.builder')->rebuild();
     // Ensure that the new route name / parameters are captured by the tree.
     $menu_tree = \Drupal::menuTree()->load('tools', new MenuTreeParameters());
     $this->assertEqual(1, count($menu_tree));
     /** @var \Drupal\Core\Menu\MenuLinkTreeElement $tree_element */
     $tree_element = reset($menu_tree);
     $this->assertEqual('route_name_2', $tree_element->link->getRouteName());
     $title = $tree_element->link->getTitle();
     $this->assertFalse($title instanceof TranslationWrapper);
     $this->assertIdentical('<script>alert("Welcome to the discovered jungle!")</script>', $title);
     $this->assertFalse(SafeMarkup::isSafe($title));
 }
Exemplo n.º 3
0
 /**
  * Tests the listing of displays on a views list builder.
  *
  * @see \Drupal\views_ui\ViewListBuilder::getDisplaysList()
  * @covers ::buildRow
  */
 public function testBuildRowEntityList()
 {
     $storage = $this->getMockBuilder('Drupal\\Core\\Config\\Entity\\ConfigEntityStorage')->disableOriginalConstructor()->getMock();
     $display_manager = $this->getMockBuilder('\\Drupal\\views\\Plugin\\ViewsPluginManager')->disableOriginalConstructor()->getMock();
     $display_manager->expects($this->any())->method('getDefinition')->will($this->returnValueMap(array(array('default', TRUE, array('id' => 'default', 'title' => 'Master', 'theme' => 'views_view', 'no_ui' => TRUE, 'admin' => '')), array('page', TRUE, array('id' => 'page', 'title' => 'Page', 'uses_menu_links' => TRUE, 'uses_route' => TRUE, 'contextual_links_locations' => array('page'), 'theme' => 'views_view', 'admin' => 'Page admin label')), array('embed', TRUE, array('id' => 'embed', 'title' => 'embed', 'theme' => 'views_view', 'admin' => 'Embed admin label')))));
     $default_display = $this->getMock('Drupal\\views\\Plugin\\views\\display\\DefaultDisplay', array('initDisplay'), array(array(), 'default', $display_manager->getDefinition('default')));
     $route_provider = $this->getMock('Drupal\\Core\\Routing\\RouteProviderInterface');
     $state = $this->getMock('\\Drupal\\Core\\State\\StateInterface');
     $menu_storage = $this->getMock('\\Drupal\\Core\\Entity\\EntityStorageInterface');
     $page_display = $this->getMock('Drupal\\views\\Plugin\\views\\display\\Page', array('initDisplay', 'getPath'), array(array(), 'default', $display_manager->getDefinition('page'), $route_provider, $state, $menu_storage));
     $page_display->expects($this->any())->method('getPath')->will($this->onConsecutiveCalls($this->returnValue('test_page'), $this->returnValue('<object>malformed_path</object>'), $this->returnValue('<script>alert("placeholder_page/%")</script>')));
     $embed_display = $this->getMock('Drupal\\views\\Plugin\\views\\display\\Embed', array('initDisplay'), array(array(), 'default', $display_manager->getDefinition('embed')));
     $values = array();
     $values['status'] = FALSE;
     $values['display']['default']['id'] = 'default';
     $values['display']['default']['display_title'] = 'Display';
     $values['display']['default']['display_plugin'] = 'default';
     $values['display']['page_1']['id'] = 'page_1';
     $values['display']['page_1']['display_title'] = 'Page 1';
     $values['display']['page_1']['display_plugin'] = 'page';
     $values['display']['page_1']['display_options']['path'] = 'test_page';
     $values['display']['page_2']['id'] = 'page_2';
     $values['display']['page_2']['display_title'] = 'Page 2';
     $values['display']['page_2']['display_plugin'] = 'page';
     $values['display']['page_2']['display_options']['path'] = '<object>malformed_path</object>';
     $values['display']['page_3']['id'] = 'page_3';
     $values['display']['page_3']['display_title'] = 'Page 3';
     $values['display']['page_3']['display_plugin'] = 'page';
     $values['display']['page_3']['display_options']['path'] = '<script>alert("placeholder_page/%")</script>';
     $values['display']['embed']['id'] = 'embed';
     $values['display']['embed']['display_title'] = 'Embedded';
     $values['display']['embed']['display_plugin'] = 'embed';
     $display_manager->expects($this->any())->method('createInstance')->will($this->returnValueMap(array(array('default', $values['display']['default'], $default_display), array('page', $values['display']['page_1'], $page_display), array('page', $values['display']['page_2'], $page_display), array('page', $values['display']['page_3'], $page_display), array('embed', $values['display']['embed'], $embed_display))));
     $container = new ContainerBuilder();
     $user = $this->getMock('Drupal\\Core\\Session\\AccountInterface');
     $request_stack = new RequestStack();
     $request_stack->push(new Request());
     $views_data = $this->getMockBuilder('Drupal\\views\\ViewsData')->disableOriginalConstructor()->getMock();
     $route_provider = $this->getMock('Drupal\\Core\\Routing\\RouteProviderInterface');
     $executable_factory = new ViewExecutableFactory($user, $request_stack, $views_data, $route_provider);
     $container->set('views.executable', $executable_factory);
     $container->set('plugin.manager.views.display', $display_manager);
     \Drupal::setContainer($container);
     // Setup a view list builder with a mocked buildOperations method,
     // because t() is called on there.
     $entity_type = $this->getMock('Drupal\\Core\\Entity\\EntityTypeInterface');
     $view_list_builder = new TestViewListBuilder($entity_type, $storage, $display_manager);
     $view_list_builder->setStringTranslation($this->getStringTranslationStub());
     $view = new View($values, 'view');
     $row = $view_list_builder->buildRow($view);
     $expected_displays = array('Embed admin label', 'Page admin label', 'Page admin label', 'Page admin label');
     $this->assertEquals($expected_displays, $row['data']['view_name']['data']['#displays']);
     $display_paths = $row['data']['path']['data']['#items'];
     // These values will be escaped by Twig when rendered.
     $this->assertEquals('/test_page, /<object>malformed_path</object>, /<script>alert("placeholder_page/%")</script>', implode(', ', $display_paths));
     $this->assertFalse(SafeMarkup::isSafe('/<object>malformed_path</object>'), '/<script>alert("/<object>malformed_path</object> is not marked safe.');
     $this->assertFalse(SafeMarkup::isSafe('/<script>alert("placeholder_page/%")'), '/<script>alert("/<script>alert("placeholder_page/%") is not marked safe.');
 }
 /**
  * @dataProvider providerTestFormatPlural
  */
 public function testFormatPlural($count, $singular, $plural, array $args = array(), array $options = array(), $expected)
 {
     $translator = $this->getMock('\\Drupal\\Core\\StringTranslation\\Translator\\TranslatorInterface');
     $translator->expects($this->once())->method('getStringTranslation')->will($this->returnCallback(function ($langcode, $string) {
         return $string;
     }));
     $this->translationManager->addTranslator($translator);
     $result = $this->translationManager->formatPlural($count, $singular, $plural, $args, $options);
     $this->assertEquals($expected, $result);
     $this->assertTrue(SafeMarkup::isSafe($result));
 }
Exemplo n.º 5
0
 /**
  * Tests the rediscovering.
  */
 public function testRediscover()
 {
     \Drupal::state()->set('menu_link_content_dynamic_route.routes', ['route_name_1' => new Route('/example-path')]);
     \Drupal::service('router.builder')->rebuild();
     // Set up a custom menu link pointing to a specific path.
     $parent = MenuLinkContent::create(['title' => '<script>alert("Welcome to the discovered jungle!")</script>', 'link' => [['uri' => 'internal:/example-path']], 'menu_name' => 'tools']);
     $parent->save();
     $menu_tree = \Drupal::menuTree()->load('tools', new MenuTreeParameters());
     $this->assertEqual(1, count($menu_tree));
     /** @var \Drupal\Core\Menu\MenuLinkTreeElement $tree_element */
     $tree_element = reset($menu_tree);
     $this->assertEqual('route_name_1', $tree_element->link->getRouteName());
     // Change the underlying route and trigger the rediscovering.
     \Drupal::state()->set('menu_link_content_dynamic_route.routes', ['route_name_2' => new Route('/example-path')]);
     \Drupal::service('router.builder')->rebuild();
     // Ensure that the new route name / parameters are captured by the tree.
     $menu_tree = \Drupal::menuTree()->load('tools', new MenuTreeParameters());
     $this->assertEqual(1, count($menu_tree));
     /** @var \Drupal\Core\Menu\MenuLinkTreeElement $tree_element */
     $tree_element = reset($menu_tree);
     $this->assertEqual('route_name_2', $tree_element->link->getRouteName());
     $title = $tree_element->link->getTitle();
     $this->assertFalse($title instanceof TranslatableMarkup);
     $this->assertIdentical('<script>alert("Welcome to the discovered jungle!")</script>', $title);
     $this->assertFalse(SafeMarkup::isSafe($title));
     // Create a hierarchy.
     \Drupal::state()->set('menu_link_content_dynamic_route.routes', ['route_name_1' => new Route('/example-path'), 'route_name_2' => new Route('/example-path/child')]);
     $child = MenuLinkContent::create(['title' => 'Child', 'link' => [['uri' => 'entity:/example-path/child']], 'menu_name' => 'tools', 'parent' => 'menu_link_content:' . $parent->uuid()]);
     $child->save();
     $parent->set('link', [['uri' => 'entity:/example-path']]);
     $parent->save();
     $menu_tree = \Drupal::menuTree()->load('tools', new MenuTreeParameters());
     $this->assertEqual(1, count($menu_tree));
     /** @var \Drupal\Core\Menu\MenuLinkTreeElement $tree_element */
     $tree_element = reset($menu_tree);
     $this->assertTrue($tree_element->hasChildren);
     $this->assertEqual(1, count($tree_element->subtree));
     // Edit child element link to use 'internal' instead of 'entity'.
     $child->set('link', [['uri' => 'internal:/example-path/child']]);
     $child->save();
     \Drupal::service('plugin.manager.menu.link')->rebuild();
     $menu_tree = \Drupal::menuTree()->load('tools', new MenuTreeParameters());
     $this->assertEqual(1, count($menu_tree));
     /** @var \Drupal\Core\Menu\MenuLinkTreeElement $tree_element */
     $tree_element = reset($menu_tree);
     $this->assertTrue($tree_element->hasChildren);
     $this->assertEqual(1, count($tree_element->subtree));
 }
Exemplo n.º 6
0
 /**
  * Tests comment preview.
  */
 function testCommentPreview()
 {
     // As admin user, configure comment settings.
     $this->drupalLogin($this->adminUser);
     $this->setCommentPreview(DRUPAL_OPTIONAL);
     $this->setCommentForm(TRUE);
     $this->setCommentSubject(TRUE);
     $this->setCommentSettings('default_mode', CommentManagerInterface::COMMENT_MODE_THREADED, 'Comment paging changed.');
     $this->drupalLogout();
     // Login as web user.
     $this->drupalLogin($this->webUser);
     // Test escaping of the username on the preview form.
     \Drupal::service('module_installer')->install(['user_hooks_test']);
     \Drupal::state()->set('user_hooks_test_user_format_name_alter', TRUE);
     $edit = array();
     $edit['subject[0][value]'] = $this->randomMachineName(8);
     $edit['comment_body[0][value]'] = $this->randomMachineName(16);
     $this->drupalPostForm('node/' . $this->node->id(), $edit, t('Preview'));
     $this->assertEscaped('<em>' . $this->webUser->id() . '</em>');
     \Drupal::state()->set('user_hooks_test_user_format_name_alter_safe', TRUE);
     $this->drupalPostForm('node/' . $this->node->id(), $edit, t('Preview'));
     $this->assertTrue(SafeMarkup::isSafe($this->webUser->getDisplayName()), 'Username is marked safe');
     $this->assertNoEscaped('<em>' . $this->webUser->id() . '</em>');
     $this->assertRaw('<em>' . $this->webUser->id() . '</em>');
     // Add a user picture.
     $image = current($this->drupalGetTestFiles('image'));
     $user_edit['files[user_picture_0]'] = drupal_realpath($image->uri);
     $this->drupalPostForm('user/' . $this->webUser->id() . '/edit', $user_edit, t('Save'));
     // As the web user, fill in the comment form and preview the comment.
     $this->drupalPostForm('node/' . $this->node->id(), $edit, t('Preview'));
     // Check that the preview is displaying the title and body.
     $this->assertTitle(t('Preview comment | Drupal'), 'Page title is "Preview comment".');
     $this->assertText($edit['subject[0][value]'], 'Subject displayed.');
     $this->assertText($edit['comment_body[0][value]'], 'Comment displayed.');
     // Check that the title and body fields are displayed with the correct values.
     $this->assertFieldByName('subject[0][value]', $edit['subject[0][value]'], 'Subject field displayed.');
     $this->assertFieldByName('comment_body[0][value]', $edit['comment_body[0][value]'], 'Comment field displayed.');
     // Check that the user picture is displayed.
     $this->assertFieldByXPath("//article[contains(@class, 'preview')]//div[contains(@class, 'user-picture')]//img", NULL, 'User picture displayed.');
 }
Exemplo n.º 7
0
 /**
  * Tests how hook_link_alter() can affect escaping of the link text.
  */
 function testHookLinkAlter()
 {
     $url = Url::fromUri('http://example.com');
     $renderer = \Drupal::service('renderer');
     $link = $renderer->executeInRenderContext(new RenderContext(), function () use($url) {
         return \Drupal::l(['#markup' => '<em>link with markup</em>'], $url);
     });
     $this->setRawContent($link);
     $this->assertTrue(SafeMarkup::isSafe($link), 'The output of link generation is marked safe as it is a link.');
     // Ensure the content of the link is not escaped.
     $this->assertRaw('<em>link with markup</em>');
     // Test just adding text to an already safe string.
     \Drupal::state()->set('link_generation_test_link_alter', TRUE);
     $link = $renderer->executeInRenderContext(new RenderContext(), function () use($url) {
         return \Drupal::l(['#markup' => '<em>link with markup</em>'], $url);
     });
     $this->setRawContent($link);
     $this->assertTrue(SafeMarkup::isSafe($link), 'The output of link generation is marked safe as it is a link.');
     // Ensure the content of the link is escaped.
     $this->assertEscaped('<em>link with markup</em> <strong>Test!</strong>');
     // Test passing a safe string to t().
     \Drupal::state()->set('link_generation_test_link_alter_safe', TRUE);
     $link = $renderer->executeInRenderContext(new RenderContext(), function () use($url) {
         return \Drupal::l(['#markup' => '<em>link with markup</em>'], $url);
     });
     $this->setRawContent($link);
     $this->assertTrue(SafeMarkup::isSafe($link), 'The output of link generation is marked safe as it is a link.');
     // Ensure the content of the link is escaped.
     $this->assertRaw('<em>link with markup</em> <strong>Test!</strong>');
     // Test passing an unsafe string to t().
     $link = $renderer->executeInRenderContext(new RenderContext(), function () use($url) {
         return \Drupal::l('<em>link with markup</em>', $url);
     });
     $this->setRawContent($link);
     $this->assertTrue(SafeMarkup::isSafe($link), 'The output of link generation is marked safe as it is a link.');
     // Ensure the content of the link is escaped.
     $this->assertEscaped('<em>link with markup</em>');
     $this->assertRaw('<strong>Test!</strong>');
 }
Exemplo n.º 8
0
 /**
  * Creates the different types of attribute values.
  *
  * @param string $name
  *   The attribute name.
  * @param mixed $value
  *   The attribute value.
  *
  * @return \Drupal\Core\Template\AttributeValueBase
  *   An AttributeValueBase representation of the attribute's value.
  */
 protected function createAttributeValue($name, $value)
 {
     // If the value is already an AttributeValueBase object,
     // return a new instance of the same class, but with the new name.
     if ($value instanceof AttributeValueBase) {
         $class = get_class($value);
         return new $class($name, $value->value());
     }
     // An array value or 'class' attribute name are forced to always be an
     // AttributeArray value for consistency.
     if ($name == 'class' && !is_array($value)) {
         // Cast the value to string in case it implements MarkupInterface.
         $value = [(string) $value];
     }
     if (is_array($value)) {
         // Cast the value to an array if the value was passed in as a string.
         // @todo Decide to fix all the broken instances of class as a string
         // in core or cast them.
         $value = new AttributeArray($name, $value);
     } elseif (is_bool($value)) {
         $value = new AttributeBoolean($name, $value);
     } elseif (SafeMarkup::isSafe($value)) {
         // Attributes are not supposed to display HTML markup, so we just convert
         // the value to plain text.
         $value = PlainTextOutput::renderFromHtml($value);
         $value = new AttributeString($name, $value);
     } elseif (!is_object($value)) {
         $value = new AttributeString($name, $value);
     }
     return $value;
 }
Exemplo n.º 9
0
 /**
  * {@inheritdoc}
  */
 public function getCacheableRenderArray(array $elements)
 {
     $data = ['#markup' => $elements['#markup'], '#attached' => $elements['#attached'], '#cache' => ['contexts' => $elements['#cache']['contexts'], 'tags' => $elements['#cache']['tags'], 'max-age' => $elements['#cache']['max-age']]];
     // Preserve cacheable items if specified. If we are preserving any cacheable
     // children of the element, we assume we are only interested in their
     // individual markup and not the parent's one, thus we empty it to minimize
     // the cache entry size.
     if (!empty($elements['#cache_properties']) && is_array($elements['#cache_properties'])) {
         $data['#cache_properties'] = $elements['#cache_properties'];
         // Ensure that any safe strings are a Markup object.
         foreach (Element::properties(array_flip($elements['#cache_properties'])) as $cache_property) {
             if (isset($elements[$cache_property]) && is_scalar($elements[$cache_property]) && SafeMarkup::isSafe($elements[$cache_property])) {
                 $elements[$cache_property] = Markup::create($elements[$cache_property]);
             }
         }
         // Extract all the cacheable items from the element using cache
         // properties.
         $cacheable_items = array_intersect_key($elements, array_flip($elements['#cache_properties']));
         $cacheable_children = Element::children($cacheable_items);
         if ($cacheable_children) {
             $data['#markup'] = '';
             // Cache only cacheable children's markup.
             foreach ($cacheable_children as $key) {
                 // We can assume that #markup is safe at this point.
                 $cacheable_items[$key] = ['#markup' => Markup::create($cacheable_items[$key]['#markup'])];
             }
         }
         $data += $cacheable_items;
     }
     $data['#markup'] = Markup::create($data['#markup']);
     return $data;
 }
Exemplo n.º 10
0
  /**
   * Tests string formatting with SafeMarkup::format().
   *
   * @dataProvider providerFormat
   * @covers ::format
   *
   * @param string $string
   *   The string to run through SafeMarkup::format().
   * @param string[] $args
   *   The arguments to pass into SafeMarkup::format().
   * @param string $expected
   *   The expected result from calling the function.
   * @param string $message
   *   The message to display as output to the test.
   * @param bool $expected_is_safe
   *   Whether the result is expected to be safe for HTML display.
   */
  public function testFormat($string, array $args, $expected, $message, $expected_is_safe) {
    UrlHelper::setAllowedProtocols(['http', 'https', 'mailto']);

    $result = SafeMarkup::format($string, $args);
    $this->assertEquals($expected, $result, $message);
    $this->assertEquals($expected_is_safe, SafeMarkup::isSafe($result), 'SafeMarkup::format correctly sets the result as safe or not safe.');

    foreach ($args as $arg) {
      $this->assertSame($arg instanceof SafeMarkupTestMarkup, SafeMarkup::isSafe($arg));
    }
  }
Exemplo n.º 11
0
 /**
  * Pre-render callback: Renders #browsers into #prefix and #suffix.
  *
  * @param array $element
  *   A render array with a '#browsers' property. The '#browsers' property can
  *   contain any or all of the following keys:
  *   - 'IE': If FALSE, the element is not rendered by Internet Explorer. If
  *     TRUE, the element is rendered by Internet Explorer. Can also be a string
  *     containing an expression for Internet Explorer to evaluate as part of a
  *     conditional comment. For example, this can be set to 'lt IE 7' for the
  *     element to be rendered in Internet Explorer 6, but not in Internet
  *     Explorer 7 or higher. Defaults to TRUE.
  *   - '!IE': If FALSE, the element is not rendered by browsers other than
  *     Internet Explorer. If TRUE, the element is rendered by those browsers.
  *     Defaults to TRUE.
  *   Examples:
  *   - To render an element in all browsers, '#browsers' can be left out or set
  *     to array('IE' => TRUE, '!IE' => TRUE).
  *   - To render an element in Internet Explorer only, '#browsers' can be set
  *     to array('!IE' => FALSE).
  *   - To render an element in Internet Explorer 6 only, '#browsers' can be set
  *     to array('IE' => 'lt IE 7', '!IE' => FALSE).
  *   - To render an element in Internet Explorer 8 and higher and in all other
  *     browsers, '#browsers' can be set to array('IE' => 'gte IE 8').
  *
  * @return array
  *   The passed-in element with markup for conditional comments potentially
  *   added to '#prefix' and '#suffix'.
  */
 public static function preRenderConditionalComments($element)
 {
     $browsers = isset($element['#browsers']) ? $element['#browsers'] : array();
     $browsers += array('IE' => TRUE, '!IE' => TRUE);
     // If rendering in all browsers, no need for conditional comments.
     if ($browsers['IE'] === TRUE && $browsers['!IE']) {
         return $element;
     }
     // Determine the conditional comment expression for Internet Explorer to
     // evaluate.
     if ($browsers['IE'] === TRUE) {
         $expression = 'IE';
     } elseif ($browsers['IE'] === FALSE) {
         $expression = '!IE';
     } else {
         // The IE expression might contain some user input data.
         $expression = Xss::filterAdmin($browsers['IE']);
     }
     // If the #prefix and #suffix properties are used, wrap them with
     // conditional comment markup. The conditional comment expression is
     // evaluated by Internet Explorer only. To control the rendering by other
     // browsers, use either the "downlevel-hidden" or "downlevel-revealed"
     // technique. See http://en.wikipedia.org/wiki/Conditional_comment
     // for details.
     // Ensure what we are dealing with is safe.
     // This would be done later anyway in drupal_render().
     $prefix = isset($element['#prefix']) ? $element['#prefix'] : '';
     if ($prefix && !SafeMarkup::isSafe($prefix)) {
         $prefix = Xss::filterAdmin($prefix);
     }
     $suffix = isset($element['#suffix']) ? $element['#suffix'] : '';
     if ($suffix && !SafeMarkup::isSafe($suffix)) {
         $suffix = Xss::filterAdmin($suffix);
     }
     // We ensured above that $expression is either a string we created or is
     // admin XSS filtered, and that $prefix and $suffix are also admin XSS
     // filtered if they are unsafe. Thus, all these strings are safe.
     if (!$browsers['!IE']) {
         // "downlevel-hidden".
         $element['#prefix'] = Markup::create("\n<!--[if {$expression}]>\n" . $prefix);
         $element['#suffix'] = Markup::create($suffix . "<![endif]-->\n");
     } else {
         // "downlevel-revealed".
         $element['#prefix'] = Markup::create("\n<!--[if {$expression}]><!-->\n" . $prefix);
         $element['#suffix'] = Markup::create($suffix . "<!--<![endif]-->\n");
     }
     return $element;
 }
Exemplo n.º 12
0
 /**
  * Applies a very permissive XSS/HTML filter for admin-only use.
  *
  * Note: This method only filters if $string is not marked safe already. This
  * ensures that HTML intended for display is not filtered.
  *
  * @param string|\Drupal\Core\Render\SafeString $string
  *   A string.
  *
  * @return \Drupal\Core\Render\SafeString
  *   The escaped string wrapped in a SafeString object. If
  *   SafeMarkup::isSafe($string) returns TRUE, it won't be escaped again.
  */
 protected function xssFilterAdminIfUnsafe($string)
 {
     if (!SafeMarkup::isSafe($string)) {
         $string = Xss::filterAdmin($string);
     }
     return SafeString::create($string);
 }
Exemplo n.º 13
0
 /**
  * Escapes #plain_text or filters #markup as required.
  *
  * Drupal uses Twig's auto-escape feature to improve security. This feature
  * automatically escapes any HTML that is not known to be safe. Due to this
  * the render system needs to ensure that all markup it generates is marked
  * safe so that Twig does not do any additional escaping.
  *
  * By default all #markup is filtered to protect against XSS using the admin
  * tag list. Render arrays can alter the list of tags allowed by the filter
  * using the #allowed_tags property. This value should be an array of tags
  * that Xss::filter() would accept. Render arrays can escape text instead
  * of XSS filtering by setting the #plain_text property instead of #markup. If
  * #plain_text is used #allowed_tags is ignored.
  *
  * @param array $elements
  *   A render array with #markup set.
  *
  * @return \Drupal\Component\Render\MarkupInterface|string
  *   The escaped markup wrapped in a Markup object. If
  *   SafeMarkup::isSafe($elements['#markup']) returns TRUE, it won't be
  *   escaped or filtered again.
  *
  * @see \Drupal\Component\Utility\Html::escape()
  * @see \Drupal\Component\Utility\Xss::filter()
  * @see \Drupal\Component\Utility\Xss::adminFilter()
  */
 protected function ensureMarkupIsSafe(array $elements)
 {
     if (empty($elements['#markup']) && empty($elements['#plain_text'])) {
         return $elements;
     }
     if (!empty($elements['#plain_text'])) {
         $elements['#markup'] = Markup::create(Html::escape($elements['#plain_text']));
     } elseif (!SafeMarkup::isSafe($elements['#markup'])) {
         // The default behaviour is to XSS filter using the admin tag list.
         $tags = isset($elements['#allowed_tags']) ? $elements['#allowed_tags'] : Xss::getAdminTagList();
         $elements['#markup'] = Markup::create(Xss::filter($elements['#markup'], $tags));
     }
     return $elements;
 }
Exemplo n.º 14
0
 /**
  * Replaces all tokens in a given string with appropriate values.
  *
  * @param string $text
  *   An HTML string containing replaceable tokens. The caller is responsible
  *   for calling \Drupal\Component\Utility\Html::escape() in case the $text
  *   was plain text.
  * @param array $data
  *   (optional) An array of keyed objects. For simple replacement scenarios
  *   'node', 'user', and others are common keys, with an accompanying node or
  *   user object being the value. Some token types, like 'site', do not require
  *   any explicit information from $data and can be replaced even if it is
  *   empty.
  * @param array $options
  *   (optional) A keyed array of settings and flags to control the token
  *   replacement process. Supported options are:
  *   - langcode: A language code to be used when generating locale-sensitive
  *     tokens.
  *   - callback: A callback function that will be used to post-process the
  *     array of token replacements after they are generated.
  *   - clear: A boolean flag indicating that tokens should be removed from the
  *     final text if no replacement value can be generated.
  * @param \Drupal\Core\Render\BubbleableMetadata $bubbleable_metadata|null
  *   (optional) An object to which static::generate() and the hooks and
  *   functions that it invokes will add their required bubbleable metadata.
  *
  *   To ensure that the metadata associated with the token replacements gets
  *   attached to the same render array that contains the token-replaced text,
  *   callers of this method are encouraged to pass in a BubbleableMetadata
  *   object and apply it to the corresponding render array. For example:
  *   @code
  *     $bubbleable_metadata = new BubbleableMetadata();
  *     $build['#markup'] = $token_service->replace('Tokens: [node:nid] [current-user:uid]', ['node' => $node], [], $bubbleable_metadata);
  *     $bubbleable_metadata->applyTo($build);
  *   @endcode
  *
  *   When the caller does not pass in a BubbleableMetadata object, this
  *   method creates a local one, and applies the collected metadata to the
  *   Renderer's currently active render context.
  *
  * @return string
  *   The token result is the entered HTML text with tokens replaced. The
  *   caller is responsible for choosing the right escaping / sanitization. If
  *   the result is intended to be used as plain text, using
  *   PlainTextOutput::renderFromHtml() is recommended. If the result is just
  *   printed as part of a template relying on Twig autoescaping is possible,
  *   otherwise for example the result can be put into #markup, in which case
  *   it would be sanitized by Xss::filterAdmin().
  */
 public function replace($text, array $data = array(), array $options = array(), BubbleableMetadata $bubbleable_metadata = NULL)
 {
     $text_tokens = $this->scan($text);
     if (empty($text_tokens)) {
         return $text;
     }
     $bubbleable_metadata_is_passed_in = (bool) $bubbleable_metadata;
     $bubbleable_metadata = $bubbleable_metadata ?: new BubbleableMetadata();
     $replacements = array();
     foreach ($text_tokens as $type => $tokens) {
         $replacements += $this->generate($type, $tokens, $data, $options, $bubbleable_metadata);
         if (!empty($options['clear'])) {
             $replacements += array_fill_keys($tokens, '');
         }
     }
     // Escape the tokens, unless they are explicitly markup.
     foreach ($replacements as $token => $value) {
         $replacements[$token] = SafeMarkup::isSafe($value) ? $value : Html::escape($value);
     }
     // Optionally alter the list of replacement values.
     if (!empty($options['callback'])) {
         $function = $options['callback'];
         $function($replacements, $data, $options, $bubbleable_metadata);
     }
     $tokens = array_keys($replacements);
     $values = array_values($replacements);
     // If a local $bubbleable_metadata object was created, apply the metadata
     // it collected to the renderer's currently active render context.
     if (!$bubbleable_metadata_is_passed_in && $this->renderer->hasRenderContext()) {
         $build = [];
         $bubbleable_metadata->applyTo($build);
         $this->renderer->render($build);
     }
     return str_replace($tokens, $values, $text);
 }
Exemplo n.º 15
0
 /**
  * Run database tasks and tests to see if Drupal can run on the database.
  */
 public function runTasks()
 {
     // We need to establish a connection before we can run tests.
     if ($this->connect()) {
         foreach ($this->tasks as $task) {
             if (!isset($task['function'])) {
                 $task['function'] = 'runTestQuery';
             }
             if (method_exists($this, $task['function'])) {
                 // Returning false is fatal. No other tasks can run.
                 if (FALSE === call_user_func_array(array($this, $task['function']), $task['arguments'])) {
                     break;
                 }
             } else {
                 throw new TaskException(t("Failed to run all tasks against the database server. The task %task wasn't found.", array('%task' => $task['function'])));
             }
         }
     }
     // Check for failed results and compile message
     $message = '';
     foreach ($this->results as $result => $success) {
         if (!$success) {
             $message = SafeMarkup::isSafe($result) ? $result : SafeMarkup::checkPlain($result);
         }
     }
     if (!empty($message)) {
         $message = SafeMarkup::set('Resolve all issues below to continue the installation. For help configuring your database server, see the <a href="https://www.drupal.org/getting-started/install">installation handbook</a>, or contact your hosting provider.' . $message);
         throw new TaskException($message);
     }
 }
Exemplo n.º 16
0
 /**
  * {@inheritdoc}
  *
  * For anonymous users, the "active" class will be calculated on the server,
  * because most sites serve each anonymous user the same cached page anyway.
  * For authenticated users, the "active" class will be calculated on the
  * client (through JavaScript), only data- attributes are added to links to
  * prevent breaking the render cache. The JavaScript is added in
  * system_page_attachments().
  *
  * @see system_page_attachments()
  */
 public function generate($text, Url $url)
 {
     // Performance: avoid Url::toString() needing to retrieve the URL generator
     // service from the container.
     $url->setUrlGenerator($this->urlGenerator);
     if (is_array($text)) {
         $text = $this->renderer->render($text);
     }
     // Start building a structured representation of our link to be altered later.
     $variables = array('text' => $text, 'url' => $url, 'options' => $url->getOptions());
     // Merge in default options.
     $variables['options'] += array('attributes' => array(), 'query' => array(), 'language' => NULL, 'set_active_class' => FALSE, 'absolute' => FALSE);
     // Add a hreflang attribute if we know the language of this link's url and
     // hreflang has not already been set.
     if (!empty($variables['options']['language']) && !isset($variables['options']['attributes']['hreflang'])) {
         $variables['options']['attributes']['hreflang'] = $variables['options']['language']->getId();
     }
     // Ensure that query values are strings.
     array_walk($variables['options']['query'], function (&$value) {
         if ($value instanceof MarkupInterface) {
             $value = (string) $value;
         }
     });
     // Set the "active" class if the 'set_active_class' option is not empty.
     if (!empty($variables['options']['set_active_class']) && !$url->isExternal()) {
         // Add a "data-drupal-link-query" attribute to let the
         // drupal.active-link library know the query in a standardized manner.
         if (!empty($variables['options']['query'])) {
             $query = $variables['options']['query'];
             ksort($query);
             $variables['options']['attributes']['data-drupal-link-query'] = Json::encode($query);
         }
         // Add a "data-drupal-link-system-path" attribute to let the
         // drupal.active-link library know the path in a standardized manner.
         if ($url->isRouted() && !isset($variables['options']['attributes']['data-drupal-link-system-path'])) {
             // @todo System path is deprecated - use the route name and parameters.
             $system_path = $url->getInternalPath();
             // Special case for the front page.
             $variables['options']['attributes']['data-drupal-link-system-path'] = $system_path == '' ? '<front>' : $system_path;
         }
     }
     // Remove all HTML and PHP tags from a tooltip, calling expensive strip_tags()
     // only when a quick strpos() gives suspicion tags are present.
     if (isset($variables['options']['attributes']['title']) && strpos($variables['options']['attributes']['title'], '<') !== FALSE) {
         $variables['options']['attributes']['title'] = strip_tags($variables['options']['attributes']['title']);
     }
     // Allow other modules to modify the structure of the link.
     $this->moduleHandler->alter('link', $variables);
     // Move attributes out of options since generateFromRoute() doesn't need
     // them. Include a placeholder for the href.
     $attributes = array('href' => '') + $variables['options']['attributes'];
     unset($variables['options']['attributes']);
     $url->setOptions($variables['options']);
     // External URLs can not have cacheable metadata.
     if ($url->isExternal()) {
         $generated_link = new GeneratedLink();
         $attributes['href'] = $url->toString(FALSE);
     } else {
         $generated_url = $url->toString(TRUE);
         $generated_link = GeneratedLink::createFromObject($generated_url);
         // The result of the URL generator is a plain-text URL to use as the href
         // attribute, and it is escaped by \Drupal\Core\Template\Attribute.
         $attributes['href'] = $generated_url->getGeneratedUrl();
     }
     if (!SafeMarkup::isSafe($variables['text'])) {
         $variables['text'] = Html::escape($variables['text']);
     }
     $attributes = new Attribute($attributes);
     // This is safe because Attribute does escaping and $variables['text'] is
     // either rendered or escaped.
     return $generated_link->setGeneratedLink('<a' . $attributes . '>' . $variables['text'] . '</a>');
 }
Exemplo n.º 17
0
 /**
  * Tests string formatting with SafeMarkup::format().
  *
  * @dataProvider providerFormat
  * @covers ::format
  *
  * @param string $string
  *   The string to run through SafeMarkup::format().
  * @param string[] $args
  *   The arguments to pass into SafeMarkup::format().
  * @param string $expected
  *   The expected result from calling the function.
  * @param string $message
  *   The message to display as output to the test.
  * @param bool $expected_is_safe
  *   Whether the result is expected to be safe for HTML display.
  */
 public function testFormat($string, array $args, $expected, $message, $expected_is_safe)
 {
     $result = SafeMarkup::format($string, $args);
     $this->assertEquals($expected, $result, $message);
     $this->assertEquals($expected_is_safe, SafeMarkup::isSafe($result), 'SafeMarkup::format correctly sets the result as safe or not safe.');
     foreach ($args as $arg) {
         $this->assertSame($arg instanceof SafeMarkupTestSafeString, SafeMarkup::isSafe($arg));
     }
 }
Exemplo n.º 18
0
 /**
  * {@inheritdoc}
  */
 public function renderText($alter)
 {
     // We need to preserve the safeness of the value regardless of the
     // alterations made by this method. Any alterations or replacements made
     // within this method need to ensure that at the minimum the result is
     // XSS admin filtered. See self::renderAltered() as an example that does.
     $value_is_safe = SafeMarkup::isSafe($this->last_render);
     // Cast to a string so that empty checks and string functions work as
     // expected.
     $value = (string) $this->last_render;
     if (!empty($alter['alter_text']) && $alter['text'] !== '') {
         $tokens = $this->getRenderTokens($alter);
         $value = $this->renderAltered($alter, $tokens);
     }
     if (!empty($this->options['alter']['trim_whitespace'])) {
         $value = trim($value);
     }
     // Check if there should be no further rewrite for empty values.
     $no_rewrite_for_empty = $this->options['hide_alter_empty'] && $this->isValueEmpty($this->original_value, $this->options['empty_zero']);
     // Check whether the value is empty and return nothing, so the field isn't rendered.
     // First check whether the field should be hidden if the value(hide_alter_empty = TRUE) /the rewrite is empty (hide_alter_empty = FALSE).
     // For numeric values you can specify whether "0"/0 should be empty.
     if (($this->options['hide_empty'] && empty($value) || $alter['phase'] != static::RENDER_TEXT_PHASE_EMPTY && $no_rewrite_for_empty) && $this->isValueEmpty($value, $this->options['empty_zero'], FALSE)) {
         return '';
     }
     // Only in empty phase.
     if ($alter['phase'] == static::RENDER_TEXT_PHASE_EMPTY && $no_rewrite_for_empty) {
         // If we got here then $alter contains the value of "No results text"
         // and so there is nothing left to do.
         return ViewsRenderPipelineMarkup::create($value);
     }
     if (!empty($alter['strip_tags'])) {
         $value = strip_tags($value, $alter['preserve_tags']);
     }
     $more_link = '';
     if (!empty($alter['trim']) && !empty($alter['max_length'])) {
         $length = strlen($value);
         $value = $this->renderTrimText($alter, $value);
         if ($this->options['alter']['more_link'] && strlen($value) < $length) {
             $tokens = $this->getRenderTokens($alter);
             $more_link_text = $this->options['alter']['more_link_text'] ? $this->options['alter']['more_link_text'] : $this->t('more');
             $more_link_text = strtr(Xss::filterAdmin($more_link_text), $tokens);
             $more_link_path = $this->options['alter']['more_link_path'];
             $more_link_path = strip_tags(Html::decodeEntities($this->viewsTokenReplace($more_link_path, $tokens)));
             // Make sure that paths which were run through URL generation work as
             // well.
             $base_path = base_path();
             // Checks whether the path starts with the base_path.
             if (strpos($more_link_path, $base_path) === 0) {
                 $more_link_path = Unicode::substr($more_link_path, Unicode::strlen($base_path));
             }
             // @todo Views should expect and store a leading /. See
             //   https://www.drupal.org/node/2423913.
             $more_link = ' ' . $this->linkGenerator()->generate($more_link_text, CoreUrl::fromUserInput('/' . $more_link_path, array('attributes' => array('class' => array('views-more-link')))));
         }
     }
     if (!empty($alter['nl2br'])) {
         $value = nl2br($value);
     }
     if ($value_is_safe) {
         $value = ViewsRenderPipelineMarkup::create($value);
     }
     $this->last_render_text = $value;
     if (!empty($alter['make_link']) && (!empty($alter['path']) || !empty($alter['url']))) {
         if (!isset($tokens)) {
             $tokens = $this->getRenderTokens($alter);
         }
         $value = $this->renderAsLink($alter, $value, $tokens);
     }
     // Preserve whether or not the string is safe. Since $more_link comes from
     // \Drupal::l(), it is safe to append. Use SafeMarkup::isSafe() here because
     // renderAsLink() can return both safe and unsafe values.
     if (SafeMarkup::isSafe($value)) {
         return ViewsRenderPipelineMarkup::create($value . $more_link);
     } else {
         // If the string is not already marked safe, it is still OK to return it
         // because it will be sanitized by Twig.
         return $value . $more_link;
     }
 }
Exemplo n.º 19
0
 /**
  * Escapes a placeholder replacement value if needed.
  *
  * @param string|\Drupal\Component\Render\MarkupInterface $value
  *   A placeholder replacement value.
  *
  * @return string
  *   The properly escaped replacement value.
  */
 protected static function placeholderEscape($value)
 {
     return SafeMarkup::isSafe($value) ? (string) $value : Html::escape($value);
 }
Exemplo n.º 20
0
 /**
  * Overrides twig_escape_filter().
  *
  * Replacement function for Twig's escape filter.
  *
  * Note: This function should be kept in sync with
  * theme_render_and_autoescape().
  *
  * @param \Twig_Environment $env
  *   A Twig_Environment instance.
  * @param mixed $arg
  *   The value to be escaped.
  * @param string $strategy
  *   The escaping strategy. Defaults to 'html'.
  * @param string $charset
  *   The charset.
  * @param bool $autoescape
  *   Whether the function is called by the auto-escaping feature (TRUE) or by
  *   the developer (FALSE).
  *
  * @return string|null
  *   The escaped, rendered output, or NULL if there is no valid output.
  *
  * @todo Refactor this to keep it in sync with theme_render_and_autoescape()
  *   in https://www.drupal.org/node/2575065
  */
 public function escapeFilter(\Twig_Environment $env, $arg, $strategy = 'html', $charset = NULL, $autoescape = FALSE)
 {
     // Check for a numeric zero int or float.
     if ($arg === 0 || $arg === 0.0) {
         return 0;
     }
     // Return early for NULL and empty arrays.
     if ($arg == NULL) {
         return NULL;
     }
     // Keep Twig_Markup objects intact to support autoescaping.
     if ($autoescape && ($arg instanceof \Twig_Markup || $arg instanceof MarkupInterface)) {
         return $arg;
     }
     $return = NULL;
     if (is_scalar($arg)) {
         $return = (string) $arg;
     } elseif (is_object($arg)) {
         if ($arg instanceof RenderableInterface) {
             $arg = $arg->toRenderable();
         } elseif (method_exists($arg, '__toString')) {
             $return = (string) $arg;
         } elseif (method_exists($arg, 'toString')) {
             $return = $arg->toString();
         } else {
             throw new \Exception(t('Object of type "@class" cannot be printed.', array('@class' => get_class($arg))));
         }
     }
     // We have a string or an object converted to a string: Autoescape it!
     if (isset($return)) {
         if ($autoescape && SafeMarkup::isSafe($return, $strategy)) {
             return $return;
         }
         // Drupal only supports the HTML escaping strategy, so provide a
         // fallback for other strategies.
         if ($strategy == 'html') {
             return Html::escape($return);
         }
         return twig_escape_filter($env, $return, $strategy, $charset, $autoescape);
     }
     // This is a normal render array, which is safe by definition, with
     // special simple cases already handled.
     // Early return if this element was pre-rendered (no need to re-render).
     if (isset($arg['#printed']) && $arg['#printed'] == TRUE && isset($arg['#markup']) && strlen($arg['#markup']) > 0) {
         return $arg['#markup'];
     }
     $arg['#printed'] = FALSE;
     return $this->renderer->render($arg);
 }
Exemplo n.º 21
0
 /**
  * Tests string formatting with SafeMarkup::format().
  *
  * @dataProvider providerFormat
  * @covers ::format
  *
  * @param string $string
  *   The string to run through SafeMarkup::format().
  * @param string $args
  *   The arguments to pass into SafeMarkup::format().
  * @param string $expected
  *   The expected result from calling the function.
  * @param string $message
  *   The message to display as output to the test.
  * @param bool $expected_is_safe
  *   Whether the result is expected to be safe for HTML display.
  */
 function testFormat($string, $args, $expected, $message, $expected_is_safe)
 {
     $result = SafeMarkup::format($string, $args);
     $this->assertEquals($expected, $result, $message);
     $this->assertEquals($expected_is_safe, SafeMarkup::isSafe($result), 'SafeMarkup::format correctly sets the result as safe or not safe.');
 }