Commit d72d4c5f authored by RoSk0's avatar RoSk0

Issue #2928466 by RoSk0: Remove email_with_type field type from CRM Core

parent f0d5d80e
......@@ -45,39 +45,3 @@ crm_core_contact.organization_type.*:
sequence:
type: string
label: 'Field'
field.widget.settings.email_with_type:
type: mapping
label: 'Mail with type field display format settings'
mapping:
placeholder:
type: label
label: 'Placeholder'
size:
type: integer
label: 'Size of email field'
field.field_settings.email_with_type:
type: mapping
label: 'Mail with type settings'
field.value.email_with_type:
type: mapping
label: 'Default value and mail type'
mapping:
value:
type: string
label: 'Value'
type:
type: string
label: 'Mail type'
field.storage_settings.email_with_type:
type: mapping
label: 'Email with type settings'
mapping:
email_types:
type: sequence
label: 'Email types'
sequence:
type: string
<?php
namespace Drupal\crm_core_contact\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Url;
/**
* Plugin implementation of the 'mail_with_type' formatter.
*
* @FieldFormatter(
* id = "email_with_type",
* label = @Translation("Email with type as plain text"),
* field_types = {
* "email_with_type",
* },
* quickedit = {
* "editor" = "plain_text"
* }
* )
*/
class MailWithTypeFormatter extends FormatterBase {
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items, $langcode) {
$email_types = $this->fieldDefinition->getFieldStorageDefinition()->getSetting('email_types');
$elements = [];
foreach ($items as $delta => $item) {
$elements[$delta] = [
'#type' => 'link',
'#title' => $item->value,
'#url' => Url::fromUri('mailto:' . $item->value),
'#prefix' => $email_types[$item->type] . ': ',
];
}
return $elements;
}
}
<?php
namespace Drupal\crm_core_contact\Plugin\Field\FieldType;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Field\Plugin\Field\FieldType\EmailItem;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\TypedData\DataDefinition;
/**
* Defines the 'mail_with_type' field type.
*
* @FieldType(
* id = "email_with_type",
* label = @Translation("Email with type"),
* description = @Translation("An entity field containing an email and type values."),
* default_widget = "email_with_type",
* default_formatter = "email_with_type"
* )
*/
class MailWithTypeItem extends EmailItem {
/**
* {@inheritdoc}
*/
public static function defaultStorageSettings() {
return [
'email_types' => [],
];
}
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties = parent::propertyDefinitions($field_definition);
$properties['type'] = DataDefinition::create('string')
->setLabel(t('Mail type'))
->setRequired(TRUE);
return $properties;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
$schema = parent::schema($field_definition);
$schema['columns']['type'] = [
'type' => 'varchar_ascii',
'length' => 32,
];
return $schema;
}
/**
* {@inheritdoc}
*/
public function storageSettingsForm(array &$form, FormStateInterface $form_state, $has_data) {
$email_types = $this->getSetting('email_types');
$element['email_types'] = [
'#type' => 'textarea',
'#title' => t('Email types'),
'#default_value' => $this->emailTypesString($email_types),
'#element_validate' => [[get_class($this), 'validateEmailTypes']],
'#rows' => 3,
'#description' => $this->t('Enter allowed email types in a "key|label" format.'),
'#field_has_data' => $has_data,
'#field_name' => $this->getFieldDefinition()->getName(),
'#entity_type' => $this->getEntity()->getEntityTypeId(),
'#email_types' => $email_types,
];
return $element;
}
/**
* Generates a string representation of an array of 'email_types'.
*
* This string format is suitable for edition in a textarea.
*
* @param array $values
* An array of values, where array keys are values and array values are
* labels.
*
* @return string
* The string representation of the $values array:
* - Values are separated by a carriage return.
* - Each value is in the format "value|label" or "value".
*/
protected function emailTypesString($values) {
$lines = [];
foreach ($values as $key => $value) {
$lines[] = "$key|$value";
}
return implode("\n", $lines);
}
/**
* #element_validate callback for possible mail types.
*/
public static function validateMailTypes($element, FormStateInterface $form_state) {
$values = static::extractMailTypes($element['#value']);
if (!is_array($values)) {
$form_state->setError($element, t('Email types list: invalid input.'));
}
else {
// Check that keys are valid for the field type.
foreach ($values as $key => $value) {
if (Unicode::strlen($key) > 32) {
$form_state->setError($element, 'Email types list: each key must be a string at most 32 characters long.');
break;
}
if (preg_match('/[^a-z0-9]/', $key)) {
$form_state->setError($element, 'Email types list: only international alphanumeric characters are allowed for keys.');
break;
}
}
// Prevent removing values currently in use.
if ($element['#field_has_data']) {
$lost_keys = array_keys(array_diff_key($element['#email_types'], $values));
if (static::mailTypeInUse($element['#entity_type'], $element['#field_name'], $lost_keys)) {
$form_state->setError($element, t('Email types list: some values are being removed while currently in use.'));
}
}
$form_state->setValueForElement($element, $values);
}
}
/**
* Extracts the allowed values array from the mail_types element.
*
* @param string $string
* The raw string to extract values from.
*
* @return array|null
* The array of extracted key/value pairs, or NULL if the string is invalid.
*/
protected static function extractMailTypes($string) {
$values = [];
$list = explode("\n", $string);
$list = array_map('trim', $list);
$list = array_filter($list, 'strlen');
foreach ($list as $position => $text) {
// Check for an explicit key.
$matches = [];
if (preg_match('/(.*)\|(.*)/', $text, $matches)) {
// Trim key and value to avoid unwanted spaces issues.
$key = trim($matches[1]);
$value = trim($matches[2]);
}
else {
return;
}
$values[$key] = $value;
}
return $values;
}
/**
* Checks if any of the mail types are in use.
*
* @param string $entity_type
* ID of the entity type field belongs to.
* @param string $field_name
* Name of the field.
* @param array $email_types
* Array of mail type keys to check against.
*
* @return bool
*/
protected static function mailTypeInUse($entity_type, $field_name, $email_types) {
if ($email_types) {
$factory = \Drupal::service('entity.query');
$result = $factory->get($entity_type)
->condition($field_name . '.type', $email_types, 'IN')
->count()
->accessCheck(FALSE)
->range(0, 1)
->execute();
if ($result) {
return TRUE;
}
}
return FALSE;
}
}
<?php
namespace Drupal\crm_core_contact\Plugin\Field\FieldWidget;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\Plugin\Field\FieldWidget\EmailDefaultWidget;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element\Email;
/**
* Plugin implementation of the 'mail_type' widget.
*
* @FieldWidget(
* id = "email_with_type",
* label = @Translation("Email with type"),
* field_types = {
* "email_with_type"
* },
* multiple_values = TRUE
* )
*/
class MailWithTypeWidget extends EmailDefaultWidget {
/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$element['#type'] = 'details';
$element['#open'] = TRUE;
$element['type'] = [
'#type' => 'select',
'#title' => $this->t('Type'),
'#default_value' => isset($items[$delta]->type) ? $items[$delta]->type : NULL,
'#options' => $this->fieldDefinition->getFieldStorageDefinition()->getSetting('email_types'),
];
$element['value'] = [
'#title' => $this->t('Email'),
'#type' => 'email',
'#default_value' => isset($items[$delta]->value) ? $items[$delta]->value : NULL,
'#placeholder' => $this->getSetting('placeholder'),
'#size' => $this->getSetting('size'),
'#maxlength' => Email::EMAIL_MAX_LENGTH,
];
return $element;
}
}
<?php
namespace Drupal\Tests\crm_core_contact\Kernel;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\Core\Form\FormState;
use Drupal\crm_core_contact\Plugin\Field\FieldType\MailWithTypeItem;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests the 'email_with_type' field type.
*
* @group crm_core_contact
*/
class MailWithTypeItemTest extends KernelTestBase {
/**
* Form display.
*
* @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface
*/
protected $formDisplay;
/**
* View display.
*
* @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface
*/
protected $viewDisplay;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = [
'user',
'field',
'entity_test',
'crm_core_contact',
'options',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installEntitySchema('entity_test');
$this->installEntitySchema('user');
// Create a 'mail_with_type' field storage and field for validation.
FieldStorageConfig::create([
'field_name' => 'field_mail_with_type',
'entity_type' => 'entity_test',
'type' => 'email_with_type',
'settings' => [
'email_types' => [
'home' => 'Home',
'work' => 'Work',
]
],
])->save();
FieldConfig::create([
'entity_type' => 'entity_test',
'field_name' => 'field_mail_with_type',
'bundle' => 'entity_test',
'label' => 'Email with type',
])->save();
// Create a form display for the default form mode.
$this->formDisplay = EntityFormDisplay::create([
'targetEntityType' => 'entity_test',
'bundle' => 'entity_test',
'mode' => 'default',
'status' => TRUE,
]);
$this->formDisplay->setComponent(
'field_mail_with_type',
['type' => 'email_with_type']
)->save();
// Create view display for the default view mode.
$this->viewDisplay = EntityViewDisplay::create([
'targetEntityType' => 'entity_test',
'bundle' => 'entity_test',
'mode' => 'default',
'content' => [],
]);
$this->viewDisplay->setComponent(
'field_mail_with_type',
['type' => 'email_with_type']
)->save();
}
/**
* Tests 'mail_with_type' field type.
*/
public function testMailWithTypeItem() {
// Verify entity creation.
$value = 'mail@example.com';
$type = 'work';
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
$entity = EntityTest::create();
$entity->set('field_mail_with_type', ['value' => $value, 'type' => $type]);
$entity->save();
// Verify entity has been created properly.
$entity = $this->container->get('entity_type.manager')
->getStorage('entity_test')
->load($entity->id());
$this->assertEquals($value, $entity->field_mail_with_type->value);
$this->assertEquals($type, $entity->field_mail_with_type->type);
// Verify changing the type and value.
$new_type = 'home';
$new_value = 'test@example.com';
$entity->field_mail_with_type->type = $new_type;
$entity->field_mail_with_type->value = $new_value;
$this->assertEquals($new_type, $entity->field_mail_with_type->type);
$this->assertEquals($new_value, $entity->field_mail_with_type->value);
// Load changed entity and assert changed values.
$entity->save();
$entity = $this->container->get('entity_type.manager')
->getStorage('entity_test')
->load($entity->id());
$this->assertEquals($new_type, $entity->field_mail_with_type->type);
$this->assertEquals($new_value, $entity->field_mail_with_type->value);
// Verify form widget.
$form = [];
$form_state = new FormState();
$this->formDisplay->buildForm($entity, $form, $form_state);
$widget = $form['field_mail_with_type']['widget'];
$this->assertEquals('Email with type', $widget['#title'], 'Widget title is correct.');
$this->assertEquals('Type', $widget['type']['#title'], 'Type element title is correct.');
$this->assertEquals('home', $widget['type']['#default_value'], 'Type default value is correct.');
$this->assertEquals(['home' => 'Home', 'work' => 'Work'], $widget['type']['#options'], 'Type options are correct.');
$this->assertEquals('Email', $widget['value']['#title'], 'Email element title is correct.');
$this->assertEquals('test@example.com', $widget['value']['#default_value'], 'Email default value is correct.');
// Verify formatter.
$build = $this->viewDisplay->build($entity);
$this->container->get('renderer')->renderRoot($build);
$this->assertEquals('Home: <a href="mailto:test@example.com">test@example.com</a>', $build['field_mail_with_type']['#markup'], 'Email displayed correctly.');
// Test storage config form validator.
// Test invalid mail types configuration value.
$element = [
'#value' => "Foo\nwork|Work",
'#field_has_data' => FALSE,
'#entity_type' => 'entity_test',
'#field_name' => 'field_mail_with_type',
'#email_types' => ['home' => 'Home', 'work' => 'Work'],
];
$form_state = $this->getMock('\Drupal\Core\Form\FormState');
$form_state->expects($this->once())
->method('setError')
->with($element, 'Email types list: invalid input.');
MailWithTypeItem::validateMailTypes($element, $form_state);
// Test key that is to long.
$element['#value'] = "homelonglonglonglonglongwaytolong|Home\nwork|Work";
$form_state = $this->getMock('\Drupal\Core\Form\FormState');
$form_state->expects($this->once())
->method('setError')
->with($element, 'Email types list: each key must be a string at most 32 characters long.');
MailWithTypeItem::validateMailTypes($element, $form_state);
// Test key with invalid characters.
$element['#value'] = "domači|Home\nslužbeni|Work";
$form_state = $this->getMock('\Drupal\Core\Form\FormState');
$form_state->expects($this->once())
->method('setError')
->with($element, 'Email types list: only international alphanumeric characters are allowed for keys.');
MailWithTypeItem::validateMailTypes($element, $form_state);
// Test valid list of mail types.
$element['#value'] = "home|Home\nwork|Work";
$element['#field_has_data'] = TRUE;
$form_state = $this->getMock('\Drupal\Core\Form\FormState');
$form_state->expects($this->never())
->method('setError');
MailWithTypeItem::validateMailTypes($element, $form_state);
// Test removal of the key that already exists in the database.
$element['#value'] = "work|Work";
$form_state = $this->getMock('\Drupal\Core\Form\FormState');
$form_state->expects($this->once())
->method('setError')
->with($element, 'Email types list: some values are being removed while currently in use.');
MailWithTypeItem::validateMailTypes($element, $form_state);
// Test MailWithTypeItem::emailTypesString().
$definition = $this->getMock('\Drupal\entity_test\FieldStorageDefinition');
$definition->expects($this->once())
->method('getPropertyDefinitions')
->will($this->returnValue([]));
$field_item = new MailWithTypeItem($definition);
$reflection = new \ReflectionClass('Drupal\crm_core_contact\Plugin\Field\FieldType\MailWithTypeItem');
$method = $reflection->getMethod('emailTypesString');
$method->setAccessible(TRUE);
$this->assertSame("home|Home\nwork|Work", $method->invokeArgs($field_item, [['home' => 'Home', 'work' => 'Work']]));
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment