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

Spike: CMS Preview with Next.js #14419

Closed
1 of 9 tasks
Tracked by #15021 ...
timcosgrove opened this issue Jul 19, 2023 · 6 comments
Closed
1 of 9 tasks
Tracked by #15021 ...

Spike: CMS Preview with Next.js #14419

timcosgrove opened this issue Jul 19, 2023 · 6 comments

Comments

@timcosgrove
Copy link
Contributor

timcosgrove commented Jul 19, 2023

Requirements

A CMS user should able to view their changes to a piece of content on-the-spot, even if they are not published yet, so that they know what their content will look like when viewed by a Veteran.

Acceptance Criteria

  • Determine if a custom preview solution using Next with Drupal is a viable and better solution than Next-Drupal OOTB functionality
  • Steps for implementing Preview are researched and documented, for both Next.js and Drupal, and for custom and OOTB if we decide to pursue custom as a possibility.
  • Document what will need to change from the OOTB Next-Drupal experience if we use the OOTB functionality
  • Document how we will maintain a mixture of content types being previewed by Next.js and by Content-build

Description

As part of Next Build, each CMS instance will need a persistent Next.js instance that is responsible for rendering a single node on demand, to facilitate preview. This preview should be capable of rendering content that is in draft state as well as published content.

Next-Drupal provides guidance for how to configure a Next.js instance and a Drupal instance to coordinate to do preview: https://next-drupal.org/learn/preview-mode

This investigation should

  • look into and document what will be necessary for functioning Preview for all environments
  • determine if the OOTB Next-Drupal preview functionality, with authentication etc, is even necessary.
  • investigate how Preview will manifest in the CMS (noting that the default Next-Drupal Preview setup is substantially different than what CMS Editors currently expect, and so if we use the Next-Drupal OOTB preview functionality, we will need to modify it)
  • investigate how Next Build preview and Content Build preview can coexist, since migration will need to be take place over time and some content will need to continue to preview with Content Build while other content will use Next.

Note that for this effort, we are not looking to make changes to the UX; the Content Build preview button should be replaced by a button that previews the page with Next, for those pages that are Next.js enabled.

Please see this ticket for an example functional use case that needs to be handled re: unpublished content: #12792

Looking at how we use Next.js to accomplish this will be part of the ticket. Will we build static, or will we use a persistent preview server? Are there conflicts between our configuration for static build and having a persistent preview server.

Team

Please check the team(s) that will do this work.

  • CMS Team
  • Public Websites
  • Facilities
  • User support
  • Accelerated Publishing
@timcosgrove
Copy link
Contributor Author

timcosgrove commented Jul 20, 2023

  • UX is already defined for content build; we should follow that
  • This does not mean 'pre-save' Preview; i.e. what a person would expect 'preview' would be from the form
  • This should be able to preview drafts

There are old existing AP tickets that look at this problem.

We may not need what the next.module provides for Preview; investigate whether the next.js entity types are required, or what else those modules provide.

Does authentication matter? How does content build handle the authentication piece currently for preview? Is its solution a good one?

@timcosgrove
Copy link
Contributor Author

@timcosgrove to link existing (old) tickets re: preview setup to this one.

@tjheffner
Copy link
Contributor

tjheffner commented Sep 8, 2023

CMS PREVIEW

I followed the steps here: https://next-drupal.org/learn/preview-mode

CMS Preview Setup

  1. A next.js site will need added to Drupal at /admin/config/services/next
  1. A new role with correct permissions will need created. Role name Next.js Consumer. Required permissions:
  • Bypass content access control (to view unpublished content + revisions)
  • Issue subrequests
  • View user information
  1. A user assigned to that role will need created.
    This role & user can be re-used across environments, once created.

  2. next-drupal uses the simple_oauth module to provide bearer tokens for preview api routes.
    /admin/config/people/simple_oauth to generate keys & save configuration. Store the keys outside of the webroot.

  3. Create a consumer at /admin/config/services/consumer/add. This should be connected t the user from step 2 and a secret. The client ID & the secret will be added as environment variables to next-build.

  4. Add this to .env.local in next-build and fill in the variables from step 4:

# Authentication (Bearer)
DRUPAL_CLIENT_ID=n2moWeSaTLnGf_J-h0oUydNRYNF-qwKVQswDGRxYGiQ
DRUPAL_CLIENT_SECRET=consumersecret
  1. Update DrupalClient in next-build to use the auth provided:
