Update to Drupal 8.1.2. For more information, see https://www.drupal.org/project/drupal/releases/8.1.2

This commit is contained in:
Pantheon Automation 2016-06-02 15:56:09 -07:00 committed by Greg Anderson
parent 9eae24d844
commit 28556d630e
1322 changed files with 6699 additions and 2064 deletions

View file

@ -138,4 +138,5 @@ class RelationLinkManager extends LinkManagerBase implements RelationLinkManager
// and only clear it when the fields cache is cleared.
$this->cache->set('rest:links:relations', $data, Cache::PERMANENT, array('entity_field_info'));
}
}

View file

@ -36,4 +36,5 @@ interface TypeLinkManagerInterface extends ConfigurableLinkManagerInterface {
* bundle. Otherwise, returns false.
*/
public function getTypeInternalIds($type_uri, $context = array());
}

View file

@ -93,4 +93,5 @@ class EntityDeriver implements ContainerDeriverInterface {
}
return $this->derivatives;
}
}

View file

@ -42,4 +42,5 @@ class ResourcePluginManager extends DefaultPluginManager {
return $this->createInstance($options['id']);
}
}
}

View file

@ -146,13 +146,24 @@ class EntityResource extends ResourceBase {
}
// Overwrite the received properties.
$langcode_key = $entity->getEntityType()->getKey('langcode');
$entity_keys = $entity->getEntityType()->getKeys();
foreach ($entity->_restSubmittedFields as $field_name) {
$field = $entity->get($field_name);
// It is not possible to set the language to NULL as it is automatically
// re-initialized. As it must not be empty, skip it if it is.
if ($field_name == $langcode_key && $field->isEmpty()) {
continue;
// Entity key fields need special treatment: together they uniquely
// identify the entity. Therefore it does not make sense to modify any of
// them. However, rather than throwing an error, we just ignore them as
// long as their specified values match their current values.
if (in_array($field_name, $entity_keys, TRUE)) {
// Unchanged values for entity keys don't need access checking.
if ($original_entity->get($field_name)->getValue() === $entity->get($field_name)->getValue()) {
continue;
}
// It is not possible to set the language to NULL as it is automatically
// re-initialized. As it must not be empty, skip it if it is.
elseif (isset($entity_keys['langcode']) && $field_name === $entity_keys['langcode'] && $field->isEmpty()) {
continue;
}
}
if (!$original_entity->get($field_name)->access('edit')) {
@ -245,5 +256,4 @@ class EntityResource extends ResourceBase {
return $route;
}
}

View file

@ -2,7 +2,11 @@
namespace Drupal\rest\Plugin\views\row;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\views\Entity\Render\EntityTranslationRenderTrait;
use Drupal\views\Plugin\views\row\RowPluginBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Plugin which displays entities as raw data.
@ -18,16 +22,97 @@ use Drupal\views\Plugin\views\row\RowPluginBase;
*/
class DataEntityRow extends RowPluginBase {
use EntityTranslationRenderTrait;
/**
* {@inheritdoc}
*/
protected $usesOptions = FALSE;
/**
* Contains the entity type of this row plugin instance.
*
* @var \Drupal\Core\Entity\EntityTypeInterface
*/
protected $entityType;
/**
* The entity manager.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
*/
public $entityManager;
/**
* The language manager.
*
* @var \Drupal\Core\Language\LanguageManagerInterface
*/
protected $languageManager;
/**
* {@inheritdoc}
*
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->entityManager = $entity_manager;
$this->languageManager = $language_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition, $container->get('entity.manager'), $container->get('language_manager'));
}
/**
* {@inheritdoc}
*/
public function render($row) {
return $row->_entity;
return $this->getEntityTranslation($row->_entity, $row);
}
/**
* {@inheritdoc}
*/
public function getEntityTypeId() {
return $this->view->getBaseEntityType()->id();
}
/**
* {@inheritdoc}
*/
protected function getEntityManager() {
return $this->entityManager;
}
/**
* {@inheritdoc}
*/
protected function getLanguageManager() {
return $this->languageManager;
}
/**
* {@inheritdoc}
*/
protected function getView() {
return $this->view;
}
/**
* {@inheritdoc}
*/
public function query() {
parent::query();
$this->getEntityTranslationRenderer()->query($this->view->getQuery());
}
}

View file

@ -131,4 +131,5 @@ class RequestHandler implements ContainerAwareInterface {
public function csrfToken() {
return new Response(\Drupal::csrfToken()->get('rest'), 200, array('Content-Type' => 'text/plain'));
}
}

View file

@ -49,4 +49,5 @@ class ResourceResponse extends Response implements CacheableResponseInterface {
public function getResponseData() {
return $this->responseData;
}
}

View file

@ -114,4 +114,5 @@ class CsrfTest extends RESTTestBase {
),
);
}
}

