diff --git a/core/modules/dblog/src/Tests/Rest/DbLogResourceTest.php b/core/modules/dblog/src/Tests/Rest/DbLogResourceTest.php
index 1dd1f1145460a0cdcbb3eccfcb2a2614db82291e..279d202a14564f92afbb0b44107ec5a071c3ba2a 100644
--- a/core/modules/dblog/src/Tests/Rest/DbLogResourceTest.php
+++ b/core/modules/dblog/src/Tests/Rest/DbLogResourceTest.php
@@ -53,13 +53,13 @@ public function testWatchdog() {
$response = $this->httpRequest(Url::fromRoute('rest.dblog.GET.' . $this->defaultFormat, ['id' => 9999, '_format' => $this->defaultFormat]), 'GET');
$this->assertResponse(404);
$decoded = Json::decode($response);
- $this->assertEqual($decoded['error'], 'Log entry with ID 9999 was not found', 'Response message is correct.');
+ $this->assertEqual($decoded['message'], 'Log entry with ID 9999 was not found', 'Response message is correct.');
// Make a bad request (a true malformed request would never be a route match).
$response = $this->httpRequest(Url::fromRoute('rest.dblog.GET.' . $this->defaultFormat, ['id' => 0, '_format' => $this->defaultFormat]), 'GET');
$this->assertResponse(400);
$decoded = Json::decode($response);
- $this->assertEqual($decoded['error'], 'No log entry ID was provided', 'Response message is correct.');
+ $this->assertEqual($decoded['message'], 'No log entry ID was provided', 'Response message is correct.');
}
}
diff --git a/core/modules/page_cache/src/Tests/PageCacheTest.php b/core/modules/page_cache/src/Tests/PageCacheTest.php
index 64e4ee9f8c37ee40514d4ea14040511fc6e91d23..22b8b51a1675dd6bae6bbb2d7ed53dbbe7efcec1 100644
--- a/core/modules/page_cache/src/Tests/PageCacheTest.php
+++ b/core/modules/page_cache/src/Tests/PageCacheTest.php
@@ -8,7 +8,6 @@
use Drupal\entity_test\Entity\EntityTest;
use Drupal\simpletest\WebTestBase;
use Drupal\Core\Cache\Cache;
-use Drupal\user\Entity\Role;
use Drupal\user\RoleInterface;
/**
@@ -136,10 +135,6 @@ function testQueryParameterFormatRequests() {
$node = $this->drupalCreateNode(['type' => 'article']);
$node_uri = $node->urlInfo();
$node_url_with_hal_json_format = $node->urlInfo('canonical')->setRouteParameter('_format', 'hal_json');
- /** @var \Drupal\user\RoleInterface $role */
- $role = Role::load('anonymous');
- $role->grantPermission('restful get entity:node');
- $role->save();
$this->drupalGet($node_uri);
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
diff --git a/core/modules/rest/config/install/rest.settings.yml b/core/modules/rest/config/install/rest.settings.yml
index 2d8185e5c45d67895b8965b0f1c01e4bcd4f9d02..eb3da2df4ca6d77149ab67513f4e7e7de6b7e9d1 100644
--- a/core/modules/rest/config/install/rest.settings.yml
+++ b/core/modules/rest/config/install/rest.settings.yml
@@ -1,3 +1,11 @@
# Set the domain for REST type and relation links.
# If left blank, the site's domain will be used.
link_domain: ~
+
+# Before Drupal 8.2, EntityResource used permissions as well as the entity
+# access system for access checking. This was confusing, and it only did this
+# for historical reasons. New Drupal installations opt out from this by default
+# (hence this is set to false), existing installations opt in to it.
+# @see rest_update_8203()
+# @see https://www.drupal.org/node/2664780
+bc_entity_resource_permissions: false
diff --git a/core/modules/rest/config/schema/rest.schema.yml b/core/modules/rest/config/schema/rest.schema.yml
index 04f88a6fada02a9bdbb3ca4105f5e2c383c8c598..2c255ab88cd4f810fc973995a11a439f01088622 100644
--- a/core/modules/rest/config/schema/rest.schema.yml
+++ b/core/modules/rest/config/schema/rest.schema.yml
@@ -6,6 +6,9 @@ rest.settings:
link_domain:
type: string
label: 'Domain of the relation'
+ bc_entity_resource_permissions:
+ type: boolean
+ label: 'Whether the pre Drupal 8.2.x behavior of having permissions for EntityResource is enabled or not.'
# Method-level granularity of REST resource configuration.
rest_resource.method:
diff --git a/core/modules/rest/rest.install b/core/modules/rest/rest.install
index 4cfaa112fb0a2571c6cb7def60689bc5d1a33ffe..90f4ec92b292fc346e57c8996bc947f8d4fa3368 100644
--- a/core/modules/rest/rest.install
+++ b/core/modules/rest/rest.install
@@ -65,6 +65,16 @@ function rest_update_8202() {
}
}
+/**
+ * Enable BC for EntityResource: continue to use permissions.
+ */
+function rest_update_8203() {
+ $config_factory = \Drupal::configFactory();
+ $rest_settings = $config_factory->getEditable('rest.settings');
+ $rest_settings->set('bc_entity_resource_permissions', TRUE)
+ ->save(TRUE);
+}
+
/**
* @} End of "defgroup updates-8.1.x-to-8.2.x".
*/
diff --git a/core/modules/rest/src/Plugin/ResourceBase.php b/core/modules/rest/src/Plugin/ResourceBase.php
index 549ac544a30d4e60c278283545f074a44fe0ed7d..93684dbbc415df5d8fc3a7258b44490d389e4c70 100644
--- a/core/modules/rest/src/Plugin/ResourceBase.php
+++ b/core/modules/rest/src/Plugin/ResourceBase.php
@@ -12,6 +12,11 @@
/**
* Common base class for resource plugins.
*
+ * Note that this base class' implementation of the permissions() method
+ * generates a permission for every method for a resource. If your resource
+ * already has its own access control mechanism, you should opt out from this
+ * default permissions() method by overriding it.
+ *
* @see \Drupal\rest\Annotation\RestResource
* @see \Drupal\rest\Plugin\Type\ResourcePluginManager
* @see \Drupal\rest\Plugin\ResourceInterface
@@ -179,7 +184,7 @@ public function availableMethods() {
}
/**
- * Setups the base route for all HTTP methods.
+ * Gets the base route for a particular method.
*
* @param string $canonical_path
* The canonical path for the resource.
@@ -190,20 +195,48 @@ public function availableMethods() {
* The created base route.
*/
protected function getBaseRoute($canonical_path, $method) {
- $lower_method = strtolower($method);
-
- $route = new Route($canonical_path, array(
+ return new Route($canonical_path, array(
'_controller' => 'Drupal\rest\RequestHandler::handle',
- ), array(
- '_permission' => "restful $lower_method $this->pluginId",
),
+ $this->getBaseRouteRequirements($method),
array(),
'',
array(),
// The HTTP method is a requirement for this route.
array($method)
);
- return $route;
+ }
+
+ /**
+ * Gets the base route requirements for a particular method.
+ *
+ * @param $method
+ * The HTTP method to be used for the route.
+ *
+ * @return array
+ * An array of requirements for parameters.
+ */
+ protected function getBaseRouteRequirements($method) {
+ $lower_method = strtolower($method);
+ // Every route MUST have requirements that result in the access manager
+ // having access checks to check. If it does not, the route is made
+ // inaccessible. So, we default to granting access to everyone. If a
+ // permission exists, then we add that below. The access manager requires
+ // that ALL access checks must grant access, so this still results in
+ // correct behavior.
+ $requirements = [
+ '_access' => 'TRUE',
+ ];
+
+ // Only specify route requirements if the default permission exists. For any
+ // more advanced route definition, resource plugins extending this base
+ // class must override this method.
+ $permission = "restful $lower_method $this->pluginId";
+ if (isset($this->permissions()[$permission])) {
+ $requirements['_permission'] = $permission;
+ }
+
+ return $requirements;
}
}
diff --git a/core/modules/rest/src/Plugin/ResourceInterface.php b/core/modules/rest/src/Plugin/ResourceInterface.php
index 0bc2bfb2a83beb79df8f864ea646462e330f180f..7e92c571c49426e5e513aeb75812141c07c7a16a 100644
--- a/core/modules/rest/src/Plugin/ResourceInterface.php
+++ b/core/modules/rest/src/Plugin/ResourceInterface.php
@@ -33,6 +33,10 @@ public function routes();
* A resource plugin can define a set of user permissions that are used on the
* routes for this resource or for other purposes.
*
+ * It is not required for a resource plugin to specify permissions: if they
+ * have their own access control mechanism, they can use that, and return the
+ * empty array.
+ *
* @return array
* The permission array.
*/
diff --git a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
index 8b96940e317bc5f02fdcf17737d72606cfc97d3a..5cf42ddf64c9ca2c6f675781ea4a2bc315eba909 100644
--- a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
+++ b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
@@ -6,6 +6,7 @@
use Drupal\Core\Config\Entity\ConfigEntityType;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
+use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageException;
use Drupal\rest\Plugin\ResourceBase;
@@ -42,6 +43,13 @@ class EntityResource extends ResourceBase implements DependentPluginInterface {
*/
protected $entityType;
+ /**
+ * The config factory.
+ *
+ * @var \Drupal\Core\Config\ConfigFactoryInterface
+ */
+ protected $configFactory;
+
/**
* Constructs a Drupal\rest\Plugin\rest\resource\EntityResource object.
*
@@ -57,10 +65,13 @@ class EntityResource extends ResourceBase implements DependentPluginInterface {
* The available serialization formats.
* @param \Psr\Log\LoggerInterface $logger
* A logger instance.
+ * @param \Drupal\Core\Config\ConfigFactoryInterface
+ * The config factory.
*/
- public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, $serializer_formats, LoggerInterface $logger) {
+ public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, $serializer_formats, LoggerInterface $logger, ConfigFactoryInterface $config_factory) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $serializer_formats, $logger);
$this->entityType = $entity_type_manager->getDefinition($plugin_definition['entity_type']);
+ $this->configFactory = $config_factory;
}
/**
@@ -73,7 +84,8 @@ public static function create(ContainerInterface $container, array $configuratio
$plugin_definition,
$container->get('entity_type.manager'),
$container->getParameter('serializer.formats'),
- $container->get('logger.factory')->get('rest')
+ $container->get('logger.factory')->get('rest'),
+ $container->get('config.factory')
);
}
@@ -297,6 +309,21 @@ protected function validate(EntityInterface $entity) {
}
}
+ /**
+ * {@inheritdoc}
+ */
+ public function permissions() {
+ // @see https://www.drupal.org/node/2664780
+ if ($this->configFactory->get('rest.settings')->get('bc_entity_resource_permissions')) {
+ // The default Drupal 8.0.x and 8.1.x behavior.
+ return parent::permissions();
+ }
+ else {
+ // The default Drupal 8.2.x behavior.
+ return [];
+ }
+ }
+
/**
* {@inheritdoc}
*/
diff --git a/core/modules/rest/src/RequestHandler.php b/core/modules/rest/src/RequestHandler.php
index d75cfbbd73a2f1f325f0d3a075bdd70381d960b0..088dca298932cb0c1fdb819b8479d94352514c01 100644
--- a/core/modules/rest/src/RequestHandler.php
+++ b/core/modules/rest/src/RequestHandler.php
@@ -12,7 +12,6 @@
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
-use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
use Symfony\Component\Serializer\SerializerInterface;
@@ -131,17 +130,7 @@ public function handle(RouteMatchInterface $route_match, Request $request) {
// Invoke the operation on the resource plugin.
$format = $this->getResponseFormat($route_match, $request);
- try {
- $response = call_user_func_array(array($resource, $method), array_merge($parameters, array($unserialized, $request)));
- }
- catch (HttpException $e) {
- $error['error'] = $e->getMessage();
- $content = $serializer->serialize($error, $format);
- // Add the default content type, but only if the headers from the
- // exception have not specified it already.
- $headers = $e->getHeaders() + array('Content-Type' => $request->getMimeType($format));
- return new Response($content, $e->getStatusCode(), $headers);
- }
+ $response = call_user_func_array(array($resource, $method), array_merge($parameters, array($unserialized, $request)));
return $response instanceof ResourceResponseInterface ?
$this->renderResponse($request, $response, $serializer, $format, $resource_config) :
diff --git a/core/modules/rest/src/Tests/AuthTest.php b/core/modules/rest/src/Tests/AuthTest.php
index e9fb7d736a37b257d7465fc8fcd75b0dc9cf07ac..9f4224f64f1a58e4ce494f67a0e136e8f9f0b383 100644
--- a/core/modules/rest/src/Tests/AuthTest.php
+++ b/core/modules/rest/src/Tests/AuthTest.php
@@ -43,7 +43,6 @@ public function testRead() {
// resources via the REST API, but the request is authenticated
// with session cookies.
$permissions = $this->entityPermissions($entity_type, 'view');
- $permissions[] = 'restful get entity:' . $entity_type;
$account = $this->drupalCreateUser($permissions);
$this->drupalLogin($account);
diff --git a/core/modules/rest/src/Tests/CreateTest.php b/core/modules/rest/src/Tests/CreateTest.php
index d71b9e4a0f793e4343c8f13b7436f2f793b7143f..8c7db07bac1cb550aebb27c662908b6de7a8969b 100644
--- a/core/modules/rest/src/Tests/CreateTest.php
+++ b/core/modules/rest/src/Tests/CreateTest.php
@@ -5,6 +5,7 @@
use Drupal\comment\Tests\CommentTestTrait;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Url;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\node\Entity\Node;
use Drupal\user\Entity\User;
@@ -49,8 +50,6 @@ public function testCreateResourceRestApiNotEnabled() {
// Get the necessary user permissions to create the current entity type.
$permissions = $this->entityPermissions($entity_type, 'create');
- // POST method must be allowed for the current entity type.
- $permissions[] = 'restful post entity:' . $entity_type;
// Create the user.
$account = $this->drupalCreateUser($permissions);
@@ -77,7 +76,11 @@ public function testCreateResourceRestApiNotEnabled() {
/**
* Ensure that an entity cannot be created without the restful permission.
*/
- public function testCreateWithoutPermission() {
+ public function testCreateWithoutPermissionIfBcFlagIsOn() {
+ $rest_settings = $this->config('rest.settings');
+ $rest_settings->set('bc_entity_resource_permissions', TRUE)
+ ->save(TRUE);
+
$entity_type = 'entity_test';
// Enables the REST service for 'entity_test' entity type.
$this->enableService('entity:' . $entity_type, 'POST');
@@ -96,6 +99,14 @@ public function testCreateWithoutPermission() {
$this->httpRequest('entity/' . $entity_type, 'POST', $serialized, $this->defaultMimeType);
$this->assertResponse(403);
$this->assertFalse(EntityTest::loadMultiple(), 'No entity has been created in the database.');
+
+ // Create a user with the 'restful post entity:entity_test permission and
+ // try again. This time, we should be able to create an entity.
+ $permissions[] = 'restful post entity:' . $entity_type;
+ $account = $this->drupalCreateUser($permissions);
+ $this->drupalLogin($account);
+ $this->httpRequest('entity/' . $entity_type, 'POST', $serialized, $this->defaultMimeType);
+ $this->assertResponse(201);
}
/**
@@ -331,8 +342,6 @@ public function createAccountPerEntity($entity_type) {
$accounts = array();
// Get the necessary user permissions for the current $entity_type creation.
$permissions = $this->entityPermissions($entity_type, 'create');
- // POST method must be allowed for the current entity type.
- $permissions[] = 'restful post entity:' . $entity_type;
// Create user without administrative permissions.
$accounts[] = $this->drupalCreateUser($permissions);
// Add administrative permissions for nodes and users.
@@ -440,14 +449,14 @@ public function assertCreateEntityInvalidSerialized(EntityInterface $entity, $en
$entity->set('uuid', $this->randomMachineName(129));
$invalid_serialized = $this->serializer->serialize($entity, $this->defaultFormat, $context);
- $response = $this->httpRequest('entity/' . $entity_type, 'POST', $invalid_serialized, $this->defaultMimeType);
+ $response = $this->httpRequest(Url::fromRoute("rest.entity.$entity_type.POST")->setRouteParameter('_format', $this->defaultFormat), 'POST', $invalid_serialized, $this->defaultMimeType);
// Unprocessable Entity as response.
$this->assertResponse(422);
// Verify that the text of the response is correct.
$error = Json::decode($response);
- $this->assertEqual($error['error'], "Unprocessable Entity: validation failed.\nuuid.0.value: UUID: may not be longer than 128 characters.\n");
+ $this->assertEqual($error['message'], "Unprocessable Entity: validation failed.\nuuid.0.value: UUID: may not be longer than 128 characters.\n");
}
/**
diff --git a/core/modules/rest/src/Tests/CsrfTest.php b/core/modules/rest/src/Tests/CsrfTest.php
index b23e23d433f480f9a970d66129c6f88312b58302..f1c41a6fa78dc01effd15c04970e66e08f09456c 100644
--- a/core/modules/rest/src/Tests/CsrfTest.php
+++ b/core/modules/rest/src/Tests/CsrfTest.php
@@ -43,7 +43,6 @@ protected function setUp() {
// Create a user account that has the required permissions to create
// resources via the REST API.
$permissions = $this->entityPermissions($this->testEntityType, 'create');
- $permissions[] = 'restful post entity:' . $this->testEntityType;
$this->account = $this->drupalCreateUser($permissions);
// Serialize an entity to a string to use in the content body of the POST
diff --git a/core/modules/rest/src/Tests/DeleteTest.php b/core/modules/rest/src/Tests/DeleteTest.php
index 7de3fb48e4a75eda1495bb857a5d0c276aac31bd..60302bcda28fe10b1bdbe07c51180e5f329d5012 100644
--- a/core/modules/rest/src/Tests/DeleteTest.php
+++ b/core/modules/rest/src/Tests/DeleteTest.php
@@ -31,7 +31,6 @@ public function testDelete() {
// Create a user account that has the required permissions to delete
// resources via the REST API.
$permissions = $this->entityPermissions($entity_type, 'delete');
- $permissions[] = 'restful delete entity:' . $entity_type;
$account = $this->drupalCreateUser($permissions);
$this->drupalLogin($account);
diff --git a/core/modules/rest/src/Tests/NodeTest.php b/core/modules/rest/src/Tests/NodeTest.php
index dbdeaec0c5c6fc6722420833c80009e3528a2ee8..95dc475e0e83d07ecb1ba0af7406cdf054b4cc76 100644
--- a/core/modules/rest/src/Tests/NodeTest.php
+++ b/core/modules/rest/src/Tests/NodeTest.php
@@ -32,7 +32,6 @@ class NodeTest extends RESTTestBase {
protected function enableNodeConfiguration($method, $operation) {
$this->enableService('entity:node', $method);
$permissions = $this->entityPermissions('node', $operation);
- $permissions[] = 'restful ' . strtolower($method) . ' entity:node';
$account = $this->drupalCreateUser($permissions);
$this->drupalLogin($account);
}
diff --git a/core/modules/rest/src/Tests/PageCacheTest.php b/core/modules/rest/src/Tests/PageCacheTest.php
index d4b09e98fcd2c9c2f83e60cbe1ef46aad8865f8f..66e57f5a54d65da3dc37084f845cc0b922afc690 100644
--- a/core/modules/rest/src/Tests/PageCacheTest.php
+++ b/core/modules/rest/src/Tests/PageCacheTest.php
@@ -48,10 +48,6 @@ public function testConfigChangePageCache() {
$this->enableService('entity:entity_test', 'POST');
$permissions = [
'administer entity_test content',
- 'restful post entity:entity_test',
- 'restful get entity:entity_test',
- 'restful patch entity:entity_test',
- 'restful delete entity:entity_test',
];
$account = $this->drupalCreateUser($permissions);
diff --git a/core/modules/rest/src/Tests/ReadTest.php b/core/modules/rest/src/Tests/ReadTest.php
index 99ebbaab53793ddaf2bf6a6fc5df8ef6131d6507..dc6f5743793df6bc3f42ee7693468a9eb488e8bf 100644
--- a/core/modules/rest/src/Tests/ReadTest.php
+++ b/core/modules/rest/src/Tests/ReadTest.php
@@ -48,7 +48,6 @@ public function testRead() {
// Create a user account that has the required permissions to read
// resources via the REST API.
$permissions = $this->entityPermissions($entity_type, 'view');
- $permissions[] = 'restful get entity:' . $entity_type;
$account = $this->drupalCreateUser($permissions);
$this->drupalLogin($account);
@@ -123,12 +122,6 @@ public function testRead() {
$data = Json::decode($response);
$this->assertFalse(isset($data['field_test_text']), 'Field access protected field is not visible in the response.');
}
-
- // Try to read an entity without proper permissions.
- $this->drupalLogout();
- $response = $this->httpRequest($this->getReadUrl($entity), 'GET');
- $this->assertResponse(403);
- $this->assertIdentical('{"message":""}', $response);
}
// Try to read a resource, the user entity, which is not REST API enabled.
$account = $this->drupalCreateUser();
@@ -155,7 +148,6 @@ public function testResourceStructure() {
// Create a user account that has the required permissions to read
// resources via the REST API.
$permissions = $this->entityPermissions('node', 'view');
- $permissions[] = 'restful get entity:node';
$account = $this->drupalCreateUser($permissions);
$this->drupalLogin($account);
diff --git a/core/modules/rest/src/Tests/Update/EntityResourcePermissionsUpdateTest.php b/core/modules/rest/src/Tests/Update/EntityResourcePermissionsUpdateTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..989159e1ca801140d4ae50aa88abf1f77302666a
--- /dev/null
+++ b/core/modules/rest/src/Tests/Update/EntityResourcePermissionsUpdateTest.php
@@ -0,0 +1,56 @@
+databaseDumpFiles = [
+ __DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
+ __DIR__ . '/../../../../rest/tests/fixtures/update/drupal-8.rest-rest_update_8203.php',
+ ];
+ }
+
+ /**
+ * Tests rest_update_8203().
+ */
+ public function testBcEntityResourcePermissionSettingAdded() {
+ $permission_handler = $this->container->get('user.permissions');
+
+ $is_rest_resource_permission = function ($permission) {
+ return $permission['provider'] === 'rest' && (string) $permission['title'] !== 'Administer REST resource configuration';
+ };
+
+ // Make sure we have the expected values before the update.
+ $rest_settings = $this->config('rest.settings');
+ $this->assertFalse(array_key_exists('bc_entity_resource_permissions', $rest_settings->getRawData()));
+ $this->assertEqual([], array_filter($permission_handler->getPermissions(), $is_rest_resource_permission));
+
+ $this->runUpdates();
+
+ // Make sure we have the expected values after the update.
+ $rest_settings = $this->config('rest.settings');
+ $this->assertTrue(array_key_exists('bc_entity_resource_permissions', $rest_settings->getRawData()));
+ $this->assertTrue($rest_settings->get('bc_entity_resource_permissions'));
+ $rest_permissions = array_keys(array_filter($permission_handler->getPermissions(), $is_rest_resource_permission));
+ $this->assertEqual(['restful delete entity:node', 'restful get entity:node', 'restful patch entity:node', 'restful post entity:node'], $rest_permissions);
+ }
+
+}
diff --git a/core/modules/rest/src/Tests/UpdateTest.php b/core/modules/rest/src/Tests/UpdateTest.php
index dcab1ff76bd8b1a072d917afc7712c2c04894c84..2f8030486fe9921594e0e97228a2765fdbfb0eec 100644
--- a/core/modules/rest/src/Tests/UpdateTest.php
+++ b/core/modules/rest/src/Tests/UpdateTest.php
@@ -46,7 +46,6 @@ public function testPatchUpdate() {
// Create a user account that has the required permissions to create
// resources via the REST API.
$permissions = $this->entityPermissions($entity_type, 'update');
- $permissions[] = 'restful patch entity:' . $entity_type;
$account = $this->drupalCreateUser($permissions);
$this->drupalLogin($account);
@@ -176,10 +175,10 @@ public function testPatchUpdate() {
// Send a UUID that is too long.
$entity->set('uuid', $this->randomMachineName(129));
$invalid_serialized = $serializer->serialize($entity, $this->defaultFormat, $context);
- $response = $this->httpRequest($entity->urlInfo(), 'PATCH', $invalid_serialized, $this->defaultMimeType);
+ $response = $this->httpRequest($entity->toUrl()->setRouteParameter('_format', $this->defaultFormat), 'PATCH', $invalid_serialized, $this->defaultMimeType);
$this->assertResponse(422);
$error = Json::decode($response);
- $this->assertEqual($error['error'], "Unprocessable Entity: validation failed.\nuuid.0.value: UUID: may not be longer than 128 characters.\n");
+ $this->assertEqual($error['message'], "Unprocessable Entity: validation failed.\nuuid.0.value: UUID: may not be longer than 128 characters.\n");
// Try to update an entity without proper permissions.
$this->drupalLogout();
@@ -202,7 +201,6 @@ public function testUpdateUser() {
// Enables the REST service for 'user' entity type.
$this->enableService('entity:' . $entity_type, 'PATCH');
$permissions = $this->entityPermissions($entity_type, 'update');
- $permissions[] = 'restful patch entity:' . $entity_type;
$account = $this->drupalCreateUser($permissions);
$account->set('mail', 'old-email@example.com');
$this->drupalLogin($account);
@@ -216,18 +214,18 @@ public function testUpdateUser() {
$context = ['account' => $account];
$normalized = $serializer->normalize($account, $this->defaultFormat, $context);
$serialized = $serializer->serialize($normalized, $this->defaultFormat, $context);
- $response = $this->httpRequest($account->urlInfo(), 'PATCH', $serialized, $this->defaultMimeType);
+ $response = $this->httpRequest($account->toUrl()->setRouteParameter('_format', $this->defaultFormat), 'PATCH', $serialized, $this->defaultMimeType);
$this->assertResponse(422);
$error = Json::decode($response);
- $this->assertEqual($error['error'], "Unprocessable Entity: validation failed.\nmail: Your current password is missing or incorrect; it's required to change the Email.\n");
+ $this->assertEqual($error['message'], "Unprocessable Entity: validation failed.\nmail: Your current password is missing or incorrect; it's required to change the Email.\n");
// Try and send the new email with a password.
$normalized['pass'][0]['existing'] = 'wrong';
$serialized = $serializer->serialize($normalized, $this->defaultFormat, $context);
- $response = $this->httpRequest($account->urlInfo(), 'PATCH', $serialized, $this->defaultMimeType);
+ $response = $this->httpRequest($account->toUrl()->setRouteParameter('_format', $this->defaultFormat), 'PATCH', $serialized, $this->defaultMimeType);
$this->assertResponse(422);
$error = Json::decode($response);
- $this->assertEqual($error['error'], "Unprocessable Entity: validation failed.\nmail: Your current password is missing or incorrect; it's required to change the Email.\n");
+ $this->assertEqual($error['message'], "Unprocessable Entity: validation failed.\nmail: Your current password is missing or incorrect; it's required to change the Email.\n");
// Try again with the password.
$normalized['pass'][0]['existing'] = $account->pass_raw;
@@ -240,10 +238,10 @@ public function testUpdateUser() {
$normalized = $serializer->normalize($account, $this->defaultFormat, $context);
$normalized['pass'][0]['value'] = $new_password;
$serialized = $serializer->serialize($normalized, $this->defaultFormat, $context);
- $response = $this->httpRequest($account->urlInfo(), 'PATCH', $serialized, $this->defaultMimeType);
+ $response = $this->httpRequest($account->toUrl()->setRouteParameter('_format', $this->defaultFormat), 'PATCH', $serialized, $this->defaultMimeType);
$this->assertResponse(422);
$error = Json::decode($response);
- $this->assertEqual($error['error'], "Unprocessable Entity: validation failed.\npass: Your current password is missing or incorrect; it's required to change the Password.\n");
+ $this->assertEqual($error['message'], "Unprocessable Entity: validation failed.\npass: Your current password is missing or incorrect; it's required to change the Password.\n");
// Try again with the password.
$normalized['pass'][0]['existing'] = $account->pass_raw;
@@ -264,7 +262,6 @@ public function testUpdateComment() {
// Enables the REST service for 'comment' entity type.
$this->enableService('entity:' . $entity_type, 'PATCH', ['hal_json', 'json']);
$permissions = $this->entityPermissions($entity_type, 'update');
- $permissions[] = 'restful patch entity:' . $entity_type;
$account = $this->drupalCreateUser($permissions);
$account->set('mail', 'old-email@example.com');
$this->drupalLogin($account);
@@ -336,7 +333,7 @@ public function testUpdateComment() {
protected function patchEntity(EntityInterface $entity, array $read_only_fields, AccountInterface $account, $format, $mime_type) {
$serializer = $this->container->get('serializer');
- $url = $entity->toUrl();
+ $url = $entity->toUrl()->setRouteParameter('_format', $this->defaultFormat);
$context = ['account' => $account];
// Certain fields are always read-only, others this user simply is not
// allowed to modify. For all of them, ensure they are not serialized, else
@@ -359,7 +356,7 @@ protected function patchEntity(EntityInterface $entity, array $read_only_fields,
$this->httpRequest($url, 'PATCH', $serialized, $mime_type);
$this->assertResponse(403);
- $this->assertResponseBody('{"error":"Access denied on updating field \'' . $field . '\'."}');
+ $this->assertResponseBody('{"message":"Access denied on updating field \\u0027' . $field . '\\u0027."}');
if ($format === 'hal_json') {
// We've just tried with this read-only field, now unset it.
diff --git a/core/modules/rest/tests/fixtures/update/drupal-8.rest-rest_update_8203.php b/core/modules/rest/tests/fixtures/update/drupal-8.rest-rest_update_8203.php
new file mode 100644
index 0000000000000000000000000000000000000000..3ce8b6abdc04d1778e615800760d060fc3d8260e
--- /dev/null
+++ b/core/modules/rest/tests/fixtures/update/drupal-8.rest-rest_update_8203.php
@@ -0,0 +1,63 @@
+insert('key_value')
+ ->fields([
+ 'collection' => 'system.schema',
+ 'name' => 'rest',
+ 'value' => 'i:8000;',
+ ])
+ ->fields([
+ 'collection' => 'system.schema',
+ 'name' => 'serialization',
+ 'value' => 'i:8000;',
+ ])
+ ->execute();
+
+// Update core.extension.
+$extensions = $connection->select('config')
+ ->fields('config', ['data'])
+ ->condition('collection', '')
+ ->condition('name', 'core.extension')
+ ->execute()
+ ->fetchField();
+$extensions = unserialize($extensions);
+$extensions['module']['rest'] = 8000;
+$extensions['module']['serialization'] = 8000;
+$connection->update('config')
+ ->fields([
+ 'data' => serialize($extensions),
+ ])
+ ->condition('collection', '')
+ ->condition('name', 'core.extension')
+ ->execute();
+
+// Install the rest configuration.
+$config = [
+ 'resources' => [
+ 'entity:node' => [
+ 'GET' => [
+ 'supported_formats' => ['json'],
+ 'supported_auth' => ['basic_auth'],
+ ],
+ ],
+ ],
+ 'link_domain' => '~',
+];
+$data = $connection->insert('config')
+ ->fields([
+ 'name' => 'rest.settings',
+ 'data' => serialize($config),
+ 'collection' => ''
+ ])
+ ->execute();
diff --git a/core/modules/system/src/Tests/System/ResponseGeneratorTest.php b/core/modules/system/src/Tests/System/ResponseGeneratorTest.php
index d2f8f5fbd51e08e2dceeb56365a6e97dddc935b4..f54c6516e84c760a7f86de498b897fca473d621b 100644
--- a/core/modules/system/src/Tests/System/ResponseGeneratorTest.php
+++ b/core/modules/system/src/Tests/System/ResponseGeneratorTest.php
@@ -26,7 +26,6 @@ protected function setUp() {
$this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));
$permissions = $this->entityPermissions('node', 'view');
- $permissions[] = 'restful get entity:node';
$account = $this->drupalCreateUser($permissions);
$this->drupalLogin($account);
}
diff --git a/core/modules/views/src/Entity/Render/ConfigurableLanguageRenderer.php b/core/modules/views/src/Entity/Render/ConfigurableLanguageRenderer.php
index 2e28f9df3bbe37ca52fdd0c585f706c2023f3c58..eeea6945f351b8e5fe2653dc1777011e38d7d3a1 100644
--- a/core/modules/views/src/Entity/Render/ConfigurableLanguageRenderer.php
+++ b/core/modules/views/src/Entity/Render/ConfigurableLanguageRenderer.php
@@ -39,7 +39,7 @@ public function __construct(ViewExecutable $view, LanguageManagerInterface $lang
/**
* {@inheritdoc}
*/
- public function getLangcode(ResultRow $row) {
+ public function getLangcode(ResultRow $row, $relationship = 'none') {
return $this->langcode;
}
diff --git a/core/modules/views/src/Entity/Render/DefaultLanguageRenderer.php b/core/modules/views/src/Entity/Render/DefaultLanguageRenderer.php
index 11dbb4ab87cdfafd5dbc5943b2bd830d3447caef..4f90ced9794ec0943702cb15790e79aba498259a 100644
--- a/core/modules/views/src/Entity/Render/DefaultLanguageRenderer.php
+++ b/core/modules/views/src/Entity/Render/DefaultLanguageRenderer.php
@@ -12,8 +12,10 @@ class DefaultLanguageRenderer extends EntityTranslationRendererBase {
/**
* {@inheritdoc}
*/
- public function getLangcode(ResultRow $row) {
- return $row->_entity->getUntranslated()->language()->getId();
+ public function getLangcode(ResultRow $row, $relationship = 'none') {
+ if ($entity = $this->getEntity($row, $relationship)) {
+ return $entity->getUntranslated()->language()->getId();
+ }
}
}
diff --git a/core/modules/views/src/Entity/Render/EntityFieldRenderer.php b/core/modules/views/src/Entity/Render/EntityFieldRenderer.php
index 1b05c1b2a3605952b197aba5ea94a9cf8c2d4b36..93a3dd444d0cff1814b7c08c93c7603ab8114a1c 100644
--- a/core/modules/views/src/Entity/Render/EntityFieldRenderer.php
+++ b/core/modules/views/src/Entity/Render/EntityFieldRenderer.php
@@ -200,7 +200,8 @@ protected function buildFields(array $values) {
$field = $this->view->field[current($field_ids)];
foreach ($values as $result_row) {
if ($entity = $field->getEntity($result_row)) {
- $entities_by_bundles[$entity->bundle()][$result_row->index] = $this->getEntityTranslation($entity, $result_row);
+ $relationship = isset($field->options['relationship']) ? $field->options['relationship'] : 'none';
+ $entities_by_bundles[$entity->bundle()][$result_row->index] = $this->getEntityTranslation($entity, $result_row, $relationship);
}
}
diff --git a/core/modules/views/src/Entity/Render/EntityTranslationRenderTrait.php b/core/modules/views/src/Entity/Render/EntityTranslationRenderTrait.php
index e08ef04d301e4c8b5de3c4f27fda46fd465285b6..cdbe3ada53670ced965ffbf39057d50c947ce447 100644
--- a/core/modules/views/src/Entity/Render/EntityTranslationRenderTrait.php
+++ b/core/modules/views/src/Entity/Render/EntityTranslationRenderTrait.php
@@ -62,18 +62,20 @@ protected function getEntityTranslationRenderer() {
* The entity object the field value being processed is attached to.
* @param \Drupal\views\ResultRow $row
* The result row the field value being processed belongs to.
+ * @param string $relationship
+ * The relationship to be used, or 'none' by default.
*
* @return \Drupal\Core\Entity\FieldableEntityInterface
* The entity translation object for the specified row.
*/
- public function getEntityTranslation(EntityInterface $entity, ResultRow $row) {
+ public function getEntityTranslation(EntityInterface $entity, ResultRow $row, $relationship = 'none') {
// We assume the same language should be used for all entity fields
// belonging to a single row, even if they are attached to different entity
// types. Below we apply language fallback to ensure a valid value is always
// picked.
$translation = $entity;
if ($entity instanceof TranslatableInterface && count($entity->getTranslationLanguages()) > 1) {
- $langcode = $this->getEntityTranslationRenderer()->getLangcode($row);
+ $langcode = $this->getEntityTranslationRenderer()->getLangcode($row, $relationship);
$translation = $this->getEntityManager()->getTranslationFromContext($entity, $langcode);
}
return $translation;
diff --git a/core/modules/views/src/Entity/Render/EntityTranslationRendererBase.php b/core/modules/views/src/Entity/Render/EntityTranslationRendererBase.php
index e77992dd01969d98bda406379a875cca3b87a6e7..c4787a413a753278ff368f44e14de98394f82ad2 100644
--- a/core/modules/views/src/Entity/Render/EntityTranslationRendererBase.php
+++ b/core/modules/views/src/Entity/Render/EntityTranslationRendererBase.php
@@ -15,11 +15,13 @@ abstract class EntityTranslationRendererBase extends RendererBase {
*
* @param \Drupal\views\ResultRow $row
* The result row.
+ * @param string $relationship
+ * The relationship to be used, or 'none' by default.
*
* @return string
* A language code.
*/
- abstract public function getLangcode(ResultRow $row);
+ abstract public function getLangcode(ResultRow $row, $relationship = 'none');
/**
* {@inheritdoc}
@@ -28,27 +30,62 @@ public function query(QueryPluginBase $query, $relationship = NULL) {
}
/**
- * {@inheritdoc}
+ * Runs before each entity is rendered.
+ *
+ * @param \Drupal\views\ResultRow[] $result
+ * The full array of results from the query.
+ * @param string $relationship
+ * The relationship to be used, or 'none' by default.
*/
- public function preRender(array $result) {
+ public function preRender(array $result, $relationship = 'none') {
$view_builder = $this->view->rowPlugin->entityManager->getViewBuilder($this->entityType->id());
- /** @var \Drupal\views\ResultRow $row */
foreach ($result as $row) {
- // @todo Take relationships into account.
- // See https://www.drupal.org/node/2457999.
- $entity = $row->_entity;
- $entity->view = $this->view;
- $this->build[$entity->id()] = $view_builder->view($entity, $this->view->rowPlugin->options['view_mode'], $this->getLangcode($row));
+ if ($entity = $this->getEntity($row, $relationship)) {
+ $entity->view = $this->view;
+ $this->build[$entity->id()] = $view_builder->view($entity, $this->view->rowPlugin->options['view_mode'], $this->getLangcode($row, $relationship));
+ }
}
}
/**
- * {@inheritdoc}
+ * Renders entity data.
+ *
+ * @param \Drupal\views\ResultRow $row
+ * A single row of the query result.
+ * @param string $relationship
+ * The relationship to be used, or 'none' by default.
+ *
+ * @return array
+ * A renderable array for the entity data contained in the result row.
*/
- public function render(ResultRow $row) {
- $entity_id = $row->_entity->id();
- return $this->build[$entity_id];
+ public function render(ResultRow $row, $relationship = 'none') {
+ if ($entity = $this->getEntity($row, $relationship)) {
+ $entity_id = $entity->id();
+ return $this->build[$entity_id];
+ }
+ }
+
+ /**
+ * Gets the entity assosiated with a row.
+ *
+ * @param \Drupal\views\ResultRow $row
+ * The result row.
+ * @param string $relationship
+ * (optional) The relationship.
+ *
+ * @return \Drupal\Core\Entity\EntityInterface|null
+ * The entity might be optional, because the relationship entity might not
+ * always exist.
+ */
+ protected function getEntity($row, $relationship = 'none') {
+ if ($relationship === 'none') {
+ return $row->_entity;
+ }
+ elseif (isset($row->_relationship_entities[$relationship])) {
+ return $row->_relationship_entities[$relationship];
+ }
+ return NULL;
}
}
diff --git a/core/modules/views/src/Entity/Render/RendererBase.php b/core/modules/views/src/Entity/Render/RendererBase.php
index 60327f615f87e6b5c0e43514e07bc3cea95ef2ac..b47dd430812ce28f1222c9c0378132aa95f0436e 100644
--- a/core/modules/views/src/Entity/Render/RendererBase.php
+++ b/core/modules/views/src/Entity/Render/RendererBase.php
@@ -93,7 +93,7 @@ abstract public function query(QueryPluginBase $query, $relationship = NULL);
/**
* Runs before each entity is rendered.
*
- * @param $result
+ * @param \Drupal\views\ResultRow[] $result
* The full array of results from the query.
*/
public function preRender(array $result) {
diff --git a/core/modules/views/src/Entity/Render/TranslationLanguageRenderer.php b/core/modules/views/src/Entity/Render/TranslationLanguageRenderer.php
index 00ae5248718570134b5d101152cd5aec40632379..1d78c9d0951d9e8d4faa5ad10acdc1f6fc004395 100644
--- a/core/modules/views/src/Entity/Render/TranslationLanguageRenderer.php
+++ b/core/modules/views/src/Entity/Render/TranslationLanguageRenderer.php
@@ -40,31 +40,34 @@ public function query(QueryPluginBase $query, $relationship = NULL) {
/**
* {@inheritdoc}
*/
- public function preRender(array $result) {
+ public function preRender(array $result, $relationship = 'none') {
$view_builder = $this->view->rowPlugin->entityManager->getViewBuilder($this->entityType->id());
/** @var \Drupal\views\ResultRow $row */
foreach ($result as $row) {
- $entity = $row->_entity;
- $entity->view = $this->view;
- $langcode = $this->getLangcode($row);
- $this->build[$entity->id()][$langcode] = $view_builder->view($entity, $this->view->rowPlugin->options['view_mode'], $this->getLangcode($row));
+ if ($entity = $this->getEntity($row, $relationship)) {
+ $entity->view = $this->view;
+ $langcode = $this->getLangcode($row, $relationship);
+ $this->build[$entity->id()][$langcode] = $view_builder->view($entity, $this->view->rowPlugin->options['view_mode'], $langcode);
+ }
}
}
/**
* {@inheritdoc}
*/
- public function render(ResultRow $row) {
- $entity_id = $row->_entity->id();
- $langcode = $this->getLangcode($row);
- return $this->build[$entity_id][$langcode];
+ public function render(ResultRow $row, $relationship = 'none') {
+ if ($entity = $this->getEntity($row, $relationship)) {
+ $entity_id = $entity->id();
+ $langcode = $this->getLangcode($row, $relationship);
+ return $this->build[$entity_id][$langcode];
+ }
}
/**
* {@inheritdoc}
*/
- public function getLangcode(ResultRow $row) {
+ public function getLangcode(ResultRow $row, $relationship = 'none') {
return isset($row->{$this->langcodeAlias}) ? $row->{$this->langcodeAlias} : $this->languageManager->getDefaultLanguage()->getId();
}
diff --git a/core/modules/views/src/Plugin/views/row/EntityRow.php b/core/modules/views/src/Plugin/views/row/EntityRow.php
index f5365296531c2d10ebc3addf30f0188179d573af..d54e03295ca14bf7e1767f3635812f5fede3380c 100644
--- a/core/modules/views/src/Plugin/views/row/EntityRow.php
+++ b/core/modules/views/src/Plugin/views/row/EntityRow.php
@@ -166,7 +166,13 @@ public function summaryTitle() {
*/
public function query() {
parent::query();
- $this->getEntityTranslationRenderer()->query($this->view->getQuery());
+ if (isset($this->options['relationship'], $this->view->relationship[$this->options['relationship']])) {
+ $relationship = $this->view->relationship[$this->options['relationship']]->alias;
+ }
+ else {
+ $relationship = NULL;
+ }
+ $this->getEntityTranslationRenderer()->query($this->view->getQuery(), $relationship);
}
/**
@@ -175,7 +181,7 @@ public function query() {
public function preRender($result) {
parent::preRender($result);
if ($result) {
- $this->getEntityTranslationRenderer()->preRender($result);
+ $this->getEntityTranslationRenderer()->preRender($result, isset($this->options['relationship']) ? $this->options['relationship'] : 'none');
}
}
@@ -183,7 +189,7 @@ public function preRender($result) {
* {@inheritdoc}
*/
public function render($row) {
- return $this->getEntityTranslationRenderer()->render($row);
+ return $this->getEntityTranslationRenderer()->render($row, isset($this->options['relationship']) ? $this->options['relationship'] : 'none');
}
/**
diff --git a/core/modules/views/src/Plugin/views/row/RowPluginBase.php b/core/modules/views/src/Plugin/views/row/RowPluginBase.php
index d1e090e0aac7f7bb79f1b708f42610b88de7bdf6..a60070bf86dc2513b12200453bd08c0ce9d1ea4b 100644
--- a/core/modules/views/src/Plugin/views/row/RowPluginBase.php
+++ b/core/modules/views/src/Plugin/views/row/RowPluginBase.php
@@ -90,7 +90,7 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) {
$data = Views::viewsData()->get($relationship['table']);
$base = $data[$relationship['field']]['relationship']['base'];
if ($base == $this->base_table) {
- $relationship_handler->init($executable, $relationship);
+ $relationship_handler->init($executable, $this->displayHandler, $relationship);
$relationship_options[$relationship['id']] = $relationship_handler->adminLabel();
}
}
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_row.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_row.yml
index ad9f748197ea9fd427d74af46d1023e2c5224697..c786e20443be757aa10cdbadb0f2828fb268b32f 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_row.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_row.yml
@@ -6,8 +6,8 @@ label: ''
module: views
description: ''
tag: ''
-base_table: taxonomy_term_field_data
-base_field: nid
+base_table: entity_test
+base_field: id
core: '8'
display:
default:
@@ -21,10 +21,17 @@ display:
offset: 0
type: none
row:
- type: 'entity:taxonomy_term'
+ type: 'entity:entity_test'
options:
relationship: none
view_mode: full
+ relationships:
+ user_id:
+ table: entity_test
+ field: user_id
+ id: user_id
+ relationship: none
+ plugin_id: standard
display_plugin: default
display_title: Master
id: default
diff --git a/core/modules/views/tests/src/Kernel/Plugin/RowEntityTest.php b/core/modules/views/tests/src/Kernel/Plugin/RowEntityTest.php
index 8d2d86632a6614accf4fbb6eba1259d8f5905796..0236b595d6dbbb1533d616dcac26a8ae91704052 100644
--- a/core/modules/views/tests/src/Kernel/Plugin/RowEntityTest.php
+++ b/core/modules/views/tests/src/Kernel/Plugin/RowEntityTest.php
@@ -3,10 +3,10 @@
namespace Drupal\Tests\views\Kernel\Plugin;
use Drupal\Core\Form\FormState;
+use Drupal\entity_test\Entity\EntityTest;
+use Drupal\user\Entity\User;
use Drupal\views\Views;
use Drupal\Tests\views\Kernel\ViewsKernelTestBase;
-use Drupal\taxonomy\Entity\Vocabulary;
-use Drupal\taxonomy\Entity\Term;
/**
* Tests the generic entity row plugin.
@@ -21,7 +21,7 @@ class RowEntityTest extends ViewsKernelTestBase {
*
* @var array
*/
- public static $modules = ['taxonomy', 'text', 'filter', 'field', 'system', 'node', 'user'];
+ public static $modules = ['entity_test', 'field', 'system', 'user'];
/**
* Views used by this test.
@@ -34,27 +34,55 @@ class RowEntityTest extends ViewsKernelTestBase {
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE) {
- parent::setUp();
+ parent::setUp($import_test_views);
- $this->installEntitySchema('taxonomy_term');
- $this->installConfig(array('taxonomy'));
- \Drupal::service('router.builder')->rebuild();
+ $this->installEntitySchema('entity_test');
+ $this->installEntitySchema('user');
}
/**
* Tests the entity row handler.
*/
public function testEntityRow() {
- $vocab = Vocabulary::create(['name' => $this->randomMachineName(), 'vid' => strtolower($this->randomMachineName())]);
- $vocab->save();
- $term = Term::create(['name' => $this->randomMachineName(), 'vid' => $vocab->id() ]);
- $term->save();
+ $user = User::create([
+ 'name' => 'test user',
+ ]);
+ $user->save();
+ $entity_test = EntityTest::create([
+ 'user_id' => $user->id(),
+ 'name' => 'test entity test',
+ ]);
+ $entity_test->save();
+
+ // Ensure entities have different ids.
+ if ($entity_test->id() == $user->id()) {
+ $entity_test->delete();
+ $entity_test = EntityTest::create([
+ 'user_id' => $user->id(),
+ 'name' => 'test entity test',
+ ]);
+ $entity_test->save();
+ }
+
+ $view = Views::getView('test_entity_row');
+ $build = $view->preview();
+ $this->render($build);
+
+ $this->assertText('test entity test');
+ $this->assertNoText('Member for');
+
+ // Change the view to use a relationship to render the row.
$view = Views::getView('test_entity_row');
+ $display = &$view->storage->getDisplay('default');
+ $display['display_options']['row']['type'] = 'entity:user';
+ $display['display_options']['row']['options']['relationship'] = 'user_id';
+ $view->setDisplay('default');
$build = $view->preview();
$this->render($build);
- $this->assertText($term->getName(), 'The rendered entity appears as row in the view.');
+ $this->assertNoText('test entity test');
+ $this->assertText('Member for');
// Tests the available view mode options.
$form = array();