diff --git a/web/modules/custom/bnf/bnf.module b/web/modules/custom/bnf/bnf.module index d19fd7ed8..11e83cce5 100644 --- a/web/modules/custom/bnf/bnf.module +++ b/web/modules/custom/bnf/bnf.module @@ -3,6 +3,7 @@ use Drupal\bnf\BnfStateEnum; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Field\BaseFieldDefinition; +use Drupal\node\NodeInterface; /** * Implements hook_entity_base_field_info(). @@ -41,3 +42,28 @@ function bnf_get_bnf_state_allowed_values(): array { } return $values; } + +/** + * Implements theme_preprocess_html(). + * + * Adding the node UUID as a metatag, that we can use when the user submits + * a URL to the BNF import form. + */ +function bnf_preprocess_html(array &$variables): void { + $route = \Drupal::routeMatch(); + $node = $route->getParameter('node'); + + if ($route->getRouteName() !== 'entity.node.canonical' || !($node instanceof NodeInterface)) { + return; + } + + $uuid_metatag = [ + '#tag' => 'meta', + '#attributes' => [ + 'name' => 'uuid', + 'content' => $node->uuid(), + ], + ]; + + $variables['page']['#attached']['html_head'][] = [$uuid_metatag, 'node-uuid']; +} diff --git a/web/modules/custom/bnf/bnf_client/bnf_client.links.menu.yml b/web/modules/custom/bnf/bnf_client/bnf_client.links.menu.yml new file mode 100644 index 000000000..5bce3976d --- /dev/null +++ b/web/modules/custom/bnf/bnf_client/bnf_client.links.menu.yml @@ -0,0 +1,6 @@ +--- +bnf_client.import_content: + title: 'Import BNF content' + parent: system.admin_content + route_name: bnf_client.import_form + weight: 10 diff --git a/web/modules/custom/bnf/bnf_client/bnf_client.permissions.yml b/web/modules/custom/bnf/bnf_client/bnf_client.permissions.yml index e8a3704a5..b3a94242f 100644 --- a/web/modules/custom/bnf/bnf_client/bnf_client.permissions.yml +++ b/web/modules/custom/bnf/bnf_client/bnf_client.permissions.yml @@ -1,2 +1,4 @@ bnf client export nodes: title: 'Can export nodes to BNF' +bnf client import nodes: + title: 'Can import nodes from BNF' diff --git a/web/modules/custom/bnf/bnf_client/bnf_client.routing.yml b/web/modules/custom/bnf/bnf_client/bnf_client.routing.yml new file mode 100644 index 000000000..6d3987e66 --- /dev/null +++ b/web/modules/custom/bnf/bnf_client/bnf_client.routing.yml @@ -0,0 +1,16 @@ +--- +bnf_client.import_form: + path: '/admin/bnf/import' + defaults: + _form: '\Drupal\bnf_client\Form\BnfImportForm' + _title: 'Import' + requirements: + _permission: 'bnf client import nodes' + +bnf_client.import_confirm_form: + path: '/admin/bnf/import/{uuid}' + defaults: + _form: '\Drupal\bnf_client\Form\BnfImportConfirmForm' + _title: 'Confirm import' + requirements: + _permission: 'bnf client import nodes' diff --git a/web/modules/custom/bnf/bnf_client/src/Form/BnfImportConfirmForm.php b/web/modules/custom/bnf/bnf_client/src/Form/BnfImportConfirmForm.php new file mode 100644 index 000000000..1a8130673 --- /dev/null +++ b/web/modules/custom/bnf/bnf_client/src/Form/BnfImportConfirmForm.php @@ -0,0 +1,100 @@ +get('current_route_match'), + $container->get('bnf.importer') + ); + } + + /** + * {@inheritDoc} + */ + public function getFormId(): string { + return 'bnf_import_form_form'; + } + + /** + * {@inheritDoc} + */ + public function buildForm(array $form, FormStateInterface $form_state): array { + $form['#title'] = $this->t('Confirm import of BNF content', [], ['context' => 'BNF']); + + $uuid = $this->routeMatch->getParameter('uuid'); + $bnfServer = (string) getenv('BNF_SERVER_GRAPHQL_ENDPOINT'); + + $form_state->set('uuid', $uuid); + $form_state->set('bnfServer', $bnfServer); + + $nodeData = $this->bnfImporter->loadNodeData($uuid, $bnfServer); + + $form['uuid'] = [ + '#title' => 'UUID', + '#type' => 'textfield', + '#default_value' => $uuid, + '#disabled' => TRUE, + ]; + + $form['label'] = [ + '#title' => $this->t('Content label', [], ['context' => 'BNF']), + '#type' => 'textfield', + '#default_value' => $nodeData['title'] ?? NULL, + '#disabled' => TRUE, + ]; + + $form['actions']['submit'] = [ + '#type' => 'submit', + '#value' => $this->t('Import content'), + ]; + + return $form; + } + + /** + * {@inheritDoc} + */ + public function validateForm(array &$form, FormStateInterface $form_state): void { + + } + + /** + * {@inheritDoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state): void { + $uuid = $form_state->get('uuid'); + $bnfServer = $form_state->get('bnfServer'); + $node = $this->bnfImporter->importNode($uuid, $bnfServer); + $form_state->setRedirect('entity.node.edit_form', ['node' => $node->id()]); + } + +} diff --git a/web/modules/custom/bnf/bnf_client/src/Form/BnfImportForm.php b/web/modules/custom/bnf/bnf_client/src/Form/BnfImportForm.php new file mode 100644 index 000000000..fffb83073 --- /dev/null +++ b/web/modules/custom/bnf/bnf_client/src/Form/BnfImportForm.php @@ -0,0 +1,89 @@ +t('Import nodes from BNF', [], ['context' => 'BNF']); + $form['reference'] = [ + '#type' => 'textfield', + '#title' => $this->t('URL or UUID of content'), + ]; + + $form['actions'] = [ + '#type' => 'actions', + ]; + + $form['actions']['submit'] = [ + '#type' => 'submit', + '#value' => $this->t('Preview content'), + ]; + + return $form; + } + + /** + * {@inheritDoc} + */ + public function validateForm(array &$form, FormStateInterface $form_state): void { + $reference = $form_state->getValue('reference'); + $uuid = $this->parseAndValidateUuid($reference); + + if (empty($uuid)) { + $form_state->setErrorByName( + 'reference', + $this->t('Invalid URL or UUID.', [], ['context' => 'BNF']) + ); + } + + $form_state->set('uuid', $uuid); + } + + /** + * {@inheritDoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state): void { + $uuid = $form_state->get('uuid'); + $form_state->setRedirect('bnf_client.import_confirm_form', ['uuid' => $uuid]); + } + + /** + * Getting and validate a UUID from string or URL. + */ + protected function parseAndValidateUuid(string $reference): string|false { + // Detect if reference is a URL. + if (filter_var($reference, FILTER_VALIDATE_URL)) { + // Finding the metatag that contains the UUID. + $meta_tags = get_meta_tags($reference); + $reference = $meta_tags['uuid'] ?? NULL; + } + + return Uuid::isValid($reference) ? $reference : FALSE; + } + +} diff --git a/web/modules/custom/bnf/bnf_client/src/Services/BnfExporter.php b/web/modules/custom/bnf/bnf_client/src/Services/BnfExporter.php index 6886b86d6..b4803e9f7 100644 --- a/web/modules/custom/bnf/bnf_client/src/Services/BnfExporter.php +++ b/web/modules/custom/bnf/bnf_client/src/Services/BnfExporter.php @@ -56,7 +56,6 @@ public function exportNode(NodeInterface $node): void { try { $bnfServer = (string) getenv('BNF_SERVER_GRAPHQL_ENDPOINT'); - $bnfServer = 'https://dapple-cms.local/graphql'; if (!filter_var($bnfServer, FILTER_VALIDATE_URL)) { throw new \InvalidArgumentException('The provided BNF server URL is not valid.'); diff --git a/web/modules/custom/bnf/src/Services/BnfImporter.php b/web/modules/custom/bnf/src/Services/BnfImporter.php index 371556350..dfcb4b516 100644 --- a/web/modules/custom/bnf/src/Services/BnfImporter.php +++ b/web/modules/custom/bnf/src/Services/BnfImporter.php @@ -6,6 +6,7 @@ use Drupal\bnf\Exception\AlreadyExistsException; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\StringTranslation\TranslationInterface; +use Drupal\node\NodeInterface; use Drupal\paragraphs\ParagraphInterface; use GuzzleHttp\ClientInterface; use Psr\Log\LoggerInterface; @@ -26,11 +27,6 @@ class BnfImporter { 'text_body', ]; - /** - * The BNF UUID of the content that we import. - */ - protected string $uuid; - /** * Constructor. */ @@ -44,11 +40,11 @@ public function __construct( /** * Building the query we use to get data from source. */ - protected function getQuery(string $queryName): string { + protected function getQuery(string $queryName, string $uuid): string { // Example of GraphQL query: "nodeArticle". return <<uuid") { + $queryName(id: "$uuid") { title paragraphs { ... on ParagraphTextBody { @@ -157,10 +153,12 @@ protected function graphqlTypeToBundle(string $typeName): string { } /** - * Importing a node from a GraphQL source endpoint. + * Loading the node data from a GraphQL endpoint. + * + * @return mixed[] + * Array of node values, that we can use to create node entities. */ - public function importNode(string $uuid, string $endpointUrl, string $nodeType = 'article'): void { - $this->uuid = $uuid; + public function loadNodeData(string $uuid, string $endpointUrl, string $nodeType = 'article'): array { $queryName = 'node' . ucfirst($nodeType); $nodeStorage = $this->entityTypeManager->getStorage('node'); @@ -188,7 +186,7 @@ public function importNode(string $uuid, string $endpointUrl, string $nodeType = throw new \InvalidArgumentException('The provided callback URL must use HTTPS.'); } - $query = $this->getQuery($queryName); + $query = $this->getQuery($queryName, $uuid); $response = $this->httpClient->request('post', $endpointUrl, [ 'headers' => [ @@ -211,11 +209,25 @@ public function importNode(string $uuid, string $endpointUrl, string $nodeType = throw new \Exception('Could not retrieve content values.'); } + return $nodeData; + + } + + /** + * Importing a node from a GraphQL source endpoint. + */ + public function importNode(string $uuid, string $endpointUrl, string $nodeType = 'article'): NodeInterface { + $nodeStorage = $this->entityTypeManager->getStorage('node'); + try { + $nodeData = $this->loadNodeData($uuid, $endpointUrl, $nodeType); + $nodeData['type'] = $nodeType; $nodeData['uuid'] = $uuid; + $nodeData['status'] = NodeInterface::NOT_PUBLISHED; $nodeData['field_paragraphs'] = $this->getParagraphs($nodeData); + /** @var \Drupal\node\NodeInterface $node */ $node = $nodeStorage->create($nodeData); $node->save(); @@ -236,6 +248,8 @@ public function importNode(string $uuid, string $endpointUrl, string $nodeType = '@type' => $nodeType, ]); + return $node; + } }