export const drupal = new DrupalClient(
  process.env.NEXT_PUBLIC_DRUPAL_BASE_URL,
  {
    auth: {
      clientId: process.env.DRUPAL_CLIENT_ID,
      clientSecret: process.env.DRUPAL_CLIENT_SECRET,
    },
  }
)
  1. Enable a content type at /admin/config/services/next/entity-types. Pick Story and use Site selector as the site resolver. Check the box for the site created in step 1.

Authentication

next-drupal's OOTB preview functionality with auth is necessary. Authentication is required to view unpublished content revisions. Published pages do not require auth, the normal jsonapi routes render fine.

Preview Appearance

We can easily render a button that links to the preview environment. I added this code to a new file:
docroot/modules/custom/va_gov_api/src/Plugin/Next/SitePreviewer/Link.php which renders a preview button that looks identical to the existing preview button.

<?php

namespace Drupal\va_gov_api\Plugin\Next\SitePreviewer;

use Drupal\Core\Entity\EntityInterface;
use Drupal\next\Plugin\SitePreviewerBase;

/**
 * Provides a link to the preview page.
 *
 * @SitePreviewer(
 *  id = "link",
 *  label = "Link to preview",
 *  description = "Displays a link to the preview page."
 * )
 */
class Link extends SitePreviewerBase {

  /**
   * {@inheritdoc}
   */
  public function render(EntityInterface $entity, array $sites) {
    $build = [];

    foreach ($sites as $site) {
      $build[] = [
        '#type' => 'link',
        '#title' => $this->t('Preview'),
        '#url' => $site->getPreviewUrlForEntity($entity),
        '#attributes' => [
          'class' => ['button', 'button--primary', 'node-preview-button'],
          'target' => '_blank',
        ],
      ];
    }

    return $build;
  }

}

Clear cache and visit /admin/config/services/next/settings. Under Site Previewer select "Link to preview" instead of the iframe option. This renders a link to the next-build preview on the node view page.

In va_gov_api.module I added this:

/**
 * 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_api_next_site_preview_alter(array &$preview, array $context) {
  // Don't let next overwrite the existing node view page.
  $preview['content'] = $context['original_build'][0]['content'];

  // See va_gov_backend_preprocess_page() for existing preview button logic.
}

The preview button that directs to a next.js site is only rendered if the content type is enabled to use next at /admin/config/services/next/entity-types. If the content type is not enabled here, the existing functionality proceeds as expected.

There will need to be some cleanup / handling in va_gov_backend_preprocess_page to determine which button to show / hide, but it seems straightforward enough. There is already logic in that hook to exclude the existing preview button from rendering on certain types. We should be able to add an additional check for types that are enabled to use next.

Preview works whether running yarn dev or running yarn build & yarn start. We probably want to have a persistent preview server on different environments (yarn build:preview & yarn start), but yarn dev is fine locally.

Per environment preview

We will need to create/update the next.js site config on a per-environment basis to ensure correct urls & secrets.

next-build will likely need to be included in cms environment builds. yarn build:preview to build the api routes without generating all the static files. yarn start to run the server that is responding to requests to the preview api routes.

AWS Secrets Manager is a good candidate to securely store the different environment secrets needed, private keys, etc

@tjheffner
Copy link
Contributor

A new draft piece of content, never published:
Image
Preview page rendered:
Image

The published revision of a node that has unpublished drafts (View tab versus Latest revision)
Image
Published revision previewed:
Image

The same node, now looking at the unpublished draft revision (Latest revision tab instead of View:
Image
Unpublished revision previewed:
Image

@tjheffner
Copy link
Contributor

Example PRs:

next-build: department-of-veterans-affairs/next-build#162
va.gov-cms: #15166

@ryguyk
Copy link
Contributor

ryguyk commented Sep 13, 2023

This looks good. Very thorough!

My only real thought is about next dev vs next start for the preview server. As you point out, running the server in dev mode does work insofar as it can render the content for preview. I think what jumps out to me is that I'm not sure this proves the preview functionality by itself because getStaticProps will be called on every request when running next dev regardless. Preview mode ultimately boils down to telling the server to run getStaticProps where it otherwise wouldn't (i.e. next start on pages with SSG). I don't think this is a huge deal, but I think it might be good for us to keep in mind that we need to make sure our things are working in production mode (if that's what we decide we want to use for preview; I do think there's at least a valid question around whether dev mode would suffice).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants