/** * Reads and merges in existing files for a given package or profile. */ protected function preparePackage(Package $package, array $existing_packages, FeaturesBundleInterface $bundle = NULL) { if (isset($existing_packages[$package->getMachineName()])) { $existing_directory = $existing_packages[$package->getMachineName()]; // Scan for all files. $files = file_scan_directory($existing_directory, '/.*/'); foreach ($files as $file) { // Skip files in the any existing configuration directory, as these // will be replaced. foreach (array_keys($this->featuresManager->getExtensionStorages()->getExtensionStorages()) as $directory) { if (strpos($file->uri, $directory) !== FALSE) { continue 2; } } // Merge in the info file. if ($file->name == $package->getMachineName() . '.info') { $files = $package->getFiles(); $files['info']['string'] = $this->mergeInfoFile($package->getFiles()['info']['string'], $file->uri); $package->setFiles($files); } else { // Determine if the file is within a subdirectory of the // extension's directory. $file_directory = dirname($file->uri); if ($file_directory !== $existing_directory) { $subdirectory = substr($file_directory, strlen($existing_directory) + 1); } else { $subdirectory = NULL; } $package->appendFile(['filename' => $file->filename, 'subdirectory' => $subdirectory, 'string' => file_get_contents($file->uri)]); } } } }
/** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { $bundle = $this->assigner->getBundle($this->bundle); $this->assigner->assignConfigPackages(); $this->package->setName($form_state->getValue('name')); $this->package->setMachineName($form_state->getValue('machine_name')); $this->package->setDescription($form_state->getValue('description')); $this->package->setVersion($form_state->getValue('version')); $this->package->setBundle($bundle->getMachineName()); // Save it first just to create it in case it's a new package. $this->featuresManager->setPackage($this->package); $this->package->setConfig($this->updatePackageConfig($form_state)); $this->package->setExcluded($this->updateExcluded()); $this->package->setRequired($this->updateRequired()); // Now save it with the selected config data. $this->featuresManager->setPackage($this->package); $method_id = NULL; $trigger = $form_state->getTriggeringElement(); $op = $form_state->getValue('op'); if (!empty($trigger) && empty($op)) { $method_id = $trigger['#name']; } // Set default redirect, but allow generators to change it later. $form_state->setRedirect('features.edit', array('featurename' => $this->package->getMachineName())); if (!empty($method_id)) { $packages = array($this->package->getMachineName()); $this->generator->generatePackages($method_id, $packages, $bundle); $this->generator->applyExportFormSubmit($method_id, $form, $form_state); } $this->assigner->setCurrent($bundle); }
/** * Reads and merges in existing files for a given package or profile. * * @param \Drupal\features\Package &$package * The package. * @param array $existing_packages * An array of existing packages. * @param \Drupal\features\FeaturesBundleInterface $bundle * The bundle the package belongs to. */ protected function preparePackage(Package $package, array $existing_packages, FeaturesBundleInterface $bundle = NULL) { // If this package is already present, prepare files. if (isset($existing_packages[$package->getMachineName()])) { $existing_directory = $existing_packages[$package->getMachineName()]; $package->setDirectory($existing_directory); // Merge in the info file. $info_file_uri = $this->root . '/' . $existing_directory . '/' . $package->getMachineName() . '.info.yml'; if (file_exists($info_file_uri)) { $files = $package->getFiles(); $files['info']['string'] = $this->mergeInfoFile($package->getFiles()['info']['string'], $info_file_uri); $package->setFiles($files); } // Remove the config directories, as they will be replaced. foreach (array_keys($this->featuresManager->getExtensionStorages()->getExtensionStorages()) as $directory) { $config_directory = $this->root . '/' . $existing_directory . '/' . $directory; if (is_dir($config_directory)) { file_unmanaged_delete_recursive($config_directory); } } } }
/** * Builds the details of a package. * * @param \Drupal\features\Package $package * The package. * * @return array * A render array of a form element. */ protected function buildPackageDetail(Package $package) { $config_collection = $this->featuresManager->getConfigCollection(); $url = Url::fromRoute('features.edit', array('featurename' => $package->getMachineName())); $element['name'] = array('data' => \Drupal::l($package->getName(), $url), 'class' => array('feature-name')); $machine_name = $package->getMachineName(); // Except for the 'unpackaged' pseudo-package, display the full name, since // that's what will be generated. if ($machine_name !== 'unpackaged') { $machine_name = $this->assigner->getBundle($package->getBundle())->getFullName($machine_name); } $element['machine_name'] = $machine_name; $element['status'] = array('data' => $this->featuresManager->statusLabel($package->getStatus()), 'class' => array('column-nowrap')); // Use 'data' instead of plain string value so a blank version doesn't // remove column from table. $element['version'] = array('data' => SafeMarkup::checkPlain($package->getVersion()), 'class' => array('column-nowrap')); $overrides = $this->featuresManager->detectOverrides($package); $new_config = $this->featuresManager->detectNew($package); $conflicts = array(); $missing = array(); if ($package->getStatus() == FeaturesManagerInterface::STATUS_NO_EXPORT) { $overrides = array(); $new_config = array(); } // Bundle package configuration by type. $package_config = array(); foreach ($package->getConfig() as $item_name) { $item = $config_collection[$item_name]; $package_config[$item->getType()][] = array('name' => SafeMarkup::checkPlain($item_name), 'label' => SafeMarkup::checkPlain($item->getLabel()), 'class' => in_array($item_name, $overrides) ? 'features-override' : (in_array($item_name, $new_config) ? 'features-detected' : '')); } // Conflict config from other modules. foreach ($package->getConfigOrig() as $item_name) { if (!isset($config_collection[$item_name])) { $missing[] = $item_name; $package_config['missing'][] = array('name' => SafeMarkup::checkPlain($item_name), 'label' => SafeMarkup::checkPlain($item_name), 'class' => 'features-conflict'); } elseif (!in_array($item_name, $package->getConfig())) { $item = $config_collection[$item_name]; $conflicts[] = $item_name; $package_config[$item->getType()][] = array('name' => SafeMarkup::checkPlain($item_name), 'label' => SafeMarkup::checkPlain($item->getLabel()), 'class' => 'features-conflict'); } } // Add dependencies. $package_config['dependencies'] = array(); foreach ($package->getDependencies() as $dependency) { $package_config['dependencies'][] = array('name' => $dependency, 'label' => $this->moduleHandler->getName($dependency), 'class' => ''); } $class = ''; $label = ''; if (!empty($conflicts)) { $url = Url::fromRoute('features.edit', array('featurename' => $package->getMachineName())); $class = 'features-conflict'; $label = $this->t('Conflicts'); } elseif (!empty($overrides)) { $url = Url::fromRoute('features.diff', array('featurename' => $package->getMachineName())); $class = 'features-override'; $label = $this->featuresManager->stateLabel(FeaturesManagerInterface::STATE_OVERRIDDEN); } elseif (!empty($new_config)) { $url = Url::fromRoute('features.diff', array('featurename' => $package->getMachineName())); $class = 'features-detected'; $label = $this->t('New detected'); } elseif (!empty($missing)) { $url = Url::fromRoute('features.edit', array('featurename' => $package->getMachineName())); $class = 'features-conflict'; $label = $this->t('Missing'); } if (!empty($class)) { $element['state'] = array('data' => \Drupal::l($label, $url), 'class' => array($class, 'column-nowrap')); } else { $element['state'] = ''; } $config_types = $this->featuresManager->listConfigTypes(); // Add dependencies. $config_types['dependencies'] = $this->t('Dependencies'); $config_types['missing'] = $this->t('Missing'); uasort($config_types, 'strnatcasecmp'); $rows = array(); // Use sorted array for order. foreach ($config_types as $type => $label) { // For each component type, offer alternating rows. $row = array(); if (isset($package_config[$type])) { $row[] = array('data' => array('#type' => 'html_tag', '#tag' => 'span', '#value' => SafeMarkup::checkPlain($label), '#attributes' => array('title' => SafeMarkup::checkPlain($type), 'class' => 'features-item-label'))); $row[] = array('data' => array('#theme' => 'features_items', '#items' => $package_config[$type], '#value' => SafeMarkup::checkPlain($label), '#title' => SafeMarkup::checkPlain($type)), 'class' => 'item'); $rows[] = $row; } } $element['table'] = array('#type' => 'table', '#rows' => $rows); $details = array(); $details['description'] = array('#markup' => Xss::filterAdmin($package->getDescription())); $details['table'] = array('#type' => 'details', '#title' => array('#markup' => $this->t('Included configuration')), '#description' => array('data' => $element['table'])); $element['details'] = array('class' => array('description', 'expand'), 'data' => $details); return $element; }
/** * {@inheritdoc} */ public function getExportInfo(Package $package, FeaturesBundleInterface $bundle = NULL) { $full_name = isset($bundle) ? $bundle->getFullName($package->getMachineName()) : $package->getMachineName(); $path = ''; // Adjust export directory to be in profile. if (isset($bundle) && $bundle->isProfile()) { $path .= 'profiles/' . $bundle->getProfileName(); } // If this is not the profile package, nest the directory. if (!isset($bundle) || !$bundle->isProfilePackage($package->getMachineName())) { $path .= empty($path) ? 'modules' : '/modules'; $export_settings = $this->getExportSettings(); if (!empty($export_settings['folder'])) { $path .= '/' . $export_settings['folder']; } } return array($full_name, $path); }
/** * Returns a form element for the given overrides. * * @param \Drupal\features\Package $package * A package. * @param array $overrides * An array of overrides. * @param array $missing * An array of missing config. * * @return array * A form element. */ protected function diffOutput(Package $package, $overrides, $missing = array()) { $element = array(); $config = $this->featuresManager->getConfigCollection(); $components = array_merge($missing, $overrides); $header = array(array('data' => '', 'class' => 'diff-marker'), array('data' => $this->t('Active site config'), 'class' => 'diff-context'), array('data' => '', 'class' => 'diff-marker'), array('data' => $this->t('Feature code config'), 'class' => 'diff-context')); foreach ($components as $name) { $rows[] = array(array('data' => $name, 'colspan' => 4, 'header' => TRUE)); if (!isset($config[$name])) { $details = array('#markup' => $this->t('Component in feature missing from active config.')); } else { $active = $this->featuresManager->getActiveStorage()->read($name); $extension = $this->featuresManager->getExtensionStorages()->read($name); if (empty($extension)) { $details = array('#markup' => $this->t('Dependency detected in active config but not exported to the feature.')); } else { $diff = $this->configDiff->diff($active, $extension); $details = array('#type' => 'table', '#header' => $header, '#rows' => $this->diffFormatter->format($diff), '#attributes' => array('class' => array('diff', 'features-diff'))); } } $element[$name] = array('row' => array('data' => array('#type' => 'details', '#title' => Html::escape($name), '#open' => TRUE, '#description' => array('data' => $details))), '#attributes' => array('class' => 'diff-' . $package->getMachineName())); } return $element; }