commit
b795062ca0
8 changed files with 325 additions and 0 deletions
@ -0,0 +1,13 @@
|
||||
.form-inline { |
||||
display: flex; |
||||
gap: 20px; |
||||
flex-wrap: wrap; |
||||
max-width: 100%; |
||||
justify-content: space-evenly; |
||||
} |
||||
|
||||
|
||||
.form-inline .form-item { |
||||
flex: 1; |
||||
min-width: 250px; |
||||
} |
||||
@ -0,0 +1,8 @@
|
||||
name: 'DOI Prefill' |
||||
type: module |
||||
description: 'Generates Link to Prepopulate DOI ingest form' |
||||
package: Custom |
||||
core_version_requirement: ^10 || ^11 |
||||
dependencies: |
||||
- islandora:islandora |
||||
- prepopulate:prepopulate |
||||
@ -0,0 +1,5 @@
|
||||
styles: |
||||
version: 1.x |
||||
css: |
||||
theme: |
||||
css/doi_prefill.css: {} |
||||
@ -0,0 +1,6 @@
|
||||
<?php |
||||
|
||||
/** |
||||
* @file |
||||
* Primary module hooks for DOI Prefill module. |
||||
*/ |
||||
@ -0,0 +1,7 @@
|
||||
doi_prefill.doi_prepopulate: |
||||
path: '/doi-prefill/doi-prepopulate' |
||||
defaults: |
||||
_title: 'Doi Prepopulate' |
||||
_form: 'Drupal\doi_prefill\Form\DoiPrepopulateForm' |
||||
requirements: |
||||
_permission: 'create islandora_object content' |
||||
@ -0,0 +1,4 @@
|
||||
services: |
||||
doi_prefill.crossref_api_reader: |
||||
class: Drupal\doi_prefill\CrossrefApiReader |
||||
arguments: ['@http_client', '@logger.channel.islandora', '@config.factory'] |
||||
@ -0,0 +1,55 @@
|
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
namespace Drupal\doi_prefill; |
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface; |
||||
use Drupal\Core\Logger\LoggerChannelInterface; |
||||
use Psr\Http\Client\ClientInterface; |
||||
|
||||
/** |
||||
* Simple API class to read Crossref. |
||||
* |
||||
* See https://api.crossref.org/swagger-ui/index.html#/ |
||||
*/ |
||||
final class CrossrefApiReader { |
||||
|
||||
/** |
||||
* Constructs a CrossrefApiReader object. |
||||
*/ |
||||
public function __construct( |
||||
private readonly ClientInterface $httpClient, |
||||
private readonly LoggerChannelInterface $loggerChannelIslandora, |
||||
private readonly ConfigFactoryInterface $configFactory, |
||||
) {} |
||||
|
||||
/** |
||||
* Returns Crossref data from DOI. |
||||
*/ |
||||
public function getWork($identifier) { |
||||
$site_email = $this->configFactory->get('system.site')->get('mail'); |
||||
$endpoint = 'https://api.crossref.org/works'; |
||||
$encoded_doi = urlencode($identifier); |
||||
$url = "{$endpoint}/{$encoded_doi}"; |
||||
|
||||
try { |
||||
$response = $this->httpClient->get($url, [ |
||||
'headers' => [ |
||||
'accept' => 'application/json', |
||||
'User-Agent' => 'YourProjectName/1.0 (mailto:your-email@example.com)', |
||||
'User-Agent' => "IslandScholar/ (mailto:{$site_email})", |
||||
], |
||||
]); |
||||
$data = $response->getBody()->getContents(); |
||||
$values = json_decode($data, TRUE); |
||||
|
||||
return $values['message']; |
||||
} |
||||
catch (\Exception $e) { |
||||
$this->loggerChannelIslandora->error('Failed to fetch citation from CrossRef: ' . $e->getMessage()); |
||||
return NULL; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,227 @@
|
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
namespace Drupal\doi_prefill\Form; |
||||
|
||||
use Drupal\Core\Form\FormBase; |
||||
use Drupal\Core\Form\FormStateInterface; |
||||
use Symfony\Component\DependencyInjection\ContainerInterface; |
||||
use Drupal\doi_prefill\CrossrefApiReader; |
||||
use Symfony\Component\HttpFoundation\RedirectResponse; |
||||
use Drupal\node\Entity\Node; |
||||
use Drupal\taxonomy\Entity\Term; |
||||
use Drupal\Core\Entity\EntityTypeManagerInterface; |
||||
|
||||
/** |
||||
* Provides a DOI Prefill form. |
||||
*/ |
||||
final class DoiPrepopulateForm extends FormBase { |
||||
|
||||
/** |
||||
* The DOI API reader. |
||||
* |
||||
* @var \Drupal\doi_prefill\CrossrefApiReader |
||||
*/ |
||||
protected $doiApi; |
||||
|
||||
/** |
||||
* The entity type manager service. |
||||
* |
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface |
||||
*/ |
||||
protected $entityTypeManager; |
||||
|
||||
/** |
||||
* The constructor. |
||||
* |
||||
* @param \Drupal\doi_prefill\CrossrefApiReader $doiApi |
||||
*/ |
||||
public function __construct(CrossrefApiReader $doiApi, EntityTypeManagerInterface $entityTypeManager) { |
||||
$this->doiApi = $doiApi; |
||||
$this->entityTypeManager = $entityTypeManager; |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
public static function create(ContainerInterface $container) { |
||||
// Instantiates this form class. |
||||
return new static( |
||||
// Load the service required to construct this class. |
||||
$container->get('doi_prefill.crossref_api_reader'), |
||||
$container->get('entity_type.manager') |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
public function getFormId(): string { |
||||
return 'doi_prefill_doi_prepopulate'; |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
public function buildForm(array $form, FormStateInterface $form_state): array { |
||||
$terms = $this->entityTypeManager->getStorage('taxonomy_term')->loadByProperties([ |
||||
'name' => 'Collection', |
||||
'vid' => 'islandora_models', |
||||
]); |
||||
$term = reset($terms); |
||||
$collection_ids = \Drupal::entityQuery('node') |
||||
->accessCheck(FALSE) |
||||
->condition('field_model', $term->id()) |
||||
->execute(); |
||||
$collections = $this->entityTypeManager->getStorage('node')->loadMultiple($collection_ids); |
||||
$options = []; |
||||
foreach ($collections as $id => $collection) { |
||||
$options[$id] = $collection->label(); |
||||
} |
||||
|
||||
$form['container'] = [ |
||||
'#type' => 'container', |
||||
'#attributes' => ['class' => ['form-inline']], |
||||
]; |
||||
$form['container']['doi'] = [ |
||||
'#type' => 'textfield', |
||||
'#title' => $this->t('Enter DOI'), |
||||
'#required' => TRUE, |
||||
]; |
||||
$form['container']['collection'] = [ |
||||
'#title' => $this->t('Collection'), |
||||
'#type' => 'select', |
||||
'#options' => $options, |
||||
'#required' => TRUE, |
||||
|
||||
]; |
||||
$form['container']['redirect'] = [ |
||||
'#type' => 'select', |
||||
'#title' => $this->t("After submission?"), |
||||
'#options' => [ |
||||
'edit' => $this->t('Edit after submission'), |
||||
'resume' => $this->t('Return to this form'), |
||||
], |
||||
'#default_value' => 'edit', |
||||
]; |
||||
$form['#attached']['library'][] = 'doi_prefill/styles'; |
||||
|
||||
$form['actions'] = [ |
||||
'#type' => 'actions', |
||||
'submit' => [ |
||||
'#type' => 'submit', |
||||
'#value' => $this->t('Send'), |
||||
], |
||||
]; |
||||
|
||||
return $form; |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
public function submitForm(array &$form, FormStateInterface $form_state): void { |
||||
$doi = trim($form_state->getValue('doi')); |
||||
$collection = $form_state->getValue('collection'); |
||||
$contents = $this->doiApi->getWork($doi); |
||||
|
||||
// Build typed relations. |
||||
$typed_relations = []; |
||||
$vid = 'person'; |
||||
foreach ($contents['author'] as $author) { |
||||
$author_term = "{$author['family']}"; |
||||
if (isset($author['given'])) { |
||||
$author_term = "{$author_term}, {$author['given']}"; |
||||
} |
||||
$term = $this->getOrCreateTerm($author_term, $vid); |
||||
$typed_relations[] = [ |
||||
'target_id' => $term->id(), |
||||
'rel_type' => 'relators:aut', |
||||
]; |
||||
} |
||||
$genre = $this->getOrCreateTerm($contents['type'], 'genre'); |
||||
|
||||
// Build new node. |
||||
$new_node = Node::create([ |
||||
'title' => $contents['title'][0], |
||||
'field_member_of' => $collection, |
||||
'type' => 'islandora_object', |
||||
'field_linked_agent' => $typed_relations, |
||||
'field_publisher' => $contents['publisher'], |
||||
'field_doi' => $doi, |
||||
'field_genre' => $genre->id(), |
||||
'field_issue' => $contents['issue'], |
||||
'field_volume' => $contents['volume'], |
||||
]); |
||||
|
||||
// Optional fields. |
||||
if (isset($contents['abstract'])) { |
||||
$new_node->set('field_abstract', [ |
||||
'value' => $contents['abstract'], |
||||
'format' => 'basic_html', |
||||
]); |
||||
} |
||||
if (isset($contents['published-online'])) { |
||||
$field_date_online = []; |
||||
foreach ($contents['published-online']['date-parts'] as $date_parts) { |
||||
$field_date_online[] = ['value' => implode('-', $date_parts)]; |
||||
} |
||||
$new_node->set('field_date_online', $field_date_online); |
||||
} |
||||
|
||||
// Multivalued fields. |
||||
$field_edtf_date_issued = []; |
||||
foreach ($contents['created']['date-parts'] as $date_parts) { |
||||
$field_edtf_date_issued[] = ['value' => implode('-', $date_parts)]; |
||||
} |
||||
$new_node->set('field_edtf_date_issued', $field_edtf_date_issued); |
||||
|
||||
$field_issn = []; |
||||
foreach ($contents['ISSN'] as $issn) { |
||||
$field_issn[] = ['value' => $issn]; |
||||
} |
||||
$new_node->set('field_issn', $field_issn); |
||||
$new_node->save(); |
||||
if ($form_state->getValue('redirect') == 'edit') { |
||||
|
||||
$destination = "/node/{$new_node->id()}/edit"; |
||||
$response = new RedirectResponse($destination); |
||||
$response->send(); |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* Check if a term exists in a vocabulary. If not, create it. |
||||
* |
||||
* @param string $term_name |
||||
* The name of the term. |
||||
* @param string $vocabulary |
||||
* The machine name of the vocabulary. |
||||
* |
||||
* @return \Drupal\taxonomy\Entity\Term|null |
||||
* The term entity if found or created, or NULL on failure. |
||||
*/ |
||||
public function getOrCreateTerm($term_name, $vocabulary) { |
||||
$terms = $this->entityTypeManager->getStorage('taxonomy_term')->loadByProperties([ |
||||
'name' => $term_name, |
||||
'vid' => $vocabulary, |
||||
]); |
||||
|
||||
if ($terms) { |
||||
// Return the first found term. |
||||
return reset($terms); |
||||
} |
||||
|
||||
// If the term does not exist, create it. |
||||
$term = Term::create([ |
||||
'name' => $term_name, |
||||
'vid' => $vocabulary, |
||||
]); |
||||
$term->save(); |
||||
return $term; |
||||
|
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue