headless_cms-1.0.3/modules/headless_cms_preview/headless_cms_preview.module
modules/headless_cms_preview/headless_cms_preview.module
<?php
/**
* @file
* Module file.
*/
declare(strict_types=1);
include_once 'headless_cms_preview.basefields.inc';
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\ContentEntityFormInterface;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityFormInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Markup;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url;
use Drupal\headless_cms\HeadlessCmsUtility;
use Drupal\headless_cms_preview\Event\HeadlessPreviewUpdatedEvent;
/**
* Implements hook_entity_base_field_info().
*/
function headless_cms_preview_entity_base_field_info(EntityTypeInterface $entity_type) {
$fields = [];
if ($entity_type->id() === 'consumer') {
$fields = array_merge($fields, headless_cms_preview_consumer_basefields_01());
}
return $fields;
}
/**
* Implements hook_entity_access().
*/
function headless_cms_preview_entity_access(EntityInterface $entity, $operation, AccountInterface $account) {
/** @var \Drupal\headless_cms_preview\PreviewToken\PreviewToken|null $previewToken */
$previewToken = \Drupal::service('headless_cms_preview.preview_token_negotiator')->negotiateFromRequest();
if (!$previewToken) {
return AccessResult::neutral();
}
$entityMatchesToken = $entity->getEntityTypeId() === $previewToken->entityTypeId && $entity->uuid() === $previewToken->entityUuid;
// Allow view and update operations for the previewed entity.
if ($operation === "update" && $entityMatchesToken) {
return AccessResult::allowed()->setCacheMaxAge(0);
}
if ($operation === "view" && $entityMatchesToken) {
return AccessResult::allowed()->setCacheMaxAge(0);
}
if ($operation === "view all revisions" && $entityMatchesToken) {
return AccessResult::allowed()->setCacheMaxAge(0);
}
// Check referenced child entities.
// If an entity (e.g. a paragraph) has the parent info set,
// allow access to it, if it matches the parent in the preview token.
if ($entity instanceof ContentEntityInterface) {
if ($entity->hasField('parent_type') && $entity->get('parent_type')->getString() === $previewToken->entityTypeId && $entity->hasField('parent_id') && $entity->get("parent_id")->getString() === $previewToken->entityId) {
return AccessResult::allowed()->setCacheMaxAge(0);
}
}
// Fallback.
return AccessResult::neutral();
}
/**
* Implements hook_entity_access().
*
* @todo Evaluate if needed.
*/
function headless_cms_preview_entity_create_access_disabled(AccountInterface $account, array $context, $entity_bundle) {
/** @var \Drupal\headless_cms_preview\PreviewToken\PreviewToken|null $previewToken */
$previewToken = \Drupal::service('headless_cms_preview.preview_token_negotiator')->negotiateFromRequest();
if (!$previewToken) {
return AccessResult::neutral();
}
if ($entity_bundle === $previewToken->entityTypeBundle) {
return AccessResult::allowed()->setCacheMaxAge(0);
}
// Fallback.
return AccessResult::neutral();
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Add consumer specific form alterations.
*/
function headless_cms_preview_form_consumer_form_alter(&$form, FormStateInterface &$form_state, $form_id) {
HeadlessCmsUtility::alterConsumerForm($form);
$form['additional_settings']['headless_cms_settings']['preview_settings'] = [
'#type' => 'details',
'#title' => new TranslatableMarkup('Preview'),
'#open' => TRUE,
'headless_cms_preview_url' => $form['headless_cms_preview_url'],
'headless_cms_revision_url' => $form['headless_cms_revision_url'],
];
unset($form['headless_cms_preview_url']);
unset($form['headless_cms_revision_url']);
}
/**
* Implements hook_form_alter().
*
* We want to add the preview button to the node edit form.
*/
function headless_cms_preview_form_alter(&$form, FormStateInterface &$form_state, $form_id) {
$config = \Drupal::config('headless_cms_preview.settings');
$enabledBundles = $config->get('enabled_bundles');
$formIds = [];
foreach ($enabledBundles as $bundle) {
$formIds[] = "node_{$bundle}_form";
$formIds[] = "node_{$bundle}_edit_form";
}
if (in_array($form_id, $formIds)) {
$hasPreview = isset($form_state->getStorage()['has_been_previewed']);
$formObject = $form_state->getFormObject();
if (!$formObject instanceof ContentEntityFormInterface) {
return;
}
$entity = $formObject->getEntity();
// @phpstan-ignore property.notFound
$previewMode = $entity->type?->entity?->getPreviewMode();
$form['#attached']['library'][] = 'headless_cms_preview/save_preview_persistence';
if ($hasPreview && $formObject instanceof ContentEntityFormInterface) {
$currentUserId = \Drupal::currentUser()->id();
$tokenValue = \Drupal::service('headless_cms_preview.preview_token_manager')->encode(
$currentUserId,
$entity->uuid(),
$entity->getEntityTypeId(),
$entity->bundle(),
);
$manager = \Drupal::service('headless_cms_preview.consumer_headless_preview_manager');
$urls = $manager->getPreviewUrlsForAllConsumers($entity->getEntityTypeId(), $entity->bundle(), $entity->uuid(), $tokenValue);
$form['headless_preview'] = [
'#type' => 'details',
'#title' => 'Preview',
'#group' => 'advanced',
'#open' => TRUE,
'urls' => [
'#type' => 'html_tag',
'#tag' => 'div',
'#prefix' => Markup::create(vsprintf('<strong>%s</strong>', [
new TranslatableMarkup('Preview URLs:'),
])),
'jsonapi' => [
'#type' => 'markup',
'#markup' => vsprintf('<div><a href="%s/%s/%s/%s/%s/preview" target="_blank">%s ⧉</a></div>', [
\Drupal::request()->getSchemeAndHttpHost(),
'jsonapi',
$entity->getEntityTypeId(),
$entity->bundle(),
$entity->uuid(),
new TranslatableMarkup('JSON:API'),
]),
],
],
'token_value' => [
'#type' => 'markup',
'#markup' => Markup::create(vsprintf('<div style="margin-top: 15px"><div><strong>%s</strong></div><input class="form-text form-element form-element--type-text" id="preview-token-value" value="%s" readonly />', [
new TranslatableMarkup('Preview Token:'),
$tokenValue,
])),
'#weight' => 10,
],
];
foreach ($urls as $label => $url) {
$form['headless_preview']['urls'][$label] = [
'#type' => 'markup',
'#markup' => "<div><a href='{$url}' target='_blank'>{$label} ⧉</a></div>",
];
}
}
$form['actions']['save_preview'] = [
'#type' => 'submit',
'#access' => $previewMode != DRUPAL_DISABLED && ($entity->access('create') || $entity->access('update')),
'#value' => new TranslatableMarkup('Save Preview'),
'#weight' => 20,
'#submit' => [
'::submitForm',
'::preview',
'_headless_cms_preview_form_save_preview_submit',
],
'#gin_action_item' => TRUE,
];
}
}
/**
* Reload the page for headless previews.
*/
function _headless_cms_preview_form_save_preview_submit(&$form, FormStateInterface $form_state) {
$formObj = $form_state->getFormObject();
$eventDispatcher = \Drupal::service('event_dispatcher');
if ($formObj instanceof EntityFormInterface) {
$entity = $formObj->getEntity();
$query_options = ['query' => ['uuid' => $entity->uuid()]];
$toUrl = $entity->isNew() ? Url::fromRoute('node.add', ['node_type' => $entity->bundle()], $query_options) : $entity->toUrl('edit-form', $query_options);
if ($destination = \Drupal::request()->query->get('destination')) {
$toUrl->setRouteParameter("destination", $destination);
}
\Drupal::request()->query->remove('destination');
$form_state->setRedirectUrl($toUrl);
// Dispatch event.
$event = new HeadlessPreviewUpdatedEvent($entity, \Drupal::currentUser());
$eventDispatcher->dispatch($event, $event::EVENT_NAME);
}
}
