Commit aa2485a2 authored by xjm's avatar xjm

Merged 9.0.6.

parents 9bb004cd b6ca29bf
...@@ -4481,9 +4481,6 @@ ...@@ -4481,9 +4481,6 @@
"ext-zip": "Enabling the zip extension allows you to unzip archives", "ext-zip": "Enabling the zip extension allows you to unzip archives",
"ext-zlib": "Allow gzip compression of HTTP requests" "ext-zlib": "Allow gzip compression of HTTP requests"
}, },
"bin": [
"bin/composer"
],
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
......
...@@ -861,7 +861,8 @@ protected function buildFormAction() { ...@@ -861,7 +861,8 @@ protected function buildFormAction() {
// https://www.drupal.org/node/2504709. // https://www.drupal.org/node/2504709.
$parsed = UrlHelper::parse($request_uri); $parsed = UrlHelper::parse($request_uri);
unset($parsed['query'][static::AJAX_FORM_REQUEST], $parsed['query'][MainContentViewSubscriber::WRAPPER_FORMAT]); unset($parsed['query'][static::AJAX_FORM_REQUEST], $parsed['query'][MainContentViewSubscriber::WRAPPER_FORMAT]);
return $parsed['path'] . ($parsed['query'] ? ('?' . UrlHelper::buildQuery($parsed['query'])) : ''); $action = $parsed['path'] . ($parsed['query'] ? ('?' . UrlHelper::buildQuery($parsed['query'])) : '');
return UrlHelper::filterBadProtocol($action);
} }
/** /**
......
...@@ -559,6 +559,7 @@ ...@@ -559,6 +559,7 @@
} }
}, },
dataType: 'json', dataType: 'json',
jsonp: false,
type: 'POST', type: 'POST',
}; };
......
...@@ -246,6 +246,7 @@ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len ...@@ -246,6 +246,7 @@ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len
} }
}, },
dataType: 'json', dataType: 'json',
jsonp: false,
type: 'POST' type: 'POST'
}; };
......
...@@ -281,6 +281,7 @@ ...@@ -281,6 +281,7 @@
}, },
ajax: { ajax: {
dataType: 'json', dataType: 'json',
jsonp: false,
}, },
}; };
......
...@@ -155,7 +155,8 @@ ...@@ -155,7 +155,8 @@
isComposing: false isComposing: false
}, },
ajax: { ajax: {
dataType: 'json' dataType: 'json',
jsonp: false
} }
}; };
Drupal.autocomplete = autocomplete; Drupal.autocomplete = autocomplete;
......
...@@ -215,6 +215,9 @@ ...@@ -215,6 +215,9 @@
'figcaption', 'figcaption',
); );
const captionFilter = new CKEDITOR.filter(widgetDefinition.editables.caption.allowedContent);
captionFilter.applyTo(caption);
// Use Drupal's data-placeholder attribute to insert a CSS-based, // Use Drupal's data-placeholder attribute to insert a CSS-based,
// translation-ready placeholder for empty captions. Note that it // translation-ready placeholder for empty captions. Note that it
// also must to be done for new instances (see // also must to be done for new instances (see
......
...@@ -137,6 +137,8 @@ ...@@ -137,6 +137,8 @@
if (caption) { if (caption) {
var figure = new CKEDITOR.htmlParser.element('figure'); var figure = new CKEDITOR.htmlParser.element('figure');
caption = new CKEDITOR.htmlParser.fragment.fromHtml(caption, 'figcaption'); caption = new CKEDITOR.htmlParser.fragment.fromHtml(caption, 'figcaption');
var captionFilter = new CKEDITOR.filter(widgetDefinition.editables.caption.allowedContent);
captionFilter.applyTo(caption);
caption.attributes['data-placeholder'] = placeholderText; caption.attributes['data-placeholder'] = placeholderText;
element.replaceWith(figure); element.replaceWith(figure);
figure.add(element); figure.add(element);
......
...@@ -96,6 +96,10 @@ public static function valueCallback(&$element, $input, FormStateInterface $form ...@@ -96,6 +96,10 @@ public static function valueCallback(&$element, $input, FormStateInterface $form
foreach ($input['fids'] as $fid) { foreach ($input['fids'] as $fid) {
if ($file = File::load($fid)) { if ($file = File::load($fid)) {
$fids[] = $file->id(); $fids[] = $file->id();
if (!$file->access('download')) {
$force_default = TRUE;
break;
}
// Temporary files that belong to other users should never be // Temporary files that belong to other users should never be
// allowed. // allowed.
if ($file->isTemporary()) { if ($file->isTemporary()) {
......
...@@ -92,11 +92,10 @@ public function testPrivateFile() { ...@@ -92,11 +92,10 @@ public function testPrivateFile() {
$this->drupalGet('node/' . $new_node->id() . '/edit'); $this->drupalGet('node/' . $new_node->id() . '/edit');
$this->getSession()->getPage()->find('css', 'input[name="' . $field_name . '[0][fids]"]')->setValue($node_file->id()); $this->getSession()->getPage()->find('css', 'input[name="' . $field_name . '[0][fids]"]')->setValue($node_file->id());
$this->getSession()->getPage()->pressButton(t('Save')); $this->getSession()->getPage()->pressButton(t('Save'));
// Make sure the form submit failed - we stayed on the edit form. $this->assertUrl('node/' . $new_node->id());
$this->assertUrl('node/' . $new_node->id() . '/edit'); // Make sure the submitted hidden file field is empty.
// Check that we got the expected constraint form error. $new_node = \Drupal::entityTypeManager()->getStorage('node')->loadUnchanged($new_node->id());
$constraint = new ReferenceAccessConstraint(); $this->assertTrue($new_node->get($field_name)->isEmpty());
$this->assertRaw(new FormattableMarkup($constraint->message, ['%type' => 'file', '%id' => $node_file->id()]));
// Attempt to reuse the existing file when creating a new node, and confirm // Attempt to reuse the existing file when creating a new node, and confirm
// that access is still denied. // that access is still denied.
$edit = []; $edit = [];
...@@ -107,9 +106,10 @@ public function testPrivateFile() { ...@@ -107,9 +106,10 @@ public function testPrivateFile() {
$this->getSession()->getPage()->find('css', 'input[name="' . $field_name . '[0][fids]"]')->setValue($node_file->id()); $this->getSession()->getPage()->find('css', 'input[name="' . $field_name . '[0][fids]"]')->setValue($node_file->id());
$this->getSession()->getPage()->pressButton(t('Save')); $this->getSession()->getPage()->pressButton(t('Save'));
$new_node = $this->drupalGetNodeByTitle($edit['title[0][value]']); $new_node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
$this->assertTrue(empty($new_node), 'Node was not created.'); $this->assertUrl('node/' . $new_node->id());
$this->assertUrl('node/add/' . $type_name); // Make sure the submitted hidden file field is empty.
$this->assertRaw(new FormattableMarkup($constraint->message, ['%type' => 'file', '%id' => $node_file->id()])); $new_node = \Drupal::entityTypeManager()->getStorage('node')->loadUnchanged($new_node->id());
$this->assertTrue($new_node->get($field_name)->isEmpty());
// Now make file_test_file_download() return everything. // Now make file_test_file_download() return everything.
\Drupal::state()->set('file_test.allow_all', TRUE); \Drupal::state()->set('file_test.allow_all', TRUE);
......
...@@ -4,9 +4,12 @@ ...@@ -4,9 +4,12 @@
use Drupal\Component\Utility\Html; use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Xss; use Drupal\Component\Utility\Xss;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\filter\FilterPluginManager;
use Drupal\filter\FilterProcessResult; use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase; use Drupal\filter\Plugin\FilterBase;
use Drupal\filter\Render\FilteredMarkup; use Drupal\filter\Render\FilteredMarkup;
use Symfony\Component\DependencyInjection\ContainerInterface;
/** /**
* Provides a filter to caption elements. * Provides a filter to caption elements.
...@@ -20,7 +23,43 @@ ...@@ -20,7 +23,43 @@
* type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_REVERSIBLE * type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_REVERSIBLE
* ) * )
*/ */
class FilterCaption extends FilterBase { class FilterCaption extends FilterBase implements ContainerFactoryPluginInterface {
/**
* Filter manager.
*
* @var \Drupal\filter\FilterPluginManager
*/
protected $filterManager;
/**
* Constructs a new FilterCaption.
*
* @param array $configuration
* Configuration.
* @param string $plugin_id
* Plugin ID.
* @param mixed $plugin_definition
* Definition.
* @param \Drupal\filter\FilterPluginManager $filter_manager
* Filter plugin manager.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, FilterPluginManager $filter_manager = NULL) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->filterManager = $filter_manager ?: \Drupal::service('plugin.manager.filter');
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('plugin.manager.filter')
);
}
/** /**
* {@inheritdoc} * {@inheritdoc}
...@@ -31,6 +70,13 @@ public function process($text, $langcode) { ...@@ -31,6 +70,13 @@ public function process($text, $langcode) {
if (stristr($text, 'data-caption') !== FALSE) { if (stristr($text, 'data-caption') !== FALSE) {
$dom = Html::load($text); $dom = Html::load($text);
$xpath = new \DOMXPath($dom); $xpath = new \DOMXPath($dom);
$html_filter = $this->filterManager->createInstance('filter_html', [
'settings' => [
'allowed_html' => '<a href hreflang target rel> <em> <strong> <cite> <code> <br>',
'filter_html_help' => FALSE,
'filter_html_nofollow' => FALSE,
],
]);
foreach ($xpath->query('//*[@data-caption]') as $node) { foreach ($xpath->query('//*[@data-caption]') as $node) {
// Read the data-caption attribute's value, then delete it. // Read the data-caption attribute's value, then delete it.
$caption = Html::escape($node->getAttribute('data-caption')); $caption = Html::escape($node->getAttribute('data-caption'));
...@@ -39,10 +85,19 @@ public function process($text, $langcode) { ...@@ -39,10 +85,19 @@ public function process($text, $langcode) {
// Sanitize caption: decode HTML encoding, limit allowed HTML tags; only // Sanitize caption: decode HTML encoding, limit allowed HTML tags; only
// allow inline tags that are allowed by default, plus <br>. // allow inline tags that are allowed by default, plus <br>.
$caption = Html::decodeEntities($caption); $caption = Html::decodeEntities($caption);
$caption = FilteredMarkup::create(Xss::filter($caption, ['a', 'em', 'strong', 'cite', 'code', 'br'])); $raw_caption = $caption;
$filtered_caption = $html_filter->process($caption, $langcode);
$result->addCacheableDependency($filtered_caption);
$caption = FilteredMarkup::create($filtered_caption->getProcessedText());
// The caption must be non-empty. // The caption must be non-empty - however the Media Embed CKEditor
if (mb_strlen($caption) === 0) { // plugin uses a single space to represent a newly added caption. The
// HTML filter will transform this into an empty string and prevent the
// content editor from adding a new caption. To allow for this we treat
// a raw caption value of ' ' as valid and adding the wrapping figure
// element.
// @see core/modules/media/js/plugins/drupalmedia/plugin.es6.js
if (mb_strlen($caption) === 0 && $raw_caption !== ' ') {
continue; continue;
} }
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
namespace Drupal\user\Plugin\Block; namespace Drupal\user\Plugin\Block;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Access\AccessResult; use Drupal\Core\Access\AccessResult;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Security\TrustedCallbackInterface; use Drupal\Core\Security\TrustedCallbackInterface;
...@@ -155,7 +156,7 @@ public function build() { ...@@ -155,7 +156,7 @@ public function build() {
public static function renderPlaceholderFormAction() { public static function renderPlaceholderFormAction() {
return [ return [
'#type' => 'markup', '#type' => 'markup',
'#markup' => Url::fromRoute('<current>', [], ['query' => \Drupal::destination()->getAsArray(), 'external' => FALSE])->toString(), '#markup' => UrlHelper::filterBadProtocol(Url::fromRoute('<current>', [], ['query' => \Drupal::destination()->getAsArray(), 'external' => FALSE])->toString()),
'#cache' => ['contexts' => ['url.path', 'url.query_args']], '#cache' => ['contexts' => ['url.path', 'url.query_args']],
]; ];
} }
......
...@@ -186,7 +186,11 @@ public function getActiveWorkspace() { ...@@ -186,7 +186,11 @@ public function getActiveWorkspace() {
foreach ($this->negotiatorIds as $negotiator_id) { foreach ($this->negotiatorIds as $negotiator_id) {
$negotiator = $this->classResolver->getInstanceFromDefinition($negotiator_id); $negotiator = $this->classResolver->getInstanceFromDefinition($negotiator_id);
if ($negotiator->applies($request)) { if ($negotiator->applies($request)) {
if ($active_workspace = $negotiator->getActiveWorkspace($request)) { // By default, 'view' access is checked when a workspace is activated,
// but it should also be checked when retrieving the currently active
// workspace.
if (($negotiated_workspace = $negotiator->getActiveWorkspace($request)) && $negotiated_workspace->access('view')) {
$active_workspace = $negotiated_workspace;
break; break;
} }
} }
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
use Drupal\Tests\BrowserTestBase; use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\UpdatePathTestTrait; use Drupal\Tests\UpdatePathTestTrait;
use Drupal\Tests\user\Traits\UserCreationTrait;
/** /**
* Tests that there is no active workspace during database updates. * Tests that there is no active workspace during database updates.
...@@ -12,12 +13,14 @@ ...@@ -12,12 +13,14 @@
* @group Update * @group Update
*/ */
class ActiveWorkspaceUpdateTest extends BrowserTestBase { class ActiveWorkspaceUpdateTest extends BrowserTestBase {
use UpdatePathTestTrait; use UpdatePathTestTrait;
use UserCreationTrait;
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
protected static $modules = ['workspaces', 'workspace_update_test']; protected static $modules = ['workspaces'];
/** /**
* {@inheritdoc} * {@inheritdoc}
...@@ -29,6 +32,11 @@ class ActiveWorkspaceUpdateTest extends BrowserTestBase { ...@@ -29,6 +32,11 @@ class ActiveWorkspaceUpdateTest extends BrowserTestBase {
*/ */
protected function setUp(): void { protected function setUp(): void {
parent::setUp(); parent::setUp();
$this->setUpCurrentUser([], ['view any workspace']);
$this->container->get('module_installer')->install(['workspace_update_test']);
$this->rebuildContainer();
// Ensure the workspace_update_test_post_update_check_active_workspace() // Ensure the workspace_update_test_post_update_check_active_workspace()
// update runs. // update runs.
$existing_updates = \Drupal::keyValue('post_update')->get('existing_updates', []); $existing_updates = \Drupal::keyValue('post_update')->get('existing_updates', []);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment