SA-CORE-2019-003 by samuel.mortenson, Berdir, pwolanin, dawehner, cashwilliams, Wim Leers, xjm, larowlan, alexpott, plach, damiankloip, tstoeckler, tedbow, DamienMcKenna, effulgentsia, RobLoach, gabesullice, drumm, heshanlk, dsnopek, fago, miro_dietiker, truls1502
parent
4837822141
commit
a12f2100f8
|
|
@ -78,13 +78,15 @@ class EntityCreateAnyAccessCheck implements AccessInterface {
|
|||
if ($entity_type->getBundleEntityType()) {
|
||||
$access->addCacheTags($this->entityTypeManager->getDefinition($entity_type->getBundleEntityType())->getListCacheTags());
|
||||
|
||||
// Check if the user is allowed to create new bundles. If so, allow
|
||||
// access, so the add page can show a link to create one.
|
||||
// @see \Drupal\Core\Entity\Controller\EntityController::addPage()
|
||||
$bundle_access_control_handler = $this->entityTypeManager->getAccessControlHandler($entity_type->getBundleEntityType());
|
||||
$access = $access->orIf($bundle_access_control_handler->createAccess(NULL, $account, [], TRUE));
|
||||
if ($access->isAllowed()) {
|
||||
return $access;
|
||||
if (empty($route->getOption('_ignore_create_bundle_access'))) {
|
||||
// Check if the user is allowed to create new bundles. If so, allow
|
||||
// access, so the add page can show a link to create one.
|
||||
// @see \Drupal\Core\Entity\Controller\EntityController::addPage()
|
||||
$bundle_access_control_handler = $this->entityTypeManager->getAccessControlHandler($entity_type->getBundleEntityType());
|
||||
$access = $access->orIf($bundle_access_control_handler->createAccess(NULL, $account, [], TRUE));
|
||||
if ($access->isAllowed()) {
|
||||
return $access;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,12 @@ class MapItem extends FieldItemBase {
|
|||
$values = $values->getValue();
|
||||
}
|
||||
else {
|
||||
$values = unserialize($values);
|
||||
if (version_compare(PHP_VERSION, '7.0.0', '>=')) {
|
||||
$values = unserialize($values, ['allowed_classes' => FALSE]);
|
||||
}
|
||||
else {
|
||||
$values = unserialize($values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ namespace Drupal\hal\Normalizer;
|
|||
use Drupal\Core\Field\FieldItemInterface;
|
||||
use Drupal\Core\TypedData\TypedDataInternalPropertiesHelper;
|
||||
use Drupal\serialization\Normalizer\FieldableEntityNormalizerTrait;
|
||||
use Drupal\serialization\Normalizer\SerializedColumnNormalizerTrait;
|
||||
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
|
|
@ -13,6 +14,7 @@ use Symfony\Component\Serializer\Exception\InvalidArgumentException;
|
|||
class FieldItemNormalizer extends NormalizerBase {
|
||||
|
||||
use FieldableEntityNormalizerTrait;
|
||||
use SerializedColumnNormalizerTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
|
@ -45,6 +47,7 @@ class FieldItemNormalizer extends NormalizerBase {
|
|||
}
|
||||
|
||||
$field_item = $context['target_instance'];
|
||||
$this->checkForSerializedStrings($data, $class, $field_item);
|
||||
|
||||
// If this field is translatable, we need to create a translated instance.
|
||||
if (isset($data['lang'])) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
namespace Drupal\Tests\hal\Kernel;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\entity_test\Entity\EntitySerializedField;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
||||
|
||||
|
|
|
|||
|
|
@ -191,7 +191,12 @@ class LinkItem extends FieldItemBase implements LinkItemInterface {
|
|||
// SqlContentEntityStorage::loadFieldItems, see
|
||||
// https://www.drupal.org/node/2414835
|
||||
if (is_string($values['options'])) {
|
||||
$values['options'] = unserialize($values['options']);
|
||||
if (version_compare(PHP_VERSION, '7.0.0', '>=')) {
|
||||
$values['options'] = unserialize($values['options'], ['allowed_classes' => FALSE]);
|
||||
}
|
||||
else {
|
||||
$values['options'] = unserialize($values['options']);
|
||||
}
|
||||
}
|
||||
parent::setValue($values, $notify);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -359,7 +359,7 @@ abstract class MediaResourceTestBase extends EntityResourceTestBase {
|
|||
|
||||
// To still run the complete test coverage for POSTing a Media entity, we
|
||||
// must revoke the additional permissions that we granted.
|
||||
$role = Role::load(static::$auth ? RoleInterface::AUTHENTICATED_ID : RoleInterface::AUTHENTICATED_ID);
|
||||
$role = Role::load(static::$auth ? RoleInterface::AUTHENTICATED_ID : RoleInterface::ANONYMOUS_ID);
|
||||
$role->revokePermission('create camelids media');
|
||||
$role->trustData()->save();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ class NodeAccessControlHandler extends EntityAccessControlHandler implements Nod
|
|||
return $return_as_object ? $result : $result->isAllowed();
|
||||
}
|
||||
if (!$account->hasPermission('access content')) {
|
||||
$result = AccessResult::forbidden()->cachePerPermissions();
|
||||
$result = AccessResult::forbidden("The 'access content' permission is required.")->cachePerPermissions();
|
||||
return $return_as_object ? $result : $result->isAllowed();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@ abstract class NodeResourceTestBase extends EntityResourceTestBase {
|
|||
return parent::getExpectedUnauthorizedAccessMessage($method);
|
||||
}
|
||||
|
||||
if ($method === 'GET' || $method == 'PATCH' || $method == 'DELETE') {
|
||||
if ($method === 'GET' || $method == 'PATCH' || $method == 'DELETE' || $method == 'POST') {
|
||||
return "The 'access content' permission is required.";
|
||||
}
|
||||
return parent::getExpectedUnauthorizedAccessMessage($method);
|
||||
|
|
|
|||
|
|
@ -61,3 +61,10 @@ function rest_post_update_resource_granularity() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear caches due to changes in route definitions.
|
||||
*/
|
||||
function rest_post_update_161923() {
|
||||
// Empty post-update hook.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -377,6 +377,10 @@ class EntityResource extends ResourceBase implements DependentPluginInterface {
|
|||
case 'GET':
|
||||
$route->setRequirement('_entity_access', $this->entityType->id() . '.view');
|
||||
break;
|
||||
case 'POST':
|
||||
$route->setRequirement('_entity_create_any_access', $this->entityType->id());
|
||||
$route->setOption('_ignore_create_bundle_access', TRUE);
|
||||
break;
|
||||
case 'PATCH':
|
||||
$route->setRequirement('_entity_access', $this->entityType->id() . '.update');
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -872,18 +872,6 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
|
|||
|
||||
$request_options[RequestOptions::HEADERS]['Content-Type'] = static::$mimeType;
|
||||
|
||||
// DX: 400 when no request body.
|
||||
$response = $this->request('POST', $url, $request_options);
|
||||
$this->assertResourceErrorResponse(400, 'No entity content received.', $response);
|
||||
|
||||
$request_options[RequestOptions::BODY] = $unparseable_request_body;
|
||||
|
||||
// DX: 400 when unparseable request body.
|
||||
$response = $this->request('POST', $url, $request_options);
|
||||
$this->assertResourceErrorResponse(400, 'Syntax error', $response);
|
||||
|
||||
$request_options[RequestOptions::BODY] = $parseable_invalid_request_body;
|
||||
|
||||
if (static::$auth) {
|
||||
// DX: forgetting authentication: authentication provider-specific error
|
||||
// response.
|
||||
|
|
@ -895,16 +883,22 @@ abstract class EntityResourceTestBase extends ResourceTestBase {
|
|||
|
||||
// DX: 403 when unauthorized.
|
||||
$response = $this->request('POST', $url, $request_options);
|
||||
// @todo Remove this if-test in https://www.drupal.org/project/drupal/issues/2820364
|
||||
if (static::$entityTypeId === 'media' && !static::$auth) {
|
||||
$this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nname: Name: this field cannot hold more than 1 values.\nfield_media_file.0: You do not have access to the referenced entity (file: 3).\n", $response);
|
||||
}
|
||||
else {
|
||||
$this->assertResourceErrorResponse(403, $this->getExpectedUnauthorizedAccessMessage('POST'), $response);
|
||||
}
|
||||
$this->assertResourceErrorResponse(403, $this->getExpectedUnauthorizedAccessMessage('POST'), $response);
|
||||
|
||||
$this->setUpAuthorization('POST');
|
||||
|
||||
// DX: 400 when no request body.
|
||||
$response = $this->request('POST', $url, $request_options);
|
||||
$this->assertResourceErrorResponse(400, 'No entity content received.', $response);
|
||||
|
||||
$request_options[RequestOptions::BODY] = $unparseable_request_body;
|
||||
|
||||
// DX: 400 when unparseable request body.
|
||||
$response = $this->request('POST', $url, $request_options);
|
||||
$this->assertResourceErrorResponse(400, 'Syntax error', $response);
|
||||
|
||||
$request_options[RequestOptions::BODY] = $parseable_invalid_request_body;
|
||||
|
||||
// DX: 422 when invalid entity: multiple values sent for single-value field.
|
||||
$response = $this->request('POST', $url, $request_options);
|
||||
$label_field = $this->entity->getEntityType()->hasKey('label') ? $this->entity->getEntityType()->getKey('label') : static::$labelFieldName;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
|||
class FieldItemNormalizer extends ComplexDataNormalizer implements DenormalizerInterface {
|
||||
|
||||
use FieldableEntityNormalizerTrait;
|
||||
use SerializedColumnNormalizerTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
|
@ -32,6 +33,7 @@ class FieldItemNormalizer extends ComplexDataNormalizer implements DenormalizerI
|
|||
|
||||
/** @var \Drupal\Core\Field\FieldItemInterface $field_item */
|
||||
$field_item = $context['target_instance'];
|
||||
$this->checkForSerializedStrings($data, $class, $field_item);
|
||||
|
||||
$field_item->setValue($this->constructValue($data, $context));
|
||||
return $field_item;
|
||||
|
|
|
|||
|
|
@ -223,15 +223,24 @@ trait FieldableEntityNormalizerTrait {
|
|||
assert($item_definition instanceof FieldItemDataDefinitionInterface);
|
||||
$property_definitions = $item_definition->getPropertyDefinitions();
|
||||
|
||||
if (!is_array($data)) {
|
||||
$property_value = $data;
|
||||
$property_value_class = $property_definitions[$item_definition->getMainPropertyName()]->getClass();
|
||||
$serialized_property_names = $this->getCustomSerializedPropertyNames($field_item);
|
||||
$denormalize_property = function ($property_name, $property_value, $property_value_class, $context) use ($serialized_property_names) {
|
||||
if ($this->serializer->supportsDenormalization($property_value, $property_value_class, NULL, $context)) {
|
||||
return $this->serializer->denormalize($property_value, $property_value_class, NULL, $context);
|
||||
}
|
||||
else {
|
||||
if (in_array($property_name, $serialized_property_names, TRUE)) {
|
||||
$property_value = serialize($property_value);
|
||||
}
|
||||
return $property_value;
|
||||
}
|
||||
};
|
||||
|
||||
if (!is_array($data)) {
|
||||
$property_value = $data;
|
||||
$property_name = $item_definition->getMainPropertyName();
|
||||
$property_value_class = $property_definitions[$property_name]->getClass();
|
||||
return $denormalize_property($property_name, $property_value, $property_value_class, $context);
|
||||
}
|
||||
|
||||
$data_internal = [];
|
||||
|
|
@ -243,12 +252,7 @@ trait FieldableEntityNormalizerTrait {
|
|||
}
|
||||
$property_value = $data[$property_name];
|
||||
$property_value_class = $property_definition->getClass();
|
||||
if ($this->serializer->supportsDenormalization($property_value, $property_value_class, NULL, $context)) {
|
||||
$data_internal[$property_name] = $this->serializer->denormalize($property_value, $property_value_class, NULL, $context);
|
||||
}
|
||||
else {
|
||||
$data_internal[$property_name] = $property_value;
|
||||
}
|
||||
$data_internal[$property_name] = $denormalize_property($property_name, $property_value, $property_value_class, $context);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Drupal\serialization\Normalizer;
|
||||
|
||||
use Drupal\Core\Field\FieldItemInterface;
|
||||
use Drupal\Core\TypedData\PrimitiveInterface;
|
||||
|
||||
/**
|
||||
|
|
@ -9,6 +10,8 @@ use Drupal\Core\TypedData\PrimitiveInterface;
|
|||
*/
|
||||
class PrimitiveDataNormalizer extends NormalizerBase {
|
||||
|
||||
use SerializedColumnNormalizerTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
@ -18,6 +21,14 @@ class PrimitiveDataNormalizer extends NormalizerBase {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function normalize($object, $format = NULL, array $context = []) {
|
||||
$parent = $object->getParent();
|
||||
if ($parent instanceof FieldItemInterface && $object->getValue()) {
|
||||
$serialized_property_names = $this->getCustomSerializedPropertyNames($parent);
|
||||
if (in_array($object->getName(), $serialized_property_names, TRUE)) {
|
||||
return unserialize($object->getValue());
|
||||
}
|
||||
}
|
||||
|
||||
// Typed data casts NULL objects to their empty variants, so for example
|
||||
// the empty string ('') for string type data, or 0 for integer typed data.
|
||||
// In a better world with typed data implementing algebraic data types,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,116 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\serialization\Normalizer;
|
||||
|
||||
use Drupal\Component\Plugin\PluginInspectionInterface;
|
||||
use Drupal\Core\Field\FieldItemInterface;
|
||||
|
||||
/**
|
||||
* A trait providing methods for serialized columns.
|
||||
*/
|
||||
trait SerializedColumnNormalizerTrait {
|
||||
|
||||
/**
|
||||
* Checks if there is a serialized string for a column.
|
||||
*
|
||||
* @param mixed $data
|
||||
* The field item data to denormalize.
|
||||
* @param string $class
|
||||
* The expected class to instantiate.
|
||||
* @param \Drupal\Core\Field\FieldItemInterface $field_item
|
||||
* The field item.
|
||||
*/
|
||||
protected function checkForSerializedStrings($data, $class, FieldItemInterface $field_item) {
|
||||
// Require specialized denormalizers for fields with 'serialize' columns.
|
||||
// Note: this cannot be checked in ::supportsDenormalization() because at
|
||||
// that time we only have the field item class. ::hasSerializeColumn()
|
||||
// must be able to call $field_item->schema(), which requires a field
|
||||
// storage definition. To determine that, the entity type and bundle
|
||||
// must be known, which is contextual information that the Symfony
|
||||
// serializer does not pass to ::supportsDenormalization().
|
||||
if (!is_array($data)) {
|
||||
$data = [$field_item->getDataDefinition()->getMainPropertyName() => $data];
|
||||
}
|
||||
if ($this->dataHasStringForSerializeColumn($field_item, $data)) {
|
||||
$field_name = $field_item->getParent() ? $field_item->getParent()->getName() : $field_item->getName();
|
||||
throw new \LogicException(sprintf('The generic FieldItemNormalizer cannot denormalize string values for "%s" properties of the "%s" field (field item class: %s).', implode('", "', $this->getSerializedPropertyNames($field_item)), $field_name, $class));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the data contains string value for serialize column.
|
||||
*
|
||||
* @param \Drupal\Core\Field\FieldItemInterface $field_item
|
||||
* The field item.
|
||||
* @param array $data
|
||||
* The data being denormalized.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if there is a string value for serialize column, otherwise FALSE.
|
||||
*/
|
||||
protected function dataHasStringForSerializeColumn(FieldItemInterface $field_item, array $data) {
|
||||
foreach ($this->getSerializedPropertyNames($field_item) as $property_name) {
|
||||
if (isset($data[$property_name]) && is_string($data[$property_name])) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the names of all serialized properties.
|
||||
*
|
||||
* @param \Drupal\Core\Field\FieldItemInterface $field_item
|
||||
* The field item.
|
||||
*
|
||||
* @return string[]
|
||||
* The property names for serialized properties.
|
||||
*/
|
||||
protected function getSerializedPropertyNames(FieldItemInterface $field_item) {
|
||||
$field_storage_definition = $field_item->getFieldDefinition()->getFieldStorageDefinition();
|
||||
|
||||
if ($custom_property_names = $this->getCustomSerializedPropertyNames($field_item)) {
|
||||
return $custom_property_names;
|
||||
}
|
||||
|
||||
$field_storage_schema = $field_item->schema($field_storage_definition);
|
||||
// If there are no columns then there are no serialized properties.
|
||||
if (!isset($field_storage_schema['columns'])) {
|
||||
return [];
|
||||
}
|
||||
$serialized_columns = array_filter($field_storage_schema['columns'], function ($column_schema) {
|
||||
return isset($column_schema['serialize']) && $column_schema['serialize'] === TRUE;
|
||||
});
|
||||
return array_keys($serialized_columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the names of all properties the plugin treats as serialized data.
|
||||
*
|
||||
* This allows the field storage definition or entity type to provide a
|
||||
* setting for serialized properties. This can be used for fields that
|
||||
* handle serialized data themselves and do not rely on the serialized schema
|
||||
* flag.
|
||||
*
|
||||
* @param \Drupal\Core\Field\FieldItemInterface $field_item
|
||||
* The field item.
|
||||
*
|
||||
* @return string[]
|
||||
* The property names for serialized properties.
|
||||
*/
|
||||
protected function getCustomSerializedPropertyNames(FieldItemInterface $field_item) {
|
||||
if ($field_item instanceof PluginInspectionInterface) {
|
||||
$definition = $field_item->getPluginDefinition();
|
||||
$serialized_fields = $field_item->getEntity()->getEntityType()->get('serialized_field_property_names');
|
||||
$field_name = $field_item->getFieldDefinition()->getName();
|
||||
if (is_array($serialized_fields) && isset($serialized_fields[$field_name]) && is_array($serialized_fields[$field_name])) {
|
||||
return $serialized_fields[$field_name];
|
||||
}
|
||||
if (isset($definition['serialized_property_names']) && is_array($definition['serialized_property_names'])) {
|
||||
return $definition['serialized_property_names'];
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ namespace Drupal\Tests\serialization\Kernel;
|
|||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\entity_test\Entity\EntitySerializedField;
|
||||
use Drupal\entity_test\Entity\EntityTestMulRev;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
namespace Drupal\Tests\serialization\Unit\Normalizer;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\TypedData\FieldItemDataDefinition;
|
||||
use Drupal\Core\GeneratedUrl;
|
||||
|
|
@ -435,6 +436,33 @@ class EntityReferenceFieldItemNormalizerTest extends UnitTestCase {
|
|||
->shouldBeCalled();
|
||||
}
|
||||
|
||||
// Avoid a static method call by returning dummy property data.
|
||||
$this->fieldDefinition
|
||||
->getFieldStorageDefinition()
|
||||
->willReturn()
|
||||
->shouldBeCalled();
|
||||
$this->fieldDefinition
|
||||
->getName()
|
||||
->willReturn('field_reference')
|
||||
->shouldBeCalled();
|
||||
$entity = $this->prophesize(EntityInterface::class);
|
||||
$entity_type = $this->prophesize(EntityTypeInterface::class);
|
||||
$entity->getEntityType()
|
||||
->willReturn($entity_type->reveal())
|
||||
->shouldBeCalled();
|
||||
$this->fieldItem
|
||||
->getPluginDefinition()
|
||||
->willReturn([
|
||||
'serialized_property_names' => [
|
||||
'foo' => 'bar',
|
||||
],
|
||||
])
|
||||
->shouldBeCalled();
|
||||
$this->fieldItem
|
||||
->getEntity()
|
||||
->willReturn($entity->reveal())
|
||||
->shouldBeCalled();
|
||||
|
||||
$context = ['target_instance' => $this->fieldItem->reveal()];
|
||||
$denormalized = $this->normalizer->denormalize($data, EntityReferenceItem::class, 'json', $context);
|
||||
$this->assertSame($context['target_instance'], $denormalized);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
namespace Drupal\Tests\serialization\Unit\Normalizer;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\Plugin\Field\FieldType\CreatedItem;
|
||||
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
|
||||
use Drupal\Core\Field\Plugin\Field\FieldType\TimestampItem;
|
||||
|
|
@ -120,6 +123,29 @@ class TimestampItemNormalizerTest extends UnitTestCase {
|
|||
$timestamp_item->setValue(['value' => $timestamp_data_denormalization])
|
||||
->shouldBeCalled();
|
||||
|
||||
// Avoid a static method call by returning dummy property data.
|
||||
$field_definition = $this->prophesize(FieldDefinitionInterface::class);
|
||||
$timestamp_item
|
||||
->getFieldDefinition()
|
||||
->willReturn($field_definition->reveal())
|
||||
->shouldBeCalled();
|
||||
$timestamp_item->getPluginDefinition()
|
||||
->willReturn([
|
||||
'serialized_property_names' => [
|
||||
'foo' => 'bar',
|
||||
],
|
||||
])
|
||||
->shouldBeCalled();
|
||||
$entity = $this->prophesize(EntityInterface::class);
|
||||
$entity_type = $this->prophesize(EntityTypeInterface::class);
|
||||
$entity->getEntityType()
|
||||
->willReturn($entity_type->reveal())
|
||||
->shouldBeCalled();
|
||||
$timestamp_item
|
||||
->getEntity()
|
||||
->willReturn($entity->reveal())
|
||||
->shouldBeCalled();
|
||||
|
||||
$context = [
|
||||
'target_instance' => $timestamp_item->reveal(),
|
||||
'datetime_allowed_formats' => [\DateTime::RFC3339],
|
||||
|
|
|
|||
Loading…
Reference in New Issue