FeaturesDiffForm.php 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. <?php
  2. namespace Drupal\features_ui\Form;
  3. use Drupal\Component\Utility\Html;
  4. use Drupal\features\ConfigurationItem;
  5. use Drupal\features\FeaturesAssignerInterface;
  6. use Drupal\features\FeaturesManagerInterface;
  7. use Drupal\Core\Form\FormBase;
  8. use Drupal\Core\Form\FormStateInterface;
  9. use Drupal\features\Package;
  10. use Symfony\Component\DependencyInjection\ContainerInterface;
  11. use Drupal\Component\Diff\DiffFormatter;
  12. use Drupal\config_update\ConfigRevertInterface;
  13. use Drupal\config_update\ConfigDiffInterface;
  14. /**
  15. * Defines the features differences form.
  16. */
  17. class FeaturesDiffForm extends FormBase {
  18. /**
  19. * The features manager.
  20. *
  21. * @var array
  22. */
  23. protected $featuresManager;
  24. /**
  25. * The package assigner.
  26. *
  27. * @var array
  28. */
  29. protected $assigner;
  30. /**
  31. * The config differ.
  32. *
  33. * @var \Drupal\config_update\ConfigDiffInterface
  34. */
  35. protected $configDiff;
  36. /**
  37. * The diff formatter.
  38. *
  39. * @var \Drupal\Core\Diff\DiffFormatter
  40. */
  41. protected $diffFormatter;
  42. /**
  43. * The config reverter.
  44. *
  45. * @var \Drupal\config_update\ConfigRevertInterface
  46. */
  47. protected $configRevert;
  48. /**
  49. * Constructs a FeaturesDiffForm object.
  50. *
  51. * @param \Drupal\features\FeaturesManagerInterface $features_manager
  52. * The features manager.
  53. */
  54. public function __construct(FeaturesManagerInterface $features_manager, FeaturesAssignerInterface $assigner,
  55. ConfigDiffInterface $config_diff, DiffFormatter $diff_formatter,
  56. ConfigRevertInterface $config_revert) {
  57. $this->featuresManager = $features_manager;
  58. $this->assigner = $assigner;
  59. $this->configDiff = $config_diff;
  60. $this->diffFormatter = $diff_formatter;
  61. $this->configRevert = $config_revert;
  62. $this->diffFormatter->show_header = FALSE;
  63. $this->diffFormatter->leading_context_lines = 0;
  64. $this->diffFormatter->trailing_context_lines = 0;
  65. }
  66. /**
  67. * {@inheritdoc}
  68. */
  69. public static function create(ContainerInterface $container) {
  70. return new static(
  71. $container->get('features.manager'),
  72. $container->get('features_assigner'),
  73. $container->get('config_update.config_diff'),
  74. $container->get('diff.formatter'),
  75. $container->get('features.config_update')
  76. );
  77. }
  78. /**
  79. * {@inheritdoc}
  80. */
  81. public function getFormId() {
  82. return 'features_diff_form';
  83. }
  84. /**
  85. * {@inheritdoc}
  86. */
  87. public function buildForm(array $form, FormStateInterface $form_state, $featurename = '') {
  88. $current_bundle = $this->assigner->applyBundle();
  89. $packages = $this->featuresManager->getPackages();
  90. $form = array();
  91. $machine_name = '';
  92. if (!empty($featurename) && empty($packages[$featurename])) {
  93. drupal_set_message($this->t('Feature @name does not exist.', array('@name' => $featurename)), 'error');
  94. return array();
  95. }
  96. elseif (!empty($featurename)) {
  97. $machine_name = $packages[$featurename]->getMachineName();
  98. $packages = array($packages[$featurename]);
  99. }
  100. else {
  101. $packages = $this->featuresManager->filterPackages($packages, $current_bundle->getMachineName());
  102. }
  103. $header = array(
  104. 'row' => array(
  105. 'data' => !empty($machine_name)
  106. ? $this->t('Differences in @name', array('@name' => $machine_name))
  107. : ($current_bundle->isDefault() ? $this->t('All differences') : $this->t('All differences in bundle: @bundle', array('@bundle' => $current_bundle->getName()))),
  108. ),
  109. );
  110. $options = array();
  111. foreach ($packages as $package) {
  112. if ($package->getStatus() != FeaturesManagerInterface::STATUS_NO_EXPORT) {
  113. $missing = $this->featuresManager->reorderMissing($this->featuresManager->detectMissing($package));
  114. $overrides = $this->featuresManager->detectOverrides($package, TRUE);
  115. if (!empty($overrides) || !empty($missing)) {
  116. $options += array(
  117. $package->getMachineName() => array(
  118. 'row' => array(
  119. 'data' => array(
  120. '#type' => 'html_tag',
  121. '#tag' => 'h2',
  122. '#value' => Html::escape($package->getName()),
  123. ),
  124. ),
  125. '#attributes' => array(
  126. 'class' => 'features-diff-header',
  127. ),
  128. ),
  129. );
  130. $options += $this->diffOutput($package, $overrides, $missing);
  131. }
  132. }
  133. }
  134. $form['diff'] = array(
  135. '#type' => 'tableselect',
  136. '#header' => $header,
  137. '#options' => $options,
  138. '#attributes' => array('class' => array('features-diff-listing')),
  139. '#empty' => $this->t('No differences exist in exported features.'),
  140. );
  141. $form['actions'] = array('#type' => 'actions', '#tree' => TRUE);
  142. $form['actions']['revert'] = array(
  143. '#type' => 'submit',
  144. '#value' => $this->t('Import changes'),
  145. );
  146. $form['actions']['help'] = array(
  147. '#markup' => $this->t('Import the selected changes above into the active configuration.'),
  148. );
  149. $form['#attached']['library'][] = 'system/diff';
  150. $form['#attached']['library'][] = 'features_ui/drupal.features_ui.admin';
  151. return $form;
  152. }
  153. /**
  154. * {@inheritdoc}
  155. */
  156. public function submitForm(array &$form, FormStateInterface $form_state) {
  157. $this->assigner->assignConfigPackages();
  158. $config = $this->featuresManager->getConfigCollection();
  159. $items = array_filter($form_state->getValue('diff'));
  160. if (empty($items)) {
  161. drupal_set_message($this->t('No configuration was selected for import.'), 'warning');
  162. return;
  163. }
  164. foreach ($items as $config_name) {
  165. if (isset($config[$config_name])) {
  166. $item = $config[$config_name];
  167. $type = ConfigurationItem::fromConfigStringToConfigType($item->getType());
  168. $this->configRevert->revert($type, $item->getShortName());
  169. }
  170. else {
  171. $item = $this->featuresManager->getConfigType($config_name);
  172. $type = ConfigurationItem::fromConfigStringToConfigType($item['type']);
  173. $this->configRevert->import($type, $item['name_short']);
  174. }
  175. drupal_set_message($this->t('Imported @name', array('@name' => $config_name)));
  176. }
  177. }
  178. /**
  179. * Returns a form element for the given overrides.
  180. *
  181. * @param \Drupal\features\Package $package
  182. * A package.
  183. * @param array $overrides
  184. * An array of overrides.
  185. * @param array $missing
  186. * An array of missing config.
  187. *
  188. * @return array
  189. * A form element.
  190. */
  191. protected function diffOutput(Package $package, $overrides, $missing = array()) {
  192. $element = array();
  193. $config = $this->featuresManager->getConfigCollection();
  194. $components = array_merge($missing, $overrides);
  195. $header = array(
  196. array('data' => '', 'class' => 'diff-marker'),
  197. array('data' => $this->t('Active site config'), 'class' => 'diff-context'),
  198. array('data' => '', 'class' => 'diff-marker'),
  199. array('data' => $this->t('Feature code config'), 'class' => 'diff-context'),
  200. );
  201. foreach ($components as $name) {
  202. $rows[] = array(array('data' => $name, 'colspan' => 4, 'header' => TRUE));
  203. if (!isset($config[$name])) {
  204. $details = array(
  205. '#markup' => $this->t('Component in feature missing from active config.'),
  206. );
  207. }
  208. else {
  209. $active = $this->featuresManager->getActiveStorage()->read($name);
  210. $extension = $this->featuresManager->getExtensionStorages()->read($name);
  211. if (empty($extension)) {
  212. $details = array(
  213. '#markup' => $this->t('Dependency detected in active config but not exported to the feature.'),
  214. );
  215. }
  216. else {
  217. $diff = $this->configDiff->diff($active, $extension);
  218. $details = array(
  219. '#type' => 'table',
  220. '#header' => $header,
  221. '#rows' => $this->diffFormatter->format($diff),
  222. '#attributes' => array('class' => array('diff', 'features-diff')),
  223. );
  224. }
  225. }
  226. $element[$name] = array(
  227. 'row' => array(
  228. 'data' => array(
  229. '#type' => 'details',
  230. '#title' => Html::escape($name),
  231. '#open' => TRUE,
  232. '#description' => array(
  233. 'data' => $details,
  234. ),
  235. ),
  236. ),
  237. '#attributes' => array(
  238. 'class' => 'diff-' . $package->getMachineName(),
  239. ),
  240. );
  241. }
  242. return $element;
  243. }
  244. }