diff --git a/config/sync/feature_toggle.features.yml b/config/sync/feature_toggle.features.yml index fbbeee8c2d..8c0f2ebdcc 100644 --- a/config/sync/feature_toggle.features.yml +++ b/config/sync/feature_toggle.features.yml @@ -6,3 +6,4 @@ features: feature_health_connect_number: FEATURE_HEALTH_CONNECT_NUMBER feature_event_outreach_checkbox: FEATURE_EVENT_OUTREACH_CHECKBOX feature_event_outreach_checkbox_all: FEATURE_EVENT_OUTREACH_CHECKBOX_ALL + feature_next_story_preview: FEATURE_NEXT_STORY_PREVIEW diff --git a/config/sync/next.next_entity_type_config.node.news_story.yml b/config/sync/next.next_entity_type_config.node.news_story.yml new file mode 100644 index 0000000000..db54b378ca --- /dev/null +++ b/config/sync/next.next_entity_type_config.node.news_story.yml @@ -0,0 +1,12 @@ +uuid: 67c41c39-6232-472c-807a-088084bb6b6f +langcode: en +status: true +dependencies: { } +id: node.news_story +site_resolver: site_selector +configuration: + sites: + next_build_preview_server: next_build_preview_server + field_name: { } +revalidator: '' +revalidator_configuration: { } diff --git a/config/sync/next.next_entity_type_config.node.story_listing.yml b/config/sync/next.next_entity_type_config.node.story_listing.yml new file mode 100644 index 0000000000..84515b26da --- /dev/null +++ b/config/sync/next.next_entity_type_config.node.story_listing.yml @@ -0,0 +1,11 @@ +uuid: b7fce347-c71d-4af8-ad58-3916f96d9829 +langcode: en +status: true +dependencies: { } +id: node.story_listing +site_resolver: site_selector +configuration: + sites: + next_build_preview_server: next_build_preview_server +revalidator: '' +revalidator_configuration: { } diff --git a/config/sync/next.settings.yml b/config/sync/next.settings.yml index 6d1945a747..6a6d3d9548 100644 --- a/config/sync/next.settings.yml +++ b/config/sync/next.settings.yml @@ -1,10 +1,10 @@ _core: default_config_hash: xRrZztTZj9Lor1bWUC6p2aB3S29Vg8m94UrOwc5AZps langcode: en -site_previewer: iframe +site_previewer: link site_previewer_configuration: width: 100% - sync_route: false + sync_route: 0 sync_route_skip_routes: '' preview_url_generator: simple_oauth preview_url_generator_configuration: diff --git a/docroot/modules/custom/va_gov_backend/va_gov_backend.module b/docroot/modules/custom/va_gov_backend/va_gov_backend.module index b98edea58c..ac816d2a1b 100644 --- a/docroot/modules/custom/va_gov_backend/va_gov_backend.module +++ b/docroot/modules/custom/va_gov_backend/va_gov_backend.module @@ -29,7 +29,6 @@ use Drupal\paragraphs\Entity\Paragraph; use Drupal\taxonomy\TermInterface; use Drupal\user\Entity\Role; use Drupal\user\RoleInterface; -use Drupal\va_gov_build_trigger\Form\PreviewForm; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\Mime\MimeTypeGuesserInterface; @@ -971,53 +970,6 @@ function va_gov_backend_preprocess_page_title(&$variables) { } } -/** - * Add va_gov preview button to view node on the front end. - */ -function va_gov_backend_preprocess_page(&$variables) { - - $node = \Drupal::routeMatch()->getParameter('node'); - if ($node instanceof NodeInterface) { - // It's a node. - $exclusion_types_from_config = \Drupal::service('va_gov_backend.exclusion_types')->getExcludedTypes(); - $list_types = [ - // List pages don't play nicely with preview. - 'event_listing', - 'health_services_listing,', - 'leadership_listing', - 'locations_listing', - 'press_releases_listing', - 'publication_listing', - 'story_listing', - ]; - $exclusion_types = array_merge(array_values($exclusion_types_from_config), array_values($list_types)); - $last_saved_by_an_editor = $node->get('field_last_saved_by_an_editor')->value; - $variables['page']['last_saved_by_an_editor'] = $last_saved_by_an_editor ? \Drupal::service('date.formatter')->format($last_saved_by_an_editor, 'custom', 'F j Y g:ia') : 'Unknown'; - // Exclude staff pages without bios. - if ($node->bundle() === 'person_profile' && $node->field_complete_biography_create->value === '0') { - $exclusion_types[] = 'person_profile'; - } - // Make sure we aren't on the node form or an excluded type. - $route_name = \Drupal::routeMatch()->getRouteName(); - $language = \Drupal::languageManager()->getCurrentLanguage()->getId(); - if (($route_name !== 'entity.node.edit_form') && - (!in_array($node->bundle(), $exclusion_types)) && - ($language === 'en')) { - // Make sure we aren't on /training-guide. - $current_uri = \Drupal::request()->getRequestUri(); - if ($current_uri !== '/training-guide') { - $node = \Drupal::routeMatch()->getParameter('node'); - $nid = $node->id(); - $host = \Drupal::request()->getHost(); - $preview_form = new PreviewForm(); - $url = $preview_form->getEnvironment($host, $nid); - $button = 'Preview'; - $variables['page']['sidebar_second']['#markup'] = $button; - } - } - } -} - /** * Callback handler for workbench_access_assign_user form - invalidate cache. * diff --git a/docroot/modules/custom/va_gov_preview/src/EventSubscriber/PreviewEventSubscriber.php b/docroot/modules/custom/va_gov_preview/src/EventSubscriber/PreviewEventSubscriber.php new file mode 100644 index 0000000000..1c80e88138 --- /dev/null +++ b/docroot/modules/custom/va_gov_preview/src/EventSubscriber/PreviewEventSubscriber.php @@ -0,0 +1,270 @@ +entityTypeManager = $entityTypeManager; + $this->routeMatch = $route_match; + $this->requestStack = $request_stack; + $this->languageManager = $language_manager; + $this->dateFormatter = $date_formatter; + $this->exclusionTypes = $exclusion_types; + $this->nextEntityTypeManager = $next_entity_type_manager; + $this->nextSettingsManager = $next_settings_manager; + $this->nextPreviewEnabled = $feature_status->getStatus(self::NEXT_PREVIEW_FEATURE_NAME); + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents(): array { + return [ + PagePreprocessEvent::name() => 'preprocessPage', + ]; + } + + /** + * Preprocess a node page to display the preview button. + * + * @param \Drupal\preprocess_event_dispatcher\Event\PagePreprocessEvent $event + * Event. + */ + public function preprocessPage(PagePreprocessEvent $event): void { + /** @var \Drupal\preprocess_event_dispatcher\Variables\PageEventVariables $variables */ + $variables = $event->getVariables(); + $node = $variables->getNode(); + $vars = &$variables->getRootVariablesByReference(); + + if ($node === NULL) { + return; + } + + $route_name = $this->routeMatch->getRouteName(); + $language = $this->languageManager->getCurrentLanguage()->getId(); + + // Make sure we aren't on the node form or an excluded type. + if (($route_name !== 'entity.node.edit_form') && ($language === 'en')) { + $last_saved_by_an_editor = $node->get('field_last_saved_by_an_editor')->value; + $vars['page']['last_saved_by_an_editor'] = $last_saved_by_an_editor ? $this->dateFormatter->format($last_saved_by_an_editor, 'custom', 'F j Y g:ia') : 'Unknown'; + + $button = $this->generatePreviewButton($node); + $vars['page']['sidebar_second']['#markup'] = $button; + } + } + + /** + * Generate a preview button for a node. + * + * @param \Drupal\node\NodeInterface $node + * Node. + */ + protected function generatePreviewButton(NodeInterface $node): string|null { + // Needs to come first because listing types are allowed here. + if ($this->nextPreviewEnabled && $this->checkNextEnabledTypes($node->bundle())) { + $url = $this->generateNextBuildPreviewLink($node); + } + // Otherwise return default preview experience. + else { + if ($this->checkExcludedTypes($node)) { + return NULL; + } + // Make sure we aren't on /training-guide. + $current_uri = $this->requestStack->getCurrentRequest()->getRequestUri(); + if ($current_uri === '/training-guide') { + return NULL; + } + + $node = $this->routeMatch->getParameter('node'); + $nid = $node->id(); + $host = $this->requestStack->getCurrentRequest()->getHost(); + $preview_form = new PreviewForm(); + $url = $preview_form->getEnvironment($host, $nid); + } + return '' . $this->t('Preview') . ''; + } + + /** + * Next-build enabled preview requires certain config entities to exist. + * + * @param string $type + * The node type. + * + * @return bool + * TRUE if the node type has a corresponding next config entity. + */ + protected function checkNextEnabledTypes(string $type): bool { + // @todo Replace this array with a proper config check. + $enabled_types = ['news_story', 'story_listing']; + return in_array($type, $enabled_types); + } + + /** + * Existing preview functionality has some conditions. + * + * @param \Drupal\node\NodeInterface $node + * The node type. + * + * @return bool + * TRUE if the node type is excluded from preview. + */ + protected function checkExcludedTypes(NodeInterface $node): bool { + $exclusion_types_from_config = $this->exclusionTypes->getExcludedTypes(); + $list_types = [ + // List pages don't play nicely with preview. + 'event_listing', + 'health_services_listing,', + 'leadership_listing', + 'locations_listing', + 'press_releases_listing', + 'publication_listing', + 'story_listing', + ]; + $exclusion_types = array_merge(array_values($exclusion_types_from_config), array_values($list_types)); + // Exclude staff pages without bios. + if ($node->bundle() === 'person_profile' && $node->get('field_complete_biography_create')->value === '0') { + $exclusion_types[] = 'person_profile'; + } + + return (in_array($node->bundle(), $exclusion_types)); + } + + /** + * Generate a preview link targeting an associated next-build server. + * + * @param \Drupal\node\NodeInterface $node + * Node. + * + * @return string + * Preview link with required URL query params. + */ + protected function generateNextBuildPreviewLink(NodeInterface $node): string { + $sites = $this->nextEntityTypeManager->getSitesForEntity($node); + + $url = $sites['next_build_preview_server']->getPreviewUrlForEntity($node); + + return $url->toString(); + } + +} diff --git a/docroot/modules/custom/va_gov_preview/src/Plugin/Next/SitePreviewer/Link.php b/docroot/modules/custom/va_gov_preview/src/Plugin/Next/SitePreviewer/Link.php new file mode 100644 index 0000000000..a133ec10ec --- /dev/null +++ b/docroot/modules/custom/va_gov_preview/src/Plugin/Next/SitePreviewer/Link.php @@ -0,0 +1,40 @@ + 'link', + '#title' => $this->t('Preview'), + '#url' => $site->getPreviewUrlForEntity($entity), + '#attributes' => [ + 'class' => ['button', 'button--primary', 'node-preview-button'], + 'target' => '_blank', + ], + ]; + } + + return $build; + } + +} diff --git a/docroot/modules/custom/va_gov_preview/va_gov_preview.info.yml b/docroot/modules/custom/va_gov_preview/va_gov_preview.info.yml index 2e4f9873a8..c10b4cb912 100644 --- a/docroot/modules/custom/va_gov_preview/va_gov_preview.info.yml +++ b/docroot/modules/custom/va_gov_preview/va_gov_preview.info.yml @@ -8,4 +8,5 @@ type: module core_version_requirement: ^9 || ^10 dependencies: - drupal:serialization + - drupal:hook_event_dispatcher - va_gov_build_trigger diff --git a/docroot/modules/custom/va_gov_preview/va_gov_preview.module b/docroot/modules/custom/va_gov_preview/va_gov_preview.module index 2ebc5e59e3..3ef4adf68e 100644 --- a/docroot/modules/custom/va_gov_preview/va_gov_preview.module +++ b/docroot/modules/custom/va_gov_preview/va_gov_preview.module @@ -19,3 +19,25 @@ function va_gov_preview_help($route_name, RouteMatchInterface $route_match) { return $output; } } + +/** + * Alter the result of \Drupal\next\Plugin\SitePreviewerInterface::render. + * + * This hook is called after the preview has been assembled. + * + * @param array &$preview + * The preview renderable array from the site_previewer. + * @param array $context + * Context in which the entity is previewed with the following keys: + * - 'plugin': the site_previewer plugin. + * - 'entity': the entity in preview. + * - 'sites': the sites for this entity. + * - 'original_build': the original un-altered build. + * + * @ingroup next_api + */ +function va_gov_preview_next_site_preview_alter(array &$preview, array $context) { + // Prevent drupal/next overwriting the existing node view content section. + $preview['content'] = $context['original_build'][0]['content']; + unset($preview[0]); +} diff --git a/docroot/modules/custom/va_gov_preview/va_gov_preview.services.yml b/docroot/modules/custom/va_gov_preview/va_gov_preview.services.yml index 232e7cad81..3487236e1d 100644 --- a/docroot/modules/custom/va_gov_preview/va_gov_preview.services.yml +++ b/docroot/modules/custom/va_gov_preview/va_gov_preview.services.yml @@ -4,3 +4,19 @@ services: arguments: ['@messenger', '@string_translation'] tags: - { name: encoder, priority: 10, format: static_html } + va_gov_preview.preview_event_subscriber: + class: Drupal\va_gov_preview\EventSubscriber\PreviewEventSubscriber + arguments: + [ + '@entity_type.manager', + '@current_route_match', + '@request_stack', + '@language_manager', + '@date.formatter', + '@va_gov_backend.exclusion_types', + '@next.entity_type.manager', + '@next.settings.manager', + '@feature_toggle.feature_status' + ] + tags: + - { name: event_subscriber }