updated core to 8.6.2
This commit is contained in:
@@ -191,13 +191,19 @@ function _contextual_links_to_id($contextual_links) {
|
||||
/**
|
||||
* Unserializes the result of _contextual_links_to_id().
|
||||
*
|
||||
* @see _contextual_links_to_id
|
||||
* Note that $id is user input. Before calling this method the ID should be
|
||||
* checked against the token stored in the 'data-contextual-token' attribute
|
||||
* which is passed via the 'tokens' request parameter to
|
||||
* \Drupal\contextual\ContextualController::render().
|
||||
*
|
||||
* @param string $id
|
||||
* A serialized representation of a #contextual_links property value array.
|
||||
*
|
||||
* @return array
|
||||
* The value for a #contextual_links property.
|
||||
*
|
||||
* @see _contextual_links_to_id()
|
||||
* @see \Drupal\contextual\ContextualController::render()
|
||||
*/
|
||||
function _contextual_id_to_links($id) {
|
||||
$contextual_links = [];
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Post update functions for Contextual Links.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Ensure new page loads use the updated JS and get the updated markup.
|
||||
*/
|
||||
function contextual_post_update_fixed_endpoint_and_markup() {
|
||||
// Empty update to trigger a change to css_js_query_string and invalidate
|
||||
// cached markup.
|
||||
}
|
||||
@@ -168,12 +168,16 @@
|
||||
// Collect the IDs for all contextual links placeholders.
|
||||
const ids = [];
|
||||
$placeholders.each(function() {
|
||||
ids.push($(this).attr('data-contextual-id'));
|
||||
ids.push({
|
||||
id: $(this).attr('data-contextual-id'),
|
||||
token: $(this).attr('data-contextual-token'),
|
||||
});
|
||||
});
|
||||
|
||||
// Update all contextual links placeholders whose HTML is cached.
|
||||
const uncachedIDs = _.filter(ids, contextualID => {
|
||||
const html = storage.getItem(`Drupal.contextual.${contextualID}`);
|
||||
const uncachedIDs = [];
|
||||
const uncachedTokens = [];
|
||||
ids.forEach(contextualID => {
|
||||
const html = storage.getItem(`Drupal.contextual.${contextualID.id}`);
|
||||
if (html && html.length) {
|
||||
// Initialize after the current execution cycle, to make the AJAX
|
||||
// request for retrieving the uncached contextual links as soon as
|
||||
@@ -182,13 +186,14 @@
|
||||
// Drupal.contextual.collection.
|
||||
window.setTimeout(() => {
|
||||
initContextual(
|
||||
$context.find(`[data-contextual-id="${contextualID}"]`),
|
||||
$context.find(`[data-contextual-id="${contextualID.id}"]`),
|
||||
html,
|
||||
);
|
||||
});
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
return true;
|
||||
uncachedIDs.push(contextualID.id);
|
||||
uncachedTokens.push(contextualID.token);
|
||||
});
|
||||
|
||||
// Perform an AJAX request to let the server render the contextual links
|
||||
@@ -197,7 +202,7 @@
|
||||
$.ajax({
|
||||
url: Drupal.url('contextual/render'),
|
||||
type: 'POST',
|
||||
data: { 'ids[]': uncachedIDs },
|
||||
data: { 'ids[]': uncachedIDs, 'tokens[]': uncachedTokens },
|
||||
dataType: 'json',
|
||||
success(results) {
|
||||
_.each(results, (html, contextualID) => {
|
||||
|
||||
@@ -95,25 +95,31 @@
|
||||
|
||||
var ids = [];
|
||||
$placeholders.each(function () {
|
||||
ids.push($(this).attr('data-contextual-id'));
|
||||
ids.push({
|
||||
id: $(this).attr('data-contextual-id'),
|
||||
token: $(this).attr('data-contextual-token')
|
||||
});
|
||||
});
|
||||
|
||||
var uncachedIDs = _.filter(ids, function (contextualID) {
|
||||
var html = storage.getItem('Drupal.contextual.' + contextualID);
|
||||
var uncachedIDs = [];
|
||||
var uncachedTokens = [];
|
||||
ids.forEach(function (contextualID) {
|
||||
var html = storage.getItem('Drupal.contextual.' + contextualID.id);
|
||||
if (html && html.length) {
|
||||
window.setTimeout(function () {
|
||||
initContextual($context.find('[data-contextual-id="' + contextualID + '"]'), html);
|
||||
initContextual($context.find('[data-contextual-id="' + contextualID.id + '"]'), html);
|
||||
});
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
return true;
|
||||
uncachedIDs.push(contextualID.id);
|
||||
uncachedTokens.push(contextualID.token);
|
||||
});
|
||||
|
||||
if (uncachedIDs.length > 0) {
|
||||
$.ajax({
|
||||
url: Drupal.url('contextual/render'),
|
||||
type: 'POST',
|
||||
data: { 'ids[]': uncachedIDs },
|
||||
data: { 'ids[]': uncachedIDs, 'tokens[]': uncachedTokens },
|
||||
dataType: 'json',
|
||||
success: function success(results) {
|
||||
_.each(results, function (html, contextualID) {
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
|
||||
namespace Drupal\contextual;
|
||||
|
||||
use Drupal\Component\Utility\Crypt;
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\Core\Render\RendererInterface;
|
||||
use Drupal\Core\Site\Settings;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
@@ -63,8 +65,16 @@ class ContextualController implements ContainerInjectionInterface {
|
||||
throw new BadRequestHttpException(t('No contextual ids specified.'));
|
||||
}
|
||||
|
||||
$tokens = $request->request->get('tokens');
|
||||
if (!isset($tokens)) {
|
||||
throw new BadRequestHttpException(t('No contextual ID tokens specified.'));
|
||||
}
|
||||
|
||||
$rendered = [];
|
||||
foreach ($ids as $id) {
|
||||
foreach ($ids as $key => $id) {
|
||||
if (!isset($tokens[$key]) || !Crypt::hashEquals($tokens[$key], Crypt::hmacBase64($id, Settings::getHashSalt() . \Drupal::service('private_key')->get()))) {
|
||||
throw new BadRequestHttpException('Invalid contextual ID specified.');
|
||||
}
|
||||
$element = [
|
||||
'#type' => 'contextual_links',
|
||||
'#contextual_links' => _contextual_id_to_links($id),
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Drupal\contextual\Element;
|
||||
|
||||
use Drupal\Component\Utility\Crypt;
|
||||
use Drupal\Core\Site\Settings;
|
||||
use Drupal\Core\Template\Attribute;
|
||||
use Drupal\Core\Render\Element\RenderElement;
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
@@ -43,7 +45,12 @@ class ContextualLinksPlaceholder extends RenderElement {
|
||||
* @see _contextual_links_to_id()
|
||||
*/
|
||||
public static function preRenderPlaceholder(array $element) {
|
||||
$element['#markup'] = new FormattableMarkup('<div@attributes></div>', ['@attributes' => new Attribute(['data-contextual-id' => $element['#id']])]);
|
||||
$token = Crypt::hmacBase64($element['#id'], Settings::getHashSalt() . \Drupal::service('private_key')->get());
|
||||
$attribute = new Attribute([
|
||||
'data-contextual-id' => $element['#id'],
|
||||
'data-contextual-token' => $token,
|
||||
]);
|
||||
$element['#markup'] = new FormattableMarkup('<div@attributes></div>', ['@attributes' => $attribute]);
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
namespace Drupal\Tests\contextual\Functional;
|
||||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Component\Utility\Crypt;
|
||||
use Drupal\Core\Site\Settings;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\Core\Template\Attribute;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
@@ -140,17 +141,76 @@ class ContextualDynamicContextTest extends BrowserTestBase {
|
||||
$this->assertRaw('<li class="menu-testcontextual-hidden-manage-edit"><a href="' . base_path() . 'menu-test-contextual/1/edit" class="use-ajax" data-dialog-type="modal" data-is-something>Edit menu - contextual</a></li>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the contextual placeholder content is protected by a token.
|
||||
*/
|
||||
public function testTokenProtection() {
|
||||
$this->drupalLogin($this->editorUser);
|
||||
|
||||
// Create a node that will have a contextual link.
|
||||
$node1 = $this->drupalCreateNode(['type' => 'article', 'promote' => 1]);
|
||||
|
||||
// Now, on the front page, all article nodes should have contextual links
|
||||
// placeholders, as should the view that contains them.
|
||||
$id = 'node:node=' . $node1->id() . ':changed=' . $node1->getChangedTime() . '&langcode=en';
|
||||
|
||||
// Editor user: can access contextual links and can edit articles.
|
||||
$this->drupalGet('node');
|
||||
$this->assertContextualLinkPlaceHolder($id);
|
||||
|
||||
$http_client = $this->getHttpClient();
|
||||
$url = Url::fromRoute('contextual.render', [], [
|
||||
'query' => [
|
||||
'_format' => 'json',
|
||||
'destination' => 'node',
|
||||
],
|
||||
])->setAbsolute()->toString();
|
||||
|
||||
$response = $http_client->request('POST', $url, [
|
||||
'cookies' => $this->getSessionCookies(),
|
||||
'form_params' => ['ids' => [$id], 'tokens' => []],
|
||||
'http_errors' => FALSE,
|
||||
]);
|
||||
$this->assertEquals('400', $response->getStatusCode());
|
||||
$this->assertContains('No contextual ID tokens specified.', (string) $response->getBody());
|
||||
|
||||
$response = $http_client->request('POST', $url, [
|
||||
'cookies' => $this->getSessionCookies(),
|
||||
'form_params' => ['ids' => [$id], 'tokens' => ['wrong_token']],
|
||||
'http_errors' => FALSE,
|
||||
]);
|
||||
$this->assertEquals('400', $response->getStatusCode());
|
||||
$this->assertContains('Invalid contextual ID specified.', (string) $response->getBody());
|
||||
|
||||
$response = $http_client->request('POST', $url, [
|
||||
'cookies' => $this->getSessionCookies(),
|
||||
'form_params' => ['ids' => [$id], 'tokens' => ['wrong_key' => $this->createContextualIdToken($id)]],
|
||||
'http_errors' => FALSE,
|
||||
]);
|
||||
$this->assertEquals('400', $response->getStatusCode());
|
||||
$this->assertContains('Invalid contextual ID specified.', (string) $response->getBody());
|
||||
|
||||
$response = $http_client->request('POST', $url, [
|
||||
'cookies' => $this->getSessionCookies(),
|
||||
'form_params' => ['ids' => [$id], 'tokens' => [$this->createContextualIdToken($id)]],
|
||||
'http_errors' => FALSE,
|
||||
]);
|
||||
$this->assertEquals('200', $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a contextual link placeholder with the given id exists.
|
||||
*
|
||||
* @param string $id
|
||||
* A contextual link id.
|
||||
*
|
||||
* @return bool
|
||||
* The result of the assertion.
|
||||
*/
|
||||
protected function assertContextualLinkPlaceHolder($id) {
|
||||
return $this->assertRaw('<div' . new Attribute(['data-contextual-id' => $id]) . '></div>', format_string('Contextual link placeholder with id @id exists.', ['@id' => $id]));
|
||||
$this->assertSession()->elementAttributeContains(
|
||||
'css',
|
||||
'div[data-contextual-id="' . $id . '"]',
|
||||
'data-contextual-token',
|
||||
$this->createContextualIdToken($id)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,12 +218,9 @@ class ContextualDynamicContextTest extends BrowserTestBase {
|
||||
*
|
||||
* @param string $id
|
||||
* A contextual link id.
|
||||
*
|
||||
* @return bool
|
||||
* The result of the assertion.
|
||||
*/
|
||||
protected function assertNoContextualLinkPlaceHolder($id) {
|
||||
return $this->assertNoRaw('<div' . new Attribute(['data-contextual-id' => $id]) . '></div>', format_string('Contextual link placeholder with id @id does not exist.', ['@id' => $id]));
|
||||
$this->assertSession()->elementNotExists('css', 'div[data-contextual-id="' . $id . '"]');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,6 +235,7 @@ class ContextualDynamicContextTest extends BrowserTestBase {
|
||||
* The response object.
|
||||
*/
|
||||
protected function renderContextualLinks($ids, $current_path) {
|
||||
$tokens = array_map([$this, 'createContextualIdToken'], $ids);
|
||||
$http_client = $this->getHttpClient();
|
||||
$url = Url::fromRoute('contextual.render', [], [
|
||||
'query' => [
|
||||
@@ -188,9 +246,22 @@ class ContextualDynamicContextTest extends BrowserTestBase {
|
||||
|
||||
return $http_client->request('POST', $this->buildUrl($url), [
|
||||
'cookies' => $this->getSessionCookies(),
|
||||
'form_params' => ['ids' => $ids],
|
||||
'form_params' => ['ids' => $ids, 'tokens' => $tokens],
|
||||
'http_errors' => FALSE,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a contextual ID token.
|
||||
*
|
||||
* @param string $id
|
||||
* The contextual ID to create a token for.
|
||||
*
|
||||
* @return string
|
||||
* The contextual ID token.
|
||||
*/
|
||||
protected function createContextualIdToken($id) {
|
||||
return Crypt::hmacBase64($id, Settings::getHashSalt() . $this->container->get('private_key')->get());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user