summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/lib/Drupal/Core/Entity/ContentEntityBase.php90
-rw-r--r--core/modules/node/src/Entity/Node.php15
-rw-r--r--core/modules/node/src/NodeForm.php58
-rw-r--r--core/modules/node/src/NodeTranslationHandler.php5
-rw-r--r--core/modules/node/src/Tests/NodeTranslationUITest.php37
-rw-r--r--core/modules/system/src/Tests/Installer/InstallerTranslationTest.php2
6 files changed, 149 insertions, 58 deletions
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityBase.php b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
index 9d0e5d6..f554197 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityBase.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
@@ -138,13 +138,20 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
protected $isDefaultRevision = TRUE;
/**
- * Holds entity keys like the ID, bundle and revision ID.
+ * Holds translatable entity keys such as the ID, bundle and revision ID.
*
* @var array
*/
protected $entityKeys = array();
/**
+ * Holds translatable entity keys such as the label.
+ *
+ * @var array
+ */
+ protected $translatableEntityKeys = array();
+
+ /**
* Overrides Entity::__construct().
*/
public function __construct(array $values, $entity_type, $bundle = FALSE, $translations = array()) {
@@ -165,14 +172,36 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
$this->values = $values;
foreach ($this->getEntityType()->getKeys() as $key => $field_name) {
if (isset($this->values[$field_name])) {
- if (is_array($this->values[$field_name]) && isset($this->values[$field_name][LanguageInterface::LANGCODE_DEFAULT])) {
- if (is_array($this->values[$field_name][LanguageInterface::LANGCODE_DEFAULT])) {
- if (isset($this->values[$field_name][LanguageInterface::LANGCODE_DEFAULT][0]['value'])) {
- $this->entityKeys[$key] = $this->values[$field_name][LanguageInterface::LANGCODE_DEFAULT][0]['value'];
+ if (is_array($this->values[$field_name])) {
+ // We store untranslatable fields into an entity key without using a
+ // langcode key.
+ if (!$this->getFieldDefinition($field_name)->isTranslatable()) {
+ if (isset($this->values[$field_name][LanguageInterface::LANGCODE_DEFAULT])) {
+ if (is_array($this->values[$field_name][LanguageInterface::LANGCODE_DEFAULT])) {
+ if (isset($this->values[$field_name][LanguageInterface::LANGCODE_DEFAULT][0]['value'])) {
+ $this->entityKeys[$key] = $this->values[$field_name][LanguageInterface::LANGCODE_DEFAULT][0]['value'];
+ }
+ }
+ else {
+ $this->entityKeys[$key] = $this->values[$field_name][LanguageInterface::LANGCODE_DEFAULT];
+ }
}
}
else {
- $this->entityKeys[$key] = $this->values[$field_name][LanguageInterface::LANGCODE_DEFAULT];
+ // We save translatable fields such as the publishing status of a node
+ // into an entity key array keyed by langcode as a performance
+ // optimization, so we don't have to go through TypedData when we
+ // need these values.
+ foreach ($this->values[$field_name] as $langcode => $field_value) {
+ if (is_array($this->values[$field_name][$langcode])) {
+ if (isset($this->values[$field_name][$langcode][0]['value'])) {
+ $this->translatableEntityKeys[$key][$langcode] = $this->values[$field_name][$langcode][0]['value'];
+ }
+ }
+ else {
+ $this->translatableEntityKeys[$key][$langcode] = $this->values[$field_name][$langcode];
+ }
+ }
}
}
}
@@ -537,12 +566,12 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
// Get the language code if the property exists.
// Try to read the value directly from the list of entity keys which got
// initialized in __construct(). This avoids creating a field item object.
- if (isset($this->entityKeys['langcode'])) {
- $this->defaultLangcode = $this->entityKeys['langcode'];
+ if (isset($this->translatableEntityKeys['langcode'][$this->activeLangcode])) {
+ $this->defaultLangcode = $this->translatableEntityKeys['langcode'][$this->activeLangcode];
}
elseif ($this->hasField($this->langcodeKey) && ($item = $this->get($this->langcodeKey)) && isset($item->language)) {
$this->defaultLangcode = $item->language->getId();
- $this->entityKeys['langcode'] = $this->defaultLangcode;
+ $this->translatableEntityKeys['langcode'][$this->activeLangcode] = $this->defaultLangcode;
}
if (empty($this->defaultLangcode)) {
@@ -583,8 +612,13 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
// that check, as it ready only and must not change, unsetting it could
// lead to recursions.
if ($key = array_search($name, $this->getEntityType()->getKeys())) {
- if (isset($this->entityKeys[$key]) && $key != 'bundle') {
- unset($this->entityKeys[$key]);
+ if ($key != 'bundle') {
+ if (isset($this->entityKeys[$key])) {
+ unset($this->entityKeys[$key]);
+ }
+ elseif (isset($this->translatableEntityKeys[$key][$this->activeLangcode])) {
+ unset($this->translatableEntityKeys[$key][$this->activeLangcode]);
+ }
}
}
@@ -710,8 +744,6 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
$translation->enforceIsNew = &$this->enforceIsNew;
$translation->newRevision = &$this->newRevision;
$translation->translationInitialize = FALSE;
- // Reset language-dependent properties.
- unset($translation->entityKeys['label']);
$translation->typedData = NULL;
return $translation;
@@ -1020,18 +1052,34 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C
* The value of the entity key, NULL if not defined.
*/
protected function getEntityKey($key) {
- if (!isset($this->entityKeys[$key]) || !array_key_exists($key, $this->entityKeys)) {
- if ($this->getEntityType()->hasKey($key)) {
- $field_name = $this->getEntityType()->getKey($key);
- $property = $this->getFieldDefinition($field_name)->getFieldStorageDefinition()->getMainPropertyName();
- $this->entityKeys[$key] = $this->get($field_name)->$property;
+ // If the value is known already, return it.
+ if (isset($this->entityKeys[$key])) {
+ return $this->entityKeys[$key];
+ }
+ if (isset($this->translatableEntityKeys[$key][$this->activeLangcode])) {
+ return $this->translatableEntityKeys[$key][$this->activeLangcode];
+ }
+
+ // Otherwise fetch the value by creating a field object.
+ $value = NULL;
+ if ($this->getEntityType()->hasKey($key)) {
+ $field_name = $this->getEntityType()->getKey($key);
+ $definition = $this->getFieldDefinition($field_name);
+ $property = $definition->getFieldStorageDefinition()->getMainPropertyName();
+ $value = $this->get($field_name)->$property;
+
+ // Put it in the right array, depending on whether it is translatable.
+ if ($definition->isTranslatable()) {
+ $this->translatableEntityKeys[$key][$this->activeLangcode] = $value;
}
else {
- $this->entityKeys[$key] = NULL;
+ $this->entityKeys[$key] = $value;
}
-
}
- return $this->entityKeys[$key];
+ else {
+ $this->entityKeys[$key] = $value;
+ }
+ return $value;
}
/**
diff --git a/core/modules/node/src/Entity/Node.php b/core/modules/node/src/Entity/Node.php
index 2f57843..afa31e3 100644
--- a/core/modules/node/src/Entity/Node.php
+++ b/core/modules/node/src/Entity/Node.php
@@ -53,7 +53,9 @@ use Drupal\user\UserInterface;
* "bundle" = "type",
* "label" = "title",
* "langcode" = "langcode",
- * "uuid" = "uuid"
+ * "uuid" = "uuid",
+ * "status" = "status",
+ * "uid" = "uid",
* },
* bundle_entity_type = "node_type",
* field_ui_base_route = "entity.node_type.edit_form",
@@ -73,6 +75,13 @@ class Node extends ContentEntityBase implements NodeInterface {
use EntityChangedTrait;
/**
+ * Whether the node is being previewed or not.
+ *
+ * @var true|null
+ */
+ public $in_preview = NULL;
+
+ /**
* {@inheritdoc}
*/
public function preSave(EntityStorageInterface $storage) {
@@ -258,7 +267,7 @@ class Node extends ContentEntityBase implements NodeInterface {
* {@inheritdoc}
*/
public function isPublished() {
- return (bool) $this->get('status')->value;
+ return (bool) $this->getEntityKey('status');
}
/**
@@ -280,7 +289,7 @@ class Node extends ContentEntityBase implements NodeInterface {
* {@inheritdoc}
*/
public function getOwnerId() {
- return $this->get('uid')->target_id;
+ return $this->getEntityKey('uid');
}
/**
diff --git a/core/modules/node/src/NodeForm.php b/core/modules/node/src/NodeForm.php
index 33dec70..c584660 100644
--- a/core/modules/node/src/NodeForm.php
+++ b/core/modules/node/src/NodeForm.php
@@ -7,7 +7,6 @@
namespace Drupal\node;
-use Drupal\Component\Utility\Html;
use Drupal\Core\Entity\ContentEntityForm;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Form\FormStateInterface;
@@ -204,10 +203,33 @@ class NodeForm extends ContentEntityForm {
$form['#attached']['library'][] = 'node/form';
+ $form['#entity_builders']['update_status'] = [$this, 'updateStatus'];
+
return $form;
}
/**
+ * Entity builder updating the node status with the submitted value.
+ *
+ * @param string $entity_type_id
+ * The entity type identifier.
+ * @param \Drupal\node\NodeInterface $node
+ * The node updated with the submitted values.
+ * @param array $form
+ * The complete form array.
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ * The current state of the form.
+ *
+ * @see \Drupal\node\NodeForm::form()
+ */
+ function updateStatus($entity_type_id, NodeInterface $node, array $form, FormStateInterface $form_state) {
+ $element = $form_state->getTriggeringElement();
+ if (isset($element['#published_status'])) {
+ $node->setPublished($element['#published_status']);
+ }
+ }
+
+ /**
* {@inheritdoc}
*/
protected function actions(array $form, FormStateInterface $form_state) {
@@ -232,6 +254,8 @@ class NodeForm extends ContentEntityForm {
// Add a "Publish" button.
$element['publish'] = $element['submit'];
+ // If the "Publish" button is clicked, we want to update the status to "published".
+ $element['publish']['#published_status'] = TRUE;
$element['publish']['#dropbutton'] = 'save';
if ($node->isNew()) {
$element['publish']['#value'] = t('Save and publish');
@@ -240,10 +264,11 @@ class NodeForm extends ContentEntityForm {
$element['publish']['#value'] = $node->isPublished() ? t('Save and keep published') : t('Save and publish');
}
$element['publish']['#weight'] = 0;
- array_unshift($element['publish']['#submit'], '::publish');
// Add a "Unpublish" button.
$element['unpublish'] = $element['submit'];
+ // If the "Unpublish" button is clicked, we want to update the status to "unpublished".
+ $element['unpublish']['#published_status'] = FALSE;
$element['unpublish']['#dropbutton'] = 'save';
if ($node->isNew()) {
$element['unpublish']['#value'] = t('Save as unpublished');
@@ -252,7 +277,6 @@ class NodeForm extends ContentEntityForm {
$element['unpublish']['#value'] = !$node->isPublished() ? t('Save and keep unpublished') : t('Save and unpublish');
}
$element['unpublish']['#weight'] = 10;
- array_unshift($element['unpublish']['#submit'], '::unpublish');
// If already published, the 'publish' button is primary.
if ($node->isPublished()) {
@@ -328,34 +352,6 @@ class NodeForm extends ContentEntityForm {
}
/**
- * Form submission handler for the 'publish' action.
- *
- * @param $form
- * An associative array containing the structure of the form.
- * @param $form_state
- * The current state of the form.
- */
- public function publish(array $form, FormStateInterface $form_state) {
- $node = $this->entity;
- $node->setPublished(TRUE);
- return $node;
- }
-
- /**
- * Form submission handler for the 'unpublish' action.
- *
- * @param $form
- * An associative array containing the structure of the form.
- * @param $form_state
- * The current state of the form.
- */
- public function unpublish(array $form, FormStateInterface $form_state) {
- $node = $this->entity;
- $node->setPublished(FALSE);
- return $node;
- }
-
- /**
* {@inheritdoc}
*/
public function buildEntity(array $form, FormStateInterface $form_state) {
diff --git a/core/modules/node/src/NodeTranslationHandler.php b/core/modules/node/src/NodeTranslationHandler.php
index 3692c8b..41c6909 100644
--- a/core/modules/node/src/NodeTranslationHandler.php
+++ b/core/modules/node/src/NodeTranslationHandler.php
@@ -7,8 +7,8 @@
namespace Drupal\node;
-use Drupal\Core\Entity\EntityInterface;
use Drupal\content_translation\ContentTranslationHandler;
+use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
/**
@@ -76,9 +76,8 @@ class NodeTranslationHandler extends ContentTranslationHandler {
*/
public function entityFormEntityBuild($entity_type, EntityInterface $entity, array $form, FormStateInterface $form_state) {
if ($form_state->hasValue('content_translation')) {
- $form_object = $form_state->getFormObject();
$translation = &$form_state->getValue('content_translation');
- $translation['status'] = $form_object->getEntity()->isPublished();
+ $translation['status'] = $entity->isPublished();
// $form['content_translation']['name'] is the equivalent field
// for translation author uid.
$account = $entity->uid->entity;
diff --git a/core/modules/node/src/Tests/NodeTranslationUITest.php b/core/modules/node/src/Tests/NodeTranslationUITest.php
index 608ab43..287920b 100644
--- a/core/modules/node/src/Tests/NodeTranslationUITest.php
+++ b/core/modules/node/src/Tests/NodeTranslationUITest.php
@@ -12,6 +12,7 @@ use Drupal\content_translation\Tests\ContentTranslationUITestBase;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Url;
use Drupal\node\Entity\Node;
+use Drupal\language\Entity\ConfigurableLanguage;
/**
* Tests the Node Translation UI.
@@ -58,6 +59,42 @@ class NodeTranslationUITest extends ContentTranslationUITestBase {
}
/**
+ * Tests changing the published status on a node without fields.
+ */
+ function testPublishedStatusNoFields() {
+ // Test changing the published status of an article without fields.
+ $this->drupalLogin($this->administrator);
+ // Delete all fields.
+ $this->drupalGet('admin/structure/types/manage/article/fields');
+ $this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.' . $this->fieldName . '/delete', array(), t('Delete'));
+ $this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.field_tags/delete', array(), t('Delete'));
+ $this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.field_image/delete', array(), t('Delete'));
+
+ // Add a node.
+ $default_langcode = $this->langcodes[0];
+ $values[$default_langcode] = array('title' => array(array('value' => $this->randomMachineName())));
+ $entity_id = $this->createEntity($values[$default_langcode], $default_langcode);
+ $entity = entity_load($this->entityTypeId, $entity_id, TRUE);
+
+ // Add a content translation.
+ $langcode = 'fr';
+ $language = ConfigurableLanguage::load($langcode);
+ $values[$langcode] = array('title' => array(array('value' => $this->randomMachineName())));
+
+ $add_url = Url::fromRoute('content_translation.translation_add_' . $entity->getEntityTypeId(), [
+ $entity->getEntityTypeId() => $entity->id(),
+ 'source' => $default_langcode,
+ 'target' => $langcode
+ ], array('language' => $language));
+ $this->drupalPostForm($add_url, $this->getEditValues($values, $langcode), t('Save and unpublish (this translation)'));
+
+ $entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
+ $translation = $entity->getTranslation($langcode);
+ // Make sure we unpublished the node correctly.
+ $this->assertFalse($this->manager->getTranslationMetadata($translation)->isPublished(), 'The translation has been correctly unpublished.');
+ }
+
+ /**
* Overrides \Drupal\content_translation\Tests\ContentTranslationUITestBase::getTranslatorPermission().
*/
protected function getTranslatorPermissions() {
diff --git a/core/modules/system/src/Tests/Installer/InstallerTranslationTest.php b/core/modules/system/src/Tests/Installer/InstallerTranslationTest.php
index 7f3a66f..215f60e 100644
--- a/core/modules/system/src/Tests/Installer/InstallerTranslationTest.php
+++ b/core/modules/system/src/Tests/Installer/InstallerTranslationTest.php
@@ -57,6 +57,8 @@ class InstallerTranslationTest extends InstallerTestBase {
$this->assertText('German');
$this->assertNoText('English');
+ // The current container still has the english as current language, rebuild.
+ $this->rebuildContainer();
/** @var \Drupal\user\Entity\User $account */
$account = User::load(0);
$this->assertEqual($account->language()->getId(), 'en', 'Anonymous user is English.');