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