Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VACMS-9885 site previewer #16138

Merged
merged 14 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config/sync/feature_toggle.features.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
12 changes: 12 additions & 0 deletions config/sync/next.next_entity_type_config.node.news_story.yml
Original file line number Diff line number Diff line change
@@ -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: { }
11 changes: 11 additions & 0 deletions config/sync/next.next_entity_type_config.node.story_listing.yml
Original file line number Diff line number Diff line change
@@ -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: { }
4 changes: 2 additions & 2 deletions config/sync/next.settings.yml
Original file line number Diff line number Diff line change
@@ -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:
Expand Down
48 changes: 0 additions & 48 deletions docroot/modules/custom/va_gov_backend/va_gov_backend.module
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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 = '<a class="button button--primary js-form-submit form-submit node-preview-button" rel="noopener" target="_blank" href="' . $url . '">Preview</a>';
$variables['page']['sidebar_second']['#markup'] = $button;
}
}
}
}

/**
* Callback handler for workbench_access_assign_user form - invalidate cache.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
<?php

namespace Drupal\va_gov_preview\EventSubscriber;

use Drupal\Core\Datetime\DateFormatter;
use Drupal\Core\Entity\EntityTypeManager;
use Drupal\Core\Http\RequestStack;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\feature_toggle\FeatureStatus;
use Drupal\next\NextEntityTypeManagerInterface;
use Drupal\next\NextSettingsManagerInterface;
use Drupal\node\NodeInterface;
use Drupal\preprocess_event_dispatcher\Event\PagePreprocessEvent;
use Drupal\va_gov_backend\Service\ExclusionTypes;
use Drupal\va_gov_build_trigger\Form\PreviewForm;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
* VA.gov Lovell Entity Event Subscriber.
*/
class PreviewEventSubscriber implements EventSubscriberInterface {

use StringTranslationTrait;

/**
* The Feature toggle name for outreach checkbox.
*/
const NEXT_PREVIEW_FEATURE_NAME = 'feature_next_story_preview';

/**
* The entity manager.
*
* @var \Drupal\Core\Entity\EntityTypeManager
*/
private $entityTypeManager;

/**
* The route match interface.
*
* @var \Drupal\Core\Routing\RouteMatchInterface
*/
protected $routeMatch;

/**
* The request stack.
*
* @var \Drupal\Core\Http\RequestStack
*/
protected $requestStack;

/**
* The language manager interface.
*
* @var \Drupal\Core\Language\LanguageManagerInterface
*/
protected $languageManager;

/**
* The date formatter service.
*
* @var \Drupal\Core\Datetime\DateFormatter
*/
protected $dateFormatter;

/**
* The VA gov backend exclusion types service.
*
* @var \Drupal\va_gov_backend\Service\ExclusionTypes
*/
protected $exclusionTypes;

/**
* The next entity type manager.
*
* @var \Drupal\next\NextEntityTypeManagerInterface
*/
protected NextEntityTypeManagerInterface $nextEntityTypeManager;

/**
* The next settings manager.
*
* @var \Drupal\next\NextSettingsManagerInterface
*/
protected NextSettingsManagerInterface $nextSettingsManager;

/**
* TRUE if the next preview checkbox feature toggle is enabled.
*
* @var bool
*/
private bool $nextPreviewEnabled;

/**
* Constructs the EventSubscriber object.
*
* @param \Drupal\Core\Entity\EntityTypeManager $entityTypeManager
* The string entity type service.
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* Interface for classes representing the result of routing.
* @param \Drupal\Core\Http\RequestStack $request_stack
* The request stack.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
* @param \Drupal\Core\Datetime\DateFormatter $date_formatter
* The date formatter.
* @param \Drupal\va_gov_backend\Service\ExclusionTypes $exclusion_types
* The va_gov_backend exclusion types service.
* @param \Drupal\next\NextEntityTypeManagerInterface $next_entity_type_manager
* Interface for retrieving all connected Next.js sites.
* @param \Drupal\next\NextSettingsManagerInterface $next_settings_manager
* Interface for retrieving specific Next.js site settings.
* @param \Drupal\feature_toggle\FeatureStatus $feature_status
* Interface for checking CMS feature flags.
*/
public function __construct(
tjheffner marked this conversation as resolved.
Show resolved Hide resolved
EntityTypeManager $entityTypeManager,
RouteMatchInterface $route_match,
RequestStack $request_stack,
LanguageManagerInterface $language_manager,
DateFormatter $date_formatter,
ExclusionTypes $exclusion_types,
NextEntityTypeManagerInterface $next_entity_type_manager,
NextSettingsManagerInterface $next_settings_manager,
FeatureStatus $feature_status,
) {
$this->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 '<a class="button button--primary js-form-submit form-submit node-preview-button" rel="noopener" target="_blank" href="' . $url . '">' . $this->t('Preview') . '</a>';
}

/**
* 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();
}

}
Loading
Loading