Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176

This commit is contained in:
Pantheon Automation 2015-08-17 17:00:26 -07:00 committed by Greg Anderson
commit 9921556621
13277 changed files with 1459781 additions and 0 deletions

View file

@ -0,0 +1,46 @@
<?php
/**
* @file
* Contains \Drupal\config\Form\ConfigExportForm.
*/
namespace Drupal\config\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Defines the configuration export form.
*/
class ConfigExportForm extends FormBase {
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'config_export_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form['description'] = array(
'#markup' => '<p>' . $this->t('Use the export button below to download your site configuration.') . '</p>',
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => $this->t('Export'),
);
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$form_state->setRedirect('config.export_download');
}
}

View file

@ -0,0 +1,108 @@
<?php
/**
* @file
* Contains \Drupal\config\Form\ConfigImportForm.
*/
namespace Drupal\config\Form;
use Drupal\Core\Archiver\ArchiveTar;
use Drupal\Core\Config\StorageInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Defines the configuration import form.
*/
class ConfigImportForm extends FormBase {
/**
* The configuration storage.
*
* @var \Drupal\Core\Config\StorageInterface
*/
protected $configStorage;
/**
* Constructs a new ConfigImportForm.
*
* @param \Drupal\Core\Config\StorageInterface $config_storage
* The configuration storage.
*/
public function __construct(StorageInterface $config_storage) {
$this->configStorage = $config_storage;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('config.storage.staging')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'config_import_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form['import_tarball'] = array(
'#type' => 'file',
'#title' => $this->t('Configuration archive'),
'#description' => $this->t('Allowed types: @extensions.', array('@extensions' => 'tar.gz tgz tar.bz2')),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => $this->t('Upload'),
);
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
$file_upload = $this->getRequest()->files->get('files[import_tarball]', NULL, TRUE);
if ($file_upload && $file_upload->isValid()) {
$form_state->setValue('import_tarball', $file_upload->getRealPath());
}
else {
$form_state->setErrorByName('import_tarball', $this->t('The file could not be uploaded.'));
}
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
if ($path = $form_state->getValue('import_tarball')) {
$this->configStorage->deleteAll();
try {
$archiver = new ArchiveTar($path, 'gz');
$files = array();
foreach ($archiver->listContent() as $file) {
$files[] = $file['filename'];
}
$archiver->extractList($files, config_get_config_directory(CONFIG_STAGING_DIRECTORY));
drupal_set_message($this->t('Your configuration files were successfully uploaded and are ready for import.'));
$form_state->setRedirect('config.sync');
}
catch (\Exception $e) {
drupal_set_message($this->t('Could not extract the contents of the tar file. The error message is <em>@message</em>', array('@message' => $e->getMessage())), 'error');
}
drupal_unlink($path);
}
}
}

View file

