View source
<?php
namespace Drupal\cms_content_sync\Form;
use Drupal\cms_content_sync\Entity\Flow;
use Drupal\cms_content_sync\Entity\Pool;
use Drupal\cms_content_sync\SyncCoreInterface\SyncCoreFactory;
use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandler;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Link;
use Drupal\Core\Site\Settings;
use EdgeBox\SyncCore\Exception\SyncCoreException;
use EdgeBox\SyncCore\V1\Helper;
use Symfony\Component\DependencyInjection\ContainerInterface;
class PoolForm extends EntityForm {
public const siteIdMaxLength = 20;
public const STEP_SYNC_CORE = 'sync-core';
public const STEP_POOL = 'pool';
public const CONTAINER_ID = 'pool-form-container';
protected $backendUrl;
protected $configMachineName;
protected $overwrittenSiteId;
public function __construct(EntityTypeManagerInterface $entityTypeManager, ModuleHandler $moduleHandler) {
$this->entityTypeManager = $entityTypeManager;
$this->moduleHandler = $moduleHandler;
}
public static function create(ContainerInterface $container) {
return new static($container
->get('entity_type.manager'), $container
->get('module_handler'));
}
public function ajaxReturn($form, FormStateInterface $form_state) {
return $form['elements'];
}
public function connectSyncCore($form, FormStateInterface $form_state) {
$form_state
->setRebuild();
}
public function checkSiteId($form, FormStateInterface $form_state) {
$form_state
->setRebuild();
}
public function createNew($form, FormStateInterface $form_state) {
$form_state
->setValue('id', 'new');
$data = $form_state
->getUserInput();
$data['id'] = 'new';
$form_state
->setUserInput($data);
$form_state
->setRebuild();
}
public function form(array $form, FormStateInterface $form_state) {
$form = parent::form($form, $form_state);
$form['#tree'] = false;
$defaults = $this
->getDefaults($form_state);
if (!empty($defaults['backend_url'])) {
$form['default_backend_url'] = [
'#type' => 'hidden',
'#value' => $defaults['backend_url'],
];
}
if (!empty($defaults['name'])) {
$form['default_name'] = [
'#type' => 'hidden',
'#value' => $defaults['name'],
];
}
if (!empty($defaults['id'])) {
$form['default_id'] = [
'#type' => 'hidden',
'#value' => $defaults['id'],
];
}
$pool = $this->entity;
if (!is_null($pool->id)) {
$this->configMachineName = $pool->id;
$cms_content_sync_settings = Settings::get('cms_content_sync');
if (!is_null($cms_content_sync_settings) && isset($cms_content_sync_settings['pools'][$pool->id]['backend_url'])) {
$this->backendUrl = $cms_content_sync_settings['pools'][$pool->id]['backend_url'];
}
}
if (!isset($this->configMachineName)) {
$this->configMachineName = '<machine_name_of_the_configuration>';
}
$step = $this
->getCurrentFormStep($form_state);
if (self::STEP_SYNC_CORE === $step) {
$elements = $this
->syncCoreForm($form, $form_state);
}
else {
$elements = $this
->poolForm($form, $form_state);
}
$form['elements'] = array_merge([
'#prefix' => '<div id="' . self::CONTAINER_ID . '">',
'#suffix' => '</div>',
'step' => [
'#type' => 'hidden',
'#value' => $step,
],
], $elements);
return $form;
}
public function validateForm(array &$form, FormStateInterface $form_state) {
parent::validateForm($form, $form_state);
$step = $this
->getLastFormStep($form_state);
$url = $form_state
->getValue('backend_url');
$client = SyncCoreFactory::getSyncCore($url);
$allowed_length = 0;
try {
$client
->getReportingService()
->getStatus();
$longest_entity_name = 0;
$bundleInfoService = \Drupal::service('entity_type.bundle.info');
$entity_types = $bundleInfoService
->getAllBundleInfo();
foreach ($entity_types as $type_key => $entity_type) {
foreach ($entity_type as $entity_bundle_name => $entity_bundle) {
$length = strlen($type_key) + strlen($entity_bundle_name) + 1;
if ($length > $longest_entity_name) {
$longest_entity_name = $length;
}
}
}
$allowed_length = 120 - 7;
$allowed_length = $allowed_length - 6 - 1 - 1 - 1 - $longest_entity_name - 1;
} catch (SyncCoreException $e) {
$form_state
->setErrorByName('backend_url', $this
->t('The Sync Core is not accessible (did not respond with 200 OK). Please configure your outbound traffic to allow traffic to the Sync Core. The error message is @message', [
'@message' => $e
->getMessage(),
]));
}
if (self::STEP_POOL === $step) {
$api = $form_state
->getValue('id');
if (!preg_match('@^([a-z0-9\\-_]+)$@', $api)) {
$form_state
->setErrorByName('id', $this
->t('Please only use letters, numbers and dashes.'));
}
if ('drupal' == $api || 'api-unify' == $api) {
$form_state
->setErrorByName('api', $this
->t('This name is reserved.'));
}
$site_id = $form_state
->getValue('site_id');
$length = strlen($api) + strlen($site_id);
if ($length > $allowed_length) {
$form_state
->setErrorByName('id', $this
->t('The Pool machine name + site id combined mustn\'t be longer than @allowed characters.', [
'@allowed' => $allowed_length,
]));
}
}
}
public function save(array $form, FormStateInterface $form_state) {
$pool = $this->entity;
if (!$pool
->label()) {
$pool->label = $this
->getRemotePools($form_state)[$pool
->id()];
}
$status = $pool
->save();
if ($status) {
if (empty(Flow::getAll())) {
$link = Link::createFromRoute($this
->t('Create a Flow'), 'entity.cms_content_sync_flow.add_form')
->toString();
\Drupal::messenger()
->addStatus($this
->t('Saved Pool %label. Well done! @create to continue the setup.', [
'%label' => $pool
->label(),
'@create' => $link,
]));
\Drupal::messenger()
->addStatus($this
->t('If you have connected another site already, @copy. Mirroring means you can simply swap the push and pull settings.', [
'@copy' => Link::createFromRoute('copy or mirror the configuration from another site', 'entity.cms_content_sync_flow.copy_remote')
->toString(),
]));
}
else {
\Drupal::messenger()
->addStatus($this
->t('Saved Pool %label.', [
'%label' => $pool
->label(),
]));
}
}
else {
\Drupal::messenger()
->addStatus($this
->t('The %label Pool could not be saved.', [
'%label' => $pool
->label(),
]));
}
$destination = \Drupal::request()->query
->get('destination');
\Drupal::request()->query
->remove('destination');
$options = [];
if (!empty($destination)) {
$options['query']['destination'] = $destination;
}
$form_state
->setRedirect('entity.cms_content_sync_pool.export', [
'cms_content_sync_pool' => $this->entity
->id(),
], $options);
}
public function exist($id) {
$entity = $this->entityTypeManager
->getStorage('cms_content_sync_pool')
->getQuery()
->condition('id', $id)
->execute();
return (bool) $entity;
}
protected function getDefaults(FormStateInterface $form_state) {
return [
'backend_url' => empty($_GET['backend_url']) ? $form_state
->getValue('default_backend_url') : $_GET['backend_url'],
'name' => empty($_GET['name']) ? $form_state
->getValue('default_name') : $_GET['name'],
'id' => empty($_GET['id']) ? $form_state
->getValue('default_id') : $_GET['id'],
];
}
protected function getCurrentFormStep(FormStateInterface $form_state) {
$pool = $this->entity;
if (!$pool
->getSyncCoreUrl() && !$form_state
->getValue('backend_url')) {
return self::STEP_SYNC_CORE;
}
return self::STEP_POOL;
}
protected function syncCoreForm(array $form, FormStateInterface $form_state, $collapsed = false) {
$pool = $this->entity;
if ($collapsed) {
$elements['backend_url'] = [
'#type' => 'hidden',
'#value' => $form_state
->getValue('backend_url') ? $form_state
->getValue('backend_url') : $pool
->getSyncCoreUrl(),
];
return $elements;
}
$elements['headline'] = [
'#markup' => '<br><br><h1>Step 1: Pick Sync Core</h1>' . '<div class="messages messages--status">' . $this
->t("The Sync Core is the central distribution service used to syndicate content between sites. If you don't have one yet, just <a href='mailto:info@cms-content-sync.io?subject=Evaluation%20Sync%20Core&body=Please%20provide%20me%20an%20evaluation%20Sync%20Core'>contact us</a>. <br>\n<br>\n<strong>Want to learn more?</strong><br>\nUse <a href='https://www.cms-content-sync.io/content-staging/'>Content Staging</a> to publish content from a private site to a public site. Pricing for Content Staging is documented <a href='https://www.cms-content-sync.io/content-sync-for-drupal/content-staging-for-drupal/'>here</a>.<br>\nUse <a href='https://www.cms-content-sync.io/content-syndication/'>Content Syndication</a> to connect many public sites to share content between them. Pricing for Content Syndication is documented <a href='https://www.cms-content-sync.io/content-sync-for-drupal/content-syndication-for-drupal/'>here</a>.\n") . '</div><br><br>',
];
$default_backend_url = $this
->getDefaults($form_state)['backend_url'];
$pools = Pool::getAll();
$urls = [];
$default_auth = null;
foreach ($pools as $existing_pool) {
$url = $existing_pool
->getSyncCoreUrl();
$urls[$url] = Helper::obfuscateCredentials($url);
}
if (count($urls) && !isset($_GET['add-sync-core']) && empty($default_backend_url)) {
$url = $pool
->getSyncCoreUrl();
if ($url) {
$urls[$url] = Helper::obfuscateCredentials($url);
}
$elements['backend_url'] = [
'#type' => 'radios',
'#title' => $this
->t('Sync Core URL'),
'#default_value' => $url ? $url : array_slice(array_keys($urls), 0, 1)[0],
'#description' => $this
->t('The backend url can be overwritten within your environment specific settings.php file by using <i>@settings</i>.', [
'@settings' => '$settings["cms_content_sync"]["pools"]["' . $this->configMachineName . '"]["backend_url"] = "http://cms-content-sync-example:8691/rest"',
'@config_machine_name' => $this->configMachineName,
]) . '<br><a href="?add-sync-core">Add new</a>',
'#options' => $urls,
'#required' => true,
];
}
else {
$elements['backend_url'] = [
'#type' => 'url',
'#title' => $this
->t('Sync Core URL'),
'#default_value' => empty($default_backend_url) ? $pool
->getSyncCoreUrl() : $default_backend_url,
'#description' => $this
->t('The backend url can be overwritten within your environment specific settings.php file by using <i>@settings</i>.', [
'@settings' => '$settings["cms_content_sync"]["pools"]["' . $this->configMachineName . '"]["backend_url"] = "http://cms-content-sync-example.de:8691/rest"',
'@config_machine_name' => $this->configMachineName,
]),
'#required' => true,
];
}
if (isset($this->backendUrl)) {
$elements['backend_url']['#disabled'] = true;
$elements['backend_url']['#default_value'] = $this->backendUrl;
$elements['backend_url']['#description'] = $this
->t('The backend url is set within the environment specific settings.php file.');
}
$elements['continue'] = [
'#prefix' => '<br><br>',
'#type' => 'submit',
'#submit' => [
'::connectSyncCore',
],
'#value' => $this
->t('Connect'),
'#name' => 'connect',
'#attributes' => [
'class' => [
'button--primary',
],
],
'#ajax' => [
'callback' => '::ajaxReturn',
'wrapper' => self::CONTAINER_ID,
'method' => 'replace',
'effect' => 'fade',
'progress' => [
'type' => 'throbber',
'message' => 'loading settings...',
],
],
];
return $elements;
}
protected function poolForm(array $form, FormStateInterface $form_state) {
$elements = $this
->syncCoreForm($form, $form_state, true);
$elements['headline'] = [
'#markup' => '<br><br><h1>Step 3: Pool properties</h1>',
];
$pool = $this->entity;
$default_name = $this
->getDefaults($form_state)['name'];
$default_id = $this
->getDefaults($form_state)['id'];
$options = $pool
->isNew() && empty($default_id) ? $this
->getRemotePools($form_state) : [];
if (count($options) && !$form_state
->getValue('id')) {
$elements['id'] = [
'#type' => 'radios',
'#title' => $this
->t('Existing pools'),
'#required' => true,
'#default_value' => array_slice(array_keys($options), 0, 1)[0],
'#options' => $options,
];
}
else {
$elements['label'] = [
'#type' => 'textfield',
'#title' => $this
->t('Label'),
'#maxlength' => 255,
'#default_value' => empty($default_name) ? $form_state
->getValue('label') ? $form_state
->getValue('label') : ($pool
->label() ? $pool
->label() : (empty(Pool::getAll()['content']) ? $this
->t('Content') : '')) : $default_name,
'#description' => $this
->t("The pool name. If you aren't working with multiple pools just leave it as it is."),
'#required' => true,
];
$elements['id'] = [
'#type' => 'machine_name',
'#default_value' => empty($default_id) ? $form_state
->getValue('id') ? $form_state
->getValue('id') : ($pool
->id() ? $pool
->id() : (empty(Pool::getAll()['content']) ? 'content' : '')) : $default_id,
'#machine_name' => [
'exists' => [
$this,
'exist',
],
],
'#description' => $this
->t("A unique ID that must be identical on all sites that want to connect to this pool. If you aren't working with multiple pools just leave it as it is."),
'#disabled' => !$pool
->isNew(),
];
}
if ($form_state
->getTriggeringElement()) {
$actions = $this
->actions($form, $form_state);
$actions['submit']['#attributes']['class'][] = 'button--primary';
$elements = array_merge($elements, $actions);
}
if (!isset($elements['label'])) {
$elements['create'] = [
'#prefix' => '<br><br>',
'#type' => 'submit',
'#submit' => [
'::createNew',
],
'#value' => $this
->t('Create new'),
'#name' => 'create',
'#ajax' => [
'callback' => '::ajaxReturn',
'wrapper' => self::CONTAINER_ID,
'method' => 'replace',
'effect' => 'fade',
'progress' => [
'type' => 'throbber',
'message' => 'loading settings...',
],
],
];
}
return $elements;
}
protected function getRemotePools(FormStateInterface $form_state) {
$client = SyncCoreFactory::getSyncCore($form_state
->getValue('backend_url'));
$pools = $client
->getConfigurationService()
->listRemotePools();
$local_pools = Pool::getAll();
foreach ($pools as $id => $name) {
if (isset($local_pools[$id])) {
unset($pools[$id]);
}
}
return $pools;
}
protected function actions(array $form, FormStateInterface $form_state) {
$step = $this
->getCurrentFormStep($form_state);
if (self::STEP_POOL !== $step) {
return [];
}
return parent::actions($form, $form_state);
}
protected function getLastFormStep(FormStateInterface $form_state) {
$step = $form_state
->getValue('step');
if (empty($step)) {
return self::STEP_SYNC_CORE;
}
return $step;
}
}