config_preview_deploy-1.0.0-alpha3/src/Form/RebaseForm.php
src/Form/RebaseForm.php
<?php
declare(strict_types=1);
namespace Drupal\config_preview_deploy\Form;
use Drupal\config_preview_deploy\ConfigVerifier;
use Drupal\config_preview_deploy\ConfigRebaser;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
/**
* Rebase environment form.
*/
class RebaseForm extends FormBase {
/**
* The config verifier service.
*/
protected ConfigVerifier $configVerifier;
/**
* The config rebaser.
*/
protected ConfigRebaser $configRebase;
/**
* The private tempstore.
*/
protected PrivateTempStoreFactory $tempStoreFactory;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
$instance = new static();
$instance->configVerifier = $container->get('config_preview_deploy.config_verifier');
$instance->configRebase = $container->get('config_preview_deploy.config_rebase');
$instance->tempStoreFactory = $container->get('tempstore.private');
return $instance;
}
/**
* {@inheritdoc}
*/
public function getFormId(): string {
return 'config_preview_deploy_rebase_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state): array {
$form['#title'] = $this->t('Rebase Environment');
// Check if OAuth access token is available and valid.
$tempstore = $this->tempStoreFactory->get('config_preview_deploy');
$access_token = $tempstore->get('access_token');
$tokenReceivedAt = $tempstore->get('token_received_at');
// 4 minutes
$maxAge = 240;
$needsAuth = empty($access_token) || !$tokenReceivedAt || (time() - $tokenReceivedAt) > $maxAge;
if ($needsAuth) {
$message = $this->t('You need to authorize with production before rebasing.');
if ($access_token && (!$tokenReceivedAt || (time() - $tokenReceivedAt) > $maxAge)) {
$message = $this->t('Your authorization has expired. Please re-authorize with production.');
}
$form['not_authorized'] = [
'#markup' => '<div class="messages messages--warning">' . $message . '</div>',
];
$form['actions']['authorize'] = [
'#type' => 'link',
'#title' => $this->t('Authorize'),
'#url' => Url::fromRoute('config_preview_deploy.oauth_authorize', [], ['query' => ['target' => 'rebase']]),
'#attributes' => ['class' => ['button', 'button--primary']],
];
$form['actions']['back'] = [
'#type' => 'link',
'#title' => $this->t('Back to Dashboard'),
'#url' => Url::fromRoute('config_preview_deploy.dashboard'),
'#attributes' => ['class' => ['button']],
];
return $form;
}
$form['warning'] = [
'#markup' => '<div class="messages messages--warning">' .
$this->t('This will rebase your configuration changes on the latest production configuration. Use the Config Checkpoint UI module to rollback if needed.') .
'</div>',
];
$form['description'] = [
'#type' => 'details',
'#title' => $this->t('What happens during rebase'),
'#open' => TRUE,
];
$form['description']['steps'] = [
'#theme' => 'item_list',
'#items' => [
$this->t('Your current configuration changes will be saved'),
$this->t('Production configuration will be fetched via authenticated API'),
$this->t('Production configuration will be imported'),
$this->t('A new base checkpoint will be created'),
$this->t('Your saved changes will be re-applied'),
$this->t('Any conflicts will be reported for manual resolution'),
],
];
$form['confirmation'] = [
'#type' => 'checkbox',
'#title' => $this->t('I understand that the environment is reset to production configuration and changes of the current environment are re-applied afterwards. If there are conflicting changes and re-applying fails, the environment is left with the latest state of production. The conflicting changes must be re-done. Alternatively, the Config Checkpoint UI module may be used to rollback to the previous state.'),
'#required' => TRUE,
];
$form['actions'] = [
'#type' => 'actions',
];
$form['actions']['rebase'] = [
'#type' => 'submit',
'#value' => $this->t('Rebase Configuration'),
'#button_type' => 'primary',
];
$form['actions']['cancel'] = [
'#type' => 'link',
'#title' => $this->t('Cancel'),
'#url' => Url::fromRoute('config_preview_deploy.dashboard'),
'#attributes' => ['class' => ['button']],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state): void {
try {
// Perform rebase by fetching from production.
$result = $this->configRebase->rebase();
if ($result['success']) {
$this->messenger()->addStatus($this->t('Configuration successfully rebased on production.'));
$form_state->setRedirect('config_preview_deploy.dashboard');
}
else {
// Combine all conflict information into one error message.
$errorMessage = $this->t('Rebase completed but conflicts were found. Your original changes have been saved to temporary files for manual review.');
if (!empty($result['conflicts_file'])) {
$errorMessage .= ' ' . $this->t('Saved changes file: @file', [
'@file' => $result['conflicts_file'],
]);
}
if (!empty($result['report_file'])) {
$errorMessage .= ' ' . $this->t('Conflict report: @file', [
'@file' => $result['report_file'],
]);
}
$this->messenger()->addError($errorMessage);
}
}
catch (\Exception $e) {
$this->messenger()->addError($this->t('Failed to rebase: @message', ['@message' => $e->getMessage()]));
}
}
}
