diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index 1109452..d42326b 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -2233,6 +2233,19 @@ function drupal_get_user_timezone() {
}
/**
+ * Gets a salt useful for hardening against SQL injection.
+ *
+ * @return
+ * A salt based on information in settings.php, not in the database.
+ */
+function drupal_get_hash_salt() {
+ global $drupal_hash_salt, $databases;
+ // If the $drupal_hash_salt variable is empty, a hash of the serialized
+ // database credentials is used as a fallback salt.
+ return empty($drupal_hash_salt) ? hash('sha256', serialize($databases)) : $drupal_hash_salt;
+}
+
+/**
* Provides custom PHP error handling.
*
* @param $error_level
@@ -2591,7 +2604,6 @@ function typed_data() {
* HMAC and timestamp.
*/
function drupal_valid_test_ua($new_prefix = NULL) {
- global $drupal_hash_salt;
static $test_prefix;
if (isset($new_prefix)) {
@@ -2607,7 +2619,7 @@ function drupal_valid_test_ua($new_prefix = NULL) {
// We use the salt from settings.php to make the HMAC key, since
// the database is not yet initialized and we can't access any Drupal variables.
// The file properties add more entropy not easily accessible to others.
- $key = $drupal_hash_salt . filectime(__FILE__) . fileinode(__FILE__);
+ $key = drupal_get_hash_salt() . filectime(__FILE__) . fileinode(__FILE__);
$time_diff = REQUEST_TIME - $time;
// Since we are making a local request a 5 second time window is allowed,
// and the HMAC must match.
@@ -2663,14 +2675,13 @@ function _drupal_load_test_overrides($test_prefix) {
* Generates a user agent string with a HMAC and timestamp for simpletest.
*/
function drupal_generate_test_ua($prefix) {
- global $drupal_hash_salt;
static $key;
if (!isset($key)) {
// We use the salt from settings.php to make the HMAC key, since
// the database is not yet initialized and we can't access any Drupal variables.
// The file properties add more entropy not easily accessible to others.
- $key = $drupal_hash_salt . filectime(__FILE__) . fileinode(__FILE__);
+ $key = drupal_get_hash_salt() . filectime(__FILE__) . fileinode(__FILE__);
}
// Generate a moderately secure HMAC based on the database credentials.
$salt = uniqid('', TRUE);
@@ -3186,7 +3197,7 @@ function drupal_classloader($class_loader = NULL) {
}
if ($class_loader === 'apc') {
require_once DRUPAL_ROOT . '/core/vendor/symfony/class-loader/Symfony/Component/ClassLoader/ApcClassLoader.php';
- $apc_loader = new ApcClassLoader('drupal.' . $GLOBALS['drupal_hash_salt'], $loader);
+ $apc_loader = new ApcClassLoader('drupal.' . drupal_get_hash_salt(), $loader);
$apc_loader->register();
}
else {
diff --git a/core/includes/common.inc b/core/includes/common.inc
index fbacae6..e795734 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -4842,19 +4842,6 @@ function drupal_json_decode($var) {
}
/**
- * Gets a salt useful for hardening against SQL injection.
- *
- * @return
- * A salt based on information in settings.php, not in the database.
- */
-function drupal_get_hash_salt() {
- global $drupal_hash_salt, $databases;
- // If the $drupal_hash_salt variable is empty, a hash of the serialized
- // database credentials is used as a fallback salt.
- return empty($drupal_hash_salt) ? hash('sha256', serialize($databases)) : $drupal_hash_salt;
-}
-
-/**
* Ensures the private key variable used to generate tokens is set.
*
* @return
@@ -4876,8 +4863,10 @@ function drupal_get_private_key() {
*
* @return string
* A 43-character URL-safe token for validation, based on the user session ID,
- * the global $drupal_hash_salt variable from settings.php, and the
+ * the hash salt provided from drupal_get_hash_salt(), and the
* 'drupal_private_key' configuration variable.
+ *
+ * @see drupal_get_hash_salt()
*/
function drupal_get_token($value = '') {
return drupal_hmac_base64($value, session_id() . drupal_get_private_key() . drupal_get_hash_salt());
diff --git a/core/lib/Drupal/Component/PhpStorage/PhpStorageFactory.php b/core/lib/Drupal/Component/PhpStorage/PhpStorageFactory.php
index 2952488..c82cfcd 100644
--- a/core/lib/Drupal/Component/PhpStorage/PhpStorageFactory.php
+++ b/core/lib/Drupal/Component/PhpStorage/PhpStorageFactory.php
@@ -41,7 +41,7 @@ static function get($name) {
else {
$configuration = array(
'class' => 'Drupal\Component\PhpStorage\MTimeProtectedFileStorage',
- 'secret' => $GLOBALS['drupal_hash_salt'],
+ 'secret' => drupal_get_hash_salt(),
);
}
$class = isset($configuration['class']) ? $configuration['class'] : 'Drupal\Component\PhpStorage\MTimeProtectedFileStorage';
diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php
index 785c81c..5e98001 100644
--- a/core/lib/Drupal/Core/Entity/Entity.php
+++ b/core/lib/Drupal/Core/Entity/Entity.php
@@ -257,10 +257,9 @@ public function getIterator() {
* Implements \Drupal\Core\TypedData\AccessibleInterface::access().
*/
public function access($operation = 'view', \Drupal\user\Plugin\Core\Entity\User $account = NULL) {
- $method = $operation . 'Access';
return drupal_container()->get('plugin.manager.entity')
->getAccessController($this->entityType)
- ->$method($this, LANGUAGE_DEFAULT, $account);
+ ->access($this, $operation, LANGUAGE_DEFAULT, $account);
}
/**
diff --git a/core/lib/Drupal/Core/Entity/EntityAccessController.php b/core/lib/Drupal/Core/Entity/EntityAccessController.php
index 423281f..88e1479 100644
--- a/core/lib/Drupal/Core/Entity/EntityAccessController.php
+++ b/core/lib/Drupal/Core/Entity/EntityAccessController.php
@@ -22,55 +22,51 @@ class EntityAccessController implements EntityAccessControllerInterface {
protected $accessCache = array();
/**
- * Implements \Drupal\Core\Entity\EntityAccessControllerInterface::viewAccess().
+ * Implements \Drupal\Core\Entity\EntityAccessControllerInterface::access().
+ *
+ * This method provides result caching to prevent redundant computation.
+ * To utilize this, extending classes should not override access(),
+ * but instead implement the checkAccess() method as below.
*/
- public function viewAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
- if (($access = $this->getCache($entity, 'view', $langcode, $account)) !== NULL) {
+ public function access(EntityInterface $entity, $operation, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
+ if (($access = $this->getCache($entity, $operation, $langcode, $account)) !== NULL) {
+ // Cache hit, no work necessary.
return $access;
}
- $access = (bool) $this->access($entity, 'view', $langcode, $account);
- return $this->setCache($access, $entity, 'view', $langcode, $account);
- }
+ // Invoke hook_entity_access(), hook results take precedence over overridden
+ // implementations of EntityAccessController::checkAccess(). Entities
+ // that have checks that need to be done before the hook is invoked should
+ // do so by overridding this method.
- /**
- * Implements \Drupal\Core\Entity\EntityAccessControllerInterface::createAccess().
- */
- public function createAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
- if (($access = $this->getCache($entity, 'create', $langcode, $account)) !== NULL) {
- return $access;
+ // @todo Remove this once we can rely on $account.
+ if (!$account) {
+ $account = user_load($GLOBALS['user']->uid);
}
- $access = (bool) $this->access($entity, 'create', $langcode, $account);
- return $this->setCache($access, $entity, 'create', $langcode, $account);
- }
+ // We grant access to the entity if both of these conditions are met:
+ // - No modules say to deny access.
+ // - At least one module says to grant access.
+ $access = module_invoke_all($entity->entityType() . '_access', $entity, $operation, $account, $langcode);
- /**
- * Implements \Drupal\Core\Entity\EntityAccessControllerInterface::updateAccess().
- */
- public function updateAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
- if (($access = $this->getCache($entity, 'update', $langcode, $account)) !== NULL) {
- return $access;
+ if (in_array(FALSE, $access, TRUE)) {
+ $return = FALSE;
}
-
- $access = (bool) $this->access($entity, 'update', $langcode, $account);
- return $this->setCache($access, $entity, 'update', $langcode, $account);
- }
-
- /**
- * Implements \Drupal\Core\Entity\EntityAccessControllerInterface::deleteAccess().
- */
- public function deleteAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
- if (($access = $this->getCache($entity, 'delete', $langcode, $account)) !== NULL) {
- return $access;
+ elseif (in_array(TRUE, $access, TRUE)) {
+ $return = TRUE;
}
-
- $access = (bool) $this->access($entity, 'delete', $langcode, $account);
- return $this->setCache($access, $entity, 'delete', $langcode, $account);
+ else {
+ // No result from hook, so entity checks are done.
+ $return = (bool) $this->checkAccess($entity, $operation, $langcode, $account);
+ }
+ return $this->setCache($return, $entity, $operation, $langcode, $account);
}
/**
- * Performs default, shared access checks.
+ * Performs access checks.
+ *
+ * This method is supposed to be overwritten by extending classes that
+ * do their own custom access checking.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity for which to check 'create' access.
@@ -88,22 +84,8 @@ public function deleteAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAU
* TRUE if access was granted, FALSE if access was denied and NULL if access
* could not be determined.
*/
- protected function access(EntityInterface $entity, $operation, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
- // @todo Remove this once we can rely on $account.
- if (!$account) {
- $account = user_load($GLOBALS['user']->uid);
- }
-
- // We grant access to the entity if both of these conditions are met:
- // - No modules say to deny access.
- // - At least one module says to grant access.
- $access = module_invoke_all($entity->entityType() . '_access', $entity, $operation, $account, $langcode);
- if (in_array(FALSE, $access, TRUE)) {
- return FALSE;
- }
- elseif (in_array(TRUE, $access, TRUE)) {
- return TRUE;
- }
+ protected function checkAccess(EntityInterface $entity, $operation, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
+ return NULL;
}
/**
diff --git a/core/lib/Drupal/Core/Entity/EntityAccessControllerInterface.php b/core/lib/Drupal/Core/Entity/EntityAccessControllerInterface.php
index 66730f6..ddf3dfd 100644
--- a/core/lib/Drupal/Core/Entity/EntityAccessControllerInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityAccessControllerInterface.php
@@ -16,10 +16,13 @@
interface EntityAccessControllerInterface {
/**
- * Checks 'view' access for a given entity or entity translation.
+ * Checks access to an operation on a given entity or entity translation.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
- * The entity for which to check 'view' access.
+ * The entity for which to check access.
+ * @param string $operation
+ * The operation access should be checked for.
+ * Usually one of "view", "create", "update" or "delete".
* @param string $langcode
* (optional) The language code for which to check access. Defaults to
* LANGUAGE_DEFAULT.
@@ -30,61 +33,11 @@
* @return bool
* TRUE if access was granted, FALSE otherwise.
*/
- public function viewAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL);
-
- /**
- * Checks 'create' access for a given entity or entity translation.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- * The entity for which to check 'create' access.
- * @param string $langcode
- * (optional) The language code for which to check access. Defaults to
- * LANGUAGE_DEFAULT.
- * @param \Drupal\user\Plugin\Core\Entity\User $account
- * (optional) The user for which to check access, or NULL to check access
- * for the current user. Defaults to NULL.
- *
- * @return bool
- * TRUE if access was granted, FALSE otherwise.
- */
- public function createAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL);
-
- /**
- * Checks 'update' access for a given entity or entity translation.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- * The entity to check 'update' access.
- * @param string $langcode
- * (optional) The language code for which to check access. Defaults to
- * LANGUAGE_DEFAULT.
- * @param \Drupal\user\Plugin\Core\Entity\User $account
- * (optional) The user for which to check access, or NULL to check access
- * for the current user. Defaults to NULL.
- *
- * @return bool
- * TRUE if access was granted, FALSE otherwise.
- */
- public function updateAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL);
-
- /**
- * Checks 'delete' access for a given entity or entity translation.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- * The entity for which to check 'delete' access.
- * @param string $langcode
- * (optional) The language code for which to check access. Defaults to
- * LANGUAGE_DEFAULT.
- * @param \Drupal\user\Plugin\Core\Entity\User $account
- * (optional) The user for which to check access, or NULL to check access
- * for the current user. Defaults to NULL.
- *
- * @return bool
- * TRUE if access was granted, FALSE otherwise.
- */
- public function deleteAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL);
+ public function access(EntityInterface $entity, $operation, $langcode = LANGUAGE_DEFAULT, User $account = NULL);
/**
* Clears all cached access checks.
*/
public function resetCache();
+
}
diff --git a/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php b/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php
index 97a5627..b34f3d7 100644
--- a/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php
+++ b/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php
@@ -196,7 +196,6 @@ public function isEmpty() {
* Implements \Drupal\Core\TypedData\AccessibleInterface::access().
*/
public function access($operation = 'view', \Drupal\user\Plugin\Core\Entity\User $account = NULL) {
- $method = $operation . 'Access';
// Determine the language code of this translation by cutting of the
// leading "@" from the property name to get the langcode.
// @todo Add a way to set and get the langcode so that's more obvious what
@@ -204,6 +203,6 @@ public function access($operation = 'view', \Drupal\user\Plugin\Core\Entity\User
$langcode = substr($this->getName(), 1);
return drupal_container()->get('plugin.manager.entity')
->getAccessController($this->parent->entityType())
- ->$method($this->parent, $langcode, $account);
+ ->access($this->parent, $operation, $langcode, $account);
}
}
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockAccessController.php b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockAccessController.php
index 014d54f..ffa6973 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockAccessController.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockAccessController.php
@@ -17,31 +17,18 @@
class CustomBlockAccessController extends EntityAccessController {
/**
- * Implements EntityAccessControllerInterface::viewAccess().
+ * Overrides \Drupal\Core\Entity\EntityAccessControllerInterface::checkAccess().
*/
- public function viewAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
- return TRUE;
- }
-
- /**
- * Implements EntityAccessControllerInterface::createAccess().
- */
- public function createAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
- return user_access('administer blocks', $account);
- }
-
- /**
- * Implements EntityAccessControllerInterface::updateAccess().
- */
- public function updateAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
- return user_access('administer blocks', $account);
- }
-
- /**
- * Implements EntityAccessControllerInterface::deleteAccess().
- */
- public function deleteAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
- return user_access('administer blocks', $account);
+ public function checkAccess(EntityInterface $entity, $operation, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
+ if ($operation === 'view') {
+ return TRUE;
+ }
+ elseif (in_array($operation, array('create', 'update', 'delete'))) {
+ return user_access('administer blocks', $account);
+ }
+ else {
+ return NULL;
+ }
}
}
diff --git a/core/modules/block/lib/Drupal/block/BlockAccessController.php b/core/modules/block/lib/Drupal/block/BlockAccessController.php
index 8e8a563..ea5889e 100644
--- a/core/modules/block/lib/Drupal/block/BlockAccessController.php
+++ b/core/modules/block/lib/Drupal/block/BlockAccessController.php
@@ -17,10 +17,12 @@
class BlockAccessController extends EntityAccessController {
/**
- * Overrides \Drupal\Core\Entity\EntityAccessController::viewAccess().
+ * Overrides \Drupal\Core\Entity\EntityAccessController::checkAccess().
*/
- public function viewAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
- return $entity->getPlugin()->access();
+ public function checkAccess(EntityInterface $entity, $operation, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
+ if ($operation === 'view') {
+ return $entity->getPlugin()->access();
+ }
}
}
diff --git a/core/modules/node/lib/Drupal/node/NodeAccessController.php b/core/modules/node/lib/Drupal/node/NodeAccessController.php
index ec68b7e..210f570 100644
--- a/core/modules/node/lib/Drupal/node/NodeAccessController.php
+++ b/core/modules/node/lib/Drupal/node/NodeAccessController.php
@@ -18,39 +18,22 @@
class NodeAccessController extends EntityAccessController {
/**
- * Overrides \Drupal\Core\Entity\EntityAccessController::viewAccess().
- */
- public function viewAccess(EntityInterface $node, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
- if (($cached = $this->getCache($node, 'view', $langcode, $account)) !== NULL ) {
- return $cached;
- }
-
- if (($access = $this->access($node, 'view', $langcode, $account)) !== NULL) {
- return $this->setCache((bool) $access, $node, 'view', $langcode, $account);
- };
-
- // If no modules implement hook_node_grants(), the default behavior is to
- // allow all users to view published nodes, so reflect that here.
- $status = $node instanceof EntityNG ? $node->getTranslation($langcode, FALSE)->status->value : $node->status;
- return $this->setCache($status, $node, 'view', $langcode, $account);
- }
-
- /**
* Overrides \Drupal\Core\Entity\EntityAccessController::access().
*/
- protected function access(EntityInterface $node, $operation, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
+ public function access(EntityInterface $entity, $operation, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
if (user_access('bypass node access', $account)) {
return TRUE;
}
-
if (!user_access('access content', $account)) {
return FALSE;
}
+ return parent::access($entity, $operation, $langcode, $account);
+ }
- if (($access = parent::access($node, $operation, $langcode, $account)) !== NULL) {
- return (bool) $access;
- };
-
+ /**
+ * Overrides \Drupal\Core\Entity\EntityAccessController::checkAccess().
+ */
+ protected function checkAccess(EntityInterface $node, $operation, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
// Fetch information from the node object if possible.
$status = isset($node->status) ? $node->status : NULL;
$uid = isset($node->uid) ? $node->uid : NULL;
@@ -60,12 +43,13 @@ protected function access(EntityInterface $node, $operation, $langcode = LANGUAG
$uid = $node->getTranslation($langcode, FALSE)->uid->value;
}
+ // @todo Remove this once we can rely on $account.
+ if (!$account) {
+ $account = user_load($GLOBALS['user']->uid);
+ }
+
// Check if authors can view their own unpublished nodes.
- if ($operation == 'view' && !$status && user_access('view own unpublished content', $account)) {
- // @todo Remove this once we can rely on $account.
- if (!$account) {
- $account = user_load($GLOBALS['user']->uid);
- }
+ if ($operation === 'view' && !$status && user_access('view own unpublished content', $account)) {
if ($account->id() != 0 && $account->id() == $uid) {
return TRUE;
@@ -75,14 +59,20 @@ protected function access(EntityInterface $node, $operation, $langcode = LANGUAG
// If no module specified either allow or deny, we fall back to the
// node_access table.
if (($grants = $this->accessGrants($node, $operation, $langcode, $account)) !== NULL) {
- return (bool) $grants;
+ return $grants;
+ }
+
+ // If no modules implement hook_node_grants(), the default behavior is to
+ // allow all users to view published nodes, so reflect that here.
+ if ($operation === 'view') {
+ return $status;
}
}
/**
* Determines access to nodes based on node grants.
*
- * @param \Drupal\Core\Entity\EntityInterface $entity
+ * @param \Drupal\Core\Entity\EntityInterface $node
* The entity for which to check 'create' access.
* @param string $operation
* The entity operation. Usually one of 'view', 'edit', 'create' or
diff --git a/core/modules/node/lib/Drupal/node/Tests/Views/RowPluginTest.php b/core/modules/node/lib/Drupal/node/Tests/Views/RowPluginTest.php
new file mode 100644
index 0000000..6937887
--- /dev/null
+++ b/core/modules/node/lib/Drupal/node/Tests/Views/RowPluginTest.php
@@ -0,0 +1,170 @@
+ 'Node: Row plugin',
+ 'description' => 'Tests the node row plugin.',
+ 'group' => 'Views module integration',
+ );
+ }
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->drupalCreateContentType(array('type' => 'article'));
+
+ // Create two nodes, with 5 comments on all of them.
+ for ($i = 0; $i < 2; $i++) {
+ $this->nodes[] = $this->drupalCreateNode(
+ array(
+ 'type' => 'article',
+ 'body' => array(
+ array(
+ 'value' => $this->randomName(42),
+ 'format' => filter_default_format(),
+ 'summary' => $this->randomName(),
+ ),
+ ),
+ )
+ );
+ }
+
+ foreach ($this->nodes as $node) {
+ for ($i = 0; $i < 5; $i++) {
+ $this->comments[$node->id()][] = $this->drupalCreateComment(array('nid' => $node->id()));
+ }
+ }
+ }
+
+ /**
+ * Helper function to create a random comment.
+ *
+ * @param array $settings
+ * (optional) An associative array of settings for the comment, as used in
+ * entity_create().
+ *
+ * @return \Drupal\comment\Plugin\Core\Entity\Comment
+ * Returns the created and saved comment.
+ */
+ public function drupalCreateComment(array $settings = array()) {
+ $node = node_load($settings['nid']);
+ $settings += array(
+ 'subject' => $this->randomName(),
+ 'node_type' => "comment_node_{$node->bundle()}",
+ 'comment_body' => $this->randomName(40),
+ );
+
+ $comment = entity_create('comment', $settings);
+ $comment->save();
+ return $comment;
+ }
+
+ /**
+ * Tests the node row plugin.
+ */
+ public function testRowPlugin() {
+ $view = views_get_view('test_node_row_plugin');
+ $view->initDisplay();
+ $view->setDisplay('page_1');
+ $view->initStyle();
+ $view->rowPlugin->options['view_mode'] = 'full';
+
+ // Test with view_mode full.
+ $output = $view->preview();
+ foreach ($this->nodes as $node) {
+ $body = $node->body;
+ $teaser = $body[LANGUAGE_NOT_SPECIFIED][0]['summary'];
+ $full = $body[LANGUAGE_NOT_SPECIFIED][0]['value'];
+ $this->assertFalse(strpos($output, $teaser) !== FALSE, 'Make sure the teaser appears in the output of the view.');
+ $this->assertTrue(strpos($output, $full) !== FALSE, 'Make sure the full text appears in the output of the view.');
+ }
+
+ // Test with teasers.
+ $view->rowPlugin->options['view_mode'] = 'teaser';
+ $output = $view->preview();
+ foreach ($this->nodes as $node) {
+ $body = $node->body;
+ $teaser = $body[LANGUAGE_NOT_SPECIFIED][0]['summary'];
+ $full = $body[LANGUAGE_NOT_SPECIFIED][0]['value'];
+ $this->assertTrue(strpos($output, $teaser) !== FALSE, 'Make sure the teaser appears in the output of the view.');
+ $this->assertFalse(strpos($output, $full) !== FALSE, 'Make sure the full text does not appears in the output of the view if teaser is set as viewmode.');
+ }
+
+ // Test with links disabled.
+ $view->rowPlugin->options['links'] = FALSE;
+ $output = $view->preview();
+ $this->drupalSetContent($output);
+ foreach ($this->nodes as $node) {
+ $this->assertFalse($this->xpath('//li[contains(@class, :class)]/a[contains(@href, :href)]', array(':class' => 'node-readmore', ':href' => "node/{$node->id()}")), 'Make sure no readmore link appears.');
+ }
+
+ // Test with links enabled.
+ $view->rowPlugin->options['links'] = TRUE;
+ $output = $view->preview();
+ $this->drupalSetContent($output);
+ foreach ($this->nodes as $node) {
+ $this->assertTrue($this->xpath('//li[contains(@class, :class)]/a[contains(@href, :href)]', array(':class' => 'node-readmore', ':href' => "node/{$node->id()}")), 'Make sure no readmore link appears.');
+ }
+
+ // Test with comments enabled.
+ $view->rowPlugin->options['comments'] = TRUE;
+ $output = $view->preview();
+ foreach ($this->nodes as $node) {
+ foreach ($this->comments[$node->id()] as $comment) {
+ $this->assertTrue(strpos($output, $comment->comment_body->value) !== FALSE, 'Make sure the comment appears in the output.');
+ }
+ }
+
+ // Test with comments disabled.
+ $view->rowPlugin->options['comments'] = FALSE;
+ $output = $view->preview();
+ foreach ($this->nodes as $node) {
+ foreach ($this->comments[$node->id()] as $comment) {
+ $this->assertFalse(strpos($output, $comment->comment_body->value) !== FALSE, 'Make sure the comment does not appears in the output when the comments option disabled.');
+ }
+ }
+ }
+
+}
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 36dcfe0..3182939 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -2539,8 +2539,7 @@ function node_access($op, $node, $account = NULL, $langcode = NULL) {
$account = user_load($account->uid);
}
- $method = $op . 'Access';
- return entity_access_controller('node')->$method($node, $langcode, $account);
+ return entity_access_controller('node')->access($node, $op, $langcode, $account);
}
/**
diff --git a/core/modules/node/tests/modules/node_test_views/test_views/views.view.test_node_row_plugin.yml b/core/modules/node/tests/modules/node_test_views/test_views/views.view.test_node_row_plugin.yml
new file mode 100644
index 0000000..bdd49b6
--- /dev/null
+++ b/core/modules/node/tests/modules/node_test_views/test_views/views.view.test_node_row_plugin.yml
@@ -0,0 +1,56 @@
+base_field: nid
+base_table: node
+core: 8
+description: ''
+status: '1'
+display:
+ default:
+ display_options:
+ access:
+ type: perm
+ cache:
+ type: none
+ exposed_form:
+ type: basic
+ filters:
+ status:
+ expose:
+ operator: '0'
+ field: status
+ group: '1'
+ id: status
+ table: node
+ value: '1'
+ plugin_id: boolean
+ pager:
+ options:
+ items_per_page: '10'
+ type: full
+ query:
+ type: views_query
+ row:
+ options:
+ build_mode: teaser
+ comments: '0'
+ links: '1'
+ type: node
+ sorts: { }
+ style:
+ type: default
+ title: test_node_row_plugin
+ display_plugin: default
+ display_title: Master
+ id: default
+ position: { }
+ page_1:
+ display_options:
+ path: test-node-row-plugin
+ display_plugin: page
+ display_title: Page
+ id: page_1
+ position: { }
+human_name: test_node_row_plugin
+langcode: en
+module: views
+id: test_node_row_plugin
+tag: default
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
index 35af531..a67d091 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
@@ -867,6 +867,9 @@ protected function prepareEnvironment() {
// Ensure that the current session is not changed by the new environment.
drupal_save_session(FALSE);
+ // Run all tests as a anonymous user by default, web tests will replace that
+ // during the test set up.
+ $user = drupal_anonymous_user();
// Save and clean the shutdown callbacks array because it is static cached
// and will be changed by the test run. Otherwise it will contain callbacks
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
index 4e7b6b1..7b50b10 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
@@ -775,10 +775,6 @@ protected function setUp() {
),
);
- // Replace the global $user session with an anonymous user to resemble a
- // regular installation.
- $user = drupal_anonymous_user();
-
// Reset the static batch to remove Simpletest's batch operations.
$batch = &batch_get();
$batch = array();
diff --git a/core/modules/system/lib/Drupal/system/Tests/DrupalKernel/DrupalKernelTest.php b/core/modules/system/lib/Drupal/system/Tests/DrupalKernel/DrupalKernelTest.php
index 0b08def..2b90e18 100644
--- a/core/modules/system/lib/Drupal/system/Tests/DrupalKernel/DrupalKernelTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/DrupalKernel/DrupalKernelTest.php
@@ -33,7 +33,7 @@ function setUp() {
'bin' => 'service_container',
'class' => 'Drupal\Component\PhpStorage\MTimeProtectedFileStorage',
'directory' => DRUPAL_ROOT . '/' . $this->public_files_directory . '/php',
- 'secret' => $GLOBALS['drupal_hash_salt'],
+ 'secret' => drupal_get_hash_salt(),
);
// Use a non-persistent cache to avoid queries to non-existing tables.
$this->settingsSet('cache', array('default' => 'cache.backend.memory'));
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php
index 0642032..c29e4f3 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php
@@ -89,6 +89,10 @@ function testEntityAccess() {
* Ensures that the default controller is used as a fallback.
*/
function testEntityAccessDefaultController() {
+ // The implementation requires that the global user id can be loaded.
+ global $user;
+ $user = $this->createUser(array('uid' => 2));
+
// Check that the default access controller is used for entities that don't
// have a specific access controller defined.
$controller = $this->container->get('plugin.manager.entity')->getAccessController('entity_test_default_access');
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestAccessController.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestAccessController.php
index b9e9361..382f37c 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestAccessController.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestAccessController.php
@@ -17,34 +17,21 @@
class EntityTestAccessController extends EntityAccessController {
/**
- * Implements \Drupal\Core\Entity\EntityAccessControllerInterface::viewAccess().
+ * Overrides EntityAccessControllerInterface::checkAccess().
*/
- public function viewAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
- if ($langcode != LANGUAGE_DEFAULT) {
- return user_access('view test entity translations', $account);
+ public function checkAccess(EntityInterface $entity, $operation, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
+ if ($operation === 'view') {
+ if ($langcode != LANGUAGE_DEFAULT) {
+ return user_access('view test entity translations', $account);
+ }
+ return user_access('view test entity', $account);
+ }
+ elseif (in_array($operation, array('create', 'update', 'delete'))) {
+ return user_access('administer entity_test content', $account);
+ }
+ else {
+ return NULL;
}
- return user_access('view test entity', $account);
- }
-
- /**
- * Implements \Drupal\Core\Entity\EntityAccessControllerInterface::createAccess().
- */
- public function createAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
- return user_access('administer entity_test content', $account);
- }
-
- /**
- * Implements \Drupal\Core\Entity\EntityAccessControllerInterface::updateAccess().
- */
- public function updateAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
- return user_access('administer entity_test content', $account);
- }
-
- /**
- * Implements \Drupal\Core\Entity\EntityAccessControllerInterface::deleteAccess().
- */
- public function deleteAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
- return user_access('administer entity_test content', $account);
}
}
diff --git a/core/modules/system/tests/modules/system_mail_failure_test/lib/Drupal/system_mail_failure_test/TestPhpMailFailure.php b/core/modules/system/tests/modules/system_mail_failure_test/lib/Drupal/system_mail_failure_test/TestPhpMailFailure.php
new file mode 100644
index 0000000..a719d7a
--- /dev/null
+++ b/core/modules/system/tests/modules/system_mail_failure_test/lib/Drupal/system_mail_failure_test/TestPhpMailFailure.php
@@ -0,0 +1,31 @@
+set('interface.default', 'Drupal\system_mail_failure_test\TestPhpMailFailure')->save();
+ * @endcode
+ */
+class TestPhpMailFailure extends PhpMail implements MailInterface {
+
+ /**
+ * Overrides Drupal\Core\Mail\PhpMail::mail().
+ */
+ public function mail(array $message) {
+ // Instead of attempting to send a message, just return failure.
+ return FALSE;
+ }
+}
diff --git a/core/modules/system/tests/modules/system_mail_failure_test/system_mail_failure_test.info.yml b/core/modules/system/tests/modules/system_mail_failure_test/system_mail_failure_test.info.yml
new file mode 100644
index 0000000..6f683a0
--- /dev/null
+++ b/core/modules/system/tests/modules/system_mail_failure_test/system_mail_failure_test.info.yml
@@ -0,0 +1,6 @@
+name: 'System mail failure test'
+description: 'Provides a malfunctioning mail service for testing purposes.'
+package: Testing
+version: VERSION
+core: 8.x
+hidden: true
diff --git a/core/modules/system/tests/modules/system_mail_failure_test/system_mail_failure_test.module b/core/modules/system/tests/modules/system_mail_failure_test/system_mail_failure_test.module
new file mode 100644
index 0000000..6acb6c4
--- /dev/null
+++ b/core/modules/system/tests/modules/system_mail_failure_test/system_mail_failure_test.module
@@ -0,0 +1,7 @@
+bundle()}", $account) || user_access('administer taxonomy', $account);
- }
-
- /**
- * Implements \Drupal\Core\Entity\EntityAccessControllerInterface::deleteAccess().
- */
- public function deleteAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
- return user_access("delete terms in {$entity->bundle()}", $account) || user_access('administer taxonomy', $account);
+ public function checkAccess(EntityInterface $entity, $operation, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
+ switch ($operation) {
+ case 'view':
+ return user_access('access content', $account);
+ break;
+
+ case 'create':
+ return user_access('administer taxonomy', $account);
+ break;
+
+ case 'update':
+ return user_access("update terms in {$entity->bundle()}", $account) || user_access('administer taxonomy', $account);
+ break;
+
+ case 'delete':
+ return user_access("delete terms in {$entity->bundle()}", $account) || user_access('administer taxonomy', $account);
+ break;
+ }
}
}
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyAccessController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyAccessController.php
index b1ec119..b192ba1 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyAccessController.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyAccessController.php
@@ -19,30 +19,9 @@
class VocabularyAccessController extends EntityAccessController {
/**
- * Implements \Drupal\Core\Entity\EntityAccessControllerInterface::viewAccess().
+ * Overrides \Drupal\Core\Entity\EntityAccessControllerInterface::checkAccess().
*/
- public function viewAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
- return user_access('administer taxonomy', $account);
- }
-
- /**
- * Implements \Drupal\Core\Entity\EntityAccessControllerInterface::createAccess().
- */
- public function createAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
- return user_access('administer taxonomy', $account);
- }
-
- /**
- * Implements \Drupal\Core\Entity\EntityAccessControllerInterface::updateAccess().
- */
- public function updateAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
- return user_access('administer taxonomy', $account);
- }
-
- /**
- * Implements \Drupal\Core\Entity\EntityAccessControllerInterface::deleteAccess().
- */
- public function deleteAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
+ public function checkAccess(EntityInterface $entity, $operation, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
return user_access('administer taxonomy', $account);
}
diff --git a/core/modules/user/lib/Drupal/user/RegisterFormController.php b/core/modules/user/lib/Drupal/user/RegisterFormController.php
index 2a84412..f84a3df 100644
--- a/core/modules/user/lib/Drupal/user/RegisterFormController.php
+++ b/core/modules/user/lib/Drupal/user/RegisterFormController.php
@@ -131,13 +131,14 @@ public function save(array $form, array &$form_state) {
}
else {
$op = $notify ? 'register_admin_created' : 'register_no_approval_required';
- _user_mail_notify($op, $account);
- if ($notify) {
- drupal_set_message(t('A welcome message with further instructions has been e-mailed to the new user %name.', array('@url' => url($uri['path'], $uri['options']), '%name' => $account->name)));
- }
- else {
- drupal_set_message(t('A welcome message with further instructions has been sent to your e-mail address.'));
- $form_state['redirect'] = '';
+ if (_user_mail_notify($op, $account)) {
+ if ($notify) {
+ drupal_set_message(t('A welcome message with further instructions has been e-mailed to the new user %name.', array('@url' => url($uri['path'], $uri['options']), '%name' => $account->name)));
+ }
+ else {
+ drupal_set_message(t('A welcome message with further instructions has been sent to your e-mail address.'));
+ $form_state['redirect'] = '';
+ }
}
}
}
diff --git a/core/modules/user/lib/Drupal/user/Tests/UserCreateFailMailTest.php b/core/modules/user/lib/Drupal/user/Tests/UserCreateFailMailTest.php
new file mode 100644
index 0000000..eb37e52
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/Tests/UserCreateFailMailTest.php
@@ -0,0 +1,55 @@
+ 'User create with failed mail function',
+ 'description' => 'Test the create user administration page.',
+ 'group' => 'User',
+ );
+ }
+
+ /**
+ * Tests the create user administration page.
+ */
+ protected function testUserAdd() {
+ $user = $this->drupalCreateUser(array('administer users'));
+ $this->drupalLogin($user);
+
+ // Replace the mail functionality with a fake, malfunctioning service.
+ config('system.mail')->set('interface.default', 'Drupal\system_mail_failure_test\TestPhpMailFailure')->save();
+ // Create a user, but fail to send an email.
+ $name = $this->randomName();
+ $edit = array(
+ 'name' => $name,
+ 'mail' => $this->randomName() . '@example.com',
+ 'pass[pass1]' => $pass = $this->randomString(),
+ 'pass[pass2]' => $pass,
+ 'notify' => TRUE,
+ );
+ $this->drupalPost('admin/people/create', $edit, t('Create new account'));
+
+ $this->assertText(t('Unable to send e-mail. Contact the site administrator if the problem persists.'));
+ $this->assertNoText(t('A welcome message with further instructions has been e-mailed to the new user @name.', array('@name' => $edit['name'])));
+ }
+}
diff --git a/core/modules/user/lib/Drupal/user/UserAccessController.php b/core/modules/user/lib/Drupal/user/UserAccessController.php
index 13b8c6b..eabf9b4 100644
--- a/core/modules/user/lib/Drupal/user/UserAccessController.php
+++ b/core/modules/user/lib/Drupal/user/UserAccessController.php
@@ -17,14 +17,51 @@
class UserAccessController extends EntityAccessController {
/**
- * Implements EntityAccessControllerInterface::viewAccess().
+ * Overrides EntityAccessControllerInterface::checkAccess().
*/
- public function viewAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
- $uid = $entity->uid;
+ public function checkAccess(EntityInterface $entity, $operation, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
if (!$account) {
- $account = $GLOBALS['user'];
+ $account = user_load($GLOBALS['user']->uid);
+ }
+
+ switch ($operation) {
+ case 'view':
+ $access = $this->viewAccess($entity, $langcode, $account);
+ break;
+
+ case 'create':
+ $access = user_access('administer users', $account);
+ break;
+
+ case 'update':
+ // Users can always edit their own account. Users with the 'administer
+ // users' permission can edit any account except the anonymous account.
+ $access = (($account->uid == $entity->uid) || user_access('administer users', $account)) && $entity->uid > 0;
+ break;
+
+ case 'delete':
+ // Users with 'cancel account' permission can cancel their own account,
+ // users with 'administer users' permission can cancel any account
+ // except the anonymous account.
+ $access = ((($account->uid == $entity->uid) && user_access('cancel account', $account)) || user_access('administer users', $account)) && $entity->uid > 0;
+ break;
+
+ default:
+ $access = NULL;
+ break;
}
+ return $access;
+ }
+
+ /**
+ * Check view access.
+ *
+ * See EntityAccessControllerInterface::view() for parameters.
+ */
+ protected function viewAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
+ $uid = $entity->uid;
+
// Never allow access to view the anonymous user account.
if ($uid) {
// Admins can view all, users can view own profiles at all times.
@@ -39,36 +76,4 @@ public function viewAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT
return FALSE;
}
- /**
- * Implements EntityAccessControllerInterface::createAccess().
- */
- public function createAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
- return user_access('administer users', $account);
- }
-
- /**
- * Implements EntityAccessControllerInterface::updateAccess().
- */
- public function updateAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
- if (!$account) {
- $account = $GLOBALS['user'];
- }
- // Users can always edit their own account. Users with the 'administer
- // users' permission can edit any account except the anonymous account.
- return (($account->uid == $entity->uid) || user_access('administer users', $account)) && $entity->uid > 0;
- }
-
- /**
- * Implements EntityAccessControllerInterface::deleteAccess().
- */
- public function deleteAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
- if (!$account) {
- $account = $GLOBALS['user'];
- }
- // Users with 'cancel account' permission can cancel their own account,
- // users with 'administer users' permission can cancel any account except
- // the anonymous account.
- return ((($account->uid == $entity->uid) && user_access('cancel account', $account)) || user_access('administer users', $account)) && $entity->uid > 0;
- }
-
}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/style/StylePluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/style/StylePluginBase.php
index 5f993d4..2fe7cfa 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/style/StylePluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/style/StylePluginBase.php
@@ -394,12 +394,15 @@ function pre_render($result) {
}
/**
- * Renders a single group of a grouped view.
+ * Renders a group of rows of the grouped view.
+ *
+ * @param array $rows
+ * The result rows rendered in this group.
*
* @return array
- * The render array containing the single view theme output.
+ * The render array containing the single group theme output.
*/
- protected function renderSingleGroup(array $rows = array()) {
+ protected function renderRowGroup(array $rows = array()) {
return array(
'#theme' => $this->themeFunctions(),
'#view' => $this->view,
@@ -467,7 +470,7 @@ function render_grouping_sets($sets, $level = 0) {
}
}
- $single_output = $this->renderSingleGroup($set['rows']);
+ $single_output = $this->renderRowGroup($set['rows']);
$single_output['#grouping_level'] = $level;
$single_output['#title'] = $set['group'];
$output[] = $single_output;
diff --git a/core/modules/views/views.module b/core/modules/views/views.module
index 58a7f35..4fd0f9a 100644
--- a/core/modules/views/views.module
+++ b/core/modules/views/views.module
@@ -236,6 +236,7 @@ function views_plugin_list() {
* node portion of the theme registry.
*/
function views_preprocess_node(&$vars) {
+ module_load_include('inc', 'node', 'node.views');
// The 'view' attribute of the node is added in views_preprocess_node()
if (!empty($vars['node']->view) && $vars['node']->view->storage->id()) {
$vars['view'] = $vars['node']->view;
@@ -252,7 +253,7 @@ function views_preprocess_node(&$vars) {
}
// Allow to alter comments and links based on the settings in the row plugin.
- if (!empty($vars['view']->style_plugin->row_plugin) && get_class($vars['view']->style_plugin->row_plugin) == 'views_plugin_row_node_view') {
+ if (!empty($vars['view']->rowPlugin) && $vars['view']->rowPlugin->getPluginId() == 'node') {
node_row_node_view_preprocess_node($vars);
}
}