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 }