diff --git a/composer.json b/composer.json index 2560be5..3fd07c4 100644 --- a/composer.json +++ b/composer.json @@ -2,9 +2,12 @@ "name": "discoverygarden/islandora_citations", "license": "GPL-3.0-only", "type": "drupal-module", - "require": { + "require": { + "islandora/controlled_access_terms": "^2", "seboettg/citeproc-php": "^2.6.0", - "webflo/drupal-finder": "^1.2.2", - "islandora/controlled_access_terms": "^2" + "webflo/drupal-finder": "^1.2.2" + }, + "suggest": { + "drupal/base_field_override_ui": "To allow the use of 'base' entity fields in mappings." } } diff --git a/config/schema/islandora_citations.schema.yml b/config/schema/islandora_citations.schema.yml index 2e57f56..d470434 100644 --- a/config/schema/islandora_citations.schema.yml +++ b/config/schema/islandora_citations.schema.yml @@ -22,9 +22,11 @@ field.field.*.*.*.third_party.islandora_citations: sequence: type: string label: 'CSL' + nullable: true use_entity_checkbox: label: 'CSL Mapping from Entity' type: boolean + nullable: true islandora_citations.settings: type: config_object diff --git a/islandora_citations.module b/islandora_citations.module index 090b006..e0cc701 100644 --- a/islandora_citations.module +++ b/islandora_citations.module @@ -5,77 +5,87 @@ * General hook implementations. */ -use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Form\FormStateInterface; /** * Implements hook_form_FORM_ID_alter(). */ function islandora_citations_form_field_config_edit_form_alter(&$form, FormStateInterface $form_state, $form_id) { - $schema = json_decode(file_get_contents(__DIR__ . '/data/csl-data.json'), 1); + _islandora_citations_form_field_config_edit_form_alter($form, $form_state, $form_id); +} - if ($schema) { - $cslPropertyArray = array_keys($schema['items']['properties']); - $cslFieldOptions = array_combine($cslPropertyArray, $cslPropertyArray); - $entity = $form_state->getFormObject()->getEntity(); +/** + * Implements hook_form_FORM_ID_alter(). + */ +function islandora_citations_form_base_field_override_add_form_alter(&$form, FormStateInterface $form_state, $form_id) { + _islandora_citations_form_field_config_edit_form_alter($form, $form_state, $form_id); +} - $form['islandora_citations'] = [ - '#title' => 'Citation Settings', - '#type' => 'fieldset', - ]; +/** + * Implements hook_form_FORM_ID_alter(). + */ +function islandora_citations_form_base_field_override_edit_form_alter(&$form, FormStateInterface $form_state, $form_id) { + _islandora_citations_form_field_config_edit_form_alter($form, $form_state, $form_id); +} + +/** + * Common form alteration callback to add third-party settings. + */ +function _islandora_citations_form_field_config_edit_form_alter(&$form, FormStateInterface $form_state, $form_id) { + $schema = json_decode(file_get_contents(__DIR__ . '/data/csl-data.json'), 1); - // For typed relation, there is no separate mapping. - // It can just be enabled and the mapping will be based on rel type. - if ($entity->getType() == 'typed_relation') { - $form['islandora_citations']['use_entity_checkbox'] = [ - '#title' => t('Map CSL from relation type'), - '#description' => t('Enable mapping of this typed relation field from the selected relation type. Rel types, author and creator both get mapped to author.'), - '#type' => 'checkbox', - '#required' => FALSE, - '#default_value' => $entity->getThirdPartySetting('islandora_citations', 'use_entity_checkbox'), - ]; - } + if (!$schema) { + return; + } - // For ERR (paragraphs), we only allow mapping from entity. - // Fields mapped inside the paragraph are only considered, else ignored. - // For ER fields, we allow both mapping selected from entity - // or mapping just the title of the ER entity. - if ($entity->getType() == 'entity_reference_revisions' || $entity->getType() == 'entity_reference') { - $form['islandora_citations']['use_entity_checkbox'] = [ - '#title' => t('Map from referenced entity'), - '#description' => \t('If this field is enabled, the csl mapping will be taken from the referenced entity. Make sure you map fields in the referenced entity before enabling this.'), - '#type' => 'checkbox', - '#required' => FALSE, - '#default_value' => $entity->getThirdPartySetting('islandora_citations', 'use_entity_checkbox'), - ]; - } + $cslPropertyArray = array_keys($schema['items']['properties']); + $cslFieldOptions = array_combine($cslPropertyArray, $cslPropertyArray); + /** @var \Drupal\field\FieldConfigInterface $entity */ + $entity = $form_state->getFormObject()->getEntity(); - // We do not allow direct mapping for typed relation or ERR. - if (!($entity->getType() === 'entity_reference_revisions' || $entity->getType() === 'typed_relation')) { - $form['islandora_citations']['csl_field'] = [ - '#type' => 'select', - '#title' => \t('CSL Field'), - '#description' => \t('Select which CSL value this field should be mapped to.'), - '#empty_option' => \t('- Select -'), - '#multiple' => TRUE, - '#options' => $cslFieldOptions, - '#default_value' => $entity->getThirdPartySetting('islandora_citations', 'csl_field'), - ]; - } + $form['third_party_settings']['islandora_citations'] = [ + '#title' => 'Citation Settings', + '#type' => 'fieldset', + ]; - $form['#entity_builders'][] = 'islandora_citations_field_config_edit_form_form_builder'; - return $form; + // For typed relation, there is no separate mapping. + // It can just be enabled and the mapping will be based on rel type. + if ($entity->getType() == 'typed_relation') { + $form['third_party_settings']['islandora_citations']['use_entity_checkbox'] = [ + '#title' => t('Map CSL from relation type'), + '#description' => t('Enable mapping of this typed relation field from the selected relation type. Rel types, author and creator both get mapped to author.'), + '#type' => 'checkbox', + '#required' => FALSE, + '#default_value' => $entity->getThirdPartySetting('islandora_citations', 'use_entity_checkbox'), + ]; } -} -/** - * Entity builder for the config edit form with third party options. - * - * @see islandora_citations_field_config_edit_form_alter() - */ -function islandora_citations_field_config_edit_form_form_builder($entity_type, EntityInterface $configEntity, &$form, FormStateInterface $form_state) { - $configEntity->setThirdPartySetting('islandora_citations', 'csl_field', $form_state->getValue('csl_field')); - $configEntity->setThirdPartySetting('islandora_citations', 'use_entity_checkbox', $form_state->getValue('use_entity_checkbox')); + // For ERR (paragraphs), we only allow mapping from entity. + // Fields mapped inside the paragraph are only considered, else ignored. + // For ER fields, we allow both mapping selected from entity + // or mapping just the title of the ER entity. + if ($entity->getType() == 'entity_reference_revisions' || $entity->getType() == 'entity_reference') { + $form['third_party_settings']['islandora_citations']['use_entity_checkbox'] = [ + '#title' => t('Map from referenced entity'), + '#description' => \t('If this field is enabled, the csl mapping will be taken from the referenced entity. Make sure you map fields in the referenced entity before enabling this.'), + '#type' => 'checkbox', + '#required' => FALSE, + '#default_value' => $entity->getThirdPartySetting('islandora_citations', 'use_entity_checkbox'), + ]; + } + + // We do not allow direct mapping for typed relation or ERR. + if (!($entity->getType() === 'entity_reference_revisions' || $entity->getType() === 'typed_relation')) { + $form['third_party_settings']['islandora_citations']['csl_field'] = [ + '#type' => 'select', + '#title' => \t('CSL Field'), + '#description' => \t('Select which CSL value this field should be mapped to.'), + '#empty_option' => \t('- Select -'), + '#multiple' => TRUE, + '#options' => $cslFieldOptions, + '#default_value' => $entity->getThirdPartySetting('islandora_citations', 'csl_field'), + ]; + } } /** diff --git a/islandora_citations.routing.yml b/islandora_citations.routing.yml index 6aa1606..431c68b 100644 --- a/islandora_citations.routing.yml +++ b/islandora_citations.routing.yml @@ -40,7 +40,7 @@ entity.islandora_citations.add_from_file: _permission: 'administer islandora_citations' options: _admin_route: TRUE - + islandora_citations.tab.node: path: '/admin/structure/types/manage/{node_type}/citations-map-configuration' defaults: diff --git a/islandora_citations.services.yml b/islandora_citations.services.yml index de4dffb..f1273dd 100644 --- a/islandora_citations.services.yml +++ b/islandora_citations.services.yml @@ -5,7 +5,8 @@ services: arguments: ['islandora_citations'] islandora_citations.controller: class: Drupal\islandora_citations\Controller\IslandoraCitationsController - arguments: ['@entity_field.manager'] + factory: [null, 'create'] + arguments: ['@service_container'] islandora_citations.helper: class: Drupal\islandora_citations\IslandoraCitationsHelper arguments: ['@entity_type.manager', '@file_system', '@serializer', '@logger.channel.islandora_citations'] diff --git a/src/Controller/IslandoraCitationsController.php b/src/Controller/IslandoraCitationsController.php index 2576628..24518af 100644 --- a/src/Controller/IslandoraCitationsController.php +++ b/src/Controller/IslandoraCitationsController.php @@ -2,10 +2,17 @@ namespace Drupal\islandora_citations\Controller; -use Drupal\Component\Render\FormattableMarkup; +use Drupal\base_field_override_ui\BaseFieldOverrideUI; +use Drupal\Core\Config\Entity\ConfigEntityBundleBase; use Drupal\Core\Controller\ControllerBase; -use Drupal\Core\Entity\EntityFieldManager; +use Drupal\Core\Entity\EntityFieldManagerInterface; +use Drupal\Core\Field\Entity\BaseFieldOverride; +use Drupal\Core\Field\FieldDefinitionInterface; +use Drupal\Core\Link; use Drupal\Core\StringTranslation\StringTranslationTrait; +use Drupal\Core\Url; +use Drupal\node\Entity\NodeType; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Returns responses for islandora_citations module routes. @@ -15,40 +22,50 @@ class IslandoraCitationsController extends ControllerBase { use StringTranslationTrait; /** - * The entity field manager. + * Drupal's entity field manager service. * - * @var \Drupal\Core\Entity\EntityFieldManager + * @var \Drupal\Core\Entity\EntityFieldManagerInterface */ + protected EntityFieldManagerInterface $entityFieldManager; - protected $entityFieldManager; + /** + * {@inheritDoc} + */ + public static function create(ContainerInterface $container) { + return parent::create($container) + ->setEntityFieldManager($container->get('entity_field.manager')); + } /** - * Construct. + * Setter for the entity field manager service. * - * @param \Drupal\Core\Entity\EntityFieldManager $entityFieldManager - * The entity type manager service. + * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entityFieldManager + * The entity field manager service to set. + * + * @return $this */ - public function __construct(EntityFieldManager $entityFieldManager) { + public function setEntityFieldManager(EntityFieldManagerInterface $entityFieldManager) : static { $this->entityFieldManager = $entityFieldManager; + return $this; } /** * Provide arguments for FieldConfigUpdate. * - * @param string $node_type + * @param \Drupal\node\Entity\NodeType $node_type * Node type. * * @return array * Form array. */ - public function provideArguments($node_type) { + public function provideArguments(NodeType $node_type) { $header = [ 'col1' => $this->t('Field'), 'col2' => $this->t('CSL Field'), 'col3' => $this->t('Operation'), ]; - $fields = $this->entityFieldManager->getFieldDefinitions('node', $node_type); + $fields = $this->entityFieldManager->getFieldDefinitions('node', $node_type->id()); $rows = []; foreach ($fields as $field_definition) { @@ -56,15 +73,12 @@ public function provideArguments($node_type) { if (!empty($field_definition->getTargetBundle())) { $data = $field_definition->getThirdPartySetting('islandora_citations', 'csl_field'); $dataForMappedEntities = $field_definition->getThirdPartySetting('islandora_citations', 'use_entity_checkbox'); - $rows[] = [$field_definition->getName(), + $rows[] = [ + $field_definition->getName(), $data ? implode(',', $data) : ($dataForMappedEntities ? 'Mapped from entity' : '-'), - [ - 'data' => new FormattableMarkup('@name', - [ - ':link' => 'fields/node.' . $node_type . '.' . $field_definition->getName(), - '@name' => $this->t('Edit'), - ]), - ], + [ + 'data' => $this->getLinkToField('node', $node_type, $field_definition), + ], ]; } } @@ -76,11 +90,59 @@ public function provideArguments($node_type) { ]; } + /** + * Helper; generate link to the field configuration page. + * + * @param string $type + * The type of entity to which the field is associated. + * @param \Drupal\Core\Config\Entity\ConfigEntityBundleBase $bundle + * The bundle with which the field is associated. + * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition + * The field definition of which to link to the configuration page. + * + * @return \Drupal\Core\Link|\Drupal\Core\StringTranslation\TranslatableMarkup + * A link to a page to configure the given field, or a string. + */ + protected function getLinkToField(string $type, ConfigEntityBundleBase $bundle, FieldDefinitionInterface $field_definition) { + $add_destination = static function (Url $url) { + $query = $url->getOption('query'); + $query['destination'] = Url::fromRoute('')->toString(); + $url->setOption('query', $query); + return $url; + }; + /** @var \Drupal\Core\Field\Entity\BaseFieldOverride|\Drupal\field\Entity\FieldConfig $config */ + $config = $field_definition->getConfig($bundle->id()); + if ($config instanceof BaseFieldOverride) { + if ($this->moduleHandler()->moduleExists('base_field_override_ui')) { + return $config->isNew() ? + new Link( + $this->t('Add'), + $add_destination(BaseFieldOverrideUI::getAddRouteInfo($config)), + ) : + new Link( + $this->t('Edit'), + $add_destination(BaseFieldOverrideUI::getEditRouteInfo($config)), + ); + } + + return $this->t('Not applicable'); + } + else { + return new Link( + $this->t('Edit'), + $add_destination(Url::fromRoute("entity.field_config.{$type}_field_edit_form", [ + $bundle->getEntityTypeId() => $bundle->id(), + 'field_config' => $config->id(), + ])), + ); + } + } + /** * Provide arguments for FieldConfigUpdate. * - * @param string $paragraphs_type - * Node type. + * @param \Drupal\paragraphs\Entity\ParagraphsType $paragraphs_type + * Paragraph type. * * @return array * Form array. @@ -101,11 +163,7 @@ public function paragraphsArguments($paragraphs_type) { $rows[] = [$field_definition->getName(), $data ? implode(',', $data) : '-', [ - 'data' => new FormattableMarkup('@name', - [ - ':link' => 'fields/paragraph.' . $paragraphs_type->id() . '.' . $field_definition->getName(), - '@name' => $this->t('Edit'), - ]), + 'data' => $this->getLinkToField('paragraph', $paragraphs_type, $field_definition), ], ]; } diff --git a/src/Form/SelectCslForm.php b/src/Form/SelectCslForm.php index 658c3a6..a1d395b 100644 --- a/src/Form/SelectCslForm.php +++ b/src/Form/SelectCslForm.php @@ -61,13 +61,15 @@ class SelectCslForm extends FormBase { protected $logger; /** - * {@inheritdoc} + * Constructor. */ - public function __construct(IslandoraCitationsHelper $citationHelper, - RouteMatchInterface $route_match, - EntityTypeManagerInterface $entity_type_manager, - AliasManagerInterface $pathAliasManager, - LoggerInterface $logger) { + public function __construct( + IslandoraCitationsHelper $citationHelper, + RouteMatchInterface $route_match, + EntityTypeManagerInterface $entity_type_manager, + AliasManagerInterface $pathAliasManager, + LoggerInterface $logger, + ) { $this->citationHelper = $citationHelper; $this->routeMatch = $route_match; $this->entityTypeManager = $entity_type_manager; diff --git a/src/Normalizer/ContentEntityNormalizer.php b/src/Normalizer/ContentEntityNormalizer.php index 3837bab..710cf29 100644 --- a/src/Normalizer/ContentEntityNormalizer.php +++ b/src/Normalizer/ContentEntityNormalizer.php @@ -2,7 +2,6 @@ namespace Drupal\islandora_citations\Normalizer; -use Drupal\Core\Config\Entity\ThirdPartySettingsInterface; use Drupal\Core\Entity\ContentEntityInterface; /** @@ -19,19 +18,18 @@ class ContentEntityNormalizer extends NormalizerBase { * {@inheritdoc} */ public function normalize($object, $format = NULL, array $context = []) { + assert($object instanceof ContentEntityInterface); $normalized_field_items = []; foreach ($object->getFields(TRUE) as $field_item_list) { /** @var \Drupal\Core\Field\FieldDefinitionInterface $fieldDefinition */ $fieldDefinition = $field_item_list->getFieldDefinition(); - // Do not process if there are no third party settings. - if (!($fieldDefinition instanceof ThirdPartySettingsInterface)) { - continue; - } + /** @var \Drupal\Core\Field\Entity\BaseFieldOverride|\Drupal\field\Entity\FieldConfig $field_config */ + $field_config = $fieldDefinition->getConfig($object->bundle()); - $mapFromSelectedCsl = $fieldDefinition->getThirdPartySetting('islandora_citations', 'csl_field'); - $mapFromEntity = $fieldDefinition->getThirdPartySetting('islandora_citations', 'use_entity_checkbox'); + $mapFromSelectedCsl = $field_config->getThirdPartySetting('islandora_citations', 'csl_field'); + $mapFromEntity = $field_config->getThirdPartySetting('islandora_citations', 'use_entity_checkbox'); // Do not process if field is not mapped. if (empty($mapFromSelectedCsl) && empty($mapFromEntity)) {