Skip to content
Snippets Groups Projects
Commit a6c2e2b1 authored by catch's avatar catch
Browse files

Issue #2506213 by hchonov, mkalkbrenner: Update content entity changed timestamp on UI save

parent 3123ff0a
No related branches found
No related tags found
2 merge requests!7452Issue #1797438. HTML5 validation is preventing form submit and not fully...,!789Issue #3210310: Adjust Database API to remove deprecated Drupal 9 code in Drupal 10
Showing
with 192 additions and 67 deletions
......@@ -62,6 +62,15 @@ public function form(array $form, FormStateInterface $form_state) {
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
parent::submitForm($form, $form_state);
// Update the changed timestamp of the entity.
$this->updateChangedTime($this->entity);
}
/**
* {@inheritdoc}
*/
......@@ -269,4 +278,18 @@ public function updateFormLangcode($entity_type_id, EntityInterface $entity, arr
}
}
/**
* Updates the changed time of the entity.
*
* Applies only if the entity implements the EntityChangedInterface.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity updated with the submitted values.
*/
public function updateChangedTime(EntityInterface $entity) {
if ($entity->getEntityType()->isSubclassOf(EntityChangedInterface::class)) {
$entity->setChangedTime(REQUEST_TIME);
}
}
}
......@@ -29,6 +29,16 @@ interface EntityChangedInterface {
*/
public function getChangedTime();
/**
* Sets the timestamp of the last entity change for the current translation.
*
* @param int $timestamp
* The timestamp of the last entity save operation.
*
* @return $this
*/
public function setChangedTime($timestamp);
/**
* Gets the timestamp of the last entity change across all translations.
*
......
......@@ -28,4 +28,26 @@ public function getChangedTimeAcrossTranslations() {
return $changed;
}
/**
* Gets the timestamp of the last entity change for the current translation.
*
* @return int
* The timestamp of the last entity save operation.
*/
public function getChangedTime() {
return $this->get('changed')->value;
}
/**
* Sets the timestamp of the last entity change for the current translation.
*
* @param int $timestamp
* The timestamp of the last entity save operation.
*
* @return $this
*/
public function setChangedTime($timestamp) {
$this->set('changed', $timestamp);
return $this;
}
}
......@@ -25,7 +25,7 @@ public function validate($entity, Constraint $constraint) {
$saved_entity = \Drupal::entityManager()->getStorage($entity->getEntityTypeId())->loadUnchanged($entity->id());
// A change to any other translation must add a violation to the current
// translation because there might be untranslatable shared fields.
if ($saved_entity && $saved_entity->getChangedTimeAcrossTranslations() > $entity->getChangedTime()) {
if ($saved_entity && $saved_entity->getChangedTimeAcrossTranslations() > $entity->getChangedTimeAcrossTranslations()) {
$this->context->addViolation($constraint->message);
}
}
......
......@@ -219,13 +219,6 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
return $fields;
}
/**
* {@inheritdoc}
*/
public function getChangedTime() {
return $this->get('changed')->value;
}
/**
* {@inheritdoc}
*/
......
......@@ -527,13 +527,6 @@ public function setThread($thread) {
return $this;
}
/**
* {@inheritdoc}
*/
public function getChangedTime() {
return $this->get('changed')->value;
}
/**
* {@inheritdoc}
*/
......
......@@ -479,6 +479,13 @@ public function entityFormAlter(array &$form, FormStateInterface $form_state, En
if (isset($form['actions']['delete'])) {
$form['actions']['delete']['#submit'][] = array($this, 'entityFormDelete');
}
// Handle entity form submission before the entity has been saved.
foreach (Element::children($form['actions']) as $action) {
if (isset($form['actions'][$action]['#type']) && $form['actions'][$action]['#type'] == 'submit') {
array_unshift($form['actions'][$action]['#submit'], [$this, 'entityFormSubmit']);
}
}
}
/**
......@@ -579,7 +586,6 @@ public function entityFormEntityBuild($entity_type, EntityInterface $entity, arr
$metadata->setAuthor(!empty($values['uid']) ? User::load($values['uid']) : User::load(0));
$metadata->setPublished(!empty($values['status']));
$metadata->setCreatedTime(!empty($values['created']) ? strtotime($values['created']) : REQUEST_TIME);
$metadata->setChangedTime(REQUEST_TIME);
$source_langcode = $this->getSourceLangcode($form_state);
if ($source_langcode) {
......@@ -611,6 +617,30 @@ function entityFormValidate($form, FormStateInterface $form_state) {
}
}
/**
* Form submission handler for ContentTranslationHandler::entityFormAlter().
*
* Updates metadata fields, which should be updated only after the validation
* has run and before the entity is saved.
*/
function entityFormSubmit($form, FormStateInterface $form_state) {
/** @var \Drupal\Core\Entity\ContentEntityFormInterface $form_object */
$form_object = $form_state->getFormObject();
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
$entity = $form_object->getEntity();
// ContentEntityForm::submit will update the changed timestamp on submit
// after the entity has been validated, so that it does not break the
// EntityChanged constraint validator. The content translation metadata
// field for the changed timestamp does not have such a constraint defined
// at the moment, but it is correct to update it's value in a submission
// handler as well and have the same logic like in the Form API.
if ($entity->hasField('content_translation_changed')) {
$metadata = $this->manager->getTranslationMetadata($entity);
$metadata->setChangedTime(REQUEST_TIME);
}
}
/**
* Form submission handler for ContentTranslationHandler::entityFormAlter().
*
......
......@@ -65,6 +65,7 @@ function testTranslationUI() {
$this->doTestAuthoringInfo();
$this->doTestTranslationEdit();
$this->doTestTranslationChanged();
$this->doTestChangedTimeAfterSaveWithoutChanges();
$this->doTestTranslationDeletion();
}
......@@ -546,4 +547,30 @@ protected function doTestTranslationChanged() {
}
}
/**
* Test the changed time after API and FORM save without changes.
*/
public function doTestChangedTimeAfterSaveWithoutChanges() {
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
// Test only entities, which implement the EntityChangedInterface.
if ($entity->getEntityType()->isSubclassOf('Drupal\Core\Entity\EntityChangedInterface')) {
$changed_timestamp = $entity->getChangedTime();
$entity->save();
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
$this->assertEqual($changed_timestamp, $entity->getChangedTime(), 'The entity\'s changed time wasn\'t updated after API save without changes.');
// Ensure different save timestamps.
sleep(1);
// Save the entity on the regular edit form.
$language = $entity->language();
$edit_path = $entity->urlInfo('edit-form', array('language' => $language));
$this->drupalPostForm($edit_path, [], $this->getFormSubmitAction($entity, $language->getId()));
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
$this->assertNotEqual($changed_timestamp, $entity->getChangedTime(), 'The entity\'s changed time was updated after form save without changes.');
}
}
}
......@@ -110,13 +110,6 @@ public function getCreatedTime() {
return $this->get('created')->value;
}
/**
* {@inheritdoc}
*/
public function getChangedTime() {
return $this->get('changed')->value;
}
/**
* {@inheritdoc}
*/
......
......@@ -132,13 +132,6 @@ public function getWeight() {
return (int) $this->get('weight')->value;
}
/**
* {@inheritdoc}
*/
public function getChangedTime() {
return $this->get('changed')->value;
}
/**
* {@inheritdoc}
*/
......
......@@ -231,13 +231,6 @@ public function setCreatedTime($timestamp) {
return $this;
}
/**
* {@inheritdoc}
*/
public function getChangedTime() {
return $this->get('changed')->value;
}
/**
* {@inheritdoc}
*/
......@@ -498,6 +491,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
->setDescription(t('Briefly describe the changes you have made.'))
->setRevisionable(TRUE)
->setTranslatable(TRUE)
->setDefaultValue('')
->setDisplayOptions('form', array(
'type' => 'string_textarea',
'weight' => 25,
......
<?php
/**
* @file
* Contains \Drupal\node\Tests\NodeFormSaveChangedTimeTest.
*/
namespace Drupal\node\Tests;
use Drupal\simpletest\WebTestBase;
/**
* Tests updating the changed time after API and FORM entity save.
*
* @group node
*/
class NodeFormSaveChangedTimeTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array(
'node',
);
/**
* An user with permissions to create and edit articles.
*
* @var \Drupal\user\UserInterface
*/
protected $authorUser;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Create a node type.
$this->drupalCreateContentType(array(
'type' => 'article',
'name' => 'Article',
));
$this->authorUser = $this->drupalCreateUser(['access content', 'create article content', 'edit any article content'], 'author');
$this->drupalLogin($this->authorUser);
// Create one node of the above node type .
$this->drupalCreateNode(array(
'type' => 'article',
));
}
/**
* Test the changed time after API and FORM save without changes.
*/
public function testChangedTimeAfterSaveWithoutChanges() {
$node = entity_load('node', 1);
$changed_timestamp = $node->getChangedTime();
$node->save();
$node = entity_load('node', 1, TRUE);
$this->assertEqual($changed_timestamp, $node->getChangedTime(), "The entity's changed time wasn't updated after API save without changes.");
// Ensure different save timestamps.
sleep(1);
// Save the node on the regular node edit form.
$this->drupalPostForm('node/1/edit', array(), t('Save'));
$node = entity_load('node', 1, TRUE);
$this->assertNotEqual($changed_timestamp, $node->getChangedTime(), "The entity's changed time was updated after form save without changes.");
}
}
......@@ -389,6 +389,7 @@ protected function removeNodeFieldsForNonAdminUsers(NodeInterface $node) {
$node->set('promote', NULL);
$node->set('sticky', NULL);
$node->set('revision_timestamp', NULL);
$node->set('revision_log', NULL);
$node->set('uid', NULL);
return $node;
......
......@@ -46,12 +46,4 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
return $fields;
}
/**
* {@inheritdoc}
*/
public function getChangedTime() {
return $this->get('changed')->value;
}
}
......@@ -72,12 +72,4 @@ public function save() {
sleep(1);
parent::save();
}
/**
* {@inheritdoc}
*/
public function getChangedTime() {
return $this->get('changed')->value;
}
}
......@@ -187,13 +187,6 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
return $fields;
}
/**
* {@inheritdoc}
*/
public function getChangedTime() {
return $this->get('changed')->value;
}
/**
* {@inheritdoc}
*/
......
......@@ -389,13 +389,6 @@ public function setUsername($username) {
return $this;
}
/**
* {@inheritdoc}
*/
public function getChangedTime() {
return $this->get('changed')->value;
}
/**
* {@inheritdoc}
*/
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment