diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/AuthorFormatter.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/AuthorFormatter.php new file mode 100644 index 0000000..5b5be15 --- /dev/null +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/AuthorFormatter.php @@ -0,0 +1,47 @@ + $item) { + /** @var $referenced_user \Drupal\user\UserInterface */ + if ($referenced_user = $item->entity) { + $elements[$delta] = array( + '#theme' => 'username', + '#account' => $referenced_user, + '#link_options' => array('attributes' => array('rel' => 'author')), + ); + } + } + + return $elements; + } + +} diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php new file mode 100644 index 0000000..6861a00 --- /dev/null +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/TimestampFormatter.php @@ -0,0 +1,40 @@ + $item) { + $elements[$delta] = array('#markup' => format_date($item->value)); + } + + return $elements; + } + +} diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/BooleanItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/BooleanItem.php index d674e75..4dc42e4 100644 --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/BooleanItem.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/BooleanItem.php @@ -18,7 +18,8 @@ * id = "boolean", * label = @Translation("Boolean"), * description = @Translation("An entity field containing a boolean value."), - * no_ui = TRUE + * no_ui = TRUE, + * default_widget = "boolean", * ) */ class BooleanItem extends FieldItemBase { diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/CreatedItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/CreatedItem.php index 9b2e055..72c0ea9 100644 --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/CreatedItem.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/CreatedItem.php @@ -14,7 +14,9 @@ * id = "created", * label = @Translation("Created"), * description = @Translation("An entity field containing a UNIX timestamp of when the entity has been created."), - * no_ui = TRUE + * no_ui = TRUE, + * default_widget = "timestamp", + * default_formatter = "timestamp", * ) */ class CreatedItem extends TimestampItem { diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/TimestampItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/TimestampItem.php index a2d87a6..db15ea9 100644 --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/TimestampItem.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/TimestampItem.php @@ -18,7 +18,9 @@ * id = "timestamp", * label = @Translation("Timestamp"), * description = @Translation("An entity field containing a UNIX timestamp value."), - * no_ui = TRUE + * no_ui = TRUE, + * default_widget = "timestamp", + * default_formatter = "timestamp", * ) */ class TimestampItem extends FieldItemBase { diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/AuthorAutocompleteWidget.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/AuthorAutocompleteWidget.php new file mode 100644 index 0000000..9c79397 --- /dev/null +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/AuthorAutocompleteWidget.php @@ -0,0 +1,77 @@ +getOwner()? $entity->getOwner()->getUsername() : ''; + + $user_config = \Drupal::config('user.settings'); + $element['target_id']['#description'] = $this->t('Leave blank for %anonymous.', array('%anonymous' => $user_config->get('anonymous'))); + + $element['target_id']['#element_validate'] = array(array($this, 'elementValidate')); + + return $element; + } + + /** + * Validates an element. + * + * @todo Convert to massageFormValues() after https://drupal.org/node/2226723 lands. + */ + public function elementValidate($element, &$form_state, $form) { + $form_builder = \Drupal::formBuilder(); + $value = $element['#value']; + // The use of empty() is mandatory in the context of usernames + // as the empty string denotes the anonymous user. In case we + // are dealing with an anonymous user we set the user ID to 0. + if (empty($value)) { + $value = 0; + } + else { + $account = user_load_by_name($value); + if ($account !== FALSE) { + $value = $account->id(); + } + else { + // Edge case: a non-existing numeric username should not be treated as + // a user ID (entity reference target_id). The ValidReference constraint + // would consider this a valid user ID, therefore we need additional + // validation here. + $form_builder->setError($element, $form_state, $this->t('The username %name does not exist.', array('%name' => $value))); + $value = NULL; + } + } + $form_builder->setValue($element, $value, $form_state); + } + +} diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/BooleanWidget.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/BooleanWidget.php new file mode 100644 index 0000000..66f4d07 --- /dev/null +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/BooleanWidget.php @@ -0,0 +1,38 @@ + 'checkbox', + '#default_value' => isset($items[$delta]->value) ? $items[$delta]->value : NULL, + ); + + return $element; + } + +} diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/RouteBasedAutocompleteWidget.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/RouteBasedAutocompleteWidget.php new file mode 100644 index 0000000..40ed80b --- /dev/null +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/RouteBasedAutocompleteWidget.php @@ -0,0 +1,42 @@ + 'textfield', + '#default_value' => $items[$delta]->value, + '#autocomplete_route_name' => $this->getSetting('route_name'), + ); + + return $element; + } + +} diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/TimestampWidget.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/TimestampWidget.php new file mode 100644 index 0000000..1d27fe7 --- /dev/null +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/TimestampWidget.php @@ -0,0 +1,75 @@ +value) ? format_date($items[$delta]->value, 'custom', 'Y-m-d H:i:s O') : ''; + $element['value'] = $element + array( + '#type' => 'textfield', + '#default_value' => $default_value, + '#maxlength' => 25, + '#element_validate' => array( + array($this, 'elementValidate'), + ), + ); + + $timestamp = (int) $items[$delta]->value; + $created_timestamp = (int) $items->getEntity()->getCreatedTime(); + $element['value']['#description'] = $this->t('Format: %time. The date format is YYYY-MM-DD and %timezone is the time zone offset from UTC. Leave blank to use the time of form submission.', array('%time' => !empty($default_value) ? + date_format(date_create($default_value), 'Y-m-d H:i:s O') : format_date($created_timestamp, 'custom', 'Y-m-d H:i:s O'), '%timezone' => !empty($default_value) ? date_format(date_create($default_value), 'O') : format_date($created_timestamp, 'custom', 'O'))); + + return $element; + } + + /** + * Validates an element. + * + * @todo Convert to massageFormValues() after https://drupal.org/node/2226723 lands. + */ + public function elementValidate($element, &$form_state, $form) { + $value = trim($element['#value']); + if (empty($value)) { + $value = $this->getSetting('use_request_time_on_empty') ? REQUEST_TIME : 0; + } + else { + $date = new DrupalDateTime($value); + if ($date->hasErrors()) { + $value = FALSE; + } + else { + $value = $date->getTimestamp(); + } + } + form_set_value($element, $value, $form_state); + } + +} diff --git a/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationController.php b/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationController.php index f691c97..bfb06fc 100644 --- a/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationController.php +++ b/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationController.php @@ -414,7 +414,7 @@ function entityFormValidate($form, &$form_state) { if (!empty($translation['name']) && !($account = user_load_by_name($translation['name']))) { form_set_error('content_translation][name', $form_state, t('The translation authoring username %name does not exist.', array('%name' => $translation['name']))); } - // Validate the "authored on" field. + // Validate the "created on" field. if (!empty($translation['created']) && strtotime($translation['created']) === FALSE) { form_set_error('content_translation][created', $form_state, t('You have to specify a valid translation authoring date.')); } diff --git a/core/modules/datetime/datetime.module b/core/modules/datetime/datetime.module index 3a016b5..9980153 100644 --- a/core/modules/datetime/datetime.module +++ b/core/modules/datetime/datetime.module @@ -986,23 +986,13 @@ function datetime_range_years($string, $date = NULL) { } /** - * Implements hook_form_BASE_FORM_ID_alter() for node forms. + * Implements hook_entity_base_field_info_alter(). */ -function datetime_form_node_form_alter(&$form, &$form_state, $form_id) { - $format_type = datetime_default_format_type(); - - // Alter the 'Authored on' date to use datetime. - $form['created']['#type'] = 'datetime'; - $date_format = entity_load('date_format', 'html_date')->getPattern($format_type); - $time_format = entity_load('date_format', 'html_time')->getPattern($format_type); - $form['created']['#description'] = t('Format: %format. Leave blank to use the time of form submission.', array('%format' => datetime_format_example($date_format . ' ' . $time_format))); - unset($form['created']['#maxlength']); -} - -/** - * Implements hook_node_prepare_form(). - */ -function datetime_node_prepare_form(NodeInterface $node, $operation, array &$form_state) { - // Prepare the 'Authored on' date to use datetime. - $node->date = DrupalDateTime::createFromTimestamp($node->getCreatedTime()); +function datetime_entity_base_field_info_alter(&$fields, \Drupal\Core\Entity\EntityTypeInterface $entity_type) { + if ($entity_type->id() == 'node') { + $fields['created']->setDisplayOptions('form', array( + 'type' => 'datetime_timestamp', + 'weight' => 0, + )); + } } diff --git a/core/modules/datetime/lib/Drupal/datetime/Plugin/Field/FieldWidget/DateTimeTimestampWidget.php b/core/modules/datetime/lib/Drupal/datetime/Plugin/Field/FieldWidget/DateTimeTimestampWidget.php new file mode 100644 index 0000000..395dd81 --- /dev/null +++ b/core/modules/datetime/lib/Drupal/datetime/Plugin/Field/FieldWidget/DateTimeTimestampWidget.php @@ -0,0 +1,63 @@ +getPattern($format_type); + $time_format = entity_load('date_format', 'html_time')->getPattern($format_type); + $default_value = isset($items[$delta]->value) ? DrupalDateTime::createFromTimestamp($items[$delta]->value) : ''; + $element['value'] = $element + array( + '#type' => 'datetime', + '#default_value' => $default_value, + '#element_validate' => array( + array($this, 'elementValidate'), + ), + ); + $element['value']['#description'] = $this->t('Format: %format. Leave blank to use the time of form submission.', array('%format' => datetime_format_example($date_format . ' ' . $time_format))); + + return $element; + } + + /** + * Validates an element. + * + * @todo Convert to massageFormValues() after https://drupal.org/node/2226723 lands. + */ + public function elementValidate($element, &$form_state, $form) { + $date = $element['#value']['object']; + if ($date->hasErrors()) { + $value = -1; + } + else { + $value = $date->getTimestamp(); + } + form_set_value($element, $value, $form_state); + } + +} diff --git a/core/modules/forum/lib/Drupal/forum/Tests/ForumBlockTest.php b/core/modules/forum/lib/Drupal/forum/Tests/ForumBlockTest.php index 3799288..a1fbfe3 100644 --- a/core/modules/forum/lib/Drupal/forum/Tests/ForumBlockTest.php +++ b/core/modules/forum/lib/Drupal/forum/Tests/ForumBlockTest.php @@ -174,8 +174,8 @@ protected function createForumTopics($count = 5) { 'body[0][value]' => $body, // Forum posts are ordered by timestamp, so force a unique timestamp by // adding the index. - 'created[date]' => $date->format('Y-m-d'), - 'created[time]' => $date->format('H:i:s'), + 'created[0][value][date]' => $date->format('Y-m-d'), + 'created[0][value][time]' => $date->format('H:i:s'), ); // Create the forum topic, preselecting the forum ID via a URL parameter. diff --git a/core/modules/node/lib/Drupal/node/Entity/Node.php b/core/modules/node/lib/Drupal/node/Entity/Node.php index 0e1d20f..cddf69f 100644 --- a/core/modules/node/lib/Drupal/node/Entity/Node.php +++ b/core/modules/node/lib/Drupal/node/Entity/Node.php @@ -383,13 +383,25 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { $fields['uid'] = FieldDefinition::create('entity_reference') ->setLabel(t('Author')) - ->setDescription(t('The user that is the node author.')) + ->setDescription(t('The user ID of the node author.')) ->setRevisionable(TRUE) + ->setTranslatable(TRUE) ->setSettings(array( 'target_type' => 'user', 'default_value' => 0, )) - ->setTranslatable(TRUE); + ->setDisplayOptions('view', array( + 'label' => 'hidden', + 'type' => 'author', + 'weight' => 0, + )) + ->setDisplayOptions('form', array( + 'type' => 'author_autocomplete', + 'weight' => -1, + 'settings' => array( + 'route_name' => 'user.autocomplete', + ), + )); $fields['status'] = FieldDefinition::create('boolean') ->setLabel(t('Publishing status')) @@ -398,10 +410,23 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ->setTranslatable(TRUE); $fields['created'] = FieldDefinition::create('created') - ->setLabel(t('Created')) + ->setLabel(t('Publication date')) ->setDescription(t('The time that the node was created.')) ->setRevisionable(TRUE) - ->setTranslatable(TRUE); + ->setTranslatable(TRUE) + ->setDisplayOptions('view', array( + 'label' => 'hidden', + 'type' => 'timestamp', + 'weight' => 0, + )) + ->setDisplayOptions('form', array( + 'type' => 'timestamp', + 'weight' => 0, + 'settings' => array( + 'use_request_time_on_empty' => TRUE, + ), + )) + ->setDisplayConfigurable('form', TRUE); $fields['changed'] = FieldDefinition::create('changed') ->setLabel(t('Changed')) @@ -410,16 +435,22 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ->setTranslatable(TRUE); $fields['promote'] = FieldDefinition::create('boolean') - ->setLabel(t('Promote')) - ->setDescription(t('A boolean indicating whether the node should be displayed on the front page.')) + ->setLabel(t('Promoted to front page')) ->setRevisionable(TRUE) - ->setTranslatable(TRUE); + ->setTranslatable(TRUE) + ->setDisplayOptions('form', array( + 'type' => 'boolean', + 'weight' => -1, + )); $fields['sticky'] = FieldDefinition::create('boolean') - ->setLabel(t('Sticky')) - ->setDescription(t('A boolean indicating whether the node should be displayed at the top of lists in which it appears.')) + ->setLabel(t('Sticky at top of lists')) ->setRevisionable(TRUE) - ->setTranslatable(TRUE); + ->setTranslatable(TRUE) + ->setDisplayOptions('form', array( + 'type' => 'boolean', + 'weight' => 0, + )); $fields['revision_timestamp'] = FieldDefinition::create('timestamp') ->setLabel(t('Revision timestamp')) @@ -434,11 +465,18 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { ->setQueryable(FALSE) ->setRevisionable(TRUE); - $fields['log'] = FieldDefinition::create('string') - ->setLabel(t('Log')) - ->setDescription(t('The log entry explaining the changes in this revision.')) + $fields['log'] = FieldDefinition::create('string_long') + ->setLabel(t('Revision log message')) + ->setDescription(t('Briefly describe the changes you have made.')) ->setRevisionable(TRUE) - ->setTranslatable(TRUE); + ->setTranslatable(TRUE) + ->setDisplayOptions('form', array( + 'type' => 'text_textarea', + 'weight' => 0, + 'settings' => array( + 'rows' => 4, + ), + )); return $fields; } diff --git a/core/modules/node/lib/Drupal/node/NodeFormController.php b/core/modules/node/lib/Drupal/node/NodeFormController.php index 672c8ef..412ef5a 100644 --- a/core/modules/node/lib/Drupal/node/NodeFormController.php +++ b/core/modules/node/lib/Drupal/node/NodeFormController.php @@ -7,12 +7,9 @@ namespace Drupal\node; -use Drupal\Component\Utility\NestedArray; use Drupal\Core\Cache\Cache; -use Drupal\Core\Datetime\DrupalDateTime; use Drupal\Core\Entity\ContentEntityFormController; use Drupal\Core\Language\Language; -use Drupal\Component\Utility\String; /** * Form controller for the node edit forms. @@ -46,7 +43,6 @@ protected function prepareEntity() { } } else { - $node->date = format_date($node->getCreatedTime(), 'custom', 'Y-m-d H:i:s O'); // Remove the log message from the original node entity. $node->log = NULL; } @@ -61,6 +57,8 @@ public function form(array $form, array &$form_state) { /** @var \Drupal\node\NodeInterface $node */ $node = $this->entity; + $form = parent::form($form, $form_state, $node); + if ($this->operation == 'edit') { $form['#title'] = $this->t('Edit @type @title', array('@type' => node_get_type_label($node), '@title' => $node->label())); } @@ -94,7 +92,8 @@ public function form(array $form, array &$form_state) { '#default_value' => $node->getUntranslated()->language()->id, '#languages' => Language::STATE_ALL, '#access' => isset($language_configuration['language_show']) && $language_configuration['language_show'], - ); + ); + $form['advanced'] = array( '#type' => 'vertical_tabs', @@ -126,14 +125,12 @@ public function form(array $form, array &$form_state) { '#default_value' => $node->isNewRevision(), '#access' => $node->isNewRevision() || user_access('administer nodes'), '#group' => 'revision_information', + // Ensure the 'Create new revision' checkbox sits above the revision log + // message. + '#weight' => -1, ); - $form['log'] = array( - '#type' => 'textarea', - '#title' => t('Revision log message'), - '#rows' => 4, - '#default_value' => !empty($node->log->value) ? $node->log->value : '', - '#description' => t('Briefly describe the changes you have made.'), + $form['log'] += array( '#states' => array( 'visible' => array( ':input[name="revision"]' => array('checked' => TRUE), @@ -164,26 +161,17 @@ public function form(array $form, array &$form_state) { '#optional' => TRUE, ); - $form['uid'] = array( - '#type' => 'textfield', - '#title' => t('Authored by'), - '#maxlength' => 60, - '#autocomplete_route_name' => 'user.autocomplete', - '#default_value' => $node->getOwnerId()? $node->getOwner()->getUsername() : '', - '#weight' => -1, - '#description' => t('Leave blank for %anonymous.', array('%anonymous' => $user_config->get('anonymous'))), + $form['uid'] += array( '#group' => 'author', '#access' => user_access('administer nodes'), ); - $form['created'] = array( - '#type' => 'textfield', - '#title' => t('Authored on'), - '#maxlength' => 25, - '#description' => t('Format: %time. The date format is YYYY-MM-DD and %timezone is the time zone offset from UTC. Leave blank to use the time of form submission.', array('%time' => !empty($node->date) ? date_format(date_create($node->date), 'Y-m-d H:i:s O') : format_date($node->getCreatedTime(), 'custom', 'Y-m-d H:i:s O'), '%timezone' => !empty($node->date) ? date_format(date_create($node->date), 'O') : format_date($node->getCreatedTime(), 'custom', 'O'))), - '#default_value' => !empty($node->date) ? $node->date : '', + $form['uid']['widget'][0]['value']['#title'] = $this->t('Authored by'); + + $form['created'] += array( '#group' => 'author', '#access' => user_access('administer nodes'), ); + $form['created']['widget'][0]['value']['#title'] = $this->t('Authored on'); // Node options for administrators. $form['options'] = array( @@ -200,23 +188,17 @@ public function form(array $form, array &$form_state) { '#optional' => TRUE, ); - $form['promote'] = array( - '#type' => 'checkbox', - '#title' => t('Promoted to front page'), - '#default_value' => $node->isPromoted(), + $form['promote'] += array( '#group' => 'options', '#access' => user_access('administer nodes'), ); - $form['sticky'] = array( - '#type' => 'checkbox', - '#title' => t('Sticky at top of lists'), - '#default_value' => $node->isSticky(), + $form['sticky'] += array( '#group' => 'options', '#access' => user_access('administer nodes'), ); - return parent::form($form, $form_state, $node); + return $form; } /** @@ -309,21 +291,6 @@ public function validate(array $form, array &$form_state) { $this->setFormError('changed', $form_state, $this->t('The content on this page has either been modified by another user, or you have already submitted modifications using this form. As a result, your changes cannot be saved.')); } - // Validate the "authored by" field. - if (!empty($form_state['values']['uid']) && !($account = user_load_by_name($form_state['values']['uid']))) { - // The use of empty() is mandatory in the context of usernames - // as the empty string denotes the anonymous user. In case we - // are dealing with an anonymous user we set the user ID to 0. - $this->setFormError('uid', $form_state, $this->t('The username %name does not exist.', array('%name' => $form_state['values']['uid']))); - } - - // Validate the "authored on" field. - // The date element contains the date object. - $date = $node->date instanceof DrupalDateTime ? $node->date : new DrupalDateTime($node->date); - if ($date->hasErrors()) { - $this->setFormError('date', $form_state, $this->t('You have to specify a valid date.')); - } - // Invoke hook_node_validate() for validation needed by modules. // Can't use \Drupal::moduleHandler()->invokeAll(), because $form_state must // be receivable by reference. @@ -413,31 +380,6 @@ public function unpublish(array $form, array &$form_state) { } /** - * {@inheritdoc} - */ - public function buildEntity(array $form, array &$form_state) { - /** @var \Drupal\node\NodeInterface $entity */ - $entity = parent::buildEntity($form, $form_state); - // A user might assign the node author by entering a user name in the node - // form, which we then need to translate to a user ID. - if (!empty($form_state['values']['uid']) && $account = user_load_by_name($form_state['values']['uid'])) { - $entity->setOwnerId($account->id()); - } - else { - $entity->setOwnerId(0); - } - - if (!empty($form_state['values']['created']) && $form_state['values']['created'] instanceOf DrupalDateTime) { - $entity->setCreatedTime($form_state['values']['created']->getTimestamp()); - } - else { - $entity->setCreatedTime(REQUEST_TIME); - } - return $entity; - } - - - /** * Overrides Drupal\Core\Entity\EntityFormController::save(). */ public function save(array $form, array &$form_state) { diff --git a/core/modules/node/lib/Drupal/node/NodeTranslationController.php b/core/modules/node/lib/Drupal/node/NodeTranslationController.php index bb4d094..48f8711 100644 --- a/core/modules/node/lib/Drupal/node/NodeTranslationController.php +++ b/core/modules/node/lib/Drupal/node/NodeTranslationController.php @@ -80,8 +80,9 @@ public function entityFormEntityBuild($entity_type, EntityInterface $entity, arr $translation['status'] = $form_controller->getEntity()->isPublished(); // $form['content_translation']['name'] is the equivalent field // for translation author uid. - $translation['name'] = $form_state['values']['uid']; - $translation['created'] = $form_state['values']['created']; + $account = user_load($form_state['values']['uid'][0]['target_id']); + $translation['name'] = $account ? $account->getUsername() : ''; + $translation['created'] = format_date($form_state['values']['created'][0]['value'], 'custom', 'Y-m-d H:i:s O'); } parent::entityFormEntityBuild($entity_type, $entity, $form, $form_state); } diff --git a/core/modules/node/lib/Drupal/node/NodeViewBuilder.php b/core/modules/node/lib/Drupal/node/NodeViewBuilder.php index 34ca5de..2e2b279 100644 --- a/core/modules/node/lib/Drupal/node/NodeViewBuilder.php +++ b/core/modules/node/lib/Drupal/node/NodeViewBuilder.php @@ -160,10 +160,6 @@ protected function alterBuild(array &$build, EntityInterface $entity, EntityView 'metadata' => array('changed' => $entity->getChangedTime()), ); } - - // The node 'submitted' info is not rendered in a standard way (renderable - // array) so we have to add a cache tag manually. - $build['#cache']['tags']['user'][] = $entity->getOwnerId(); } } diff --git a/core/modules/node/lib/Drupal/node/Tests/MultiStepNodeFormBasicOptionsTest.php b/core/modules/node/lib/Drupal/node/Tests/MultiStepNodeFormBasicOptionsTest.php index 67bdbe3..5620a8f 100644 --- a/core/modules/node/lib/Drupal/node/Tests/MultiStepNodeFormBasicOptionsTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/MultiStepNodeFormBasicOptionsTest.php @@ -62,13 +62,13 @@ function testMultiStepNodeFormBasicOptions() { $edit = array( 'title[0][value]' => 'a', - 'promote' => FALSE, - 'sticky' => 1, + 'promote[0][value]' => FALSE, + 'sticky[0][value]' => 1, "{$this->field_name}[0][value]" => $this->randomString(32), ); $this->drupalPostForm('node/add/page', $edit, t('Add another item')); - $this->assertNoFieldChecked('edit-promote', 'promote stayed unchecked'); - $this->assertFieldChecked('edit-sticky', 'sticky stayed checked'); + $this->assertNoFieldChecked('edit-promote-0-value', 'promote stayed unchecked'); + $this->assertFieldChecked('edit-sticky-0-value', 'sticky stayed checked'); } } diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeCacheTagsTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeCacheTagsTest.php index 0d3dc2a..ca5711e 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeCacheTagsTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeCacheTagsTest.php @@ -63,7 +63,7 @@ protected function createEntity() { * {@inheritdoc} */ protected function getAdditionalCacheTagsForEntity(EntityInterface $node) { - return array('user:' . $node->getOwnerId()); + return array('user:' . $node->getOwnerId(), 'user_view:' . TRUE); } } diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeCreationTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeCreationTest.php index c5b4514..8be476c 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeCreationTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeCreationTest.php @@ -149,7 +149,7 @@ public function testAuthorAutocomplete() { $this->drupalGet('node/add/page'); - $result = $this->xpath('//input[@id="edit-uid" and contains(@data-autocomplete-path, "user/autocomplete")]'); + $result = $this->xpath('//input[@id="edit-uid-0-value" and contains(@data-autocomplete-path, "user/autocomplete")]'); $this->assertEqual(count($result), 0, 'No autocompletion without access user profiles.'); $admin_user = $this->drupalCreateUser(array('administer nodes', 'create page content', 'access user profiles')); @@ -157,7 +157,7 @@ public function testAuthorAutocomplete() { $this->drupalGet('node/add/page'); - $result = $this->xpath('//input[@id="edit-uid" and contains(@data-autocomplete-path, "user/autocomplete")]'); + $result = $this->xpath('//input[@id="edit-uid-0-target-id" and contains(@data-autocomplete-path, "user/autocomplete")]'); $this->assertEqual(count($result), 1, 'Ensure that the user does have access to the autocompletion'); } diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php b/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php index 4012908..dbe8ac5 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php @@ -158,11 +158,11 @@ protected function doTestAuthoringInfo() { 'promote' => (bool) mt_rand(0, 1), ); $edit = array( - 'uid' => $user->getUsername(), - 'created[date]' => format_date($values[$langcode]['created'], 'custom', 'Y-m-d'), - 'created[time]' => format_date($values[$langcode]['created'], 'custom', 'H:i:s'), - 'sticky' => $values[$langcode]['sticky'], - 'promote' => $values[$langcode]['promote'], + 'uid[0][target_id]' => $user->getUsername(), + 'created[0][value][date]' => format_date($values[$langcode]['created'], 'custom', 'Y-m-d'), + 'created[0][value][time]' => format_date($values[$langcode]['created'], 'custom', 'H:i:s'), + 'sticky[0][value]' => $values[$langcode]['sticky'], + 'promote[0][value]' => $values[$langcode]['promote'], ); $this->drupalPostForm($path, $edit, $this->getFormSubmitAction($entity, $langcode), array('language' => $languages[$langcode])); } @@ -170,7 +170,7 @@ protected function doTestAuthoringInfo() { $entity = entity_load($this->entityTypeId, $this->entityId, TRUE); foreach ($this->langcodes as $langcode) { $this->assertEqual($entity->translation[$langcode]['uid'], $values[$langcode]['uid'], 'Translation author correctly stored.'); - $this->assertEqual($entity->translation[$langcode]['created'], $values[$langcode]['created'], 'Translation date correctly stored.'); + $this->assertEqual($entity->translation[$langcode]['created'], $values[$langcode]['created'][0]['value'], 'Translation date correctly stored.'); $translation = $entity->getTranslation($langcode); $this->assertEqual($translation->getOwnerId(), $values[$langcode]['uid'], 'Author of translation correctly stored.'); $this->assertEqual($translation->getCreatedTime(), $values[$langcode]['created'], 'Date of Translation correctly stored.'); diff --git a/core/modules/node/lib/Drupal/node/Tests/PageEditTest.php b/core/modules/node/lib/Drupal/node/Tests/PageEditTest.php index 2361d13..ff97e9a 100644 --- a/core/modules/node/lib/Drupal/node/Tests/PageEditTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/PageEditTest.php @@ -114,21 +114,21 @@ function testPageAuthoredBy() { // Try to change the 'authored by' field to an invalid user name. $edit = array( - 'uid' => 'invalid-name', + 'uid[0][target_id]' => 'invalid-name', ); $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save and keep published')); - $this->assertText('The username invalid-name does not exist.'); + $this->assertRaw(format_string('The username %name does not exist.', array('%name' => 'invalid-name'))); // Change the authored by field to an empty string, which should assign // authorship to the anonymous user (uid 0). - $edit['uid'] = ''; + $edit['uid[0][target_id]'] = ''; $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save and keep published')); $node = node_load($node->id(), TRUE); $this->assertIdentical($node->getOwnerId(), '0', 'Node authored by anonymous user.'); // Change the authored by field to another user's name (that is not // logged in). - $edit['uid'] = $this->web_user->getUsername(); + $edit['uid[0][target_id]'] = $this->web_user->getUsername(); $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save and keep published')); $node = node_load($node->id(), TRUE); $this->assertIdentical($node->getOwnerId(), $this->web_user->id(), 'Node authored by normal user.'); @@ -136,6 +136,6 @@ function testPageAuthoredBy() { // Check that normal users cannot change the authored by information. $this->drupalLogin($this->web_user); $this->drupalGet('node/' . $node->id() . '/edit'); - $this->assertNoFieldByName('uid'); + $this->assertNoFieldByName('uid[0][target_id]'); } } diff --git a/core/modules/node/lib/Drupal/node/Tests/PagePreviewTest.php b/core/modules/node/lib/Drupal/node/Tests/PagePreviewTest.php index 48a8ea6..4a6cebe 100644 --- a/core/modules/node/lib/Drupal/node/Tests/PagePreviewTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/PagePreviewTest.php @@ -192,7 +192,7 @@ function testPagePreviewWithRevisions() { $edit[$title_key] = $this->randomName(8); $edit[$body_key] = $this->randomName(16); $edit[$term_key] = $this->term->id(); - $edit['log'] = $this->randomName(32); + $edit['log[0][value]'] = $this->randomName(32); $this->drupalPostForm('node/add/page', $edit, t('Preview')); // Check that the preview is displaying the title, body and term. @@ -207,7 +207,7 @@ function testPagePreviewWithRevisions() { $this->assertFieldByName($term_key, $edit[$term_key], 'Term field displayed.'); // Check that the log field has the correct value. - $this->assertFieldByName('log', $edit['log'], 'Log field displayed.'); + $this->assertFieldByName('log[0][value]', $edit['log[0][value]'], 'Log field displayed.'); } } diff --git a/core/modules/node/node.module b/core/modules/node/node.module index b05fc2c..7cbdd60 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -175,6 +175,12 @@ function node_theme() { 'field__node__title' => array( 'base hook' => 'field', ), + 'field__node__uid' => array( + 'base hook' => 'field', + ), + 'field__node__created' => array( + 'base hook' => 'field', + ), ); } @@ -209,20 +215,6 @@ function node_entity_view_display_alter(EntityViewDisplayInterface $display, $co } /** - * Implements hook_entity_form_display_alter(). - */ -function node_entity_form_display_alter(EntityFormDisplayInterface $form_display, $context) { - if ($context['entity_type'] == 'node') { - $node_type = node_type_load($context['bundle']); - // @todo Reconsider when per-bundle overrides of field definitions are - // possible - https://drupal.org/node/2114707. - if (!$node_type->has_title) { - $form_display->removeComponent('title'); - } - } -} - -/** * Entity URI callback. * * @param \Drupal\node\NodeInterface $node @@ -636,14 +628,9 @@ function template_preprocess_node(&$variables) { $variables['node'] = $variables['elements']['#node']; /** @var \Drupal\node\NodeInterface $node */ $node = $variables['node']; - - $variables['date'] = format_date($node->getCreatedTime()); - $username = array( - '#theme' => 'username', - '#account' => $node->getOwner(), - '#link_options' => array('attributes' => array('rel' => 'author')), - ); - $variables['name'] = drupal_render($username); + $variables['date'] = drupal_render($variables['elements']['created'], TRUE); + unset($variables['elements']['created']); + $variables['name'] = drupal_render($variables['elements']['uid'], TRUE); $variables['node_url'] = $node->url('canonical', array( 'language' => $node->language(), @@ -705,9 +692,9 @@ function template_preprocess_node(&$variables) { } /** - * Returns HTML for the node title field. + * Returns HTML for the node 'title' field. * - * This is an override of theme_field() for the node title field. See that + * This is an override of theme_field() for the node 'title' field. See that * function for documentation about its details and overrides. * * @param array $variables @@ -722,6 +709,40 @@ function theme_field__node__title($variables) { } /** + * Returns HTML for the node 'uid' field. + * + * This is an override of theme_field() for the node 'uid' field. See that + * function for documentation about its details and overrides. + * + * @param array $variables + * An associative array. See theme_field() for details. + * + * @see theme_field() + * + * @ingroup themeable + */ +function theme_field__node__uid($variables) { + return '' . drupal_render($variables['items']) . ''; +} + +/** + * Returns HTML for the node 'created' field. + * + * This is an override of theme_field() for the node 'created' field. See that + * function for documentation about its details and overrides. + * + * @param array $variables + * An associative array. See theme_field() for details. + * + * @see theme_field() + * + * @ingroup themeable + */ +function theme_field__node__created($variables) { + return '' . drupal_render($variables['items']) . ''; +} + +/** * Implements hook_permission(). */ function node_permission() { diff --git a/core/modules/node/templates/node.html.twig b/core/modules/node/templates/node.html.twig index 5bf5ad9..720fae5 100644 --- a/core/modules/node/templates/node.html.twig +++ b/core/modules/node/templates/node.html.twig @@ -21,10 +21,8 @@ * {{ content|without('field_example') %} to temporarily suppress the printing * of a given child element. * - user_picture: The node author's picture from user-picture.html.twig. - * - date: Formatted creation date. Preprocess functions can reformat it by - * calling format_date() with the desired parameters on - * $variables['created']. - * - name: Themed username of node author output from theme_username(). + * - date: Themed creation date field. + * - name: Themed author date field. * - node_url: Direct URL of the current node. * - display_submitted: Whether submission information should be displayed. * - submitted: Submission information created from name and date during diff --git a/core/modules/system/lib/Drupal/system/Tests/Cache/PageCacheTagsIntegrationTest.php b/core/modules/system/lib/Drupal/system/Tests/Cache/PageCacheTagsIntegrationTest.php index 7e9d1d5..72529d5 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Cache/PageCacheTagsIntegrationTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Cache/PageCacheTagsIntegrationTest.php @@ -91,6 +91,7 @@ function testPageCacheTags() { 'node_view:1', 'node:' . $node_1->id(), 'user:' . $author_1->id(), + 'user_view:' . TRUE, 'filter_format:basic_html', 'menu:tools', 'menu:footer', @@ -116,6 +117,7 @@ function testPageCacheTags() { 'node_view:1', 'node:' . $node_2->id(), 'user:' . $author_2->id(), + 'user_view:' . TRUE, 'filter_format:full_html', 'menu:tools', 'menu:footer', diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/LegacyTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/LegacyTest.php index cddfefd..b45c929 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/LegacyTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/LegacyTest.php @@ -38,8 +38,8 @@ function testTaxonomyLegacyNode() { $date = new DrupalDateTime('1969-01-01 00:00:00'); $edit = array(); $edit['title[0][value]'] = $this->randomName(); - $edit['created[date]'] = $date->format('Y-m-d'); - $edit['created[time]'] = $date->format('H:i:s'); + $edit['created[0][value][date]'] = $date->format('Y-m-d'); + $edit['created[0][value][time]'] = $date->format('H:i:s'); $edit['body[0][value]'] = $this->randomName(); $edit['field_tags'] = $this->randomName(); $this->drupalPostForm('node/add/article', $edit, t('Save and publish')); diff --git a/core/modules/text/lib/Drupal/text/Plugin/Field/FieldWidget/TextareaWidget.php b/core/modules/text/lib/Drupal/text/Plugin/Field/FieldWidget/TextareaWidget.php index 3c57008..8474b2b 100644 --- a/core/modules/text/lib/Drupal/text/Plugin/Field/FieldWidget/TextareaWidget.php +++ b/core/modules/text/lib/Drupal/text/Plugin/Field/FieldWidget/TextareaWidget.php @@ -18,7 +18,12 @@ * id = "text_textarea", * label = @Translation("Text area (multiple rows)"), * field_types = { - * "text_long" + * "text_long", + * "string_long", + * }, + * settings = { + * "rows" = "5", + * "placeholder" = "" * } * ) */ diff --git a/core/themes/bartik/templates/node.html.twig b/core/themes/bartik/templates/node.html.twig index 6afb107..234462c 100644 --- a/core/themes/bartik/templates/node.html.twig +++ b/core/themes/bartik/templates/node.html.twig @@ -21,10 +21,8 @@ * {{ content|without('field_example') }} to exclude the printing of a * given child element. * - user_picture: The node author's picture from user-picture.html.twig. - * - date: Formatted creation date. Preprocess functions can reformat it by - * calling format_date() with the desired parameters on - * $variables['created']. - * - name: Themed username of node author output from theme_username(). + * - date: Themed creation date field. + * - name: Themed author date field. * - node_url: Direct URL of the current node. * - display_submitted: Whether submission information should be displayed. * - submitted: Submission information created from name and date during