示例#1
0
 /**
  * @covers ::setFeaturesInfo
  */
 public function testSetFeaturesInfo()
 {
     $package = new Package('test_feature', []);
     $this->assertEquals([], $package->getFeaturesInfo());
     $package->setFeaturesInfo(['bundle' => 'test_bundle']);
     $this->assertEquals(['bundle' => 'test_bundle'], $package->getFeaturesInfo());
     $this->assertEquals('test_bundle', $package->getBundle());
 }
示例#2
0
 /**
  * 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;
 }
示例#3
0
 /**
  * Generates and adds .info.yml files to a package.
  *
  * @param \Drupal\features\Package $package
  *   The package.
  */
 protected function addInfoFile(Package $package)
 {
     $info = ['name' => $package->getName(), 'description' => $package->getDescription(), 'type' => $package->getType(), 'core' => $package->getCore(), 'dependencies' => $package->getDependencies(), 'themes' => $package->getThemes(), 'version' => $package->getVersion()];
     $features_info = [];
     // Assign to a "package" named for the profile.
     if ($package->getBundle()) {
         $bundle = $this->getAssigner()->getBundle($package->getBundle());
     }
     // Save the current bundle in the info file so the package
     // can be reloaded later by the AssignmentPackages plugin.
     if (isset($bundle) && !$bundle->isDefault()) {
         $info['package'] = $bundle->getName();
         $features_info['bundle'] = $bundle->getMachineName();
     } else {
         unset($features_info['bundle']);
     }
     if ($package->getConfig()) {
         foreach (array('excluded', 'required') as $constraint) {
             if (!empty($package->{'get' . $constraint}())) {
                 $features_info[$constraint] = $package->{'get' . $constraint}();
             } else {
                 unset($features_info[$constraint]);
             }
         }
         if (empty($features_info)) {
             $features_info = TRUE;
         }
     }
     // The name and description need to be cast as strings from the
     // TranslatableMarkup objects returned by t() to avoid raising an
     // InvalidDataTypeException on Yaml serialization.
     foreach (array('name', 'description') as $key) {
         $info[$key] = (string) $info[$key];
     }
     // Add profile-specific info data.
     if ($info['type'] == 'profile') {
         // Set the distribution name.
         $info['distribution'] = ['name' => $info['name']];
     }
     $package->appendFile(['filename' => $package->getMachineName() . '.info.yml', 'subdirectory' => NULL, 'string' => Yaml::encode(array_filter($info))], 'info');
     $package->appendFile(['filename' => $package->getMachineName() . '.features.yml', 'subdirectory' => NULL, 'string' => Yaml::encode($features_info)], 'features');
 }