@ -0,0 +1,206 @@
<?php
/**
* @file
* Contains \Drupal\config\Form\ConfigSingleExportForm.
*/
namespace Drupal\config\Form;
use Drupal\Component\Serialization\Yaml;
use Drupal\Core\Config\StorageInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormState;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a form for exporting a single configuration file.
*/
class ConfigSingleExportForm extends FormBase {
/**
* The entity manager.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
*/
protected $entityManager;
/**
* The config storage.
*
* @var \Drupal\Core\Config\StorageInterface
*/
protected $configStorage;
/**
* Tracks the valid config entity type definitions.
*
* @var \Drupal\Core\Entity\EntityTypeInterface[]
*/
protected $definitions = array();
/**
* Constructs a new ConfigSingleImportForm.
*
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
* @param \Drupal\Core\Config\StorageInterface $config_storage
* The config storage.
*/
public function __construct(EntityManagerInterface $entity_manager, StorageInterface $config_storage) {
$this->entityManager = $entity_manager;
$this->configStorage = $config_storage;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity.manager'),
$container->get('config.storage')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'config_single_export_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, $config_type = NULL, $config_name = NULL) {
foreach ($this->entityManager->getDefinitions() as $entity_type => $definition) {
if ($definition->isSubclassOf('Drupal\Core\Config\Entity\ConfigEntityInterface')) {
$this->definitions[$entity_type] = $definition;
}
}
$entity_types = array_map(function (EntityTypeInterface $definition) {
return $definition->getLabel();
}, $this->definitions);
// Sort the entity types by label, then add the simple config to the top.
uasort($entity_types, 'strnatcasecmp');
$config_types = array(
'system.simple' => $this->t('Simple configuration'),
) + $entity_types;
$form['config_type'] = array(
'#title' => $this->t('Configuration type'),
'#type' => 'select',
'#options' => $config_types,
'#default_value' => $config_type,
'#ajax' => array(
'callback' => '::updateConfigurationType',
'wrapper' => 'edit-config-type-wrapper',
),
);
$default_type = $form_state->getValue('config_type', $config_type);
$form['config_name'] = array(
'#title' => $this->t('Configuration name'),
'#type' => 'select',
'#options' => $this->findConfiguration($default_type),
'#default_value' => $config_name,
'#required' => TRUE,
'#prefix' => '<div id="edit-config-type-wrapper">',
'#suffix' => '</div>',
'#ajax' => array(
'callback' => '::updateExport',
'wrapper' => 'edit-export-wrapper',
),
);
$form['export'] = array(
'#title' => $this->t('Here is your configuration:'),
'#type' => 'textarea',
'#rows' => 24,
'#required' => TRUE,
'#prefix' => '<div id="edit-export-wrapper">',
'#suffix' => '</div>',
);
if ($config_type && $config_name) {
$fake_form_state = (new FormState())->setValues([
'config_type' => $config_type,
'config_name' => $config_name,
]);
$form['export'] = $this->updateExport($form, $fake_form_state);
}
return $form;
}
/**
* Handles switching the configuration type selector.
*/
public function updateConfigurationType($form, FormStateInterface $form_state) {
$form['config_name']['#options'] = $this->findConfiguration($form_state->getValue('config_type'));
return $form['config_name'];
}
/**
* Handles switching the export textarea.
*/
public function updateExport($form, FormStateInterface $form_state) {
// Determine the full config name for the selected config entity.
if ($form_state->getValue('config_type') !== 'system.simple') {
$definition = $this->entityManager->getDefinition($form_state->getValue('config_type'));
$name = $definition->getConfigPrefix() . '.' . $form_state->getValue('config_name');
}
// The config name is used directly for simple configuration.
else {
$name = $form_state->getValue('config_name');
}
// Read the raw data for this config name, encode it, and display it.
$form['export']['#value'] = Yaml::encode($this->configStorage->read($name));
$form['export']['#description'] = $this->t('Filename: %name', array('%name' => $name . '.yml'));
return $form['export'];
}
/**
* Handles switching the configuration type selector.
*/
protected function findConfiguration($config_type) {
$names = array(
'' => $this->t('- Select -'),
);
// For a given entity type, load all entities.
if ($config_type && $config_type !== 'system.simple') {
$entity_storage = $this->entityManager->getStorage($config_type);
foreach ($entity_storage->loadMultiple() as $entity) {
$entity_id = $entity->id();
$label = $entity->label() ?: $entity_id;
$names[$entity_id] = $label;
}
}
// Handle simple configuration.
else {
// Gather the config entity prefixes.
$config_prefixes = array_map(function (EntityTypeInterface $definition) {
return $definition->getConfigPrefix() . '.';
}, $this->definitions);
// Find all config, and then filter our anything matching a config prefix.
$names = $this->configStorage->listAll();
$names = array_combine($names, $names);
foreach ($names as $config_name) {
foreach ($config_prefixes as $config_prefix) {
if (strpos($config_name, $config_prefix) === 0) {
unset($names[$config_name]);
}
}
}
}
return $names;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// Nothing to submit.
}
}

View file

