Commit 73fcf586 authored by alexpott's avatar alexpott

Issue #2489472 by cilefen, amateescu, bircher, saki007ster, swentel, andypost,...

Issue #2489472 by cilefen, amateescu, bircher, saki007ster, swentel, andypost, alexpott, Bojhan: Field-based module dependency uninstall message is unhelpful and not grammatically correct
parent 867bf59f
...@@ -3,5 +3,5 @@ services: ...@@ -3,5 +3,5 @@ services:
class: Drupal\field\FieldUninstallValidator class: Drupal\field\FieldUninstallValidator
tags: tags:
- { name: module_install.uninstall_validator } - { name: module_install.uninstall_validator }
arguments: ['@entity.manager', '@string_translation'] arguments: ['@entity_type.manager', '@string_translation', '@plugin.manager.field.field_type']
lazy: true lazy: true
...@@ -7,8 +7,9 @@ ...@@ -7,8 +7,9 @@
namespace Drupal\field; namespace Drupal\field;
use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleUninstallValidatorInterface; use Drupal\Core\Extension\ModuleUninstallValidatorInterface;
use Drupal\Core\Field\FieldTypePluginManagerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface; use Drupal\Core\StringTranslation\TranslationInterface;
...@@ -26,17 +27,27 @@ class FieldUninstallValidator implements ModuleUninstallValidatorInterface { ...@@ -26,17 +27,27 @@ class FieldUninstallValidator implements ModuleUninstallValidatorInterface {
*/ */
protected $fieldStorageConfigStorage; protected $fieldStorageConfigStorage;
/**
* The field type plugin manager.
*
* @var \Drupal\Core\Field\FieldTypePluginManagerInterface
*/
protected $fieldTypeManager;
/** /**
* Constructs a new FieldUninstallValidator. * Constructs a new FieldUninstallValidator.
* *
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity manager. * The entity manager.
* @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
* The string translation service. * The string translation service.
* @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager
* The field type plugin manager.
*/ */
public function __construct(EntityManagerInterface $entity_manager, TranslationInterface $string_translation) { public function __construct(EntityTypeManagerInterface $entity_type_manager, TranslationInterface $string_translation, FieldTypePluginManagerInterface $field_type_manager) {
$this->fieldStorageConfigStorage = $entity_manager->getStorage('field_storage_config'); $this->fieldStorageConfigStorage = $entity_type_manager->getStorage('field_storage_config');
$this->stringTranslation = $string_translation; $this->stringTranslation = $string_translation;
$this->fieldTypeManager = $field_type_manager;
} }
/** /**
...@@ -47,15 +58,17 @@ public function validate($module) { ...@@ -47,15 +58,17 @@ public function validate($module) {
if ($field_storages = $this->getFieldStoragesByModule($module)) { if ($field_storages = $this->getFieldStoragesByModule($module)) {
// Provide an explanation message (only mention pending deletions if there // Provide an explanation message (only mention pending deletions if there
// remain no actual, non-deleted fields.) // remain no actual, non-deleted fields.)
$non_deleted = FALSE; $fields_in_use = [];
foreach ($field_storages as $field_storage) { foreach ($field_storages as $field_storage) {
if (!$field_storage->isDeleted()) { if (!$field_storage->isDeleted()) {
$non_deleted = TRUE; $fields_in_use[$field_storage->getType()][] = $field_storage->getLabel();
break;
} }
} }
if ($non_deleted) { if (!empty($fields_in_use)) {
$reasons[] = $this->t('Fields type(s) 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 { else {
$reasons[] = $this->t('Fields pending deletion'); $reasons[] = $this->t('Fields pending deletion');
...@@ -77,4 +90,18 @@ protected function getFieldStoragesByModule($module) { ...@@ -77,4 +90,18 @@ protected function getFieldStoragesByModule($module) {
return $this->fieldStorageConfigStorage->loadByProperties(['module' => $module, 'include_deleted' => TRUE]); 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'];
}
} }
...@@ -93,14 +93,35 @@ function testReEnabledField() { ...@@ -93,14 +93,35 @@ function testReEnabledField() {
$admin_user = $this->drupalCreateUser(array('access administration pages', 'administer modules')); $admin_user = $this->drupalCreateUser(array('access administration pages', 'administer modules'));
$this->drupalLogin($admin_user); $this->drupalLogin($admin_user);
$this->drupalGet('admin/modules/uninstall'); $this->drupalGet('admin/modules/uninstall');
$this->assertText('Fields type(s) in use'); $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();
entity_create('field_config', array(
'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_storage->delete();
$field_storage2->delete();
$this->drupalGet('admin/modules/uninstall'); $this->drupalGet('admin/modules/uninstall');
$this->assertText('Fields pending deletion'); $this->assertText('Fields pending deletion');
$this->cronRun(); $this->cronRun();
$this->assertNoText('Fields type(s) in use'); $this->assertNoText("The Telephone number field type is used in the following field: node.field_telephone");
$this->assertNoText('Fields pending deletion'); $this->assertNoText('Fields pending deletion');
} }
} }
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
namespace Drupal\Tests\field\Unit; namespace Drupal\Tests\field\Unit;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\simpletest\AssertHelperTrait; use Drupal\simpletest\AssertHelperTrait;
use Drupal\Tests\UnitTestCase; use Drupal\Tests\UnitTestCase;
...@@ -23,6 +24,13 @@ class FieldUninstallValidatorTest extends UnitTestCase { ...@@ -23,6 +24,13 @@ class FieldUninstallValidatorTest extends UnitTestCase {
*/ */
protected $fieldUninstallValidator; protected $fieldUninstallValidator;
/**
* The mock field type plugin manager;
*
* @var \Drupal\Core\Field\FieldTypePluginManagerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $fieldTypePluginManager;
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
...@@ -30,7 +38,7 @@ protected function setUp() { ...@@ -30,7 +38,7 @@ protected function setUp() {
parent::setUp(); parent::setUp();
$this->fieldUninstallValidator = $this->getMockBuilder('Drupal\field\FieldUninstallValidator') $this->fieldUninstallValidator = $this->getMockBuilder('Drupal\field\FieldUninstallValidator')
->disableOriginalConstructor() ->disableOriginalConstructor()
->setMethods(['getFieldStoragesByModule']) ->setMethods(['getFieldStoragesByModule', 'getFieldTypeLabel'])
->getMock(); ->getMock();
$this->fieldUninstallValidator->setStringTranslation($this->getStringTranslationStub()); $this->fieldUninstallValidator->setStringTranslation($this->getStringTranslationStub());
} }
...@@ -79,12 +87,24 @@ public function testValidateNoDeleted() { ...@@ -79,12 +87,24 @@ public function testValidateNoDeleted() {
$field_storage->expects($this->once()) $field_storage->expects($this->once())
->method('isDeleted') ->method('isDeleted')
->willReturn(FALSE); ->willReturn(FALSE);
$field_type = $this->randomMachineName();
$field_storage->expects($this->once())
->method('getType')
->willReturn($field_type);
$field_name = $this->randomMachineName();
$field_storage->expects($this->once())
->method('getLabel')
->willReturn($field_name);
$this->fieldUninstallValidator->expects($this->once()) $this->fieldUninstallValidator->expects($this->once())
->method('getFieldStoragesByModule') ->method('getFieldStoragesByModule')
->willReturn([$field_storage]); ->willReturn([$field_storage]);
$field_type_label = $this->randomMachineName();
$this->fieldUninstallValidator->expects($this->once())
->method('getFieldTypeLabel')
->willReturn($field_type_label);
$module = $this->randomMachineName(); $module = $this->randomMachineName();
$expected = ['Fields type(s) in use']; $expected = ["The <em class=\"placeholder\">$field_type_label</em> field type is used in the following field: $field_name"];
$reasons = $this->fieldUninstallValidator->validate($module); $reasons = $this->fieldUninstallValidator->validate($module);
$this->assertSame($expected, $this->castSafeStrings($reasons)); $this->assertSame($expected, $this->castSafeStrings($reasons));
} }
......
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