示例#4
0
 /**
  * {@inheritdoc}
  */
 public function buildForm(array $form, FormStateInterface $form_state, $featurename = '')
 {
     $session = $this->getRequest()->getSession();
     $trigger = $form_state->getTriggeringElement();
     if ($trigger['#name'] == 'package') {
         // Save current bundle name for later ajax callback.
         $this->oldBundle = $this->bundle;
     } elseif ($trigger['#name'] == 'conflicts') {
         if (isset($session)) {
             $session->set('features_allow_conflicts', $form_state->getValue('conflicts'));
         }
     }
     if (!$form_state->isValueEmpty('package')) {
         $bundle_name = $form_state->getValue('package');
         $bundle = $this->assigner->getBundle($bundle_name);
     } else {
         $bundle = $this->assigner->loadBundle();
     }
     // Only store bundle name, not full object.
     $this->bundle = $bundle->getMachineName();
     $this->allowConflicts = FALSE;
     if (isset($session)) {
         $this->allowConflicts = $session->get('features_allow_conflicts', FALSE);
     }
     // Pass the $force argument as TRUE because we want to include any excluded
     // configuration items. These should show up as automatically assigned, but
     // not selected, thus allowing the admin to reselect if desired.
     // @see FeaturesManagerInterface::assignConfigPackage()
     $this->assigner->assignConfigPackages(TRUE);
     $packages = $this->featuresManager->getPackages();
     if (empty($packages[$featurename])) {
         $featurename = str_replace(array('-', ' '), '_', $featurename);
         $this->package = $this->featuresManager->initPackage($featurename, NULL, '', 'module', $bundle);
     } else {
         $this->package = $packages[$featurename];
     }
     if (!empty($packages[$featurename]) && $this->package->getBundle() !== $this->bundle && $form_state->isValueEmpty('package')) {
         // Make sure the current bundle matches what is stored in the package.
         // But only do this if the Package value hasn't been manually changed.
         $bundle = $this->assigner->getBundle($this->package->getBundle());
         if (empty($bundle)) {
             // Create bundle if it doesn't exist yet
             $bundle = $this->assigner->createBundleFromDefault($this->package->getBundle());
         }
         $this->bundle = $bundle->getMachineName();
         $this->assigner->reset();
         $this->assigner->assignConfigPackages(TRUE);
         $packages = $this->featuresManager->getPackages();
         $this->package = $packages[$featurename];
     }
     $form = array('#show_operations' => FALSE, '#prefix' => '<div id="features-edit-wrapper">', '#suffix' => '</div>');
     $form['info'] = array('#type' => 'fieldset', '#title' => $this->t('General Information'), '#tree' => FALSE, '#weight' => 2, '#prefix' => "<div id='features-export-info'>", '#suffix' => '</div>');
     $form['info']['name'] = array('#title' => $this->t('Name'), '#description' => $this->t('Example: Image gallery') . ' (' . $this->t('Do not begin name with numbers.') . ')', '#type' => 'textfield', '#default_value' => $this->package->getName());
     if (!$bundle->isDefault()) {
         $form['info']['name']['#description'] .= '<br/>' . $this->t('The namespace "@name_" will be prepended to the machine name', array('@name' => $bundle->getMachineName()));
     }
     $form['info']['machine_name'] = array('#type' => 'machine_name', '#title' => $this->t('Machine-readable name'), '#description' => $this->t('Example: image_gallery') . ' ' . $this->t('May only contain lowercase letters, numbers and underscores.'), '#required' => TRUE, '#default_value' => $bundle->getShortName($this->package->getMachineName()), '#machine_name' => array('source' => array('info', 'name'), 'exists' => array($this, 'featureExists')));
     if (!$bundle->isDefault()) {
         $form['info']['machine_name']['#description'] .= '<br/>' . $this->t('NOTE: Do NOT include the namespace prefix "@name_"; it will be added automatically.', array('@name' => $bundle->getMachineName()));
     }
     $form['info']['description'] = array('#title' => $this->t('Description'), '#description' => $this->t('Provide a short description of what users should expect when they install your feature.'), '#type' => 'textarea', '#rows' => 3, '#default_value' => $this->package->getDescription());
     $form['info']['package'] = array('#title' => $this->t('Bundle'), '#type' => 'select', '#options' => $this->assigner->getBundleOptions(), '#default_value' => $bundle->getMachineName(), '#ajax' => array('callback' => '::updateBundle', 'wrapper' => 'features-export-info'));
     $form['info']['version'] = array('#title' => $this->t('Version'), '#description' => $this->t('Examples: 8.x-1.0, 8.x-1.0-beta1'), '#type' => 'textfield', '#required' => FALSE, '#default_value' => $this->package->getVersion(), '#size' => 30);
     list($full_name, $path) = $this->featuresManager->getExportInfo($this->package, $bundle);
     $form['info']['directory'] = array('#title' => $this->t('Path'), '#description' => $this->t('Path to export package using Write action, relative to root directory.'), '#type' => 'textfield', '#required' => FALSE, '#default_value' => $path, '#size' => 30);
     $require_all = $this->package->getRequiredAll();
     $form['info']['require_all'] = array('#type' => 'checkbox', '#title' => $this->t('Mark all config as required'), '#default_value' => $this->package->getRequiredAll(), '#description' => $this->t('Required config will be assigned to this feature regardless of other assignment plugins.'));
     $form['conflicts'] = array('#type' => 'checkbox', '#title' => $this->t('Allow conflicts'), '#default_value' => $this->allowConflicts, '#description' => $this->t('Allow configuration to be exported to more than one feature.'), '#weight' => 8, '#ajax' => array('callback' => '::updateForm', 'wrapper' => 'features-edit-wrapper'));
     $generation_info = array();
     if (\Drupal::currentUser()->hasPermission('export configuration')) {
         // Offer available generation methods.
         $generation_info = $this->generator->getGenerationMethods();
         // Sort generation methods by weight.
         uasort($generation_info, '\\Drupal\\Component\\Utility\\SortArray::sortByWeightElement');
     }
     $form['actions'] = array('#type' => 'actions', '#tree' => TRUE);
     foreach ($generation_info as $method_id => $method) {
         $form['actions'][$method_id] = array('#type' => 'submit', '#name' => $method_id, '#value' => $this->t('@name', array('@name' => $method['name'])), '#attributes' => array('title' => Html::escape($method['description'])));
     }
     // Build the Component Listing panel on the right.
     $form['export'] = $this->buildComponentList($form_state);
     if (!empty($this->missing)) {
         if ($this->allowConflicts) {
             $form['actions']['#prefix'] = '<strong>' . $this->t('WARNING: Package contains configuration missing from site.') . '<br>' . $this->t('This configuration will be removed if you export it.') . '</strong>';
         } else {
             foreach ($generation_info as $method_id => $method) {
                 unset($form['actions'][$method_id]);
             }
             $form['actions']['#prefix'] = '<strong>' . $this->t('Package contains configuration missing from site.') . '<br>' . $this->t('Import the feature to create the missing config before you can export it.') . '<br>' . $this->t('Or, enable the Allow Conflicts option above.') . '</strong>';
         }
         $form['actions']['import_missing'] = array('#type' => 'submit', '#name' => 'import_missing', '#value' => $this->t('Import Missing'), '#attributes' => array('title' => $this->t('Import only the missing configuration items.')));
     }
     $form['#attached'] = array('library' => array('features_ui/drupal.features_ui.admin'), 'drupalSettings' => array('features' => array('excluded' => $this->excluded, 'required' => $this->required, 'conflicts' => $this->conflicts, 'autodetect' => TRUE)));
     return $form;
 }