DomainElementManager.php 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. <?php
  2. namespace Drupal\domain;
  3. use Drupal\Core\Entity\EntityInterface;
  4. use Drupal\Core\Form\FormStateInterface;
  5. use Drupal\Core\StringTranslation\StringTranslationTrait;
  6. use Drupal\Core\Entity\EntityTypeManagerInterface;
  7. /**
  8. * Generic base class for handling hidden field options.
  9. *
  10. * Since domain options are restricted for various forms (users, nodes, source)
  11. * we have a base class for handling common use cases. The details of each
  12. * implementation are generally handled by a subclass and invoked within a
  13. * hook_form_alter().
  14. *
  15. * This class has some similarities to DomainAccessManager, but only cares
  16. * about form handling. It can be used as a base class by other modules that
  17. * show/hide domain options. See the DomainSourceElementManager for a
  18. * non-default implementation.
  19. */
  20. class DomainElementManager implements DomainElementManagerInterface {
  21. use StringTranslationTrait;
  22. /**
  23. * The entity type manager.
  24. *
  25. * @var \Drupal\Core\Entity\EntityTypeManagerInterface
  26. */
  27. protected $entityTypeManager;
  28. /**
  29. * The domain storage.
  30. *
  31. * @var \Drupal\domain\DomainStorageInterface
  32. */
  33. protected $domainStorage;
  34. /**
  35. * Constructs a DomainElementManager object.
  36. *
  37. * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
  38. * The entity type manager.
  39. */
  40. public function __construct(EntityTypeManagerInterface $entity_type_manager) {
  41. $this->entityTypeManager = $entity_type_manager;
  42. $this->domainStorage = $entity_type_manager->getStorage('domain');
  43. }
  44. /**
  45. * {@inheritdoc}
  46. */
  47. public function setFormOptions(array $form, FormStateInterface $form_state, $field_name, $hide_on_disallow = FALSE) {
  48. // There are cases, such as Entity Browser, where the form is partially
  49. // invoked, but without our fields.
  50. if (!isset($form[$field_name])) {
  51. return $form;
  52. }
  53. $fields = $this->fieldList($field_name);
  54. $disallowed = $this->disallowedOptions($form_state, $form[$field_name]);
  55. $empty = empty($form[$field_name]['widget']['#options']);
  56. // If the domain form element is set as a group, and the field is not
  57. // assigned to another group, then move it. See
  58. // domain_access_form_node_form_alter().
  59. if (isset($form['domain']) && !isset($form[$field_name]['#group'])) {
  60. $form[$field_name]['#group'] = 'domain';
  61. }
  62. // Check for domains the user cannot access or the absence of any options.
  63. if (!empty($disallowed) || $empty) {
  64. // @TODO: Potentially show this information to users with permission.
  65. $form[$field_name . '_disallowed'] = [
  66. '#type' => 'value',
  67. '#value' => $disallowed,
  68. ];
  69. $form['domain_hidden_fields'] = [
  70. '#type' => 'value',
  71. '#value' => $fields,
  72. ];
  73. if ($hide_on_disallow || $empty) {
  74. $form[$field_name]['#access'] = FALSE;
  75. }
  76. elseif (!empty($disallowed)) {
  77. $form[$field_name]['widget']['#description'] .= $this->listDisallowed($disallowed);
  78. }
  79. // Call our submit function to merge in values.
  80. // Account for all the submit buttons on the node form.
  81. $buttons = ['preview', 'delete'];
  82. $submit = $this->getSubmitHandler();
  83. foreach ($form['actions'] as $key => $action) {
  84. if (!in_array($key, $buttons) && isset($form['actions'][$key]['#submit']) && !in_array($submit, $form['actions'][$key]['#submit'])) {
  85. array_unshift($form['actions'][$key]['#submit'], $submit);
  86. }
  87. }
  88. }
  89. return $form;
  90. }
  91. /**
  92. * {@inheritdoc}
  93. */
  94. public static function submitEntityForm(array &$form, FormStateInterface $form_state) {
  95. $fields = $form_state->getValue('domain_hidden_fields');
  96. foreach ($fields as $field) {
  97. $entity_values = [];
  98. $values = $form_state->getValue($field . '_disallowed');
  99. if (!empty($values)) {
  100. $info = $form_state->getBuildInfo();
  101. $node = $form_state->getFormObject()->getEntity();
  102. $entity_values = $form_state->getValue($field);
  103. }
  104. foreach ($values as $value) {
  105. $entity_values[]['target_id'] = $value;
  106. }
  107. // Prevent a fatal error caused by passing a NULL value.
  108. // See https://www.drupal.org/node/2841962.
  109. if (!empty($entity_values)) {
  110. $form_state->setValue($field, $entity_values);
  111. }
  112. }
  113. }
  114. /**
  115. * {@inheritdoc}
  116. */
  117. public function disallowedOptions(FormStateInterface $form_state, array $field) {
  118. $options = [];
  119. $info = $form_state->getBuildInfo();
  120. $entity = $form_state->getFormObject()->getEntity();
  121. $entity_values = $this->getFieldValues($entity, $field['widget']['#field_name']);
  122. if (isset($field['widget']['#options'])) {
  123. $options = array_diff_key($entity_values, $field['widget']['#options']);
  124. }
  125. return array_keys($options);
  126. }
  127. /**
  128. * {@inheritdoc}
  129. */
  130. public function fieldList($field_name) {
  131. static $fields = [];
  132. $fields[] = $field_name;
  133. // Return only unique field names. AJAX requests can result in duplicates.
  134. // See https://www.drupal.org/project/domain/issues/2930934.
  135. return array_unique($fields);
  136. }
  137. /**
  138. * {@inheritdoc}
  139. */
  140. public function getFieldValues(EntityInterface $entity, $field_name) {
  141. // @TODO: static cache.
  142. $list = [];
  143. // @TODO In tests, $entity is returning NULL.
  144. if (is_null($entity)) {
  145. return $list;
  146. }
  147. // Get the values of an entity.
  148. $values = $entity->hasField($field_name) ? $entity->get($field_name) : NULL;
  149. // Must be at least one item.
  150. if (!empty($values)) {
  151. foreach ($values as $item) {
  152. if ($target = $item->getValue()) {
  153. if ($domain = $this->domainStorage->load($target['target_id'])) {
  154. $list[$domain->id()] = $domain->getDomainId();
  155. }
  156. }
  157. }
  158. }
  159. return $list;
  160. }
  161. /**
  162. * {@inheritdoc}
  163. */
  164. public function getSubmitHandler() {
  165. return '\\Drupal\\domain\\DomainElementManager::submitEntityForm';
  166. }
  167. /**
  168. * Lists the disallowed domains in the user interface.
  169. *
  170. * @param array $disallowed
  171. * An array of domain ids.
  172. *
  173. * @return string
  174. * A string suitable for display.
  175. */
  176. public function listDisallowed(array $disallowed) {
  177. $domains = $this->domainStorage->loadMultiple($disallowed);
  178. $string = $this->t('The following domains are currently assigned and cannot be changed:');
  179. foreach ($domains as $domain) {
  180. $items[] = $domain->label();
  181. }
  182. $build = [
  183. '#theme' => 'item_list',
  184. '#items' => $items,
  185. ];
  186. $string .= render($build);
  187. return '<div class="disallowed">' . $string . '</div>';
  188. }
  189. }