FeaturesUIController.php 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. <?php
  2. namespace Drupal\features_ui\Controller;
  3. use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
  4. use Symfony\Component\DependencyInjection\ContainerInterface;
  5. use Symfony\Component\HttpFoundation\JsonResponse;
  6. use Drupal\features\FeaturesManagerInterface;
  7. use Drupal\features\FeaturesAssignerInterface;
  8. /**
  9. * Returns ajax responses for the Features UI.
  10. */
  11. class FeaturesUIController implements ContainerInjectionInterface {
  12. /**
  13. * The features manager.
  14. *
  15. * @var \Drupal\features\FeaturesManagerInterface
  16. */
  17. protected $featuresManager;
  18. /**
  19. * The package assigner.
  20. *
  21. * @var array
  22. */
  23. protected $assigner;
  24. /**
  25. * Constructs a new FeaturesUIController object.
  26. *
  27. * @param \Drupal\features\FeaturesManagerInterface $features_manager
  28. * The features manager.
  29. */
  30. public function __construct(FeaturesManagerInterface $features_manager, FeaturesAssignerInterface $assigner) {
  31. $this->featuresManager = $features_manager;
  32. $this->assigner = $assigner;
  33. }
  34. /**
  35. * {@inheritdoc}
  36. */
  37. public static function create(ContainerInterface $container) {
  38. return new static(
  39. $container->get('features.manager'),
  40. $container->get('features_assigner')
  41. );
  42. }
  43. /**
  44. * Returns a list of auto-detected config items for a feature.
  45. *
  46. * @param string $name
  47. * Short machine name of feature to process.
  48. *
  49. * @return array
  50. * List of auto-detected config items, keyed by type and short name.
  51. */
  52. public function detect($name) {
  53. $detected = array();
  54. $this->assigner->assignConfigPackages();
  55. $config_collection = $this->featuresManager->getConfigCollection();
  56. $items = $_POST['items'];
  57. if (!empty($items)) {
  58. $excluded = (!empty($_POST['excluded'])) ? $_POST['excluded'] : array();
  59. $selected = array();
  60. foreach ($items as $key) {
  61. preg_match('/^([^\[]+)(\[.+\])?\[(.+)\]\[(.+)\]$/', $key, $matches);
  62. if (!empty($matches[1]) && !empty($matches[4])) {
  63. $component = $matches[1];
  64. $item = $this->domDecode($matches[4]);
  65. if (!isset($excluded[$component][$item])) {
  66. $selected[] = $this->featuresManager->getFullName($component, $item);
  67. }
  68. }
  69. }
  70. $detected = !empty($selected) ? $this->getConfigDependents($selected, $name) : array();
  71. $detected = array_merge($detected, $selected);
  72. }
  73. $result = [];
  74. foreach ($detected as $config_name) {
  75. $item = $config_collection[$config_name];
  76. $result[$item->getType()][$item->getShortName()] = $item->getName();
  77. }
  78. return new JsonResponse($result);
  79. }
  80. /**
  81. * Returns the configuration dependent on given items.
  82. *
  83. * @param array $item_names
  84. * An array of item names.
  85. * @param string $package_name
  86. * Short machine name of feature to process.
  87. *
  88. * @return array
  89. * An array of config items.
  90. */
  91. protected function getConfigDependents(array $item_names, $package_name) {
  92. $result = [];
  93. $config_collection = $this->featuresManager->getConfigCollection();
  94. $packages = $this->featuresManager->getPackages();
  95. $settings = $this->featuresManager->getSettings();
  96. $allow_conflicts = $settings->get('conflicts');
  97. if (empty($item_names)) {
  98. $item_names = array_keys($config_collection);
  99. }
  100. // Add any existing auto-detected items already in the package config
  101. $this->package = $packages[$package_name];
  102. $package_config = isset($this->package) ? $this->package->getConfig() : array();
  103. $package_config = !empty($package_config) ? array_unique(array_merge($package_config, $item_names)) : $item_names;
  104. foreach ($package_config as $config_name) {
  105. if (!$config_collection[$config_name]->getPackageExcluded()) {
  106. $result[] = $config_name;
  107. }
  108. }
  109. // Now add dependents of the items selected
  110. foreach ($item_names as $item_name) {
  111. if ($config_collection[$item_name]->getPackage()) {
  112. foreach ($config_collection[$item_name]->getDependents() as $dependent_item_name) {
  113. if (isset($config_collection[$dependent_item_name])) {
  114. $allow = TRUE;
  115. if (!$allow_conflicts && $config_collection[$dependent_item_name]->getPackage()) {
  116. if ($packages[$config_collection[$dependent_item_name]->getPackage()]) {
  117. $allow = ($packages[$config_collection[$dependent_item_name]->getPackage()]->getStatus() == FeaturesManagerInterface::STATUS_NO_EXPORT)
  118. || ($config_collection[$item_name]->getPackage() == $config_collection[$dependent_item_name]->getPackage());
  119. }
  120. }
  121. if ($allow) {
  122. $result[] = $dependent_item_name;
  123. }
  124. }
  125. }
  126. }
  127. }
  128. return $result;
  129. }
  130. /**
  131. * Encodes a given key.
  132. *
  133. * @param string $key
  134. * The key to encode.
  135. *
  136. * @return string
  137. * The encoded key.
  138. */
  139. protected function domEncode($key) {
  140. $replacements = $this->domEncodeMap();
  141. return strtr($key, $replacements);
  142. }
  143. /**
  144. * Decodes a given key.
  145. *
  146. * @param string $key
  147. * The key to decode.
  148. *
  149. * @return string
  150. * The decoded key.
  151. */
  152. protected function domDecode($key) {
  153. $replacements = array_flip($this->domEncodeMap());
  154. return strtr($key, $replacements);
  155. }
  156. /**
  157. * Returns encoding map for decode and encode options.
  158. *
  159. * @return array
  160. * An encoding map.
  161. */
  162. protected function domEncodeMap() {
  163. return array(
  164. ':' => '__' . ord(':') . '__',
  165. '/' => '__' . ord('/') . '__',
  166. ',' => '__' . ord(',') . '__',
  167. '.' => '__' . ord('.') . '__',
  168. '<' => '__' . ord('<') . '__',
  169. '>' => '__' . ord('>') . '__',
  170. '%' => '__' . ord('%') . '__',
  171. ')' => '__' . ord(')') . '__',
  172. '(' => '__' . ord('(') . '__',
  173. );
  174. }
  175. }