Skip to content

Commit

Permalink
DDST-166: Make handle suffix configurable
Browse files Browse the repository at this point in the history
DDST-166: Make handle suffix configurable

DDST-166: Update readme

DDST-166: Code sniffer fixes for the module

DDST-166: Hardening

DDST-166: Hardening

DDST-166: More Hardening

DDST-166: Rework implementation

DDST-166: Clean up

DDST-166: More clean-up
  • Loading branch information
bibliophileaxe committed Jun 19, 2024
1 parent e13e957 commit c1fd912
Show file tree
Hide file tree
Showing 16 changed files with 296 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
namespace Drupal\dgi_actions_ark_identifier\Plugin\Action;

use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\dgi_actions_ezid\Utility\EzidTrait;
use Drupal\dgi_actions\Plugin\Action\DeleteIdentifier;
use Drupal\dgi_actions\Plugin\Action\HttpActionDeleteTrait;
use Drupal\dgi_actions\Utility\IdentifierUtils;
use Drupal\dgi_actions_ezid\Utility\EzidTrait;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\RequestOptions;
use Psr\Http\Message\ResponseInterface;
Expand Down
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 handle 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 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 handle 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 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,63 @@
# 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.

## 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 unique constraints to the fields of an entity if the entity type is an instance of `ContentEntityTypeInterface`.

3. **Entity Bundle Field Alteration**: The module implements the `hook_entity_bundle_field_info_alter` hook to add unique constraints to the fields of an entity bundle if the entity type is an instance of `ContentEntityTypeInterface`.

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. An example configuration file which
uses the field_pid as suffix and field_handle as identifier is provided with the module.

## 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:
- 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 Handle Constraints
core_version_requirement: ^9 || ^10
dependencies:
- dgi_actions_handle:dgi_actions_handle
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
<?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) {
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());
}
}
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'];

// 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
2 changes: 1 addition & 1 deletion src/Form/DataProfileDeleteForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

use Drupal\Core\Entity\EntityConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url;

/**
* Builds the form to delete Data Profile setting entities.
Expand Down
12 changes: 6 additions & 6 deletions src/Plugin/Action/IdentifierAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@

namespace Drupal\dgi_actions\Plugin\Action;

use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\dgi_actions\Entity\IdentifierInterface;
use Drupal\dgi_actions\Utility\IdentifierUtils;
use Drupal\Core\Action\ConfigurableActionBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Psr\Log\LoggerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\dgi_actions\Entity\IdentifierInterface;
use Drupal\dgi_actions\Utility\IdentifierUtils;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
* Base class for Identifier Actions.
Expand Down
2 changes: 1 addition & 1 deletion src/Plugin/Action/MintIdentifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

namespace Drupal\dgi_actions\Plugin\Action;

use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Entity\Exception\UndefinedLinkTemplateException;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Field\EntityReferenceFieldItemList;
use Drupal\Core\Form\FormStateInterface;

Expand Down
Loading

0 comments on commit c1fd912

Please sign in to comment.