1<?php
2
3namespace Drupal\node;
4
5use Drupal\Core\DependencyInjection\DeprecatedServicePropertyTrait;
6use Drupal\Core\Entity\BundleEntityFormBase;
7use Drupal\Core\Entity\EntityFieldManagerInterface;
8use Drupal\Core\Entity\EntityTypeInterface;
9use Drupal\Core\Form\FormStateInterface;
10use Drupal\language\Entity\ContentLanguageSettings;
11use Symfony\Component\DependencyInjection\ContainerInterface;
12
13/**
14 * Form handler for node type forms.
15 *
16 * @internal
17 */
18class NodeTypeForm extends BundleEntityFormBase {
19  use DeprecatedServicePropertyTrait;
20
21  /**
22   * {@inheritdoc}
23   */
24  protected $deprecatedProperties = [
25    'entityManager' => 'entity.manager',
26  ];
27
28  /**
29   * The entity field manager.
30   *
31   * @var \Drupal\Core\Entity\EntityFieldManagerInterface
32   */
33  protected $entityFieldManager;
34
35  /**
36   * Constructs the NodeTypeForm object.
37   *
38   * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
39   *   The entity field manager.
40   */
41  public function __construct(EntityFieldManagerInterface $entity_field_manager) {
42    $this->entityFieldManager = $entity_field_manager;
43  }
44
45  /**
46   * {@inheritdoc}
47   */
48  public static function create(ContainerInterface $container) {
49    return new static(
50      $container->get('entity_field.manager')
51    );
52  }
53
54  /**
55   * {@inheritdoc}
56   */
57  public function form(array $form, FormStateInterface $form_state) {
58    $form = parent::form($form, $form_state);
59
60    $type = $this->entity;
61    if ($this->operation == 'add') {
62      $form['#title'] = $this->t('Add content type');
63      $fields = $this->entityFieldManager->getBaseFieldDefinitions('node');
64      // Create a node with a fake bundle using the type's UUID so that we can
65      // get the default values for workflow settings.
66      // @todo Make it possible to get default values without an entity.
67      //   https://www.drupal.org/node/2318187
68      $node = $this->entityTypeManager->getStorage('node')->create(['type' => $type->uuid()]);
69    }
70    else {
71      $form['#title'] = $this->t('Edit %label content type', ['%label' => $type->label()]);
72      $fields = $this->entityFieldManager->getFieldDefinitions('node', $type->id());
73      // Create a node to get the current values for workflow settings fields.
74      $node = $this->entityTypeManager->getStorage('node')->create(['type' => $type->id()]);
75    }
76
77    $form['name'] = [
78      '#title' => t('Name'),
79      '#type' => 'textfield',
80      '#default_value' => $type->label(),
81      '#description' => t('The human-readable name of this content type. This text will be displayed as part of the list on the <em>Add content</em> page. This name must be unique.'),
82      '#required' => TRUE,
83      '#size' => 30,
84    ];
85
86    $form['type'] = [
87      '#type' => 'machine_name',
88      '#default_value' => $type->id(),
89      '#maxlength' => EntityTypeInterface::BUNDLE_MAX_LENGTH,
90      '#disabled' => $type->isLocked(),
91      '#machine_name' => [
92        'exists' => ['Drupal\node\Entity\NodeType', 'load'],
93        'source' => ['name'],
94      ],
95      '#description' => t('A unique machine-readable name for this content type. It must only contain lowercase letters, numbers, and underscores. This name will be used for constructing the URL of the %node-add page, in which underscores will be converted into hyphens.', [
96        '%node-add' => t('Add content'),
97      ]),
98    ];
99
100    $form['description'] = [
101      '#title' => t('Description'),
102      '#type' => 'textarea',
103      '#default_value' => $type->getDescription(),
104      '#description' => t('This text will be displayed on the <em>Add new content</em> page.'),
105    ];
106
107    $form['additional_settings'] = [
108      '#type' => 'vertical_tabs',
109      '#attached' => [
110        'library' => ['node/drupal.content_types'],
111      ],
112    ];
113
114    $form['submission'] = [
115      '#type' => 'details',
116      '#title' => t('Submission form settings'),
117      '#group' => 'additional_settings',
118      '#open' => TRUE,
119    ];
120    $form['submission']['title_label'] = [
121      '#title' => t('Title field label'),
122      '#type' => 'textfield',
123      '#default_value' => $fields['title']->getLabel(),
124      '#required' => TRUE,
125    ];
126    $form['submission']['preview_mode'] = [
127      '#type' => 'radios',
128      '#title' => t('Preview before submitting'),
129      '#default_value' => $type->getPreviewMode(),
130      '#options' => [
131        DRUPAL_DISABLED => t('Disabled'),
132        DRUPAL_OPTIONAL => t('Optional'),
133        DRUPAL_REQUIRED => t('Required'),
134      ],
135    ];
136    $form['submission']['help'] = [
137      '#type' => 'textarea',
138      '#title' => t('Explanation or submission guidelines'),
139      '#default_value' => $type->getHelp(),
140      '#description' => t('This text will be displayed at the top of the page when creating or editing content of this type.'),
141    ];
142    $form['workflow'] = [
143      '#type' => 'details',
144      '#title' => t('Publishing options'),
145      '#group' => 'additional_settings',
146    ];
147    $workflow_options = [
148      'status' => $node->status->value,
149      'promote' => $node->promote->value,
150      'sticky' => $node->sticky->value,
151      'revision' => $type->shouldCreateNewRevision(),
152    ];
153    // Prepare workflow options to be used for 'checkboxes' form element.
154    $keys = array_keys(array_filter($workflow_options));
155    $workflow_options = array_combine($keys, $keys);
156    $form['workflow']['options'] = [
157      '#type' => 'checkboxes',
158      '#title' => t('Default options'),
159      '#default_value' => $workflow_options,
160      '#options' => [
161        'status' => t('Published'),
162        'promote' => t('Promoted to front page'),
163        'sticky' => t('Sticky at top of lists'),
164        'revision' => t('Create new revision'),
165      ],
166      '#description' => t('Users with sufficient access rights will be able to override these options.'),
167    ];
168    if ($this->moduleHandler->moduleExists('language')) {
169      $form['language'] = [
170        '#type' => 'details',
171        '#title' => t('Language settings'),
172        '#group' => 'additional_settings',
173      ];
174
175      $language_configuration = ContentLanguageSettings::loadByEntityTypeBundle('node', $type->id());
176      $form['language']['language_configuration'] = [
177        '#type' => 'language_configuration',
178        '#entity_information' => [
179          'entity_type' => 'node',
180          'bundle' => $type->id(),
181        ],
182        '#default_value' => $language_configuration,
183      ];
184    }
185    $form['display'] = [
186      '#type' => 'details',
187      '#title' => t('Display settings'),
188      '#group' => 'additional_settings',
189    ];
190    $form['display']['display_submitted'] = [
191      '#type' => 'checkbox',
192      '#title' => t('Display author and date information'),
193      '#default_value' => $type->displaySubmitted(),
194      '#description' => t('Author username and publish date will be displayed.'),
195    ];
196
197    return $this->protectBundleIdElement($form);
198  }
199
200  /**
201   * {@inheritdoc}
202   */
203  protected function actions(array $form, FormStateInterface $form_state) {
204    $actions = parent::actions($form, $form_state);
205    $actions['submit']['#value'] = t('Save content type');
206    return $actions;
207  }
208
209  /**
210   * {@inheritdoc}
211   */
212  public function validateForm(array &$form, FormStateInterface $form_state) {
213    parent::validateForm($form, $form_state);
214
215    $id = trim($form_state->getValue('type'));
216    // '0' is invalid, since elsewhere we check it using empty().
217    if ($id == '0') {
218      $form_state->setErrorByName('type', $this->t("Invalid machine-readable name. Enter a name other than %invalid.", ['%invalid' => $id]));
219    }
220  }
221
222  /**
223   * {@inheritdoc}
224   */
225  public function save(array $form, FormStateInterface $form_state) {
226    $type = $this->entity;
227    $type->setNewRevision($form_state->getValue(['options', 'revision']));
228    $type->set('type', trim($type->id()));
229    $type->set('name', trim($type->label()));
230
231    $status = $type->save();
232
233    $t_args = ['%name' => $type->label()];
234
235    if ($status == SAVED_UPDATED) {
236      $this->messenger()->addStatus($this->t('The content type %name has been updated.', $t_args));
237    }
238    elseif ($status == SAVED_NEW) {
239      node_add_body_field($type);
240      $this->messenger()->addStatus($this->t('The content type %name has been added.', $t_args));
241      $context = array_merge($t_args, ['link' => $type->toLink($this->t('View'), 'collection')->toString()]);
242      $this->logger('node')->notice('Added content type %name.', $context);
243    }
244
245    $fields = $this->entityFieldManager->getFieldDefinitions('node', $type->id());
246    // Update title field definition.
247    $title_field = $fields['title'];
248    $title_label = $form_state->getValue('title_label');
249    if ($title_field->getLabel() != $title_label) {
250      $title_field->getConfig($type->id())->setLabel($title_label)->save();
251    }
252    // Update workflow options.
253    // @todo Make it possible to get default values without an entity.
254    //   https://www.drupal.org/node/2318187
255    $node = $this->entityTypeManager->getStorage('node')->create(['type' => $type->id()]);
256    foreach (['status', 'promote', 'sticky'] as $field_name) {
257      $value = (bool) $form_state->getValue(['options', $field_name]);
258      if ($node->$field_name->value != $value) {
259        $fields[$field_name]->getConfig($type->id())->setDefaultValue($value)->save();
260      }
261    }
262
263    $this->entityFieldManager->clearCachedFieldDefinitions();
264    $form_state->setRedirectUrl($type->toUrl('collection'));
265  }
266
267}
268