Move into nested docroot
This commit is contained in:
parent
83a0d3a149
commit
c8b70abde9
13405 changed files with 0 additions and 0 deletions
146
web/core/modules/field/src/ConfigImporterFieldPurger.php
Normal file
146
web/core/modules/field/src/ConfigImporterFieldPurger.php
Normal file
|
@ -0,0 +1,146 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field;
|
||||
|
||||
use Drupal\Core\Config\ConfigImporter;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityStorage;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Processes field purges before a configuration synchronization.
|
||||
*/
|
||||
class ConfigImporterFieldPurger {
|
||||
|
||||
/**
|
||||
* Processes fields targeted for purge as part of a configuration sync.
|
||||
*
|
||||
* This takes care of deleting the field if necessary, and purging the data on
|
||||
* the fly.
|
||||
*
|
||||
* @param array $context
|
||||
* The batch context.
|
||||
* @param \Drupal\Core\Config\ConfigImporter $config_importer
|
||||
* The config importer.
|
||||
*/
|
||||
public static function process(array &$context, ConfigImporter $config_importer) {
|
||||
if (!isset($context['sandbox']['field'])) {
|
||||
static::initializeSandbox($context, $config_importer);
|
||||
}
|
||||
|
||||
// Get the list of field storages to purge.
|
||||
$field_storages = static::getFieldStoragesToPurge($context['sandbox']['field']['extensions'], $config_importer->getUnprocessedConfiguration('delete'));
|
||||
// Get the first field storage to process.
|
||||
$field_storage = reset($field_storages);
|
||||
if (!isset($context['sandbox']['field']['current_storage_id']) || $context['sandbox']['field']['current_storage_id'] != $field_storage->id()) {
|
||||
$context['sandbox']['field']['current_storage_id'] = $field_storage->id();
|
||||
// If the storage has not been deleted yet we need to do that. This is the
|
||||
// case when the storage deletion is staged.
|
||||
if (!$field_storage->isDeleted()) {
|
||||
$field_storage->delete();
|
||||
}
|
||||
}
|
||||
field_purge_batch($context['sandbox']['field']['purge_batch_size'], $field_storage->uuid());
|
||||
$context['sandbox']['field']['current_progress']++;
|
||||
$fields_to_delete_count = count(static::getFieldStoragesToPurge($context['sandbox']['field']['extensions'], $config_importer->getUnprocessedConfiguration('delete')));
|
||||
if ($fields_to_delete_count == 0) {
|
||||
$context['finished'] = 1;
|
||||
}
|
||||
else {
|
||||
$context['finished'] = $context['sandbox']['field']['current_progress'] / $context['sandbox']['field']['steps_to_delete'];
|
||||
$context['message'] = \Drupal::translation()->translate('Purging field @field_label', array('@field_label' => $field_storage->label()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the batch context sandbox for processing field deletions.
|
||||
*
|
||||
* This calculates the number of steps necessary to purge all the field data
|
||||
* and saves data for later use.
|
||||
*
|
||||
* @param array $context
|
||||
* The batch context.
|
||||
* @param \Drupal\Core\Config\ConfigImporter $config_importer
|
||||
* The config importer.
|
||||
*/
|
||||
protected static function initializeSandbox(array &$context, ConfigImporter $config_importer) {
|
||||
$context['sandbox']['field']['purge_batch_size'] = \Drupal::config('field.settings')->get('purge_batch_size');
|
||||
// Save the future list of installed extensions to limit the amount of times
|
||||
// the configuration is read from disk.
|
||||
$context['sandbox']['field']['extensions'] = $config_importer->getStorageComparer()->getSourceStorage()->read('core.extension');
|
||||
|
||||
$context['sandbox']['field']['steps_to_delete'] = 0;
|
||||
$fields = static::getFieldStoragesToPurge($context['sandbox']['field']['extensions'], $config_importer->getUnprocessedConfiguration('delete'));
|
||||
foreach ($fields as $field) {
|
||||
$row_count = \Drupal::entityManager()->getStorage($field->getTargetEntityTypeId())
|
||||
->countFieldData($field);
|
||||
if ($row_count > 0) {
|
||||
// The number of steps to delete each field is determined by the
|
||||
// purge_batch_size setting. For example if the field has 9 rows and the
|
||||
// batch size is 10 then this will add 1 step to $number_of_steps.
|
||||
$how_many_steps = ceil($row_count / $context['sandbox']['field']['purge_batch_size']);
|
||||
$context['sandbox']['field']['steps_to_delete'] += $how_many_steps;
|
||||
}
|
||||
}
|
||||
// Each field possibly needs one last field_purge_batch() call to remove the
|
||||
// last field and the field storage itself.
|
||||
$context['sandbox']['field']['steps_to_delete'] += count($fields);
|
||||
|
||||
$context['sandbox']['field']['current_progress'] = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of fields to purge before configuration synchronization.
|
||||
*
|
||||
* If, during a configuration synchronization, a field is being deleted and
|
||||
* the module that provides the field type is being uninstalled then the field
|
||||
* data must be purged before the module is uninstalled. Also, if deleted
|
||||
* fields exist whose field types are provided by modules that are being
|
||||
* uninstalled their data need to be purged too.
|
||||
*
|
||||
* @param array $extensions
|
||||
* The list of extensions that will be enabled after the configuration
|
||||
* synchronization has finished.
|
||||
* @param array $deletes
|
||||
* The configuration that will be deleted by the configuration
|
||||
* synchronization.
|
||||
*
|
||||
* @return \Drupal\field\Entity\FieldStorageConfig[]
|
||||
* An array of field storages that need purging before configuration can be
|
||||
* synchronized.
|
||||
*/
|
||||
public static function getFieldStoragesToPurge(array $extensions, array $deletes) {
|
||||
$providers = array_keys($extensions['module']);
|
||||
$providers[] = 'core';
|
||||
$storages_to_delete = array();
|
||||
|
||||
// Gather fields that will be deleted during configuration synchronization
|
||||
// where the module that provides the field type is also being uninstalled.
|
||||
$field_storage_ids = array();
|
||||
foreach ($deletes as $config_name) {
|
||||
$field_storage_config_prefix = \Drupal::entityManager()->getDefinition('field_storage_config')->getConfigPrefix();
|
||||
if (strpos($config_name, $field_storage_config_prefix . '.') === 0) {
|
||||
$field_storage_ids[] = ConfigEntityStorage::getIDFromConfigName($config_name, $field_storage_config_prefix);
|
||||
}
|
||||
}
|
||||
if (!empty($field_storage_ids)) {
|
||||
$field_storages = \Drupal::entityQuery('field_storage_config')
|
||||
->condition('id', $field_storage_ids, 'IN')
|
||||
->condition('module', $providers, 'NOT IN')
|
||||
->execute();
|
||||
if (!empty($field_storages)) {
|
||||
$storages_to_delete = FieldStorageConfig::loadMultiple($field_storages);
|
||||
}
|
||||
}
|
||||
|
||||
// Gather deleted fields from modules that are being uninstalled.
|
||||
/** @var \Drupal\field\FieldStorageConfigInterface[] $field_storages */
|
||||
$field_storages = entity_load_multiple_by_properties('field_storage_config', array('deleted' => TRUE, 'include_deleted' => TRUE));
|
||||
foreach ($field_storages as $field_storage) {
|
||||
if (!in_array($field_storage->getTypeProvider(), $providers)) {
|
||||
$storages_to_delete[$field_storage->id()] = $field_storage;
|
||||
}
|
||||
}
|
||||
return $storages_to_delete;
|
||||
}
|
||||
|
||||
}
|
344
web/core/modules/field/src/Entity/FieldConfig.php
Normal file
344
web/core/modules/field/src/Entity/FieldConfig.php
Normal file
|
@ -0,0 +1,344 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Entity;
|
||||
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Field\FieldConfigBase;
|
||||
use Drupal\Core\Field\FieldException;
|
||||
use Drupal\field\FieldStorageConfigInterface;
|
||||
use Drupal\field\FieldConfigInterface;
|
||||
|
||||
/**
|
||||
* Defines the Field entity.
|
||||
*
|
||||
* @ConfigEntityType(
|
||||
* id = "field_config",
|
||||
* label = @Translation("Field"),
|
||||
* handlers = {
|
||||
* "access" = "Drupal\field\FieldConfigAccessControlHandler",
|
||||
* "storage" = "Drupal\field\FieldConfigStorage"
|
||||
* },
|
||||
* config_prefix = "field",
|
||||
* entity_keys = {
|
||||
* "id" = "id",
|
||||
* "label" = "label"
|
||||
* },
|
||||
* config_export = {
|
||||
* "id",
|
||||
* "field_name",
|
||||
* "entity_type",
|
||||
* "bundle",
|
||||
* "label",
|
||||
* "description",
|
||||
* "required",
|
||||
* "translatable",
|
||||
* "default_value",
|
||||
* "default_value_callback",
|
||||
* "settings",
|
||||
* "field_type",
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class FieldConfig extends FieldConfigBase implements FieldConfigInterface {
|
||||
|
||||
/**
|
||||
* Flag indicating whether the field is deleted.
|
||||
*
|
||||
* The delete() method marks the field as "deleted" and removes the
|
||||
* corresponding entry from the config storage, but keeps its definition in
|
||||
* the state storage while field data is purged by a separate
|
||||
* garbage-collection process.
|
||||
*
|
||||
* Deleted fields stay out of the regular entity lifecycle (notably, their
|
||||
* values are not populated in loaded entities, and are not saved back).
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $deleted = FALSE;
|
||||
|
||||
/**
|
||||
* The associated FieldStorageConfig entity.
|
||||
*
|
||||
* @var \Drupal\field\Entity\FieldStorageConfig
|
||||
*/
|
||||
protected $fieldStorage;
|
||||
|
||||
/**
|
||||
* Constructs a FieldConfig object.
|
||||
*
|
||||
* In most cases, Field entities are created via
|
||||
* FieldConfig::create($values), where $values is the same
|
||||
* parameter as in this constructor.
|
||||
*
|
||||
* @param array $values
|
||||
* An array of field properties, keyed by property name. The
|
||||
* storage associated with the field can be specified either with:
|
||||
* - field_storage: the FieldStorageConfigInterface object,
|
||||
* or by referring to an existing field storage in the current configuration
|
||||
* with:
|
||||
* - field_name: The field name.
|
||||
* - entity_type: The entity type.
|
||||
* Additionally, a 'bundle' property is required to indicate the entity
|
||||
* bundle to which the field is attached to. Other array elements will be
|
||||
* used to set the corresponding properties on the class; see the class
|
||||
* property documentation for details.
|
||||
*
|
||||
* @see entity_create()
|
||||
*/
|
||||
public function __construct(array $values, $entity_type = 'field_config') {
|
||||
// Allow either an injected FieldStorageConfig object, or a field_name and
|
||||
// entity_type.
|
||||
if (isset($values['field_storage'])) {
|
||||
if (!$values['field_storage'] instanceof FieldStorageConfigInterface) {
|
||||
throw new FieldException('Attempt to create a configurable field for a non-configurable field storage.');
|
||||
}
|
||||
$field_storage = $values['field_storage'];
|
||||
$values['field_name'] = $field_storage->getName();
|
||||
$values['entity_type'] = $field_storage->getTargetEntityTypeId();
|
||||
// The internal property is fieldStorage, not field_storage.
|
||||
unset($values['field_storage']);
|
||||
$values['fieldStorage'] = $field_storage;
|
||||
}
|
||||
else {
|
||||
if (empty($values['field_name'])) {
|
||||
throw new FieldException('Attempt to create a field without a field_name.');
|
||||
}
|
||||
if (empty($values['entity_type'])) {
|
||||
throw new FieldException("Attempt to create a field '{$values['field_name']}' without an entity_type.");
|
||||
}
|
||||
}
|
||||
// 'bundle' is required in either case.
|
||||
if (empty($values['bundle'])) {
|
||||
throw new FieldException("Attempt to create a field '{$values['field_name']}' without a bundle.");
|
||||
}
|
||||
|
||||
parent::__construct($values, $entity_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postCreate(EntityStorageInterface $storage) {
|
||||
parent::postCreate($storage);
|
||||
|
||||
// Validate that we have a valid storage for this field. This throws an
|
||||
// exception if the storage is invalid.
|
||||
$this->getFieldStorageDefinition();
|
||||
|
||||
// 'Label' defaults to the field name (mostly useful for fields created in
|
||||
// tests).
|
||||
if (empty($this->label)) {
|
||||
$this->label = $this->getName();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\Core\Entity\Entity::preSave().
|
||||
*
|
||||
* @throws \Drupal\Core\Field\FieldException
|
||||
* If the field definition is invalid.
|
||||
* @throws \Drupal\Core\Entity\EntityStorageException
|
||||
* In case of failures at the configuration storage level.
|
||||
*/
|
||||
public function preSave(EntityStorageInterface $storage) {
|
||||
$entity_manager = \Drupal::entityManager();
|
||||
$field_type_manager = \Drupal::service('plugin.manager.field.field_type');
|
||||
|
||||
$storage_definition = $this->getFieldStorageDefinition();
|
||||
|
||||
// Filter out unknown settings and make sure all settings are present, so
|
||||
// that a complete field definition is passed to the various hooks and
|
||||
// written to config.
|
||||
$default_settings = $field_type_manager->getDefaultFieldSettings($storage_definition->getType());
|
||||
$this->settings = array_intersect_key($this->settings, $default_settings) + $default_settings;
|
||||
|
||||
if ($this->isNew()) {
|
||||
// Notify the entity storage.
|
||||
$entity_manager->onFieldDefinitionCreate($this);
|
||||
}
|
||||
else {
|
||||
// Some updates are always disallowed.
|
||||
if ($this->entity_type != $this->original->entity_type) {
|
||||
throw new FieldException("Cannot change an existing field's entity_type.");
|
||||
}
|
||||
if ($this->bundle != $this->original->bundle) {
|
||||
throw new FieldException("Cannot change an existing field's bundle.");
|
||||
}
|
||||
if ($storage_definition->uuid() != $this->original->getFieldStorageDefinition()->uuid()) {
|
||||
throw new FieldException("Cannot change an existing field's storage.");
|
||||
}
|
||||
// Notify the entity storage.
|
||||
$entity_manager->onFieldDefinitionUpdate($this, $this->original);
|
||||
}
|
||||
|
||||
parent::preSave($storage);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
parent::calculateDependencies();
|
||||
// Mark the field_storage_config as a dependency.
|
||||
$this->addDependency('config', $this->getFieldStorageDefinition()->getConfigDependencyName());
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function preDelete(EntityStorageInterface $storage, array $fields) {
|
||||
$state = \Drupal::state();
|
||||
|
||||
parent::preDelete($storage, $fields);
|
||||
// Keep the field definitions in the state storage so we can use them
|
||||
// later during field_purge_batch().
|
||||
$deleted_fields = $state->get('field.field.deleted') ?: array();
|
||||
foreach ($fields as $field) {
|
||||
if (!$field->deleted) {
|
||||
$config = $field->toArray();
|
||||
$config['deleted'] = TRUE;
|
||||
$config['field_storage_uuid'] = $field->getFieldStorageDefinition()->uuid();
|
||||
$deleted_fields[$field->uuid()] = $config;
|
||||
}
|
||||
}
|
||||
$state->set('field.field.deleted', $deleted_fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function postDelete(EntityStorageInterface $storage, array $fields) {
|
||||
// Clear the cache upfront, to refresh the results of getBundles().
|
||||
\Drupal::entityManager()->clearCachedFieldDefinitions();
|
||||
|
||||
// Notify the entity storage.
|
||||
foreach ($fields as $field) {
|
||||
if (!$field->deleted) {
|
||||
\Drupal::entityManager()->onFieldDefinitionDelete($field);
|
||||
}
|
||||
}
|
||||
|
||||
// If this is part of a configuration synchronization then the following
|
||||
// configuration updates are not necessary.
|
||||
$entity = reset($fields);
|
||||
if ($entity->isSyncing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete the associated field storages if they are not used anymore and are
|
||||
// not persistent.
|
||||
$storages_to_delete = array();
|
||||
foreach ($fields as $field) {
|
||||
$storage_definition = $field->getFieldStorageDefinition();
|
||||
if (!$field->deleted && !$field->isUninstalling() && $storage_definition->isDeletable()) {
|
||||
// Key by field UUID to avoid deleting the same storage twice.
|
||||
$storages_to_delete[$storage_definition->uuid()] = $storage_definition;
|
||||
}
|
||||
}
|
||||
if ($storages_to_delete) {
|
||||
\Drupal::entityManager()->getStorage('field_storage_config')->delete($storages_to_delete);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function linkTemplates() {
|
||||
$link_templates = parent::linkTemplates();
|
||||
if (\Drupal::moduleHandler()->moduleExists('field_ui')) {
|
||||
$link_templates["{$this->entity_type}-field-edit-form"] = 'entity.field_config.' . $this->entity_type . '_field_edit_form';
|
||||
$link_templates["{$this->entity_type}-storage-edit-form"] = 'entity.field_config.' . $this->entity_type . '_storage_edit_form';
|
||||
$link_templates["{$this->entity_type}-field-delete-form"] = 'entity.field_config.' . $this->entity_type . '_field_delete_form';
|
||||
|
||||
if (isset($link_templates['config-translation-overview'])) {
|
||||
$link_templates["config-translation-overview.{$this->entity_type}"] = "entity.field_config.config_translation_overview.{$this->entity_type}";
|
||||
}
|
||||
}
|
||||
return $link_templates;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function urlRouteParameters($rel) {
|
||||
$parameters = parent::urlRouteParameters($rel);
|
||||
$entity_type = \Drupal::entityManager()->getDefinition($this->entity_type);
|
||||
$bundle_parameter_key = $entity_type->getBundleEntityType() ?: 'bundle';
|
||||
$parameters[$bundle_parameter_key] = $this->bundle;
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isDeleted() {
|
||||
return $this->deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldStorageDefinition() {
|
||||
if (!$this->fieldStorage) {
|
||||
$fields = $this->entityManager()->getFieldStorageDefinitions($this->entity_type);
|
||||
if (!isset($fields[$this->field_name])) {
|
||||
throw new FieldException("Attempt to create a field {$this->field_name} that does not exist on entity type {$this->entity_type}.");
|
||||
}
|
||||
if (!$fields[$this->field_name] instanceof FieldStorageConfigInterface) {
|
||||
throw new FieldException("Attempt to create a configurable field of non-configurable field storage {$this->field_name}.");
|
||||
}
|
||||
$this->fieldStorage = $fields[$this->field_name];
|
||||
}
|
||||
|
||||
return $this->fieldStorage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isDisplayConfigurable($context) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDisplayOptions($display_context) {
|
||||
// Hide configurable fields by default.
|
||||
return array('type' => 'hidden');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isReadOnly() {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isComputed() {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a field config entity based on the entity type and field name.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* ID of the entity type.
|
||||
* @param string $bundle
|
||||
* Bundle name.
|
||||
* @param string $field_name
|
||||
* Name of the field.
|
||||
*
|
||||
* @return static
|
||||
* The field config entity if one exists for the provided field
|
||||
* name, otherwise NULL.
|
||||
*/
|
||||
public static function loadByName($entity_type_id, $bundle, $field_name) {
|
||||
return \Drupal::entityManager()->getStorage('field_config')->load($entity_type_id . '.' . $bundle . '.' . $field_name);
|
||||
}
|
||||
|
||||
}
|
827
web/core/modules/field/src/Entity/FieldStorageConfig.php
Normal file
827
web/core/modules/field/src/Entity/FieldStorageConfig.php
Normal file
|
@ -0,0 +1,827 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Entity;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityBase;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
use Drupal\Core\Field\FieldException;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\Core\TypedData\OptionsProviderInterface;
|
||||
use Drupal\field\FieldStorageConfigInterface;
|
||||
|
||||
/**
|
||||
* Defines the Field storage configuration entity.
|
||||
*
|
||||
* @ConfigEntityType(
|
||||
* id = "field_storage_config",
|
||||
* label = @Translation("Field storage"),
|
||||
* handlers = {
|
||||
* "storage" = "Drupal\field\FieldStorageConfigStorage"
|
||||
* },
|
||||
* config_prefix = "storage",
|
||||
* entity_keys = {
|
||||
* "id" = "id",
|
||||
* "label" = "id"
|
||||
* },
|
||||
* config_export = {
|
||||
* "id",
|
||||
* "field_name",
|
||||
* "entity_type",
|
||||
* "type",
|
||||
* "settings",
|
||||
* "module",
|
||||
* "locked",
|
||||
* "cardinality",
|
||||
* "translatable",
|
||||
* "indexes",
|
||||
* "persist_with_no_fields",
|
||||
* "custom_storage",
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class FieldStorageConfig extends ConfigEntityBase implements FieldStorageConfigInterface {
|
||||
|
||||
/**
|
||||
* The maximum length of the field name, in characters.
|
||||
*
|
||||
* For fields created through Field UI, this includes the 'field_' prefix.
|
||||
*/
|
||||
const NAME_MAX_LENGTH = 32;
|
||||
|
||||
/**
|
||||
* The field ID.
|
||||
*
|
||||
* The ID consists of 2 parts: the entity type and the field name.
|
||||
*
|
||||
* Example: node.body, user.field_main_image.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* The field name.
|
||||
*
|
||||
* This is the name of the property under which the field values are placed in
|
||||
* an entity: $entity->{$field_name}. The maximum length is
|
||||
* Field:NAME_MAX_LENGTH.
|
||||
*
|
||||
* Example: body, field_main_image.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $field_name;
|
||||
|
||||
/**
|
||||
* The name of the entity type the field can be attached to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entity_type;
|
||||
|
||||
/**
|
||||
* The field type.
|
||||
*
|
||||
* Example: text, integer.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* The name of the module that provides the field type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $module;
|
||||
|
||||
/**
|
||||
* Field-type specific settings.
|
||||
*
|
||||
* An array of key/value pairs, The keys and default values are defined by the
|
||||
* field type.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $settings = [];
|
||||
|
||||
/**
|
||||
* The field cardinality.
|
||||
*
|
||||
* The maximum number of values the field can hold. Possible values are
|
||||
* positive integers or
|
||||
* FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED. Defaults to 1.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $cardinality = 1;
|
||||
|
||||
/**
|
||||
* Flag indicating whether the field is translatable.
|
||||
*
|
||||
* Defaults to TRUE.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $translatable = TRUE;
|
||||
|
||||
/**
|
||||
* Flag indicating whether the field is available for editing.
|
||||
*
|
||||
* If TRUE, some actions not available though the UI (but are still possible
|
||||
* through direct API manipulation):
|
||||
* - field settings cannot be changed,
|
||||
* - new fields cannot be created
|
||||
* - existing fields cannot be deleted.
|
||||
* Defaults to FALSE.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $locked = FALSE;
|
||||
|
||||
/**
|
||||
* Flag indicating whether the field storage should be deleted when orphaned.
|
||||
*
|
||||
* By default field storages for configurable fields are removed when there
|
||||
* are no remaining fields using them. If multiple modules provide bundles
|
||||
* which need to use the same field storage then setting this to TRUE will
|
||||
* preserve the field storage regardless of what happens to the bundles. The
|
||||
* classic use case for this is node body field storage since Book, Forum, the
|
||||
* Standard profile and bundle (node type) creation through the UI all use
|
||||
* same field storage.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $persist_with_no_fields = FALSE;
|
||||
|
||||
/**
|
||||
* A boolean indicating whether or not the field item uses custom storage.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $custom_storage = FALSE;
|
||||
|
||||
/**
|
||||
* The custom storage indexes for the field data storage.
|
||||
*
|
||||
* This set of indexes is merged with the "default" indexes specified by the
|
||||
* field type in hook_field_schema() to determine the actual set of indexes
|
||||
* that get created.
|
||||
*
|
||||
* The indexes are defined using the same definition format as Schema API
|
||||
* index specifications. Only columns that are part of the field schema, as
|
||||
* defined by the field type in hook_field_schema(), are allowed.
|
||||
*
|
||||
* Some storage backends might not support indexes, and discard that
|
||||
* information.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $indexes = [];
|
||||
|
||||
/**
|
||||
* Flag indicating whether the field is deleted.
|
||||
*
|
||||
* The delete() method marks the field as "deleted" and removes the
|
||||
* corresponding entry from the config storage, but keeps its definition in
|
||||
* the state storage while field data is purged by a separate
|
||||
* garbage-collection process.
|
||||
*
|
||||
* Deleted fields stay out of the regular entity lifecycle (notably, their
|
||||
* values are not populated in loaded entities, and are not saved back).
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $deleted = FALSE;
|
||||
|
||||
/**
|
||||
* The field schema.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $schema;
|
||||
|
||||
/**
|
||||
* An array of field property definitions.
|
||||
*
|
||||
* @var \Drupal\Core\TypedData\DataDefinitionInterface[]
|
||||
*
|
||||
* @see \Drupal\Core\TypedData\ComplexDataDefinitionInterface::getPropertyDefinitions()
|
||||
*/
|
||||
protected $propertyDefinitions;
|
||||
|
||||
/**
|
||||
* Static flag set to prevent recursion during field deletes.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected static $inDeletion = FALSE;
|
||||
|
||||
/**
|
||||
* Constructs a FieldStorageConfig object.
|
||||
*
|
||||
* In most cases, Field entities are created via
|
||||
* FieldStorageConfig::create($values)), where $values is the same parameter
|
||||
* as in this constructor.
|
||||
*
|
||||
* @param array $values
|
||||
* An array of field properties, keyed by property name. Most array
|
||||
* elements will be used to set the corresponding properties on the class;
|
||||
* see the class property documentation for details. Some array elements
|
||||
* have special meanings and a few are required. Special elements are:
|
||||
* - name: required. As a temporary Backwards Compatibility layer right now,
|
||||
* a 'field_name' property can be accepted in place of 'id'.
|
||||
* - entity_type: required.
|
||||
* - type: required.
|
||||
*
|
||||
* @see entity_create()
|
||||
*/
|
||||
public function __construct(array $values, $entity_type = 'field_storage_config') {
|
||||
// Check required properties.
|
||||
if (empty($values['field_name'])) {
|
||||
throw new FieldException('Attempt to create a field storage without a field name.');
|
||||
}
|
||||
if (!preg_match('/^[_a-z]+[_a-z0-9]*$/', $values['field_name'])) {
|
||||
throw new FieldException("Attempt to create a field storage {$values['field_name']} with invalid characters. Only lowercase alphanumeric characters and underscores are allowed, and only lowercase letters and underscore are allowed as the first character");
|
||||
}
|
||||
if (empty($values['type'])) {
|
||||
throw new FieldException("Attempt to create a field storage {$values['field_name']} with no type.");
|
||||
}
|
||||
if (empty($values['entity_type'])) {
|
||||
throw new FieldException("Attempt to create a field storage {$values['field_name']} with no entity_type.");
|
||||
}
|
||||
|
||||
parent::__construct($values, $entity_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function id() {
|
||||
return $this->getTargetEntityTypeId() . '.' . $this->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides \Drupal\Core\Entity\Entity::preSave().
|
||||
*
|
||||
* @throws \Drupal\Core\Field\FieldException
|
||||
* If the field definition is invalid.
|
||||
* @throws \Drupal\Core\Entity\EntityStorageException
|
||||
* In case of failures at the configuration storage level.
|
||||
*/
|
||||
public function preSave(EntityStorageInterface $storage) {
|
||||
// Clear the derived data about the field.
|
||||
unset($this->schema);
|
||||
|
||||
// Filter out unknown settings and make sure all settings are present, so
|
||||
// that a complete field definition is passed to the various hooks and
|
||||
// written to config.
|
||||
$field_type_manager = \Drupal::service('plugin.manager.field.field_type');
|
||||
$default_settings = $field_type_manager->getDefaultStorageSettings($this->type);
|
||||
$this->settings = array_intersect_key($this->settings, $default_settings) + $default_settings;
|
||||
|
||||
if ($this->isNew()) {
|
||||
$this->preSaveNew($storage);
|
||||
}
|
||||
else {
|
||||
$this->preSaveUpdated($storage);
|
||||
}
|
||||
|
||||
parent::preSave($storage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares saving a new field definition.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
|
||||
* The entity storage.
|
||||
*
|
||||
* @throws \Drupal\Core\Field\FieldException
|
||||
* If the field definition is invalid.
|
||||
*/
|
||||
protected function preSaveNew(EntityStorageInterface $storage) {
|
||||
$entity_manager = \Drupal::entityManager();
|
||||
$field_type_manager = \Drupal::service('plugin.manager.field.field_type');
|
||||
|
||||
// Assign the ID.
|
||||
$this->id = $this->id();
|
||||
|
||||
// Field name cannot be longer than FieldStorageConfig::NAME_MAX_LENGTH characters.
|
||||
// We use Unicode::strlen() because the DB layer assumes that column widths
|
||||
// are given in characters rather than bytes.
|
||||
if (Unicode::strlen($this->getName()) > static::NAME_MAX_LENGTH) {
|
||||
throw new FieldException('Attempt to create a field storage with an name longer than ' . static::NAME_MAX_LENGTH . ' characters: ' . $this->getName());
|
||||
}
|
||||
|
||||
// Disallow reserved field names.
|
||||
$disallowed_field_names = array_keys($entity_manager->getBaseFieldDefinitions($this->getTargetEntityTypeId()));
|
||||
if (in_array($this->getName(), $disallowed_field_names)) {
|
||||
throw new FieldException("Attempt to create field storage {$this->getName()} which is reserved by entity type {$this->getTargetEntityTypeId()}.");
|
||||
}
|
||||
|
||||
// Check that the field type is known.
|
||||
$field_type = $field_type_manager->getDefinition($this->getType(), FALSE);
|
||||
if (!$field_type) {
|
||||
throw new FieldException("Attempt to create a field storage of unknown type {$this->getType()}.");
|
||||
}
|
||||
$this->module = $field_type['provider'];
|
||||
|
||||
// Notify the entity manager.
|
||||
$entity_manager->onFieldStorageDefinitionCreate($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
parent::calculateDependencies();
|
||||
// Ensure the field is dependent on the providing module.
|
||||
$this->addDependency('module', $this->getTypeProvider());
|
||||
// Ask the field type for any additional storage dependencies.
|
||||
// @see \Drupal\Core\Field\FieldItemInterface::calculateStorageDependencies()
|
||||
$definition = \Drupal::service('plugin.manager.field.field_type')->getDefinition($this->getType(), FALSE);
|
||||
$this->addDependencies($definition['class']::calculateStorageDependencies($this));
|
||||
|
||||
// Ensure the field is dependent on the provider of the entity type.
|
||||
$entity_type = \Drupal::entityManager()->getDefinition($this->entity_type);
|
||||
$this->addDependency('module', $entity_type->getProvider());
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares saving an updated field definition.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
|
||||
* The entity storage.
|
||||
*/
|
||||
protected function preSaveUpdated(EntityStorageInterface $storage) {
|
||||
$module_handler = \Drupal::moduleHandler();
|
||||
$entity_manager = \Drupal::entityManager();
|
||||
|
||||
// Some updates are always disallowed.
|
||||
if ($this->getType() != $this->original->getType()) {
|
||||
throw new FieldException("Cannot change the field type for an existing field storage.");
|
||||
}
|
||||
if ($this->getTargetEntityTypeId() != $this->original->getTargetEntityTypeId()) {
|
||||
throw new FieldException("Cannot change the entity type for an existing field storage.");
|
||||
}
|
||||
|
||||
// See if any module forbids the update by throwing an exception. This
|
||||
// invokes hook_field_storage_config_update_forbid().
|
||||
$module_handler->invokeAll('field_storage_config_update_forbid', array($this, $this->original));
|
||||
|
||||
// Notify the entity manager. A listener can reject the definition
|
||||
// update as invalid by raising an exception, which stops execution before
|
||||
// the definition is written to config.
|
||||
$entity_manager->onFieldStorageDefinitionUpdate($this, $this->original);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function postSave(EntityStorageInterface $storage, $update = TRUE) {
|
||||
if ($update) {
|
||||
// Invalidate the render cache for all affected entities.
|
||||
$entity_manager = \Drupal::entityManager();
|
||||
$entity_type = $this->getTargetEntityTypeId();
|
||||
if ($entity_manager->hasHandler($entity_type, 'view_builder')) {
|
||||
$entity_manager->getViewBuilder($entity_type)->resetCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function preDelete(EntityStorageInterface $storage, array $field_storages) {
|
||||
$state = \Drupal::state();
|
||||
|
||||
// Set the static flag so that we don't delete field storages whilst
|
||||
// deleting fields.
|
||||
static::$inDeletion = TRUE;
|
||||
|
||||
// Delete or fix any configuration that is dependent, for example, fields.
|
||||
parent::preDelete($storage, $field_storages);
|
||||
|
||||
// Keep the field definitions in the state storage so we can use them later
|
||||
// during field_purge_batch().
|
||||
$deleted_storages = $state->get('field.storage.deleted') ?: array();
|
||||
foreach ($field_storages as $field_storage) {
|
||||
if (!$field_storage->deleted) {
|
||||
$config = $field_storage->toArray();
|
||||
$config['deleted'] = TRUE;
|
||||
$config['bundles'] = $field_storage->getBundles();
|
||||
$deleted_storages[$field_storage->uuid()] = $config;
|
||||
}
|
||||
}
|
||||
|
||||
$state->set('field.storage.deleted', $deleted_storages);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function postDelete(EntityStorageInterface $storage, array $fields) {
|
||||
// Notify the storage.
|
||||
foreach ($fields as $field) {
|
||||
if (!$field->deleted) {
|
||||
\Drupal::entityManager()->onFieldStorageDefinitionDelete($field);
|
||||
$field->deleted = TRUE;
|
||||
}
|
||||
}
|
||||
// Unset static flag.
|
||||
static::$inDeletion = FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSchema() {
|
||||
if (!isset($this->schema)) {
|
||||
// Get the schema from the field item class.
|
||||
$class = $this->getFieldItemClass();
|
||||
$schema = $class::schema($this);
|
||||
// Fill in default values for optional entries.
|
||||
$schema += array(
|
||||
'columns' => array(),
|
||||
'unique keys' => array(),
|
||||
'indexes' => array(),
|
||||
'foreign keys' => array(),
|
||||
);
|
||||
|
||||
// Merge custom indexes with those specified by the field type. Custom
|
||||
// indexes prevail.
|
||||
$schema['indexes'] = $this->indexes + $schema['indexes'];
|
||||
|
||||
$this->schema = $schema;
|
||||
}
|
||||
|
||||
return $this->schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasCustomStorage() {
|
||||
return $this->custom_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isBaseField() {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getColumns() {
|
||||
$schema = $this->getSchema();
|
||||
// A typical use case for the method is to iterate on the columns, while
|
||||
// some other use cases rely on identifying the first column with the key()
|
||||
// function. Since the schema is persisted in the Field object, we take care
|
||||
// of resetting the array pointer so that the former does not interfere with
|
||||
// the latter.
|
||||
reset($schema['columns']);
|
||||
return $schema['columns'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getBundles() {
|
||||
if (!$this->isDeleted()) {
|
||||
$map = \Drupal::entityManager()->getFieldMap();
|
||||
if (isset($map[$this->getTargetEntityTypeId()][$this->getName()]['bundles'])) {
|
||||
return $map[$this->getTargetEntityTypeId()][$this->getName()]['bundles'];
|
||||
}
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName() {
|
||||
return $this->field_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isDeleted() {
|
||||
return $this->deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTypeProvider() {
|
||||
return $this->module;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getType() {
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSettings() {
|
||||
// @todo FieldTypePluginManager maintains its own static cache. However, do
|
||||
// some CPU and memory profiling to see if it's worth statically caching
|
||||
// $field_type_info, or the default field storage and field settings,
|
||||
// within $this.
|
||||
$field_type_manager = \Drupal::service('plugin.manager.field.field_type');
|
||||
|
||||
$settings = $field_type_manager->getDefaultStorageSettings($this->getType());
|
||||
return $this->settings + $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSetting($setting_name) {
|
||||
// @todo See getSettings() about potentially statically caching this.
|
||||
// We assume here that one call to array_key_exists() is more efficient
|
||||
// than calling getSettings() when all we need is a single setting.
|
||||
if (array_key_exists($setting_name, $this->settings)) {
|
||||
return $this->settings[$setting_name];
|
||||
}
|
||||
$settings = $this->getSettings();
|
||||
if (array_key_exists($setting_name, $settings)) {
|
||||
return $settings[$setting_name];
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setSetting($setting_name, $value) {
|
||||
$this->settings[$setting_name] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setSettings(array $settings) {
|
||||
$this->settings = $settings + $this->settings;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isTranslatable() {
|
||||
return $this->translatable;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isRevisionable() {
|
||||
// All configurable fields are revisionable.
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setTranslatable($translatable) {
|
||||
$this->translatable = $translatable;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getProvider() {
|
||||
return 'field';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLabel() {
|
||||
return $this->label();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDescription() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCardinality() {
|
||||
return $this->cardinality;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setCardinality($cardinality) {
|
||||
$this->cardinality = $cardinality;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getOptionsProvider($property_name, FieldableEntityInterface $entity) {
|
||||
// If the field item class implements the interface, create an orphaned
|
||||
// runtime item object, so that it can be used as the options provider
|
||||
// without modifying the entity being worked on.
|
||||
if (is_subclass_of($this->getFieldItemClass(), OptionsProviderInterface::class)) {
|
||||
$items = $entity->get($this->getName());
|
||||
return \Drupal::service('plugin.manager.field.field_type')->createFieldItem($items, 0);
|
||||
}
|
||||
// @todo: Allow setting custom options provider, see
|
||||
// https://www.drupal.org/node/2002138.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isMultiple() {
|
||||
$cardinality = $this->getCardinality();
|
||||
return ($cardinality == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) || ($cardinality > 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isLocked() {
|
||||
return $this->locked;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setLocked($locked) {
|
||||
$this->locked = $locked;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTargetEntityTypeId() {
|
||||
return $this->entity_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isQueryable() {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a field has any data.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the field has data for any entity; FALSE otherwise.
|
||||
*/
|
||||
public function hasData() {
|
||||
return \Drupal::entityManager()->getStorage($this->entity_type)->countFieldData($this, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the magic __sleep() method.
|
||||
*
|
||||
* Using the Serialize interface and serialize() / unserialize() methods
|
||||
* breaks entity forms in PHP 5.4.
|
||||
* @todo Investigate in https://www.drupal.org/node/2074253.
|
||||
*/
|
||||
public function __sleep() {
|
||||
// Only serialize necessary properties, excluding those that can be
|
||||
// recalculated.
|
||||
$properties = get_object_vars($this);
|
||||
unset($properties['schema'], $properties['propertyDefinitions'], $properties['original']);
|
||||
return array_keys($properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConstraints() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConstraint($constraint_name) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPropertyDefinition($name) {
|
||||
if (!isset($this->propertyDefinitions)) {
|
||||
$this->getPropertyDefinitions();
|
||||
}
|
||||
if (isset($this->propertyDefinitions[$name])) {
|
||||
return $this->propertyDefinitions[$name];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPropertyDefinitions() {
|
||||
if (!isset($this->propertyDefinitions)) {
|
||||
$class = $this->getFieldItemClass();
|
||||
$this->propertyDefinitions = $class::propertyDefinitions($this);
|
||||
}
|
||||
return $this->propertyDefinitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPropertyNames() {
|
||||
return array_keys($this->getPropertyDefinitions());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMainPropertyName() {
|
||||
$class = $this->getFieldItemClass();
|
||||
return $class::mainPropertyName();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getUniqueStorageIdentifier() {
|
||||
return $this->uuid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to retrieve the field item class.
|
||||
*/
|
||||
protected function getFieldItemClass() {
|
||||
$type_definition = \Drupal::typedDataManager()
|
||||
->getDefinition('field_item:' . $this->getType());
|
||||
return $type_definition['class'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a field config entity based on the entity type and field name.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* ID of the entity type.
|
||||
* @param string $field_name
|
||||
* Name of the field.
|
||||
*
|
||||
* @return static
|
||||
* The field config entity if one exists for the provided field name,
|
||||
* otherwise NULL.
|
||||
*/
|
||||
public static function loadByName($entity_type_id, $field_name) {
|
||||
return \Drupal::entityManager()->getStorage('field_storage_config')->load($entity_type_id . '.' . $field_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isDeletable() {
|
||||
// The field storage is not deleted, is configured to be removed when there
|
||||
// are no fields, the field storage has no bundles, and field storages are
|
||||
// not in the process of being deleted.
|
||||
return !$this->deleted && !$this->persist_with_no_fields && count($this->getBundles()) == 0 && !static::$inDeletion;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIndexes() {
|
||||
return $this->indexes;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setIndexes(array $indexes) {
|
||||
$this->indexes = $indexes;
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Entity\EntityAccessControlHandler;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Defines the access control handler for the field entity type.
|
||||
*
|
||||
* @see \Drupal\field\Entity\FieldConfig
|
||||
*/
|
||||
class FieldConfigAccessControlHandler extends EntityAccessControlHandler {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
|
||||
if ($operation == 'delete') {
|
||||
$field_storage_entity = $entity->getFieldStorageDefinition();
|
||||
if ($field_storage_entity->isLocked()) {
|
||||
return AccessResult::forbidden()->addCacheableDependency($field_storage_entity);
|
||||
}
|
||||
else {
|
||||
return AccessResult::allowedIfHasPermission($account, 'administer ' . $entity->getTargetEntityTypeId() . ' fields')->addCacheableDependency($field_storage_entity);
|
||||
}
|
||||
}
|
||||
return AccessResult::allowedIfHasPermission($account, 'administer ' . $entity->getTargetEntityTypeId() . ' fields');
|
||||
}
|
||||
|
||||
}
|
21
web/core/modules/field/src/FieldConfigInterface.php
Normal file
21
web/core/modules/field/src/FieldConfigInterface.php
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface defining a field entity.
|
||||
*/
|
||||
interface FieldConfigInterface extends ConfigEntityInterface, FieldDefinitionInterface {
|
||||
|
||||
/**
|
||||
* Gets the deleted flag of the field.
|
||||
*
|
||||
* @return bool
|
||||
* Returns TRUE if the field is deleted.
|
||||
*/
|
||||
public function isDeleted();
|
||||
|
||||
}
|
180
web/core/modules/field/src/FieldConfigStorage.php
Normal file
180
web/core/modules/field/src/FieldConfigStorage.php
Normal file
|
@ -0,0 +1,180 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field;
|
||||
|
||||
use Drupal\Core\Config\Config;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Field\FieldConfigStorageBase;
|
||||
use Drupal\Core\Field\FieldTypePluginManagerInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Component\Uuid\UuidInterface;
|
||||
use Drupal\Core\State\StateInterface;
|
||||
|
||||
/**
|
||||
* Controller class for fields.
|
||||
*/
|
||||
class FieldConfigStorage extends FieldConfigStorageBase {
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* The state keyvalue collection.
|
||||
*
|
||||
* @var \Drupal\Core\State\StateInterface
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* The field type plugin manager.
|
||||
*
|
||||
* @var \Drupal\Core\Field\FieldTypePluginManagerInterface
|
||||
*/
|
||||
protected $fieldTypeManager;
|
||||
|
||||
/**
|
||||
* Constructs a FieldConfigStorage object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type definition.
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The config factory service.
|
||||
* @param \Drupal\Component\Uuid\UuidInterface $uuid_service
|
||||
* The UUID service.
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* The language manager.
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
* @param \Drupal\Core\State\StateInterface $state
|
||||
* The state key value store.
|
||||
* @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager
|
||||
* The field type plugin manager.
|
||||
*/
|
||||
public function __construct(EntityTypeInterface $entity_type, ConfigFactoryInterface $config_factory, UuidInterface $uuid_service, LanguageManagerInterface $language_manager, EntityManagerInterface $entity_manager, StateInterface $state, FieldTypePluginManagerInterface $field_type_manager) {
|
||||
parent::__construct($entity_type, $config_factory, $uuid_service, $language_manager);
|
||||
$this->entityManager = $entity_manager;
|
||||
$this->state = $state;
|
||||
$this->fieldTypeManager = $field_type_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static(
|
||||
$entity_type,
|
||||
$container->get('config.factory'),
|
||||
$container->get('uuid'),
|
||||
$container->get('language_manager'),
|
||||
$container->get('entity.manager'),
|
||||
$container->get('state'),
|
||||
$container->get('plugin.manager.field.field_type')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function importDelete($name, Config $new_config, Config $old_config) {
|
||||
// If the field storage has been deleted in the same import, the field will
|
||||
// be deleted by then, and there is nothing left to do. Just return TRUE so
|
||||
// that the file does not get written to active store.
|
||||
if (!$old_config->get()) {
|
||||
return TRUE;
|
||||
}
|
||||
return parent::importDelete($name, $new_config, $old_config);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadByProperties(array $conditions = array()) {
|
||||
// Include deleted fields if specified in the $conditions parameters.
|
||||
$include_deleted = isset($conditions['include_deleted']) ? $conditions['include_deleted'] : FALSE;
|
||||
unset($conditions['include_deleted']);
|
||||
|
||||
$fields = array();
|
||||
|
||||
// Get fields stored in configuration. If we are explicitly looking for
|
||||
// deleted fields only, this can be skipped, because they will be
|
||||
// retrieved from state below.
|
||||
if (empty($conditions['deleted'])) {
|
||||
if (isset($conditions['entity_type']) && isset($conditions['bundle']) && isset($conditions['field_name'])) {
|
||||
// Optimize for the most frequent case where we do have a specific ID.
|
||||
$id = $conditions['entity_type'] . '.' . $conditions['bundle'] . '.' . $conditions['field_name'];
|
||||
$fields = $this->loadMultiple(array($id));
|
||||
}
|
||||
else {
|
||||
// No specific ID, we need to examine all existing fields.
|
||||
$fields = $this->loadMultiple();
|
||||
}
|
||||
}
|
||||
|
||||
// Merge deleted fields (stored in state) if needed.
|
||||
if ($include_deleted || !empty($conditions['deleted'])) {
|
||||
$deleted_fields = $this->state->get('field.field.deleted') ?: array();
|
||||
$deleted_storages = $this->state->get('field.storage.deleted') ?: array();
|
||||
foreach ($deleted_fields as $id => $config) {
|
||||
// If the field storage itself is deleted, inject it directly in the field.
|
||||
if (isset($deleted_storages[$config['field_storage_uuid']])) {
|
||||
$config['field_storage'] = $this->entityManager->getStorage('field_storage_config')->create($deleted_storages[$config['field_storage_uuid']]);
|
||||
}
|
||||
$fields[$id] = $this->create($config);
|
||||
}
|
||||
}
|
||||
|
||||
// Collect matching fields.
|
||||
$matching_fields = array();
|
||||
foreach ($fields as $field) {
|
||||
// Some conditions are checked against the field storage.
|
||||
$field_storage = $field->getFieldStorageDefinition();
|
||||
|
||||
// Only keep the field if it matches all conditions.
|
||||
foreach ($conditions as $key => $value) {
|
||||
// Extract the actual value against which the condition is checked.
|
||||
switch ($key) {
|
||||
case 'field_name':
|
||||
$checked_value = $field_storage->getName();
|
||||
break;
|
||||
|
||||
case 'field_id':
|
||||
case 'field_storage_uuid':
|
||||
$checked_value = $field_storage->uuid();
|
||||
break;
|
||||
|
||||
case 'uuid';
|
||||
$checked_value = $field->uuid();
|
||||
break;
|
||||
|
||||
case 'deleted';
|
||||
$checked_value = $field->isDeleted();
|
||||
break;
|
||||
|
||||
default:
|
||||
$checked_value = $field->get($key);
|
||||
break;
|
||||
}
|
||||
|
||||
// Skip to the next field as soon as one condition does not match.
|
||||
if ($checked_value != $value) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
// When returning deleted fields, key the results by UUID since they
|
||||
// can include several fields with the same ID.
|
||||
$key = $include_deleted ? $field->uuid() : $field->id();
|
||||
$matching_fields[$key] = $field;
|
||||
}
|
||||
|
||||
return $matching_fields;
|
||||
}
|
||||
|
||||
}
|
153
web/core/modules/field/src/FieldStorageConfigInterface.php
Normal file
153
web/core/modules/field/src/FieldStorageConfigInterface.php
Normal file
|
@ -0,0 +1,153 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface defining a field storage entity.
|
||||
*/
|
||||
interface FieldStorageConfigInterface extends ConfigEntityInterface, FieldStorageDefinitionInterface {
|
||||
|
||||
/**
|
||||
* Returns the field type.
|
||||
*
|
||||
* @return string
|
||||
* The field type, i.e. the id of a field type plugin. For example 'text'.
|
||||
*/
|
||||
public function getType();
|
||||
|
||||
/**
|
||||
* Returns the name of the module providing the field type.
|
||||
*
|
||||
* @return string
|
||||
* The name of the module that provides the field type.
|
||||
*/
|
||||
public function getTypeProvider();
|
||||
|
||||
/**
|
||||
* Returns the list of bundles where the field storage has fields.
|
||||
*
|
||||
* @return array
|
||||
* An array of bundle names.
|
||||
*/
|
||||
public function getBundles();
|
||||
|
||||
/**
|
||||
* Returns whether the field is deleted or not.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the field is deleted.
|
||||
*/
|
||||
public function isDeleted();
|
||||
|
||||
/**
|
||||
* Checks if the field storage can be deleted.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the field storage can be deleted.
|
||||
*/
|
||||
public function isDeletable();
|
||||
|
||||
/**
|
||||
* Returns whether the field storage is locked or not.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the field storage is locked.
|
||||
*/
|
||||
public function isLocked();
|
||||
|
||||
/**
|
||||
* Sets the locked flag.
|
||||
*
|
||||
* @param bool $locked
|
||||
* Sets value of locked flag.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setLocked($locked);
|
||||
|
||||
/**
|
||||
* Sets the maximum number of items allowed for the field.
|
||||
*
|
||||
* @param int $cardinality
|
||||
* The cardinality value.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setCardinality($cardinality);
|
||||
|
||||
/**
|
||||
* Sets the value for a field setting by name.
|
||||
*
|
||||
* @param string $setting_name
|
||||
* The name of the setting.
|
||||
* @param mixed $value
|
||||
* The value of the setting.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setSetting($setting_name, $value);
|
||||
|
||||
/**
|
||||
* Sets field storage settings.
|
||||
*
|
||||
* Note that the method does not unset existing settings not specified in the
|
||||
* incoming $settings array.
|
||||
*
|
||||
* For example:
|
||||
* @code
|
||||
* // Given these are the default settings.
|
||||
* $storage_definition->getSettings() === [
|
||||
* 'fruit' => 'apple',
|
||||
* 'season' => 'summer',
|
||||
* ];
|
||||
* // Change only the 'fruit' setting.
|
||||
* $storage_definition->setSettings(['fruit' => 'banana']);
|
||||
* // The 'season' setting persists unchanged.
|
||||
* $storage_definition->getSettings() === [
|
||||
* 'fruit' => 'banana',
|
||||
* 'season' => 'summer',
|
||||
* ];
|
||||
* @endcode
|
||||
*
|
||||
* For clarity, it is preferred to use setSetting() if not all available
|
||||
* settings are supplied.
|
||||
*
|
||||
* @param array $settings
|
||||
* The array of storage settings.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setSettings(array $settings);
|
||||
|
||||
/**
|
||||
* Sets whether the field is translatable.
|
||||
*
|
||||
* @param bool $translatable
|
||||
* Whether the field is translatable.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setTranslatable($translatable);
|
||||
|
||||
/**
|
||||
* Returns the custom storage indexes for the field data storage.
|
||||
*
|
||||
* @return array
|
||||
* An array of custom indexes.
|
||||
*/
|
||||
public function getIndexes();
|
||||
|
||||
/**
|
||||
* Sets the custom storage indexes for the field data storage..
|
||||
*
|
||||
* @param array $indexes
|
||||
* The array of custom indexes.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setIndexes(array $indexes);
|
||||
|
||||
}
|
174
web/core/modules/field/src/FieldStorageConfigStorage.php
Normal file
174
web/core/modules/field/src/FieldStorageConfigStorage.php
Normal file
|
@ -0,0 +1,174 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field;
|
||||
|
||||
use Drupal\Component\Uuid\UuidInterface;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityStorage;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Field\FieldTypePluginManagerInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\State\StateInterface;
|
||||
|
||||
/**
|
||||
* Controller class for "field storage" configuration entities.
|
||||
*/
|
||||
class FieldStorageConfigStorage extends ConfigEntityStorage {
|
||||
|
||||
/**
|
||||
* The module handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* The entity manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityManagerInterface
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* The state keyvalue collection.
|
||||
*
|
||||
* @var \Drupal\Core\State\StateInterface
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* The field type plugin manager.
|
||||
*
|
||||
* @var \Drupal\Core\Field\FieldTypePluginManagerInterface
|
||||
*/
|
||||
protected $fieldTypeManager;
|
||||
|
||||
/**
|
||||
* Constructs a FieldStorageConfigStorage object.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
|
||||
* The entity type definition.
|
||||
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
||||
* The config factory service.
|
||||
* @param \Drupal\Component\Uuid\UuidInterface $uuid_service
|
||||
* The UUID service.
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* The language manager.
|
||||
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
|
||||
* The entity manager.
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler.
|
||||
* @param \Drupal\Core\State\StateInterface $state
|
||||
* The state key value store.
|
||||
* @param \Drupal\Component\Plugin\PluginManagerInterface\FieldTypePluginManagerInterface $field_type_manager
|
||||
* The field type plugin manager.
|
||||
*/
|
||||
public function __construct(EntityTypeInterface $entity_type, ConfigFactoryInterface $config_factory, UuidInterface $uuid_service, LanguageManagerInterface $language_manager, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, StateInterface $state, FieldTypePluginManagerInterface $field_type_manager) {
|
||||
parent::__construct($entity_type, $config_factory, $uuid_service, $language_manager);
|
||||
$this->entityManager = $entity_manager;
|
||||
$this->moduleHandler = $module_handler;
|
||||
$this->state = $state;
|
||||
$this->fieldTypeManager = $field_type_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
|
||||
return new static(
|
||||
$entity_type,
|
||||
$container->get('config.factory'),
|
||||
$container->get('uuid'),
|
||||
$container->get('language_manager'),
|
||||
$container->get('entity.manager'),
|
||||
$container->get('module_handler'),
|
||||
$container->get('state'),
|
||||
$container->get('plugin.manager.field.field_type')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loadByProperties(array $conditions = array()) {
|
||||
// Include deleted fields if specified in the $conditions parameters.
|
||||
$include_deleted = isset($conditions['include_deleted']) ? $conditions['include_deleted'] : FALSE;
|
||||
unset($conditions['include_deleted']);
|
||||
|
||||
/** @var \Drupal\field\FieldStorageConfigInterface[] $storages */
|
||||
$storages = array();
|
||||
|
||||
// Get field storages living in configuration. If we are explicitly looking
|
||||
// for deleted storages only, this can be skipped, because they will be
|
||||
// retrieved from state below.
|
||||
if (empty($conditions['deleted'])) {
|
||||
if (isset($conditions['entity_type']) && isset($conditions['field_name'])) {
|
||||
// Optimize for the most frequent case where we do have a specific ID.
|
||||
$id = $conditions['entity_type'] . $conditions['field_name'];
|
||||
$storages = $this->loadMultiple(array($id));
|
||||
}
|
||||
else {
|
||||
// No specific ID, we need to examine all existing storages.
|
||||
$storages = $this->loadMultiple();
|
||||
}
|
||||
}
|
||||
|
||||
// Merge deleted field storages (living in state) if needed.
|
||||
if ($include_deleted || !empty($conditions['deleted'])) {
|
||||
$deleted_storages = $this->state->get('field.storage.deleted') ?: array();
|
||||
foreach ($deleted_storages as $id => $config) {
|
||||
$storages[$id] = $this->create($config);
|
||||
}
|
||||
}
|
||||
|
||||
// Collect matching fields.
|
||||
$matches = array();
|
||||
foreach ($storages as $field) {
|
||||
foreach ($conditions as $key => $value) {
|
||||
// Extract the actual value against which the condition is checked.
|
||||
$checked_value = $field->get($key);
|
||||
// Skip to the next field as soon as one condition does not match.
|
||||
if ($checked_value != $value) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
// When returning deleted fields, key the results by UUID since they can
|
||||
// include several fields with the same ID.
|
||||
$key = $include_deleted ? $field->uuid() : $field->id();
|
||||
$matches[$key] = $field;
|
||||
}
|
||||
|
||||
return $matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function mapFromStorageRecords(array $records) {
|
||||
foreach ($records as $id => &$record) {
|
||||
$class = $this->fieldTypeManager->getPluginClass($record['type']);
|
||||
if (empty($class)) {
|
||||
$config_id = $this->getPrefix() . $id;
|
||||
throw new \RuntimeException("Unable to determine class for field type '{$record['type']}' found in the '$config_id' configuration");
|
||||
}
|
||||
$record['settings'] = $class::storageSettingsFromConfigData($record['settings']);
|
||||
}
|
||||
return parent::mapFromStorageRecords($records);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function mapToStorageRecord(EntityInterface $entity) {
|
||||
$record = parent::mapToStorageRecord($entity);
|
||||
$class = $this->fieldTypeManager->getPluginClass($record['type']);
|
||||
$record['settings'] = $class::storageSettingsToConfigData($record['settings']);
|
||||
return $record;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field;
|
||||
|
||||
use Drupal\Core\Field\FieldException;
|
||||
|
||||
/**
|
||||
* Exception class thrown by hook_field_storage_config_update_forbid().
|
||||
*/
|
||||
class FieldStorageConfigUpdateForbiddenException extends FieldException {}
|
102
web/core/modules/field/src/FieldUninstallValidator.php
Normal file
102
web/core/modules/field/src/FieldUninstallValidator.php
Normal file
|
@ -0,0 +1,102 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field;
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Extension\ModuleUninstallValidatorInterface;
|
||||
use Drupal\Core\Field\FieldTypePluginManagerInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Core\StringTranslation\TranslationInterface;
|
||||
|
||||
/**
|
||||
* Prevents uninstallation of modules providing active field storage.
|
||||
*/
|
||||
class FieldUninstallValidator implements ModuleUninstallValidatorInterface {
|
||||
|
||||
use StringTranslationTrait;
|
||||
|
||||
/**
|
||||
* The field storage config storage.
|
||||
*
|
||||
* @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface
|
||||
*/
|
||||
protected $fieldStorageConfigStorage;
|
||||
|
||||
/**
|
||||
* The field type plugin manager.
|
||||
*
|
||||
* @var \Drupal\Core\Field\FieldTypePluginManagerInterface
|
||||
*/
|
||||
protected $fieldTypeManager;
|
||||
|
||||
/**
|
||||
* Constructs a new FieldUninstallValidator.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
|
||||
* The entity manager.
|
||||
* @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
|
||||
* The string translation service.
|
||||
* @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager
|
||||
* The field type plugin manager.
|
||||
*/
|
||||
public function __construct(EntityTypeManagerInterface $entity_type_manager, TranslationInterface $string_translation, FieldTypePluginManagerInterface $field_type_manager) {
|
||||
$this->fieldStorageConfigStorage = $entity_type_manager->getStorage('field_storage_config');
|
||||
$this->stringTranslation = $string_translation;
|
||||
$this->fieldTypeManager = $field_type_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate($module) {
|
||||
$reasons = [];
|
||||
if ($field_storages = $this->getFieldStoragesByModule($module)) {
|
||||
// Provide an explanation message (only mention pending deletions if there
|
||||
// remain no actual, non-deleted fields.)
|
||||
$fields_in_use = [];
|
||||
foreach ($field_storages as $field_storage) {
|
||||
if (!$field_storage->isDeleted()) {
|
||||
$fields_in_use[$field_storage->getType()][] = $field_storage->getLabel();
|
||||
}
|
||||
}
|
||||
if (!empty($fields_in_use)) {
|
||||
foreach ($fields_in_use as $field_type => $field_storages) {
|
||||
$field_type_label = $this->getFieldTypeLabel($field_type);
|
||||
$reasons[] = $this->formatPlural(count($fields_in_use[$field_type]), 'The %field_type_label field type is used in the following field: @fields', 'The %field_type_label field type is used in the following fields: @fields', ['%field_type_label' => $field_type_label, '@fields' => implode(', ', $field_storages)]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$reasons[] = $this->t('Fields pending deletion');
|
||||
}
|
||||
}
|
||||
return $reasons;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all field storages for a specified module.
|
||||
*
|
||||
* @param string $module
|
||||
* The module to filter field storages by.
|
||||
*
|
||||
* @return \Drupal\field\FieldStorageConfigInterface[]
|
||||
* An array of field storages for a specified module.
|
||||
*/
|
||||
protected function getFieldStoragesByModule($module) {
|
||||
return $this->fieldStorageConfigStorage->loadByProperties(['module' => $module, 'include_deleted' => TRUE]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the label for a specified field type.
|
||||
*
|
||||
* @param string $field_type
|
||||
* The field type.
|
||||
*
|
||||
* @return string
|
||||
* The field type label.
|
||||
*/
|
||||
protected function getFieldTypeLabel($field_type) {
|
||||
return $this->fieldTypeManager->getDefinitions()[$field_type]['label'];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Plugin\migrate\process;
|
||||
|
||||
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Plugin\migrate\process\StaticMap;
|
||||
use Drupal\migrate\Row;
|
||||
use Drupal\migrate_drupal\Plugin\MigrateCckFieldPluginManagerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* @MigrateProcessPlugin(
|
||||
* id = "field_type"
|
||||
* )
|
||||
*/
|
||||
class FieldType extends StaticMap implements ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* The cckfield plugin manager.
|
||||
*
|
||||
* @var \Drupal\migrate_drupal\Plugin\MigrateCckFieldPluginManagerInterface
|
||||
*/
|
||||
protected $cckPluginManager;
|
||||
|
||||
/**
|
||||
* The migration object.
|
||||
*
|
||||
* @var \Drupal\migrate\Plugin\MigrationInterface
|
||||
*/
|
||||
protected $migration;
|
||||
|
||||
/**
|
||||
* Constructs a FieldType plugin.
|
||||
*
|
||||
* @param array $configuration
|
||||
* The plugin configuration.
|
||||
* @param string $plugin_id
|
||||
* The plugin ID.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin definition.
|
||||
* @param \Drupal\migrate_drupal\Plugin\MigrateCckFieldPluginManagerInterface $cck_plugin_manager
|
||||
* The cckfield plugin manager.
|
||||
* @param \Drupal\migrate\Plugin\MigrationInterface $migration
|
||||
* The migration being run.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrateCckFieldPluginManagerInterface $cck_plugin_manager, MigrationInterface $migration = NULL) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition);
|
||||
$this->cckPluginManager = $cck_plugin_manager;
|
||||
$this->migration = $migration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('plugin.manager.migrate.cckfield'),
|
||||
$migration
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
|
||||
$field_type = is_array($value) ? $value[0] : $value;
|
||||
|
||||
try {
|
||||
$plugin_id = $this->cckPluginManager->getPluginIdFromFieldType($field_type, [], $this->migration);
|
||||
return $this->cckPluginManager->createInstance($plugin_id, [], $this->migration)->getFieldType($row);
|
||||
}
|
||||
catch (PluginNotFoundException $e) {
|
||||
return parent::transform($value, $migrate_executable, $row, $destination_property);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Plugin\migrate\process\d6;
|
||||
|
||||
use Drupal\migrate\ProcessPluginBase;
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Set the default field settings.
|
||||
*
|
||||
* @MigrateProcessPlugin(
|
||||
* id = "field_formatter_settings_defaults"
|
||||
* )
|
||||
*/
|
||||
class FieldFormatterSettingsDefaults extends ProcessPluginBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Set field formatter settings when the map didn't map: for date
|
||||
* formatters, the fallback format, for everything else, empty array.
|
||||
*/
|
||||
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
|
||||
// If the 1 index is set then the map missed.
|
||||
if (isset($value[1])) {
|
||||
$module = $row->getSourceProperty('module');
|
||||
if ($module === 'date') {
|
||||
$value = array('format_type' => 'fallback');
|
||||
}
|
||||
elseif ($module === 'number') {
|
||||
// We have to do the lookup here in the process plugin because for
|
||||
// number we need to calculated the settings based on the type not just
|
||||
// the module which works well for other field types.
|
||||
return $this->numberSettings($row->getDestinationProperty('options/type'), $value[1]);
|
||||
}
|
||||
else {
|
||||
$value = array();
|
||||
}
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* The field type.
|
||||
* @param $format
|
||||
* The format selected for the field on the display.
|
||||
*
|
||||
* @return array
|
||||
* The correct default settings.
|
||||
*
|
||||
* @throws \Drupal\migrate\MigrateException
|
||||
*/
|
||||
protected function numberSettings($type, $format) {
|
||||
$map = [
|
||||
'number_decimal' => [
|
||||
'us_0' => [
|
||||
'scale' => 0,
|
||||
'decimal_separator' => '.',
|
||||
'thousand_separator' => ',',
|
||||
'prefix_suffix' => TRUE,
|
||||
],
|
||||
'us_1' => [
|
||||
'scale' => 1,
|
||||
'decimal_separator' => '.',
|
||||
'thousand_separator' => ',',
|
||||
'prefix_suffix' => TRUE,
|
||||
],
|
||||
'us_2' => [
|
||||
'scale' => 2,
|
||||
'decimal_separator' => '.',
|
||||
'thousand_separator' => ',',
|
||||
'prefix_suffix' => TRUE,
|
||||
],
|
||||
'be_0' => [
|
||||
'scale' => 0,
|
||||
'decimal_separator' => ',',
|
||||
'thousand_separator' => '.',
|
||||
'prefix_suffix' => TRUE,
|
||||
],
|
||||
'be_1' => [
|
||||
'scale' => 1,
|
||||
'decimal_separator' => ',',
|
||||
'thousand_separator' => '.',
|
||||
'prefix_suffix' => TRUE,
|
||||
],
|
||||
'be_2' => [
|
||||
'scale' => 2,
|
||||
'decimal_separator' => ',',
|
||||
'thousand_separator' => '.',
|
||||
'prefix_suffix' => TRUE,
|
||||
],
|
||||
'fr_0' => [
|
||||
'scale' => 0,
|
||||
'decimal_separator' => ',',
|
||||
'thousand_separator' => ' ',
|
||||
'prefix_suffix' => TRUE,
|
||||
],
|
||||
'fr_1' => [
|
||||
'scale' => 1,
|
||||
'decimal_separator' => ',',
|
||||
'thousand_separator' => ' ',
|
||||
'prefix_suffix' => TRUE,
|
||||
],
|
||||
'fr_2' => [
|
||||
'scale' => 2,
|
||||
'decimal_separator' => ',',
|
||||
'thousand_separator' => ' ',
|
||||
'prefix_suffix' => TRUE,
|
||||
],
|
||||
],
|
||||
'number_integer' => [
|
||||
'us_0' => [
|
||||
'thousand_separator' => ',',
|
||||
'prefix_suffix' => TRUE,
|
||||
],
|
||||
'be_0' => [
|
||||
'thousand_separator' => '.',
|
||||
'prefix_suffix' => TRUE,
|
||||
],
|
||||
'fr_0' => [
|
||||
'thousand_separator' => ' ',
|
||||
'prefix_suffix' => TRUE,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
return isset($map[$type][$format]) ? $map[$type][$format] : [];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Plugin\migrate\process\d6;
|
||||
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\ProcessPluginBase;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* @MigrateProcessPlugin(
|
||||
* id = "d6_field_instance_defaults"
|
||||
* )
|
||||
*/
|
||||
class FieldInstanceDefaults extends ProcessPluginBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Set the field instance defaults.
|
||||
*/
|
||||
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
|
||||
list($widget_type, $widget_settings) = $value;
|
||||
$default = array();
|
||||
|
||||
switch ($widget_type) {
|
||||
case 'text_textfield':
|
||||
case 'number':
|
||||
case 'phone_textfield':
|
||||
if (!empty($widget_settings['default_value'][0]['value'])) {
|
||||
$default['value'] = $widget_settings['default_value'][0]['value'];
|
||||
}
|
||||
break;
|
||||
|
||||
case 'imagefield_widget':
|
||||
// @todo, load the image and populate the defaults.
|
||||
// $default['default_image'] = $widget_settings['default_image'];
|
||||
break;
|
||||
|
||||
case 'date_select':
|
||||
if (!empty($widget_settings['default_value'])) {
|
||||
$default['default_date_type'] = 'relative';
|
||||
$default['default_date'] = $widget_settings['default_value'];
|
||||
}
|
||||
break;
|
||||
|
||||
case 'email_textfield':
|
||||
if (!empty($widget_settings['default_value'][0]['email'])) {
|
||||
$default['value'] = $widget_settings['default_value'][0]['email'];
|
||||
}
|
||||
break;
|
||||
|
||||
case 'link':
|
||||
if (!empty($widget_settings['default_value'][0]['url'])) {
|
||||
$default['title'] = $widget_settings['default_value'][0]['title'];
|
||||
$default['url'] = $widget_settings['default_value'][0]['url'];
|
||||
$default['options'] = ['attributes' => []];
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!empty($default)) {
|
||||
$default = array($default);
|
||||
}
|
||||
return $default;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Plugin\migrate\process\d6;
|
||||
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\ProcessPluginBase;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* @MigrateProcessPlugin(
|
||||
* id = "d6_field_field_settings"
|
||||
* )
|
||||
*/
|
||||
class FieldInstanceSettings extends ProcessPluginBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Set the field instance defaults.
|
||||
*/
|
||||
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
|
||||
list($widget_type, $widget_settings, $field_settings) = $value;
|
||||
$settings = array();
|
||||
switch ($widget_type) {
|
||||
case 'number':
|
||||
$settings['min'] = $field_settings['min'];
|
||||
$settings['max'] = $field_settings['max'];
|
||||
$settings['prefix'] = $field_settings['prefix'];
|
||||
$settings['suffix'] = $field_settings['suffix'];
|
||||
break;
|
||||
|
||||
case 'link':
|
||||
// $settings['url'] = $widget_settings['default_value'][0]['url'];
|
||||
// D6 has optional, required, value and none. D8 only has disabled (0)
|
||||
// optional (1) and required (2).
|
||||
$map = array('disabled' => 0, 'optional' => 1, 'required' => 2);
|
||||
$settings['title'] = $map[$field_settings['title']];
|
||||
break;
|
||||
|
||||
case 'filefield_widget':
|
||||
$settings['file_extensions'] = $widget_settings['file_extensions'];
|
||||
$settings['file_directory'] = $widget_settings['file_path'];
|
||||
$settings['description_field'] = $field_settings['description_field'];
|
||||
$settings['max_filesize'] = $this->convertSizeUnit($widget_settings['max_filesize_per_file']);
|
||||
break;
|
||||
|
||||
case 'imagefield_widget':
|
||||
$settings['file_extensions'] = $widget_settings['file_extensions'];
|
||||
$settings['file_directory'] = $widget_settings['file_path'];
|
||||
$settings['max_filesize'] = $this->convertSizeUnit($widget_settings['max_filesize_per_file']);
|
||||
$settings['alt_field'] = $widget_settings['alt'];
|
||||
$settings['alt_field_required'] = $widget_settings['custom_alt'];
|
||||
$settings['title_field'] = $widget_settings['title'];
|
||||
$settings['title_field_required'] = $widget_settings['custom_title'];
|
||||
// With nothing entered for min or max resolution in Drupal 6, zero is
|
||||
// stored. For Drupal 8 this should be an empty string.
|
||||
$settings['max_resolution'] = !empty($widget_settings['max_resolution']) ? $widget_settings['max_resolution'] : '';
|
||||
$settings['min_resolution'] = !empty($widget_settings['min_resolution']) ? $widget_settings['min_resolution'] : '';
|
||||
break;
|
||||
|
||||
}
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert file size strings into their D8 format.
|
||||
*
|
||||
* D6 stores file size using a "K" for kilobytes and "M" for megabytes where
|
||||
* as D8 uses "KB" and "MB" respectively.
|
||||
*
|
||||
* @param string $size_string
|
||||
* The size string, eg 10M
|
||||
*
|
||||
* @return string
|
||||
* The D8 version of the size string.
|
||||
*/
|
||||
protected function convertSizeUnit($size_string) {
|
||||
$size_unit = substr($size_string, strlen($size_string) - 1);
|
||||
if ($size_unit == "M" || $size_unit == "K") {
|
||||
return $size_string . "B";
|
||||
}
|
||||
return $size_string;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Plugin\migrate\process\d6;
|
||||
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\ProcessPluginBase;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Get the field instance widget settings.
|
||||
*
|
||||
* @MigrateProcessPlugin(
|
||||
* id = "field_instance_widget_settings"
|
||||
* )
|
||||
*/
|
||||
class FieldInstanceWidgetSettings extends ProcessPluginBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Get the field instance default/mapped widget settings.
|
||||
*/
|
||||
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
|
||||
list($widget_type, $widget_settings) = $value;
|
||||
return $this->getSettings($widget_type, $widget_settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the default D8 and specified D6 settings for a widget type.
|
||||
*
|
||||
* @param string $widget_type
|
||||
* The widget type.
|
||||
* @param array $widget_settings
|
||||
* The widget settings from D6 for this widget.
|
||||
*
|
||||
* @return array
|
||||
* A valid array of settings.
|
||||
*/
|
||||
public function getSettings($widget_type, $widget_settings) {
|
||||
$progress = isset($widget_settings['progress_indicator']) ? $widget_settings['progress_indicator'] : 'throbber';
|
||||
$size = isset($widget_settings['size']) ? $widget_settings['size'] : 60;
|
||||
$rows = isset($widget_settings['rows']) ? $widget_settings['rows'] : 5;
|
||||
|
||||
$settings = array(
|
||||
'text_textfield' => array(
|
||||
'size' => $size,
|
||||
'placeholder' => '',
|
||||
),
|
||||
'text_textarea' => array(
|
||||
'rows' => $rows,
|
||||
'placeholder' => '',
|
||||
),
|
||||
'number' => array(
|
||||
'placeholder' => '',
|
||||
),
|
||||
'email_textfield' => array(
|
||||
'placeholder' => '',
|
||||
),
|
||||
'link' => array(
|
||||
'placeholder_url' => '',
|
||||
'placeholder_title' => '',
|
||||
),
|
||||
'filefield_widget' => array(
|
||||
'progress_indicator' => $progress,
|
||||
),
|
||||
'imagefield_widget' => array(
|
||||
'progress_indicator' => $progress,
|
||||
'preview_image_style' => 'thumbnail',
|
||||
),
|
||||
'optionwidgets_onoff' => array(
|
||||
'display_label' => FALSE,
|
||||
),
|
||||
'phone_textfield' => array(
|
||||
'placeholder' => '',
|
||||
),
|
||||
);
|
||||
|
||||
return isset($settings[$widget_type]) ? $settings[$widget_type] : array();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Plugin\migrate\process\d6;
|
||||
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\ProcessPluginBase;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Get the field settings.
|
||||
*
|
||||
* @MigrateProcessPlugin(
|
||||
* id = "field_settings"
|
||||
* )
|
||||
*/
|
||||
class FieldSettings extends ProcessPluginBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* Get the field default/mapped settings.
|
||||
*/
|
||||
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
|
||||
list($field_type, $global_settings) = $value;
|
||||
return $this->getSettings($field_type, $global_settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the default D8 and specified D6 settings.
|
||||
*
|
||||
* @param string $field_type
|
||||
* The field type.
|
||||
* @param array $global_settings
|
||||
* The field settings.
|
||||
*
|
||||
* @return array
|
||||
* A valid array of settings.
|
||||
*/
|
||||
public function getSettings($field_type, $global_settings) {
|
||||
$max_length = isset($global_settings['max_length']) ? $global_settings['max_length'] : '';
|
||||
$max_length = empty($max_length) ? 255 : $max_length;
|
||||
$allowed_values = [];
|
||||
if (isset($global_settings['allowed_values'])) {
|
||||
$list = explode("\n", $global_settings['allowed_values']);
|
||||
$list = array_map('trim', $list);
|
||||
$list = array_filter($list, 'strlen');
|
||||
switch ($field_type) {
|
||||
case 'list_string':
|
||||
case 'list_integer':
|
||||
case 'list_float':
|
||||
foreach ($list as $value) {
|
||||
$value = explode("|", $value);
|
||||
$allowed_values[$value[0]] = isset($value[1]) ? $value[1] : $value[0];
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$allowed_values = $list;
|
||||
}
|
||||
}
|
||||
|
||||
$settings = array(
|
||||
'text' => array(
|
||||
'max_length' => $max_length,
|
||||
),
|
||||
'datetime' => array('datetime_type' => 'datetime'),
|
||||
'list_string' => array(
|
||||
'allowed_values' => $allowed_values,
|
||||
),
|
||||
'list_integer' => array(
|
||||
'allowed_values' => $allowed_values,
|
||||
),
|
||||
'list_float' => array(
|
||||
'allowed_values' => $allowed_values,
|
||||
),
|
||||
'boolean' => array(
|
||||
'allowed_values' => $allowed_values,
|
||||
),
|
||||
);
|
||||
|
||||
return isset($settings[$field_type]) ? $settings[$field_type] : array();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Plugin\migrate\process\d6;
|
||||
|
||||
use Drupal\migrate\MigrateException;
|
||||
use Drupal\migrate\ProcessPluginBase;
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Gives us a chance to set per field defaults.
|
||||
*
|
||||
* @MigrateProcessPlugin(
|
||||
* id = "field_type_defaults"
|
||||
* )
|
||||
*/
|
||||
class FieldTypeDefaults extends ProcessPluginBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
|
||||
if (is_array($value)) {
|
||||
if ($row->getSourceProperty('module') == 'date') {
|
||||
$value = 'datetime_default';
|
||||
}
|
||||
else {
|
||||
throw new MigrateException(sprintf('Failed to lookup field type %s in the static map.', var_export($value, TRUE)));
|
||||
}
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Plugin\migrate\process\d7;
|
||||
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\ProcessPluginBase;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* @MigrateProcessPlugin(
|
||||
* id = "d7_field_instance_defaults"
|
||||
* )
|
||||
*/
|
||||
class FieldInstanceDefaults extends ProcessPluginBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
|
||||
list($default_value, $widget_settings) = $value;
|
||||
$widget_type = $widget_settings['type'];
|
||||
|
||||
$default = array();
|
||||
|
||||
foreach ($default_value as $item) {
|
||||
switch ($widget_type) {
|
||||
// Add special processing here if needed.
|
||||
default:
|
||||
$default[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Plugin\migrate\process\d7;
|
||||
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\ProcessPluginBase;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* @MigrateProcessPlugin(
|
||||
* id = "d7_field_instance_settings"
|
||||
* )
|
||||
*/
|
||||
class FieldInstanceSettings extends ProcessPluginBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
|
||||
list($instance_settings, $widget_settings, $field_settings) = $value;
|
||||
$widget_type = $widget_settings['type'];
|
||||
|
||||
// Get entityreference handler settings from source field configuration.
|
||||
if ($row->getSourceProperty('type') == "entityreference") {
|
||||
$instance_settings['handler'] = 'default:' . $field_settings['target_type'];
|
||||
// Transform the sort settings to D8 structure.
|
||||
$sort = [
|
||||
'field' => '_none',
|
||||
'direction' => 'ASC',
|
||||
];
|
||||
if (!empty(array_filter($field_settings['handler_settings']['sort']))) {
|
||||
if ($field_settings['handler_settings']['sort']['type'] == "property") {
|
||||
$sort = [
|
||||
'field' => $field_settings['handler_settings']['sort']['property'],
|
||||
'direction' => $field_settings['handler_settings']['sort']['direction'],
|
||||
];
|
||||
}
|
||||
elseif ($field_settings['handler_settings']['sort']['type'] == "field") {
|
||||
$sort = [
|
||||
'field' => $field_settings['handler_settings']['sort']['field'],
|
||||
'direction' => $field_settings['handler_settings']['sort']['direction'],
|
||||
];
|
||||
}
|
||||
}
|
||||
if (empty($field_settings['handler_settings']['target_bundles'])) {
|
||||
$field_settings['handler_settings']['target_bundles'] = NULL;
|
||||
}
|
||||
$field_settings['handler_settings']['sort'] = $sort;
|
||||
$instance_settings['handler_settings'] = $field_settings['handler_settings'];
|
||||
}
|
||||
|
||||
switch ($widget_type) {
|
||||
case 'image_image':
|
||||
$settings = $instance_settings;
|
||||
$settings['default_image'] = array(
|
||||
'alt' => '',
|
||||
'title' => '',
|
||||
'width' => NULL,
|
||||
'height' => NULL,
|
||||
'uuid' => '',
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
$settings = $instance_settings;
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Plugin\migrate\process\d7;
|
||||
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\ProcessPluginBase;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* @MigrateProcessPlugin(
|
||||
* id = "d7_field_settings"
|
||||
* )
|
||||
*/
|
||||
class FieldSettings extends ProcessPluginBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
|
||||
$value = $row->getSourceProperty('settings');
|
||||
|
||||
switch ($row->getSourceProperty('type')) {
|
||||
case 'image':
|
||||
if (!is_array($value['default_image'])) {
|
||||
$value['default_image'] = array('uuid' => '');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'taxonomy_term_reference':
|
||||
$value['target_type'] = 'taxonomy_term';
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
}
|
106
web/core/modules/field/src/Plugin/migrate/source/d6/Field.php
Normal file
106
web/core/modules/field/src/Plugin/migrate/source/d6/Field.php
Normal file
|
@ -0,0 +1,106 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Plugin\migrate\source\d6;
|
||||
|
||||
use Drupal\migrate\Row;
|
||||
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
|
||||
|
||||
/**
|
||||
* Drupal 6 field source from database.
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "d6_field",
|
||||
* source_provider = "content"
|
||||
* )
|
||||
*/
|
||||
class Field extends DrupalSqlBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
$query = $this->select('content_node_field', 'cnf')
|
||||
->fields('cnf', array(
|
||||
'field_name',
|
||||
'type',
|
||||
'global_settings',
|
||||
'required',
|
||||
'multiple',
|
||||
'db_storage',
|
||||
'module',
|
||||
'db_columns',
|
||||
'active',
|
||||
'locked',
|
||||
))
|
||||
->distinct();
|
||||
// Only import fields which are actually being used.
|
||||
$query->innerJoin('content_node_field_instance', 'cnfi', 'cnfi.field_name = cnf.field_name');
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
return array(
|
||||
'field_name' => $this->t('Field name'),
|
||||
'type' => $this->t('Type (text, integer, ....)'),
|
||||
'widget_type' => $this->t('An instance-specific widget type'),
|
||||
'global_settings' => $this->t('Global settings. Shared with every field instance.'),
|
||||
'required' => $this->t('Required'),
|
||||
'multiple' => $this->t('Multiple'),
|
||||
'db_storage' => $this->t('DB storage'),
|
||||
'module' => $this->t('Module'),
|
||||
'db_columns' => $this->t('DB Columns'),
|
||||
'active' => $this->t('Active'),
|
||||
'locked' => $this->t('Locked'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prepareRow(Row $row) {
|
||||
// The instance widget_type helps determine what D8 field type we'll use.
|
||||
// Identify the distinct widget_types being used in D6.
|
||||
$widget_types = $this->select('content_node_field_instance', 'cnfi')
|
||||
->fields('cnfi', ['widget_type'])
|
||||
->condition('field_name', $row->getSourceProperty('field_name'))
|
||||
->distinct()
|
||||
->orderBy('widget_type')
|
||||
->execute()
|
||||
->fetchCol();
|
||||
// Arbitrarily use the first widget_type - if there are multiples, let the
|
||||
// migrator know.
|
||||
$row->setSourceProperty('widget_type', $widget_types[0]);
|
||||
if (count($widget_types) > 1) {
|
||||
$this->migration->getIdMap()->saveMessage(
|
||||
['field_name' => $row->getSourceProperty('field_name')],
|
||||
$this->t('Widget types @types are used in Drupal 6 field instances: widget type @selected_type applied to the Drupal 8 base field', [
|
||||
'@types' => implode(', ', $widget_types),
|
||||
'@selected_type' => $widget_types[0],
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
// Unserialize data.
|
||||
$global_settings = unserialize($row->getSourceProperty('global_settings'));
|
||||
$db_columns = unserialize($row->getSourceProperty('db_columns'));
|
||||
$row->setSourceProperty('global_settings', $global_settings);
|
||||
$row->setSourceProperty('db_columns', $db_columns);
|
||||
return parent::prepareRow($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
$ids['field_name'] = array(
|
||||
'type' => 'string',
|
||||
'alias' => 'cnf',
|
||||
);
|
||||
return $ids;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Plugin\migrate\source\d6;
|
||||
|
||||
use Drupal\migrate\Row;
|
||||
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
|
||||
|
||||
/**
|
||||
* Drupal 6 field instances source from database.
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "d6_field_instance",
|
||||
* source_provider = "content"
|
||||
* )
|
||||
*/
|
||||
class FieldInstance extends DrupalSqlBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
$query = $this->select('content_node_field_instance', 'cnfi')->fields('cnfi');
|
||||
if (isset($this->configuration['node_type'])) {
|
||||
$query->condition('cnfi.type_name', $this->configuration['node_type']);
|
||||
}
|
||||
$query->join('content_node_field', 'cnf', 'cnf.field_name = cnfi.field_name');
|
||||
$query->fields('cnf');
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
return array(
|
||||
'field_name' => $this->t('The machine name of field.'),
|
||||
'type_name' => $this->t('Content type where this field is in use.'),
|
||||
'weight' => $this->t('Weight.'),
|
||||
'label' => $this->t('A name to show.'),
|
||||
'widget_type' => $this->t('Widget type.'),
|
||||
'widget_settings' => $this->t('Serialize data with widget settings.'),
|
||||
'display_settings' => $this->t('Serialize data with display settings.'),
|
||||
'description' => $this->t('A description of field.'),
|
||||
'widget_module' => $this->t('Module that implements widget.'),
|
||||
'widget_active' => $this->t('Status of widget'),
|
||||
'module' => $this->t('The module that provides the field.'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prepareRow(Row $row) {
|
||||
// Unserialize data.
|
||||
$widget_settings = unserialize($row->getSourceProperty('widget_settings'));
|
||||
$display_settings = unserialize($row->getSourceProperty('display_settings'));
|
||||
$global_settings = unserialize($row->getSourceProperty('global_settings'));
|
||||
$row->setSourceProperty('widget_settings', $widget_settings);
|
||||
$row->setSourceProperty('display_settings', $display_settings);
|
||||
$row->setSourceProperty('global_settings', $global_settings);
|
||||
return parent::prepareRow($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
$ids = array(
|
||||
'field_name' => array(
|
||||
'type' => 'string',
|
||||
'alias' => 'cnfi',
|
||||
),
|
||||
'type_name' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
);
|
||||
return $ids;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Plugin\migrate\source\d6;
|
||||
|
||||
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
|
||||
|
||||
/**
|
||||
* The field instance per form display source class.
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "d6_field_instance_per_form_display",
|
||||
* source_provider = "content"
|
||||
* )
|
||||
*/
|
||||
class FieldInstancePerFormDisplay extends DrupalSqlBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function initializeIterator() {
|
||||
$rows = array();
|
||||
$result = $this->prepareQuery()->execute();
|
||||
while ($field_row = $result->fetchAssoc()) {
|
||||
$bundle = $field_row['type_name'];
|
||||
$field_name = $field_row['field_name'];
|
||||
|
||||
$index = "$bundle.$field_name";
|
||||
$rows[$index]['type_name'] = $bundle;
|
||||
$rows[$index]['widget_active'] = (bool) $field_row['widget_active'];
|
||||
$rows[$index]['field_name'] = $field_name;
|
||||
$rows[$index]['type'] = $field_row['type'];
|
||||
$rows[$index]['module'] = $field_row['module'];
|
||||
$rows[$index]['weight'] = $field_row['weight'];
|
||||
$rows[$index]['widget_type'] = $field_row['widget_type'];
|
||||
$rows[$index]['widget_settings'] = unserialize($field_row['widget_settings']);
|
||||
$rows[$index]['display_settings'] = unserialize($field_row['display_settings']);
|
||||
}
|
||||
|
||||
return new \ArrayIterator($rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
$query = $this->select('content_node_field_instance', 'cnfi')
|
||||
->fields('cnfi', array(
|
||||
'field_name',
|
||||
'type_name',
|
||||
'weight',
|
||||
'label',
|
||||
'widget_type',
|
||||
'widget_settings',
|
||||
'display_settings',
|
||||
'description',
|
||||
'widget_module',
|
||||
'widget_active',
|
||||
))
|
||||
->fields('cnf', array(
|
||||
'type',
|
||||
'module',
|
||||
));
|
||||
$query->join('content_node_field', 'cnf', 'cnfi.field_name = cnf.field_name');
|
||||
$query->orderBy('cnfi.weight');
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
return array(
|
||||
'field_name' => $this->t('The machine name of field.'),
|
||||
'type_name' => $this->t('Content type where this field is used.'),
|
||||
'weight' => $this->t('Weight.'),
|
||||
'label' => $this->t('A name to show.'),
|
||||
'widget_type' => $this->t('Widget type.'),
|
||||
'widget_settings' => $this->t('Serialize data with widget settings.'),
|
||||
'display_settings' => $this->t('Serialize data with display settings.'),
|
||||
'description' => $this->t('A description of field.'),
|
||||
'widget_module' => $this->t('Module that implements widget.'),
|
||||
'widget_active' => $this->t('Status of widget'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
$ids['type_name']['type'] = 'string';
|
||||
$ids['field_name']['type'] = 'string';
|
||||
return $ids;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Plugin\migrate\source\d6;
|
||||
|
||||
use Drupal\node\Plugin\migrate\source\d6\ViewModeBase;
|
||||
|
||||
/**
|
||||
* The field instance per view mode source class.
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "d6_field_instance_per_view_mode",
|
||||
* source_provider = "content"
|
||||
* )
|
||||
*/
|
||||
class FieldInstancePerViewMode extends ViewModeBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function initializeIterator() {
|
||||
$rows = array();
|
||||
$result = $this->prepareQuery()->execute();
|
||||
while ($field_row = $result->fetchAssoc()) {
|
||||
// These are added to every view mode row.
|
||||
$field_row['display_settings'] = unserialize($field_row['display_settings']);
|
||||
$field_row['widget_settings'] = unserialize($field_row['widget_settings']);
|
||||
$bundle = $field_row['type_name'];
|
||||
$field_name = $field_row['field_name'];
|
||||
|
||||
foreach ($this->getViewModes() as $view_mode) {
|
||||
if (isset($field_row['display_settings'][$view_mode]) && empty($field_row['display_settings'][$view_mode]['exclude'])) {
|
||||
$index = $view_mode . "." . $bundle . "." . $field_name;
|
||||
$rows[$index]['entity_type'] = 'node';
|
||||
$rows[$index]['view_mode'] = $view_mode;
|
||||
$rows[$index]['type_name'] = $bundle;
|
||||
$rows[$index]['field_name'] = $field_name;
|
||||
$rows[$index]['type'] = $field_row['type'];
|
||||
$rows[$index]['module'] = $field_row['module'];
|
||||
$rows[$index]['weight'] = $field_row['weight'];
|
||||
$rows[$index]['label'] = $field_row['display_settings']['label']['format'];
|
||||
$rows[$index]['display_settings'] = $field_row['display_settings'][$view_mode];
|
||||
$rows[$index]['widget_settings'] = $field_row['widget_settings'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new \ArrayIterator($rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
$query = $this->select('content_node_field_instance', 'cnfi')
|
||||
->fields('cnfi', array(
|
||||
'field_name',
|
||||
'type_name',
|
||||
'weight',
|
||||
'label',
|
||||
'display_settings',
|
||||
'widget_settings',
|
||||
))
|
||||
->fields('cnf', array(
|
||||
'type',
|
||||
'module',
|
||||
));
|
||||
$query->join('content_node_field', 'cnf', 'cnfi.field_name = cnf.field_name');
|
||||
$query->orderBy('cnfi.weight');
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
return array(
|
||||
'field_name' => $this->t('The machine name of field.'),
|
||||
'type_name' => $this->t('Content type where this field is used.'),
|
||||
'weight' => $this->t('Weight.'),
|
||||
'label' => $this->t('A name to show.'),
|
||||
'widget_type' => $this->t('Widget type.'),
|
||||
'widget_settings' => $this->t('Serialize data with widget settings.'),
|
||||
'display_settings' => $this->t('Serialize data with display settings.'),
|
||||
'description' => $this->t('A description of field.'),
|
||||
'widget_module' => $this->t('Module that implements widget.'),
|
||||
'widget_active' => $this->t('Status of widget'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
$ids['type_name']['type'] = 'string';
|
||||
$ids['view_mode']['type'] = 'string';
|
||||
$ids['entity_type']['type'] = 'string';
|
||||
$ids['field_name']['type'] = 'string';
|
||||
return $ids;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Plugin\migrate\source\d7;
|
||||
|
||||
use Drupal\migrate\Row;
|
||||
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
|
||||
|
||||
/**
|
||||
* Drupal 7 field source from database.
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "d7_field"
|
||||
* )
|
||||
*/
|
||||
class Field extends DrupalSqlBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
$query = $this->select('field_config', 'fc')
|
||||
->distinct()
|
||||
->fields('fc')
|
||||
->fields('fci', array('entity_type'))
|
||||
->condition('fc.active', 1)
|
||||
->condition('fc.deleted', 0)
|
||||
->condition('fc.storage_active', 1);
|
||||
$query->join('field_config_instance', 'fci', 'fc.id = fci.field_id');
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
return array(
|
||||
'field_name' => $this->t('The name of this field.'),
|
||||
'type' => $this->t('The type of this field.'),
|
||||
'module' => $this->t('The module that implements the field type.'),
|
||||
'storage' => $this->t('The field storage.'),
|
||||
'locked' => $this->t('Locked'),
|
||||
'cardinality' => $this->t('Cardinality'),
|
||||
'translatable' => $this->t('Translatable'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prepareRow(Row $row, $keep = TRUE) {
|
||||
foreach (unserialize($row->getSourceProperty('data')) as $key => $value) {
|
||||
$row->setSourceProperty($key, $value);
|
||||
}
|
||||
return parent::prepareRow($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
return array(
|
||||
'field_name' => array(
|
||||
'type' => 'string',
|
||||
'alias' => 'fc',
|
||||
),
|
||||
'entity_type' => array(
|
||||
'type' => 'string',
|
||||
'alias' => 'fci',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Plugin\migrate\source\d7;
|
||||
|
||||
use Drupal\migrate\Row;
|
||||
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
|
||||
|
||||
/**
|
||||
* Drupal 7 field instances source from database.
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "d7_field_instance",
|
||||
* source_provider = "field"
|
||||
* )
|
||||
*/
|
||||
class FieldInstance extends DrupalSqlBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
$query = $this->select('field_config_instance', 'fci')
|
||||
->fields('fci')
|
||||
->condition('fci.deleted', 0)
|
||||
->condition('fc.active', 1)
|
||||
->condition('fc.deleted', 0)
|
||||
->condition('fc.storage_active', 1)
|
||||
->fields('fc', array('type'));
|
||||
|
||||
$query->innerJoin('field_config', 'fc', 'fci.field_id = fc.id');
|
||||
$query->addField('fc', 'data', 'field_data');
|
||||
|
||||
// Optionally filter by entity type and bundle.
|
||||
if (isset($this->configuration['entity_type'])) {
|
||||
$query->condition('fci.entity_type', $this->configuration['entity_type']);
|
||||
|
||||
if (isset($this->configuration['bundle'])) {
|
||||
$query->condition('fci.bundle', $this->configuration['bundle']);
|
||||
}
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
return array(
|
||||
'field_name' => $this->t('The machine name of field.'),
|
||||
'entity_type' => $this->t('The entity type.'),
|
||||
'bundle' => $this->t('The entity bundle.'),
|
||||
'default_value' => $this->t('Default value'),
|
||||
'instance_settings' => $this->t('Field instance settings.'),
|
||||
'widget_settings' => $this->t('Widget settings.'),
|
||||
'display_settings' => $this->t('Display settings.'),
|
||||
'field_settings' => $this->t('Field settings.'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prepareRow(Row $row) {
|
||||
$data = unserialize($row->getSourceProperty('data'));
|
||||
|
||||
$row->setSourceProperty('label', $data['label']);
|
||||
$row->setSourceProperty('description', $data['description']);
|
||||
$row->setSourceProperty('required', $data['required']);
|
||||
|
||||
$default_value = !empty($data['default_value']) ? $data['default_value'] : array();
|
||||
if ($data['widget']['type'] == 'email_textfield' && $default_value) {
|
||||
$default_value[0]['value'] = $default_value[0]['email'];
|
||||
unset($default_value[0]['email']);
|
||||
}
|
||||
$row->setSourceProperty('default_value', $default_value);
|
||||
|
||||
// Settings.
|
||||
$row->setSourceProperty('instance_settings', $data['settings']);
|
||||
$row->setSourceProperty('widget_settings', $data['widget']);
|
||||
$row->setSourceProperty('display_settings', $data['display']);
|
||||
|
||||
// This is for parity with the d6_field_instance plugin.
|
||||
$row->setSourceProperty('widget_type', $data['widget']['type']);
|
||||
|
||||
$field_data = unserialize($row->getSourceProperty('field_data'));
|
||||
$row->setSourceProperty('field_settings', $field_data['settings']);
|
||||
|
||||
return parent::prepareRow($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
return array(
|
||||
'entity_type' => array(
|
||||
'type' => 'string',
|
||||
'alias' => 'fci',
|
||||
),
|
||||
'bundle' => array(
|
||||
'type' => 'string',
|
||||
'alias' => 'fci',
|
||||
),
|
||||
'field_name' => array(
|
||||
'type' => 'string',
|
||||
'alias' => 'fci',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Plugin\migrate\source\d7;
|
||||
|
||||
use Drupal\migrate\Row;
|
||||
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
|
||||
|
||||
/**
|
||||
* The field instance per form display source class.
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "d7_field_instance_per_form_display"
|
||||
* )
|
||||
*/
|
||||
class FieldInstancePerFormDisplay extends DrupalSqlBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
$query = $this->select('field_config_instance', 'fci')
|
||||
->fields('fci', array(
|
||||
'field_name',
|
||||
'bundle',
|
||||
'data',
|
||||
'entity_type'
|
||||
))
|
||||
->fields('fc', array(
|
||||
'type',
|
||||
'module',
|
||||
));
|
||||
$query->join('field_config', 'fc', 'fci.field_id = fc.id');
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prepareRow(Row $row) {
|
||||
$data = unserialize($row->getSourceProperty('data'));
|
||||
$row->setSourceProperty('widget', $data['widget']);
|
||||
$row->setSourceProperty('widget_settings', $data['widget']['settings']);
|
||||
return parent::prepareRow($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
return array(
|
||||
'field_name' => $this->t('The machine name of field.'),
|
||||
'bundle' => $this->t('Content type where this field is used.'),
|
||||
'data' => $this->t('Field configuration data.'),
|
||||
'entity_type' => $this->t('The entity type.'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
return array(
|
||||
'bundle' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
'field_name' => array(
|
||||
'type' => 'string',
|
||||
'alias' => 'fci',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Plugin\migrate\source\d7;
|
||||
|
||||
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
|
||||
|
||||
/**
|
||||
* The field instance per view mode source class.
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "d7_field_instance_per_view_mode",
|
||||
* source_provider = "field"
|
||||
* )
|
||||
*/
|
||||
class FieldInstancePerViewMode extends DrupalSqlBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function initializeIterator() {
|
||||
$rows = array();
|
||||
$result = $this->prepareQuery()->execute();
|
||||
foreach ($result as $field_instance) {
|
||||
$data = unserialize($field_instance['data']);
|
||||
// We don't need to include the serialized data in the returned rows.
|
||||
unset($field_instance['data']);
|
||||
|
||||
foreach ($data['display'] as $view_mode => $info) {
|
||||
// Rename type to formatter_type in the info array.
|
||||
$info['formatter_type'] = $info['type'];
|
||||
unset($info['type']);
|
||||
|
||||
$rows[] = array_merge($field_instance, $info, [
|
||||
'view_mode' => $view_mode,
|
||||
]);
|
||||
}
|
||||
}
|
||||
return new \ArrayIterator($rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
$query = $this->select('field_config_instance', 'fci')
|
||||
->fields('fci', ['entity_type', 'bundle', 'field_name', 'data'])
|
||||
->fields('fc', ['type']);
|
||||
$query->join('field_config', 'fc', 'fc.field_name = fci.field_name');
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
return array(
|
||||
'entity_type' => $this->t('The entity type ID.'),
|
||||
'bundle' => $this->t('The bundle ID.'),
|
||||
'field_name' => $this->t('Machine name of the field.'),
|
||||
'view_mode' => $this->t('The original machine name of the view mode.'),
|
||||
'label' => $this->t('The display label of the field.'),
|
||||
'type' => $this->t('The field ID.'),
|
||||
'formatter_type' => $this->t('The formatter ID.'),
|
||||
'settings' => $this->t('Array of formatter-specific settings.'),
|
||||
'module' => $this->t('The module providing the formatter.'),
|
||||
'weight' => $this->t('Display weight of the field.'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
return array(
|
||||
'entity_type' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
'bundle' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
'view_mode' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
'field_name' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function count() {
|
||||
return $this->initializeIterator()->count();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Plugin\migrate\source\d7;
|
||||
|
||||
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
|
||||
|
||||
/**
|
||||
* @MigrateSource(
|
||||
* id = "d7_view_mode"
|
||||
* )
|
||||
*/
|
||||
class ViewMode extends DrupalSqlBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function initializeIterator() {
|
||||
$rows = [];
|
||||
$result = $this->prepareQuery()->execute();
|
||||
foreach ($result as $field_instance) {
|
||||
$data = unserialize($field_instance['data']);
|
||||
foreach (array_keys($data['display']) as $view_mode) {
|
||||
$key = $field_instance['entity_type'] . '.' . $view_mode;
|
||||
$rows[$key] = [
|
||||
'entity_type' => $field_instance['entity_type'],
|
||||
'view_mode' => $view_mode,
|
||||
];
|
||||
}
|
||||
}
|
||||
return new \ArrayIterator($rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
return [
|
||||
'view_mode' => $this->t('The view mode ID.'),
|
||||
'entity_type' => $this->t('The entity type ID.'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
return $this->select('field_config_instance', 'fci')
|
||||
->fields('fci', ['entity_type', 'data']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
return [
|
||||
'entity_type' => [
|
||||
'type' => 'string',
|
||||
],
|
||||
'view_mode' => [
|
||||
'type' => 'string',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function count() {
|
||||
return $this->initializeIterator()->count();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
// @codingStandardsIgnoreFile
|
||||
|
||||
/**
|
||||
* This file was generated via php core/scripts/generate-proxy-class.php 'Drupal\field\FieldUninstallValidator' "core/modules/field/src".
|
||||
*/
|
||||
|
||||
namespace Drupal\field\ProxyClass {
|
||||
|
||||
/**
|
||||
* Provides a proxy class for \Drupal\field\FieldUninstallValidator.
|
||||
*
|
||||
* @see \Drupal\Component\ProxyBuilder
|
||||
*/
|
||||
class FieldUninstallValidator implements \Drupal\Core\Extension\ModuleUninstallValidatorInterface
|
||||
{
|
||||
|
||||
use \Drupal\Core\DependencyInjection\DependencySerializationTrait;
|
||||
|
||||
/**
|
||||
* The id of the original proxied service.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $drupalProxyOriginalServiceId;
|
||||
|
||||
/**
|
||||
* The real proxied service, after it was lazy loaded.
|
||||
*
|
||||
* @var \Drupal\field\FieldUninstallValidator
|
||||
*/
|
||||
protected $service;
|
||||
|
||||
/**
|
||||
* The service container.
|
||||
*
|
||||
* @var \Symfony\Component\DependencyInjection\ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* Constructs a ProxyClass Drupal proxy object.
|
||||
*
|
||||
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
|
||||
* The container.
|
||||
* @param string $drupal_proxy_original_service_id
|
||||
* The service ID of the original service.
|
||||
*/
|
||||
public function __construct(\Symfony\Component\DependencyInjection\ContainerInterface $container, $drupal_proxy_original_service_id)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->drupalProxyOriginalServiceId = $drupal_proxy_original_service_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazy loads the real service from the container.
|
||||
*
|
||||
* @return object
|
||||
* Returns the constructed real service.
|
||||
*/
|
||||
protected function lazyLoadItself()
|
||||
{
|
||||
if (!isset($this->service)) {
|
||||
$this->service = $this->container->get($this->drupalProxyOriginalServiceId);
|
||||
}
|
||||
|
||||
return $this->service;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate($module)
|
||||
{
|
||||
return $this->lazyLoadItself()->validate($module);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setStringTranslation(\Drupal\Core\StringTranslation\TranslationInterface $translation)
|
||||
{
|
||||
return $this->lazyLoadItself()->setStringTranslation($translation);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
249
web/core/modules/field/src/Tests/Boolean/BooleanFieldTest.php
Normal file
249
web/core/modules/field/src/Tests/Boolean/BooleanFieldTest.php
Normal file
|
@ -0,0 +1,249 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Tests\Boolean;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests boolean field functionality.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class BooleanFieldTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = [
|
||||
'entity_test',
|
||||
'field_ui',
|
||||
'options',
|
||||
'field_test_boolean_access_denied',
|
||||
];
|
||||
|
||||
/**
|
||||
* A field to use in this test class.
|
||||
*
|
||||
* @var \Drupal\field\Entity\FieldStorageConfig
|
||||
*/
|
||||
protected $fieldStorage;
|
||||
|
||||
/**
|
||||
* The field used in this test class.
|
||||
*
|
||||
* @var \Drupal\field\Entity\FieldConfig
|
||||
*/
|
||||
protected $field;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser(array(
|
||||
'view test entity',
|
||||
'administer entity_test content',
|
||||
'administer entity_test form display',
|
||||
'administer entity_test fields',
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests boolean field.
|
||||
*/
|
||||
function testBooleanField() {
|
||||
$on = $this->randomMachineName();
|
||||
$off = $this->randomMachineName();
|
||||
$label = $this->randomMachineName();
|
||||
|
||||
// Create a field with settings to validate.
|
||||
$field_name = Unicode::strtolower($this->randomMachineName());
|
||||
$this->fieldStorage = FieldStorageConfig::create(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'boolean',
|
||||
));
|
||||
$this->fieldStorage->save();
|
||||
$this->field = FieldConfig::create(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
'label' => $label,
|
||||
'required' => TRUE,
|
||||
'settings' => array(
|
||||
'on_label' => $on,
|
||||
'off_label' => $off,
|
||||
),
|
||||
));
|
||||
$this->field->save();
|
||||
|
||||
// Create a form display for the default form mode.
|
||||
entity_get_form_display('entity_test', 'entity_test', 'default')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'boolean_checkbox',
|
||||
))
|
||||
->save();
|
||||
// Create a display for the full view mode.
|
||||
entity_get_display('entity_test', 'entity_test', 'full')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'boolean',
|
||||
))
|
||||
->save();
|
||||
|
||||
// Display creation form.
|
||||
$this->drupalGet('entity_test/add');
|
||||
$this->assertFieldByName("{$field_name}[value]", '', 'Widget found.');
|
||||
$this->assertText($this->field->label(), 'Uses field label by default.');
|
||||
$this->assertNoRaw($on, 'Does not use the "On" label.');
|
||||
|
||||
// Submit and ensure it is accepted.
|
||||
$edit = array(
|
||||
"{$field_name}[value]" => 1,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
|
||||
$id = $match[1];
|
||||
$this->assertText(t('entity_test @id has been created.', array('@id' => $id)));
|
||||
|
||||
// Verify that boolean value is displayed.
|
||||
$entity = EntityTest::load($id);
|
||||
$display = entity_get_display($entity->getEntityTypeId(), $entity->bundle(), 'full');
|
||||
$content = $display->build($entity);
|
||||
$this->setRawContent(\Drupal::service('renderer')->renderRoot($content));
|
||||
$this->assertRaw('<div class="field__item">' . $on . '</div>');
|
||||
|
||||
// Test with "On" label option.
|
||||
entity_get_form_display('entity_test', 'entity_test', 'default')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'boolean_checkbox',
|
||||
'settings' => array(
|
||||
'display_label' => FALSE,
|
||||
)
|
||||
))
|
||||
->save();
|
||||
|
||||
$this->drupalGet('entity_test/add');
|
||||
$this->assertFieldByName("{$field_name}[value]", '', 'Widget found.');
|
||||
$this->assertRaw($on);
|
||||
$this->assertNoText($this->field->label());
|
||||
|
||||
// Test if we can change the on label.
|
||||
$on = $this->randomMachineName();
|
||||
$edit = array(
|
||||
'settings[on_label]' => $on,
|
||||
);
|
||||
$this->drupalPostForm('entity_test/structure/entity_test/fields/entity_test.entity_test.' . $field_name, $edit, t('Save settings'));
|
||||
// Check if we see the updated labels in the creation form.
|
||||
$this->drupalGet('entity_test/add');
|
||||
$this->assertRaw($on);
|
||||
|
||||
// Go to the form display page and check if the default settings works as
|
||||
// expected.
|
||||
$fieldEditUrl = 'entity_test/structure/entity_test/form-display';
|
||||
$this->drupalGet($fieldEditUrl);
|
||||
|
||||
// Click on the widget settings button to open the widget settings form.
|
||||
$this->drupalPostAjaxForm(NULL, array(), $field_name . "_settings_edit");
|
||||
|
||||
$this->assertText(
|
||||
'Use field label instead of the "On label" as label',
|
||||
t('Display setting checkbox available.')
|
||||
);
|
||||
|
||||
// Enable setting.
|
||||
$edit = array('fields[' . $field_name . '][settings_edit_form][settings][display_label]' => 1);
|
||||
$this->drupalPostAjaxForm(NULL, $edit, $field_name . "_plugin_settings_update");
|
||||
$this->drupalPostForm(NULL, NULL, 'Save');
|
||||
|
||||
// Go again to the form display page and check if the setting
|
||||
// is stored and has the expected effect.
|
||||
$this->drupalGet($fieldEditUrl);
|
||||
$this->assertText('Use field label: Yes', 'Checking the display settings checkbox updated the value.');
|
||||
|
||||
$this->drupalPostAjaxForm(NULL, array(), $field_name . "_settings_edit");
|
||||
$this->assertText(
|
||||
'Use field label instead of the "On label" as label',
|
||||
t('Display setting checkbox is available')
|
||||
);
|
||||
$this->assertFieldByXPath(
|
||||
'*//input[starts-with(@id, "edit-fields-' . $field_name . '-settings-edit-form-settings-display-label") and @value="1"]',
|
||||
TRUE,
|
||||
t('Display label changes label of the checkbox')
|
||||
);
|
||||
|
||||
// Test the boolean field settings.
|
||||
$this->drupalGet('entity_test/structure/entity_test/fields/entity_test.entity_test.' . $field_name);
|
||||
$this->assertFieldById('edit-settings-on-label', $on);
|
||||
$this->assertFieldById('edit-settings-off-label', $off);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test field access.
|
||||
*/
|
||||
public function testFormAccess() {
|
||||
$on = 'boolean_on';
|
||||
$off = 'boolean_off';
|
||||
$label = 'boolean_label';
|
||||
$field_name = 'boolean_name';
|
||||
$this->fieldStorage = FieldStorageConfig::create([
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'boolean',
|
||||
]);
|
||||
$this->fieldStorage->save();
|
||||
$this->field = FieldConfig::create([
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
'label' => $label,
|
||||
'settings' => [
|
||||
'on_label' => $on,
|
||||
'off_label' => $off,
|
||||
],
|
||||
]);
|
||||
$this->field->save();
|
||||
|
||||
// Create a form display for the default form mode.
|
||||
entity_get_form_display('entity_test', 'entity_test', 'default')
|
||||
->setComponent($field_name, [
|
||||
'type' => 'boolean_checkbox',
|
||||
])
|
||||
->save();
|
||||
|
||||
// Create a display for the full view mode.
|
||||
entity_get_display('entity_test', 'entity_test', 'full')
|
||||
->setComponent($field_name, [
|
||||
'type' => 'boolean',
|
||||
])
|
||||
->save();
|
||||
|
||||
// Display creation form.
|
||||
$this->drupalGet('entity_test/add');
|
||||
$this->assertFieldByName("{$field_name}[value]");
|
||||
|
||||
// Should be posted OK.
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
|
||||
$id = $match[1];
|
||||
$this->assertText(t('entity_test @id has been created.', ['@id' => $id]));
|
||||
|
||||
// Tell the test module to disable access to the field.
|
||||
\Drupal::state()->set('field.test_boolean_field_access_field', $field_name);
|
||||
$this->drupalGet('entity_test/add');
|
||||
// Field should not be there anymore.
|
||||
$this->assertNoFieldByName("{$field_name}[value]");
|
||||
// Should still be able to post the form.
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
|
||||
$id = $match[1];
|
||||
$this->assertText(t('entity_test @id has been created.', ['@id' => $id]));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Tests\Boolean;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests the Boolean field formatter settings.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class BooleanFormatterSettingsTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['field', 'field_ui', 'text', 'node', 'user'];
|
||||
|
||||
/**
|
||||
* The name of the entity bundle that is created in the test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $bundle;
|
||||
|
||||
/**
|
||||
* The name of the Boolean field to use for testing.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create a content type. Use Node because it has Field UI pages that work.
|
||||
$type_name = Unicode::strtolower($this->randomMachineName(8)) . '_test';
|
||||
$type = $this->drupalCreateContentType(array('name' => $type_name, 'type' => $type_name));
|
||||
$this->bundle = $type->id();
|
||||
|
||||
$admin_user = $this->drupalCreateUser(array('access content', 'administer content types', 'administer node fields', 'administer node display', 'bypass node access', 'administer nodes'));
|
||||
$this->drupalLogin($admin_user);
|
||||
|
||||
$this->fieldName = Unicode::strtolower($this->randomMachineName(8));
|
||||
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'field_name' => $this->fieldName,
|
||||
'entity_type' => 'node',
|
||||
'type' => 'boolean',
|
||||
]);
|
||||
$field_storage->save();
|
||||
|
||||
$instance = FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => $this->bundle,
|
||||
'label' => $this->randomMachineName(),
|
||||
]);
|
||||
$instance->save();
|
||||
|
||||
$display = entity_get_display('node', $this->bundle, 'default')
|
||||
->setComponent($this->fieldName, [
|
||||
'type' => 'boolean',
|
||||
'settings' => [],
|
||||
]);
|
||||
$display->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the formatter settings page for the Boolean formatter.
|
||||
*/
|
||||
function testBooleanFormatterSettings() {
|
||||
// List the options we expect to see on the settings form. Omit the one
|
||||
// with the Unicode check/x characters, which does not appear to work
|
||||
// well in WebTestBase.
|
||||
$options = array(
|
||||
'Yes / No',
|
||||
'True / False',
|
||||
'On / Off',
|
||||
'Enabled / Disabled',
|
||||
'1 / 0',
|
||||
'Custom',
|
||||
);
|
||||
|
||||
// Define what the "default" option should look like, depending on the
|
||||
// field settings.
|
||||
$default = 'Field settings (@on / @off)';
|
||||
|
||||
// For several different values of the field settings, test that the
|
||||
// options, including default, are shown correctly.
|
||||
$settings = array(
|
||||
array('Yes', 'No'),
|
||||
array('On', 'Off'),
|
||||
array('TRUE', 'FALSE'),
|
||||
);
|
||||
|
||||
foreach ($settings as $values) {
|
||||
// Set up the field settings.
|
||||
$this->drupalGet('admin/structure/types/manage/' . $this->bundle . '/fields/node.' . $this->bundle . '.' . $this->fieldName);
|
||||
$this->drupalPostForm(NULL, array(
|
||||
'settings[on_label]' => $values[0],
|
||||
'settings[off_label]' => $values[1],
|
||||
), 'Save settings');
|
||||
|
||||
// Open the Manage Display page and trigger the field settings form.
|
||||
$this->drupalGet('admin/structure/types/manage/' . $this->bundle . '/display');
|
||||
$this->drupalPostAjaxForm(NULL, array(), $this->fieldName . '_settings_edit');
|
||||
|
||||
// Test that the settings options are present in the correct format.
|
||||
foreach ($options as $string) {
|
||||
$this->assertText($string);
|
||||
}
|
||||
$this->assertText(SafeMarkup::format($default, array('@on' => $values[0], '@off' => $values[1])));
|
||||
}
|
||||
|
||||
foreach ($settings as $values) {
|
||||
$this->drupalGet('admin/structure/types/manage/' . $this->bundle . '/display');
|
||||
$result = $this->xpath('//div[contains(@class, :class) and contains(text(), :text)]', [':class' => 'field-plugin-summary', ':text' => 'Display: TRUE / FALSE']);
|
||||
$this->assertEqual(count($result), 1, "Boolean formatter settings summary exist.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
107
web/core/modules/field/src/Tests/Email/EmailFieldTest.php
Normal file
107
web/core/modules/field/src/Tests/Email/EmailFieldTest.php
Normal file
|
@ -0,0 +1,107 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Tests\Email;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Tests email field functionality.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class EmailFieldTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'entity_test', 'field_ui');
|
||||
|
||||
/**
|
||||
* A field storage to use in this test class.
|
||||
*
|
||||
* @var \Drupal\field\Entity\FieldStorageConfig
|
||||
*/
|
||||
protected $fieldStorage;
|
||||
|
||||
/**
|
||||
* The field used in this test class.
|
||||
*
|
||||
* @var \Drupal\field\Entity\FieldConfig
|
||||
*/
|
||||
protected $field;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser(array(
|
||||
'view test entity',
|
||||
'administer entity_test content',
|
||||
'administer content types',
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests email field.
|
||||
*/
|
||||
function testEmailField() {
|
||||
// Create a field with settings to validate.
|
||||
$field_name = Unicode::strtolower($this->randomMachineName());
|
||||
$this->fieldStorage = FieldStorageConfig::create(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'email',
|
||||
));
|
||||
$this->fieldStorage->save();
|
||||
$this->field = FieldConfig::create([
|
||||
'field_storage' => $this->fieldStorage,
|
||||
'bundle' => 'entity_test',
|
||||
]);
|
||||
$this->field->save();
|
||||
|
||||
// Create a form display for the default form mode.
|
||||
entity_get_form_display('entity_test', 'entity_test', 'default')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'email_default',
|
||||
'settings' => array(
|
||||
'placeholder' => 'example@example.com',
|
||||
),
|
||||
))
|
||||
->save();
|
||||
// Create a display for the full view mode.
|
||||
entity_get_display('entity_test', 'entity_test', 'full')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'email_mailto',
|
||||
))
|
||||
->save();
|
||||
|
||||
// Display creation form.
|
||||
$this->drupalGet('entity_test/add');
|
||||
$this->assertFieldByName("{$field_name}[0][value]", '', 'Widget found.');
|
||||
$this->assertRaw('placeholder="example@example.com"');
|
||||
|
||||
// Submit a valid email address and ensure it is accepted.
|
||||
$value = 'test@example.com';
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $value,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
|
||||
$id = $match[1];
|
||||
$this->assertText(t('entity_test @id has been created.', array('@id' => $id)));
|
||||
$this->assertRaw($value);
|
||||
|
||||
// Verify that a mailto link is displayed.
|
||||
$entity = EntityTest::load($id);
|
||||
$display = entity_get_display($entity->getEntityTypeId(), $entity->bundle(), 'full');
|
||||
$content = $display->build($entity);
|
||||
$this->setRawContent(\Drupal::service('renderer')->renderRoot($content));
|
||||
$this->assertLinkByHref('mailto:test@example.com');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,554 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Tests\EntityReference;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\field_ui\Tests\FieldUiTestTrait;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
|
||||
/**
|
||||
* Tests for the administrative UI.
|
||||
*
|
||||
* @group entity_reference
|
||||
*/
|
||||
class EntityReferenceAdminTest extends WebTestBase {
|
||||
|
||||
use FieldUiTestTrait;
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* Enable path module to ensure that the selection handler does not fail for
|
||||
* entities with a path field.
|
||||
* Enable views_ui module to see the no_view_help text.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node', 'field_ui', 'path', 'taxonomy', 'block', 'views_ui'];
|
||||
|
||||
/**
|
||||
* The name of the content type created for testing purposes.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->drupalPlaceBlock('system_breadcrumb_block');
|
||||
|
||||
// Create a content type, with underscores.
|
||||
$type_name = strtolower($this->randomMachineName(8)) . '_test';
|
||||
$type = $this->drupalCreateContentType(array('name' => $type_name, 'type' => $type_name));
|
||||
$this->type = $type->id();
|
||||
|
||||
// Create test user.
|
||||
$admin_user = $this->drupalCreateUser(array(
|
||||
'access content',
|
||||
'administer node fields',
|
||||
'administer node display',
|
||||
'administer views',
|
||||
'create ' . $type_name . ' content',
|
||||
'edit own ' . $type_name . ' content',
|
||||
));
|
||||
$this->drupalLogin($admin_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the Entity Reference Admin UI.
|
||||
*/
|
||||
public function testFieldAdminHandler() {
|
||||
$bundle_path = 'admin/structure/types/manage/' . $this->type;
|
||||
|
||||
// First step: 'Add new field' on the 'Manage fields' page.
|
||||
$this->drupalGet($bundle_path . '/fields/add-field');
|
||||
|
||||
// Check if the commonly referenced entity types appear in the list.
|
||||
$this->assertOption('edit-new-storage-type', 'field_ui:entity_reference:node');
|
||||
$this->assertOption('edit-new-storage-type', 'field_ui:entity_reference:user');
|
||||
|
||||
$this->drupalPostForm(NULL, array(
|
||||
'label' => 'Test label',
|
||||
'field_name' => 'test',
|
||||
'new_storage_type' => 'entity_reference',
|
||||
), t('Save and continue'));
|
||||
|
||||
// Node should be selected by default.
|
||||
$this->assertFieldByName('settings[target_type]', 'node');
|
||||
|
||||
// Check that all entity types can be referenced.
|
||||
$this->assertFieldSelectOptions('settings[target_type]', array_keys(\Drupal::entityManager()->getDefinitions()));
|
||||
|
||||
// Second step: 'Field settings' form.
|
||||
$this->drupalPostForm(NULL, array(), t('Save field settings'));
|
||||
|
||||
// The base handler should be selected by default.
|
||||
$this->assertFieldByName('settings[handler]', 'default:node');
|
||||
|
||||
// The base handler settings should be displayed.
|
||||
$entity_type_id = 'node';
|
||||
$bundles = $this->container->get('entity_type.bundle.info')->getBundleInfo($entity_type_id);
|
||||
foreach ($bundles as $bundle_name => $bundle_info) {
|
||||
$this->assertFieldByName('settings[handler_settings][target_bundles][' . $bundle_name . ']');
|
||||
}
|
||||
|
||||
reset($bundles);
|
||||
|
||||
// Test the sort settings.
|
||||
// Option 0: no sort.
|
||||
$this->assertFieldByName('settings[handler_settings][sort][field]', '_none');
|
||||
$this->assertNoFieldByName('settings[handler_settings][sort][direction]');
|
||||
// Option 1: sort by field.
|
||||
$this->drupalPostAjaxForm(NULL, array('settings[handler_settings][sort][field]' => 'nid'), 'settings[handler_settings][sort][field]');
|
||||
$this->assertFieldByName('settings[handler_settings][sort][direction]', 'ASC');
|
||||
|
||||
// Test that a non-translatable base field is a sort option.
|
||||
$this->assertFieldByXPath("//select[@name='settings[handler_settings][sort][field]']/option[@value='nid']");
|
||||
// Test that a translatable base field is a sort option.
|
||||
$this->assertFieldByXPath("//select[@name='settings[handler_settings][sort][field]']/option[@value='title']");
|
||||
// Test that a configurable field is a sort option.
|
||||
$this->assertFieldByXPath("//select[@name='settings[handler_settings][sort][field]']/option[@value='body.value']");
|
||||
|
||||
// Set back to no sort.
|
||||
$this->drupalPostAjaxForm(NULL, array('settings[handler_settings][sort][field]' => '_none'), 'settings[handler_settings][sort][field]');
|
||||
$this->assertNoFieldByName('settings[handler_settings][sort][direction]');
|
||||
|
||||
// Third step: confirm.
|
||||
$this->drupalPostForm(NULL, array(
|
||||
'required' => '1',
|
||||
'settings[handler_settings][target_bundles][' . key($bundles) . ']' => key($bundles),
|
||||
), t('Save settings'));
|
||||
|
||||
// Check that the field appears in the overview form.
|
||||
$this->assertFieldByXPath('//table[@id="field-overview"]//tr[@id="field-test"]/td[1]', 'Test label', 'Field was created and appears in the overview page.');
|
||||
|
||||
// Check that the field settings form can be submitted again, even when the
|
||||
// field is required.
|
||||
// The first 'Edit' link is for the Body field.
|
||||
$this->clickLink(t('Edit'), 1);
|
||||
$this->drupalPostForm(NULL, array(), t('Save settings'));
|
||||
|
||||
// Switch the target type to 'taxonomy_term' and check that the settings
|
||||
// specific to its selection handler are displayed.
|
||||
$field_name = 'node.' . $this->type . '.field_test';
|
||||
$edit = array(
|
||||
'settings[target_type]' => 'taxonomy_term',
|
||||
);
|
||||
$this->drupalPostForm($bundle_path . '/fields/' . $field_name . '/storage', $edit, t('Save field settings'));
|
||||
$this->drupalGet($bundle_path . '/fields/' . $field_name);
|
||||
$this->assertFieldByName('settings[handler_settings][auto_create]');
|
||||
|
||||
// Switch the target type to 'user' and check that the settings specific to
|
||||
// its selection handler are displayed.
|
||||
$field_name = 'node.' . $this->type . '.field_test';
|
||||
$edit = array(
|
||||
'settings[target_type]' => 'user',
|
||||
);
|
||||
$this->drupalPostForm($bundle_path . '/fields/' . $field_name . '/storage', $edit, t('Save field settings'));
|
||||
$this->drupalGet($bundle_path . '/fields/' . $field_name);
|
||||
$this->assertFieldByName('settings[handler_settings][filter][type]', '_none');
|
||||
|
||||
// Switch the target type to 'node'.
|
||||
$field_name = 'node.' . $this->type . '.field_test';
|
||||
$edit = array(
|
||||
'settings[target_type]' => 'node',
|
||||
);
|
||||
$this->drupalPostForm($bundle_path . '/fields/' . $field_name . '/storage', $edit, t('Save field settings'));
|
||||
|
||||
// Try to select the views handler.
|
||||
$edit = array(
|
||||
'settings[handler]' => 'views',
|
||||
);
|
||||
$this->drupalPostAjaxForm($bundle_path . '/fields/' . $field_name, $edit, 'settings[handler]');
|
||||
$this->assertRaw(t('No eligible views were found. <a href=":create">Create a view</a> with an <em>Entity Reference</em> display, or add such a display to an <a href=":existing">existing view</a>.', array(
|
||||
':create' => \Drupal::url('views_ui.add'),
|
||||
':existing' => \Drupal::url('entity.view.collection'),
|
||||
)));
|
||||
$this->drupalPostForm(NULL, $edit, t('Save settings'));
|
||||
// If no eligible view is available we should see a message.
|
||||
$this->assertText('The views entity selection mode requires a view.');
|
||||
|
||||
// Enable the entity_reference_test module which creates an eligible view.
|
||||
$this->container->get('module_installer')->install(array('entity_reference_test'));
|
||||
$this->resetAll();
|
||||
$this->drupalGet($bundle_path . '/fields/' . $field_name);
|
||||
$this->drupalPostAjaxForm($bundle_path . '/fields/' . $field_name, $edit, 'settings[handler]');
|
||||
$edit = array(
|
||||
'settings[handler_settings][view][view_and_display]' => 'test_entity_reference:entity_reference_1',
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save settings'));
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Switch the target type to 'entity_test'.
|
||||
$edit = array(
|
||||
'settings[target_type]' => 'entity_test',
|
||||
);
|
||||
$this->drupalPostForm($bundle_path . '/fields/' . $field_name . '/storage', $edit, t('Save field settings'));
|
||||
$this->drupalGet($bundle_path . '/fields/' . $field_name);
|
||||
$edit = array(
|
||||
'settings[handler]' => 'views',
|
||||
);
|
||||
$this->drupalPostAjaxForm($bundle_path . '/fields/' . $field_name, $edit, 'settings[handler]');
|
||||
$edit = array(
|
||||
'required' => FALSE,
|
||||
'settings[handler_settings][view][view_and_display]' => 'test_entity_reference_entity_test:entity_reference_1',
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save settings'));
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Create a new view and display it as a entity reference.
|
||||
$edit = array(
|
||||
'id' => 'node_test_view',
|
||||
'label' => 'Node Test View',
|
||||
'show[wizard_key]' => 'node',
|
||||
'show[sort]' => 'none',
|
||||
'page[create]' => 1,
|
||||
'page[title]' => 'Test Node View',
|
||||
'page[path]' => 'test/node/view',
|
||||
'page[style][style_plugin]' => 'default',
|
||||
'page[style][row_plugin]' => 'fields',
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/views/add', $edit, t('Save and edit'));
|
||||
$this->drupalPostForm(NULL, array(), t('Duplicate as Entity Reference'));
|
||||
$this->clickLink(t('Settings'));
|
||||
$edit = array(
|
||||
'style_options[search_fields][title]' => 'title',
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply'));
|
||||
|
||||
// Set sort to NID ascending.
|
||||
$edit = [
|
||||
'name[node_field_data.nid]' => 1,
|
||||
];
|
||||
$this->drupalPostForm('admin/structure/views/nojs/add-handler/node_test_view/entity_reference_1/sort', $edit, t('Add and configure sort criteria'));
|
||||
$this->drupalPostForm(NULL, NULL, t('Apply'));
|
||||
|
||||
$this->drupalPostForm('admin/structure/views/view/node_test_view/edit/entity_reference_1', array(), t('Save'));
|
||||
$this->clickLink(t('Settings'));
|
||||
|
||||
// Create a test entity reference field.
|
||||
$field_name = 'test_entity_ref_field';
|
||||
$edit = array(
|
||||
'new_storage_type' => 'field_ui:entity_reference:node',
|
||||
'label' => 'Test Entity Reference Field',
|
||||
'field_name' => $field_name,
|
||||
);
|
||||
$this->drupalPostForm($bundle_path . '/fields/add-field', $edit, t('Save and continue'));
|
||||
|
||||
// Set to unlimited.
|
||||
$edit = array(
|
||||
'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save field settings'));
|
||||
|
||||
// Add the view to the test field.
|
||||
$edit = array(
|
||||
'settings[handler]' => 'views',
|
||||
);
|
||||
$this->drupalPostAjaxForm(NULL, $edit, 'settings[handler]');
|
||||
$edit = array(
|
||||
'required' => FALSE,
|
||||
'settings[handler_settings][view][view_and_display]' => 'node_test_view:entity_reference_1',
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save settings'));
|
||||
|
||||
// Create nodes.
|
||||
$node1 = Node::create([
|
||||
'type' => $this->type,
|
||||
'title' => 'Foo Node',
|
||||
]);
|
||||
$node1->save();
|
||||
$node2 = Node::create([
|
||||
'type' => $this->type,
|
||||
'title' => 'Foo Node',
|
||||
]);
|
||||
$node2->save();
|
||||
|
||||
// Try to add a new node and fill the entity reference field.
|
||||
$this->drupalGet('node/add/' . $this->type);
|
||||
$result = $this->xpath('//input[@name="field_test_entity_ref_field[0][target_id]" and contains(@data-autocomplete-path, "/entity_reference_autocomplete/node/views/")]');
|
||||
$target_url = $this->getAbsoluteUrl($result[0]['data-autocomplete-path']);
|
||||
$this->drupalGet($target_url, array('query' => array('q' => 'Foo')));
|
||||
$this->assertRaw($node1->getTitle() . ' (' . $node1->id() . ')');
|
||||
$this->assertRaw($node2->getTitle() . ' (' . $node2->id() . ')');
|
||||
|
||||
// Try to add a new node, fill the entity reference field and submit the
|
||||
// form.
|
||||
$this->drupalPostForm('node/add/' . $this->type, [], t('Add another item'));
|
||||
$edit = array(
|
||||
'title[0][value]' => 'Example',
|
||||
'field_test_entity_ref_field[0][target_id]' => 'Foo Node (' . $node1->id() . ')',
|
||||
'field_test_entity_ref_field[1][target_id]' => 'Foo Node (' . $node2->id() . ')',
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertResponse(200);
|
||||
|
||||
$edit = array(
|
||||
'title[0][value]' => 'Example',
|
||||
'field_test_entity_ref_field[0][target_id]' => 'Test'
|
||||
);
|
||||
$this->drupalPostForm('node/add/' . $this->type, $edit, t('Save'));
|
||||
|
||||
// Assert that entity reference autocomplete field is validated.
|
||||
$this->assertText(t('There are no entities matching "@entity"', ['@entity' => 'Test']));
|
||||
|
||||
$edit = array(
|
||||
'title[0][value]' => 'Test',
|
||||
'field_test_entity_ref_field[0][target_id]' => $node1->getTitle()
|
||||
);
|
||||
$this->drupalPostForm('node/add/' . $this->type, $edit, t('Save'));
|
||||
|
||||
// Assert the results multiple times to avoid sorting problem of nodes with
|
||||
// the same title.
|
||||
$this->assertText(t('Multiple entities match this reference;'));
|
||||
$this->assertText(t("@node1", ['@node1' => $node1->getTitle() . ' (' . $node1->id() . ')']));
|
||||
$this->assertText(t("@node2", ['@node2' => $node2->getTitle() . ' (' . $node2->id() . ')']));
|
||||
$this->assertText(t('Specify the one you want by appending the id in parentheses, like "@example".', ['@example' => $node2->getTitle() . ' (' . $node2->id() . ')']));
|
||||
|
||||
$edit = array(
|
||||
'title[0][value]' => 'Test',
|
||||
'field_test_entity_ref_field[0][target_id]' => $node1->getTitle() . ' (' . $node1->id() . ')'
|
||||
);
|
||||
$this->drupalPostForm('node/add/' . $this->type, $edit, t('Save'));
|
||||
$this->assertLink($node1->getTitle());
|
||||
|
||||
// Tests adding default values to autocomplete widgets.
|
||||
Vocabulary::create(array('vid' => 'tags', 'name' => 'tags'))->save();
|
||||
$taxonomy_term_field_name = $this->createEntityReferenceField('taxonomy_term', ['tags']);
|
||||
$field_path = 'node.' . $this->type . '.field_' . $taxonomy_term_field_name;
|
||||
$this->drupalGet($bundle_path . '/fields/' . $field_path . '/storage');
|
||||
$edit = [
|
||||
'cardinality' => -1,
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save field settings'));
|
||||
$this->drupalGet($bundle_path . '/fields/' . $field_path);
|
||||
$term_name = $this->randomString();
|
||||
$edit = [
|
||||
// This must be set before new entities will be auto-created.
|
||||
'settings[handler_settings][auto_create]' => 1,
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save settings'));
|
||||
$this->drupalGet($bundle_path . '/fields/' . $field_path);
|
||||
$edit = [
|
||||
// A term that doesn't yet exist.
|
||||
'default_value_input[field_' . $taxonomy_term_field_name . '][0][target_id]' => $term_name,
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save settings'));
|
||||
// The term should now exist.
|
||||
$term = taxonomy_term_load_multiple_by_name($term_name, 'tags')[1];
|
||||
$this->assertIdentical(1, count($term), 'Taxonomy term was auto created when set as field default.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the formatters for the Entity References.
|
||||
*/
|
||||
public function testAvailableFormatters() {
|
||||
// Create a new vocabulary.
|
||||
Vocabulary::create(array('vid' => 'tags', 'name' => 'tags'))->save();
|
||||
|
||||
// Create entity reference field with taxonomy term as a target.
|
||||
$taxonomy_term_field_name = $this->createEntityReferenceField('taxonomy_term', ['tags']);
|
||||
|
||||
// Create entity reference field with user as a target.
|
||||
$user_field_name = $this->createEntityReferenceField('user');
|
||||
|
||||
// Create entity reference field with node as a target.
|
||||
$node_field_name = $this->createEntityReferenceField('node', [$this->type]);
|
||||
|
||||
// Create entity reference field with date format as a target.
|
||||
$date_format_field_name = $this->createEntityReferenceField('date_format');
|
||||
|
||||
// Display all newly created Entity Reference configuration.
|
||||
$this->drupalGet('admin/structure/types/manage/' . $this->type . '/display');
|
||||
|
||||
// Check for Taxonomy Term select box values.
|
||||
// Test if Taxonomy Term Entity Reference Field has the correct formatters.
|
||||
$this->assertFieldSelectOptions('fields[field_' . $taxonomy_term_field_name . '][type]', array(
|
||||
'entity_reference_label',
|
||||
'entity_reference_entity_id',
|
||||
'entity_reference_rss_category',
|
||||
'entity_reference_entity_view',
|
||||
'hidden',
|
||||
));
|
||||
|
||||
// Test if User Reference Field has the correct formatters.
|
||||
// Author should be available for this field.
|
||||
// RSS Category should not be available for this field.
|
||||
$this->assertFieldSelectOptions('fields[field_' . $user_field_name . '][type]', array(
|
||||
'author',
|
||||
'entity_reference_entity_id',
|
||||
'entity_reference_entity_view',
|
||||
'entity_reference_label',
|
||||
'hidden',
|
||||
));
|
||||
|
||||
// Test if Node Entity Reference Field has the correct formatters.
|
||||
// RSS Category should not be available for this field.
|
||||
$this->assertFieldSelectOptions('fields[field_' . $node_field_name . '][type]', array(
|
||||
'entity_reference_label',
|
||||
'entity_reference_entity_id',
|
||||
'entity_reference_entity_view',
|
||||
'hidden',
|
||||
));
|
||||
|
||||
// Test if Date Format Reference Field has the correct formatters.
|
||||
// RSS Category & Entity View should not be available for this field.
|
||||
// This could be any field without a ViewBuilder.
|
||||
$this->assertFieldSelectOptions('fields[field_' . $date_format_field_name . '][type]', array(
|
||||
'entity_reference_label',
|
||||
'entity_reference_entity_id',
|
||||
'hidden',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests field settings for an entity reference field when the field has
|
||||
* multiple target bundles and is set to auto-create the target entity.
|
||||
*/
|
||||
public function testMultipleTargetBundles() {
|
||||
/** @var \Drupal\taxonomy\Entity\Vocabulary[] $vocabularies */
|
||||
$vocabularies = [];
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$vid = Unicode::strtolower($this->randomMachineName());
|
||||
$vocabularies[$i] = Vocabulary::create([
|
||||
'name' => $this->randomString(),
|
||||
'vid' => $vid,
|
||||
]);
|
||||
$vocabularies[$i]->save();
|
||||
}
|
||||
|
||||
// Create a new field pointing to the first vocabulary.
|
||||
$field_name = $this->createEntityReferenceField('taxonomy_term', [$vocabularies[0]->id()]);
|
||||
$field_name = "field_$field_name";
|
||||
$field_id = 'node.' . $this->type . '.' . $field_name;
|
||||
$path = 'admin/structure/types/manage/' . $this->type . '/fields/' . $field_id;
|
||||
|
||||
$this->drupalGet($path);
|
||||
|
||||
// Expect that there's no 'auto_create_bundle' selected.
|
||||
$this->assertNoFieldByName('settings[handler_settings][auto_create_bundle]');
|
||||
|
||||
$edit = [
|
||||
'settings[handler_settings][target_bundles][' . $vocabularies[1]->id() . ']' => TRUE,
|
||||
];
|
||||
// Enable the second vocabulary as a target bundle.
|
||||
$this->drupalPostAjaxForm($path, $edit, key($edit));
|
||||
// Expect a select element with the two vocabularies as options.
|
||||
$this->assertFieldByXPath("//select[@name='settings[handler_settings][auto_create_bundle]']/option[@value='" . $vocabularies[0]->id() . "']");
|
||||
$this->assertFieldByXPath("//select[@name='settings[handler_settings][auto_create_bundle]']/option[@value='" . $vocabularies[1]->id() . "']");
|
||||
|
||||
$edit = [
|
||||
'settings[handler_settings][auto_create]' => TRUE,
|
||||
'settings[handler_settings][auto_create_bundle]' => $vocabularies[1]->id(),
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save settings'));
|
||||
|
||||
/** @var \Drupal\field\Entity\FieldConfig $field_config */
|
||||
$field_config = FieldConfig::load($field_id);
|
||||
// Expect that the target bundle has been saved in the backend.
|
||||
$this->assertEqual($field_config->getSetting('handler_settings')['auto_create_bundle'], $vocabularies[1]->id());
|
||||
|
||||
// Delete the other bundle. Field config should not be affected.
|
||||
$vocabularies[0]->delete();
|
||||
$field_config = FieldConfig::load($field_id);
|
||||
$this->assertTrue($field_config->getSetting('handler_settings')['auto_create']);
|
||||
$this->assertIdentical($field_config->getSetting('handler_settings')['auto_create_bundle'], $vocabularies[1]->id());
|
||||
|
||||
// Delete the bundle set for entity auto-creation. Auto-created settings
|
||||
// should be reset (no auto-creation).
|
||||
$vocabularies[1]->delete();
|
||||
$field_config = FieldConfig::load($field_id);
|
||||
$this->assertFalse($field_config->getSetting('handler_settings')['auto_create']);
|
||||
$this->assertFalse(isset($field_config->getSetting('handler_settings')['auto_create_bundle']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Entity Reference fields with a given target type.
|
||||
*
|
||||
* @param string $target_type
|
||||
* The name of the target type
|
||||
* @param string[] $bundles
|
||||
* A list of bundle IDs. Defaults to [].
|
||||
*
|
||||
* @return string
|
||||
* Returns the generated field name
|
||||
*/
|
||||
protected function createEntityReferenceField($target_type, $bundles = []) {
|
||||
// Generates a bundle path for the newly created content type.
|
||||
$bundle_path = 'admin/structure/types/manage/' . $this->type;
|
||||
|
||||
// Generate a random field name, must be only lowercase characters.
|
||||
$field_name = strtolower($this->randomMachineName());
|
||||
|
||||
$storage_edit = $field_edit = array();
|
||||
$storage_edit['settings[target_type]'] = $target_type;
|
||||
foreach ($bundles as $bundle) {
|
||||
$field_edit['settings[handler_settings][target_bundles][' . $bundle . ']'] = TRUE;
|
||||
}
|
||||
|
||||
$this->fieldUIAddNewField($bundle_path, $field_name, NULL, 'entity_reference', $storage_edit, $field_edit);
|
||||
|
||||
// Returns the generated field name.
|
||||
return $field_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a select element contains the specified options.
|
||||
*
|
||||
* @param string $name
|
||||
* The field name.
|
||||
* @param array $expected_options
|
||||
* An array of expected options.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the assertion succeeded, FALSE otherwise.
|
||||
*/
|
||||
protected function assertFieldSelectOptions($name, array $expected_options) {
|
||||
$xpath = $this->buildXPathQuery('//select[@name=:name]', array(':name' => $name));
|
||||
$fields = $this->xpath($xpath);
|
||||
if ($fields) {
|
||||
$field = $fields[0];
|
||||
$options = $this->getAllOptionsList($field);
|
||||
|
||||
sort($options);
|
||||
sort($expected_options);
|
||||
|
||||
return $this->assertIdentical($options, $expected_options);
|
||||
}
|
||||
else {
|
||||
return $this->fail('Unable to find field ' . $name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts all options from a select element.
|
||||
*
|
||||
* @param \SimpleXMLElement $element
|
||||
* The select element field information.
|
||||
*
|
||||
* @return array
|
||||
* An array of option values as strings.
|
||||
*/
|
||||
protected function getAllOptionsList(\SimpleXMLElement $element) {
|
||||
$options = array();
|
||||
// Add all options items.
|
||||
foreach ($element->option as $option) {
|
||||
$options[] = (string) $option['value'];
|
||||
}
|
||||
|
||||
// Loops trough all the option groups
|
||||
foreach ($element->optgroup as $optgroup) {
|
||||
$options = array_merge($this->getAllOptionsList($optgroup), $options);
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,228 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Tests\EntityReference;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Tests creating new entity (e.g. taxonomy-term) from an autocomplete widget.
|
||||
*
|
||||
* @group entity_reference
|
||||
*/
|
||||
class EntityReferenceAutoCreateTest extends WebTestBase {
|
||||
|
||||
use EntityReferenceTestTrait;
|
||||
|
||||
public static $modules = ['node', 'taxonomy'];
|
||||
|
||||
/**
|
||||
* The name of a content type that will reference $referencedType.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $referencingType;
|
||||
|
||||
/**
|
||||
* The name of a content type that will be referenced by $referencingType.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $referencedType;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create "referencing" and "referenced" node types.
|
||||
$referencing = $this->drupalCreateContentType();
|
||||
$this->referencingType = $referencing->id();
|
||||
|
||||
$referenced = $this->drupalCreateContentType();
|
||||
$this->referencedType = $referenced->id();
|
||||
|
||||
FieldStorageConfig::create(array(
|
||||
'field_name' => 'test_field',
|
||||
'entity_type' => 'node',
|
||||
'translatable' => FALSE,
|
||||
'entity_types' => array(),
|
||||
'settings' => array(
|
||||
'target_type' => 'node',
|
||||
),
|
||||
'type' => 'entity_reference',
|
||||
'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
|
||||
))->save();
|
||||
|
||||
FieldConfig::create([
|
||||
'label' => 'Entity reference field',
|
||||
'field_name' => 'test_field',
|
||||
'entity_type' => 'node',
|
||||
'bundle' => $referencing->id(),
|
||||
'settings' => array(
|
||||
'handler' => 'default',
|
||||
'handler_settings' => array(
|
||||
// Reference a single vocabulary.
|
||||
'target_bundles' => array(
|
||||
$referenced->id(),
|
||||
),
|
||||
// Enable auto-create.
|
||||
'auto_create' => TRUE,
|
||||
),
|
||||
),
|
||||
])->save();
|
||||
|
||||
entity_get_display('node', $referencing->id(), 'default')
|
||||
->setComponent('test_field')
|
||||
->save();
|
||||
entity_get_form_display('node', $referencing->id(), 'default')
|
||||
->setComponent('test_field', array(
|
||||
'type' => 'entity_reference_autocomplete',
|
||||
))
|
||||
->save();
|
||||
|
||||
$account = $this->drupalCreateUser(['access content', "create $this->referencingType content"]);
|
||||
$this->drupalLogin($account);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the autocomplete input element appears and the creation of a new
|
||||
* entity.
|
||||
*/
|
||||
public function testAutoCreate() {
|
||||
$this->drupalGet('node/add/' . $this->referencingType);
|
||||
$this->assertFieldByXPath('//input[@id="edit-test-field-0-target-id" and contains(@class, "form-autocomplete")]', NULL, 'The autocomplete input element appears.');
|
||||
|
||||
$new_title = $this->randomMachineName();
|
||||
|
||||
// Assert referenced node does not exist.
|
||||
$base_query = \Drupal::entityQuery('node');
|
||||
$base_query
|
||||
->condition('type', $this->referencedType)
|
||||
->condition('title', $new_title);
|
||||
|
||||
$query = clone $base_query;
|
||||
$result = $query->execute();
|
||||
$this->assertFalse($result, 'Referenced node does not exist yet.');
|
||||
|
||||
$edit = array(
|
||||
'title[0][value]' => $this->randomMachineName(),
|
||||
'test_field[0][target_id]' => $new_title,
|
||||
);
|
||||
$this->drupalPostForm("node/add/$this->referencingType", $edit, 'Save');
|
||||
|
||||
// Assert referenced node was created.
|
||||
$query = clone $base_query;
|
||||
$result = $query->execute();
|
||||
$this->assertTrue($result, 'Referenced node was created.');
|
||||
$referenced_nid = key($result);
|
||||
$referenced_node = Node::load($referenced_nid);
|
||||
|
||||
// Assert the referenced node is associated with referencing node.
|
||||
$result = \Drupal::entityQuery('node')
|
||||
->condition('type', $this->referencingType)
|
||||
->execute();
|
||||
|
||||
$referencing_nid = key($result);
|
||||
$referencing_node = Node::load($referencing_nid);
|
||||
$this->assertEqual($referenced_nid, $referencing_node->test_field->target_id, 'Newly created node is referenced from the referencing node.');
|
||||
|
||||
// Now try to view the node and check that the referenced node is shown.
|
||||
$this->drupalGet('node/' . $referencing_node->id());
|
||||
$this->assertText($referencing_node->label(), 'Referencing node label found.');
|
||||
$this->assertText($referenced_node->label(), 'Referenced node label found.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if an entity reference field having multiple target bundles is
|
||||
* storing the auto-created entity in the right destination.
|
||||
*/
|
||||
public function testMultipleTargetBundles() {
|
||||
/** @var \Drupal\taxonomy\Entity\Vocabulary[] $vocabularies */
|
||||
$vocabularies = [];
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$vid = Unicode::strtolower($this->randomMachineName());
|
||||
$vocabularies[$i] = Vocabulary::create([
|
||||
'name' => $this->randomMachineName(),
|
||||
'vid' => $vid,
|
||||
]);
|
||||
$vocabularies[$i]->save();
|
||||
}
|
||||
|
||||
// Create a taxonomy term entity reference field that saves the auto-created
|
||||
// taxonomy terms in the second vocabulary from the two that were configured
|
||||
// as targets.
|
||||
$field_name = Unicode::strtolower($this->randomMachineName());
|
||||
$handler_settings = [
|
||||
'target_bundles' => [
|
||||
$vocabularies[0]->id() => $vocabularies[0]->id(),
|
||||
$vocabularies[1]->id() => $vocabularies[1]->id(),
|
||||
],
|
||||
'auto_create' => TRUE,
|
||||
'auto_create_bundle' => $vocabularies[1]->id(),
|
||||
];
|
||||
$this->createEntityReferenceField('node', $this->referencingType, $field_name, $this->randomString(), 'taxonomy_term', 'default', $handler_settings);
|
||||
/** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $fd */
|
||||
entity_get_form_display('node', $this->referencingType, 'default')
|
||||
->setComponent($field_name, ['type' => 'entity_reference_autocomplete'])
|
||||
->save();
|
||||
|
||||
$term_name = $this->randomString();
|
||||
$edit = [
|
||||
$field_name . '[0][target_id]' => $term_name,
|
||||
'title[0][value]' => $this->randomString(),
|
||||
];
|
||||
|
||||
$this->drupalPostForm('node/add/' . $this->referencingType, $edit, 'Save');
|
||||
/** @var \Drupal\taxonomy\Entity\Term $term */
|
||||
$term = taxonomy_term_load_multiple_by_name($term_name);
|
||||
$term = reset($term);
|
||||
|
||||
// The new term is expected to be stored in the second vocabulary.
|
||||
$this->assertEqual($vocabularies[1]->id(), $term->bundle());
|
||||
|
||||
/** @var \Drupal\field\Entity\FieldConfig $field_config */
|
||||
$field_config = FieldConfig::loadByName('node', $this->referencingType, $field_name);
|
||||
$handler_settings = $field_config->getSetting('handler_settings');
|
||||
|
||||
// Change the field setting to store the auto-created terms in the first
|
||||
// vocabulary and test again.
|
||||
$handler_settings['auto_create_bundle'] = $vocabularies[0]->id();
|
||||
$field_config->setSetting('handler_settings', $handler_settings);
|
||||
$field_config->save();
|
||||
|
||||
$term_name = $this->randomString();
|
||||
$edit = [
|
||||
$field_name . '[0][target_id]' => $term_name,
|
||||
'title[0][value]' => $this->randomString(),
|
||||
];
|
||||
|
||||
$this->drupalPostForm('node/add/' . $this->referencingType, $edit, 'Save');
|
||||
/** @var \Drupal\taxonomy\Entity\Term $term */
|
||||
$term = taxonomy_term_load_multiple_by_name($term_name);
|
||||
$term = reset($term);
|
||||
|
||||
// The second term is expected to be stored in the first vocabulary.
|
||||
$this->assertEqual($vocabularies[0]->id(), $term->bundle());
|
||||
|
||||
// @todo Re-enable this test when WebTestBase::curlHeaderCallback() provides
|
||||
// a way to catch and assert user-triggered errors.
|
||||
|
||||
// Test the case when the field config settings are inconsistent.
|
||||
//unset($handler_settings['auto_create_bundle']);
|
||||
//$field_config->setSetting('handler_settings', $handler_settings);
|
||||
//$field_config->save();
|
||||
//
|
||||
//$this->drupalGet('node/add/' . $this->referencingType);
|
||||
//$error_message = sprintf(
|
||||
// "Create referenced entities if they don't already exist option is enabled but a specific destination bundle is not set. You should re-visit and fix the settings of the '%s' (%s) field.",
|
||||
// $field_config->getLabel(),
|
||||
// $field_config->getName()
|
||||
//);
|
||||
//$this->assertErrorLogged($error_message);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Tests\EntityReference;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\config\Tests\SchemaCheckTestTrait;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests entity reference field default values storage in CMI.
|
||||
*
|
||||
* @group entity_reference
|
||||
*/
|
||||
class EntityReferenceFieldDefaultValueTest extends WebTestBase {
|
||||
|
||||
use SchemaCheckTestTrait;
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['field_ui', 'node'];
|
||||
|
||||
/**
|
||||
* A user with permission to administer content types, node fields, etc.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create default content type.
|
||||
$this->drupalCreateContentType(array('type' => 'reference_content'));
|
||||
$this->drupalCreateContentType(array('type' => 'referenced_content'));
|
||||
|
||||
// Create admin user.
|
||||
$this->adminUser = $this->drupalCreateUser(array('access content', 'administer content types', 'administer node fields', 'administer node form display', 'bypass node access'));
|
||||
$this->drupalLogin($this->adminUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that default values are correctly translated to UUIDs in config.
|
||||
*/
|
||||
function testEntityReferenceDefaultValue() {
|
||||
// Create a node to be referenced.
|
||||
$referenced_node = $this->drupalCreateNode(array('type' => 'referenced_content'));
|
||||
|
||||
$field_name = Unicode::strtolower($this->randomMachineName());
|
||||
$field_storage = FieldStorageConfig::create(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'node',
|
||||
'type' => 'entity_reference',
|
||||
'settings' => array('target_type' => 'node'),
|
||||
));
|
||||
$field_storage->save();
|
||||
$field = FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'reference_content',
|
||||
'settings' => array(
|
||||
'handler' => 'default',
|
||||
'handler_settings' => array(
|
||||
'target_bundles' => array('referenced_content'),
|
||||
'sort' => array('field' => '_none'),
|
||||
),
|
||||
),
|
||||
]);
|
||||
$field->save();
|
||||
|
||||
// Set created node as default_value.
|
||||
$field_edit = array(
|
||||
'default_value_input[' . $field_name . '][0][target_id]' => $referenced_node->getTitle() . ' (' . $referenced_node->id() . ')',
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/types/manage/reference_content/fields/node.reference_content.' . $field_name, $field_edit, t('Save settings'));
|
||||
|
||||
// Check that default value is selected in default value form.
|
||||
$this->drupalGet('admin/structure/types/manage/reference_content/fields/node.reference_content.' . $field_name);
|
||||
$this->assertRaw('name="default_value_input[' . $field_name . '][0][target_id]" value="' . $referenced_node->getTitle() . ' (' . $referenced_node->id() . ')', 'The default value is selected in instance settings page');
|
||||
|
||||
// Check if the ID has been converted to UUID in config entity.
|
||||
$config_entity = $this->config('field.field.node.reference_content.' . $field_name)->get();
|
||||
$this->assertTrue(isset($config_entity['default_value'][0]['target_uuid']), 'Default value contains target_uuid property');
|
||||
$this->assertEqual($config_entity['default_value'][0]['target_uuid'], $referenced_node->uuid(), 'Content uuid and config entity uuid are the same');
|
||||
// Ensure the configuration has the expected dependency on the entity that
|
||||
// is being used a default value.
|
||||
$this->assertEqual(array($referenced_node->getConfigDependencyName()), $config_entity['dependencies']['content']);
|
||||
|
||||
// Clear field definitions cache in order to avoid stale cache values.
|
||||
\Drupal::entityManager()->clearCachedFieldDefinitions();
|
||||
|
||||
// Create a new node to check that UUID has been converted to numeric ID.
|
||||
$new_node = Node::create(['type' => 'reference_content']);
|
||||
$this->assertEqual($new_node->get($field_name)->offsetGet(0)->target_id, $referenced_node->id());
|
||||
|
||||
// Ensure that the entity reference config schemas are correct.
|
||||
$field_config = $this->config('field.field.node.reference_content.' . $field_name);
|
||||
$this->assertConfigSchema(\Drupal::service('config.typed'), $field_config->getName(), $field_config->get());
|
||||
$field_storage_config = $this->config('field.storage.node.' . $field_name);
|
||||
$this->assertConfigSchema(\Drupal::service('config.typed'), $field_storage_config->getName(), $field_storage_config->get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that dependencies due to default values can be removed.
|
||||
*
|
||||
* @see \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem::onDependencyRemoval()
|
||||
*/
|
||||
function testEntityReferenceDefaultConfigValue() {
|
||||
// Create a node to be referenced.
|
||||
$referenced_node_type = $this->drupalCreateContentType(array('type' => 'referenced_config_to_delete'));
|
||||
$referenced_node_type2 = $this->drupalCreateContentType(array('type' => 'referenced_config_to_preserve'));
|
||||
|
||||
$field_name = Unicode::strtolower($this->randomMachineName());
|
||||
$field_storage = FieldStorageConfig::create(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'node',
|
||||
'type' => 'entity_reference',
|
||||
'settings' => array('target_type' => 'node_type'),
|
||||
'cardinality' => FieldStorageConfig::CARDINALITY_UNLIMITED,
|
||||
));
|
||||
$field_storage->save();
|
||||
$field = FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'reference_content',
|
||||
'settings' => array(
|
||||
'handler' => 'default',
|
||||
'handler_settings' => array(
|
||||
'sort' => array('field' => '_none'),
|
||||
),
|
||||
),
|
||||
]);
|
||||
$field->save();
|
||||
|
||||
// Set created node as default_value.
|
||||
$field_edit = array(
|
||||
'default_value_input[' . $field_name . '][0][target_id]' => $referenced_node_type->label() . ' (' . $referenced_node_type->id() . ')',
|
||||
'default_value_input[' . $field_name . '][1][target_id]' => $referenced_node_type2->label() . ' (' . $referenced_node_type2->id() . ')',
|
||||
);
|
||||
$this->drupalPostForm('admin/structure/types/manage/reference_content/fields/node.reference_content.' . $field_name, $field_edit, t('Save settings'));
|
||||
|
||||
// Check that the field has a dependency on the default value.
|
||||
$config_entity = $this->config('field.field.node.reference_content.' . $field_name)->get();
|
||||
$this->assertTrue(in_array($referenced_node_type->getConfigDependencyName(), $config_entity['dependencies']['config'], TRUE), 'The node type referenced_config_to_delete is a dependency of the field.');
|
||||
$this->assertTrue(in_array($referenced_node_type2->getConfigDependencyName(), $config_entity['dependencies']['config'], TRUE), 'The node type referenced_config_to_preserve is a dependency of the field.');
|
||||
|
||||
// Check that the field does not have a dependency on the default value
|
||||
// after deleting the node type.
|
||||
$referenced_node_type->delete();
|
||||
$config_entity = $this->config('field.field.node.reference_content.' . $field_name)->get();
|
||||
$this->assertFalse(in_array($referenced_node_type->getConfigDependencyName(), $config_entity['dependencies']['config'], TRUE), 'The node type referenced_config_to_delete not a dependency of the field.');
|
||||
$this->assertTrue(in_array($referenced_node_type2->getConfigDependencyName(), $config_entity['dependencies']['config'], TRUE), 'The node type referenced_config_to_preserve is a dependency of the field.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,322 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Tests\EntityReference;
|
||||
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Tests the translation of entity reference field display on nodes.
|
||||
*
|
||||
* @group entity_reference
|
||||
*/
|
||||
class EntityReferenceFieldTranslatedReferenceViewTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Flag indicating whether the field is translatable.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $translatable = TRUE;
|
||||
|
||||
/**
|
||||
* The langcode of the source language.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $baseLangcode = 'en';
|
||||
|
||||
/**
|
||||
* Target langcode for translation.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $translateToLangcode = 'hu';
|
||||
|
||||
/**
|
||||
* The test entity type name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $testEntityTypeName = 'node';
|
||||
|
||||
/**
|
||||
* Entity type which have the entity reference field.
|
||||
*
|
||||
* @var \Drupal\node\Entity\NodeType
|
||||
*/
|
||||
protected $referrerType;
|
||||
|
||||
/**
|
||||
* Entity type which can be referenced.
|
||||
*
|
||||
* @var \Drupal\node\Entity\NodeType
|
||||
*/
|
||||
protected $referencedType;
|
||||
|
||||
/**
|
||||
* The referrer entity.
|
||||
*
|
||||
* @var \Drupal\node\Entity\Node
|
||||
*/
|
||||
protected $referrerEntity;
|
||||
|
||||
/**
|
||||
* The entity to refer.
|
||||
*
|
||||
* @var \Drupal\node\Entity\Node
|
||||
*/
|
||||
protected $referencedEntityWithoutTranslation;
|
||||
|
||||
/**
|
||||
* The entity to refer.
|
||||
*
|
||||
* @var \Drupal\node\Entity\Node
|
||||
*/
|
||||
protected $referencedEntityWithTranslation;
|
||||
|
||||
/**
|
||||
* The machine name of the entity reference field.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $referenceFieldName = 'test_reference_field';
|
||||
|
||||
/**
|
||||
* The label of the untranslated referenced entity, used in assertions.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $labelOfNotTranslatedReference;
|
||||
|
||||
/**
|
||||
* The original label of the referenced entity, used in assertions.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $originalLabel;
|
||||
|
||||
/**
|
||||
* The translated label of the referenced entity, used in assertions.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $translatedLabel;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array(
|
||||
'language',
|
||||
'content_translation',
|
||||
'node',
|
||||
);
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->labelOfNotTranslatedReference = $this->randomMachineName();
|
||||
$this->originalLabel = $this->randomMachineName();
|
||||
$this->translatedLabel = $this->randomMachineName();
|
||||
|
||||
$this->setUpLanguages();
|
||||
|
||||
// We setup languages, so we need to ensure that the language manager
|
||||
// and language path processor is updated.
|
||||
$this->rebuildContainer();
|
||||
|
||||
$this->setUpContentTypes();
|
||||
$this->enableTranslation();
|
||||
$this->setUpEntityReferenceField();
|
||||
$this->createContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the entity is displayed in an entity reference field.
|
||||
*/
|
||||
public function testEntityReferenceDisplay() {
|
||||
// Create a translated referrer entity.
|
||||
$this->referrerEntity = $this->createReferrerEntity();
|
||||
$this->assertEntityReferenceDisplay();
|
||||
|
||||
// Disable translation for referrer content type.
|
||||
$this->drupalLogin($this->rootUser);
|
||||
$this->drupalPostForm('admin/config/regional/content-language', ['settings[node][referrer][translatable]' => FALSE], t('Save configuration'));
|
||||
|
||||
// Create a referrer entity without translation.
|
||||
$this->referrerEntity = $this->createReferrerEntity(FALSE);
|
||||
$this->assertEntityReferenceDisplay();
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert entity reference display.
|
||||
*/
|
||||
protected function assertEntityReferenceDisplay() {
|
||||
$url = $this->referrerEntity->urlInfo();
|
||||
$translation_url = $this->referrerEntity->urlInfo('canonical', ['language' => ConfigurableLanguage::load($this->translateToLangcode)]);
|
||||
|
||||
$this->drupalGet($url);
|
||||
$this->assertText($this->labelOfNotTranslatedReference, 'The label of not translated reference is displayed.');
|
||||
$this->assertText($this->originalLabel, 'The default label of translated reference is displayed.');
|
||||
$this->assertNoText($this->translatedLabel, 'The translated label of translated reference is not displayed.');
|
||||
$this->drupalGet($translation_url);
|
||||
$this->assertText($this->labelOfNotTranslatedReference, 'The label of not translated reference is displayed.');
|
||||
$this->assertNoText($this->originalLabel, 'The default label of translated reference is not displayed.');
|
||||
$this->assertText($this->translatedLabel, 'The translated label of translated reference is displayed.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds additional languages.
|
||||
*/
|
||||
protected function setUpLanguages() {
|
||||
ConfigurableLanguage::createFromLangcode($this->translateToLangcode)->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a test subject contents, with translation.
|
||||
*/
|
||||
protected function createContent() {
|
||||
$this->referencedEntityWithTranslation = $this->createReferencedEntityWithTranslation();
|
||||
$this->referencedEntityWithoutTranslation = $this->createNotTranslatedReferencedEntity();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables translations where it needed.
|
||||
*/
|
||||
protected function enableTranslation() {
|
||||
// Enable translation for the entity types and ensure the change is picked
|
||||
// up.
|
||||
\Drupal::service('content_translation.manager')->setEnabled($this->testEntityTypeName, $this->referrerType->id(), TRUE);
|
||||
\Drupal::service('content_translation.manager')->setEnabled($this->testEntityTypeName, $this->referencedType->id(), TRUE);
|
||||
drupal_static_reset();
|
||||
\Drupal::entityManager()->clearCachedDefinitions();
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
\Drupal::service('entity.definition_update_manager')->applyUpdates();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds term reference field for the article content type.
|
||||
*/
|
||||
protected function setUpEntityReferenceField() {
|
||||
FieldStorageConfig::create(array(
|
||||
'field_name' => $this->referenceFieldName,
|
||||
'entity_type' => $this->testEntityTypeName,
|
||||
'type' => 'entity_reference',
|
||||
'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
|
||||
'translatable' => $this->translatable,
|
||||
'settings' => array(
|
||||
'allowed_values' => array(
|
||||
array(
|
||||
'target_type' => $this->testEntityTypeName,
|
||||
),
|
||||
),
|
||||
),
|
||||
))->save();
|
||||
|
||||
FieldConfig::create([
|
||||
'field_name' => $this->referenceFieldName,
|
||||
'bundle' => $this->referrerType->id(),
|
||||
'entity_type' => $this->testEntityTypeName,
|
||||
])
|
||||
->save();
|
||||
entity_get_form_display($this->testEntityTypeName, $this->referrerType->id(), 'default')
|
||||
->setComponent($this->referenceFieldName, array(
|
||||
'type' => 'entity_reference_autocomplete',
|
||||
))
|
||||
->save();
|
||||
entity_get_display($this->testEntityTypeName, $this->referrerType->id(), 'default')
|
||||
->setComponent($this->referenceFieldName, array(
|
||||
'type' => 'entity_reference_label',
|
||||
))
|
||||
->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create content types.
|
||||
*/
|
||||
protected function setUpContentTypes() {
|
||||
$this->referrerType = $this->drupalCreateContentType(array(
|
||||
'type' => 'referrer',
|
||||
'name' => 'Referrer',
|
||||
));
|
||||
$this->referencedType = $this->drupalCreateContentType(array(
|
||||
'type' => 'referenced_page',
|
||||
'name' => 'Referenced Page',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a referenced entity with a translation.
|
||||
*/
|
||||
protected function createReferencedEntityWithTranslation() {
|
||||
/** @var \Drupal\node\Entity\Node $node */
|
||||
$node = entity_create($this->testEntityTypeName, array(
|
||||
'title' => $this->originalLabel,
|
||||
'type' => $this->referencedType->id(),
|
||||
'description' => array(
|
||||
'value' => $this->randomMachineName(),
|
||||
'format' => 'basic_html',
|
||||
),
|
||||
'langcode' => $this->baseLangcode,
|
||||
));
|
||||
$node->save();
|
||||
$node->addTranslation($this->translateToLangcode, array(
|
||||
'title' => $this->translatedLabel,
|
||||
));
|
||||
$node->save();
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create the referenced entity.
|
||||
*/
|
||||
protected function createNotTranslatedReferencedEntity() {
|
||||
/** @var \Drupal\node\Entity\Node $node */
|
||||
$node = entity_create($this->testEntityTypeName, array(
|
||||
'title' => $this->labelOfNotTranslatedReference,
|
||||
'type' => $this->referencedType->id(),
|
||||
'description' => array(
|
||||
'value' => $this->randomMachineName(),
|
||||
'format' => 'basic_html',
|
||||
),
|
||||
'langcode' => $this->baseLangcode,
|
||||
));
|
||||
$node->save();
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the referrer entity.
|
||||
*/
|
||||
protected function createReferrerEntity($translatable = TRUE) {
|
||||
/** @var \Drupal\node\Entity\Node $node */
|
||||
$node = entity_create($this->testEntityTypeName, array(
|
||||
'title' => $this->randomMachineName(),
|
||||
'type' => $this->referrerType->id(),
|
||||
'description' => array(
|
||||
'value' => $this->randomMachineName(),
|
||||
'format' => 'basic_html',
|
||||
),
|
||||
$this->referenceFieldName => array(
|
||||
array('target_id' => $this->referencedEntityWithTranslation->id()),
|
||||
array('target_id' => $this->referencedEntityWithoutTranslation->id()),
|
||||
),
|
||||
'langcode' => $this->baseLangcode,
|
||||
));
|
||||
if ($translatable) {
|
||||
$node->addTranslation($this->translateToLangcode, $node->toArray());
|
||||
}
|
||||
$node->save();
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Tests\EntityReference;
|
||||
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Tests an autocomplete widget with file upload.
|
||||
*
|
||||
* @group entity_reference
|
||||
*/
|
||||
class EntityReferenceFileUploadTest extends WebTestBase {
|
||||
|
||||
public static $modules = array('entity_reference', 'node', 'file');
|
||||
|
||||
/**
|
||||
* The name of a content type that will reference $referencedType.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $referencingType;
|
||||
|
||||
/**
|
||||
* The name of a content type that will be referenced by $referencingType.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $referencedType;
|
||||
|
||||
/**
|
||||
* Node id.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $nodeId;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create "referencing" and "referenced" node types.
|
||||
$referencing = $this->drupalCreateContentType();
|
||||
$this->referencingType = $referencing->id();
|
||||
|
||||
$referenced = $this->drupalCreateContentType();
|
||||
$this->referencedType = $referenced->id();
|
||||
$this->nodeId = $this->drupalCreateNode(array('type' => $referenced->id()))->id();
|
||||
|
||||
FieldStorageConfig::create(array(
|
||||
'field_name' => 'test_field',
|
||||
'entity_type' => 'node',
|
||||
'translatable' => FALSE,
|
||||
'entity_types' => array(),
|
||||
'settings' => array(
|
||||
'target_type' => 'node',
|
||||
),
|
||||
'type' => 'entity_reference',
|
||||
'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
|
||||
))->save();
|
||||
|
||||
FieldConfig::create([
|
||||
'label' => 'Entity reference field',
|
||||
'field_name' => 'test_field',
|
||||
'entity_type' => 'node',
|
||||
'required' => TRUE,
|
||||
'bundle' => $referencing->id(),
|
||||
'settings' => array(
|
||||
'handler' => 'default',
|
||||
'handler_settings' => array(
|
||||
// Reference a single vocabulary.
|
||||
'target_bundles' => array(
|
||||
$referenced->id(),
|
||||
),
|
||||
),
|
||||
),
|
||||
])->save();
|
||||
|
||||
|
||||
// Create a file field.
|
||||
$file_field_name = 'file_field';
|
||||
$field_storage = FieldStorageConfig::create(array(
|
||||
'field_name' => $file_field_name,
|
||||
'entity_type' => 'node',
|
||||
'type' => 'file'
|
||||
));
|
||||
$field_storage->save();
|
||||
FieldConfig::create([
|
||||
'entity_type' => 'node',
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => $referencing->id(),
|
||||
'label' => $this->randomMachineName() . '_label',
|
||||
])->save();
|
||||
|
||||
entity_get_display('node', $referencing->id(), 'default')
|
||||
->setComponent('test_field')
|
||||
->setComponent($file_field_name)
|
||||
->save();
|
||||
entity_get_form_display('node', $referencing->id(), 'default')
|
||||
->setComponent('test_field', array(
|
||||
'type' => 'entity_reference_autocomplete',
|
||||
))
|
||||
->setComponent($file_field_name, array(
|
||||
'type' => 'file_generic',
|
||||
))
|
||||
->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the autocomplete input element does not cause ajax fatal.
|
||||
*/
|
||||
public function testFileUpload() {
|
||||
$user1 = $this->drupalCreateUser(array('access content', "create $this->referencingType content"));
|
||||
$this->drupalLogin($user1);
|
||||
|
||||
$test_file = current($this->drupalGetTestFiles('text'));
|
||||
$edit['files[file_field_0]'] = drupal_realpath($test_file->uri);
|
||||
$this->drupalPostForm('node/add/' . $this->referencingType, $edit, 'Upload');
|
||||
$this->assertResponse(200);
|
||||
$edit = array(
|
||||
'title[0][value]' => $this->randomMachineName(),
|
||||
'test_field[0][target_id]' => $this->nodeId,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, 'Save');
|
||||
$this->assertResponse(200);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,221 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Tests\EntityReference;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\config\Tests\AssertConfigEntityImportTrait;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests various Entity reference UI components.
|
||||
*
|
||||
* @group entity_reference
|
||||
*/
|
||||
class EntityReferenceIntegrationTest extends WebTestBase {
|
||||
|
||||
use AssertConfigEntityImportTrait;
|
||||
use EntityReferenceTestTrait;
|
||||
|
||||
/**
|
||||
* The entity type used in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entityType = 'entity_test';
|
||||
|
||||
/**
|
||||
* The bundle used in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $bundle = 'entity_test';
|
||||
|
||||
/**
|
||||
* The name of the field used in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName;
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['config_test', 'entity_test', 'field_ui'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create a test user.
|
||||
$web_user = $this->drupalCreateUser(array('administer entity_test content', 'administer entity_test fields', 'view test entity'));
|
||||
$this->drupalLogin($web_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the entity reference field with all its supported field widgets.
|
||||
*/
|
||||
public function testSupportedEntityTypesAndWidgets() {
|
||||
foreach ($this->getTestEntities() as $key => $referenced_entities) {
|
||||
$this->fieldName = 'field_test_' . $referenced_entities[0]->getEntityTypeId();
|
||||
|
||||
// Create an Entity reference field.
|
||||
$this->createEntityReferenceField($this->entityType, $this->bundle, $this->fieldName, $this->fieldName, $referenced_entities[0]->getEntityTypeId(), 'default', array(), 2);
|
||||
|
||||
// Test the default 'entity_reference_autocomplete' widget.
|
||||
entity_get_form_display($this->entityType, $this->bundle, 'default')->setComponent($this->fieldName)->save();
|
||||
|
||||
$entity_name = $this->randomMachineName();
|
||||
$edit = array(
|
||||
'name[0][value]' => $entity_name,
|
||||
$this->fieldName . '[0][target_id]' => $referenced_entities[0]->label() . ' (' . $referenced_entities[0]->id() . ')',
|
||||
// Test an input of the entity label without a ' (entity_id)' suffix.
|
||||
$this->fieldName . '[1][target_id]' => $referenced_entities[1]->label(),
|
||||
);
|
||||
$this->drupalPostForm($this->entityType . '/add', $edit, t('Save'));
|
||||
$this->assertFieldValues($entity_name, $referenced_entities);
|
||||
|
||||
// Try to post the form again with no modification and check if the field
|
||||
// values remain the same.
|
||||
/** @var \Drupal\Core\Entity\EntityStorageInterface $storage */
|
||||
$storage = $this->container->get('entity_type.manager')->getStorage($this->entityType);
|
||||
$entity = current($storage->loadByProperties(['name' => $entity_name]));
|
||||
$this->drupalGet($this->entityType . '/manage/' . $entity->id() . '/edit');
|
||||
$this->assertFieldByName($this->fieldName . '[0][target_id]', $referenced_entities[0]->label() . ' (' . $referenced_entities[0]->id() . ')');
|
||||
$this->assertFieldByName($this->fieldName . '[1][target_id]', $referenced_entities[1]->label() . ' (' . $referenced_entities[1]->id() . ')');
|
||||
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
$this->assertFieldValues($entity_name, $referenced_entities);
|
||||
|
||||
// Test the 'entity_reference_autocomplete_tags' widget.
|
||||
entity_get_form_display($this->entityType, $this->bundle, 'default')->setComponent($this->fieldName, array(
|
||||
'type' => 'entity_reference_autocomplete_tags',
|
||||
))->save();
|
||||
|
||||
$entity_name = $this->randomMachineName();
|
||||
$target_id = $referenced_entities[0]->label() . ' (' . $referenced_entities[0]->id() . ')';
|
||||
// Test an input of the entity label without a ' (entity_id)' suffix.
|
||||
$target_id .= ', ' . $referenced_entities[1]->label();
|
||||
$edit = array(
|
||||
'name[0][value]' => $entity_name,
|
||||
$this->fieldName . '[target_id]' => $target_id,
|
||||
);
|
||||
$this->drupalPostForm($this->entityType . '/add', $edit, t('Save'));
|
||||
$this->assertFieldValues($entity_name, $referenced_entities);
|
||||
|
||||
// Try to post the form again with no modification and check if the field
|
||||
// values remain the same.
|
||||
$entity = current($storage->loadByProperties(['name' => $entity_name]));
|
||||
$this->drupalGet($this->entityType . '/manage/' . $entity->id() . '/edit');
|
||||
$this->assertFieldByName($this->fieldName . '[target_id]', $target_id . ' (' . $referenced_entities[1]->id() . ')');
|
||||
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
$this->assertFieldValues($entity_name, $referenced_entities);
|
||||
|
||||
// Test all the other widgets supported by the entity reference field.
|
||||
// Since we don't know the form structure for these widgets, just test
|
||||
// that editing and saving an already created entity works.
|
||||
$exclude = array('entity_reference_autocomplete', 'entity_reference_autocomplete_tags');
|
||||
$entity = current($storage->loadByProperties(['name' => $entity_name]));
|
||||
$supported_widgets = \Drupal::service('plugin.manager.field.widget')->getOptions('entity_reference');
|
||||
$supported_widget_types = array_diff(array_keys($supported_widgets), $exclude);
|
||||
|
||||
foreach ($supported_widget_types as $widget_type) {
|
||||
entity_get_form_display($this->entityType, $this->bundle, 'default')->setComponent($this->fieldName, array(
|
||||
'type' => $widget_type,
|
||||
))->save();
|
||||
|
||||
$this->drupalPostForm($this->entityType . '/manage/' . $entity->id() . '/edit', array(), t('Save'));
|
||||
$this->assertFieldValues($entity_name, $referenced_entities);
|
||||
}
|
||||
|
||||
// Reset to the default 'entity_reference_autocomplete' widget.
|
||||
entity_get_form_display($this->entityType, $this->bundle, 'default')->setComponent($this->fieldName)->save();
|
||||
|
||||
// Set first entity as the default_value.
|
||||
$field_edit = array(
|
||||
'default_value_input[' . $this->fieldName . '][0][target_id]' => $referenced_entities[0]->label() . ' (' . $referenced_entities[0]->id() . ')',
|
||||
);
|
||||
if ($key == 'content') {
|
||||
$field_edit['settings[handler_settings][target_bundles][' . $referenced_entities[0]->getEntityTypeId() . ']'] = TRUE;
|
||||
}
|
||||
$this->drupalPostForm($this->entityType . '/structure/' . $this->bundle . '/fields/' . $this->entityType . '.' . $this->bundle . '.' . $this->fieldName, $field_edit, t('Save settings'));
|
||||
// Ensure the configuration has the expected dependency on the entity that
|
||||
// is being used a default value.
|
||||
$field = FieldConfig::loadByName($this->entityType, $this->bundle, $this->fieldName);
|
||||
$this->assertTrue(in_array($referenced_entities[0]->getConfigDependencyName(), $field->getDependencies()[$key]), SafeMarkup::format('Expected @type dependency @name found', ['@type' => $key, '@name' => $referenced_entities[0]->getConfigDependencyName()]));
|
||||
// Ensure that the field can be imported without change even after the
|
||||
// default value deleted.
|
||||
$referenced_entities[0]->delete();
|
||||
// Reload the field since deleting the default value can change the field.
|
||||
\Drupal::entityManager()->getStorage($field->getEntityTypeId())->resetCache([$field->id()]);
|
||||
$field = FieldConfig::loadByName($this->entityType, $this->bundle, $this->fieldName);
|
||||
$this->assertConfigEntityImport($field);
|
||||
|
||||
// Once the default value has been removed after saving the dependency
|
||||
// should be removed.
|
||||
$field = FieldConfig::loadByName($this->entityType, $this->bundle, $this->fieldName);
|
||||
$field->save();
|
||||
$dependencies = $field->getDependencies();
|
||||
$this->assertFalse(isset($dependencies[$key]) && in_array($referenced_entities[0]->getConfigDependencyName(), $dependencies[$key]), SafeMarkup::format('@type dependency @name does not exist.', ['@type' => $key, '@name' => $referenced_entities[0]->getConfigDependencyName()]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the reference field values are correct.
|
||||
*
|
||||
* @param string $entity_name
|
||||
* The name of the test entity.
|
||||
* @param \Drupal\Core\Entity\EntityInterface[] $referenced_entities
|
||||
* An array of referenced entities.
|
||||
*/
|
||||
protected function assertFieldValues($entity_name, $referenced_entities) {
|
||||
$entity = current($this->container->get('entity_type.manager')->getStorage(
|
||||
$this->entityType)->loadByProperties(['name' => $entity_name]));
|
||||
|
||||
$this->assertTrue($entity, format_string('%entity_type: Entity found in the database.', array('%entity_type' => $this->entityType)));
|
||||
|
||||
$this->assertEqual($entity->{$this->fieldName}->target_id, $referenced_entities[0]->id());
|
||||
$this->assertEqual($entity->{$this->fieldName}->entity->id(), $referenced_entities[0]->id());
|
||||
$this->assertEqual($entity->{$this->fieldName}->entity->label(), $referenced_entities[0]->label());
|
||||
|
||||
$this->assertEqual($entity->{$this->fieldName}[1]->target_id, $referenced_entities[1]->id());
|
||||
$this->assertEqual($entity->{$this->fieldName}[1]->entity->id(), $referenced_entities[1]->id());
|
||||
$this->assertEqual($entity->{$this->fieldName}[1]->entity->label(), $referenced_entities[1]->label());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates two content and two config test entities.
|
||||
*
|
||||
* @return array
|
||||
* An array of entity objects.
|
||||
*/
|
||||
protected function getTestEntities() {
|
||||
$config_entity_1 = entity_create('config_test', array('id' => $this->randomMachineName(), 'label' => $this->randomMachineName()));
|
||||
$config_entity_1->save();
|
||||
$config_entity_2 = entity_create('config_test', array('id' => $this->randomMachineName(), 'label' => $this->randomMachineName()));
|
||||
$config_entity_2->save();
|
||||
|
||||
$content_entity_1 = EntityTest::create(array('name' => $this->randomMachineName()));
|
||||
$content_entity_1->save();
|
||||
$content_entity_2 = EntityTest::create(array('name' => $this->randomMachineName()));
|
||||
$content_entity_2->save();
|
||||
|
||||
return array(
|
||||
'config' => array(
|
||||
$config_entity_1,
|
||||
$config_entity_2,
|
||||
),
|
||||
'content' => array(
|
||||
$content_entity_1,
|
||||
$content_entity_2,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Tests\EntityReference;
|
||||
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Provides common functionality for the EntityReference test classes.
|
||||
*/
|
||||
trait EntityReferenceTestTrait {
|
||||
|
||||
/**
|
||||
* Creates a field of an entity reference field storage on the specified bundle.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The type of entity the field will be attached to.
|
||||
* @param string $bundle
|
||||
* The bundle name of the entity the field will be attached to.
|
||||
* @param string $field_name
|
||||
* The name of the field; if it already exists, a new instance of the existing
|
||||
* field will be created.
|
||||
* @param string $field_label
|
||||
* The label of the field.
|
||||
* @param string $target_entity_type
|
||||
* The type of the referenced entity.
|
||||
* @param string $selection_handler
|
||||
* The selection handler used by this field.
|
||||
* @param array $selection_handler_settings
|
||||
* An array of settings supported by the selection handler specified above.
|
||||
* (e.g. 'target_bundles', 'sort', 'auto_create', etc).
|
||||
* @param int $cardinality
|
||||
* The cardinality of the field.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\Plugin\EntityReferenceSelection\SelectionBase::buildConfigurationForm()
|
||||
*/
|
||||
protected function createEntityReferenceField($entity_type, $bundle, $field_name, $field_label, $target_entity_type, $selection_handler = 'default', $selection_handler_settings = array(), $cardinality = 1) {
|
||||
// Look for or add the specified field to the requested entity bundle.
|
||||
if (!FieldStorageConfig::loadByName($entity_type, $field_name)) {
|
||||
FieldStorageConfig::create(array(
|
||||
'field_name' => $field_name,
|
||||
'type' => 'entity_reference',
|
||||
'entity_type' => $entity_type,
|
||||
'cardinality' => $cardinality,
|
||||
'settings' => array(
|
||||
'target_type' => $target_entity_type,
|
||||
),
|
||||
))->save();
|
||||
}
|
||||
if (!FieldConfig::loadByName($entity_type, $bundle, $field_name)) {
|
||||
FieldConfig::create(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => $entity_type,
|
||||
'bundle' => $bundle,
|
||||
'label' => $field_label,
|
||||
'settings' => array(
|
||||
'handler' => $selection_handler,
|
||||
'handler_settings' => $selection_handler_settings,
|
||||
),
|
||||
))->save();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Tests\EntityReference;
|
||||
|
||||
use Drupal\Core\Entity\Entity\EntityFormDisplay;
|
||||
use Drupal\Core\Entity\Entity\EntityViewDisplay;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests possible XSS security issues in entity references.
|
||||
*
|
||||
* @group entity_reference
|
||||
*/
|
||||
class EntityReferenceXSSTest extends WebTestBase {
|
||||
|
||||
use EntityReferenceTestTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $modules = ['node'];
|
||||
|
||||
/**
|
||||
* Tests markup is escaped in the entity reference select and label formatter.
|
||||
*/
|
||||
public function testEntityReferenceXSS() {
|
||||
$this->drupalCreateContentType(['type' => 'article']);
|
||||
|
||||
// Create a node with markup in the title.
|
||||
$node_type_one = $this->drupalCreateContentType();
|
||||
$node = [
|
||||
'type' => $node_type_one->id(),
|
||||
'title' => '<em>I am kitten</em>',
|
||||
];
|
||||
$referenced_node = $this->drupalCreateNode($node);
|
||||
|
||||
$node_type_two = $this->drupalCreateContentType(['name' => '<em>bundle with markup</em>']);
|
||||
$this->drupalCreateNode([
|
||||
'type' => $node_type_two->id(),
|
||||
'title' => 'My bundle has markup',
|
||||
]);
|
||||
|
||||
$this->createEntityReferenceField('node', 'article', 'entity_reference_test', 'Entity Reference test', 'node', 'default', ['target_bundles' => [$node_type_one->id(), $node_type_two->id()]]);
|
||||
|
||||
EntityFormDisplay::load('node.article.default')
|
||||
->setComponent('entity_reference_test', ['type' => 'options_select'])
|
||||
->save();
|
||||
EntityViewDisplay::load('node.article.default')
|
||||
->setComponent('entity_reference_test', ['type' => 'entity_reference_label'])
|
||||
->save();
|
||||
|
||||
// Create a node and reference the node with markup in the title.
|
||||
$this->drupalLogin($this->rootUser);
|
||||
$this->drupalGet('node/add/article');
|
||||
$this->assertEscaped($referenced_node->getTitle());
|
||||
$this->assertEscaped($node_type_two->label());
|
||||
|
||||
$edit = [
|
||||
'title[0][value]' => $this->randomString(),
|
||||
'entity_reference_test' => $referenced_node->id()
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, 'Save and publish');
|
||||
$this->assertEscaped($referenced_node->getTitle());
|
||||
|
||||
// Test the options_buttons type.
|
||||
EntityFormDisplay::load('node.article.default')
|
||||
->setComponent('entity_reference_test', ['type' => 'options_buttons'])
|
||||
->save();
|
||||
$this->drupalGet('node/add/article');
|
||||
$this->assertEscaped($referenced_node->getTitle());
|
||||
// options_buttons does not support optgroups.
|
||||
$this->assertNoText('bundle with markup');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Tests\EntityReference\Views;
|
||||
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\views\Views;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Tests entity reference selection handler.
|
||||
*
|
||||
* @group entity_reference
|
||||
*/
|
||||
class SelectionTest extends WebTestBase {
|
||||
|
||||
public static $modules = ['node', 'views', 'entity_reference_test', 'entity_test'];
|
||||
|
||||
/**
|
||||
* Nodes for testing.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $nodes = array();
|
||||
|
||||
/**
|
||||
* The entity reference field to test.
|
||||
*
|
||||
* @var \Drupal\Core\Field\FieldDefinitionInterface
|
||||
*/
|
||||
protected $field;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create nodes.
|
||||
$type = $this->drupalCreateContentType()->id();
|
||||
$node1 = $this->drupalCreateNode(array('type' => $type));
|
||||
$node2 = $this->drupalCreateNode(array('type' => $type));
|
||||
$node3 = $this->drupalCreateNode();
|
||||
|
||||
foreach (array($node1, $node2, $node3) as $node) {
|
||||
$this->nodes[$node->getType()][$node->id()] = $node->label();
|
||||
}
|
||||
|
||||
// Create a field.
|
||||
$field_storage = FieldStorageConfig::create(array(
|
||||
'field_name' => 'test_field',
|
||||
'entity_type' => 'entity_test',
|
||||
'translatable' => FALSE,
|
||||
'settings' => array(
|
||||
'target_type' => 'node',
|
||||
),
|
||||
'type' => 'entity_reference',
|
||||
'cardinality' => '1',
|
||||
));
|
||||
$field_storage->save();
|
||||
$field = FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'test_bundle',
|
||||
'settings' => array(
|
||||
'handler' => 'views',
|
||||
'handler_settings' => array(
|
||||
'view' => array(
|
||||
'view_name' => 'test_entity_reference',
|
||||
'display_name' => 'entity_reference_1',
|
||||
'arguments' => array(),
|
||||
),
|
||||
),
|
||||
),
|
||||
]);
|
||||
$field->save();
|
||||
$this->field = $field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm the expected results are returned.
|
||||
*
|
||||
* @param array $result
|
||||
* Query results keyed by node type and nid.
|
||||
*/
|
||||
protected function assertResults(array $result) {
|
||||
$success = FALSE;
|
||||
foreach ($result as $node_type => $values) {
|
||||
foreach ($values as $nid => $label) {
|
||||
if (!$success = $this->nodes[$node_type][$nid] == trim(strip_tags($label))) {
|
||||
// There was some error, so break.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->assertTrue($success, 'Views selection handler returned expected values.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the selection handler.
|
||||
*/
|
||||
public function testSelectionHandler() {
|
||||
// Get values from selection handler.
|
||||
$handler = $this->container->get('plugin.manager.entity_reference_selection')->getSelectionHandler($this->field);
|
||||
$result = $handler->getReferenceableEntities();
|
||||
$this->assertResults($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the selection handler with a relationship.
|
||||
*/
|
||||
public function testSelectionHandlerRelationship() {
|
||||
// Add a relationship to the view.
|
||||
$view = Views::getView('test_entity_reference');
|
||||
$view->setDisplay();
|
||||
$view->displayHandlers->get('default')->setOption('relationships', array(
|
||||
'test_relationship' => array(
|
||||
'id' => 'uid',
|
||||
'table' => 'node_field_data',
|
||||
'field' => 'uid',
|
||||
),
|
||||
));
|
||||
|
||||
// Add a filter depending on the relationship to the test view.
|
||||
$view->displayHandlers->get('default')->setOption('filters', array(
|
||||
'uid' => array(
|
||||
'id' => 'uid',
|
||||
'table' => 'users_field_data',
|
||||
'field' => 'uid',
|
||||
'relationship' => 'test_relationship',
|
||||
)
|
||||
));
|
||||
|
||||
// Set view to distinct so only one row per node is returned.
|
||||
$query_options = $view->display_handler->getOption('query');
|
||||
$query_options['options']['distinct'] = TRUE;
|
||||
$view->display_handler->setOption('query', $query_options);
|
||||
$view->save();
|
||||
|
||||
// Get values from the selection handler.
|
||||
$handler = $this->container->get('plugin.manager.entity_reference_selection')->getSelectionHandler($this->field);
|
||||
$result = $handler->getReferenceableEntities();
|
||||
$this->assertResults($result);
|
||||
}
|
||||
|
||||
}
|
92
web/core/modules/field/src/Tests/FieldAccessTest.php
Normal file
92
web/core/modules/field/src/Tests/FieldAccessTest.php
Normal file
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Tests Field access.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class FieldAccessTest extends FieldTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'field_test');
|
||||
|
||||
/**
|
||||
* Node entity to use in this test.
|
||||
*
|
||||
* @var \Drupal\node\Entity\Node
|
||||
*/
|
||||
protected $node;
|
||||
|
||||
/**
|
||||
* Field value to test display on nodes.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $testViewFieldValue;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$web_user = $this->drupalCreateUser(array('view test_view_field content'));
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
// Create content type.
|
||||
$content_type_info = $this->drupalCreateContentType();
|
||||
$content_type = $content_type_info->id();
|
||||
|
||||
$field_storage = array(
|
||||
'field_name' => 'test_view_field',
|
||||
'entity_type' => 'node',
|
||||
'type' => 'text',
|
||||
);
|
||||
FieldStorageConfig::create($field_storage)->save();
|
||||
$field = array(
|
||||
'field_name' => $field_storage['field_name'],
|
||||
'entity_type' => 'node',
|
||||
'bundle' => $content_type,
|
||||
);
|
||||
FieldConfig::create($field)->save();
|
||||
|
||||
// Assign display properties for the 'default' and 'teaser' view modes.
|
||||
foreach (array('default', 'teaser') as $view_mode) {
|
||||
entity_get_display('node', $content_type, $view_mode)
|
||||
->setComponent($field_storage['field_name'])
|
||||
->save();
|
||||
}
|
||||
|
||||
// Create test node.
|
||||
$this->testViewFieldValue = 'This is some text';
|
||||
$settings = array();
|
||||
$settings['type'] = $content_type;
|
||||
$settings['title'] = 'Field view access test';
|
||||
$settings['test_view_field'] = array(array('value' => $this->testViewFieldValue));
|
||||
$this->node = $this->drupalCreateNode($settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that hook_entity_field_access() is called.
|
||||
*/
|
||||
function testFieldAccess() {
|
||||
|
||||
// Assert the text is visible.
|
||||
$this->drupalGet('node/' . $this->node->id());
|
||||
$this->assertText($this->testViewFieldValue);
|
||||
|
||||
// Assert the text is not visible for anonymous users.
|
||||
// The field_test module implements hook_entity_field_access() which will
|
||||
// specifically target the 'test_view_field' field.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('node/' . $this->node->id());
|
||||
$this->assertNoText($this->testViewFieldValue);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
/**
|
||||
* Helper class for \Drupal\field\Tests\FieldDefaultValueCallbackTest.
|
||||
*/
|
||||
class FieldDefaultValueCallbackProvider {
|
||||
|
||||
/**
|
||||
* Helper callback calculating a default value.
|
||||
*/
|
||||
public static function calculateDefaultValue() {
|
||||
return [['value' => 'Calculated default value']];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests the default value callback.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class FieldDefaultValueCallbackTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'field_test', 'field_ui');
|
||||
|
||||
/**
|
||||
* The field name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $fieldName;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->fieldName = 'field_test';
|
||||
|
||||
// Create Article node types.
|
||||
if ($this->profile != 'standard') {
|
||||
$this->drupalCreateContentType(array(
|
||||
'type' => 'article',
|
||||
'name' => 'Article',
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function testDefaultValueCallbackForm() {
|
||||
// Create a field and storage for checking.
|
||||
/** @var \Drupal\field\Entity\FieldStorageConfig $field_storage */
|
||||
FieldStorageConfig::create([
|
||||
'field_name' => $this->fieldName,
|
||||
'entity_type' => 'node',
|
||||
'type' => 'text',
|
||||
])->save();
|
||||
/** @var \Drupal\field\Entity\FieldConfig $field_config */
|
||||
$field_config = FieldConfig::create([
|
||||
'entity_type' => 'node',
|
||||
'field_name' => $this->fieldName,
|
||||
'bundle' => 'article',
|
||||
]);
|
||||
$field_config->save();
|
||||
|
||||
$this->drupalLogin($this->rootUser);
|
||||
|
||||
// Check that the default field form is visible when no callback is set.
|
||||
$this->drupalGet('/admin/structure/types/manage/article/fields/node.article.field_test');
|
||||
$this->assertFieldByName('default_value_input[field_test][0][value]', NULL, 'The default field form is visible.');
|
||||
|
||||
// Set a different field value, it should be on the field.
|
||||
$default_value = $this->randomString();
|
||||
$field_config->setDefaultValue([['value' => $default_value]])->save();
|
||||
$this->drupalGet('/admin/structure/types/manage/article/fields/node.article.field_test');
|
||||
$this->assertFieldByName('default_value_input[field_test][0][value]', $default_value, 'The default field form is visible.');
|
||||
|
||||
// Set a different field value to the field directly, instead of an array.
|
||||
$default_value = $this->randomString();
|
||||
$field_config->setDefaultValue($default_value)->save();
|
||||
$this->drupalGet('/admin/structure/types/manage/article/fields/node.article.field_test');
|
||||
$this->assertFieldByName('default_value_input[field_test][0][value]', $default_value, 'The default field form is visible.');
|
||||
|
||||
// Set a default value callback instead, and the default field form should
|
||||
// not be visible.
|
||||
$field_config->setDefaultValueCallback('\Drupal\field\Tests\FieldDefaultValueCallbackProvider::calculateDefaultValue')->save();
|
||||
$this->drupalGet('/admin/structure/types/manage/article/fields/node.article.field_test');
|
||||
$this->assertNoFieldByName('default_value_input[field_test][0][value]', 'Calculated default value', 'The default field form is not visible when a callback is defined.');
|
||||
}
|
||||
|
||||
}
|
59
web/core/modules/field/src/Tests/FieldHelpTest.php
Normal file
59
web/core/modules/field/src/Tests/FieldHelpTest.php
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests help display for the Field module.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class FieldHelpTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array.
|
||||
*/
|
||||
public static $modules = array('field', 'help');
|
||||
|
||||
// Tests field help implementation without optional core modules enabled.
|
||||
protected $profile = 'minimal';
|
||||
|
||||
/**
|
||||
* The admin user that will be created.
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create the admin user.
|
||||
$this->adminUser = $this->drupalCreateUser(array('access administration pages', 'view the administration theme'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the Field module's help page.
|
||||
*/
|
||||
public function testFieldHelp() {
|
||||
// Log in the admin user.
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Visit the Help page and make sure no warnings or notices are thrown.
|
||||
$this->drupalGet('admin/help/field');
|
||||
|
||||
// Enable the Options, Email and Field API Test modules.
|
||||
\Drupal::service('module_installer')->install(array('options', 'field_test'));
|
||||
$this->resetAll();
|
||||
\Drupal::service('plugin.manager.field.widget')->clearCachedDefinitions();
|
||||
\Drupal::service('plugin.manager.field.field_type')->clearCachedDefinitions();
|
||||
|
||||
$this->drupalGet('admin/help/field');
|
||||
$this->assertLink('Options', 0, 'Options module is listed on the Field help page.');
|
||||
$this->assertText('Field API Test', 'Modules with field types that do not implement hook_help are listed.');
|
||||
$this->assertNoLink('Field API Test', 'Modules with field types that do not implement hook_help are not linked.');
|
||||
$this->assertNoLink('Link', 'Modules that have not been installed, are not listed.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Delete field storages and fields during config synchronization and uninstall
|
||||
* module that provides the field type through the UI.
|
||||
*
|
||||
* @group field
|
||||
* @see \Drupal\field\ConfigImporterFieldPurger
|
||||
* @see field_config_import_steps_alter()
|
||||
* @see field_form_config_admin_import_form_alter()
|
||||
*/
|
||||
class FieldImportDeleteUninstallUiTest extends FieldTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('entity_test', 'telephone', 'config', 'filter', 'datetime');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser(array('synchronize configuration')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests deleting field storages and fields as part of config import.
|
||||
*/
|
||||
public function testImportDeleteUninstall() {
|
||||
// Create a telephone field.
|
||||
$field_storage = FieldStorageConfig::create(array(
|
||||
'field_name' => 'field_tel',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'telephone',
|
||||
));
|
||||
$field_storage->save();
|
||||
FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'entity_test',
|
||||
])->save();
|
||||
|
||||
// Create a text field.
|
||||
$date_field_storage = FieldStorageConfig::create(array(
|
||||
'field_name' => 'field_date',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'datetime',
|
||||
));
|
||||
$date_field_storage->save();
|
||||
FieldConfig::create([
|
||||
'field_storage' => $date_field_storage,
|
||||
'bundle' => 'entity_test',
|
||||
])->save();
|
||||
|
||||
// Create an entity which has values for the telephone and text field.
|
||||
$entity = EntityTest::create();
|
||||
$value = '+0123456789';
|
||||
$entity->field_tel = $value;
|
||||
$entity->field_date = time();
|
||||
$entity->name->value = $this->randomMachineName();
|
||||
$entity->save();
|
||||
|
||||
// Delete the text field before exporting configuration so that we can test
|
||||
// that deleted fields that are provided by modules that will be uninstalled
|
||||
// are also purged and that the UI message includes such fields.
|
||||
$date_field_storage->delete();
|
||||
|
||||
// Verify entity has been created properly.
|
||||
$id = $entity->id();
|
||||
$entity = EntityTest::load($id);
|
||||
$this->assertEqual($entity->field_tel->value, $value);
|
||||
$this->assertEqual($entity->field_tel[0]->value, $value);
|
||||
|
||||
$active = $this->container->get('config.storage');
|
||||
$sync = $this->container->get('config.storage.sync');
|
||||
$this->copyConfig($active, $sync);
|
||||
|
||||
// Stage uninstall of the Telephone module.
|
||||
$core_extension = $this->config('core.extension')->get();
|
||||
unset($core_extension['module']['telephone']);
|
||||
$sync->write('core.extension', $core_extension);
|
||||
|
||||
// Stage the field deletion
|
||||
$sync->delete('field.storage.entity_test.field_tel');
|
||||
$sync->delete('field.field.entity_test.entity_test.field_tel');
|
||||
$this->drupalGet('admin/config/development/configuration');
|
||||
// Test that the message for one field being purged during a configuration
|
||||
// synchronization is correct.
|
||||
$this->assertText('This synchronization will delete data from the field entity_test.field_tel.');
|
||||
|
||||
// Stage an uninstall of the datetime module to test the message for
|
||||
// multiple fields.
|
||||
unset($core_extension['module']['datetime']);
|
||||
$sync->write('core.extension', $core_extension);
|
||||
|
||||
$this->drupalGet('admin/config/development/configuration');
|
||||
$this->assertText('This synchronization will delete data from the fields: entity_test.field_tel, entity_test.field_date.');
|
||||
|
||||
// This will purge all the data, delete the field and uninstall the
|
||||
// Telephone and Text modules.
|
||||
$this->drupalPostForm(NULL, array(), t('Import all'));
|
||||
$this->assertNoText('Field data will be deleted by this synchronization.');
|
||||
$this->rebuildContainer();
|
||||
$this->assertFalse(\Drupal::moduleHandler()->moduleExists('telephone'));
|
||||
$this->assertFalse(\Drupal::entityManager()->loadEntityByUuid('field_storage_config', $field_storage->uuid()), 'The telephone field has been deleted by the configuration synchronization');
|
||||
$deleted_storages = \Drupal::state()->get('field.storage.deleted') ?: array();
|
||||
$this->assertFalse(isset($deleted_storages[$field_storage->uuid()]), 'Telephone field has been completed removed from the system.');
|
||||
$this->assertFalse(isset($deleted_storages[$field_storage->uuid()]), 'Text field has been completed removed from the system.');
|
||||
}
|
||||
|
||||
}
|
65
web/core/modules/field/src/Tests/FieldTestBase.php
Normal file
65
web/core/modules/field/src/Tests/FieldTestBase.php
Normal file
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Parent class for Field API tests.
|
||||
*/
|
||||
abstract class FieldTestBase extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Generate random values for a field_test field.
|
||||
*
|
||||
* @param $cardinality
|
||||
* Number of values to generate.
|
||||
* @return
|
||||
* An array of random values, in the format expected for field values.
|
||||
*/
|
||||
function _generateTestFieldValues($cardinality) {
|
||||
$values = array();
|
||||
for ($i = 0; $i < $cardinality; $i++) {
|
||||
// field_test fields treat 0 as 'empty value'.
|
||||
$values[$i]['value'] = mt_rand(1, 127);
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that a field has the expected values in an entity.
|
||||
*
|
||||
* This function only checks a single column in the field values.
|
||||
*
|
||||
* @param EntityInterface $entity
|
||||
* The entity to test.
|
||||
* @param $field_name
|
||||
* The name of the field to test
|
||||
* @param $expected_values
|
||||
* The array of expected values.
|
||||
* @param $langcode
|
||||
* (Optional) The language code for the values. Defaults to
|
||||
* \Drupal\Core\Language\LanguageInterface::LANGCODE_DEFAULT.
|
||||
* @param $column
|
||||
* (Optional) The name of the column to check. Defaults to 'value'.
|
||||
*/
|
||||
function assertFieldValues(EntityInterface $entity, $field_name, $expected_values, $langcode = LanguageInterface::LANGCODE_DEFAULT, $column = 'value') {
|
||||
// Re-load the entity to make sure we have the latest changes.
|
||||
$storage = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity->getEntityTypeId());
|
||||
$storage->resetCache([$entity->id()]);
|
||||
$e = $storage->load($entity->id());
|
||||
|
||||
$field = $values = $e->getTranslation($langcode)->$field_name;
|
||||
// Filter out empty values so that they don't mess with the assertions.
|
||||
$field->filterEmptyItems();
|
||||
$values = $field->getValue();
|
||||
$this->assertEqual(count($values), count($expected_values), 'Expected number of values were saved.');
|
||||
foreach ($expected_values as $key => $value) {
|
||||
$this->assertEqual($values[$key][$column], $value, format_string('Value @value was saved correctly.', array('@value' => $value)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
691
web/core/modules/field/src/Tests/FormTest.php
Normal file
691
web/core/modules/field/src/Tests/FormTest.php
Normal file
|
@ -0,0 +1,691 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Entity\Entity\EntityFormDisplay;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\Core\Form\FormState;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\entity_test\Entity\EntityTestBaseFieldDisplay;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Tests field form handling.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class FormTest extends FieldTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* Locale is installed so that TranslatableMarkup actually does something.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'field_test', 'options', 'entity_test', 'locale');
|
||||
|
||||
/**
|
||||
* An array of values defining a field single.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fieldStorageSingle;
|
||||
|
||||
/**
|
||||
* An array of values defining a field multiple.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fieldStorageMultiple;
|
||||
|
||||
/**
|
||||
* An array of values defining a field with unlimited cardinality.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fieldStorageUnlimited;
|
||||
|
||||
/**
|
||||
* An array of values defining a field.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $field;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$web_user = $this->drupalCreateUser(array('view test entity', 'administer entity_test content'));
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
$this->fieldStorageSingle = array(
|
||||
'field_name' => 'field_single',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field',
|
||||
);
|
||||
$this->fieldStorageMultiple = array(
|
||||
'field_name' => 'field_multiple',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field',
|
||||
'cardinality' => 4,
|
||||
);
|
||||
$this->fieldStorageUnlimited = array(
|
||||
'field_name' => 'field_unlimited',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field',
|
||||
'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
|
||||
);
|
||||
|
||||
$this->field = array(
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
'label' => $this->randomMachineName() . '_label',
|
||||
'description' => '[site:name]_description',
|
||||
'weight' => mt_rand(0, 127),
|
||||
'settings' => array(
|
||||
'test_field_setting' => $this->randomMachineName(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function testFieldFormSingle() {
|
||||
$field_storage = $this->fieldStorageSingle;
|
||||
$field_name = $field_storage['field_name'];
|
||||
$this->field['field_name'] = $field_name;
|
||||
FieldStorageConfig::create($field_storage)->save();
|
||||
FieldConfig::create($this->field)->save();
|
||||
entity_get_form_display($this->field['entity_type'], $this->field['bundle'], 'default')
|
||||
->setComponent($field_name)
|
||||
->save();
|
||||
|
||||
// Display creation form.
|
||||
$this->drupalGet('entity_test/add');
|
||||
|
||||
// Create token value expected for description.
|
||||
$token_description = Html::escape($this->config('system.site')->get('name')) . '_description';
|
||||
$this->assertText($token_description, 'Token replacement for description is displayed');
|
||||
$this->assertFieldByName("{$field_name}[0][value]", '', 'Widget is displayed');
|
||||
$this->assertNoField("{$field_name}[1][value]", 'No extraneous widget is displayed');
|
||||
|
||||
// Check that hook_field_widget_form_alter() does not believe this is the
|
||||
// default value form.
|
||||
$this->assertNoText('From hook_field_widget_form_alter(): Default form is true.', 'Not default value form in hook_field_widget_form_alter().');
|
||||
|
||||
// Submit with invalid value (field-level validation).
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => -1
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertRaw(t('%name does not accept the value -1.', array('%name' => $this->field['label'])), 'Field validation fails with invalid input.');
|
||||
// TODO : check that the correct field is flagged for error.
|
||||
|
||||
// Create an entity
|
||||
$value = mt_rand(1, 127);
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $value,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
|
||||
$id = $match[1];
|
||||
$this->assertText(t('entity_test @id has been created.', array('@id' => $id)), 'Entity was created');
|
||||
$entity = EntityTest::load($id);
|
||||
$this->assertEqual($entity->{$field_name}->value, $value, 'Field value was saved');
|
||||
|
||||
// Display edit form.
|
||||
$this->drupalGet('entity_test/manage/' . $id . '/edit');
|
||||
$this->assertFieldByName("{$field_name}[0][value]", $value, 'Widget is displayed with the correct default value');
|
||||
$this->assertNoField("{$field_name}[1][value]", 'No extraneous widget is displayed');
|
||||
|
||||
// Update the entity.
|
||||
$value = mt_rand(1, 127);
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $value,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertText(t('entity_test @id has been updated.', array('@id' => $id)), 'Entity was updated');
|
||||
$this->container->get('entity.manager')->getStorage('entity_test')->resetCache(array($id));
|
||||
$entity = EntityTest::load($id);
|
||||
$this->assertEqual($entity->{$field_name}->value, $value, 'Field value was updated');
|
||||
|
||||
// Empty the field.
|
||||
$value = '';
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $value
|
||||
);
|
||||
$this->drupalPostForm('entity_test/manage/' . $id . '/edit', $edit, t('Save'));
|
||||
$this->assertText(t('entity_test @id has been updated.', array('@id' => $id)), 'Entity was updated');
|
||||
$this->container->get('entity.manager')->getStorage('entity_test')->resetCache(array($id));
|
||||
$entity = EntityTest::load($id);
|
||||
$this->assertTrue($entity->{$field_name}->isEmpty(), 'Field was emptied');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests field widget default values on entity forms.
|
||||
*/
|
||||
function testFieldFormDefaultValue() {
|
||||
$field_storage = $this->fieldStorageSingle;
|
||||
$field_name = $field_storage['field_name'];
|
||||
$this->field['field_name'] = $field_name;
|
||||
$default = rand(1, 127);
|
||||
$this->field['default_value'] = array(array('value' => $default));
|
||||
FieldStorageConfig::create($field_storage)->save();
|
||||
FieldConfig::create($this->field)->save();
|
||||
entity_get_form_display($this->field['entity_type'], $this->field['bundle'], 'default')
|
||||
->setComponent($field_name)
|
||||
->save();
|
||||
|
||||
// Display creation form.
|
||||
$this->drupalGet('entity_test/add');
|
||||
// Test that the default value is displayed correctly.
|
||||
$this->assertFieldByXpath("//input[@name='{$field_name}[0][value]' and @value='$default']");
|
||||
|
||||
// Try to submit an empty value.
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => '',
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
|
||||
$id = $match[1];
|
||||
$this->assertText(t('entity_test @id has been created.', array('@id' => $id)), 'Entity was created.');
|
||||
$entity = EntityTest::load($id);
|
||||
$this->assertTrue($entity->{$field_name}->isEmpty(), 'Field is now empty.');
|
||||
}
|
||||
|
||||
function testFieldFormSingleRequired() {
|
||||
$field_storage = $this->fieldStorageSingle;
|
||||
$field_name = $field_storage['field_name'];
|
||||
$this->field['field_name'] = $field_name;
|
||||
$this->field['required'] = TRUE;
|
||||
FieldStorageConfig::create($field_storage)->save();
|
||||
FieldConfig::create($this->field)->save();
|
||||
entity_get_form_display($this->field['entity_type'], $this->field['bundle'], 'default')
|
||||
->setComponent($field_name)
|
||||
->save();
|
||||
|
||||
// Submit with missing required value.
|
||||
$edit = array();
|
||||
$this->drupalPostForm('entity_test/add', $edit, t('Save'));
|
||||
$this->assertRaw(t('@name field is required.', array('@name' => $this->field['label'])), 'Required field with no value fails validation');
|
||||
|
||||
// Create an entity
|
||||
$value = mt_rand(1, 127);
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $value,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
|
||||
$id = $match[1];
|
||||
$this->assertText(t('entity_test @id has been created.', array('@id' => $id)), 'Entity was created');
|
||||
$entity = EntityTest::load($id);
|
||||
$this->assertEqual($entity->{$field_name}->value, $value, 'Field value was saved');
|
||||
|
||||
// Edit with missing required value.
|
||||
$value = '';
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $value,
|
||||
);
|
||||
$this->drupalPostForm('entity_test/manage/' . $id . '/edit', $edit, t('Save'));
|
||||
$this->assertRaw(t('@name field is required.', array('@name' => $this->field['label'])), 'Required field with no value fails validation');
|
||||
}
|
||||
|
||||
function testFieldFormUnlimited() {
|
||||
$field_storage = $this->fieldStorageUnlimited;
|
||||
$field_name = $field_storage['field_name'];
|
||||
$this->field['field_name'] = $field_name;
|
||||
FieldStorageConfig::create($field_storage)->save();
|
||||
FieldConfig::create($this->field)->save();
|
||||
entity_get_form_display($this->field['entity_type'], $this->field['bundle'], 'default')
|
||||
->setComponent($field_name)
|
||||
->save();
|
||||
|
||||
// Display creation form -> 1 widget.
|
||||
$this->drupalGet('entity_test/add');
|
||||
$this->assertFieldByName("{$field_name}[0][value]", '', 'Widget 1 is displayed');
|
||||
$this->assertNoField("{$field_name}[1][value]", 'No extraneous widget is displayed');
|
||||
|
||||
// Check if aria-describedby attribute is placed on multiple value widgets.
|
||||
$elements = $this->xpath('//table[@id="field-unlimited-values" and @aria-describedby="edit-field-unlimited--description"]');
|
||||
$this->assertTrue(isset($elements[0]), t('aria-describedby attribute is properly placed on multiple value widgets.'));
|
||||
|
||||
// Press 'add more' button -> 2 widgets.
|
||||
$this->drupalPostForm(NULL, array(), t('Add another item'));
|
||||
$this->assertFieldByName("{$field_name}[0][value]", '', 'Widget 1 is displayed');
|
||||
$this->assertFieldByName("{$field_name}[1][value]", '', 'New widget is displayed');
|
||||
$this->assertNoField("{$field_name}[2][value]", 'No extraneous widget is displayed');
|
||||
// TODO : check that non-field inputs are preserved ('title'), etc.
|
||||
|
||||
// Yet another time so that we can play with more values -> 3 widgets.
|
||||
$this->drupalPostForm(NULL, array(), t('Add another item'));
|
||||
|
||||
// Prepare values and weights.
|
||||
$count = 3;
|
||||
$delta_range = $count - 1;
|
||||
$values = $weights = $pattern = $expected_values = array();
|
||||
$edit = array();
|
||||
for ($delta = 0; $delta <= $delta_range; $delta++) {
|
||||
// Assign unique random values and weights.
|
||||
do {
|
||||
$value = mt_rand(1, 127);
|
||||
} while (in_array($value, $values));
|
||||
do {
|
||||
$weight = mt_rand(-$delta_range, $delta_range);
|
||||
} while (in_array($weight, $weights));
|
||||
$edit["{$field_name}[$delta][value]"] = $value;
|
||||
$edit["{$field_name}[$delta][_weight]"] = $weight;
|
||||
// We'll need three slightly different formats to check the values.
|
||||
$values[$delta] = $value;
|
||||
$weights[$delta] = $weight;
|
||||
$field_values[$weight]['value'] = (string) $value;
|
||||
$pattern[$weight] = "<input [^>]*value=\"$value\" [^>]*";
|
||||
}
|
||||
|
||||
// Press 'add more' button -> 4 widgets
|
||||
$this->drupalPostForm(NULL, $edit, t('Add another item'));
|
||||
for ($delta = 0; $delta <= $delta_range; $delta++) {
|
||||
$this->assertFieldByName("{$field_name}[$delta][value]", $values[$delta], "Widget $delta is displayed and has the right value");
|
||||
$this->assertFieldByName("{$field_name}[$delta][_weight]", $weights[$delta], "Widget $delta has the right weight");
|
||||
}
|
||||
ksort($pattern);
|
||||
$pattern = implode('.*', array_values($pattern));
|
||||
$this->assertPattern("|$pattern|s", 'Widgets are displayed in the correct order');
|
||||
$this->assertFieldByName("{$field_name}[$delta][value]", '', "New widget is displayed");
|
||||
$this->assertFieldByName("{$field_name}[$delta][_weight]", $delta, "New widget has the right weight");
|
||||
$this->assertNoField("{$field_name}[" . ($delta + 1) . '][value]', 'No extraneous widget is displayed');
|
||||
|
||||
// Submit the form and create the entity.
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
|
||||
$id = $match[1];
|
||||
$this->assertText(t('entity_test @id has been created.', array('@id' => $id)), 'Entity was created');
|
||||
$entity = EntityTest::load($id);
|
||||
ksort($field_values);
|
||||
$field_values = array_values($field_values);
|
||||
$this->assertIdentical($entity->{$field_name}->getValue(), $field_values, 'Field values were saved in the correct order');
|
||||
|
||||
// Display edit form: check that the expected number of widgets is
|
||||
// displayed, with correct values change values, reorder, leave an empty
|
||||
// value in the middle.
|
||||
// Submit: check that the entity is updated with correct values
|
||||
// Re-submit: check that the field can be emptied.
|
||||
|
||||
// Test with several multiple fields in a form
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the position of the required label.
|
||||
*/
|
||||
public function testFieldFormUnlimitedRequired() {
|
||||
$field_name = $this->fieldStorageUnlimited['field_name'];
|
||||
$this->field['field_name'] = $field_name;
|
||||
$this->field['required'] = TRUE;
|
||||
FieldStorageConfig::create($this->fieldStorageUnlimited)->save();
|
||||
FieldConfig::create($this->field)->save();
|
||||
entity_get_form_display($this->field['entity_type'], $this->field['bundle'], 'default')
|
||||
->setComponent($field_name)
|
||||
->save();
|
||||
|
||||
// Display creation form -> 1 widget.
|
||||
$this->drupalGet('entity_test/add');
|
||||
// Check that the Required symbol is present for the multifield label.
|
||||
$element = $this->xpath('//h4[contains(@class, "label") and contains(@class, "js-form-required") and contains(text(), :value)]', array(':value' => $this->field['label']));
|
||||
$this->assertTrue(isset($element[0]), 'Required symbol added field label.');
|
||||
// Check that the label of the field input is visually hidden and contains
|
||||
// the field title and an indication of the delta for a11y.
|
||||
$element = $this->xpath('//label[@for=:for and contains(@class, "visually-hidden") and contains(text(), :value)]', array(':for' => 'edit-field-unlimited-0-value', ':value' => $this->field['label'] . ' (value 1)'));
|
||||
$this->assertTrue(isset($element[0]), 'Required symbol not added for field input.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests widget handling of multiple required radios.
|
||||
*/
|
||||
function testFieldFormMultivalueWithRequiredRadio() {
|
||||
// Create a multivalue test field.
|
||||
$field_storage = $this->fieldStorageUnlimited;
|
||||
$field_name = $field_storage['field_name'];
|
||||
$this->field['field_name'] = $field_name;
|
||||
FieldStorageConfig::create($field_storage)->save();
|
||||
FieldConfig::create($this->field)->save();
|
||||
entity_get_form_display($this->field['entity_type'], $this->field['bundle'], 'default')
|
||||
->setComponent($field_name)
|
||||
->save();
|
||||
|
||||
// Add a required radio field.
|
||||
FieldStorageConfig::create(array(
|
||||
'field_name' => 'required_radio_test',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'list_string',
|
||||
'settings' => array(
|
||||
'allowed_values' => array('yes' => 'yes', 'no' => 'no'),
|
||||
),
|
||||
))->save();
|
||||
$field = array(
|
||||
'field_name' => 'required_radio_test',
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
'required' => TRUE,
|
||||
);
|
||||
FieldConfig::create($field)->save();
|
||||
entity_get_form_display($field['entity_type'], $field['bundle'], 'default')
|
||||
->setComponent($field['field_name'], array(
|
||||
'type' => 'options_buttons',
|
||||
))
|
||||
->save();
|
||||
|
||||
// Display creation form.
|
||||
$this->drupalGet('entity_test/add');
|
||||
|
||||
// Press the 'Add more' button.
|
||||
$this->drupalPostForm(NULL, array(), t('Add another item'));
|
||||
|
||||
// Verify that no error is thrown by the radio element.
|
||||
$this->assertNoFieldByXpath('//div[contains(@class, "error")]', FALSE, 'No error message is displayed.');
|
||||
|
||||
// Verify that the widget is added.
|
||||
$this->assertFieldByName("{$field_name}[0][value]", '', 'Widget 1 is displayed');
|
||||
$this->assertFieldByName("{$field_name}[1][value]", '', 'New widget is displayed');
|
||||
$this->assertNoField("{$field_name}[2][value]", 'No extraneous widget is displayed');
|
||||
}
|
||||
|
||||
function testFieldFormJSAddMore() {
|
||||
$field_storage = $this->fieldStorageUnlimited;
|
||||
$field_name = $field_storage['field_name'];
|
||||
$this->field['field_name'] = $field_name;
|
||||
FieldStorageConfig::create($field_storage)->save();
|
||||
FieldConfig::create($this->field)->save();
|
||||
entity_get_form_display($this->field['entity_type'], $this->field['bundle'], 'default')
|
||||
->setComponent($field_name)
|
||||
->save();
|
||||
|
||||
// Display creation form -> 1 widget.
|
||||
$this->drupalGet('entity_test/add');
|
||||
|
||||
// Press 'add more' button a couple times -> 3 widgets.
|
||||
// drupalPostAjaxForm() will not work iteratively, so we add those through
|
||||
// non-JS submission.
|
||||
$this->drupalPostForm(NULL, array(), t('Add another item'));
|
||||
$this->drupalPostForm(NULL, array(), t('Add another item'));
|
||||
|
||||
// Prepare values and weights.
|
||||
$count = 3;
|
||||
$delta_range = $count - 1;
|
||||
$values = $weights = $pattern = $expected_values = $edit = array();
|
||||
for ($delta = 0; $delta <= $delta_range; $delta++) {
|
||||
// Assign unique random values and weights.
|
||||
do {
|
||||
$value = mt_rand(1, 127);
|
||||
} while (in_array($value, $values));
|
||||
do {
|
||||
$weight = mt_rand(-$delta_range, $delta_range);
|
||||
} while (in_array($weight, $weights));
|
||||
$edit["{$field_name}[$delta][value]"] = $value;
|
||||
$edit["{$field_name}[$delta][_weight]"] = $weight;
|
||||
// We'll need three slightly different formats to check the values.
|
||||
$values[$delta] = $value;
|
||||
$weights[$delta] = $weight;
|
||||
$field_values[$weight]['value'] = (string) $value;
|
||||
$pattern[$weight] = "<input [^>]*value=\"$value\" [^>]*";
|
||||
}
|
||||
// Press 'add more' button through Ajax, and place the expected HTML result
|
||||
// as the tested content.
|
||||
$commands = $this->drupalPostAjaxForm(NULL, $edit, $field_name . '_add_more');
|
||||
$this->setRawContent($commands[2]['data']);
|
||||
|
||||
for ($delta = 0; $delta <= $delta_range; $delta++) {
|
||||
$this->assertFieldByName("{$field_name}[$delta][value]", $values[$delta], "Widget $delta is displayed and has the right value");
|
||||
$this->assertFieldByName("{$field_name}[$delta][_weight]", $weights[$delta], "Widget $delta has the right weight");
|
||||
}
|
||||
ksort($pattern);
|
||||
$pattern = implode('.*', array_values($pattern));
|
||||
$this->assertPattern("|$pattern|s", 'Widgets are displayed in the correct order');
|
||||
$this->assertFieldByName("{$field_name}[$delta][value]", '', "New widget is displayed");
|
||||
$this->assertFieldByName("{$field_name}[$delta][_weight]", $delta, "New widget has the right weight");
|
||||
$this->assertNoField("{$field_name}[" . ($delta + 1) . '][value]', 'No extraneous widget is displayed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests widgets handling multiple values.
|
||||
*/
|
||||
function testFieldFormMultipleWidget() {
|
||||
// Create a field with fixed cardinality, configure the form to use a
|
||||
// "multiple" widget.
|
||||
$field_storage = $this->fieldStorageMultiple;
|
||||
$field_name = $field_storage['field_name'];
|
||||
$this->field['field_name'] = $field_name;
|
||||
FieldStorageConfig::create($field_storage)->save();
|
||||
FieldConfig::create($this->field)->save();
|
||||
entity_get_form_display($this->field['entity_type'], $this->field['bundle'], 'default')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'test_field_widget_multiple',
|
||||
))
|
||||
->save();
|
||||
|
||||
// Display creation form.
|
||||
$this->drupalGet('entity_test/add');
|
||||
$this->assertFieldByName($field_name, '', 'Widget is displayed.');
|
||||
|
||||
// Create entity with three values.
|
||||
$edit = array(
|
||||
$field_name => '1, 2, 3',
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
|
||||
$id = $match[1];
|
||||
|
||||
// Check that the values were saved.
|
||||
$entity_init = EntityTest::load($id);
|
||||
$this->assertFieldValues($entity_init, $field_name, array(1, 2, 3));
|
||||
|
||||
// Display the form, check that the values are correctly filled in.
|
||||
$this->drupalGet('entity_test/manage/' . $id . '/edit');
|
||||
$this->assertFieldByName($field_name, '1, 2, 3', 'Widget is displayed.');
|
||||
|
||||
// Submit the form with more values than the field accepts.
|
||||
$edit = array($field_name => '1, 2, 3, 4, 5');
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertRaw('this field cannot hold more than 4 values', 'Form validation failed.');
|
||||
// Check that the field values were not submitted.
|
||||
$this->assertFieldValues($entity_init, $field_name, array(1, 2, 3));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests fields with no 'edit' access.
|
||||
*/
|
||||
function testFieldFormAccess() {
|
||||
$entity_type = 'entity_test_rev';
|
||||
// Create a "regular" field.
|
||||
$field_storage = $this->fieldStorageSingle;
|
||||
$field_storage['entity_type'] = $entity_type;
|
||||
$field_name = $field_storage['field_name'];
|
||||
$field = $this->field;
|
||||
$field['field_name'] = $field_name;
|
||||
$field['entity_type'] = $entity_type;
|
||||
$field['bundle'] = $entity_type;
|
||||
FieldStorageConfig::create($field_storage)->save();
|
||||
FieldConfig::create($field)->save();
|
||||
entity_get_form_display($entity_type, $entity_type, 'default')
|
||||
->setComponent($field_name)
|
||||
->save();
|
||||
|
||||
// Create a field with no edit access. See
|
||||
// field_test_entity_field_access().
|
||||
$field_storage_no_access = array(
|
||||
'field_name' => 'field_no_edit_access',
|
||||
'entity_type' => $entity_type,
|
||||
'type' => 'test_field',
|
||||
);
|
||||
$field_name_no_access = $field_storage_no_access['field_name'];
|
||||
$field_no_access = array(
|
||||
'field_name' => $field_name_no_access,
|
||||
'entity_type' => $entity_type,
|
||||
'bundle' => $entity_type,
|
||||
'default_value' => array(0 => array('value' => 99)),
|
||||
);
|
||||
FieldStorageConfig::create($field_storage_no_access)->save();
|
||||
FieldConfig::create($field_no_access)->save();
|
||||
entity_get_form_display($field_no_access['entity_type'], $field_no_access['bundle'], 'default')
|
||||
->setComponent($field_name_no_access)
|
||||
->save();
|
||||
|
||||
// Test that the form structure includes full information for each delta
|
||||
// apart from #access.
|
||||
$entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type)
|
||||
->create(array('id' => 0, 'revision_id' => 0));
|
||||
|
||||
$display = entity_get_form_display($entity_type, $entity_type, 'default');
|
||||
$form = array();
|
||||
$form_state = new FormState();
|
||||
$display->buildForm($entity, $form, $form_state);
|
||||
|
||||
$this->assertFalse($form[$field_name_no_access]['#access'], 'Field #access is FALSE for the field without edit access.');
|
||||
|
||||
// Display creation form.
|
||||
$this->drupalGet($entity_type . '/add');
|
||||
$this->assertNoFieldByName("{$field_name_no_access}[0][value]", '', 'Widget is not displayed if field access is denied.');
|
||||
|
||||
// Create entity.
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => 1,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
preg_match("|$entity_type/manage/(\d+)|", $this->url, $match);
|
||||
$id = $match[1];
|
||||
|
||||
// Check that the default value was saved.
|
||||
$storage = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type);
|
||||
$entity = $storage->load($id);
|
||||
$this->assertEqual($entity->$field_name_no_access->value, 99, 'Default value was saved for the field with no edit access.');
|
||||
$this->assertEqual($entity->$field_name->value, 1, 'Entered value vas saved for the field with edit access.');
|
||||
|
||||
// Create a new revision.
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => 2,
|
||||
'revision' => TRUE,
|
||||
);
|
||||
$this->drupalPostForm($entity_type . '/manage/' . $id . '/edit', $edit, t('Save'));
|
||||
|
||||
// Check that the new revision has the expected values.
|
||||
$storage->resetCache([$id]);
|
||||
$entity = $storage->load($id);
|
||||
$this->assertEqual($entity->$field_name_no_access->value, 99, 'New revision has the expected value for the field with no edit access.');
|
||||
$this->assertEqual($entity->$field_name->value, 2, 'New revision has the expected value for the field with edit access.');
|
||||
|
||||
// Check that the revision is also saved in the revisions table.
|
||||
// $entity = entity_revision_load($entity_type, $entity->getRevisionId());
|
||||
$this->assertEqual($entity->$field_name_no_access->value, 99, 'New revision has the expected value for the field with no edit access.');
|
||||
$this->assertEqual($entity->$field_name->value, 2, 'New revision has the expected value for the field with edit access.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests hiding a field in a form.
|
||||
*/
|
||||
function testHiddenField() {
|
||||
$entity_type = 'entity_test_rev';
|
||||
$field_storage = $this->fieldStorageSingle;
|
||||
$field_storage['entity_type'] = $entity_type;
|
||||
$field_name = $field_storage['field_name'];
|
||||
$this->field['field_name'] = $field_name;
|
||||
$this->field['default_value'] = array(0 => array('value' => 99));
|
||||
$this->field['entity_type'] = $entity_type;
|
||||
$this->field['bundle'] = $entity_type;
|
||||
FieldStorageConfig::create($field_storage)->save();
|
||||
$this->field = FieldConfig::create($this->field);
|
||||
$this->field->save();
|
||||
// We explicitly do not assign a widget in a form display, so the field
|
||||
// stays hidden in forms.
|
||||
|
||||
// Display the entity creation form.
|
||||
$this->drupalGet($entity_type . '/add');
|
||||
|
||||
// Create an entity and test that the default value is assigned correctly to
|
||||
// the field that uses the hidden widget.
|
||||
$this->assertNoField("{$field_name}[0][value]", 'The field does not appear in the form');
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
preg_match('|' . $entity_type . '/manage/(\d+)|', $this->url, $match);
|
||||
$id = $match[1];
|
||||
$this->assertText(t('entity_test_rev @id has been created.', array('@id' => $id)), 'Entity was created');
|
||||
$storage = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type);
|
||||
|
||||
$entity = $storage->load($id);
|
||||
$this->assertEqual($entity->{$field_name}->value, 99, 'Default value was saved');
|
||||
|
||||
// Update the field to remove the default value, and switch to the default
|
||||
// widget.
|
||||
$this->field->setDefaultValue(array());
|
||||
$this->field->save();
|
||||
entity_get_form_display($entity_type, $this->field->getTargetBundle(), 'default')
|
||||
->setComponent($this->field->getName(), array(
|
||||
'type' => 'test_field_widget',
|
||||
))
|
||||
->save();
|
||||
|
||||
// Display edit form.
|
||||
$this->drupalGet($entity_type . '/manage/' . $id . '/edit');
|
||||
$this->assertFieldByName("{$field_name}[0][value]", 99, 'Widget is displayed with the correct default value');
|
||||
|
||||
// Update the entity.
|
||||
$value = mt_rand(1, 127);
|
||||
$edit = array("{$field_name}[0][value]" => $value);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertText(t('entity_test_rev @id has been updated.', array('@id' => $id)), 'Entity was updated');
|
||||
$storage->resetCache([$id]);
|
||||
$entity = $storage->load($id);
|
||||
$this->assertEqual($entity->{$field_name}->value, $value, 'Field value was updated');
|
||||
|
||||
// Set the field back to hidden.
|
||||
entity_get_form_display($entity_type, $this->field->getTargetBundle(), 'default')
|
||||
->removeComponent($this->field->getName())
|
||||
->save();
|
||||
|
||||
// Create a new revision.
|
||||
$edit = array('revision' => TRUE);
|
||||
$this->drupalPostForm($entity_type . '/manage/' . $id . '/edit', $edit, t('Save'));
|
||||
|
||||
// Check that the expected value has been carried over to the new revision.
|
||||
$storage->resetCache(array($id));
|
||||
$entity = $storage->load($id);
|
||||
$this->assertEqual($entity->{$field_name}->value, $value, 'New revision has the expected value for the field with the Hidden widget');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the form display of the label for multi-value fields.
|
||||
*/
|
||||
public function testLabelOnMultiValueFields() {
|
||||
$user = $this->drupalCreateUser(['administer entity_test content']);
|
||||
$this->drupalLogin($user);
|
||||
|
||||
FieldStorageConfig::create([
|
||||
'entity_type' => 'entity_test_base_field_display',
|
||||
'field_name' => 'foo',
|
||||
'type' => 'text',
|
||||
'cardinality' => FieldStorageConfig::CARDINALITY_UNLIMITED,
|
||||
])->save();
|
||||
FieldConfig::create([
|
||||
'entity_type' => 'entity_test_base_field_display',
|
||||
'bundle' => 'bar',
|
||||
'field_name' => 'foo',
|
||||
// Set a dangerous label to test XSS filtering.
|
||||
'label' => "<script>alert('a configurable field');</script>",
|
||||
])->save();
|
||||
EntityFormDisplay::create([
|
||||
'targetEntityType' => 'entity_test_base_field_display',
|
||||
'bundle' => 'bar',
|
||||
'mode' => 'default',
|
||||
])->setComponent('foo', ['type' => 'text_textfield'])->enable()->save();
|
||||
|
||||
$entity = EntityTestBaseFieldDisplay::create(['type' => 'bar']);
|
||||
$entity->save();
|
||||
|
||||
$this->drupalGet('entity_test_base_field_display/manage/' . $entity->id());
|
||||
$this->assertResponse(200);
|
||||
$this->assertText('A field with multiple values');
|
||||
// Test if labels were XSS filtered.
|
||||
$this->assertEscaped("<script>alert('a configurable field');</script>");
|
||||
}
|
||||
|
||||
}
|
194
web/core/modules/field/src/Tests/NestedFormTest.php
Normal file
194
web/core/modules/field/src/Tests/NestedFormTest.php
Normal file
|
@ -0,0 +1,194 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Tests field elements in nested forms.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class NestedFormTest extends FieldTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('field_test', 'entity_test');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$web_user = $this->drupalCreateUser(array('view test entity', 'administer entity_test content'));
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
$this->fieldStorageSingle = array(
|
||||
'field_name' => 'field_single',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field',
|
||||
);
|
||||
$this->fieldStorageUnlimited = array(
|
||||
'field_name' => 'field_unlimited',
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'test_field',
|
||||
'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
|
||||
);
|
||||
|
||||
$this->field = array(
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
'label' => $this->randomMachineName() . '_label',
|
||||
'description' => '[site:name]_description',
|
||||
'weight' => mt_rand(0, 127),
|
||||
'settings' => array(
|
||||
'test_field_setting' => $this->randomMachineName(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Field API form integration within a subform.
|
||||
*/
|
||||
function testNestedFieldForm() {
|
||||
// Add two fields on the 'entity_test'
|
||||
FieldStorageConfig::create($this->fieldStorageSingle)->save();
|
||||
FieldStorageConfig::create($this->fieldStorageUnlimited)->save();
|
||||
$this->field['field_name'] = 'field_single';
|
||||
$this->field['label'] = 'Single field';
|
||||
FieldConfig::create($this->field)->save();
|
||||
entity_get_form_display($this->field['entity_type'], $this->field['bundle'], 'default')
|
||||
->setComponent($this->field['field_name'])
|
||||
->save();
|
||||
$this->field['field_name'] = 'field_unlimited';
|
||||
$this->field['label'] = 'Unlimited field';
|
||||
FieldConfig::create($this->field)->save();
|
||||
entity_get_form_display($this->field['entity_type'], $this->field['bundle'], 'default')
|
||||
->setComponent($this->field['field_name'])
|
||||
->save();
|
||||
|
||||
// Create two entities.
|
||||
$entity_type = 'entity_test';
|
||||
$storage = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type);
|
||||
|
||||
$entity_1 = $storage->create(['id' => 1]);
|
||||
$entity_1->enforceIsNew();
|
||||
$entity_1->field_single->value = 0;
|
||||
$entity_1->field_unlimited->value = 1;
|
||||
$entity_1->save();
|
||||
|
||||
$entity_2 = $storage->create(array('id' => 2));
|
||||
$entity_2->enforceIsNew();
|
||||
$entity_2->field_single->value = 10;
|
||||
$entity_2->field_unlimited->value = 11;
|
||||
$entity_2->save();
|
||||
|
||||
// Display the 'combined form'.
|
||||
$this->drupalGet('test-entity/nested/1/2');
|
||||
$this->assertFieldByName('field_single[0][value]', 0, 'Entity 1: field_single value appears correctly is the form.');
|
||||
$this->assertFieldByName('field_unlimited[0][value]', 1, 'Entity 1: field_unlimited value 0 appears correctly is the form.');
|
||||
$this->assertFieldByName('entity_2[field_single][0][value]', 10, 'Entity 2: field_single value appears correctly is the form.');
|
||||
$this->assertFieldByName('entity_2[field_unlimited][0][value]', 11, 'Entity 2: field_unlimited value 0 appears correctly is the form.');
|
||||
|
||||
// Submit the form and check that the entities are updated accordingly.
|
||||
$edit = array(
|
||||
'field_single[0][value]' => 1,
|
||||
'field_unlimited[0][value]' => 2,
|
||||
'field_unlimited[1][value]' => 3,
|
||||
'entity_2[field_single][0][value]' => 11,
|
||||
'entity_2[field_unlimited][0][value]' => 12,
|
||||
'entity_2[field_unlimited][1][value]' => 13,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$entity_1 = $storage->load(1);
|
||||
$entity_2 = $storage->load(2);
|
||||
$this->assertFieldValues($entity_1, 'field_single', array(1));
|
||||
$this->assertFieldValues($entity_1, 'field_unlimited', array(2, 3));
|
||||
$this->assertFieldValues($entity_2, 'field_single', array(11));
|
||||
$this->assertFieldValues($entity_2, 'field_unlimited', array(12, 13));
|
||||
|
||||
// Submit invalid values and check that errors are reported on the
|
||||
// correct widgets.
|
||||
$edit = array(
|
||||
'field_unlimited[1][value]' => -1,
|
||||
);
|
||||
$this->drupalPostForm('test-entity/nested/1/2', $edit, t('Save'));
|
||||
$this->assertRaw(t('%label does not accept the value -1', array('%label' => 'Unlimited field')), 'Entity 1: the field validation error was reported.');
|
||||
$error_field = $this->xpath('//input[@id=:id and contains(@class, "error")]', array(':id' => 'edit-field-unlimited-1-value'));
|
||||
$this->assertTrue($error_field, 'Entity 1: the error was flagged on the correct element.');
|
||||
$edit = array(
|
||||
'entity_2[field_unlimited][1][value]' => -1,
|
||||
);
|
||||
$this->drupalPostForm('test-entity/nested/1/2', $edit, t('Save'));
|
||||
$this->assertRaw(t('%label does not accept the value -1', array('%label' => 'Unlimited field')), 'Entity 2: the field validation error was reported.');
|
||||
$error_field = $this->xpath('//input[@id=:id and contains(@class, "error")]', array(':id' => 'edit-entity-2-field-unlimited-1-value'));
|
||||
$this->assertTrue($error_field, 'Entity 2: the error was flagged on the correct element.');
|
||||
|
||||
// Test that reordering works on both entities.
|
||||
$edit = array(
|
||||
'field_unlimited[0][_weight]' => 0,
|
||||
'field_unlimited[1][_weight]' => -1,
|
||||
'entity_2[field_unlimited][0][_weight]' => 0,
|
||||
'entity_2[field_unlimited][1][_weight]' => -1,
|
||||
);
|
||||
$this->drupalPostForm('test-entity/nested/1/2', $edit, t('Save'));
|
||||
$this->assertFieldValues($entity_1, 'field_unlimited', array(3, 2));
|
||||
$this->assertFieldValues($entity_2, 'field_unlimited', array(13, 12));
|
||||
|
||||
// Test the 'add more' buttons. Only Ajax submission is tested, because
|
||||
// the two 'add more' buttons present in the form have the same #value,
|
||||
// which confuses drupalPostForm().
|
||||
// 'Add more' button in the first entity:
|
||||
$this->drupalGet('test-entity/nested/1/2');
|
||||
$this->drupalPostAjaxForm(NULL, array(), 'field_unlimited_add_more');
|
||||
$this->assertFieldByName('field_unlimited[0][value]', 3, 'Entity 1: field_unlimited value 0 appears correctly is the form.');
|
||||
$this->assertFieldByName('field_unlimited[1][value]', 2, 'Entity 1: field_unlimited value 1 appears correctly is the form.');
|
||||
$this->assertFieldByName('field_unlimited[2][value]', '', 'Entity 1: field_unlimited value 2 appears correctly is the form.');
|
||||
$this->assertFieldByName('field_unlimited[3][value]', '', 'Entity 1: an empty widget was added for field_unlimited value 3.');
|
||||
// 'Add more' button in the first entity (changing field values):
|
||||
$edit = array(
|
||||
'entity_2[field_unlimited][0][value]' => 13,
|
||||
'entity_2[field_unlimited][1][value]' => 14,
|
||||
'entity_2[field_unlimited][2][value]' => 15,
|
||||
);
|
||||
$this->drupalPostAjaxForm(NULL, $edit, 'entity_2_field_unlimited_add_more');
|
||||
$this->assertFieldByName('entity_2[field_unlimited][0][value]', 13, 'Entity 2: field_unlimited value 0 appears correctly is the form.');
|
||||
$this->assertFieldByName('entity_2[field_unlimited][1][value]', 14, 'Entity 2: field_unlimited value 1 appears correctly is the form.');
|
||||
$this->assertFieldByName('entity_2[field_unlimited][2][value]', 15, 'Entity 2: field_unlimited value 2 appears correctly is the form.');
|
||||
$this->assertFieldByName('entity_2[field_unlimited][3][value]', '', 'Entity 2: an empty widget was added for field_unlimited value 3.');
|
||||
// Save the form and check values are saved correctly.
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
$this->assertFieldValues($entity_1, 'field_unlimited', array(3, 2));
|
||||
$this->assertFieldValues($entity_2, 'field_unlimited', array(13, 14, 15));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests entity level validation within subforms.
|
||||
*/
|
||||
public function testNestedEntityFormEntityLevelValidation() {
|
||||
// Create two entities.
|
||||
$storage = $this->container->get('entity_type.manager')
|
||||
->getStorage('entity_test_constraints');
|
||||
|
||||
$entity_1 = $storage->create();
|
||||
$entity_1->save();
|
||||
|
||||
$entity_2 = $storage->create();
|
||||
$entity_2->save();
|
||||
|
||||
// Display the 'combined form'.
|
||||
$this->drupalGet("test-entity-constraints/nested/{$entity_1->id()}/{$entity_2->id()}");
|
||||
$this->assertFieldByName('entity_2[changed]', 0, 'Entity 2: changed value appears correctly in the form.');
|
||||
|
||||
// Submit the form and check that the entities are updated accordingly.
|
||||
$edit = ['entity_2[changed]' => REQUEST_TIME - 86400];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
|
||||
$elements = $this->cssSelect('.entity-2.error');
|
||||
$this->assertEqual(1, count($elements), 'The whole nested entity form has been correctly flagged with an error class.');
|
||||
}
|
||||
|
||||
}
|
562
web/core/modules/field/src/Tests/Number/NumberFieldTest.php
Normal file
562
web/core/modules/field/src/Tests/Number/NumberFieldTest.php
Normal file
|
@ -0,0 +1,562 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Tests\Number;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Tests the creation of numeric fields.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class NumberFieldTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'entity_test', 'field_ui');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->drupalLogin($this->drupalCreateUser(array(
|
||||
'view test entity',
|
||||
'administer entity_test content',
|
||||
'administer content types',
|
||||
'administer node fields',
|
||||
'administer node display',
|
||||
'bypass node access',
|
||||
'administer entity_test fields',
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test decimal field.
|
||||
*/
|
||||
function testNumberDecimalField() {
|
||||
// Create a field with settings to validate.
|
||||
$field_name = Unicode::strtolower($this->randomMachineName());
|
||||
FieldStorageConfig::create(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'decimal',
|
||||
'settings' => array(
|
||||
'precision' => 8, 'scale' => 4,
|
||||
)
|
||||
))->save();
|
||||
FieldConfig::create([
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
])->save();
|
||||
|
||||
entity_get_form_display('entity_test', 'entity_test', 'default')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'number',
|
||||
'settings' => array(
|
||||
'placeholder' => '0.00'
|
||||
),
|
||||
))
|
||||
->save();
|
||||
entity_get_display('entity_test', 'entity_test', 'default')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'number_decimal',
|
||||
))
|
||||
->save();
|
||||
|
||||
// Display creation form.
|
||||
$this->drupalGet('entity_test/add');
|
||||
$this->assertFieldByName("{$field_name}[0][value]", '', 'Widget is displayed');
|
||||
$this->assertRaw('placeholder="0.00"');
|
||||
|
||||
// Submit a signed decimal value within the allowed precision and scale.
|
||||
$value = '-1234.5678';
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $value,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
|
||||
$id = $match[1];
|
||||
$this->assertText(t('entity_test @id has been created.', array('@id' => $id)), 'Entity was created');
|
||||
$this->assertRaw($value, 'Value is displayed.');
|
||||
|
||||
// Try to create entries with more than one decimal separator; assert fail.
|
||||
$wrong_entries = array(
|
||||
'3.14.159',
|
||||
'0..45469',
|
||||
'..4589',
|
||||
'6.459.52',
|
||||
'6.3..25',
|
||||
);
|
||||
|
||||
foreach ($wrong_entries as $wrong_entry) {
|
||||
$this->drupalGet('entity_test/add');
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $wrong_entry,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertRaw(t('%name must be a number.', array('%name' => $field_name)), 'Correctly failed to save decimal value with more than one decimal point.');
|
||||
}
|
||||
|
||||
// Try to create entries with minus sign not in the first position.
|
||||
$wrong_entries = array(
|
||||
'3-3',
|
||||
'4-',
|
||||
'1.3-',
|
||||
'1.2-4',
|
||||
'-10-10',
|
||||
);
|
||||
|
||||
foreach ($wrong_entries as $wrong_entry) {
|
||||
$this->drupalGet('entity_test/add');
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $wrong_entry,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertRaw(t('%name must be a number.', array('%name' => $field_name)), 'Correctly failed to save decimal value with minus sign in the wrong position.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test integer field.
|
||||
*/
|
||||
function testNumberIntegerField() {
|
||||
$minimum = rand(-4000, -2000);
|
||||
$maximum = rand(2000, 4000);
|
||||
|
||||
// Create a field with settings to validate.
|
||||
$field_name = Unicode::strtolower($this->randomMachineName());
|
||||
$storage = FieldStorageConfig::create(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'integer',
|
||||
));
|
||||
$storage->save();
|
||||
|
||||
FieldConfig::create([
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
'settings' => array(
|
||||
'min' => $minimum, 'max' => $maximum, 'prefix' => 'ThePrefix',
|
||||
)
|
||||
])->save();
|
||||
|
||||
entity_get_form_display('entity_test', 'entity_test', 'default')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'number',
|
||||
'settings' => array(
|
||||
'placeholder' => '4'
|
||||
),
|
||||
))
|
||||
->save();
|
||||
entity_get_display('entity_test', 'entity_test', 'default')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'number_integer',
|
||||
'settings' => array(
|
||||
'prefix_suffix' => FALSE,
|
||||
),
|
||||
))
|
||||
->save();
|
||||
|
||||
// Check the storage schema.
|
||||
$expected = array(
|
||||
'columns' => array(
|
||||
'value' => array(
|
||||
'type' => 'int',
|
||||
'unsigned' => '',
|
||||
'size' => 'normal'
|
||||
),
|
||||
),
|
||||
'unique keys' => array(),
|
||||
'indexes' => array(),
|
||||
'foreign keys' => array()
|
||||
);
|
||||
$this->assertEqual($storage->getSchema(), $expected);
|
||||
|
||||
// Display creation form.
|
||||
$this->drupalGet('entity_test/add');
|
||||
$this->assertFieldByName("{$field_name}[0][value]", '', 'Widget is displayed');
|
||||
$this->assertRaw('placeholder="4"');
|
||||
|
||||
// Submit a valid integer
|
||||
$value = rand($minimum, $maximum);
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $value,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
|
||||
$id = $match[1];
|
||||
$this->assertText(t('entity_test @id has been created.', array('@id' => $id)), 'Entity was created');
|
||||
|
||||
// Try to set a value below the minimum value
|
||||
$this->drupalGet('entity_test/add');
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $minimum - 1,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertRaw(t('%name must be higher than or equal to %minimum.', array('%name' => $field_name, '%minimum' => $minimum)), 'Correctly failed to save integer value less than minimum allowed value.');
|
||||
|
||||
// Try to set a decimal value
|
||||
$this->drupalGet('entity_test/add');
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => 1.5,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertRaw(t('%name is not a valid number.', array('%name' => $field_name)), 'Correctly failed to save decimal value to integer field.');
|
||||
|
||||
// Try to set a value above the maximum value
|
||||
$this->drupalGet('entity_test/add');
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $maximum + 1,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertRaw(t('%name must be lower than or equal to %maximum.', array('%name' => $field_name, '%maximum' => $maximum)), 'Correctly failed to save integer value greater than maximum allowed value.');
|
||||
|
||||
// Try to set a wrong integer value.
|
||||
$this->drupalGet('entity_test/add');
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => '20-40',
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertRaw(t('%name must be a number.', array('%name' => $field_name)), 'Correctly failed to save wrong integer value.');
|
||||
|
||||
// Test with valid entries.
|
||||
$valid_entries = array(
|
||||
'-1234',
|
||||
'0',
|
||||
'1234',
|
||||
);
|
||||
|
||||
foreach ($valid_entries as $valid_entry) {
|
||||
$this->drupalGet('entity_test/add');
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $valid_entry,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
|
||||
$id = $match[1];
|
||||
$this->assertText(t('entity_test @id has been created.', array('@id' => $id)), 'Entity was created');
|
||||
$this->assertRaw($valid_entry, 'Value is displayed.');
|
||||
$this->assertNoFieldByXpath('//div[@content="' . $valid_entry . '"]', NULL, 'The "content" attribute is not present since the Prefix is not being displayed');
|
||||
}
|
||||
|
||||
// Test for the content attribute when a Prefix is displayed. Presumably this also tests for the attribute when a Suffix is displayed.
|
||||
entity_get_display('entity_test', 'entity_test', 'default')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'number_integer',
|
||||
'settings' => array(
|
||||
'prefix_suffix' => TRUE,
|
||||
),
|
||||
))
|
||||
->save();
|
||||
$integer_value = '123';
|
||||
$this->drupalGet('entity_test/add');
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $integer_value,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
|
||||
$id = $match[1];
|
||||
$this->assertText(t('entity_test @id has been created.', array('@id' => $id)), 'Entity was created');
|
||||
$this->drupalGet('entity_test/' . $id);
|
||||
$this->assertFieldByXPath('//div[@content="' . $integer_value . '"]', 'ThePrefix' . $integer_value, 'The "content" attribute has been set to the value of the field, and the prefix is being displayed.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test float field.
|
||||
*/
|
||||
function testNumberFloatField() {
|
||||
// Create a field with settings to validate.
|
||||
$field_name = Unicode::strtolower($this->randomMachineName());
|
||||
FieldStorageConfig::create(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'float',
|
||||
))->save();
|
||||
|
||||
FieldConfig::create([
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
])->save();
|
||||
|
||||
entity_get_form_display('entity_test', 'entity_test', 'default')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'number',
|
||||
'settings' => array(
|
||||
'placeholder' => '0.00'
|
||||
),
|
||||
))
|
||||
->save();
|
||||
|
||||
entity_get_display('entity_test', 'entity_test', 'default')
|
||||
->setComponent($field_name, array(
|
||||
'type' => 'number_decimal',
|
||||
))
|
||||
->save();
|
||||
|
||||
// Display creation form.
|
||||
$this->drupalGet('entity_test/add');
|
||||
$this->assertFieldByName("{$field_name}[0][value]", '', 'Widget is displayed');
|
||||
$this->assertRaw('placeholder="0.00"');
|
||||
|
||||
// Submit a signed decimal value within the allowed precision and scale.
|
||||
$value = '-1234.5678';
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $value,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
|
||||
$id = $match[1];
|
||||
$this->assertText(t('entity_test @id has been created.', array('@id' => $id)), 'Entity was created');
|
||||
|
||||
// Ensure that the 'number_decimal' formatter displays the number with the
|
||||
// expected rounding.
|
||||
$this->drupalGet('entity_test/' . $id);
|
||||
$this->assertRaw(round($value, 2));
|
||||
|
||||
// Try to create entries with more than one decimal separator; assert fail.
|
||||
$wrong_entries = array(
|
||||
'3.14.159',
|
||||
'0..45469',
|
||||
'..4589',
|
||||
'6.459.52',
|
||||
'6.3..25',
|
||||
);
|
||||
|
||||
foreach ($wrong_entries as $wrong_entry) {
|
||||
$this->drupalGet('entity_test/add');
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $wrong_entry,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertRaw(t('%name must be a number.', array('%name' => $field_name)), 'Correctly failed to save float value with more than one decimal point.');
|
||||
}
|
||||
|
||||
// Try to create entries with minus sign not in the first position.
|
||||
$wrong_entries = array(
|
||||
'3-3',
|
||||
'4-',
|
||||
'1.3-',
|
||||
'1.2-4',
|
||||
'-10-10',
|
||||
);
|
||||
|
||||
foreach ($wrong_entries as $wrong_entry) {
|
||||
$this->drupalGet('entity_test/add');
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $wrong_entry,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertRaw(t('%name must be a number.', array('%name' => $field_name)), 'Correctly failed to save float value with minus sign in the wrong position.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test default formatter behavior
|
||||
*/
|
||||
function testNumberFormatter() {
|
||||
$type = Unicode::strtolower($this->randomMachineName());
|
||||
$float_field = Unicode::strtolower($this->randomMachineName());
|
||||
$integer_field = Unicode::strtolower($this->randomMachineName());
|
||||
$thousand_separators = array('', '.', ',', ' ', chr(8201), "'");
|
||||
$decimal_separators = array('.', ',');
|
||||
$prefix = $this->randomMachineName();
|
||||
$suffix = $this->randomMachineName();
|
||||
$random_float = rand(0, pow(10, 6));
|
||||
$random_integer = rand(0, pow(10, 6));
|
||||
|
||||
// Create a content type containing float and integer fields.
|
||||
$this->drupalCreateContentType(array('type' => $type));
|
||||
|
||||
FieldStorageConfig::create(array(
|
||||
'field_name' => $float_field,
|
||||
'entity_type' => 'node',
|
||||
'type' => 'float',
|
||||
))->save();
|
||||
|
||||
FieldStorageConfig::create(array(
|
||||
'field_name' => $integer_field,
|
||||
'entity_type' => 'node',
|
||||
'type' => 'integer',
|
||||
))->save();
|
||||
|
||||
FieldConfig::create([
|
||||
'field_name' => $float_field,
|
||||
'entity_type' => 'node',
|
||||
'bundle' => $type,
|
||||
'settings' => array(
|
||||
'prefix' => $prefix,
|
||||
'suffix' => $suffix
|
||||
),
|
||||
])->save();
|
||||
|
||||
FieldConfig::create([
|
||||
'field_name' => $integer_field,
|
||||
'entity_type' => 'node',
|
||||
'bundle' => $type,
|
||||
'settings' => array(
|
||||
'prefix' => $prefix,
|
||||
'suffix' => $suffix
|
||||
),
|
||||
])->save();
|
||||
|
||||
entity_get_form_display('node', $type, 'default')
|
||||
->setComponent($float_field, array(
|
||||
'type' => 'number',
|
||||
'settings' => array(
|
||||
'placeholder' => '0.00'
|
||||
),
|
||||
))
|
||||
->setComponent($integer_field, array(
|
||||
'type' => 'number',
|
||||
'settings' => array(
|
||||
'placeholder' => '0.00'
|
||||
),
|
||||
))
|
||||
->save();
|
||||
|
||||
entity_get_display('node', $type, 'default')
|
||||
->setComponent($float_field, array(
|
||||
'type' => 'number_decimal',
|
||||
))
|
||||
->setComponent($integer_field, array(
|
||||
'type' => 'number_unformatted',
|
||||
))
|
||||
->save();
|
||||
|
||||
// Create a node to test formatters.
|
||||
$node = Node::create([
|
||||
'type' => $type,
|
||||
'title' => $this->randomMachineName(),
|
||||
$float_field => ['value' => $random_float],
|
||||
$integer_field => ['value' => $random_integer],
|
||||
]);
|
||||
$node->save();
|
||||
|
||||
// Go to manage display page.
|
||||
$this->drupalGet("admin/structure/types/manage/$type/display");
|
||||
|
||||
// Configure number_decimal formatter for the 'float' field type.
|
||||
$thousand_separator = $thousand_separators[array_rand($thousand_separators)];
|
||||
$decimal_separator = $decimal_separators[array_rand($decimal_separators)];
|
||||
$scale = rand(0, 10);
|
||||
|
||||
$this->drupalPostAjaxForm(NULL, array(), "${float_field}_settings_edit");
|
||||
$edit = array(
|
||||
"fields[${float_field}][settings_edit_form][settings][prefix_suffix]" => TRUE,
|
||||
"fields[${float_field}][settings_edit_form][settings][scale]" => $scale,
|
||||
"fields[${float_field}][settings_edit_form][settings][decimal_separator]" => $decimal_separator,
|
||||
"fields[${float_field}][settings_edit_form][settings][thousand_separator]" => $thousand_separator,
|
||||
);
|
||||
$this->drupalPostAjaxForm(NULL, $edit, "${float_field}_plugin_settings_update");
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
|
||||
// Check number_decimal and number_unformatted formatters behavior.
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$float_formatted = number_format($random_float, $scale, $decimal_separator, $thousand_separator);
|
||||
$this->assertRaw("$prefix$float_formatted$suffix", 'Prefix and suffix added');
|
||||
$this->assertRaw((string) $random_integer);
|
||||
|
||||
// Configure the number_decimal formatter.
|
||||
entity_get_display('node', $type, 'default')
|
||||
->setComponent($integer_field, array(
|
||||
'type' => 'number_integer',
|
||||
))
|
||||
->save();
|
||||
$this->drupalGet("admin/structure/types/manage/$type/display");
|
||||
|
||||
$thousand_separator = $thousand_separators[array_rand($thousand_separators)];
|
||||
|
||||
$this->drupalPostAjaxForm(NULL, array(), "${integer_field}_settings_edit");
|
||||
$edit = array(
|
||||
"fields[${integer_field}][settings_edit_form][settings][prefix_suffix]" => FALSE,
|
||||
"fields[${integer_field}][settings_edit_form][settings][thousand_separator]" => $thousand_separator,
|
||||
);
|
||||
$this->drupalPostAjaxForm(NULL, $edit, "${integer_field}_plugin_settings_update");
|
||||
$this->drupalPostForm(NULL, array(), t('Save'));
|
||||
|
||||
// Check number_integer formatter behavior.
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
|
||||
$integer_formatted = number_format($random_integer, 0, '', $thousand_separator);
|
||||
$this->assertRaw($integer_formatted, 'Random integer formatted');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests setting the minimum value of a float field through the interface.
|
||||
*/
|
||||
function testCreateNumberFloatField() {
|
||||
// Create a float field.
|
||||
$field_name = Unicode::strtolower($this->randomMachineName());
|
||||
FieldStorageConfig::create(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'float',
|
||||
))->save();
|
||||
|
||||
$field = FieldConfig::create([
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
]);
|
||||
$field->save();
|
||||
|
||||
// Set the minimum value to a float value.
|
||||
$this->assertSetMinimumValue($field, 0.0001);
|
||||
// Set the minimum value to an integer value.
|
||||
$this->assertSetMinimumValue($field, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests setting the minimum value of a decimal field through the interface.
|
||||
*/
|
||||
function testCreateNumberDecimalField() {
|
||||
// Create a decimal field.
|
||||
$field_name = Unicode::strtolower($this->randomMachineName());
|
||||
FieldStorageConfig::create(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'decimal',
|
||||
))->save();
|
||||
|
||||
$field = FieldConfig::create([
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
]);
|
||||
$field->save();
|
||||
|
||||
// Set the minimum value to a decimal value.
|
||||
$this->assertSetMinimumValue($field, 0.1);
|
||||
// Set the minimum value to an integer value.
|
||||
$this->assertSetMinimumValue($field, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to set the minimum value of a field.
|
||||
*/
|
||||
function assertSetMinimumValue($field, $minimum_value) {
|
||||
$field_configuration_url = 'entity_test/structure/entity_test/fields/entity_test.entity_test.' . $field->getName();
|
||||
|
||||
// Set the minimum value.
|
||||
$edit = array(
|
||||
'settings[min]' => $minimum_value,
|
||||
);
|
||||
$this->drupalPostForm($field_configuration_url, $edit, t('Save settings'));
|
||||
// Check if an error message is shown.
|
||||
$this->assertNoRaw(t('%name is not a valid number.', array('%name' => t('Minimum'))), 'Saved ' . gettype($minimum_value) . ' value as minimal value on a ' . $field->getType() . ' field');
|
||||
// Check if a success message is shown.
|
||||
$this->assertRaw(t('Saved %label configuration.', array('%label' => $field->getLabel())));
|
||||
// Check if the minimum value was actually set.
|
||||
$this->drupalGet($field_configuration_url);
|
||||
$this->assertFieldById('edit-settings-min', $minimum_value, 'Minimal ' . gettype($minimum_value) . ' value was set on a ' . $field->getType() . ' field.');
|
||||
}
|
||||
|
||||
}
|
102
web/core/modules/field/src/Tests/String/StringFieldTest.php
Normal file
102
web/core/modules/field/src/Tests/String/StringFieldTest.php
Normal file
|
@ -0,0 +1,102 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Tests\String;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Tests the creation of string fields.
|
||||
*
|
||||
* @group text
|
||||
*/
|
||||
class StringFieldTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('entity_test', 'file');
|
||||
|
||||
/**
|
||||
* A user without any special permissions.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $webUser;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->webUser = $this->drupalCreateUser(array('view test entity', 'administer entity_test content'));
|
||||
$this->drupalLogin($this->webUser);
|
||||
}
|
||||
|
||||
// Test fields.
|
||||
|
||||
/**
|
||||
* Test widgets.
|
||||
*/
|
||||
function testTextfieldWidgets() {
|
||||
$this->_testTextfieldWidgets('string', 'string_textfield');
|
||||
$this->_testTextfieldWidgets('string_long', 'string_textarea');
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for testTextfieldWidgets().
|
||||
*/
|
||||
function _testTextfieldWidgets($field_type, $widget_type) {
|
||||
// Create a field.
|
||||
$field_name = Unicode::strtolower($this->randomMachineName());
|
||||
$field_storage = FieldStorageConfig::create(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => $field_type
|
||||
));
|
||||
$field_storage->save();
|
||||
FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'entity_test',
|
||||
'label' => $this->randomMachineName() . '_label',
|
||||
])->save();
|
||||
entity_get_form_display('entity_test', 'entity_test', 'default')
|
||||
->setComponent($field_name, array(
|
||||
'type' => $widget_type,
|
||||
'settings' => array(
|
||||
'placeholder' => 'A placeholder on ' . $widget_type,
|
||||
),
|
||||
))
|
||||
->save();
|
||||
entity_get_display('entity_test', 'entity_test', 'full')
|
||||
->setComponent($field_name)
|
||||
->save();
|
||||
|
||||
// Display creation form.
|
||||
$this->drupalGet('entity_test/add');
|
||||
$this->assertFieldByName("{$field_name}[0][value]", '', 'Widget is displayed');
|
||||
$this->assertNoFieldByName("{$field_name}[0][format]", '1', 'Format selector is not displayed');
|
||||
$this->assertRaw(format_string('placeholder="A placeholder on @widget_type"', array('@widget_type' => $widget_type)));
|
||||
|
||||
// Submit with some value.
|
||||
$value = $this->randomMachineName();
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $value,
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
|
||||
$id = $match[1];
|
||||
$this->assertText(t('entity_test @id has been created.', array('@id' => $id)), 'Entity was created');
|
||||
|
||||
// Display the entity.
|
||||
$entity = EntityTest::load($id);
|
||||
$display = entity_get_display($entity->getEntityTypeId(), $entity->bundle(), 'full');
|
||||
$content = $display->build($entity);
|
||||
$this->setRawContent(\Drupal::service('renderer')->renderRoot($content));
|
||||
$this->assertText($value, 'Filtered tags are not displayed');
|
||||
}
|
||||
|
||||
}
|
134
web/core/modules/field/src/Tests/TranslationWebTest.php
Normal file
134
web/core/modules/field/src/Tests/TranslationWebTest.php
Normal file
|
@ -0,0 +1,134 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
|
||||
/**
|
||||
* Tests multilanguage fields logic that require a full environment.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class TranslationWebTest extends FieldTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('language', 'field_test', 'entity_test');
|
||||
|
||||
/**
|
||||
* The name of the field to use in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName;
|
||||
|
||||
/**
|
||||
* The name of the entity type to use in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entityTypeId = 'entity_test_mulrev';
|
||||
|
||||
/**
|
||||
* The field storage to use in this test.
|
||||
*
|
||||
* @var \Drupal\field\Entity\FieldStorageConfig
|
||||
*/
|
||||
protected $fieldStorage;
|
||||
|
||||
/**
|
||||
* The field to use in this test.
|
||||
*
|
||||
* @var \Drupal\field\Entity\FieldConfig
|
||||
*/
|
||||
protected $field;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->fieldName = Unicode::strtolower($this->randomMachineName() . '_field_name');
|
||||
|
||||
$field_storage = array(
|
||||
'field_name' => $this->fieldName,
|
||||
'entity_type' => $this->entityTypeId,
|
||||
'type' => 'test_field',
|
||||
'cardinality' => 4,
|
||||
);
|
||||
FieldStorageConfig::create($field_storage)->save();
|
||||
$this->fieldStorage = FieldStorageConfig::load($this->entityTypeId . '.' . $this->fieldName);
|
||||
|
||||
$field = array(
|
||||
'field_storage' => $this->fieldStorage,
|
||||
'bundle' => $this->entityTypeId,
|
||||
);
|
||||
FieldConfig::create($field)->save();
|
||||
$this->field = FieldConfig::load($this->entityTypeId . '.' . $field['bundle'] . '.' . $this->fieldName);
|
||||
|
||||
entity_get_form_display($this->entityTypeId, $this->entityTypeId, 'default')
|
||||
->setComponent($this->fieldName)
|
||||
->save();
|
||||
|
||||
for ($i = 0; $i < 3; ++$i) {
|
||||
ConfigurableLanguage::create(array(
|
||||
'id' => 'l' . $i,
|
||||
'label' => $this->randomString(),
|
||||
))->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests field translations when creating a new revision.
|
||||
*/
|
||||
function testFieldFormTranslationRevisions() {
|
||||
$web_user = $this->drupalCreateUser(array('view test entity', 'administer entity_test content'));
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
// Prepare the field translations.
|
||||
field_test_entity_info_translatable($this->entityTypeId, TRUE);
|
||||
$entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($this->entityTypeId)
|
||||
->create();
|
||||
$available_langcodes = array_flip(array_keys($this->container->get('language_manager')->getLanguages()));
|
||||
$field_name = $this->fieldStorage->getName();
|
||||
|
||||
// Store the field translations.
|
||||
ksort($available_langcodes);
|
||||
$entity->langcode->value = key($available_langcodes);
|
||||
foreach ($available_langcodes as $langcode => $value) {
|
||||
$translation = $entity->hasTranslation($langcode) ? $entity->getTranslation($langcode) : $entity->addTranslation($langcode);
|
||||
$translation->{$field_name}->value = $value + 1;
|
||||
}
|
||||
$entity->save();
|
||||
|
||||
// Create a new revision.
|
||||
$edit = array(
|
||||
"{$field_name}[0][value]" => $entity->{$field_name}->value,
|
||||
'revision' => TRUE,
|
||||
);
|
||||
$this->drupalPostForm($this->entityTypeId . '/manage/' . $entity->id() . '/edit', $edit, t('Save'));
|
||||
|
||||
// Check translation revisions.
|
||||
$this->checkTranslationRevisions($entity->id(), $entity->getRevisionId(), $available_langcodes);
|
||||
$this->checkTranslationRevisions($entity->id(), $entity->getRevisionId() + 1, $available_langcodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the field translation attached to the entity revision identified
|
||||
* by the passed arguments were correctly stored.
|
||||
*/
|
||||
private function checkTranslationRevisions($id, $revision_id, $available_langcodes) {
|
||||
$field_name = $this->fieldStorage->getName();
|
||||
$entity = entity_revision_load($this->entityTypeId, $revision_id);
|
||||
foreach ($available_langcodes as $langcode => $value) {
|
||||
$passed = $entity->getTranslation($langcode)->{$field_name}->value == $value + 1;
|
||||
$this->assertTrue($passed, format_string('The @language translation for revision @revision was correctly stored', array('@language' => $langcode, '@revision' => $entity->getRevisionId())));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Tests\Update;
|
||||
|
||||
use Drupal\system\Tests\Update\UpdatePathTestBase;
|
||||
|
||||
/**
|
||||
* Tests the update for the 'size' setting of the 'email_default' field widget.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class EmailWidgetSizeSettingUpdateTest extends UpdatePathTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setDatabaseDumpFiles() {
|
||||
$this->databaseDumpFiles = [
|
||||
__DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
|
||||
__DIR__ . '/../../../tests/fixtures/update/drupal-8.email_widget_size_setting-2578741.php',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests field_post_update_email_widget_size_setting().
|
||||
*
|
||||
* @see field_post_update_email_widget_size_setting()
|
||||
*/
|
||||
public function testFieldPostUpdateEmailWidgetSizeSetting() {
|
||||
$configFactory = $this->container->get('config.factory');
|
||||
|
||||
// Load the 'node.article.default' entity form display and check that the
|
||||
// widget for 'field_email_2578741' does not have a 'size' setting.
|
||||
/** @var \Drupal\Core\Config\Config $config */
|
||||
$config = $configFactory->get('core.entity_form_display.node.article.default');
|
||||
$settings = $config->get('content.field_email_2578741.settings');
|
||||
$this->assertTrue(!isset($settings['size']), 'The size setting does not exist prior to running the update functions.');
|
||||
|
||||
// Run updates.
|
||||
$this->runUpdates();
|
||||
|
||||
// Reload the config and check that the 'size' setting has been populated.
|
||||
$config = $configFactory->get('core.entity_form_display.node.article.default');
|
||||
$settings = $config->get('content.field_email_2578741.settings');
|
||||
$this->assertEqual($settings['size'], 60, 'The size setting exists and it has the correct default value.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Tests\Update;
|
||||
|
||||
use Drupal\system\Tests\Update\UpdatePathTestBase;
|
||||
|
||||
/**
|
||||
* Tests the update for the entity reference 'handler' setting.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class EntityReferenceHandlerSettingUpdateTest extends UpdatePathTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setDatabaseDumpFiles() {
|
||||
$this->databaseDumpFiles = [
|
||||
__DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests field_post_update_entity_reference_handler_setting().
|
||||
*
|
||||
* @see field_post_update_entity_reference_handler_setting()
|
||||
*/
|
||||
public function testFieldPostUpdateERHandlerSetting() {
|
||||
$configFactory = $this->container->get('config.factory');
|
||||
|
||||
// Load the 'node.article.field_image' field config, and check that its
|
||||
// 'handler' setting is wrong.
|
||||
/** @var \Drupal\Core\Config\Config */
|
||||
$config = $configFactory->get('field.field.node.article.field_image');
|
||||
$settings = $config->get('settings');
|
||||
$this->assertEqual($settings['handler'], 'default:node');
|
||||
|
||||
// Run updates.
|
||||
$this->runUpdates();
|
||||
|
||||
// Reload the config, and check that the 'handler' setting has been fixed.
|
||||
$config = $configFactory->get('field.field.node.article.field_image');
|
||||
$settings = $config->get('settings');
|
||||
$this->assertEqual($settings['handler'], 'default:file');
|
||||
}
|
||||
|
||||
}
|
144
web/core/modules/field/src/Tests/Update/FieldUpdateTest.php
Normal file
144
web/core/modules/field/src/Tests/Update/FieldUpdateTest.php
Normal file
|
@ -0,0 +1,144 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Tests\Update;
|
||||
|
||||
use Drupal\Core\Config\Config;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\system\Tests\Update\UpdatePathTestBase;
|
||||
|
||||
/**
|
||||
* Tests that field settings are properly updated during database updates.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class FieldUpdateTest extends UpdatePathTestBase {
|
||||
|
||||
/**
|
||||
* The config factory service.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigFactoryInterface
|
||||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->configFactory = $this->container->get('config.factory');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setDatabaseDumpFiles() {
|
||||
$this->databaseDumpFiles = [
|
||||
__DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
|
||||
__DIR__ . '/../../../tests/fixtures/update/drupal-8.views_entity_reference_plugins-2429191.php',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests field_update_8001().
|
||||
*
|
||||
* @see field_update_8001()
|
||||
*/
|
||||
public function testFieldUpdate8001() {
|
||||
// Load the 'node.field_image' field storage config, and check that is has
|
||||
// a 'target_bundle' setting.
|
||||
$config = $this->configFactory->get('field.storage.node.field_image');
|
||||
$settings = $config->get('settings');
|
||||
$this->assertTrue(array_key_exists('target_bundle', $settings));
|
||||
|
||||
// Run updates.
|
||||
$this->runUpdates();
|
||||
|
||||
// Reload the config, and check that the 'target_bundle' setting has been
|
||||
// removed.
|
||||
$config = $this->configFactory->get('field.storage.node.field_image');
|
||||
$settings = $config->get('settings');
|
||||
$this->assertFalse(array_key_exists('target_bundle', $settings));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests field_update_8002().
|
||||
*
|
||||
* @see field_update_8002()
|
||||
*/
|
||||
public function testFieldUpdate8002() {
|
||||
// Check that 'entity_reference' is the provider and a dependency of the
|
||||
// test field storage .
|
||||
$field_storage = $this->configFactory->get('field.storage.node.field_ref_views_select_2429191');
|
||||
$this->assertIdentical($field_storage->get('module'), 'entity_reference');
|
||||
$this->assertEntityRefDependency($field_storage, TRUE);
|
||||
|
||||
// Check that 'entity_reference' is a dependency of the test field.
|
||||
$field = $this->configFactory->get('field.field.node.article.field_ref_views_select_2429191');
|
||||
$this->assertEntityRefDependency($field, TRUE);
|
||||
|
||||
// Check that 'entity_reference' is a dependency of the test view.
|
||||
$view = $this->configFactory->get('views.view.entity_reference_plugins_2429191');
|
||||
$this->assertEntityRefDependency($view, TRUE);
|
||||
|
||||
// Run updates.
|
||||
$this->runUpdates();
|
||||
|
||||
// Check that 'entity_reference' is no longer a dependency of the test field
|
||||
// and view.
|
||||
$field_storage = $this->configFactory->get('field.storage.node.field_ref_views_select_2429191');
|
||||
$this->assertIdentical($field_storage->get('module'), 'core');
|
||||
$this->assertEntityRefDependency($field_storage, FALSE);
|
||||
$field = $this->configFactory->get('field.field.node.article.field_ref_views_select_2429191');
|
||||
$this->assertEntityRefDependency($field, FALSE);
|
||||
$view = $this->configFactory->get('views.view.entity_reference_plugins_2429191');
|
||||
$this->assertEntityRefDependency($view, FALSE);
|
||||
|
||||
// Check that field selection, based on the view, still works. It only
|
||||
// selects nodes whose title contains 'foo'.
|
||||
$node_1 = Node::create(['type' => 'article', 'title' => 'foobar']);
|
||||
$node_1->save();
|
||||
$node_2 = Node::create(['type' => 'article', 'title' => 'barbaz']);
|
||||
$node_2->save();
|
||||
$field = FieldConfig::load('node.article.field_ref_views_select_2429191');
|
||||
$selection = \Drupal::service('plugin.manager.entity_reference_selection')->getSelectionHandler($field);
|
||||
$referencable = $selection->getReferenceableEntities();
|
||||
$this->assertEqual(array_keys($referencable['article']), [$node_1->id()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests field_update_8003().
|
||||
*
|
||||
* @see field_update_8003()
|
||||
*/
|
||||
public function testFieldUpdate8003() {
|
||||
// Run updates.
|
||||
$this->runUpdates();
|
||||
|
||||
// Check that the new 'auto_create_bundle' setting is populated correctly.
|
||||
$field = $this->configFactory->get('field.field.node.article.field_ref_autocreate_2412569');
|
||||
$handler_settings = $field->get('settings.handler_settings');
|
||||
|
||||
$expected_target_bundles = ['tags' => 'tags', 'test' => 'test'];
|
||||
$this->assertEqual($handler_settings['target_bundles'], $expected_target_bundles);
|
||||
|
||||
$this->assertTrue($handler_settings['auto_create']);
|
||||
$this->assertEqual($handler_settings['auto_create_bundle'], 'tags');
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a config depends on 'entity_reference' or not
|
||||
*
|
||||
* @param \Drupal\Core\Config\Config $config
|
||||
* The config to test.
|
||||
* @param bool $present
|
||||
* TRUE to test that entity_reference is present, FALSE to test that it is
|
||||
* absent.
|
||||
*/
|
||||
protected function assertEntityRefDependency(Config $config, $present) {
|
||||
$dependencies = $config->get('dependencies');
|
||||
$dependencies += ['module' => []];
|
||||
$this->assertEqual(in_array('entity_reference', $dependencies['module']), $present);
|
||||
}
|
||||
|
||||
}
|
82
web/core/modules/field/src/Tests/Views/FieldTestBase.php
Normal file
82
web/core/modules/field/src/Tests/Views/FieldTestBase.php
Normal file
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Tests\Views;
|
||||
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\views\Tests\ViewTestBase;
|
||||
use Drupal\views\Tests\ViewTestData;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Provides some helper methods for testing fieldapi integration into views.
|
||||
*
|
||||
* @todo Test on a generic entity not on a node. What has to be tested:
|
||||
* - Make sure that every wanted field is added to the according entity type.
|
||||
* - Make sure the joins are done correctly.
|
||||
* - Use basic fields and make sure that the full wanted object is built.
|
||||
* - Use relationships between different entity types, for example node and
|
||||
* the node author(user).
|
||||
*/
|
||||
abstract class FieldTestBase extends ViewTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'field_test_views');
|
||||
|
||||
/**
|
||||
* Stores the field definitions used by the test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $fieldStorages;
|
||||
|
||||
/**
|
||||
* Stores the fields of the field storage. They have the same keys as the
|
||||
* field storages.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $fields;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Ensure the page node type exists.
|
||||
NodeType::create([
|
||||
'type' => 'page',
|
||||
'name' => 'page',
|
||||
])->save();
|
||||
|
||||
ViewTestData::createTestViews(get_class($this), array('field_test_views'));
|
||||
}
|
||||
|
||||
function setUpFieldStorages($amount = 3, $type = 'string') {
|
||||
// Create three fields.
|
||||
$field_names = array();
|
||||
for ($i = 0; $i < $amount; $i++) {
|
||||
$field_names[$i] = 'field_name_' . $i;
|
||||
$this->fieldStorages[$i] = FieldStorageConfig::create(array(
|
||||
'field_name' => $field_names[$i],
|
||||
'entity_type' => 'node',
|
||||
'type' => $type,
|
||||
));
|
||||
$this->fieldStorages[$i]->save();
|
||||
}
|
||||
return $field_names;
|
||||
}
|
||||
|
||||
function setUpFields($bundle = 'page') {
|
||||
foreach ($this->fieldStorages as $key => $field_storage) {
|
||||
$this->fields[$key] = FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => $bundle,
|
||||
]);
|
||||
$this->fields[$key]->save();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
161
web/core/modules/field/src/Tests/Views/FieldUITest.php
Normal file
161
web/core/modules/field/src/Tests/Views/FieldUITest.php
Normal file
|
@ -0,0 +1,161 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Tests\Views;
|
||||
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests the UI of the field field handler.
|
||||
*
|
||||
* @group field
|
||||
* @see \Drupal\field\Plugin\views\field\Field
|
||||
*/
|
||||
class FieldUITest extends FieldTestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_view_fieldapi');
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('views_ui');
|
||||
|
||||
/**
|
||||
* A user with the 'administer views' permission.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $account;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->account = $this->drupalCreateUser(array('administer views'));
|
||||
$this->drupalLogin($this->account);
|
||||
|
||||
$this->setUpFieldStorages(1, 'text');
|
||||
$this->setUpFields();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests basic field handler settings in the UI.
|
||||
*/
|
||||
public function testHandlerUI() {
|
||||
$url = "admin/structure/views/nojs/handler/test_view_fieldapi/default/field/field_name_0";
|
||||
$this->drupalGet($url);
|
||||
|
||||
// Tests the available formatter options.
|
||||
$result = $this->xpath('//select[@id=:id]/option', array(':id' => 'edit-options-type'));
|
||||
$options = array_map(function($item) {
|
||||
return (string) $item->attributes()->value[0];
|
||||
}, $result);
|
||||
// @todo Replace this sort by assertArray once it's in.
|
||||
sort($options, SORT_STRING);
|
||||
$this->assertEqual($options, array('text_default', 'text_trimmed'), 'The text formatters for a simple text field appear as expected.');
|
||||
|
||||
$this->drupalPostForm(NULL, array('options[type]' => 'text_trimmed'), t('Apply'));
|
||||
|
||||
$this->drupalGet($url);
|
||||
$this->assertOptionSelected('edit-options-type', 'text_trimmed');
|
||||
|
||||
$random_number = rand(100, 400);
|
||||
$this->drupalPostForm(NULL, array('options[settings][trim_length]' => $random_number), t('Apply'));
|
||||
$this->drupalGet($url);
|
||||
$this->assertFieldByName('options[settings][trim_length]', $random_number, 'The formatter setting got saved.');
|
||||
|
||||
// Save the view and test whether the settings are saved.
|
||||
$this->drupalPostForm('admin/structure/views/view/test_view_fieldapi', array(), t('Save'));
|
||||
$view = Views::getView('test_view_fieldapi');
|
||||
$view->initHandlers();
|
||||
$this->assertEqual($view->field['field_name_0']->options['type'], 'text_trimmed');
|
||||
$this->assertEqual($view->field['field_name_0']->options['settings']['trim_length'], $random_number);
|
||||
|
||||
// Now change the formatter back to 'default' which doesn't have any
|
||||
// settings. We want to ensure that the settings are empty then.
|
||||
$edit['options[type]'] = 'text_default';
|
||||
$this->drupalPostForm('admin/structure/views/nojs/handler/test_view_fieldapi/default/field/field_name_0', $edit, t('Apply'));
|
||||
$this->drupalPostForm('admin/structure/views/view/test_view_fieldapi', [], t('Save'));
|
||||
$view = Views::getView('test_view_fieldapi');
|
||||
$view->initHandlers();
|
||||
$this->assertEqual($view->field['field_name_0']->options['type'], 'text_default');
|
||||
$this->assertEqual($view->field['field_name_0']->options['settings'], []);
|
||||
|
||||
// Ensure that the view depends on the field storage.
|
||||
$dependencies = \Drupal::service('config.manager')->findConfigEntityDependents('config', [$this->fieldStorages[0]->getConfigDependencyName()]);
|
||||
$this->assertTrue(isset($dependencies['views.view.test_view_fieldapi']), 'The view is dependent on the field storage.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the basic field handler form when aggregation is enabled.
|
||||
*/
|
||||
public function testHandlerUIAggregation() {
|
||||
// Enable aggregation.
|
||||
$edit = array('group_by' => '1');
|
||||
$this->drupalPostForm('admin/structure/views/nojs/display/test_view_fieldapi/default/group_by', $edit, t('Apply'));
|
||||
|
||||
$url = "admin/structure/views/nojs/handler/test_view_fieldapi/default/field/field_name_0";
|
||||
$this->drupalGet($url);
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Test the click sort column options.
|
||||
// Tests the available formatter options.
|
||||
$result = $this->xpath('//select[@id=:id]/option', array(':id' => 'edit-options-click-sort-column'));
|
||||
$options = array_map(function($item) {
|
||||
return (string) $item->attributes()->value[0];
|
||||
}, $result);
|
||||
sort($options, SORT_STRING);
|
||||
|
||||
$this->assertEqual($options, array('format', 'value'), 'The expected sort field options were found.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests adding a boolean field filter handler.
|
||||
*/
|
||||
public function testBooleanFilterHandler() {
|
||||
// Create a boolean field.
|
||||
$field_name = 'field_boolean';
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'node',
|
||||
'type' => 'boolean',
|
||||
]);
|
||||
$field_storage->save();
|
||||
$field = FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'page',
|
||||
]);
|
||||
$field->save();
|
||||
|
||||
$url = "admin/structure/views/nojs/add-handler/test_view_fieldapi/default/filter";
|
||||
$this->drupalPostForm($url, ['name[node__' . $field_name . '.' . $field_name . '_value]' => TRUE], t('Add and configure @handler', array('@handler' => t('filter criteria'))));
|
||||
$this->assertResponse(200);
|
||||
// Verify that using a boolean field as a filter also results in using the
|
||||
// boolean plugin.
|
||||
$option = $this->xpath('//label[@for="edit-options-value-1"]');
|
||||
$this->assertEqual(t('True'), (string) $option[0]);
|
||||
$option = $this->xpath('//label[@for="edit-options-value-0"]');
|
||||
$this->assertEqual(t('False'), (string) $option[0]);
|
||||
|
||||
// Expose the filter and see if the 'Any' option is added and if we can save
|
||||
// it.
|
||||
$this->drupalPostForm(NULL, [], 'Expose filter');
|
||||
$option = $this->xpath('//label[@for="edit-options-value-all"]');
|
||||
$this->assertEqual(t('- Any -'), (string) $option[0]);
|
||||
$this->drupalPostForm(NULL, ['options[value]' => 'All', 'options[expose][required]' => FALSE], 'Apply');
|
||||
$this->drupalPostForm(NULL, [], 'Save');
|
||||
$this->drupalGet('/admin/structure/views/nojs/handler/test_view_fieldapi/default/filter/field_boolean_value');
|
||||
$this->assertFieldChecked('edit-options-value-all');
|
||||
}
|
||||
|
||||
}
|
310
web/core/modules/field/src/Tests/Views/HandlerFieldFieldTest.php
Normal file
310
web/core/modules/field/src/Tests/Views/HandlerFieldFieldTest.php
Normal file
|
@ -0,0 +1,310 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Tests\Views;
|
||||
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\views\ViewExecutable;
|
||||
use Drupal\views\Views;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Tests the field itself of the Field integration.
|
||||
*
|
||||
* @group field
|
||||
* @TODO
|
||||
* Check a entity-type with bundles
|
||||
* Check a entity-type without bundles
|
||||
* Check locale:disabled, locale:enabled and locale:enabled with another language
|
||||
* Check revisions
|
||||
*/
|
||||
class HandlerFieldFieldTest extends FieldTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = array('node', 'field_test');
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_view_fieldapi');
|
||||
|
||||
/**
|
||||
* Test nodes.
|
||||
*
|
||||
* @var \Drupal\node\NodeInterface[]
|
||||
*/
|
||||
public $nodes;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Setup basic fields.
|
||||
$this->setUpFieldStorages(3);
|
||||
|
||||
// Setup a field with cardinality > 1.
|
||||
$this->fieldStorages[3] = FieldStorageConfig::create(array(
|
||||
'field_name' => 'field_name_3',
|
||||
'entity_type' => 'node',
|
||||
'type' => 'string',
|
||||
'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
|
||||
));
|
||||
$this->fieldStorages[3]->save();
|
||||
// Setup a field that will have no value.
|
||||
$this->fieldStorages[4] = FieldStorageConfig::create(array(
|
||||
'field_name' => 'field_name_4',
|
||||
'entity_type' => 'node',
|
||||
'type' => 'string',
|
||||
'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
|
||||
));
|
||||
$this->fieldStorages[4]->save();
|
||||
|
||||
// Setup a text field.
|
||||
$this->fieldStorages[5] = FieldStorageConfig::create(array(
|
||||
'field_name' => 'field_name_5',
|
||||
'entity_type' => 'node',
|
||||
'type' => 'text',
|
||||
));
|
||||
$this->fieldStorages[5]->save();
|
||||
|
||||
// Setup a text field with access control.
|
||||
// @see field_test_entity_field_access()
|
||||
$this->fieldStorages[6] = FieldStorageConfig::create(array(
|
||||
'field_name' => 'field_no_view_access',
|
||||
'entity_type' => 'node',
|
||||
'type' => 'text',
|
||||
));
|
||||
$this->fieldStorages[6]->save();
|
||||
|
||||
$this->setUpFields();
|
||||
|
||||
// Create some nodes.
|
||||
$this->nodes = array();
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$edit = array('type' => 'page');
|
||||
|
||||
foreach (array(0, 1, 2, 5) as $key) {
|
||||
$field_storage = $this->fieldStorages[$key];
|
||||
$edit[$field_storage->getName()][0]['value'] = $this->randomMachineName(8);
|
||||
}
|
||||
// Add a hidden value for the no-view field.
|
||||
$edit[$this->fieldStorages[6]->getName()][0]['value'] = 'ssh secret squirrel';
|
||||
for ($j = 0; $j < 5; $j++) {
|
||||
$edit[$this->fieldStorages[3]->getName()][$j]['value'] = $this->randomMachineName(8);
|
||||
}
|
||||
// Set this field to be empty.
|
||||
$edit[$this->fieldStorages[4]->getName()] = array(array('value' => NULL));
|
||||
|
||||
$this->nodes[$i] = $this->drupalCreateNode($edit);
|
||||
}
|
||||
|
||||
$this->container->get('views.views_data')->clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the testing view with random field data.
|
||||
*
|
||||
* @param \Drupal\views\ViewExecutable $view
|
||||
* The view to add field data to.
|
||||
*/
|
||||
protected function prepareView(ViewExecutable $view) {
|
||||
$view->storage->invalidateCaches();
|
||||
$view->initDisplay();
|
||||
foreach ($this->fieldStorages as $field_storage) {
|
||||
$field_name = $field_storage->getName();
|
||||
$view->display_handler->options['fields'][$field_name]['id'] = $field_name;
|
||||
$view->display_handler->options['fields'][$field_name]['table'] = 'node__' . $field_name;
|
||||
$view->display_handler->options['fields'][$field_name]['field'] = $field_name;
|
||||
}
|
||||
}
|
||||
|
||||
public function testFieldRender() {
|
||||
$this->_testSimpleFieldRender();
|
||||
$this->_testInaccessibleFieldRender();
|
||||
$this->_testFormatterSimpleFieldRender();
|
||||
$this->_testMultipleFieldRender();
|
||||
}
|
||||
|
||||
public function _testSimpleFieldRender() {
|
||||
$view = Views::getView('test_view_fieldapi');
|
||||
$this->prepareView($view);
|
||||
$this->executeView($view);
|
||||
|
||||
// Tests that the rendered fields match the actual value of the fields.
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
for ($key = 0; $key < 2; $key++) {
|
||||
$field_name = $this->fieldStorages[$key]->getName();
|
||||
$rendered_field = $view->style_plugin->getField($i, $field_name);
|
||||
$expected_field = $this->nodes[$i]->$field_name->value;
|
||||
$this->assertEqual($rendered_field, $expected_field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function _testInaccessibleFieldRender() {
|
||||
$view = Views::getView('test_view_fieldapi');
|
||||
$this->prepareView($view);
|
||||
$this->executeView($view);
|
||||
|
||||
// Check that the field handler for the hidden field is correctly removed
|
||||
// from the display.
|
||||
// @see https://www.drupal.org/node/2382931
|
||||
$this->assertFalse(array_key_exists('field_no_view_access', $view->field));
|
||||
|
||||
// Check that the access-denied field is not visible.
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$field_name = $this->fieldStorages[6]->getName();
|
||||
$rendered_field = $view->style_plugin->getField($i, $field_name);
|
||||
$this->assertFalse($rendered_field, 'Hidden field not rendered');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that fields with formatters runs as expected.
|
||||
*/
|
||||
public function _testFormatterSimpleFieldRender() {
|
||||
$view = Views::getView('test_view_fieldapi');
|
||||
$this->prepareView($view);
|
||||
$view->displayHandlers->get('default')->options['fields'][$this->fieldStorages[5]->getName()]['type'] = 'text_trimmed';
|
||||
$view->displayHandlers->get('default')->options['fields'][$this->fieldStorages[5]->getName()]['settings'] = array(
|
||||
'trim_length' => 3,
|
||||
);
|
||||
$this->executeView($view);
|
||||
|
||||
// Make sure that the formatter works as expected.
|
||||
// @TODO: actually there should be a specific formatter.
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$rendered_field = $view->style_plugin->getField($i, $this->fieldStorages[5]->getName());
|
||||
$this->assertEqual(strlen(html_entity_decode($rendered_field)), 3);
|
||||
}
|
||||
}
|
||||
|
||||
public function _testMultipleFieldRender() {
|
||||
$view = Views::getView('test_view_fieldapi');
|
||||
$field_name = $this->fieldStorages[3]->getName();
|
||||
|
||||
// Test delta limit.
|
||||
$this->prepareView($view);
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['group_rows'] = TRUE;
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['delta_limit'] = 3;
|
||||
$this->executeView($view);
|
||||
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$rendered_field = $view->style_plugin->getField($i, $field_name);
|
||||
$items = array();
|
||||
$pure_items = $this->nodes[$i]->{$field_name}->getValue();
|
||||
$pure_items = array_splice($pure_items, 0, 3);
|
||||
foreach ($pure_items as $j => $item) {
|
||||
$items[] = $pure_items[$j]['value'];
|
||||
}
|
||||
$this->assertEqual($rendered_field, implode(', ', $items), 'The amount of items is limited.');
|
||||
}
|
||||
|
||||
// Test that an empty field is rendered without error.
|
||||
$view->style_plugin->getField(4, $this->fieldStorages[4]->getName());
|
||||
$view->destroy();
|
||||
|
||||
// Test delta limit + offset
|
||||
$this->prepareView($view);
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['group_rows'] = TRUE;
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['delta_limit'] = 3;
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['delta_offset'] = 1;
|
||||
$this->executeView($view);
|
||||
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$rendered_field = $view->style_plugin->getField($i, $field_name);
|
||||
$items = array();
|
||||
$pure_items = $this->nodes[$i]->{$field_name}->getValue();
|
||||
$pure_items = array_splice($pure_items, 1, 3);
|
||||
foreach ($pure_items as $j => $item) {
|
||||
$items[] = $pure_items[$j]['value'];
|
||||
}
|
||||
$this->assertEqual($rendered_field, implode(', ', $items), 'The amount of items is limited and the offset is correct.');
|
||||
}
|
||||
$view->destroy();
|
||||
|
||||
// Test delta limit + reverse.
|
||||
$this->prepareView($view);
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['delta_offset'] = 0;
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['group_rows'] = TRUE;
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['delta_limit'] = 3;
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['delta_reversed'] = TRUE;
|
||||
$this->executeView($view);
|
||||
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$rendered_field = $view->style_plugin->getField($i, $field_name);
|
||||
$items = array();
|
||||
$pure_items = $this->nodes[$i]->{$field_name}->getValue();
|
||||
array_splice($pure_items, 0, -3);
|
||||
$pure_items = array_reverse($pure_items);
|
||||
foreach ($pure_items as $j => $item) {
|
||||
$items[] = $pure_items[$j]['value'];
|
||||
}
|
||||
$this->assertEqual($rendered_field, implode(', ', $items), 'The amount of items is limited and they are reversed.');
|
||||
}
|
||||
$view->destroy();
|
||||
|
||||
// Test delta first last.
|
||||
$this->prepareView($view);
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['group_rows'] = TRUE;
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['delta_limit'] = 0;
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['delta_first_last'] = TRUE;
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['delta_reversed'] = FALSE;
|
||||
$this->executeView($view);
|
||||
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$rendered_field = $view->style_plugin->getField($i, $field_name);
|
||||
$items = array();
|
||||
$pure_items = $this->nodes[$i]->{$field_name}->getValue();
|
||||
$items[] = $pure_items[0]['value'];
|
||||
$items[] = $pure_items[4]['value'];
|
||||
$this->assertEqual($rendered_field, implode(', ', $items), 'Items are limited to first and last.');
|
||||
}
|
||||
$view->destroy();
|
||||
|
||||
// Test delta limit + custom separator.
|
||||
$this->prepareView($view);
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['delta_first_last'] = FALSE;
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['delta_limit'] = 3;
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['group_rows'] = TRUE;
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['separator'] = ':';
|
||||
$this->executeView($view);
|
||||
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$rendered_field = $view->style_plugin->getField($i, $field_name);
|
||||
$items = array();
|
||||
$pure_items = $this->nodes[$i]->{$field_name}->getValue();
|
||||
$pure_items = array_splice($pure_items, 0, 3);
|
||||
foreach ($pure_items as $j => $item) {
|
||||
$items[] = $pure_items[$j]['value'];
|
||||
}
|
||||
$this->assertEqual($rendered_field, implode(':', $items), 'The amount of items is limited and the custom separator is correct.');
|
||||
}
|
||||
$view->destroy();
|
||||
|
||||
// Test separator with HTML, ensure it is escaped.
|
||||
$this->prepareView($view);
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['group_rows'] = TRUE;
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['delta_limit'] = 3;
|
||||
$view->displayHandlers->get('default')->options['fields'][$field_name]['separator'] = '<h2>test</h2>';
|
||||
$this->executeView($view);
|
||||
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$rendered_field = $view->style_plugin->getField($i, $field_name);
|
||||
$items = [];
|
||||
$pure_items = $this->nodes[$i]->{$field_name}->getValue();
|
||||
$pure_items = array_splice($pure_items, 0, 3);
|
||||
foreach ($pure_items as $j => $item) {
|
||||
$items[] = $pure_items[$j]['value'];
|
||||
}
|
||||
$this->assertEqual($rendered_field, implode('<h2>test</h2>', $items), 'The custom separator is correctly escaped.');
|
||||
}
|
||||
$view->destroy();
|
||||
}
|
||||
|
||||
}
|
124
web/core/modules/field/src/Tests/reEnableModuleFieldTest.php
Normal file
124
web/core/modules/field/src/Tests/reEnableModuleFieldTest.php
Normal file
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\field\Tests;
|
||||
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Tests the behavior of a field module after being disabled and re-enabled.
|
||||
*
|
||||
* @group field
|
||||
*/
|
||||
class reEnableModuleFieldTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array(
|
||||
'field',
|
||||
'node',
|
||||
// We use telephone module instead of test_field because test_field is
|
||||
// hidden and does not display on the admin/modules page.
|
||||
'telephone'
|
||||
);
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalCreateContentType(array('type' => 'article'));
|
||||
$this->drupalLogin($this->drupalCreateUser(array(
|
||||
'create article content',
|
||||
'edit own article content',
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the behavior of a field module after being disabled and re-enabled.
|
||||
*
|
||||
* @see field_system_info_alter()
|
||||
*/
|
||||
function testReEnabledField() {
|
||||
|
||||
// Add a telephone field to the article content type.
|
||||
$field_storage = FieldStorageConfig::create(array(
|
||||
'field_name' => 'field_telephone',
|
||||
'entity_type' => 'node',
|
||||
'type' => 'telephone',
|
||||
));
|
||||
$field_storage->save();
|
||||
FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'article',
|
||||
'label' => 'Telephone Number',
|
||||
])->save();
|
||||
|
||||
entity_get_form_display('node', 'article', 'default')
|
||||
->setComponent('field_telephone', array(
|
||||
'type' => 'telephone_default',
|
||||
'settings' => array(
|
||||
'placeholder' => '123-456-7890',
|
||||
),
|
||||
))
|
||||
->save();
|
||||
|
||||
entity_get_display('node', 'article', 'default')
|
||||
->setComponent('field_telephone', array(
|
||||
'type' => 'telephone_link',
|
||||
'weight' => 1,
|
||||
))
|
||||
->save();
|
||||
|
||||
// Display the article node form and verify the telephone widget is present.
|
||||
$this->drupalGet('node/add/article');
|
||||
$this->assertFieldByName("field_telephone[0][value]", '', 'Widget found.');
|
||||
|
||||
// Submit an article node with a telephone field so data exist for the
|
||||
// field.
|
||||
$edit = array(
|
||||
'title[0][value]' => $this->randomMachineName(),
|
||||
'field_telephone[0][value]' => "123456789",
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->assertRaw('<a href="tel:123456789">');
|
||||
|
||||
// Test that the module can't be uninstalled from the UI while there is data
|
||||
// for it's fields.
|
||||
$admin_user = $this->drupalCreateUser(array('access administration pages', 'administer modules'));
|
||||
$this->drupalLogin($admin_user);
|
||||
$this->drupalGet('admin/modules/uninstall');
|
||||
$this->assertText("The Telephone number field type is used in the following field: node.field_telephone");
|
||||
|
||||
// Add another telephone field to a different entity type in order to test
|
||||
// the message for the case when multiple fields are blocking the
|
||||
// uninstallation of a module.
|
||||
$field_storage2 = entity_create('field_storage_config', array(
|
||||
'field_name' => 'field_telephone_2',
|
||||
'entity_type' => 'user',
|
||||
'type' => 'telephone',
|
||||
));
|
||||
$field_storage2->save();
|
||||
FieldConfig::create([
|
||||
'field_storage' => $field_storage2,
|
||||
'bundle' => 'user',
|
||||
'label' => 'User Telephone Number',
|
||||
])->save();
|
||||
|
||||
$this->drupalGet('admin/modules/uninstall');
|
||||
$this->assertText("The Telephone number field type is used in the following fields: node.field_telephone, user.field_telephone_2");
|
||||
|
||||
// Delete both fields.
|
||||
$field_storage->delete();
|
||||
$field_storage2->delete();
|
||||
|
||||
$this->drupalGet('admin/modules/uninstall');
|
||||
$this->assertText('Fields pending deletion');
|
||||
$this->cronRun();
|
||||
$this->assertNoText("The Telephone number field type is used in the following field: node.field_telephone");
|
||||
$this->assertNoText('Fields pending deletion');
|
||||
}
|
||||
|
||||
}
|
Reference in a new issue