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:
parent
9eae24d844
commit
28556d630e
1322 changed files with 6699 additions and 2064 deletions
|
@ -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'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -36,4 +36,5 @@ interface TypeLinkManagerInterface extends ConfigurableLinkManagerInterface {
|
|||
* bundle. Otherwise, returns false.
|
||||
*/
|
||||
public function getTypeInternalIds($type_uri, $context = array());
|
||||
|
||||
}
|
||||
|
|
|
@ -93,4 +93,5 @@ class EntityDeriver implements ContainerDeriverInterface {
|
|||
}
|
||||
return $this->derivatives;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -42,4 +42,5 @@ class ResourcePluginManager extends DefaultPluginManager {
|
|||
return $this->createInstance($options['id']);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -131,4 +131,5 @@ class RequestHandler implements ContainerAwareInterface {
|
|||
public function csrfToken() {
|
||||
return new Response(\Drupal::csrfToken()->get('rest'), 200, array('Content-Type' => 'text/plain'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -49,4 +49,5 @@ class ResourceResponse extends Response implements CacheableResponseInterface {
|
|||
public function getResponseData() {
|
||||
return $this->responseData;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -114,4 +114,5 @@ class CsrfTest extends RESTTestBase {
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -72,4 +72,5 @@ class DeleteTest extends RESTTestBase {
|
|||
$this->assertEqual($account->id(), $user->id(), 'User still exists in the database.');
|
||||
$this->assertResponse(405);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -194,4 +194,5 @@ class NodeTest extends RESTTestBase {
|
|||
$this->assertResponse(400);
|
||||
$this->assertResponseBody('{"error":"A string must be provided as a bundle value."}');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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('');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -133,4 +133,5 @@ class StubRequestHandlerResourcePlugin extends ResourceBase {
|
|||
|
||||
function get() {}
|
||||
function patch() {}
|
||||
|
||||
}
|
||||
|
|
|
@ -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.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Reference in a new issue