gatsby_endpoints-8.x-1.0-alpha1/src/Form/GatsbyEndpointForm.php
src/Form/GatsbyEndpointForm.php
<?php
namespace Drupal\gatsby_endpoints\Form;
use Drupal\gatsby_endpoints\Plugin\GatsbyEndpointInterface;
use Drupal\Core\Entity\ContentEntityTypeInterface;
use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Entity\EntityTypeBundleInfo;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\SubformState;
use Drupal\Core\Plugin\PluginFormFactoryInterface;
use Drupal\Core\Plugin\PluginWithFormsInterface;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a form for editing Gatsby Endpoint entities.
*/
class GatsbyEndpointForm extends EntityForm {
/**
* The plugin form manager.
*
* @var \Drupal\Core\Plugin\PluginFormFactoryInterface
*/
protected $pluginFormFactory;
/**
* The entity bundle service.
*
* @var \Drupal\Core\Entity\EntityTypeBundleInfo
*/
protected $entityTypeBundleInfo;
/**
* GatsbyEndpointForm constructor.
*
* @param \Drupal\Core\Plugin\PluginFormFactoryInterface $plugin_form_manager
* The plugin form manager.
* @param \Drupal\Core\Entity\EntityTypeBundleInfo $entity_type_bundle_info
* The entity type bundle info service.
*/
public function __construct(PluginFormFactoryInterface $plugin_form_manager,
EntityTypeBundleInfo $entity_type_bundle_info) {
$this->pluginFormFactory = $plugin_form_manager;
$this->entityTypeBundleInfo = $entity_type_bundle_info;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('plugin_form.factory'),
$container->get('entity_type.bundle.info')
);
}
/**
* {@inheritdoc}
*/
public function form(array $form, FormStateInterface $form_state) {
$form = parent::form($form, $form_state);
/** @var \Drupal\gatsby_endpoints\Entity\GatsbyEndpointInterface $entity */
$entity = $this->entity;
$form['label'] = [
'#type' => 'textfield',
'#title' => $this->t('Label'),
'#maxlength' => 255,
'#default_value' => $entity->label(),
'#description' => $this->t("Label for the Gatsby endpoint."),
'#required' => TRUE,
];
$form['id'] = [
'#type' => 'machine_name',
'#default_value' => $entity->id(),
'#machine_name' => [
'exists' => '\Drupal\gatsby_endpoints\Entity\GatsbyEndpoint::load',
],
'#disabled' => !$entity->isNew(),
];
// Render the Preview URLs AJAX enabled fieldset.
$preview_description = $this->t("Enter any Gatsby Live Preview URLs to trigger for this endpoint.");
$this->addAjaxFieldset($form, $form_state, 'preview', $preview_description);
// Render the Build URLs AJAX enabled fieldset.
$build_description = $this->t("Enter any Gatsby Build Hooks or Build URLs to trigger for this endpoint.");
$this->addAjaxFieldset($form, $form_state, 'build', $build_description);
$build_entities_description = $this->t("Select which entities should trigger builds/previews for this endpoint.");
$this->addEntityAjaxFieldset($form, $form_state, 'build', $build_entities_description);
$form['build_trigger'] = [
'#type' => 'select',
'#options' => [
'incremental' => $this->t("Trigger builds incrementally (requires Gatsby Cloud)"),
'cron' => $this->t("Trigger builds on cron runs"),
'manual' => $this->t("Trigger builds manually with the built in drush command"),
],
'#title' => $this->t("Build Trigger"),
'#default_value' => $entity->getBuildTrigger() ? $entity->getBuildTrigger() : 'incremental',
'#description' => $this->t('Select how Gatsby build URLs should be
triggered. Incremental builds require a
<a href="@gatsby-link">Gatsby Cloud Account</a>. This setting has no
effect if there are no build URLs entered above.',
[
'@gatsby-link' => 'https://gatsbyjs.com',
]
),
'#required' => TRUE,
];
$form['weight'] = [
'#type' => 'number',
'#title' => $this->t('Weight'),
'#max' => 100,
'#min' => -100,
'#size' => 3,
'#default_value' => $entity->getWeight() ? $entity->getWeight() : 0,
'#description' => $this->t("Set the weight, lighter endpoints will be rendered first."),
'#required' => TRUE,
];
$form['#tree'] = TRUE;
$form['settings'] = [];
$subform_state = SubformState::createForSubform($form['settings'], $form, $form_state);
$form['settings'] = $this->getPluginForm($entity->getPlugin())
->buildConfigurationForm($form['settings'], $subform_state);
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// Remove empty Build and Preview URLs.
$this->removeEmptyUrls($form_state, 'preview');
$this->removeEmptyUrls($form_state, 'build');
parent::submitForm($form, $form_state);
/** @var \Drupal\gatsby_endpoints\Entity\GatsbyEndpointInterface $entity */
$entity = $this->entity;
$sub_form_state = SubformState::createForSubform($form['settings'], $form, $form_state);
// Call the plugin submit handler.
$this->getPluginForm($entity->getPlugin())
->submitConfigurationForm($form, $sub_form_state);
$entity->save();
$this->messenger()->addStatus($this->t('The Gatsby endpoint configuration has been saved.'));
$form_state->setRedirectUrl(Url::fromRoute('gatsby_endpoints.gatsby_endpoints_collection'));
}
/**
* {@inheritdoc}
*/
protected function getPluginForm(GatsbyEndpointInterface $gatsbyEndpoint) {
if ($gatsbyEndpoint instanceof PluginWithFormsInterface) {
return $this->pluginFormFactory->createInstance($gatsbyEndpoint, 'configure');
}
return $gatsbyEndpoint;
}
/**
* Adds a Form Element to an AJAX Fieldset.
*/
public function addFormElement(array &$form, FormStateInterface $form_state, $key, $element) {
$cnt = $form_state->get($key . '_' . $element . '_cnt');
$form_state->set($key . '_' . $element . '_cnt', $cnt + 1);
$form_state->setRebuild();
}
/**
* Removes a Form Element from an AJAX Fieldset.
*/
public function removeFormElement(array &$form, FormStateInterface $form_state, $key, $element) {
$cnt = $form_state->get($key . '_' . $element . '_cnt');
if ($cnt > 1) {
$form_state->set($key . '_' . $element . '_cnt', $cnt - 1);
}
$form_state->setRebuild();
}
/**
* Adds a Build Url.
*/
public function addBuildUrl(array &$form, FormStateInterface $form_state) {
$this->addFormElement($form, $form_state, 'build', 'url');
}
/**
* Removes a Build Url.
*/
public function removeBuildUrl(array &$form, FormStateInterface $form_state) {
$this->removeFormElement($form, $form_state, 'build', 'url');
}
/**
* Ajax callback for Build Urls that returns the correct fieldset.
*/
public function buildUrlCallback(array &$form, FormStateInterface $form_state) {
return $form['build_urls'];
}
/**
* Adds a Preview Url.
*/
public function addPreviewUrl(array &$form, FormStateInterface $form_state) {
$this->addFormElement($form, $form_state, 'preview', 'url');
}
/**
* Removes a Preview Url.
*/
public function removePreviewUrl(array &$form, FormStateInterface $form_state) {
$this->removeFormElement($form, $form_state, 'preview', 'url');
}
/**
* Ajax callback for Preview Urls that returns the correct fieldset.
*/
public function previewUrlCallback(array &$form, FormStateInterface $form_state) {
return $form['preview_urls'];
}
/**
* Renders AJAX fieldsets for Build and Preview URLs.
*/
public function addAjaxFieldset(array &$form, FormStateInterface $form_state, $key, $description) {
$label = ucfirst($key);
$url_cnt = $form_state->get($key . '_url_cnt');
/** @var \Drupal\gatsby_endpoints\Entity\GatsbyEndpointInterface $entity */
$entity = $this->entity;
// If there is no count yet, then this is the first time rendering.
if ($url_cnt === NULL) {
$urls = $entity->getUrls($key);
$url_cnt = !empty($urls) && !empty($urls[$key . '_url']) ? count($urls[$key . '_url']) : 0;
if ($url_cnt === 0) {
$url_cnt = 1;
}
$form_state->set($key . '_url_cnt', $url_cnt);
}
$form[$key . '_urls'] = [
'#type' => 'fieldset',
'#title' => $this->t("Gatsby @label URLs", ['@label' => $label]),
'#prefix' => '<div id="' . $key . '-urls-fieldset-wrapper">',
'#suffix' => '</div>',
];
$form[$key . '_urls']['description'] = [
'#markup' => $description,
'#weight' => -100,
];
for ($i = 0; $i < $url_cnt; $i++) {
$form[$key . '_urls'][$key . '_url'][$i] = [
'#type' => 'url',
'#title' => $this->t('Gatsby @label URL #@cnt', [
'@label' => $label,
'@cnt' => $i + 1,
]),
'#maxlength' => 255,
'#default_value' => !empty($urls[$key . '_url'][$i]) ? $urls[$key . '_url'][$i] : "",
'#required' => FALSE,
];
}
$form[$key . '_urls']['actions'] = [
'#type' => 'actions',
];
$form[$key . '_urls']['actions']['add_' . $key . '_url'] = [
'#type' => 'submit',
'#value' => $this->t('Add Additional @label URL', ['@label' => $label]),
'#submit' => ['::add' . $label . 'Url'],
'#ajax' => [
'callback' => '::' . $key . 'UrlCallback',
'wrapper' => $key . '-urls-fieldset-wrapper',
],
];
if ($url_cnt > 1) {
$form[$key . '_urls']['actions']['remove_' . $key . '_url'] = [
'#type' => 'submit',
'#value' => $this->t('Remove @label URL', ['@label' => $label]),
'#submit' => ['::remove' . $label . 'Url'],
'#ajax' => [
'callback' => '::' . $key . 'UrlCallback',
'wrapper' => $key . '-urls-fieldset-wrapper',
],
];
}
}
/**
* Removes empty URLs from preview and build fieldsets.
*/
public function removeEmptyUrls(FormStateInterface $form_state, $key) {
$values = $form_state->getValue($key . '_urls');
$values[$key . '_url'] = array_values(array_filter($values[$key . '_url']));
$form_state->setValue($key . '_urls', $values);
}
/**
* Adds a Build Entity Fieldset.
*/
public function addBuildEntityFieldset(array &$form, FormStateInterface $form_state) {
$this->addFormElement($form, $form_state, 'build', 'entity');
}
/**
* Removes a Build Entity Fieldset Url.
*/
public function removeBuildEntityFieldset(array &$form, FormStateInterface $form_state) {
$this->removeFormElement($form, $form_state, 'build', 'entity');
}
/**
* Ajax callback for Build Entity Fieldsets that returns the correct fieldset.
*/
public function buildEntityCallback(array &$form, FormStateInterface $form_state) {
return $form['build_entity_types'];
}
/**
* Ajax callback for build entities that returns the correct bundles.
*/
public function buildEntityBundleCallback(array &$form, FormStateInterface $form_state) {
// Determine what element triggered this callback.
$triggering_element = $form_state->getTriggeringElement();
$wrapper_elements = explode('-', $triggering_element['#ajax']['wrapper']);
$element_id = intval(array_pop($wrapper_elements));
$form['build_entity_types'][$element_id]['#open'] = TRUE;
return $form['build_entity_types'][$element_id];
}
/**
* Renders AJAX fieldsets for Entity selection.
*/
public function addEntityAjaxFieldset(array &$form, FormStateInterface $form_state, $key, $description) {
$label = ucfirst($key);
$entity_cnt = $form_state->get($key . '_entity_cnt');
/** @var \Drupal\gatsby_endpoints\Entity\GatsbyEndpointInterface $entity */
$entity = $this->entity;
// If there is no count yet, then this is the first time rendering.
if ($entity_cnt === NULL) {
$entities = $entity->getEntityTypes($key);
$entity_cnt = !empty($entities) ? count($entities) - 1 : 0;
if ($entity_cnt === 0) {
$entity_cnt = 1;
}
$form_state->set($key . '_entity_cnt', $entity_cnt);
}
$form[$key . '_entity_types'] = [
'#type' => 'fieldset',
'#title' => $this->t("Gatsby @label Entities", ['@label' => $label]),
'#prefix' => '<div id="' . $key . '-entity-fieldset-wrapper">',
'#suffix' => '</div>',
];
$form[$key . '_entity_types']['description'] = [
'#markup' => $description,
'#weight' => -100,
];
for ($i = 0; $i < $entity_cnt; $i++) {
// Get the default entity type.
$entity_type = !empty($entities[$i]['entity_type']) ?
$entities[$i]['entity_type'] : "";
// Check if there was an entity type set in the form state.
$form_values = $form_state->getValues();
if (!empty($form_values[$key . '_entity_types'][$i]['entity_type'])) {
$entity_type = $form_values[$key . '_entity_types'][$i]['entity_type'];
}
$content_entity_types = $this->getContentEntityTypes();
$form[$key . '_entity_types'][$i] = [
'#type' => 'details',
'#title' => $this->t("Gatsby @label Entity #@cnt @entity", [
'@label' => $label,
'@cnt' => $i + 1,
'@entity' => !empty($entity_type) ? "(" . $content_entity_types[$entity_type] . ")" : "",
]),
'#prefix' => '<div id="' . $key . '-entity-fieldset-' . $i . '">',
'#suffix' => '</div>',
'#open' => empty($entity_type) ? TRUE : FALSE,
];
$entity_types = ['' => $this->t("-- Select Entity Type --")] + $content_entity_types;
$form[$key . '_entity_types'][$i]['entity_type'] = [
'#type' => 'select',
'#options' => $entity_types,
'#title' => $this->t("Entity Type"),
'#default_value' => $entity_type,
];
$form[$key . '_entity_types'][$i]['entity_type']['#ajax'] = [
'callback' => '::' . $key . 'EntityBundleCallback',
'wrapper' => $key . '-entity-fieldset-' . $i,
'event' => 'change',
'progress' => [
'type' => 'throbber',
'message' => $this->t('Loading bundles...'),
],
];
if ($entity_type) {
$entity_bundles = !empty($entities[$i]['entity_bundles']) ?
$entities[$i]['entity_bundles'] : [];
$include_entities = !empty($entities[$i]['include_entities']) ?
$entities[$i]['include_entities'] : [];
$form[$key . '_entity_types'][$i]['entity_bundles'] = [
'#type' => 'checkboxes',
'#options' => $this->getContentEntityBundles($entity_type),
'#title' => $this->t("Entity Bundle(s)"),
'#default_value' => $entity_bundles,
];
if ($entity_type == 'node') {
$build_published = !empty($entities[$i]['build_published']) ?
$entities[$i]['build_published'] : FALSE;
$form[$key . '_entity_types'][$i]['build_published'] = [
'#type' => 'checkbox',
'#title' => $this->t('Only trigger builds for published content'),
'#description' => $this->t('Depending on your content workflow, you may only
want builds to be triggered for published content. By checking this box
only published content will trigger a build.'),
'#default_value' => $build_published,
'#weight' => 3,
];
}
$included_entities_description = $this->t("Select which entities should not trigger builds/previews but should be
included in builds/previews. This is commonly used for entities such as media, files, or paragraphs where you don't
want these items to trigger a new build, but you do want to make sure they are sent to Gatsby if they are
attached to an entity that triggers a build.");
$form[$key . '_entity_types'][$i]['include_entities'] = [
'#type' => 'checkboxes',
'#options' => $content_entity_types,
'#title' => $this->t("Include Entities"),
'#description' => $included_entities_description,
'#default_value' => $include_entities,
'#weight' => 4,
];
}
}
$form[$key . '_entity_types']['actions'] = [
'#type' => 'actions',
];
$form[$key . '_entity_types']['actions']['add_' . $key . '_entity'] = [
'#type' => 'submit',
'#value' => $this->t('Add Additional @label Entity', ['@label' => $label]),
'#submit' => ['::add' . $label . 'EntityFieldset'],
'#ajax' => [
'callback' => '::' . $key . 'EntityCallback',
'wrapper' => $key . '-entity-fieldset-wrapper',
],
];
if ($entity_cnt > 1) {
$form[$key . '_entity_types']['actions']['remove_' . $key . '_entity'] = [
'#type' => 'submit',
'#value' => $this->t('Remove @label Entity', ['@label' => $label]),
'#submit' => ['::remove' . $label . 'EntityFieldset'],
'#ajax' => [
'callback' => '::' . $key . 'EntityCallback',
'wrapper' => $key . '-entity-fieldset-wrapper',
],
];
}
}
/**
* Gets a list of all the defined content entities in the system.
*
* @return array
* An array of content entities definitions.
*/
private function getContentEntityTypes() {
$content_entity_types = [];
$allEntityTypes = $this->entityTypeManager->getDefinitions();
foreach ($allEntityTypes as $entity_type_id => $entity_type) {
// Add all content entity types but not the gatsby log entity provided
// by the gatsby_fastbuilds module (if it exists).
if ($entity_type instanceof ContentEntityTypeInterface &&
$entity_type_id !== 'gatsby_log_entity') {
$content_entity_types[$entity_type_id] = $entity_type->getLabel();
}
}
return $content_entity_types;
}
/**
* Gets a list of all the defined bundles for a content entity type.
*
* @return array
* An array of bundles for a specific content entity type.
*/
private function getContentEntityBundles($entity_type) {
$bundle_definitions = $this->entityTypeBundleInfo->getBundleInfo($entity_type);
$bundles = [];
foreach ($bundle_definitions as $bundle => $bundle_definition) {
$bundles[$bundle] = $bundle_definition['label'];
}
return $bundles;
}
}
