Skip to content
Snippets Groups Projects
Commit 86512263 authored by Alex Pott's avatar Alex Pott
Browse files

Issue #3396585 by alexpott, daniel.bosen: Add suffix field to entities and use...

Issue #3396585 by alexpott, daniel.bosen: Add suffix field to entities and use it to allow the counter ID to be changed
parent 622ad4c3
No related branches found
No related tags found
1 merge request!31Resolve #3396585 "Republication of old"
Pipeline #42280 passed
Showing with 232 additions and 27 deletions
......@@ -61,6 +61,7 @@ vgwort.entity_type_info:
type: string
label: 'View mode to use to generate text for VG Wort'
fields:
label: 'A list of fields to look for participants (Authors, Translators and Publishers)'
type: sequence
orderby: value
sequence:
......
......@@ -4,7 +4,9 @@ namespace Drupal\vgwort;
use Drupal\Core\Config\ConfigCrudEvent;
use Drupal\Core\Config\ConfigEvents;
use Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\vgwort\Plugin\Field\CounterIdFieldItemList;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class ConfigSubscriber implements EventSubscriberInterface {
......@@ -19,16 +21,22 @@ class ConfigSubscriber implements EventSubscriberInterface {
*/
private ParticipantListManager $participantListManager;
/**
* @var \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface
*/
private EntityDefinitionUpdateManagerInterface $entityDefinitionUpdateManager;
/**
* Constructs the ConfigSubscriber.
*/
public function __construct(EntityFieldManagerInterface $entityFieldManager, ParticipantListManager $participantListManager) {
public function __construct(EntityFieldManagerInterface $entityFieldManager, ParticipantListManager $participantListManager, EntityDefinitionUpdateManagerInterface $entityDefinitionUpdateManager) {
$this->entityFieldManager = $entityFieldManager;
$this->participantListManager = $participantListManager;
$this->entityDefinitionUpdateManager = $entityDefinitionUpdateManager;
}
/**
* Rebuilds the router when the default or admin theme is changed.
* Clears caches and creates base fields when VG Wort entity types change.
*
* @param \Drupal\Core\Config\ConfigCrudEvent $event
* The configuration event.
......@@ -38,6 +46,42 @@ class ConfigSubscriber implements EventSubscriberInterface {
if ($saved_config->getName() == 'vgwort.settings' && $event->isChanged('entity_types')) {
$this->entityFieldManager->clearCachedFieldDefinitions();
$this->participantListManager->clearCachedDefinitions();
// Manage base field creation.
$new_entity_types = array_keys($saved_config->get('entity_types') ?? []);
$old_entity_types = array_keys($saved_config->getOriginal('entity_types') ?? []);
// Uninstall base fields.
foreach (array_diff($old_entity_types, $new_entity_types) as $entity_type_id) {
$field_storage_definition = $this->entityDefinitionUpdateManager->getFieldStorageDefinition(CounterIdFieldItemList::SUFFIX_FIELD_NAME, $entity_type_id);
if ($field_storage_definition) {
$this->entityDefinitionUpdateManager->uninstallFieldStorageDefinition($field_storage_definition);
}
}
// Install base fields.
foreach (array_diff($new_entity_types, $old_entity_types) as $entity_type_id) {
$this->entityDefinitionUpdateManager->installFieldStorageDefinition(CounterIdFieldItemList::SUFFIX_FIELD_NAME, $entity_type_id, 'vgwort', CounterIdFieldItemList::getSuffixFieldDefinition());
}
}
}
/**
* Removes base fields when the module is uninstalled.
*
* @param \Drupal\Core\Config\ConfigCrudEvent $event
* The configuration event.
*/
public function onConfigDelete(ConfigCrudEvent $event) {
$saved_config = $event->getConfig();
if ($saved_config->getName() == 'vgwort.settings') {
foreach (array_keys($saved_config->getOriginal('entity_types') ?? []) as $entity_type_id) {
$field_storage_definition = $this->entityDefinitionUpdateManager->getFieldStorageDefinition(CounterIdFieldItemList::SUFFIX_FIELD_NAME, $entity_type_id);
if ($field_storage_definition) {
$this->entityDefinitionUpdateManager->uninstallFieldStorageDefinition($field_storage_definition);
}
}
}
}
......@@ -46,6 +90,7 @@ class ConfigSubscriber implements EventSubscriberInterface {
*/
public static function getSubscribedEvents(): array {
$events[ConfigEvents::SAVE][] = ['onConfigSave', 0];
$events[ConfigEvents::DELETE][] = ['onConfigDelete', 0];
return $events;
}
......
......@@ -3,6 +3,7 @@
namespace Drupal\vgwort\Plugin\Field;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Field\FieldItemList;
use Drupal\Core\TypedData\ComputedItemListTrait;
......@@ -14,6 +15,11 @@ use Drupal\Core\TypedData\ComputedItemListTrait;
class CounterIdFieldItemList extends FieldItemList {
use ComputedItemListTrait;
/**
* The base field name for suffixing the VG Wort counter ID.
*/
public const SUFFIX_FIELD_NAME = 'vgwort_counter_suffix';
/**
* {@inheritdoc}
*/
......@@ -79,6 +85,11 @@ class CounterIdFieldItemList extends FieldItemList {
$value = "$prefix.$publisher_id-";
$value .= $override_counter_field_value ?? $entity->uuid();
$suffix = (int) $entity->get(static::SUFFIX_FIELD_NAME)->value;
if ($suffix !== 0) {
$value .= '-' . $suffix;
}
/** @var \Drupal\Core\Field\FieldItemInterface $item */
$item = $this->createItem(0, $value);
// Example: domain.met.vgwort.de/na/vgzm.970-123456789
......@@ -88,4 +99,31 @@ class CounterIdFieldItemList extends FieldItemList {
$this->list[0] = $item;
}
/**
* Gets the suffix base field definition.
*
* @return \Drupal\Core\Field\BaseFieldDefinition
* The suffix base field definition.
*/
public static function getSuffixFieldDefinition(): BaseFieldDefinition {
return BaseFieldDefinition::create('integer')
->setLabel(t('VG Wort counter suffix'))
->setSetting('unsigned', TRUE)
->setInternal(TRUE)
->setTranslatable(FALSE)
->setRevisionable(FALSE);
}
/**
* {@inheritdoc}
*/
public function postSave($update) {
$entity = $this->getEntity();
if (isset($entity->original) && (int) $entity->get(static::SUFFIX_FIELD_NAME)->value !== (int) $entity->original->get(static::SUFFIX_FIELD_NAME)->value) {
// If the suffix value has changed force the value to be computed again.
$this->valueComputed = FALSE;
}
return parent::postSave($update);
}
}
......@@ -48,6 +48,9 @@ class VGWortTest extends BrowserTestBase {
* Tests the normal operation of the module.
*/
public function testVgWortHappyPath(): void {
// Test that installing the module has created the suffix field on nodes.
$this->assertTrue(\Drupal::database()->schema()->fieldExists('node_field_data', 'vgwort_counter_suffix'));
// Login as an admin user to set up VG Wort, use the field UI and create
// content.
$admin_user = $this->createUser([], 'site admin', TRUE);
......@@ -190,6 +193,9 @@ class VGWortTest extends BrowserTestBase {
$this->assertSession()->fieldNotExists('uninstall[vgwort]');
$this->drupalLogout();
// Test that uninstalling the module has removed the suffix field on nodes.
$this->assertFalse(\Drupal::database()->schema()->fieldExists('node_field_data', 'vgwort_counter_suffix'));
$this->drupalGet('node/1', ['query' => ['cache-breaking-string']]);
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->elementNotExists('css', 'main img');
......@@ -201,12 +207,14 @@ class VGWortTest extends BrowserTestBase {
$this->submitForm([], 'Install');
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->pageTextContains('Module VG Wort has been enabled.');
// Test that reinstalling the module has created the suffix field on nodes.
$this->assertTrue(\Drupal::database()->schema()->fieldExists('node_field_data', 'vgwort_counter_suffix'));
}
/**
* Tests adding a reference to participants.
*/
public function testAddParticipantsReference() : void {
public function testAddParticipantsReference(): void {
$this->createEntityReferenceField('node', 'article', 'field_participants', 'Participants', 'user');
_vgwort_add_entity_reference_to_participant_map('node', 'field_participants');
......@@ -237,4 +245,44 @@ class VGWortTest extends BrowserTestBase {
$this->assertSame('search_index', $config->get('entity_types.node.view_mode'));
}
/**
* Tests enabling and disabling on entity types.
*/
public function testSuffixBaseField(): void {
// Initial state.
$this->assertFalse(\Drupal::database()->schema()->fieldExists('taxonomy_term_field_data', 'vgwort_counter_suffix'));
$this->assertTrue(\Drupal::database()->schema()->fieldExists('node_field_data', 'vgwort_counter_suffix'));
$this->drupalLogin($this->createUser([], 'site admin', TRUE));
// Enable on taxonomy
$this->drupalGet('admin/config/system/vgwort');
$this->assertSession()->fieldExists('username')->setValue('aaaBBB');
$this->assertSession()->fieldExists('password')->setValue('t3st');
$this->assertSession()->fieldExists('publisher_id')->setValue('1234567');
$this->assertSession()->fieldExists('domain')->setValue('example.com');
$this->assertSession()->fieldExists('entity_types[taxonomy_term][enabled]')->check();
$this->submitForm([], 'Save configuration');
$this->assertTrue(\Drupal::database()->schema()->fieldExists('taxonomy_term_field_data', 'vgwort_counter_suffix'));
$this->assertTrue(\Drupal::database()->schema()->fieldExists('node_field_data', 'vgwort_counter_suffix'));
// Remove taxonomy.
$this->assertSession()->fieldExists('entity_types[taxonomy_term][enabled]')->uncheck();
$this->submitForm([], 'Save configuration');
$this->assertFalse(\Drupal::database()->schema()->fieldExists('taxonomy_term_field_data', 'vgwort_counter_suffix'));
$this->assertTrue(\Drupal::database()->schema()->fieldExists('node_field_data', 'vgwort_counter_suffix'));
// Remove node and add taxonomy.
$this->assertSession()->fieldExists('entity_types[taxonomy_term][enabled]')->check();
$this->assertSession()->fieldExists('entity_types[node][enabled]')->uncheck();
$this->submitForm([], 'Save configuration');
$this->assertTrue(\Drupal::database()->schema()->fieldExists('taxonomy_term_field_data', 'vgwort_counter_suffix'));
$this->assertFalse(\Drupal::database()->schema()->fieldExists('node_field_data', 'vgwort_counter_suffix'));
// Remove taxonomy.
$this->assertSession()->fieldExists('entity_types[taxonomy_term][enabled]')->uncheck();
$this->submitForm([], 'Save configuration');
$this->assertFalse(\Drupal::database()->schema()->fieldExists('taxonomy_term_field_data', 'vgwort_counter_suffix'));
$this->assertFalse(\Drupal::database()->schema()->fieldExists('node_field_data', 'vgwort_counter_suffix'));
}
}
......@@ -55,7 +55,7 @@ class VGWortUpdateBeta2Test extends UpdatePathTestBase {
'publisher_id' => '',
'image_domain' => '',
'registration_wait_days' => 14,
'entity_types' => ['node', 'taxonomy'],
'entity_types' => ['node', 'taxonomy_term'],
'test_mode' => FALSE,
];
Database::getConnection()->update('config')
......@@ -69,7 +69,12 @@ class VGWortUpdateBeta2Test extends UpdatePathTestBase {
// Run updates and test them.
$this->runUpdates();
$this->assertSame(['node' => ['view_mode' => 'full'], 'taxonomy' => ['view_mode' => 'full']], $this->config('vgwort.settings')->get('entity_types'));
$this->assertSame(['node' => ['view_mode' => 'full'], 'taxonomy_term' => ['view_mode' => 'full']], $this->config('vgwort.settings')->get('entity_types'));
// Ensure base field has been created.
// @see vgwort_update_20006()
$schema = \Drupal::database()->schema();
$this->assertTrue($schema->fieldExists('node_field_data', 'vgwort_counter_suffix'));
$this->assertTrue($schema->fieldExists('taxonomy_term_field_data', 'vgwort_counter_suffix'));
}
}
......@@ -59,7 +59,7 @@ class VGWortUpdateBeta3Test extends UpdatePathTestBase {
->fetchField();
$settings = unserialize($settings, ['allowed_classes' => FALSE]);
$settings['entity_types']['node'] = ['field_test1', 'field_test2'];
$settings['entity_types']['taxonomy'] = [];
$settings['entity_types']['taxonomy_term'] = [];
Database::getConnection()->update('config')
->fields([
'data' => serialize($settings),
......@@ -71,7 +71,7 @@ class VGWortUpdateBeta3Test extends UpdatePathTestBase {
// Run updates and test them.
$this->runUpdates();
$this->assertSame(['node' => ['view_mode' => 'full', 'fields' => ['field_test1', 'field_test2']], 'taxonomy' => ['view_mode' => 'full']], $this->config('vgwort.settings')->get('entity_types'));
$this->assertSame(['node' => ['view_mode' => 'full', 'fields' => ['field_test1', 'field_test2']], 'taxonomy_term' => ['view_mode' => 'full']], $this->config('vgwort.settings')->get('entity_types'));
}
}
......@@ -61,6 +61,8 @@ class VGWortUpdateBeta4Test extends UpdatePathTestBase {
$this->assertFalse($schema->fieldExists(EntityJobMapper::TABLE, 'revision_id'));
$this->assertFalse($schema->fieldExists(EntityJobMapper::TABLE, 'counter_id'));
$this->assertEquals(['entity_type', 'entity_id'], $find_primary_key_columns->invoke($schema, EntityJobMapper::TABLE));
// Ensure base field has not been created.
$this->assertFalse($schema->fieldExists('node_field_data', 'vgwort_counter_suffix'));
// Run updates and test them.
$this->runUpdates();
......@@ -81,6 +83,10 @@ class VGWortUpdateBeta4Test extends UpdatePathTestBase {
->condition('entity_id', 5)
->execute()->fetchObject();
$this->assertFalse($row);
// Ensure base field has been created.
// @see vgwort_update_20006()
$this->assertTrue($schema->fieldExists('node_field_data', 'vgwort_counter_suffix'));
}
/**
......
......@@ -25,9 +25,13 @@ class EntityTypesConfigTest extends KernelTestBase {
$this->installEntitySchema('entity_test');
$this->assertArrayNotHasKey('vgwort_counter_id', $this->container->get('entity_field.manager')->getBaseFieldDefinitions('entity_test'));
$this->config('vgwort.settings')->set('entity_types', ['entity_test' => []])->save();
// Test that enabling on entity test has created the suffix field.
$this->assertTrue(\Drupal::database()->schema()->fieldExists('entity_test', 'vgwort_counter_suffix'));
$this->assertArrayHasKey('vgwort_counter_id', $this->container->get('entity_field.manager')->getBaseFieldDefinitions('entity_test'));
$this->config('vgwort.settings')->set('entity_types', [])->save();
$this->assertArrayNotHasKey('vgwort_counter_id', $this->container->get('entity_field.manager')->getBaseFieldDefinitions('entity_test'));
// Test that disabling on entity_test has removed the suffix field.
$this->assertFalse(\Drupal::database()->schema()->fieldExists('entity_test', 'vgwort_counter_suffix'));
}
}
......@@ -207,4 +207,38 @@ class VgWortKernelTest extends VgWortKernelTestBase {
$this->assertSame('vgzm.123456-' . $another_entity->uuid(), $another_entity->vgwort_counter_id->value);
}
/**
* Tests that the suffix field is appended to the counter ID field.
*/
public function testSuffixField() {
$entity_storage = $this->container->get('entity_type.manager')->getStorage(static::ENTITY_TYPE);
/** @var \Drupal\entity_test\Entity\EntityTestRevPub $entity */
$entity = $entity_storage->create([
'text' => 'Some text',
'name' => 'A title',
]);
$entity->save();
$another_entity = $entity_storage->create([
'text' => 'Another text',
'name' => 'Anoter title',
]);
$another_entity->save();
$this->assertSame('vgzm.123456-' . $entity->uuid(), $entity->vgwort_counter_id->value);
$this->assertSame('vgzm.123456-' . $another_entity->uuid(), $another_entity->vgwort_counter_id->value);
$entity->vgwort_counter_suffix = 1;
$entity->save();
$this->assertSame('vgzm.123456-' . $entity->uuid() . '-1', $entity->vgwort_counter_id->value);
$this->assertSame('vgzm.123456-' . $another_entity->uuid(), $another_entity->vgwort_counter_id->value);
$another_entity->vgwort_counter_suffix = 5;
$another_entity->save();
$entity->vgwort_counter_suffix = 0;
$entity->save();
$this->assertSame('vgzm.123456-' . $entity->uuid(), $entity->vgwort_counter_id->value);
$this->assertSame('vgzm.123456-' . $another_entity->uuid() . '-5', $another_entity->vgwort_counter_id->value);
}
}
......@@ -34,11 +34,10 @@ abstract class VgWortKernelTestBase extends KernelTestBase {
*/
protected function setUp(): void {
parent::setUp();
$this->installVgWort();
$this->installEntitySchema('user');
User::create(['name' => 'User 1', 'status' => TRUE])->save();
$this->installEntitySchema(static::ENTITY_TYPE);
$this->installVgWort();
User::create(['name' => 'User 1', 'status' => TRUE])->save();
$fieldStorage = FieldStorageConfig::create([
'field_name' => 'vgwort_test',
......
......@@ -2,6 +2,9 @@
namespace Drupal\Tests\vgwort\Traits;
use Drupal\Core\Config\FileStorage;
use Drupal\Core\Config\InstallStorage;
use Drupal\Core\Config\StorageInterface;
use Drupal\vgwort\EntityJobMapper;
/**
......@@ -15,23 +18,30 @@ trait KernelSetupTrait {
private function installVgWort(): void {
$this->installSchema('advancedqueue', ['advancedqueue']);
$this->installSchema('vgwort', [EntityJobMapper::TABLE]);
$this->installConfig('vgwort');
$entity_types = $this->config('vgwort.settings')->get('entity_types');
if (!$this->container->get('module_handler')->moduleExists('node')) {
$entity_types = [];
// We can not use installConfig() for vgwort because the module has a
// dependency on node.
$default_install_path = $this->container->get('extension.path.resolver')->getPath('module', 'vgwort') . '/' . InstallStorage::CONFIG_INSTALL_DIRECTORY;
$storage = new FileStorage($default_install_path, StorageInterface::DEFAULT_COLLECTION);
foreach ($storage->listAll() as $config_name) {
$data = $storage->read($config_name);
if ($config_name === 'vgwort.settings') {
$data['publisher_id'] = 123456;
$data['image_domain'] = 'http://example.com';
$data['legal_rights'] = [
'distribution' => TRUE,
'public_access' => TRUE,
'reproduction' => TRUE,
'declaration_of_granting' => TRUE,
'other_public_communication' => FALSE,
];
if (!$this->container->get('module_handler')->moduleExists('node')) {
$data['entity_types'] = [];
}
}
$this->config($config_name)->setData($data)->save();
}
$this->config('vgwort.settings')
->set('publisher_id', 123456)
->set('image_domain', 'http://example.com')
->set('entity_types', $entity_types)
->set('legal_rights', [
'distribution' => TRUE,
'public_access' => TRUE,
'reproduction' => TRUE,
'declaration_of_granting' => TRUE,
'other_public_communication' => FALSE,
])
->save();
if (array_key_exists('ENTITY_TYPE', (new \ReflectionClass($this))->getConstants())) {
_vgwort_add_entity_reference_to_participant_map($this::ENTITY_TYPE, 'user_id');
......
......@@ -9,6 +9,7 @@ use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\RevisionableInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\vgwort\EntityJobMapper;
use Drupal\vgwort\Plugin\Field\CounterIdFieldItemList;
/**
* Implements hook_schema().
......@@ -207,3 +208,14 @@ function vgwort_update_20005(&$sandbox): TranslatableMarkup {
$sandbox['#finished'] = ($sandbox['current'] / $sandbox['total']);
return new TranslatableMarkup('@count vgwort_entity_registration rows processed.', ['@count' => $sandbox['current']]);
}
/**
* Create 'vgwort_counter_suffix' base fields.
*/
function vgwort_update_20006(&$sandbox) {
$config = \Drupal::configFactory()->getEditable('vgwort.settings');
$entityDefinitionUpdateManager = \Drupal::service('entity.definition_update_manager');
foreach (array_keys($config->get('entity_types')) as $entity_type_id) {
$entityDefinitionUpdateManager->installFieldStorageDefinition(CounterIdFieldItemList::SUFFIX_FIELD_NAME, $entity_type_id, 'vgwort', CounterIdFieldItemList::getSuffixFieldDefinition());
}
}
......@@ -10,6 +10,7 @@ use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityViewModeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\vgwort\Plugin\Field\CounterIdFieldItemList;
/**
* Implements hook_theme().
......@@ -43,6 +44,8 @@ function vgwort_entity_base_field_info(EntityTypeInterface $entity_type): array
'type' => 'vgwort_counter_id_image',
])
->setDisplayConfigurable('view', TRUE);
$fields[CounterIdFieldItemList::SUFFIX_FIELD_NAME] = CounterIdFieldItemList::getSuffixFieldDefinition();
}
return $fields;
......
......@@ -4,7 +4,7 @@ services:
parent: default_plugin_manager
vgwort.config_subscriber:
class: Drupal\vgwort\ConfigSubscriber
arguments: ['@entity_field.manager', '@vgwort.participant_list_manager']
arguments: ['@entity_field.manager', '@vgwort.participant_list_manager', '@entity.definition_update_manager']
tags:
- { name: event_subscriber }
vgwort.message_generator:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment