Commit 6edcdee3 authored by webchick's avatar webchick

Issue #1709960 by Berdir, zserno, jessebeach | joachim: Declare a maximum...

Issue #1709960 by Berdir, zserno, jessebeach | joachim: Declare a maximum length for entity and bundle machine names.
parent f5e0c172
......@@ -9,6 +9,8 @@
use Drupal\Core\DependencyInjection\DependencySerialization;
use Drupal\Component\Utility\String;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Config\Entity\Exception\ConfigEntityIdLengthException;
use Drupal\Core\Entity\Exception\UndefinedLinkTemplateException;
use Drupal\Core\Language\Language;
use Drupal\Core\Session\AccountInterface;
......@@ -336,6 +338,18 @@ public function getEntityType() {
* {@inheritdoc}
*/
public function preSave(EntityStorageInterface $storage) {
// Check if this is an entity bundle.
if ($this->getEntityType()->getBundleOf()) {
// Throw an exception if the bundle ID is longer than 32 characters.
if (Unicode::strlen($this->id()) > EntityTypeInterface::BUNDLE_MAX_LENGTH) {
throw new ConfigEntityIdLengthException(String::format(
'Attempt to create a bundle with an ID longer than @max characters: @id.', array(
'@max' => EntityTypeInterface::BUNDLE_MAX_LENGTH,
'@id' => $this->id(),
)
));
}
}
}
/**
......
......@@ -7,7 +7,9 @@
namespace Drupal\Core\Entity;
use Drupal\Component\Utility\String;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Entity\Exception\EntityTypeIdLengthException;
/**
* Provides an implementation of an entity type and its metadata.
......@@ -182,8 +184,21 @@ class EntityType implements EntityTypeInterface {
*
* @param array $definition
* An array of values from the annotation.
*
* @throws \Drupal\Core\Entity\Exception\EntityTypeIdLengthException
* Thrown when attempting to instantiate an entity type with too long ID.
*/
public function __construct($definition) {
// Throw an exception if the entity type ID is longer than 32 characters.
if (Unicode::strlen($definition['id']) > static::ID_MAX_LENGTH) {
throw new EntityTypeIdLengthException(String::format(
'Attempt to create an entity type with an ID longer than @max characters: @id.', array(
'@max' => static::ID_MAX_LENGTH,
'@id' => $definition['id'],
)
));
}
foreach ($definition as $property => $value) {
$this->{$property} = $value;
}
......
......@@ -17,6 +17,16 @@
*/
interface EntityTypeInterface {
/**
* The maximum length of ID, in characters.
*/
const ID_MAX_LENGTH = 32;
/**
* The maximum length of bundle name, in characters.
*/
const BUNDLE_MAX_LENGTH = 32;
/**
* Gets any arbitrary property.
*
......
<?php
/**
* @file
* Contains \Drupal\Core\Entity\Exception\EntityTypeIdLengthException.
*/
namespace Drupal\Core\Entity\Exception;
/**
* Defines an exception thrown when an entity ID is too long.
*/
class EntityTypeIdLengthException extends \Exception { }
......@@ -5,6 +5,8 @@
* Install, update and uninstall functions for the custom block module.
*/
use Drupal\Core\Entity\EntityTypeInterface;
/**
* Implements hook_schema().
*/
......@@ -44,7 +46,7 @@ function custom_block_schema() {
'type' => array(
'description' => 'The type of this custom block.',
'type' => 'varchar',
'length' => 32,
'length' => EntityTypeInterface::BUNDLE_MAX_LENGTH,
'not null' => TRUE,
'default' => '',
),
......@@ -64,7 +66,7 @@ function custom_block_schema() {
),
'primary key' => array('id'),
'indexes' => array(
'block_custom_type' => array(array('type', 4)),
'block_custom_type' => array('type'),
),
'unique keys' => array(
'revision_id' => array('revision_id'),
......@@ -161,7 +163,7 @@ function custom_block_schema_0() {
'type' => array(
'description' => 'The type of this custom block.',
'type' => 'varchar',
'length' => 32,
'length' => EntityTypeInterface::BUNDLE_MAX_LENGTH,
'not null' => TRUE,
'default' => '',
),
......@@ -181,7 +183,7 @@ function custom_block_schema_0() {
),
'primary key' => array('id'),
'indexes' => array(
'block_custom_type' => array(array('type', 4)),
'block_custom_type' => array('type'),
),
'unique keys' => array(
'revision_id' => array('revision_id'),
......
......@@ -8,6 +8,7 @@
namespace Drupal\custom_block;
use Drupal\Core\Entity\EntityFormController;
use Drupal\Core\Entity\EntityTypeInterface;
/**
* Base form controller for category edit forms.
......@@ -36,6 +37,7 @@ public function form(array $form, array &$form_state) {
'#machine_name' => array(
'exists' => 'custom_block_type_load',
),
'#maxlength' => EntityTypeInterface::BUNDLE_MAX_LENGTH,
'#disabled' => !$block_type->isNew(),
);
......
......@@ -190,7 +190,8 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['type'] = FieldDefinition::create('entity_reference')
->setLabel(t('Block type'))
->setDescription(t('The block type.'))
->setSetting('target_type', 'custom_block_type');
->setSetting('target_type', 'custom_block_type')
->setSetting('max_length', EntityTypeInterface::BUNDLE_MAX_LENGTH);
$fields['log'] = FieldDefinition::create('string')
->setLabel(t('Revision log message'))
......
......@@ -7,6 +7,7 @@
namespace Drupal\comment\Tests\Entity;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Entity\EntityType;
use Drupal\Tests\UnitTestCase;
/**
......@@ -74,10 +75,16 @@ public function testLocks() {
$comment->expects($this->any())
->method('getThread')
->will($this->returnValue(''));
$comment->expects($this->at(0))
$entity_type = $this->getMock('\Drupal\Core\Entity\EntityTypeInterface');
$comment->expects($this->any())
->method('getEntityType')
->will($this->returnValue($entity_type));
$comment->expects($this->at(1))
->method('get')
->with('status')
->will($this->returnValue((object) array('value' => NULL)));
$storage = $this->getMock('Drupal\comment\CommentStorageInterface');
$comment->preSave($storage);
$comment->postSave($storage);
......
......@@ -5,6 +5,7 @@
* Installation functions for Content Translation module.
*/
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Language\Language;
use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
......@@ -17,7 +18,7 @@ function content_translation_schema() {
'fields' => array(
'entity_type' => array(
'type' => 'varchar',
'length' => 128,
'length' => EntityTypeInterface::ID_MAX_LENGTH,
'not null' => TRUE,
'default' => '',
'description' => 'The entity type this translation relates to',
......
......@@ -9,6 +9,7 @@
use Drupal\Core\Entity\EntityFormController;
use Drupal\Component\Utility\String;
use Drupal\Core\Entity\EntityTypeInterface;
/**
* Form controller for node type forms.
......@@ -45,7 +46,7 @@ public function form(array $form, array &$form_state) {
$form['type'] = array(
'#type' => 'machine_name',
'#default_value' => $type->id(),
'#maxlength' => 32,
'#maxlength' => EntityTypeInterface::BUNDLE_MAX_LENGTH,
'#disabled' => $type->isLocked(),
'#machine_name' => array(
'exists' => 'node_type_load',
......
......@@ -122,7 +122,8 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['vid'] = FieldDefinition::create('entity_reference')
->setLabel(t('Vocabulary'))
->setDescription(t('The vocabulary to which the term is assigned.'))
->setSetting('target_type', 'taxonomy_vocabulary');
->setSetting('target_type', 'taxonomy_vocabulary')
->setSetting('max_length', EntityTypeInterface::BUNDLE_MAX_LENGTH);
$fields['langcode'] = FieldDefinition::create('language')
->setLabel(t('Language code'))
......
......@@ -258,14 +258,19 @@ function testTermAutocompletion() {
$third_term->save();
// Try to autocomplete a term name that matches both terms.
// We should get both term in a json encoded string.
// We should get both terms in a json encoded string.
$input = '10/';
$path = 'taxonomy/autocomplete/node/taxonomy_' . $this->vocabulary->id();
// The result order is not guaranteed, so check each term separately.
$result = $this->drupalGet($path, array('query' => array('q' => $input)));
// Pull the label properties from the array of arrays.
$data = Json::decode($result);
$this->assertEqual($data[0]['label'], String::checkPlain($first_term->getName()), 'Autocomplete returned the first matching term');
$this->assertEqual($data[1]['label'], String::checkPlain($second_term->getName()), 'Autocomplete returned the second matching term');
$data = array_map(function ($item) {
return $item['label'];
}, $data);
$this->assertTrue(in_array(String::checkPlain($first_term->getName()), $data), 'Autocomplete returned the first matching term');
$this->assertTrue(in_array(String::checkPlain($second_term->getName()), $data), 'Autocomplete returned the second matching term');
// Try to autocomplete a term name that matches first term.
// We should only get the first term in a json encoded string.
......
......@@ -8,6 +8,7 @@
namespace Drupal\taxonomy;
use Drupal\Core\Entity\EntityFormController;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Language\Language;
/**
......@@ -37,7 +38,7 @@ public function form(array $form, array &$form_state) {
$form['vid'] = array(
'#type' => 'machine_name',
'#default_value' => $vocabulary->id(),
'#maxlength' => 255,
'#maxlength' => EntityTypeInterface::BUNDLE_MAX_LENGTH,
'#machine_name' => array(
'exists' => 'taxonomy_vocabulary_load',
'source' => array('name'),
......
......@@ -5,6 +5,8 @@
* Install, update and uninstall functions for the taxonomy module.
*/
use Drupal\Core\Entity\EntityTypeInterface;
/**
* Implements hook_schema().
*/
......@@ -26,7 +28,7 @@ function taxonomy_schema() {
),
'vid' => array(
'type' => 'varchar',
'length' => 255,
'length' => EntityTypeInterface::BUNDLE_MAX_LENGTH,
'not null' => TRUE,
'default' => '',
'description' => 'The ID of the vocabulary to which the term is assigned.',
......@@ -75,8 +77,8 @@ function taxonomy_schema() {
'uuid' => array('uuid'),
),
'indexes' => array(
'taxonomy_tree' => array(array('vid', 64), 'weight', 'name'),
'vid_name' => array(array('vid', 64), 'name'),
'taxonomy_tree' => array('vid', 'weight', 'name'),
'vid_name' => array('vid', 'name'),
'name' => array('name'),
),
);
......
......@@ -11,6 +11,7 @@
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Language\Language;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* @coversDefaultClass \Drupal\Core\Config\Entity\ConfigEntityStorage
......@@ -75,6 +76,13 @@ class ConfigEntityStorageTest extends UnitTestCase {
*/
protected $entityQuery;
/**
* The entity manager used for testing.
*
* @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $entityManager;
/**
* {@inheritdoc}
*/
......@@ -129,6 +137,17 @@ protected function setUp() {
->method('getQuery')
->will($this->returnValue($this->entityQuery));
$this->entityStorage->setModuleHandler($this->moduleHandler);
$this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface');
$this->entityManager->expects($this->any())
->method('getDefinition')
->with('test_entity_type')
->will($this->returnValue($this->entityType));
$container = new ContainerBuilder();
$container->set('entity.manager', $this->entityManager);
\Drupal::setContainer($container);
}
/**
......
......@@ -30,104 +30,62 @@ public static function getInfo() {
);
}
/**
* Tests that we get an exception when the length of the config prefix that is
* returned by getConfigPrefix() exceeds the maximum defined prefix length.
* Sets up a ConfigEntityType object for a given set of values.
*
* @dataProvider providerPrefixLengthExceeds
* @covers ::getConfigPrefix()
* @param array $definition
* An array of values to use for the ConfigEntityType.
*
* @return \Drupal\Core\Entity\EntityTypeInterface
*/
public function testConfigPrefixLengthExceeds($entity_data, $exception, $message) {
$config_entity = new ConfigEntityType($entity_data);
$this->setExpectedException($exception, $message);
$this->assertEmpty($config_entity->getConfigPrefix());
protected function setUpConfigEntityType($definition) {
if (!isset($definition['id'])) {
$definition += array(
'id' => 'example_config_entity_type',
);
}
return new ConfigEntityType($definition);
}
/**
* Provides arguments to instantiate a ConfigEntityType with a configuration
* entity prefix that exceeds the maximum character length.
* Tests that we get an exception when the length of the config prefix that is
* returned by getConfigPrefix() exceeds the maximum defined prefix length.
*
* @return array
* @covers ::getConfigPrefix()
*/
public function providerPrefixLengthExceeds() {
$test_parameters = array();
public function testConfigPrefixLengthExceeds() {
$message_text = 'The configuration file name prefix @config_prefix exceeds the maximum character limit of @max_char.';
// A provider length of 24 and id length of 59 (+1 for the .) results
// in a config length of 84, which is too long.
$entity_data = array(
'provider' => $this->randomName(24),
'id' => $this->randomName(59),
);
$test_parameters[] = array(
$entity_data,
'\Drupal\Core\Config\ConfigPrefixLengthException',
String::format($message_text, array(
'@config_prefix' => $entity_data['provider'] . '.' . $entity_data['id'],
'@max_char' => ConfigEntityType::PREFIX_LENGTH,
)),
);
// A provider length of 24 and config_prefix length of 59 (+1 for the .)
// results in a config length of 84, which is too long.
$entity_data = array(
$definition = array(
'provider' => $this->randomName(24),
'config_prefix' => $this->randomName(59),
);
$test_parameters[] = array(
$entity_data,
'\Drupal\Core\Config\ConfigPrefixLengthException',
String::format($message_text, array(
'@config_prefix' => $entity_data['provider'] . '.' . $entity_data['config_prefix'],
'@max_char' => ConfigEntityType::PREFIX_LENGTH,
)),
);
return $test_parameters;
$config_entity = $this->setUpConfigEntityType($definition);
$this->setExpectedException('\Drupal\Core\Config\ConfigPrefixLengthException', String::format($message_text, array(
'@config_prefix' => $definition['provider'] . '.' . $definition['config_prefix'],
'@max_char' => ConfigEntityType::PREFIX_LENGTH,
)));
$this->assertEmpty($config_entity->getConfigPrefix());
}
/**
* Tests that a valid config prefix returned by getConfigPrefix()
* does not throw an exception and is formatted as expected.
*
* @dataProvider providerPrefixLengthValid
* @covers ::getConfigPrefix()
*/
public function testConfigPrefixLengthValid($entity_data) {
$config_entity = new ConfigEntityType($entity_data);
if (isset($entity_data['config_prefix'])) {
$expected_prefix = $entity_data['provider'] . '.' . $entity_data['config_prefix'];
} else {
$expected_prefix = $entity_data['provider'] . '.' . $entity_data['id'];
}
$this->assertEquals($expected_prefix, $config_entity->getConfigPrefix());
}
/**
* Provides arguments to instantiate a ConfigEntityType with a configuration
* entity prefix that does not exceed the maximum character length.
*
* @return array
*/
public function providerPrefixLengthValid() {
$test_parameters = array();
public function testConfigPrefixLengthValid() {
// A provider length of 24 and config_prefix length of 58 (+1 for the .)
// results in a config length of 83, which is right at the limit.
$test_parameters[] = array(array(
$definition = array(
'provider' => $this->randomName(24),
'config_prefix' => $this->randomName(58),
));
// A provider length of 24 and id length of 58 (+1 for the .) results in a
// config length of 83, which is right at the limit.
$test_parameters[] = array(array(
'provider' => $this->randomName(24),
'id' => $this->randomName(58),
));
return $test_parameters;
);
$config_entity = $this->setUpConfigEntityType($definition);
$expected_prefix = $definition['provider'] . '.' . $definition['config_prefix'];
$this->assertEquals($expected_prefix, $config_entity->getConfigPrefix());
}
}
......@@ -11,7 +11,7 @@
use Drupal\Tests\UnitTestCase;
/**
* Tests the \Drupal\Core\Entity\EntityType class.
* @coversDefaultClass \Drupal\Core\Entity\EntityType
*
* @group Drupal
* @group Entity
......@@ -38,6 +38,9 @@ public static function getInfo() {
* @return \Drupal\Core\Entity\EntityTypeInterface
*/
protected function setUpEntityType($definition) {
$definition += array(
'id' => 'example_entity_type',
);
return new EntityType($definition);
}
......@@ -190,6 +193,25 @@ public function testGetViewBuilderClass() {
$this->assertSame($controller, $entity_type->getViewBuilderClass());
}
/**
* @covers ::__construct
*/
public function testIdExceedsMaxLength() {
$id = $this->randomName(33);
$message = 'Attempt to create an entity type with an ID longer than 32 characters: ' . $id;
$this->setExpectedException('Drupal\Core\Entity\Exception\EntityTypeIdLengthException', $message);
$this->setUpEntityType(array('id' => $id));
}
/**
* @covers ::id
*/
public function testId() {
$id = $this->randomName(32);
$entity_type = $this->setUpEntityType(array('id' => $id));
$this->assertEquals($id, $entity_type->id());
}
/**
* Gets a mock controller class name.
*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment