Update Composer, update everything

This commit is contained in:
Oliver Davies 2018-11-23 12:29:20 +00:00
parent ea3e94409f
commit dda5c284b6
19527 changed files with 1135420 additions and 351004 deletions

View file

@ -5,4 +5,4 @@ package: Core
version: VERSION
core: 8.x
dependencies:
- node
- drupal:node

View file

@ -12,6 +12,7 @@
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\user\UserInterface;
/**
* Entities changed before this time are always shown as read.
@ -27,7 +28,7 @@ define('HISTORY_READ_LIMIT', REQUEST_TIME - 30 * 24 * 60 * 60);
function history_help($route_name, RouteMatchInterface $route_match) {
switch ($route_name) {
case 'help.page.history':
$output = '<h3>' . t('About') . '</h3>';
$output = '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('The History module keeps track of which content a user has read. It marks content as <em>new</em> or <em>updated</em> depending on the last time the user viewed it. History records that are older than one month are removed during cron, which means that content older than one month is always considered <em>read</em>. The History module does not have a user interface but it provides a filter to <a href=":views-help">Views</a> to show new or updated content. For more information, see the <a href=":url">online documentation for the History module</a>.', [':views-help' => (\Drupal::moduleHandler()->moduleExists('views')) ? \Drupal::url('help.page', ['name' => 'views']) : '#', ':url' => 'https://www.drupal.org/documentation/modules/history']) . '</p>';
return $output;
}
@ -159,7 +160,7 @@ function history_node_delete(EntityInterface $node) {
/**
* Implements hook_user_cancel().
*/
function history_user_cancel($edit, $account, $method) {
function history_user_cancel($edit, UserInterface $account, $method) {
switch ($method) {
case 'user_cancel_reassign':
db_delete('history')

View file

@ -14,7 +14,7 @@ function history_views_data() {
// We're actually defining a specific instance of the table, so let's
// alias it so that we can later add the real table for other purposes if we
// need it.
$data['history']['table']['group'] = t('Content');
$data['history']['table']['group'] = t('Content');
// Explain how this table joins to others.
$data['history']['table']['join'] = [

View file

@ -0,0 +1,146 @@
/**
* @file
* JavaScript API for the History module, with client-side caching.
*
* May only be loaded for authenticated users, with the History module enabled.
*/
(function($, Drupal, drupalSettings, storage) {
const currentUserID = parseInt(drupalSettings.user.uid, 10);
// Any comment that is older than 30 days is automatically considered read,
// so for these we don't need to perform a request at all!
const secondsIn30Days = 2592000;
const thirtyDaysAgo =
Math.round(new Date().getTime() / 1000) - secondsIn30Days;
// Use the data embedded in the page, if available.
let embeddedLastReadTimestamps = false;
if (drupalSettings.history && drupalSettings.history.lastReadTimestamps) {
embeddedLastReadTimestamps = drupalSettings.history.lastReadTimestamps;
}
/**
* @namespace
*/
Drupal.history = {
/**
* Fetch "last read" timestamps for the given nodes.
*
* @param {Array} nodeIDs
* An array of node IDs.
* @param {function} callback
* A callback that is called after the requested timestamps were fetched.
*/
fetchTimestamps(nodeIDs, callback) {
// Use the data embedded in the page, if available.
if (embeddedLastReadTimestamps) {
callback();
return;
}
$.ajax({
url: Drupal.url('history/get_node_read_timestamps'),
type: 'POST',
data: { 'node_ids[]': nodeIDs },
dataType: 'json',
success(results) {
Object.keys(results || {}).forEach(nodeID => {
storage.setItem(
`Drupal.history.${currentUserID}.${nodeID}`,
results[nodeID],
);
});
callback();
},
});
},
/**
* Get the last read timestamp for the given node.
*
* @param {number|string} nodeID
* A node ID.
*
* @return {number}
* A UNIX timestamp.
*/
getLastRead(nodeID) {
// Use the data embedded in the page, if available.
if (embeddedLastReadTimestamps && embeddedLastReadTimestamps[nodeID]) {
return parseInt(embeddedLastReadTimestamps[nodeID], 10);
}
return parseInt(
storage.getItem(`Drupal.history.${currentUserID}.${nodeID}`) || 0,
10,
);
},
/**
* Marks a node as read, store the last read timestamp client-side.
*
* @param {number|string} nodeID
* A node ID.
*/
markAsRead(nodeID) {
$.ajax({
url: Drupal.url(`history/${nodeID}/read`),
type: 'POST',
dataType: 'json',
success(timestamp) {
// If the data is embedded in the page, don't store on the client
// side.
if (
embeddedLastReadTimestamps &&
embeddedLastReadTimestamps[nodeID]
) {
return;
}
storage.setItem(
`Drupal.history.${currentUserID}.${nodeID}`,
timestamp,
);
},
});
},
/**
* Determines whether a server check is necessary.
*
* Any content that is >30 days old never gets a "new" or "updated"
* indicator. Any content that was published before the oldest known reading
* also never gets a "new" or "updated" indicator, because it must've been
* read already.
*
* @param {number|string} nodeID
* A node ID.
* @param {number} contentTimestamp
* The time at which some content (e.g. a comment) was published.
*
* @return {bool}
* Whether a server check is necessary for the given node and its
* timestamp.
*/
needsServerCheck(nodeID, contentTimestamp) {
// First check if the content is older than 30 days, then we can bail
// early.
if (contentTimestamp < thirtyDaysAgo) {
return false;
}
// Use the data embedded in the page, if available.
if (embeddedLastReadTimestamps && embeddedLastReadTimestamps[nodeID]) {
return (
contentTimestamp > parseInt(embeddedLastReadTimestamps[nodeID], 10)
);
}
const minLastReadTimestamp = parseInt(
storage.getItem(`Drupal.history.${currentUserID}.${nodeID}`) || 0,
10,
);
return contentTimestamp > minLastReadTimestamp;
},
};
})(jQuery, Drupal, drupalSettings, window.localStorage);

View file

@ -1,41 +1,23 @@
/**
* @file
* JavaScript API for the History module, with client-side caching.
*
* May only be loaded for authenticated users, with the History module enabled.
*/
* DO NOT EDIT THIS FILE.
* See the following change record for more information,
* https://www.drupal.org/node/2815083
* @preserve
**/
(function ($, Drupal, drupalSettings, storage) {
'use strict';
var currentUserID = parseInt(drupalSettings.user.uid, 10);
// Any comment that is older than 30 days is automatically considered read,
// so for these we don't need to perform a request at all!
var thirtyDaysAgo = Math.round(new Date().getTime() / 1000) - 30 * 24 * 60 * 60;
var secondsIn30Days = 2592000;
var thirtyDaysAgo = Math.round(new Date().getTime() / 1000) - secondsIn30Days;
// Use the data embedded in the page, if available.
var embeddedLastReadTimestamps = false;
if (drupalSettings.history && drupalSettings.history.lastReadTimestamps) {
embeddedLastReadTimestamps = drupalSettings.history.lastReadTimestamps;
}
/**
* @namespace
*/
Drupal.history = {
/**
* Fetch "last read" timestamps for the given nodes.
*
* @param {Array} nodeIDs
* An array of node IDs.
* @param {function} callback
* A callback that is called after the requested timestamps were fetched.
*/
fetchTimestamps: function (nodeIDs, callback) {
// Use the data embedded in the page, if available.
fetchTimestamps: function fetchTimestamps(nodeIDs, callback) {
if (embeddedLastReadTimestamps) {
callback();
return;
@ -44,50 +26,28 @@
$.ajax({
url: Drupal.url('history/get_node_read_timestamps'),
type: 'POST',
data: {'node_ids[]': nodeIDs},
data: { 'node_ids[]': nodeIDs },
dataType: 'json',
success: function (results) {
for (var nodeID in results) {
if (results.hasOwnProperty(nodeID)) {
storage.setItem('Drupal.history.' + currentUserID + '.' + nodeID, results[nodeID]);
}
}
success: function success(results) {
Object.keys(results || {}).forEach(function (nodeID) {
storage.setItem('Drupal.history.' + currentUserID + '.' + nodeID, results[nodeID]);
});
callback();
}
});
},
/**
* Get the last read timestamp for the given node.
*
* @param {number|string} nodeID
* A node ID.
*
* @return {number}
* A UNIX timestamp.
*/
getLastRead: function (nodeID) {
// Use the data embedded in the page, if available.
getLastRead: function getLastRead(nodeID) {
if (embeddedLastReadTimestamps && embeddedLastReadTimestamps[nodeID]) {
return parseInt(embeddedLastReadTimestamps[nodeID], 10);
}
return parseInt(storage.getItem('Drupal.history.' + currentUserID + '.' + nodeID) || 0, 10);
},
/**
* Marks a node as read, store the last read timestamp client-side.
*
* @param {number|string} nodeID
* A node ID.
*/
markAsRead: function (nodeID) {
markAsRead: function markAsRead(nodeID) {
$.ajax({
url: Drupal.url('history/' + nodeID + '/read'),
type: 'POST',
dataType: 'json',
success: function (timestamp) {
// If the data is embedded in the page, don't store on the client
// side.
success: function success(timestamp) {
if (embeddedLastReadTimestamps && embeddedLastReadTimestamps[nodeID]) {
return;
}
@ -96,32 +56,11 @@
}
});
},
/**
* Determines whether a server check is necessary.
*
* Any content that is >30 days old never gets a "new" or "updated"
* indicator. Any content that was published before the oldest known reading
* also never gets a "new" or "updated" indicator, because it must've been
* read already.
*
* @param {number|string} nodeID
* A node ID.
* @param {number} contentTimestamp
* The time at which some content (e.g. a comment) was published.
*
* @return {bool}
* Whether a server check is necessary for the given node and its
* timestamp.
*/
needsServerCheck: function (nodeID, contentTimestamp) {
// First check if the content is older than 30 days, then we can bail
// early.
needsServerCheck: function needsServerCheck(nodeID, contentTimestamp) {
if (contentTimestamp < thirtyDaysAgo) {
return false;
}
// Use the data embedded in the page, if available.
if (embeddedLastReadTimestamps && embeddedLastReadTimestamps[nodeID]) {
return contentTimestamp > parseInt(embeddedLastReadTimestamps[nodeID], 10);
}
@ -130,5 +69,4 @@
return contentTimestamp > minLastReadTimestamp;
}
};
})(jQuery, Drupal, drupalSettings, window.localStorage);
})(jQuery, Drupal, drupalSettings, window.localStorage);

View file

@ -0,0 +1,21 @@
/**
* @file
* Marks the nodes listed in drupalSettings.history.nodesToMarkAsRead as read.
*
* Uses the History module JavaScript API.
*
* @see Drupal.history
*/
(function(window, Drupal, drupalSettings) {
// When the window's "load" event is triggered, mark all enumerated nodes as
// read. This still allows for Drupal behaviors (which are triggered on the
// "DOMContentReady" event) to add "new" and "updated" indicators.
window.addEventListener('load', () => {
if (drupalSettings.history && drupalSettings.history.nodesToMarkAsRead) {
Object.keys(drupalSettings.history.nodesToMarkAsRead).forEach(
Drupal.history.markAsRead,
);
}
});
})(window, Drupal, drupalSettings);

View file

@ -1,23 +1,14 @@
/**
* @file
* Marks the nodes listed in drupalSettings.history.nodesToMarkAsRead as read.
*
* Uses the History module JavaScript API.
*
* @see Drupal.history
*/
* DO NOT EDIT THIS FILE.
* See the following change record for more information,
* https://www.drupal.org/node/2815083
* @preserve
**/
(function (window, Drupal, drupalSettings) {
'use strict';
// When the window's "load" event is triggered, mark all enumerated nodes as
// read. This still allows for Drupal behaviors (which are triggered on the
// "DOMContentReady" event) to add "new" and "updated" indicators.
window.addEventListener('load', function () {
if (drupalSettings.history && drupalSettings.history.nodesToMarkAsRead) {
Object.keys(drupalSettings.history.nodesToMarkAsRead).forEach(Drupal.history.markAsRead);
}
});
})(window, Drupal, drupalSettings);
})(window, Drupal, drupalSettings);

View file

@ -51,7 +51,7 @@ class HistoryController extends ControllerBase {
// Update the history table, stating that this user viewed this node.
history_write($node->id());
return new JsonResponse((int)history_read($node->id()));
return new JsonResponse((int) history_read($node->id()));
}
}

View file

@ -42,6 +42,9 @@ class HistoryUserTimestamp extends Node {
}
}
/**
* {@inheritdoc}
*/
protected function defineOptions() {
$options = parent::defineOptions();
@ -50,6 +53,9 @@ class HistoryUserTimestamp extends Node {
return $options;
}
/**
* {@inheritdoc}
*/
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
parent::buildOptionsForm($form, $form_state);
if (\Drupal::moduleHandler()->moduleExists('comment')) {
@ -61,6 +67,9 @@ class HistoryUserTimestamp extends Node {
}
}
/**
* {@inheritdoc}
*/
public function query() {
// Only add ourselves to the query if logged in.
if (\Drupal::currentUser()->isAnonymous()) {
@ -96,7 +105,7 @@ class HistoryUserTimestamp extends Node {
'#theme' => 'mark',
'#status' => $mark,
];
return $this->renderLink(drupal_render($build), $values);
return $this->renderLink(\Drupal::service('renderer')->render($build), $values);
}
}

View file

@ -20,7 +20,9 @@ class HistoryUserTimestamp extends FilterPluginBase {
use UncacheableDependencyTrait;
// Don't display empty space where the operator would be.
/**
* {@inheritdoc}
*/
public $no_operator = TRUE;
/**
@ -30,6 +32,9 @@ class HistoryUserTimestamp extends FilterPluginBase {
return FALSE;
}
/**
* {@inheritdoc}
*/
public function buildExposeForm(&$form, FormStateInterface $form_state) {
parent::buildExposeForm($form, $form_state);
// @todo There are better ways of excluding required and multiple (object flags)
@ -38,6 +43,9 @@ class HistoryUserTimestamp extends FilterPluginBase {
unset($form['expose']['remember']);
}
/**
* {@inheritdoc}
*/
protected function valueForm(&$form, FormStateInterface $form_state) {
// Only present a checkbox for the exposed filter itself. There's no way
// to tell the difference between not checked and the default value, so
@ -57,6 +65,9 @@ class HistoryUserTimestamp extends FilterPluginBase {
}
}
/**
* {@inheritdoc}
*/
public function query() {
// This can only work if we're authenticated in.
if (!\Drupal::currentUser()->isAuthenticated()) {
@ -70,7 +81,6 @@ class HistoryUserTimestamp extends FilterPluginBase {
// Hey, Drupal kills old history, so nodes that haven't been updated
// since HISTORY_READ_LIMIT are bzzzzzzzt outta here!
$limit = REQUEST_TIME - HISTORY_READ_LIMIT;
$this->ensureMyTable();
@ -79,9 +89,9 @@ class HistoryUserTimestamp extends FilterPluginBase {
$clause = '';
$clause2 = '';
if ($ces = $this->query->ensureTable('comment_entity_statistics', $this->relationship)) {
$clause = ("OR $ces.last_comment_timestamp > (***CURRENT_TIME*** - $limit)");
$clause2 = "OR $field < $ces.last_comment_timestamp";
if ($alias = $this->query->ensureTable('comment_entity_statistics', $this->relationship)) {
$clause = "OR $alias.last_comment_timestamp > (***CURRENT_TIME*** - $limit)";
$clause2 = "OR $field < $alias.last_comment_timestamp";
}
// NULL means a history record doesn't exist. That's clearly new content.
@ -90,6 +100,9 @@ class HistoryUserTimestamp extends FilterPluginBase {
$this->query->addWhereExpression($this->options['group'], "($field IS NULL AND ($node.changed > (***CURRENT_TIME*** - $limit) $clause)) OR $field < $node.changed $clause2");
}
/**
* {@inheritdoc}
*/
public function adminSummary() {
if (!empty($this->options['exposed'])) {
return $this->t('exposed');

View file

@ -4,9 +4,8 @@ namespace Drupal\Tests\history\Functional;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Url;
use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
use Drupal\Tests\BrowserTestBase;
use GuzzleHttp\Cookie\CookieJar;
use Drupal\Tests\system\Functional\Cache\AssertPageCacheContextsAndTagsTrait;
/**
* Tests the History endpoints.
@ -38,20 +37,6 @@ class HistoryTest extends BrowserTestBase {
*/
protected $testNode;
/**
* The cookie jar holding the testing session cookies for Guzzle requests.
*
* @var \GuzzleHttp\Client;
*/
protected $client;
/**
* The Guzzle HTTP client.
*
* @var \GuzzleHttp\Cookie\CookieJar;
*/
protected $cookies;
protected function setUp() {
parent::setUp();
@ -60,8 +45,6 @@ class HistoryTest extends BrowserTestBase {
$this->user = $this->drupalCreateUser(['create page content', 'access content']);
$this->drupalLogin($this->user);
$this->testNode = $this->drupalCreateNode(['type' => 'page', 'uid' => $this->user->id()]);
$this->client = $this->getHttpClient();
}
/**
@ -75,16 +58,14 @@ class HistoryTest extends BrowserTestBase {
*/
protected function getNodeReadTimestamps(array $node_ids) {
// Perform HTTP request.
$http_client = $this->getHttpClient();
$url = Url::fromRoute('history.get_last_node_view')
->setAbsolute()
->toString();
return $this->client->post($url, [
'body' => http_build_query(['node_ids' => $node_ids]),
'cookies' => $this->cookies,
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/x-www-form-urlencoded',
],
return $http_client->request('POST', $url, [
'form_params' => ['node_ids' => $node_ids],
'cookies' => $this->getSessionCookies(),
'http_errors' => FALSE,
]);
}
@ -99,12 +80,11 @@ class HistoryTest extends BrowserTestBase {
* The response body.
*/
protected function markNodeAsRead($node_id) {
$http_client = $this->getHttpClient();
$url = Url::fromRoute('history.read_node', ['node' => $node_id], ['absolute' => TRUE])->toString();
return $this->client->post($url, [
'cookies' => $this->cookies,
'headers' => [
'Accept' => 'application/json',
],
return $http_client->request('POST', $url, [
'cookies' => $this->getSessionCookies(),
'http_errors' => FALSE,
]);
}
@ -156,19 +136,4 @@ class HistoryTest extends BrowserTestBase {
$this->assertEquals(403, $response->getStatusCode());
}
/**
* Obtain the HTTP client and set the cookies.
*
* @return \GuzzleHttp\Client
* The client with BrowserTestBase configuration.
*/
protected function getHttpClient() {
// Similar code is also employed to test CSRF tokens.
// @see \Drupal\Tests\system\Functional\CsrfRequestHeaderTest::testRouteAccess()
$domain = parse_url($this->getUrl(), PHP_URL_HOST);
$session_id = $this->getSession()->getCookie($this->getSessionName());
$this->cookies = CookieJar::fromArray([$this->getSessionName() => $session_id], $domain);
return $this->getSession()->getDriver()->getClient()->getClient();
}
}

View file

@ -1,18 +1,20 @@
<?php
namespace Drupal\history\Tests\Views;
namespace Drupal\Tests\history\Kernel\Views;
use Drupal\node\Entity\Node;
use Drupal\Tests\views\Kernel\ViewsKernelTestBase;
use Drupal\user\Entity\User;
use Drupal\views\Views;
use Drupal\views\Tests\ViewTestBase;
/**
* Tests the history timestamp handlers.
*
* @group history
* @see \Drupal\history\Plugin\views\field\HistoryTimestamp.
* @see \Drupal\history\Plugin\views\filter\HistoryTimestamp.
* @see \Drupal\history\Plugin\views\field\HistoryUserTimestamp
* @see \Drupal\history\Plugin\views\filter\HistoryUserTimestamp
*/
class HistoryTimestampTest extends ViewTestBase {
class HistoryTimestampTest extends ViewsKernelTestBase {
/**
* Modules to enable.
@ -28,16 +30,41 @@ class HistoryTimestampTest extends ViewTestBase {
*/
public static $testViews = ['test_history'];
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
$this->installEntitySchema('node');
$this->installEntitySchema('user');
$this->installSchema('history', ['history']);
// Use classy theme because its marker is wrapped in a span so it can be
// easily targeted with xpath.
\Drupal::service('theme_handler')->install(['classy']);
\Drupal::theme()->setActiveTheme(\Drupal::service('theme.initialization')->initTheme('classy'));
}
/**
* Tests the handlers.
*/
public function testHandlers() {
$nodes = [];
$nodes[] = $this->drupalCreateNode();
$nodes[] = $this->drupalCreateNode();
$node = Node::create([
'title' => 'n1',
'type' => 'default',
]);
$node->save();
$nodes[] = $node;
$node = Node::create([
'title' => 'n2',
'type' => 'default',
]);
$node->save();
$nodes[] = $node;
$account = $this->drupalCreateUser();
$this->drupalLogin($account);
$account = User::create(['name' => 'admin']);
$account->save();
\Drupal::currentUser()->setAccount($account);
db_insert('history')
@ -54,7 +81,6 @@ class HistoryTimestampTest extends ViewTestBase {
'timestamp' => REQUEST_TIME + 100,
])->execute();
$column_map = [
'nid' => 'nid',
];
@ -83,7 +109,6 @@ class HistoryTimestampTest extends ViewTestBase {
$view = Views::getView('test_history');
$view->setDisplay('page_2');
$this->executeView($view);
}
}