Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DDST 166: Make handle suffix configurable #43

Merged
merged 13 commits into from
Jul 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions modules/dgi_actions_handle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
constraint_settings:
jordandukart marked this conversation as resolved.
Show resolved Hide resolved
- 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
Original file line number Diff line number Diff line change
@@ -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)$/'
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
<?php

/**
* @file
* Contains the dgi_actions_handle_constraints module.
*/

use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\ContentEntityTypeInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Form\FormStateInterface;

/**
* Implements hook_entity_base_field_info_alter().
*
* Adds the UniqueField, Required constraint to the suffix and identifer fields.
*/
function _dgi_actions_handle_constraints_suffix_validation_add_constraint(array &$fields, EntityTypeInterface $entity_type): 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) {
bibliophileaxe marked this conversation as resolved.
Show resolved Hide resolved
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);
bibliophileaxe marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
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') {
jordandukart marked this conversation as resolved.
Show resolved Hide resolved
$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()) {
jordandukart marked this conversation as resolved.
Show resolved Hide resolved
$form[$constraintSetting['field_name']]['#disabled'] = TRUE;
}

}
}
catch (\Exception $e) {
Drupal::logger('dgi_actions_handle_constraints')->error($e->getMessage());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public function defaultConfiguration(): array {
'username' => NULL,
'password' => NULL,
'prefix' => NULL,
'suffix_field' => NULL,
];
}

Expand Down Expand Up @@ -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;
}

Expand All @@ -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.
Expand Down
9 changes: 8 additions & 1 deletion modules/dgi_actions_handle/src/Utility/HandleTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand Down
Loading