123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446 |
- <?php
- namespace Drupal\features;
- use Drupal\Component\Plugin\PluginManagerInterface;
- use Drupal\Core\Config\ConfigFactoryInterface;
- use Drupal\Core\Config\ExtensionInstallStorage;
- use Drupal\Core\Entity\EntityTypeManagerInterface;
- use Drupal\Core\Config\StorageInterface;
- use Drupal\Core\StringTranslation\StringTranslationTrait;
- use Drupal\features\Entity\FeaturesBundle;
- /**
- * Class responsible for performing package assignment.
- */
- class FeaturesAssigner implements FeaturesAssignerInterface {
- use StringTranslationTrait;
- /**
- * The package assignment method plugin manager.
- *
- * @var \Drupal\Component\Plugin\PluginManagerInterface
- */
- protected $assignerManager;
- /**
- * The features manager.
- *
- * @var \Drupal\features\FeaturesManagerInterface
- */
- protected $featuresManager;
- /**
- * The configuration factory.
- *
- * @var \Drupal\Core\Config\ConfigFactoryInterface
- */
- protected $configFactory;
- /**
- * The configuration storage.
- *
- * @var \Drupal\Core\Config\StorageInterface
- */
- protected $configStorage;
- /**
- * The entity type manager.
- *
- * @var \Drupal\Core\Entity\EntityTypeManagerInterface
- */
- protected $entityTypeManager;
- /**
- * Local cache for package assignment method instances.
- *
- * @var array
- */
- protected $methods;
- /**
- * Bundles.
- *
- * @var array of \Drupal\features\FeaturesBundleInterface
- */
- protected $bundles;
- /**
- * Currently active bundle.
- *
- * @var \Drupal\features\FeaturesBundleInterface
- */
- protected $currentBundle;
- /**
- * Constructs a new FeaturesAssigner object.
- *
- * @param \Drupal\features\FeaturesManagerInterface $features_manager
- * The features manager.
- * @param \Drupal\Component\Plugin\PluginManagerInterface $assigner_manager
- * The package assignment methods plugin manager.
- * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
- * The entity type manager.
- * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
- * The configuration factory.
- * @param \Drupal\Core\Config\StorageInterface $config_storage
- * The configuration factory.
- */
- public function __construct(FeaturesManagerInterface $features_manager, PluginManagerInterface $assigner_manager, EntityTypeManagerInterface $entity_type_manager, ConfigFactoryInterface $config_factory, StorageInterface $config_storage) {
- $this->featuresManager = $features_manager;
- $this->assignerManager = $assigner_manager;
- $this->entityTypeManager = $entity_type_manager;
- $this->configFactory = $config_factory;
- $this->configStorage = $config_storage;
- $this->bundles = $this->getBundleList();
- $this->currentBundle = $this->getBundle(FeaturesBundleInterface::DEFAULT_BUNDLE);
- // Ensure bundle information is fresh.
- $this->createBundlesFromPackages();
- }
- /**
- * Initializes the injected features manager with the assigner.
- *
- * This should be called right after instantiating the assigner to make it
- * available to the features manager without introducing a circular
- * dependency.
- */
- public function initFeaturesManager() {
- $this->featuresManager->setAssigner($this);
- }
- /**
- * {@inheritdoc}
- */
- public function reset() {
- $this->methods = [];
- $this->featuresManager->reset();
- }
- /**
- * Gets enabled assignment methods.
- *
- * @return array
- * An array of enabled assignment methods, sorted by weight.
- */
- public function getEnabledAssigners() {
- $enabled = $this->currentBundle->getEnabledAssignments();
- $weights = $this->currentBundle->getAssignmentWeights();
- foreach ($enabled as $key => $value) {
- $enabled[$key] = $weights[$key];
- }
- asort($enabled);
- return $enabled;
- }
- /**
- * Clean up the package list after all config has been assigned
- */
- protected function cleanup() {
- $packages = $this->featuresManager->getPackages();
- foreach ($packages as $index => $package) {
- if ($package->getStatus() === FeaturesManagerInterface::STATUS_NO_EXPORT && empty($package->getConfig()) && empty($package->getConfigOrig())) {
- unset($packages[$index]);
- }
- }
- $this->featuresManager->setPackages($packages);
- }
- /**
- * {@inheritdoc}
- */
- public function assignConfigPackages($force = FALSE) {
- foreach ($this->getEnabledAssigners() as $method_id => $info) {
- $this->applyAssignmentMethod($method_id, $force);
- }
- $this->cleanup();
- }
- /**
- * {@inheritdoc}
- */
- public function applyAssignmentMethod($method_id, $force = FALSE) {
- $this->getAssignmentMethodInstance($method_id)->assignPackages($force);
- }
- /**
- * {@inheritdoc}
- */
- public function getAssignmentMethods() {
- return $this->assignerManager->getDefinitions();
- }
- /**
- * Returns an instance of the specified package assignment method.
- *
- * @param string $method_id
- * The string identifier of the package assignment method to use to package
- * configuration.
- *
- * @return \Drupal\features\FeaturesAssignmentMethodInterface
- */
- protected function getAssignmentMethodInstance($method_id) {
- if (!isset($this->methods[$method_id])) {
- $instance = $this->assignerManager->createInstance($method_id, []);
- $instance->setFeaturesManager($this->featuresManager);
- $instance->setAssigner($this);
- $instance->setEntityTypeManager($this->entityTypeManager);
- $instance->setConfigFactory($this->configFactory);
- $this->methods[$method_id] = $instance;
- }
- return $this->methods[$method_id];
- }
- /**
- * {@inheritdoc}
- */
- public function purgeConfiguration() {
- // Ensure that we are getting the defined package assignment information.
- // An invocation of \Drupal\Core\Extension\ModuleHandler::install() or
- // \Drupal\Core\Extension\ModuleHandler::uninstall() could invalidate the
- // cached information.
- $this->assignerManager->clearCachedDefinitions();
- $this->featuresManager->reset();
- }
- /**
- * {@inheritdoc}
- */
- public function getBundle($name = NULL) {
- if (empty($name)) {
- return $this->currentBundle;
- }
- elseif (isset($this->bundles[$name])) {
- return $this->bundles[$name];
- }
- return NULL;
- }
- /**
- * {@inheritdoc}
- */
- public function setBundle(FeaturesBundleInterface $bundle, $current = TRUE) {
- $this->bundles[$bundle->getMachineName()] = $bundle;
- if (isset($this->currentBundle) && ($current || ($bundle->getMachineName() == $this->currentBundle->getMachineName()))) {
- $this->currentBundle = $bundle;
- }
- }
- /**
- * {@inheritdoc}
- */
- public function findBundle(array $info, $features_info = NULL) {
- $bundle = NULL;
- if (!empty($features_info['bundle'])) {
- $bundle = $this->getBundle($features_info['bundle']);
- }
- elseif (!empty($info['package'])) {
- $bundle = $this->findBundleByName($info['package']);
- }
- if (!isset($bundle)) {
- // Return the default bundle.
- return $this->getBundle(FeaturesBundleInterface::DEFAULT_BUNDLE);
- }
- return $bundle;
- }
- /**
- * {@inheritdoc}
- */
- public function setCurrent(FeaturesBundleInterface $bundle) {
- $this->currentBundle = $bundle;
- $session = \Drupal::request()->getSession();
- if (isset($session)) {
- $session->set('features_current_bundle', $bundle->getMachineName());
- }
- return $bundle;
- }
- /**
- * {@inheritdoc}
- */
- public function getBundleList() {
- if (empty($this->bundles)) {
- $this->bundles = [];
- foreach ($this->entityTypeManager->getStorage('features_bundle')->loadMultiple() as $machine_name => $bundle) {
- $this->bundles[$machine_name] = $bundle;
- }
- }
- return $this->bundles;
- }
- /**
- * {@inheritdoc}
- */
- public function findBundleByName($name, $create = FALSE) {
- $bundles = $this->getBundleList();
- foreach ($bundles as $machine_name => $bundle) {
- if ($name == $bundle->getName()) {
- return $bundle;
- }
- }
- $machine_name = strtolower(str_replace([' ', '-'], '_', $name));
- if (isset($bundles[$machine_name])) {
- return $bundles[$machine_name];
- }
- return NULL;
- }
- /**
- * {@inheritdoc}
- */
- public function createBundleFromDefault($machine_name, $name = NULL, $description = NULL, $is_profile = FALSE, $profile_name = NULL) {
- // Duplicate the default bundle to get its default configuration.
- $default = $this->getBundle(FeaturesBundleInterface::DEFAULT_BUNDLE);
- if (!$default) {
- // If we don't have the default installed, generate it from the install
- // config file.
- $ext_storage = new ExtensionInstallStorage($this->configStorage);
- $record = $ext_storage->read('features.bundle.default');
- $bundle_storage = $this->entityTypeManager->getStorage('features_bundle');
- $default = $bundle_storage->createFromStorageRecord($record);
- }
- /** @var \Drupal\features\Entity\FeaturesBundle $bundle */
- $bundle = $default->createDuplicate();
- $bundle->setMachineName($machine_name);
- $name = !empty($name) ? $name : $machine_name;
- $bundle->setName($name);
- if (isset($description)) {
- $bundle->setDescription($description);
- }
- else {
- $bundle->setDescription(t('Auto-generated bundle from package @name', ['@name' => $name]));
- }
- $bundle->setIsProfile($is_profile);
- if (isset($profile_name)) {
- $bundle->setProfileName($profile_name);
- }
- $bundle->save();
- $this->setBundle($bundle);
- return $bundle;
- }
- /**
- * {@inheritdoc}
- */
- public function createBundlesFromPackages() {
- $existing_bundles = $this->getBundleList();
- $new_bundles = [];
- // Only parse from installed features.
- $modules = $this->featuresManager->getFeaturesModules(NULL, TRUE);
- foreach ($modules as $module) {
- $info = $this->featuresManager->getExtensionInfo($module);
- // @todo This entire function could be simplified a lot using packages.
- $features_info = $this->featuresManager->getFeaturesInfo($module);
- // Create a new bundle if:
- // - the feature specifies a bundle and
- // - that bundle doesn't yet exist locally.
- // Allow profiles to override previous values.
- if (!empty($features_info['bundle']) &&
- !isset($existing_bundles[$features_info['bundle']]) &&
- (!in_array($features_info['bundle'], $new_bundles) || $info['type'] == 'profile')) {
- if ($info['type'] == 'profile') {
- $new_bundle = [
- 'name' => $info['name'],
- 'description' => $info['description'],
- 'is_profile' => TRUE,
- 'profile_name' => $module->getName(),
- ];
- }
- else {
- $new_bundle = [
- 'name' => isset($info['package']) ? $info['package'] : ucwords(str_replace('_', ' ', $features_info['bundle'])),
- 'description' => NULL,
- 'is_profile' => FALSE,
- 'profile_name' => NULL,
- ];
- }
- $new_bundle['machine_name'] = $features_info['bundle'];
- $new_bundles[$new_bundle['machine_name']] = $new_bundle;
- }
- }
- foreach ($new_bundles as $new_bundle) {
- $this->createBundleFromDefault($new_bundle['machine_name'], $new_bundle['name'], $new_bundle['description'], $new_bundle['is_profile']);
- }
- }
- /**
- * {@inheritdoc}
- */
- public function getBundleOptions() {
- $list = $this->getBundleList();
- $result = [];
- foreach ($list as $machine_name => $bundle) {
- $result[$machine_name] = $bundle->getName();
- }
- return $result;
- }
- /**
- * {@inheritdoc}
- */
- public function applyBundle($machine_name = NULL) {
- $this->reset();
- $bundle = $this->loadBundle($machine_name);
- if (isset($bundle)) {
- $this->assignConfigPackages();
- return $this->currentBundle;
- }
- return NULL;
- }
- /**
- * {@inheritdoc}
- */
- public function renameBundle($old_machine, $new_machine) {
- $is_current = (isset($this->currentBundle) && ($old_machine == $this->currentBundle->getMachineName()));
- $bundle = $this->getBundle($old_machine);
- if ($bundle->getMachineName() != '') {
- // Remove old bundle from the list if it's not the Default bundle.
- unset($this->bundles[$old_machine]);
- }
- $bundle->setMachineName($new_machine);
- $this->setBundle($bundle);
- // Put the bundle into the list with the correct name.
- $this->bundles[$bundle->getMachineName()] = $bundle;
- if ($is_current) {
- $this->setCurrent($bundle);
- }
- return $bundle;
- }
- /**
- * {@inheritdoc}
- */
- public function loadBundle($machine_name = NULL) {
- if (!isset($machine_name)) {
- $session = \Drupal::request()->getSession();
- if (isset($session)) {
- $machine_name = isset($session) ? $session->get('features_current_bundle', FeaturesBundleInterface::DEFAULT_BUNDLE) : FeaturesBundleInterface::DEFAULT_BUNDLE;
- }
- }
- $bundle = $this->getBundle($machine_name);
- if (!isset($bundle)) {
- // If bundle no longer exists then return default.
- $bundle = $this->bundles[FeaturesBundleInterface::DEFAULT_BUNDLE];
- }
- return $this->setCurrent($bundle);
- }
- /**
- * {@inheritdoc}
- */
- public function removeBundle($machine_name) {
- $bundle = $this->getBundle($machine_name);
- if (isset($bundle) && !$bundle->isDefault()) {
- unset($this->bundles[$machine_name]);
- $bundle->remove();
- }
- }
- }
|