X-Git-Url: https://yaffs.net/gitweb/?a=blobdiff_plain;f=vendor%2Fdrush%2Fdrush%2Fsrc%2FDrupal%2FCommands%2Fconfig%2FConfigCommands.php;fp=vendor%2Fdrush%2Fdrush%2Fsrc%2FDrupal%2FCommands%2Fconfig%2FConfigCommands.php;h=872c664f3294038dd784c17491dfa79de51e6979;hb=af6d1fb995500ae68849458ee10d66abbdcfb252;hp=0000000000000000000000000000000000000000;hpb=680c79a86e3ed402f263faeac92e89fb6d9edcc0;p=yaffs-website diff --git a/vendor/drush/drush/src/Drupal/Commands/config/ConfigCommands.php b/vendor/drush/drush/src/Drupal/Commands/config/ConfigCommands.php new file mode 100644 index 000000000..872c664f3 --- /dev/null +++ b/vendor/drush/drush/src/Drupal/Commands/config/ConfigCommands.php @@ -0,0 +1,511 @@ +configFactory; + } + + + /** + * ConfigCommands constructor. + * @param ConfigFactoryInterface $configFactory + */ + public function __construct($configFactory) + { + parent::__construct(); + $this->configFactory = $configFactory; + } + + /** + * Display a config value, or a whole configuration object. + * + * @command config:get + * @validate-config-name + * @interact-config-name + * @param $config_name The config object name, for example "system.site". + * @param $key The config key, for example "page.front". Optional. + * @option source The config storage source to read. Additional labels may be defined in settings.php. + * @option include-overridden Apply module and settings.php overrides to values. + * @usage drush config:get system.site + * Displays the system.site config. + * @usage drush config:get system.site page.front + * Gets system.site:page.front value. + * @aliases cget,config-get + */ + public function get($config_name, $key = '', $options = ['format' => 'yaml', 'source' => 'active', 'include-overridden' => false]) + { + // Displaying overrides only applies to active storage. + $factory = $this->getConfigFactory(); + $config = $options['include-overridden'] ? $factory->get($config_name) : $factory->getEditable($config_name); + $value = $config->get($key); + // @todo If the value is TRUE (for example), nothing gets printed. Is this yaml formatter's fault? + return $key ? ["$config_name:$key" => $value] : $value; + } + + /** + * Set config value directly. Does not perform a config import. + * + * @command config:set + * @validate-config-name + * @todo @interact-config-name deferred until we have interaction for key. + * @param $config_name The config object name, for example "system.site". + * @param $key The config key, for example "page.front". + * @param $value The value to assign to the config key. Use '-' to read from STDIN. + * @option format Format to parse the object. Use "string" for string (default), and "yaml" for YAML. + * // A convenient way to pass a multiline value within a backend request. + * @option value The value to assign to the config key (if any). + * @hidden-options value + * @usage drush config:set system.site page.front node + * Sets system.site:page.front to "node". + * @aliases cset,config-set + */ + public function set($config_name, $key, $value = null, $options = ['format' => 'string', 'value' => self::REQ]) + { + // This hidden option is a convenient way to pass a value without passing a key. + $data = $options['value'] ?: $value; + + if (!isset($data)) { + throw new \Exception(dt('No config value specified.')); + } + + $config = $this->getConfigFactory()->getEditable($config_name); + // Check to see if config key already exists. + $new_key = $config->get($key) === null; + + // Special flag indicating that the value has been passed via STDIN. + if ($data === '-') { + $data = stream_get_contents(STDIN); + } + + // Now, we parse the value. + switch ($options['format']) { + case 'yaml': + $parser = new Parser(); + $data = $parser->parse($data, true); + } + + if (is_array($data) && $this->io()->confirm(dt('Do you want to update or set multiple keys on !name config.', ['!name' => $config_name]))) { + foreach ($data as $key => $value) { + $config->set($key, $value); + } + return $config->save(); + } else { + $confirmed = false; + if ($config->isNew() && $this->io()->confirm(dt('!name config does not exist. Do you want to create a new config object?', ['!name' => $config_name]))) { + $confirmed = true; + } elseif ($new_key && $this->io()->confirm(dt('!key key does not exist in !name config. Do you want to create a new config key?', ['!key' => $key, '!name' => $config_name]))) { + $confirmed = true; + } elseif ($this->io()->confirm(dt('Do you want to update !key key in !name config?', ['!key' => $key, '!name' => $config_name]))) { + $confirmed = true; + } + if ($confirmed && !\Drush\Drush::simulate()) { + return $config->set($key, $data)->save(); + } + } + } + + /** + * Open a config file in a text editor. Edits are imported after closing editor. + * + * @command config:edit + * @validate-config-name + * @interact-config-name + * @param $config_name The config object name, for example "system.site". + * @optionset_get_editor + * @allow_additional_options config-import + * @hidden-options source,partial + * @usage drush config:edit image.style.large + * Edit the image style configurations. + * @usage drush config:edit + * Choose a config file to edit. + * @usage drush --bg config-edit image.style.large + * Return to shell prompt as soon as the editor window opens. + * @aliases cedit,config-edit + * @validate-module-enabled config + */ + public function edit($config_name) + { + $config = $this->getConfigFactory()->get($config_name); + $active_storage = $config->getStorage(); + $contents = $active_storage->read($config_name); + + // Write tmp YAML file for editing + $temp_dir = drush_tempdir(); + $temp_storage = new FileStorage($temp_dir); + $temp_storage->write($config_name, $contents); + + $exec = drush_get_editor(); + drush_shell_exec_interactive($exec, $temp_storage->getFilePath($config_name)); + + // Perform import operation if user did not immediately exit editor. + if (!$options['bg']) { + $options = Drush::redispatchOptions() + ['partial' => true, 'source' => $temp_dir]; + $backend_options = ['interactive' => true]; + return (bool) drush_invoke_process('@self', 'config-import', [], $options, $backend_options); + } + } + + /** + * Delete a configuration key, or a whole object. + * + * @command config:delete + * @validate-config-name + * @interact-config-name + * @param $config_name The config object name, for example "system.site". + * @param $key A config key to clear, for example "page.front". + * @usage drush config:delete system.site + * Delete the the system.site config object. + * @usage drush config:delete system.site page.front node + * Delete the 'page.front' key from the system.site object. + * @aliases cdel,config-delete + */ + public function delete($config_name, $key = null) + { + $config = $this->getConfigFactory()->getEditable($config_name); + if ($key) { + if ($config->get($key) === null) { + throw new \Exception(dt('Configuration key !key not found.', ['!key' => $key])); + } + $config->clear($key)->save(); + } else { + $config->delete(); + } + } + + /** + * Display status of configuration (differences between the filesystem configuration and database configuration). + * + * @command config:status + * @option state A comma-separated list of states to filter results. + * @option prefix Prefix The config prefix. For example, "system". No prefix will return all names in the system. + * @option string $label A config directory label (i.e. a key in \$config_directories array in settings.php). + * @usage drush config:status + * Display configuration items that need to be synchronized. + * @usage drush config:status --state=Identical + * Display configuration items that are in default state. + * @usage drush config:status --state='Only in sync dir' --prefix=node.type. + * Display all content types that would be created in active storage on configuration import. + * @usage drush config:status --state=Any --format=list + * List all config names. + * @field-labels + * name: Name + * state: State + * @default-fields name,state + * @aliases cst,config-status + * @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields + */ + public function status($options = ['state' => 'Only in DB,Only in sync dir,Different', 'prefix' => self::REQ, 'label' => self::REQ]) + { + $config_list = array_fill_keys( + $this->configFactory->listAll($options['prefix']), + 'Identical' + ); + + $directory = $this->getDirectory($options['label']); + $storage = $this->getStorage($directory); + $state_map = [ + 'create' => 'Only in DB', + 'update' => 'Different', + 'delete' => 'Only in sync dir', + ]; + foreach ($this->getChanges($storage) as $collection) { + foreach ($collection as $operation => $configs) { + foreach ($configs as $config) { + if (!$options['prefix'] || strpos($config, $options['prefix']) === 0) { + $config_list[$config] = $state_map[$operation]; + } + } + } + } + + if ($options['state']) { + $allowed_states = explode(',', $options['state']); + if (!in_array('Any', $allowed_states)) { + $config_list = array_filter($config_list, function ($state) use ($allowed_states) { + return in_array($state, $allowed_states); + }); + } + } + + ksort($config_list); + + $rows = []; + $color_map = [ + 'Only in DB' => 'green', + 'Only in sync dir' => 'red', + 'Different' => 'yellow', + 'Identical' => 'white', + ]; + + foreach ($config_list as $config => $state) { + if ($options['format'] == 'table' && $state != 'Identical') { + $state = "$state"; + } + $rows[$config] = [ + 'name' => $config, + 'state' => $state, + ]; + } + + if ($rows) { + return new RowsOfFields($rows); + } else { + $this->logger()->notice(dt('No differences between DB and sync directory.')); + } + } + + /** + * Determine which configuration directory to use and return directory path. + * + * Directory path is determined based on the following precedence: + * 1. User-provided $directory. + * 2. Directory path corresponding to $label (mapped via $config_directories in settings.php). + * 3. Default sync directory + * + * @param string $label + * A configuration directory label. + * @param string $directory + * A configuration directory. + */ + public static function getDirectory($label, $directory = null) + { + $return = null; + // If the user provided a directory, use it. + if (!empty($directory)) { + if ($directory === true) { + // The user did not pass a specific directory, make one. + $return = FsUtils::prepareBackupDir('config-import-export'); + } else { + // The user has specified a directory. + drush_mkdir($directory); + $return = $directory; + } + } else { + // If a directory isn't specified, use the label argument or default sync directory. + $return = \config_get_config_directory($label ?: CONFIG_SYNC_DIRECTORY); + } + return Path::canonicalize($return); + } + + /** + * Returns the difference in configuration between active storage and target storage. + */ + public function getChanges($target_storage) + { + /** @var StorageInterface $active_storage */ + $active_storage = \Drupal::service('config.storage'); + + $config_comparer = new StorageComparer($active_storage, $target_storage, \Drupal::service('config.manager')); + + $change_list = []; + if ($config_comparer->createChangelist()->hasChanges()) { + foreach ($config_comparer->getAllCollectionNames() as $collection) { + $change_list[$collection] = $config_comparer->getChangelist(null, $collection); + } + } + return $change_list; + } + + /** + * Get storage corresponding to a configuration directory. + */ + public function getStorage($directory) + { + if ($directory == \config_get_config_directory(CONFIG_SYNC_DIRECTORY)) { + return \Drupal::service('config.storage.sync'); + } else { + return new FileStorage($directory); + } + } + + /** + * Build a table of config changes. + * + * @param array $config_changes + * An array of changes keyed by collection. + * + * @return Table A Symfony table object. + */ + public static function configChangesTable(array $config_changes, OutputInterface $output, $use_color = true) + { + $rows = []; + foreach ($config_changes as $collection => $changes) { + foreach ($changes as $change => $configs) { + switch ($change) { + case 'delete': + $colour = ''; + break; + case 'update': + $colour = ''; + break; + case 'create': + $colour = ''; + break; + default: + $colour = ""; + break; + } + if ($use_color) { + $prefix = $colour; + $suffix = ''; + } else { + $prefix = $suffix = ''; + } + foreach ($configs as $config) { + $rows[] = [ + $collection, + $config, + $prefix . ucfirst($change) . $suffix, + ]; + } + } + } + $table = new Table($output); + $table->setHeaders(['Collection', 'Config', 'Operation']); + $table->addRows($rows); + return $table; + } + + /** + * @hook interact @interact-config-name + */ + public function interactConfigName($input, $output) + { + if (empty($input->getArgument('config_name'))) { + $config_names = $this->getConfigFactory()->listAll(); + $choice = $this->io()->choice('Choose a configuration', drush_map_assoc($config_names)); + $input->setArgument('config_name', $choice); + } + } + + /** + * @hook interact @interact-config-label + */ + public function interactConfigLabel(InputInterface $input, ConsoleOutputInterface $output) + { + global $config_directories; + + $option_name = $input->hasOption('destination') ? 'destination' : 'source'; + if (empty($input->getArgument('label') && empty($input->getOption($option_name)))) { + $choices = drush_map_assoc(array_keys($config_directories)); + unset($choices[CONFIG_ACTIVE_DIRECTORY]); + if (count($choices) >= 2) { + $label = $this->io()->choice('Choose a '. $option_name. '.', $choices); + $input->setArgument('label', $label); + } + } + } + + /** + * Validate that a config name is valid. + * + * If the argument to be validated is not named $config_name, pass the + * argument name as the value of the annotation. + * + * @hook validate @validate-config-name + * @param \Consolidation\AnnotatedCommand\CommandData $commandData + * @return \Consolidation\AnnotatedCommand\CommandError|null + */ + public function validateConfigName(CommandData $commandData) + { + $arg_name = $commandData->annotationData()->get('validate-config-name', null) ?: 'config_name'; + $config_name = $commandData->input()->getArgument($arg_name); + $config = \Drupal::config($config_name); + if ($config->isNew()) { + $msg = dt('Config !name does not exist', ['!name' => $config_name]); + return new CommandError($msg); + } + } + + /** + * Copies configuration objects from source storage to target storage. + * + * @param StorageInterface $source + * The source config storage service. + * @param StorageInterface $destination + * The destination config storage service. + */ + public static function copyConfig(StorageInterface $source, StorageInterface $destination) + { + // Make sure the source and destination are on the default collection. + if ($source->getCollectionName() != StorageInterface::DEFAULT_COLLECTION) { + $source = $source->createCollection(StorageInterface::DEFAULT_COLLECTION); + } + if ($destination->getCollectionName() != StorageInterface::DEFAULT_COLLECTION) { + $destination = $destination->createCollection(StorageInterface::DEFAULT_COLLECTION); + } + + // Export all the configuration. + foreach ($source->listAll() as $name) { + $destination->write($name, $source->read($name)); + } + + // Export configuration collections. + foreach ($source->getAllCollectionNames() as $collection) { + $source = $source->createCollection($collection); + $destination = $destination->createCollection($collection); + foreach ($source->listAll() as $name) { + $destination->write($name, $source->read($name)); + } + } + } + + /** + * Get diff between two config sets. + * + * @param StorageInterface $destination_storage + * @param StorageInterface $source_storage + * @param OutputInterface $output + * @return array|bool + * An array of strings containing the diff. + */ + public static function getDiff(StorageInterface $destination_storage, StorageInterface $source_storage, OutputInterface $output) + { + // Copy active storage to a temporary directory. + $temp_destination_dir = drush_tempdir(); + $temp_destination_storage = new FileStorage($temp_destination_dir); + self::copyConfig($destination_storage, $temp_destination_storage); + + // Copy source storage to a temporary directory as it could be + // modified by the partial option or by decorated sync storages. + $temp_source_dir = drush_tempdir(); + $temp_source_storage = new FileStorage($temp_source_dir); + self::copyConfig($source_storage, $temp_source_storage); + + $prefix = 'diff'; + if (drush_program_exists('git') && $output->isDecorated()) { + $prefix = 'git diff --color=always'; + } + drush_shell_exec($prefix . ' -u %s %s', $temp_destination_dir, $temp_source_dir); + return drush_shell_exec_output(); + } +}