@ -0,0 +1,268 @@
<?php
/**
* @file
* Contains \Drupal\config\Form\ConfigSingleImportForm.
*/
namespace Drupal\config\Form;
use Drupal\Component\Serialization\Yaml;
use Drupal\Core\Config\StorageInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a form for importing a single configuration file.
*/
class ConfigSingleImportForm extends ConfirmFormBase {
/**
* The entity manager.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
*/
protected $entityManager;
/**
* The config storage.
*
* @var \Drupal\Core\Config\StorageInterface
*/
protected $configStorage;
/**
* If the config exists, this is that object. Otherwise, FALSE.
*
* @var \Drupal\Core\Config\Config|\Drupal\Core\Config\Entity\ConfigEntityInterface|bool
*/
protected $configExists = FALSE;
/**
* The submitted data needing to be confirmed.
*
* @var array
*/
protected $data = array();
/**
* Constructs a new ConfigSingleImportForm.
*
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
* @param \Drupal\Core\Config\StorageInterface $config_storage
* The config storage.
*/
public function __construct(EntityManagerInterface $entity_manager, StorageInterface $config_storage) {
$this->entityManager = $entity_manager;
$this->configStorage = $config_storage;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity.manager'),
$container->get('config.storage')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'config_single_import_form';
}
/**
* {@inheritdoc}
*/
public function getCancelUrl() {
return new Url('config.import_single');
}
/**
* {@inheritdoc}
*/
public function getQuestion() {
if ($this->data['config_type'] === 'system.simple') {
$name = $this->data['config_name'];
$type = $this->t('simple configuration');
}
else {
$definition = $this->entityManager->getDefinition($this->data['config_type']);
$name = $this->data['import'][$definition->getKey('id')];
$type = $definition->getLowercaseLabel();
}
$args = array(
'%name' => $name,
'@type' => strtolower($type),
);
if ($this->configExists) {
$question = $this->t('Are you sure you want to update the %name @type?', $args);
}
else {
$question = $this->t('Are you sure you want to create a new %name @type?', $args);
}
return $question;
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
// When this is the confirmation step fall through to the confirmation form.
if ($this->data) {
return parent::buildForm($form, $form_state);
}
$entity_types = array();
foreach ($this->entityManager->getDefinitions() as $entity_type => $definition) {
if ($definition->isSubclassOf('Drupal\Core\Config\Entity\ConfigEntityInterface')) {
$entity_types[$entity_type] = $definition->getLabel();
}
}
// Sort the entity types by label, then add the simple config to the top.
uasort($entity_types, 'strnatcasecmp');
$config_types = array(
'system.simple' => $this->t('Simple configuration'),
) + $entity_types;
$form['config_type'] = array(
'#title' => $this->t('Configuration type'),
'#type' => 'select',
'#options' => $config_types,
'#required' => TRUE,
);
$form['config_name'] = array(
'#title' => $this->t('Configuration name'),
'#description' => $this->t('Enter the name of the configuration file without the <em>.yml</em> extension. (e.g. <em>system.site</em>)'),
'#type' => 'textfield',
'#states' => array(
'required' => array(
':input[name="config_type"]' => array('value' => 'system.simple'),
),
'visible' => array(
':input[name="config_type"]' => array('value' => 'system.simple'),
),
),
);
$form['import'] = array(
'#title' => $this->t('Paste your configuration here'),
'#type' => 'textarea',
'#rows' => 24,
'#required' => TRUE,
);
$form['advanced'] = array(
'#type' => 'details',
'#title' => $this->t('Advanced'),
);
$form['advanced']['custom_entity_id'] = array(
'#title' => $this->t('Custom Entity ID'),
'#type' => 'textfield',
'#description' => $this->t('Specify a custom entity ID. This will override the entity ID in the configuration above.'),
);
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => $this->t('Import'),
'#button_type' => 'primary',
);
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
// The confirmation step needs no additional validation.
if ($this->data) {
return;
}
// Decode the submitted import.
$data = Yaml::decode($form_state->getValue('import'));
// Validate for config entities.
if ($form_state->getValue('config_type') !== 'system.simple') {
$definition = $this->entityManager->getDefinition($form_state->getValue('config_type'));
$id_key = $definition->getKey('id');
// If a custom entity ID is specified, override the value in the
// configuration data being imported.
if (!$form_state->isValueEmpty('custom_entity_id')) {
$data[$id_key] = $form_state->getValue('custom_entity_id');
}
$entity_storage = $this->entityManager->getStorage($form_state->getValue('config_type'));
// If an entity ID was not specified, set an error.
if (!isset($data[$id_key])) {
$form_state->setErrorByName('import', $this->t('Missing ID key "@id_key" for this @entity_type import.', array('@id_key' => $id_key, '@entity_type' => $definition->getLabel())));
return;
}
// If there is an existing entity, ensure matching ID and UUID.
if ($entity = $entity_storage->load($data[$id_key])) {
$this->configExists = $entity;
if (!isset($data['uuid'])) {
$form_state->setErrorByName('import', $this->t('An entity with this machine name already exists but the import did not specify a UUID.'));
return;
}
if ($data['uuid'] !== $entity->uuid()) {
$form_state->setErrorByName('import', $this->t('An entity with this machine name already exists but the UUID does not match.'));
return;
}
}
// If there is no entity with a matching ID, check for a UUID match.
elseif (isset($data['uuid']) && $entity_storage->loadByProperties(array('uuid' => $data['uuid']))) {
$form_state->setErrorByName('import', $this->t('An entity with this UUID already exists but the machine name does not match.'));
}
}
else {
$config = $this->config($form_state->getValue('config_name'));
$this->configExists = !$config->isNew() ? $config : FALSE;
}
// Store the decoded version of the submitted import.
$form_state->setValueForElement($form['import'], $data);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// If this form has not yet been confirmed, store the values and rebuild.
if (!$this->data) {
$form_state->setRebuild();
$this->data = $form_state->getValues();
return;
}
// If a simple configuration file was added, set the data and save.
if ($this->data['config_type'] === 'system.simple') {
$this->configFactory()->getEditable($this->data['config_name'])->setData($this->data['import'])->save();
drupal_set_message($this->t('The %name configuration was imported.', array('%name' => $this->data['config_name'])));
}
// For a config entity, create an entity and save it.
else {
try {
$entity_storage = $this->entityManager->getStorage($this->data['config_type']);
if ($this->configExists) {
$entity = $entity_storage->updateFromStorageRecord($this->configExists, $this->data['import']);
}
else {
$entity = $entity_storage->createFromStorageRecord($this->data['import']);
}
$entity->save();
drupal_set_message($this->t('The @entity_type %label was imported.', array('@entity_type' => $entity->getEntityTypeId(), '%label' => $entity->label())));
}
catch (\Exception $e) {
drupal_set_message($e->getMessage(), 'error');
}
}
}
}

