diff --git a/modules/dgi_actions_handle/README.md b/modules/dgi_actions_handle/README.md index 2e73e17..06585be 100644 --- a/modules/dgi_actions_handle/README.md +++ b/modules/dgi_actions_handle/README.md @@ -16,6 +16,27 @@ Install as usual, see [this](https://www.drupal.org/docs/extending-drupal/installing-modules) for further information. +## Features + +- Provides Action Plugins for minting and deleting Handle.net Handles. +- Provides a service data type plugin for Handle.net Handles. +- Uses uuid by default as the Handle's suffix. Also supports custom suffixes. + +## Usage +- See DGI Actions readme for general usage. +- Create a new service data type with the type `handle`. +- There is no UI for configuring the Handle's suffix field. It must be set in the + configuration file. The default suffix field is `uuid`. + +## User Notes + +- The field that is to be used for the Handle's suffix must be made unique and required + in the content type configuration so that the value does not change. If the field is not unique, + Handle generation will fail for the duplicate value. +- The Handle also must be unique and should be non-editable. +- To make the suffix and Handle fields non-editable and required, an additional module is provided + `dgi_actions_handle_constraints`. This module should be enabled and configured to make the necessary field validation changes. +- See dgi_actions_handle_constraints' [README](/modules/dgi_actions_handle_constraints/README.md) for more information. ## Troubleshooting/Issues diff --git a/modules/dgi_actions_handle/config/schema/dgi_actions_handle.schema.yml b/modules/dgi_actions_handle/config/schema/dgi_actions_handle.schema.yml index ad91744..f962843 100644 --- a/modules/dgi_actions_handle/config/schema/dgi_actions_handle.schema.yml +++ b/modules/dgi_actions_handle/config/schema/dgi_actions_handle.schema.yml @@ -19,6 +19,10 @@ dgi_actions.service_data_type.handle: label: 'Prefix' type: string description: 'Handle prefix as specified from Handle.net.' + suffix_field: + label: 'Suffix' + type: string + description: 'Field to use as handle suffix.' action.configuration.dgi_actions_delete_handle: type: dgi_actions_action_delete_base diff --git a/modules/dgi_actions_handle/modules/dgi_actions_handle_constraints/README.md b/modules/dgi_actions_handle/modules/dgi_actions_handle_constraints/README.md new file mode 100644 index 0000000..8c95b76 --- /dev/null +++ b/modules/dgi_actions_handle/modules/dgi_actions_handle_constraints/README.md @@ -0,0 +1,68 @@ +# DGI Actions Handle Constraints Module + +## Introduction + +This module is part of the DGI Actions Handle package. It provides functionality to add unique constraints to certain fields of an entity and handles the preservation of these fields during entity save operations. + +## Notes + +* This module should only be installed when a field is being used as a suffix field. +* The developer or site administrator should note that this module would make the suffix field always required and unique. + +## Requirements + +This module requires the following modules/libraries: + +* [DGI Actions Handle](https://github.com/discoverygarden/dgi_actions_handle) + +## Installation + +Install as usual, see +[this](https://www.drupal.org/docs/extending-drupal/installing-modules) for +further information. + +## Features + +1. **Unique Constraints**: The module can add a unique constraint to specified fields of an entity. + +2. **Entity Base Field Alteration**: The module implements the `hook_entity_base_field_info_alter` hook to add constraints to the configured fields. + +3. **Entity Bundle Field Alteration**: The module implements the `hook_entity_bundle_field_info_alter` hook to add constraints to the configured fields. + +4. **Entity Pre-save**: The module implements the `hook_entity_presave` hook to revert the value of the suffix field if it is changed. This is needed for spreadsheet ingest. + +5. **Form Alteration**: The module implements the `hook_form_alter` hook to disable the suffix/identifier fields that are not allowed to be changed. + +## Usage + +This module is used as part of the DGI Actions Handle package. +It is used to ensure that the fields that are used as suffixes for the Handle are unique and required. The module also ensures that the suffix field is not changed during entity save operations. +Once the configuration is set up, the module will handle the rest. + +## Configuration + +The module uses the `dgi_actions_handle_constraints.settings` configuration, which should be set up with the appropriate constraint settings. +For each field that is used as a suffix or identifier, a new value should be added to the constraint_settings array. A default configuration file which +uses the field_pid as suffix and field_handle as identifier is provided with the module. This can be updated with the appropriate field names. + +## Troubleshooting/Issues + +Having problems or solved a problem? Contact +[discoverygarden](http://support.discoverygarden.ca). + +## Maintainers/Sponsors + +Current maintainers: + +* [discoverygarden](http://www.discoverygarden.ca) + +## Development + +If you would like to contribute to this module, please check out the helpful +[Documentation](https://github.com/Islandora/islandora/wiki#wiki-documentation-for-developers), +[Developers](http://islandora.ca/developers) section on Islandora.ca and +contact [discoverygarden](http://support.discoverygarden.ca). + +## License + +[GPLv3](http://www.gnu.org/licenses/gpl-3.0.txt) diff --git a/modules/dgi_actions_handle/modules/dgi_actions_handle_constraints/config/install/dgi_actions_handle_constraints.settings.yml b/modules/dgi_actions_handle/modules/dgi_actions_handle_constraints/config/install/dgi_actions_handle_constraints.settings.yml new file mode 100644 index 0000000..a89cfe3 --- /dev/null +++ b/modules/dgi_actions_handle/modules/dgi_actions_handle_constraints/config/install/dgi_actions_handle_constraints.settings.yml @@ -0,0 +1,9 @@ +constraint_settings: + - entity_type: node + entity_bundle: islandora_object + field_name: field_pid + field_usage: suffix + - entity_type: node + entity_bundle: islandora_object + field_name: field_handle + field_usage: identifier diff --git a/modules/dgi_actions_handle/modules/dgi_actions_handle_constraints/config/schema/dgi_actions_handle_constraints.schema.yml b/modules/dgi_actions_handle/modules/dgi_actions_handle_constraints/config/schema/dgi_actions_handle_constraints.schema.yml new file mode 100644 index 0000000..0474aab --- /dev/null +++ b/modules/dgi_actions_handle/modules/dgi_actions_handle_constraints/config/schema/dgi_actions_handle_constraints.schema.yml @@ -0,0 +1,27 @@ +--- +dgi_action_handle_constraints.settings: + type: config_object + label: 'DGI Actions constraint settings' + mapping: + constraint_settings: + type: mapping + label: 'Constraint data' + mapping: + entity_type: + type: string + label: 'Entity type' + description: 'The entity type to add the constraint to.' + entity_bundle: + type: string + label: 'Entity bundle' + description: 'The entity bundle to add the constraint to.' + field_name: + type: string + label: 'Field name' + description: 'The field name to add the constraint to.' + field_usage: + type: string + label: 'Field usage' + description: 'Either identifier or suffix.' + constraints: + Regex: '/^(identifier|suffix)$/' diff --git a/modules/dgi_actions_handle/modules/dgi_actions_handle_constraints/dgi_actions_handle_constraints.info.yml b/modules/dgi_actions_handle/modules/dgi_actions_handle_constraints/dgi_actions_handle_constraints.info.yml new file mode 100644 index 0000000..e339f51 --- /dev/null +++ b/modules/dgi_actions_handle/modules/dgi_actions_handle_constraints/dgi_actions_handle_constraints.info.yml @@ -0,0 +1,7 @@ +name: 'DGI Actions Handle Constraints' +description: "Add constraints to handle fields." +type: module +package: DGI Actions +core_version_requirement: ^10 +dependencies: + - dgi_actions_handle:dgi_actions_handle diff --git a/modules/dgi_actions_handle/modules/dgi_actions_handle_constraints/dgi_actions_handle_constraints.module b/modules/dgi_actions_handle/modules/dgi_actions_handle_constraints/dgi_actions_handle_constraints.module new file mode 100644 index 0000000..53c70f8 --- /dev/null +++ b/modules/dgi_actions_handle/modules/dgi_actions_handle_constraints/dgi_actions_handle_constraints.module @@ -0,0 +1,141 @@ +get('constraint_settings') ?? []; + // Constraint settings is an array of values + // for each field that should be modified. + foreach ($constraint_settings as $constraintSetting) { + if (!isset($fields[$constraintSetting['field_name']]) || $constraintSetting['entity_type'] !== $entity_type->id()) { + continue; + } + // Both identifier and suffix fields should be unique. + $fields[$constraintSetting['field_name']]->addConstraint('UniqueField'); + + // If the field is a suffix field, set it as required. + if ($constraintSetting['field_usage'] === 'suffix') { + $fields[$constraintSetting['field_name']]->setRequired(TRUE); + } + } + } + catch (\Exception $e) { + Drupal::logger('dgi_actions_handle_constraints')->error($e->getMessage()); + } +} + +/** + * Implements hook_entity_base_field_info_alter(). + */ +function dgi_actions_handle_constraints_entity_base_field_info_alter(&$fields, EntityTypeInterface $entity_type): void { + if ($entity_type instanceof ContentEntityTypeInterface) { + _dgi_actions_handle_constraints_suffix_validation_add_constraint($fields, $entity_type); + } +} + +/** + * Implements hook_entity_bundle_field_info_alter(). + */ +function dgi_actions_handle_constraints_entity_bundle_field_info_alter(&$fields, EntityTypeInterface $entity_type): void { + if ($entity_type instanceof ContentEntityTypeInterface) { + _dgi_actions_handle_constraints_suffix_validation_add_constraint($fields, $entity_type); + } +} + +/** + * Implements hook_entity_presave(). + * + * Reverts the value of the suffix field if it is changed. + * This is needed for spreadsheet ingest. + */ +function dgi_actions_handle_constraints_entity_presave(EntityInterface $entity): void { + try { + $config = \Drupal::config('dgi_actions_handle_constraints.settings'); + $constraint_settings = $config->get('constraint_settings'); + // Constraint settings is an array of values + // for each field that should be modified. + foreach ($constraint_settings as $constraintSetting) { + // Only revert the suffix field if it is changed. + if ($constraintSetting['field_usage'] !== 'suffix' + || $entity->getEntityTypeId() !== $constraintSetting['entity_type'] + || $entity->isNew() || !$entity->hasField($constraintSetting['field_name'])) { + continue; + } + $original_entity = Drupal::entityTypeManager() + ->getStorage($entity->getEntityTypeId()) + ->loadUnchanged($entity->id()); + if (!$original_entity) { + return; + } + if ($entity->{$constraintSetting['field_name']}->value !== $original_entity->{$constraintSetting['field_name']}->value) { + $entity->{$constraintSetting['field_name']}->value = $original_entity->{$constraintSetting['field_name']}->value; + Drupal::messenger()->addWarning('The suffix field cannot be changed.'); + } + } + } + catch (\Exception $e) { + Drupal::logger('dgi_actions_handle_constraints')->error($e->getMessage()); + } +} + +/** + * Implements hook_form_alter(). + * + * Disables the suffix/identifier fields that are not allowed to be changed. + */ +function dgi_actions_handle_constraints_form_alter(array &$form, FormStateInterface $form_state, $form_id): void { + try { + $config = \Drupal::config('dgi_actions_handle_constraints.settings'); + $constraint_settings = $config->get('constraint_settings'); + // Constraint settings is an array + // of values for each field that should be modified. + foreach ($constraint_settings as $constraintSetting) { + // If the form id is not of the content + // form or the content edit form, return. + if (!($form_id === $constraintSetting['entity_type'] . '_' . $constraintSetting['entity_bundle'] . '_form' + || $form_id === $constraintSetting['entity_type'] . '_' . $constraintSetting['entity_bundle'] . '_edit_form')) { + return; + } + $entity = $form_state->getFormObject()->getEntity(); + if (!$entity instanceof ContentEntityInterface || !isset($form[$constraintSetting['field_name']])) { + continue; + } + // For the suffix field, set the description and access. + if ($constraintSetting['field_usage'] === 'suffix') { + $form[$constraintSetting['field_name']]['widget'][0]['value']['#description'] = t('This field is used as the handle suffix. The suffix field, once set, cannot be changed.'); + $form[$constraintSetting['field_name']]['#access'] = TRUE; + + // Disable the suffix field if it has a value. + if ($entity->{$constraintSetting['field_name']}->value && !$entity->isNew()) { + $form[$constraintSetting['field_name']]['#disabled'] = TRUE; + } + } + + // If the field is an identifier + // field and the entity is not new, disable it. + if ($constraintSetting['field_usage'] === 'identifier' && !$entity->isNew()) { + $form[$constraintSetting['field_name']]['#disabled'] = TRUE; + } + + } + } + catch (\Exception $e) { + Drupal::logger('dgi_actions_handle_constraints')->error($e->getMessage()); + } +} diff --git a/modules/dgi_actions_handle/src/Plugin/ServiceDataType/Handle.php b/modules/dgi_actions_handle/src/Plugin/ServiceDataType/Handle.php index 89d5def..ab50bf0 100644 --- a/modules/dgi_actions_handle/src/Plugin/ServiceDataType/Handle.php +++ b/modules/dgi_actions_handle/src/Plugin/ServiceDataType/Handle.php @@ -40,6 +40,7 @@ public function defaultConfiguration(): array { 'username' => NULL, 'password' => NULL, 'prefix' => NULL, + 'suffix_field' => NULL, ]; } @@ -76,6 +77,10 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta '#default_value' => $this->configuration['prefix'], '#required' => TRUE, ]; + $form['suffix_field'] = [ + '#type' => 'hidden', + '#default_value' => $this->configuration['suffix_field'], + ]; return $form; } @@ -96,6 +101,7 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s $this->configuration['host'] = $form_state->getValue('host'); $this->configuration['prefix'] = $form_state->getValue('prefix'); $this->configuration['username'] = $form_state->getValue('username'); + $this->configuration['suffix_field'] = $form_state->getValue('suffix_field'); $this->configuration['password'] = !empty($form_state->getValue('password')) ? $form_state->getValue('password') : $this->configuration['password']; // Handle the scenario where the user did not modify the password as this // gets stored on the entity. diff --git a/modules/dgi_actions_handle/src/Utility/HandleTrait.php b/modules/dgi_actions_handle/src/Utility/HandleTrait.php index 7ed92d6..c023602 100644 --- a/modules/dgi_actions_handle/src/Utility/HandleTrait.php +++ b/modules/dgi_actions_handle/src/Utility/HandleTrait.php @@ -62,7 +62,14 @@ public function getPrefix(): string { * Gets the suffix for the entity. */ public function getSuffix(): ?string { - // XXX: Should this be something different? + $suffix_field = $this->getIdentifier()->getServiceData()->getData()['suffix_field'] ?? FALSE; + + // If a field is configured, use that. + if ($suffix_field && $this->getEntity()->hasField($suffix_field)) { + return $this->getEntity()->get($suffix_field)->value; + } + + // Use uuid by default. return $this->getEntity()->uuid(); }