123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447 |
- <?php
- namespace Drupal\config\Form;
- use Drupal\Component\Serialization\Exception\InvalidDataTypeException;
- use Drupal\config\StorageReplaceDataWrapper;
- use Drupal\Core\Config\ConfigImporter;
- use Drupal\Core\Config\ConfigImporterException;
- use Drupal\Core\Config\ConfigManagerInterface;
- use Drupal\Core\Config\Entity\ConfigEntityInterface;
- use Drupal\Core\Config\Importer\ConfigImporterBatch;
- use Drupal\Core\Config\StorageComparer;
- use Drupal\Core\Config\StorageInterface;
- use Drupal\Core\Config\TypedConfigManagerInterface;
- use Drupal\Core\DependencyInjection\DeprecatedServicePropertyTrait;
- use Drupal\Core\Entity\EntityTypeManagerInterface;
- use Drupal\Core\Extension\ModuleExtensionList;
- use Drupal\Core\Extension\ModuleHandlerInterface;
- use Drupal\Core\Extension\ModuleInstallerInterface;
- use Drupal\Core\Extension\ThemeHandlerInterface;
- use Drupal\Core\Form\ConfirmFormBase;
- use Drupal\Core\Form\FormStateInterface;
- use Drupal\Core\Lock\LockBackendInterface;
- use Drupal\Core\Render\RendererInterface;
- use Drupal\Core\Serialization\Yaml;
- use Drupal\Core\Url;
- use Symfony\Component\DependencyInjection\ContainerInterface;
- use Symfony\Component\EventDispatcher\EventDispatcherInterface;
- /**
- * Provides a form for importing a single configuration file.
- *
- * @internal
- */
- class ConfigSingleImportForm extends ConfirmFormBase {
- use DeprecatedServicePropertyTrait;
- /**
- * {@inheritdoc}
- */
- protected $deprecatedProperties = [
- 'entityManager' => 'entity.manager',
- ];
- /**
- * The entity type manager.
- *
- * @var \Drupal\Core\Entity\EntityTypeManagerInterface
- */
- protected $entityTypeManager;
- /**
- * The config storage.
- *
- * @var \Drupal\Core\Config\StorageInterface
- */
- protected $configStorage;
- /**
- * The renderer service.
- *
- * @var \Drupal\Core\Render\RendererInterface
- */
- protected $renderer;
- /**
- * The event dispatcher.
- *
- * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
- */
- protected $eventDispatcher;
- /**
- * The configuration manager.
- *
- * @var \Drupal\Core\Config\ConfigManagerInterface
- */
- protected $configManager;
- /**
- * The database lock object.
- *
- * @var \Drupal\Core\Lock\LockBackendInterface
- */
- protected $lock;
- /**
- * 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 extension list.
- *
- * @var \Drupal\Core\Extension\ModuleExtensionList
- */
- protected $moduleExtensionList;
- /**
- * The module installer.
- *
- * @var \Drupal\Core\Extension\ModuleInstallerInterface
- */
- protected $moduleInstaller;
- /**
- * 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 = [];
- /**
- * Constructs a new ConfigSingleImportForm.
- *
- * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
- * The entity type manager.
- * @param \Drupal\Core\Config\StorageInterface $config_storage
- * The config storage.
- * @param \Drupal\Core\Render\RendererInterface $renderer
- * The renderer service.
- * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
- * The event dispatcher used to notify subscribers of config import events.
- * @param \Drupal\Core\Config\ConfigManagerInterface $config_manager
- * The configuration manager.
- * @param \Drupal\Core\Lock\LockBackendInterface $lock
- * The lock backend to ensure multiple imports do not occur at the same time.
- * @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\Extension\ModuleExtensionList $extension_list_module
- * The module extension list.
- */
- public function __construct(EntityTypeManagerInterface $entity_type_manager, StorageInterface $config_storage, RendererInterface $renderer, EventDispatcherInterface $event_dispatcher, ConfigManagerInterface $config_manager, LockBackendInterface $lock, TypedConfigManagerInterface $typed_config, ModuleHandlerInterface $module_handler, ModuleInstallerInterface $module_installer, ThemeHandlerInterface $theme_handler, ModuleExtensionList $extension_list_module) {
- $this->entityTypeManager = $entity_type_manager;
- $this->configStorage = $config_storage;
- $this->renderer = $renderer;
- // Services necessary for \Drupal\Core\Config\ConfigImporter.
- $this->eventDispatcher = $event_dispatcher;
- $this->configManager = $config_manager;
- $this->lock = $lock;
- $this->typedConfigManager = $typed_config;
- $this->moduleHandler = $module_handler;
- $this->moduleInstaller = $module_installer;
- $this->themeHandler = $theme_handler;
- $this->moduleExtensionList = $extension_list_module;
- }
- /**
- * {@inheritdoc}
- */
- public static function create(ContainerInterface $container) {
- return new static(
- $container->get('entity_type.manager'),
- $container->get('config.storage'),
- $container->get('renderer'),
- $container->get('event_dispatcher'),
- $container->get('config.manager'),
- $container->get('lock.persistent'),
- $container->get('config.typed'),
- $container->get('module_handler'),
- $container->get('module_installer'),
- $container->get('theme_handler'),
- $container->get('extension.list.module')
- );
- }
- /**
- * {@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->entityTypeManager->getDefinition($this->data['config_type']);
- $name = $this->data['import'][$definition->getKey('id')];
- $type = $definition->getSingularLabel();
- }
- $args = [
- '%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 = [];
- foreach ($this->entityTypeManager->getDefinitions() as $entity_type => $definition) {
- if ($definition->entityClassImplements(ConfigEntityInterface::class)) {
- $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 = [
- 'system.simple' => $this->t('Simple configuration'),
- ] + $entity_types;
- $form['config_type'] = [
- '#title' => $this->t('Configuration type'),
- '#type' => 'select',
- '#options' => $config_types,
- '#required' => TRUE,
- ];
- $form['config_name'] = [
- '#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' => [
- 'required' => [
- ':input[name="config_type"]' => ['value' => 'system.simple'],
- ],
- 'visible' => [
- ':input[name="config_type"]' => ['value' => 'system.simple'],
- ],
- ],
- ];
- $form['import'] = [
- '#title' => $this->t('Paste your configuration here'),
- '#type' => 'textarea',
- '#rows' => 24,
- '#required' => TRUE,
- ];
- $form['advanced'] = [
- '#type' => 'details',
- '#title' => $this->t('Advanced'),
- ];
- $form['advanced']['custom_entity_id'] = [
- '#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'] = ['#type' => 'actions'];
- $form['actions']['submit'] = [
- '#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;
- }
- try {
- // Decode the submitted import.
- $data = Yaml::decode($form_state->getValue('import'));
- }
- catch (InvalidDataTypeException $e) {
- $form_state->setErrorByName('import', $this->t('The import failed with the following message: %message', ['%message' => $e->getMessage()]));
- }
- // Validate for config entities.
- if ($form_state->getValue('config_type') && $form_state->getValue('config_type') !== 'system.simple') {
- $definition = $this->entityTypeManager->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->entityTypeManager->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.', ['@id_key' => $id_key, '@entity_type' => $definition->getLabel()]));
- return;
- }
- $config_name = $definition->getConfigPrefix() . '.' . $data[$id_key];
- // 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(['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_name = $form_state->getValue('config_name');
- $config = $this->config($config_name);
- $this->configExists = !$config->isNew() ? $config : FALSE;
- }
- // Use ConfigImporter validation.
- if (!$form_state->getErrors()) {
- $source_storage = new StorageReplaceDataWrapper($this->configStorage);
- $source_storage->replaceData($config_name, $data);
- $storage_comparer = new StorageComparer($source_storage, $this->configStorage);
- if (!$storage_comparer->createChangelist()->hasChanges()) {
- $form_state->setErrorByName('import', $this->t('There are no changes to import.'));
- }
- else {
- $config_importer = new ConfigImporter(
- $storage_comparer,
- $this->eventDispatcher,
- $this->configManager,
- $this->lock,
- $this->typedConfigManager,
- $this->moduleHandler,
- $this->moduleInstaller,
- $this->themeHandler,
- $this->getStringTranslation(),
- $this->moduleExtensionList
- );
- try {
- $config_importer->validate();
- $form_state->set('config_importer', $config_importer);
- }
- catch (ConfigImporterException $e) {
- // There are validation errors.
- $item_list = [
- '#theme' => 'item_list',
- '#items' => $config_importer->getErrors(),
- '#title' => $this->t('The configuration cannot be imported because it failed validation for the following reasons:'),
- ];
- $form_state->setErrorByName('import', $this->renderer->render($item_list));
- }
- }
- }
- // 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;
- }
- /** @var \Drupal\Core\Config\ConfigImporter $config_importer */
- $config_importer = $form_state->get('config_importer');
- if ($config_importer->alreadyImporting()) {
- $this->messenger()->addError($this->t('Another request may be importing configuration already.'));
- }
- else {
- try {
- $sync_steps = $config_importer->initialize();
- $batch = [
- 'operations' => [],
- 'finished' => [ConfigImporterBatch::class, 'finish'],
- 'title' => $this->t('Importing configuration'),
- 'init_message' => $this->t('Starting configuration import.'),
- 'progress_message' => $this->t('Completed @current step of @total.'),
- 'error_message' => $this->t('Configuration import has encountered an error.'),
- ];
- foreach ($sync_steps as $sync_step) {
- $batch['operations'][] = [[ConfigImporterBatch::class, 'process'], [$config_importer, $sync_step]];
- }
- batch_set($batch);
- }
- catch (ConfigImporterException $e) {
- // There are validation errors.
- $this->messenger()->addError($this->t('The configuration import failed for the following reasons:'));
- foreach ($config_importer->getErrors() as $message) {
- $this->messenger()->addError($message);
- }
- }
- }
- }
- }
|