View file

@ -0,0 +1,418 @@
<?php
/**
* @file
* Contains \Drupal\config\Form\ConfigSync.
*/
namespace Drupal\config\Form;
use Drupal\Component\Uuid\UuidInterface;
use Drupal\Core\Config\ConfigImporterException;
use Drupal\Core\Config\ConfigImporter;
use Drupal\Core\Config\TypedConfigManagerInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Extension\ModuleInstallerInterface;
use Drupal\Core\Extension\ThemeHandlerInterface;
use Drupal\Core\Config\ConfigManagerInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Config\StorageInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Lock\LockBackendInterface;
use Drupal\Core\Config\StorageComparer;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Url;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Construct the storage changes in a configuration synchronization form.
*/
class ConfigSync extends FormBase {
/**
* The database lock object.
*
* @var \Drupal\Core\Lock\LockBackendInterface
*/
protected $lock;
/**
* The staging configuration object.
*
* @var \Drupal\Core\Config\StorageInterface
*/
protected $stagingStorage;
/**
* The active configuration object.
*
* @var \Drupal\Core\Config\StorageInterface
*/
protected $activeStorage;
/**
* The snapshot configuration object.
*
* @var \Drupal\Core\Config\StorageInterface
*/
protected $snapshotStorage;
/**
* Event dispatcher.
*
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
*/
protected $eventDispatcher;
/**
* The configuration manager.
*
* @var \Drupal\Core\Config\ConfigManagerInterface;
*/
protected $configManager;
/**
* The typed config manager.
*
* @var \Drupal\Core\Config\TypedConfigManagerInterface
*/
protected $typedConfigManager;
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* The theme handler.
*
* @var \Drupal\Core\Extension\ThemeHandlerInterface
*/
protected $themeHandler;
/**
* The module installer.
*
* @var \Drupal\Core\Extension\ModuleInstallerInterface
*/
protected $moduleInstaller;
/**
* The renderer.
*
* @var \Drupal\Core\Render\RendererInterface
*/
protected $renderer;
/**
* Constructs the object.
*
* @param \Drupal\Core\Config\StorageInterface $staging_storage
* The source storage.
* @param \Drupal\Core\Config\StorageInterface $active_storage
* The target storage.
* @param \Drupal\Core\Config\StorageInterface $snapshot_storage
* The snapshot storage.
* @param \Drupal\Core\Lock\LockBackendInterface $lock
* The lock object.
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
* Event dispatcher.
* @param \Drupal\Core\Config\ConfigManagerInterface $config_manager
* Configuration manager.
* @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config
* The typed configuration manager.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
* @param \Drupal\Core\Extension\ModuleInstallerInterface $module_installer
* The module installer.
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
* The theme handler.
* @param \Drupal\Core\Render\RendererInterface
* The renderer.
*/
public function __construct(StorageInterface $staging_storage, StorageInterface $active_storage, StorageInterface $snapshot_storage, LockBackendInterface $lock, EventDispatcherInterface $event_dispatcher, ConfigManagerInterface $config_manager, TypedConfigManagerInterface $typed_config, ModuleHandlerInterface $module_handler, ModuleInstallerInterface $module_installer, ThemeHandlerInterface $theme_handler, RendererInterface $renderer) {
$this->stagingStorage = $staging_storage;
$this->activeStorage = $active_storage;
$this->snapshotStorage = $snapshot_storage;
$this->lock = $lock;
$this->eventDispatcher = $event_dispatcher;
$this->configManager = $config_manager;
$this->typedConfigManager = $typed_config;
$this->moduleHandler = $module_handler;
$this->moduleInstaller = $module_installer;
$this->themeHandler = $theme_handler;
$this->renderer = $renderer;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('config.storage.staging'),
$container->get('config.storage'),
$container->get('config.storage.snapshot'),
$container->get('lock.persistent'),
$container->get('event_dispatcher'),
$container->get('config.manager'),
$container->get('config.typed'),
$container->get('module_handler'),
$container->get('module_installer'),
$container->get('theme_handler'),
$container->get('renderer')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'config_admin_import_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => $this->t('Import all'),
);
$source_list = $this->stagingStorage->listAll();
$storage_comparer = new StorageComparer($this->stagingStorage, $this->activeStorage, $this->configManager);
if (empty($source_list) || !$storage_comparer->createChangelist()->hasChanges()) {
$form['no_changes'] = array(
'#type' => 'table',
'#header' => array('Name', 'Operations'),
'#rows' => array(),
'#empty' => $this->t('There are no configuration changes to import.'),
);
$form['actions']['#access'] = FALSE;
return $form;
}
elseif (!$storage_comparer->validateSiteUuid()) {
drupal_set_message($this->t('The staged configuration cannot be imported, because it originates from a different site than this site. You can only synchronize configuration between cloned instances of this site.'), 'error');
$form['actions']['#access'] = FALSE;
return $form;
}
// A list of changes will be displayed, so check if the user should be
// warned of potential losses to configuration.
if ($this->snapshotStorage->exists('core.extension')) {
$snapshot_comparer = new StorageComparer($this->activeStorage, $this->snapshotStorage, $this->configManager);
if (!$form_state->getUserInput() && $snapshot_comparer->createChangelist()->hasChanges()) {
$change_list = array();
foreach ($snapshot_comparer->getAllCollectionNames() as $collection) {
foreach ($snapshot_comparer->getChangelist(NULL, $collection) as $config_names) {
if (empty($config_names)) {
continue;
}
foreach ($config_names as $config_name) {
$change_list[] = $config_name;
}
}
}
sort($change_list);
$change_list_render = array(
'#theme' => 'item_list',
'#items' => $change_list,
);
$change_list_html = $this->renderer->renderPlain($change_list_render);
drupal_set_message($this->t('The following items in your active configuration have changes since the last import that may be lost on the next import. !changes', array('!changes' => $change_list_html)), 'warning');
}
}
// Store the comparer for use in the submit.
$form_state->set('storage_comparer', $storage_comparer);
// Add the AJAX library to the form for dialog support.
$form['#attached']['library'][] = 'core/drupal.ajax';
foreach ($storage_comparer->getAllCollectionNames() as $collection) {
if ($collection != StorageInterface::DEFAULT_COLLECTION) {
$form[$collection]['collection_heading'] = array(
'#type' => 'html_tag',
'#tag' => 'h2',
'#value' => $this->t('!collection configuration collection', array('!collection' => $collection)),
);
}
foreach ($storage_comparer->getChangelist(NULL, $collection) as $config_change_type => $config_names) {
if (empty($config_names)) {
continue;
}
// @todo A table caption would be more appropriate, but does not have the
// visual importance of a heading.
$form[$collection][$config_change_type]['heading'] = array(
'#type' => 'html_tag',
'#tag' => 'h3',
);
switch ($config_change_type) {
case 'create':
$form[$collection][$config_change_type]['heading']['#value'] = $this->formatPlural(count($config_names), '@count new', '@count new');
break;
case 'update':
$form[$collection][$config_change_type]['heading']['#value'] = $this->formatPlural(count($config_names), '@count changed', '@count changed');
break;
case 'delete':
$form[$collection][$config_change_type]['heading']['#value'] = $this->formatPlural(count($config_names), '@count removed', '@count removed');
break;
case 'rename':
$form[$collection][$config_change_type]['heading']['#value'] = $this->formatPlural(count($config_names), '@count renamed', '@count renamed');
break;
}
$form[$collection][$config_change_type]['list'] = array(
'#type' => 'table',
'#header' => array('Name', 'Operations'),
);
foreach ($config_names as $config_name) {
if ($config_change_type == 'rename') {
$names = $storage_comparer->extractRenameNames($config_name);
$route_options = array('source_name' => $names['old_name'], 'target_name' => $names['new_name']);
$config_name = $this->t('!source_name to !target_name', array('!source_name' => $names['old_name'], '!target_name' => $names['new_name']));
}
else {
$route_options = array('source_name' => $config_name);
}
if ($collection != StorageInterface::DEFAULT_COLLECTION) {
$route_name = 'config.diff_collection';
$route_options['collection'] = $collection;
}
else {
$route_name = 'config.diff';
}
$links['view_diff'] = array(
'title' => $this->t('View differences'),
'url' => Url::fromRoute($route_name, $route_options),
'attributes' => array(
'class' => array('use-ajax'),
'data-dialog-type' => 'modal',
'data-dialog-options' => json_encode(array(
'width' => 700
)),
),
);
$form[$collection][$config_change_type]['list']['#rows'][] = array(
'name' => $config_name,
'operations' => array(
'data' => array(
'#type' => 'operations',
'#links' => $links,
),
),
);
}
}
}
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$config_importer = new ConfigImporter(
$form_state->get('storage_comparer'),
$this->eventDispatcher,
$this->configManager,
$this->lock,
$this->typedConfigManager,
$this->moduleHandler,
$this->moduleInstaller,
$this->themeHandler,
$this->getStringTranslation()
);
if ($config_importer->alreadyImporting()) {
drupal_set_message($this->t('Another request may be synchronizing configuration already.'));
}
else{
try {
$sync_steps = $config_importer->initialize();
$batch = array(
'operations' => array(),
'finished' => array(get_class($this), 'finishBatch'),
'title' => t('Synchronizing configuration'),
'init_message' => t('Starting configuration synchronization.'),
'progress_message' => t('Completed @current step of @total.'),
'error_message' => t('Configuration synchronization has encountered an error.'),
'file' => drupal_get_path('module', 'config') . '/config.admin.inc',
);
foreach ($sync_steps as $sync_step) {
$batch['operations'][] = array(array(get_class($this), 'processBatch'), array($config_importer, $sync_step));
}
batch_set($batch);
}
catch (ConfigImporterException $e) {
// There are validation errors.
drupal_set_message($this->t('The configuration cannot be imported because it failed validation for the following reasons:'), 'error');
foreach ($config_importer->getErrors() as $message) {
drupal_set_message($message, 'error');
}
}
}
}
/**
* Processes the config import batch and persists the importer.
*
* @param \Drupal\Core\Config\ConfigImporter $config_importer
* The batch config importer object to persist.
* @param string $sync_step
* The synchronization step to do.
* @param array $context
* The batch context.
*/
public static function processBatch(ConfigImporter $config_importer, $sync_step, &$context) {
if (!isset($context['sandbox']['config_importer'])) {
$context['sandbox']['config_importer'] = $config_importer;
}
$config_importer = $context['sandbox']['config_importer'];
$config_importer->doSyncStep($sync_step, $context);
if ($errors = $config_importer->getErrors()) {
if (!isset($context['results']['errors'])) {
$context['results']['errors'] = array();
}
$context['results']['errors'] += $errors;
}
}
/**
* Finish batch.
*
* This function is a static function to avoid serializing the ConfigSync
* object unnecessarily.
*/
public static function finishBatch($success, $results, $operations) {
if ($success) {
if (!empty($results['errors'])) {
foreach ($results['errors'] as $error) {
drupal_set_message($error, 'error');
\Drupal::logger('config_sync')->error($error);
}
drupal_set_message(\Drupal::translation()->translate('The configuration was imported with errors.'), 'warning');
}
else {
drupal_set_message(\Drupal::translation()->translate('The configuration was imported successfully.'));
}
}
else {
// An error occurred.
// $operations contains the operations that remained unprocessed.
$error_operation = reset($operations);
$message = \Drupal::translation()->translate('An error occurred while processing %error_operation with arguments: @arguments', array('%error_operation' => $error_operation[0], '@arguments' => print_r($error_operation[1], TRUE)));
drupal_set_message($message, 'error');
}
}
}