View file

@ -72,4 +72,5 @@ class DeleteTest extends RESTTestBase {
$this->assertEqual($account->id(), $user->id(), 'User still exists in the database.');
$this->assertResponse(405);
}
}

View file

@ -194,4 +194,5 @@ class NodeTest extends RESTTestBase {
$this->assertResponse(400);
$this->assertResponseBody('{"error":"A string must be provided as a bundle value."}');
}
}

View file

@ -250,8 +250,8 @@ abstract class RESTTestBase extends WebTestBase {
* resource types.
* @param string $method
* The HTTP method to enable, e.g. GET, POST etc.
* @param string $format
* (Optional) The serialization format, e.g. hal_json.
* @param string|array $format
* (Optional) The serialization format, e.g. hal_json, or a list of formats.
* @param array $auth
* (Optional) The list of valid authentication methods.
*/
@ -261,10 +261,15 @@ abstract class RESTTestBase extends WebTestBase {
$settings = array();
if ($resource_type) {
if ($format == NULL) {
$format = $this->defaultFormat;
if (is_array($format)) {
$settings[$resource_type][$method]['supported_formats'] = $format;
}
else {
if ($format == NULL) {
$format = $this->defaultFormat;
}
$settings[$resource_type][$method]['supported_formats'][] = $format;
}
$settings[$resource_type][$method]['supported_formats'][] = $format;
if ($auth == NULL) {
$auth = $this->defaultAuth;
@ -425,4 +430,5 @@ abstract class RESTTestBase extends WebTestBase {
protected function assertResponseBody($expected, $message = '', $group = 'REST Response') {
return $this->assertIdentical($expected, $this->responseBody, $message ? $message : strtr('Response body @expected (expected) is equal to @response (actual).', array('@expected' => var_export($expected, TRUE), '@response' => var_export($this->responseBody, TRUE))), $group);
}
}

View file

@ -2,7 +2,12 @@
namespace Drupal\rest\Tests;
use Drupal\comment\Entity\Comment;
use Drupal\comment\Tests\CommentTestTrait;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\entity_test\Entity\EntityTest;
/**
* Tests the update of resources.
@ -11,12 +16,22 @@ use Drupal\Component\Serialization\Json;
*/
class UpdateTest extends RESTTestBase {
use CommentTestTrait;
/**
* Modules to install.
*
* @var array
*/
public static $modules = array('hal', 'rest', 'entity_test');
public static $modules = ['hal', 'rest', 'entity_test', 'comment'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->addDefaultCommentField('entity_test', 'entity_test');
}
/**
* Tests several valid and invalid partial update requests on test entities.
@ -69,7 +84,7 @@ class UpdateTest extends RESTTestBase {
$this->assertResponse(204);
$entity = entity_load($entity_type, $entity->id(), TRUE);
$this->assertNotNull($entity->field_test_text->value. 'Test field has not been deleted.');
$this->assertNotNull($entity->field_test_text->value . 'Test field has not been deleted.');
// Try to empty a field.
$normalized['field_test_text'] = array();
@ -220,7 +235,129 @@ class UpdateTest extends RESTTestBase {
// Verify that we can log in with the new password.
$account->pass_raw = $new_password;
$this->drupalLogin($account);
}
/**
* Test patching a comment using both HAL+JSON and JSON.
*/
public function testUpdateComment() {
$entity_type = 'comment';
// 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);
// Create & save an entity to comment on, plus a comment.
$entity_test = EntityTest::create();
$entity_test->save();
$entity_values = $this->entityValues($entity_type);
$entity_values['entity_id'] = $entity_test->id();
$entity_values['uid'] = $account->id();
$comment = Comment::create($entity_values);
$comment->save();
$this->pass('Test case 1: PATCH comment using HAL+JSON.');
$comment->setSubject('Initial subject')->save();
$read_only_fields = [
'name',
'created',
'changed',
'status',
'thread',
'entity_type',
'field_name',
'entity_id',
'uid',
];
$this->assertNotEqual('Updated subject', $comment->getSubject());
$comment->setSubject('Updated subject');
$this->patchEntity($comment, $read_only_fields, $account, 'hal_json', 'application/hal+json');
$comment = Comment::load($comment->id());
$this->assertEqual('Updated subject', $comment->getSubject());
$this->pass('Test case 1: PATCH comment using JSON.');
$comment->setSubject('Initial subject')->save();
$read_only_fields = [
'pid', // Extra compared to HAL+JSON.
'entity_id',
'uid',
'name',
'homepage', // Extra compared to HAL+JSON.
'created',
'changed',
'status',
'thread',
'entity_type',
'field_name',
];
$this->assertNotEqual('Updated subject', $comment->getSubject());
$comment->setSubject('Updated subject');
$this->patchEntity($comment, $read_only_fields, $account, 'json', 'application/json');
$comment = Comment::load($comment->id());
$this->assertEqual('Updated subject', $comment->getSubject());
}
/**
* Patches an existing entity using the passed in (modified) entity.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The updated entity to send.
* @param string[] $read_only_fields
* Names of the fields that are read-only, in validation order.
* @param \Drupal\Core\Session\AccountInterface $account
* The account to use for serialization.
* @param string $format
* A serialization format.
* @param string $mime_type
* The MIME type corresponding to the specified serialization format.
*/
protected function patchEntity(EntityInterface $entity, array $read_only_fields, AccountInterface $account, $format, $mime_type) {
$serializer = $this->container->get('serializer');
$url = $entity->toUrl();
$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
// we'll get a 403 plus an error message.
for ($i = 0; $i < count($read_only_fields); $i++) {
$field = $read_only_fields[$i];
$normalized = $serializer->normalize($entity, $format, $context);
if ($format !== 'hal_json') {
// The default normalizer always keeps fields, even if they are unset
// here because they should be omitted during a PATCH request. Therefore
// manually strip them
// @see \Drupal\Core\Entity\ContentEntityBase::__unset()
// @see \Drupal\serialization\Normalizer\EntityNormalizer::normalize()
// @see \Drupal\hal\Normalizer\ContentEntityNormalizer::normalize()
$read_only_fields_so_far = array_slice($read_only_fields, 0, $i);
$normalized = array_diff_key($normalized, array_flip($read_only_fields_so_far));
}
$serialized = $serializer->serialize($normalized, $format, $context);
$this->httpRequest($url, 'PATCH', $serialized, $mime_type);
$this->assertResponse(403);
$this->assertResponseBody('{"error":"Access denied on updating field \'' . $field . '\'."}');
if ($format === 'hal_json') {
// We've just tried with this read-only field, now unset it.
$entity->set($field, NULL);
}
}
// Finally, with all read-only fields unset, the request should succeed.
$normalized = $serializer->normalize($entity, $format, $context);
if ($format !== 'hal_json') {
$normalized = array_diff_key($normalized, array_combine($read_only_fields, $read_only_fields));
}
$serialized = $serializer->serialize($normalized, $format, $context);
$this->httpRequest($url, 'PATCH', $serialized, $mime_type);
$this->assertResponse(204);
$this->assertResponseBody('');
}
}

View file

@ -7,6 +7,7 @@ use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
use Drupal\views\Entity\View;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
@ -38,14 +39,14 @@ class StyleSerializerTest extends PluginTestBase {
*
* @var array
*/
public static $modules = array('views_ui', 'entity_test', 'hal', 'rest_test_views', 'node', 'text', 'field');
public static $modules = array('views_ui', 'entity_test', 'hal', 'rest_test_views', 'node', 'text', 'field', 'language');
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = array('test_serializer_display_field', 'test_serializer_display_entity', 'test_serializer_node_display_field', 'test_serializer_node_exposed_filter');
public static $testViews = array('test_serializer_display_field', 'test_serializer_display_entity', 'test_serializer_display_entity_translated', 'test_serializer_node_display_field', 'test_serializer_node_exposed_filter');
/**
* A user with administrative privileges to look at test entity and configure views.
@ -750,4 +751,39 @@ class StyleSerializerTest extends PluginTestBase {
$this->assertEqual($result, $expected, 'Querying a view with a starts with exposed filter on the title returns nodes whose title starts with value provided.');
$this->assertCacheContexts($cache_contexts);
}
/**
* Test multilingual entity rows.
*/
public function testMulEntityRows() {
// Create some languages.
ConfigurableLanguage::createFromLangcode('l1')->save();
ConfigurableLanguage::createFromLangcode('l2')->save();
// Create an entity with no translations.
$storage = \Drupal::entityTypeManager()->getStorage('entity_test_mul');
$storage->create(['langcode' => 'l1', 'name' => 'mul-none'])->save();
// Create some entities with translations.
$entity = $storage->create(['langcode' => 'l1', 'name' => 'mul-l1-orig']);
$entity->save();
$entity->addTranslation('l2', ['name' => 'mul-l1-l2'])->save();
$entity = $storage->create(['langcode' => 'l2', 'name' => 'mul-l2-orig']);
$entity->save();
$entity->addTranslation('l1', ['name' => 'mul-l2-l1'])->save();
// Get the names of the output.
$json = $this->drupalGetWithFormat('test/serialize/translated_entity', 'json');
$decoded = $this->container->get('serializer')->decode($json, 'hal_json');
$names = [];
foreach ($decoded as $item) {
$names[] = $item['name'][0]['value'];
}
sort($names);
// Check that the names are correct.
$expected = ['mul-l1-l2', 'mul-l1-orig', 'mul-l2-l1', 'mul-l2-orig', 'mul-none'];
$this->assertIdentical($names, $expected, 'The translated content was found in the JSON.');
}
}

View file

@ -0,0 +1,47 @@
langcode: en
status: true
dependencies:
module:
- entity_test
- rest
id: test_serializer_display_entity_translated
label: 'Test serialize translated entity rows'
module: rest
description: ''
tag: ''
base_table: entity_test_mul_property_data
base_field: id
core: 8.x
display:
default:
display_plugin: default
id: default
display_title: Master
position: null
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
query:
type: views_query
exposed_form:
type: basic
style:
type: serializer
row:
type: data_entity
title: 'Test serialize translated entity rows'
rendering_language: '***LANGUAGE_entity_translation***'
arguments: { }
rest_export_1:
display_plugin: rest_export
id: rest_export_1
display_title: serializer
position: null
display_options:
defaults:
access: false
path: test/serialize/translated_entity

View file

@ -133,4 +133,5 @@ class StubRequestHandlerResourcePlugin extends ResourceBase {
function get() {}
function patch() {}
}

View file

@ -133,4 +133,5 @@ class CollectRoutesTest extends UnitTestCase {
$this->assertEquals(count($requirements_1), 0, 'First route has no requirement.');
$this->assertEquals(count($requirements_2), 2, 'Views route with rest export had the format and method requirements added.');
}
}