summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNathaniel Catchpole2017-06-15 13:02:05 +0100
committerNathaniel Catchpole2017-06-15 13:02:05 +0100
commitc53b32284783d9f3c188116b0985caa680cabc55 (patch)
tree09a86640b6c2f23eefd1cb9e8673e0656f1f2e61
parent19854be65fea40920ea8a9c15f6e424e6ce0bcfb (diff)
Issue #2068063 by timmillwood, Berdir, tduong, Manuel Garcia, nlisgo, yongt9412, esod, lauriii, jonathan1055, vijaycs85, yoroy, amit.drupal, tkoleary, ifrik, Bojhan, xjm, tstoeckler, amateescu: Change "Save and keep un-/published" buttons to a "Published" checkbox and an included "Save" button
-rw-r--r--core/lib/Drupal/Core/Entity/EntityPublishedTrait.php3
-rw-r--r--core/modules/book/config/optional/core.entity_form_display.node.book.default.yml7
-rw-r--r--core/modules/book/tests/src/Functional/BookTest.php4
-rw-r--r--core/modules/comment/tests/src/Functional/CommentStatusFieldAccessTest.php4
-rw-r--r--core/modules/content_moderation/content_moderation.module23
-rw-r--r--core/modules/content_moderation/tests/src/Functional/ModerationStateNodeTest.php2
-rw-r--r--core/modules/content_moderation/tests/src/Functional/ModerationStateNodeTypeTest.php6
-rw-r--r--core/modules/content_moderation/tests/src/Functional/NodeAccessTest.php16
-rw-r--r--core/modules/content_translation/src/Tests/ContentTranslationLanguageChangeTest.php12
-rw-r--r--core/modules/field/tests/src/Functional/EntityReference/EntityReferenceXSSTest.php2
-rw-r--r--core/modules/file/src/Tests/FileFieldAnonymousSubmissionTest.php2
-rw-r--r--core/modules/file/src/Tests/FileFieldDisplayTest.php6
-rw-r--r--core/modules/file/src/Tests/FileFieldRevisionTest.php2
-rw-r--r--core/modules/file/src/Tests/FileFieldTestBase.php6
-rw-r--r--core/modules/file/src/Tests/FileFieldValidateTest.php4
-rw-r--r--core/modules/file/src/Tests/FileFieldWidgetTest.php10
-rw-r--r--core/modules/file/src/Tests/FilePrivateTest.php6
-rw-r--r--core/modules/filter/tests/src/Functional/FilterHooksTest.php2
-rw-r--r--core/modules/forum/config/optional/core.entity_form_display.node.forum.default.yml7
-rw-r--r--core/modules/forum/tests/src/Functional/ForumBlockTest.php2
-rw-r--r--core/modules/forum/tests/src/Functional/ForumIndexTest.php5
-rw-r--r--core/modules/image/src/Tests/ImageFieldDisplayTest.php8
-rw-r--r--core/modules/image/src/Tests/ImageFieldTestBase.php4
-rw-r--r--core/modules/image/src/Tests/ImageFieldValidateTest.php4
-rw-r--r--core/modules/inline_form_errors/tests/src/FunctionalJavascript/FormErrorHandlerCKEditorTest.php2
-rw-r--r--core/modules/language/tests/src/Functional/LanguageNegotiationUrlTest.php2
-rw-r--r--core/modules/menu_ui/src/Tests/MenuNodeTest.php32
-rw-r--r--core/modules/node/node.post_update.php29
-rw-r--r--core/modules/node/src/Entity/Node.php10
-rw-r--r--core/modules/node/src/NodeForm.php72
-rw-r--r--core/modules/node/src/Tests/Update/NodeUpdateTest.php26
-rw-r--r--core/modules/node/tests/src/Functional/NodeEditFormTest.php25
-rw-r--r--core/modules/node/tests/src/Functional/NodeFormButtonsTest.php135
-rw-r--r--core/modules/node/tests/src/Functional/NodeRevisionsUiBypassAccessTest.php4
-rw-r--r--core/modules/node/tests/src/Functional/NodeRevisionsUiTest.php4
-rw-r--r--core/modules/node/tests/src/Functional/NodeTranslationUITest.php26
-rw-r--r--core/modules/options/tests/src/Functional/OptionsFieldUITest.php2
-rw-r--r--core/modules/search/src/Tests/SearchConfigSettingsFormTest.php2
-rw-r--r--core/modules/system/src/Tests/Menu/BreadcrumbTest.php4
-rw-r--r--core/modules/system/src/Tests/Update/UpdatePathRC1TestBaseFilledTest.php2
-rw-r--r--core/modules/system/src/Tests/Update/UpdatePathTestBaseFilledTest.php2
-rw-r--r--core/modules/taxonomy/tests/src/Functional/LegacyTest.php2
-rw-r--r--core/profiles/standard/config/install/core.entity_form_display.node.article.default.yml7
-rw-r--r--core/profiles/standard/config/install/core.entity_form_display.node.page.default.yml7
-rw-r--r--core/themes/bartik/css/components/form.css8
-rw-r--r--core/themes/seven/css/layout/node-add.css9
-rw-r--r--core/themes/seven/templates/node-edit-form.html.twig31
47 files changed, 294 insertions, 296 deletions
diff --git a/core/lib/Drupal/Core/Entity/EntityPublishedTrait.php b/core/lib/Drupal/Core/Entity/EntityPublishedTrait.php
index 871aceb..59fddd5 100644
--- a/core/lib/Drupal/Core/Entity/EntityPublishedTrait.php
+++ b/core/lib/Drupal/Core/Entity/EntityPublishedTrait.php
@@ -33,8 +33,7 @@ trait EntityPublishedTrait {
}
return [$entity_type->getKey('published') => BaseFieldDefinition::create('boolean')
- ->setLabel(new TranslatableMarkup('Publishing status'))
- ->setDescription(new TranslatableMarkup('A boolean indicating the published state.'))
+ ->setLabel(new TranslatableMarkup('Published'))
->setRevisionable(TRUE)
->setTranslatable(TRUE)
->setDefaultValue(TRUE)];
diff --git a/core/modules/book/config/optional/core.entity_form_display.node.book.default.yml b/core/modules/book/config/optional/core.entity_form_display.node.book.default.yml
index 58aba45..b1db6d1 100644
--- a/core/modules/book/config/optional/core.entity_form_display.node.book.default.yml
+++ b/core/modules/book/config/optional/core.entity_form_display.node.book.default.yml
@@ -33,6 +33,13 @@ content:
weight: 15
region: content
third_party_settings: { }
+ status:
+ type: boolean_checkbox
+ settings:
+ display_label: true
+ weight: 120
+ region: content
+ third_party_settings: { }
sticky:
type: boolean_checkbox
settings:
diff --git a/core/modules/book/tests/src/Functional/BookTest.php b/core/modules/book/tests/src/Functional/BookTest.php
index 66edec9..e52e484 100644
--- a/core/modules/book/tests/src/Functional/BookTest.php
+++ b/core/modules/book/tests/src/Functional/BookTest.php
@@ -741,8 +741,8 @@ class BookTest extends BrowserTestBase {
$this->drupalPlaceBlock('book_navigation', ['block_mode' => 'book pages']);
// Unpublish book node.
- $edit = [];
- $this->drupalPostForm('node/' . $this->book->id() . '/edit', $edit, t('Save and unpublish'));
+ $edit = ['status[value]' => FALSE];
+ $this->drupalPostForm('node/' . $this->book->id() . '/edit', $edit, t('Save'));
// Test node page.
$this->drupalGet('node/' . $this->book->id());
diff --git a/core/modules/comment/tests/src/Functional/CommentStatusFieldAccessTest.php b/core/modules/comment/tests/src/Functional/CommentStatusFieldAccessTest.php
index 504b3ba..e208c71 100644
--- a/core/modules/comment/tests/src/Functional/CommentStatusFieldAccessTest.php
+++ b/core/modules/comment/tests/src/Functional/CommentStatusFieldAccessTest.php
@@ -86,14 +86,14 @@ class CommentStatusFieldAccessTest extends BrowserTestBase {
$assert->fieldNotExists('comment[0][status]');
$this->submitForm([
'title[0][value]' => 'Node 1',
- ], t('Save and publish'));
+ ], t('Save'));
$assert->fieldExists('subject[0][value]');
$this->drupalLogin($this->commentAdmin);
$this->drupalGet('node/add/article');
$assert->fieldExists('comment[0][status]');
$this->submitForm([
'title[0][value]' => 'Node 2',
- ], t('Save and publish'));
+ ], t('Save'));
$assert->fieldExists('subject[0][value]');
}
diff --git a/core/modules/content_moderation/content_moderation.module b/core/modules/content_moderation/content_moderation.module
index 31cc6c9..921fe2a 100644
--- a/core/modules/content_moderation/content_moderation.module
+++ b/core/modules/content_moderation/content_moderation.module
@@ -14,6 +14,8 @@ use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccountInterface;
@@ -167,6 +169,27 @@ function content_moderation_node_access(NodeInterface $node, $operation, Account
}
/**
+ * Implements hook_entity_field_access().
+ */
+function content_moderation_entity_field_access($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemListInterface $items = NULL) {
+ if ($items && $operation === 'edit') {
+ /** @var \Drupal\content_moderation\ModerationInformationInterface $moderation_info */
+ $moderation_info = Drupal::service('content_moderation.moderation_information');
+
+ $entity_type = \Drupal::entityTypeManager()->getDefinition($field_definition->getTargetEntityTypeId());
+
+ $entity = $items->getEntity();
+
+ // Deny edit access to the published field if the entity is being moderated.
+ if ($entity_type->hasKey('published') && $moderation_info->isModeratedEntity($entity) && $entity->moderation_state && $field_definition->getName() == $entity_type->getKey('published')) {
+ return AccessResult::forbidden();
+ }
+ }
+
+ return AccessResult::neutral();
+}
+
+/**
* Implements hook_theme().
*/
function content_moderation_theme() {
diff --git a/core/modules/content_moderation/tests/src/Functional/ModerationStateNodeTest.php b/core/modules/content_moderation/tests/src/Functional/ModerationStateNodeTest.php
index 74129a1..277acdf 100644
--- a/core/modules/content_moderation/tests/src/Functional/ModerationStateNodeTest.php
+++ b/core/modules/content_moderation/tests/src/Functional/ModerationStateNodeTest.php
@@ -62,7 +62,7 @@ class ModerationStateNodeTest extends ModerationStateTestBase {
// Create a new node.
$this->drupalPostForm('node/add/moderated_content', [
'title[0][value]' => 'non-moderated content',
- ], t('Save and publish'));
+ ], t('Save'));
$node = $this->getNodeByTitle('non-moderated content');
if (!$node) {
diff --git a/core/modules/content_moderation/tests/src/Functional/ModerationStateNodeTypeTest.php b/core/modules/content_moderation/tests/src/Functional/ModerationStateNodeTypeTest.php
index de4a7c2..0555120 100644
--- a/core/modules/content_moderation/tests/src/Functional/ModerationStateNodeTypeTest.php
+++ b/core/modules/content_moderation/tests/src/Functional/ModerationStateNodeTypeTest.php
@@ -18,10 +18,10 @@ class ModerationStateNodeTypeTest extends ModerationStateTestBase {
$this->assertText('The content type Not moderated has been added.');
$this->grantUserPermissionToCreateContentOfType($this->adminUser, 'not_moderated');
$this->drupalGet('node/add/not_moderated');
- $this->assertRaw('Save as unpublished');
+ $this->assertRaw('Save');
$this->drupalPostForm(NULL, [
'title[0][value]' => 'Test',
- ], t('Save and publish'));
+ ], t('Save'));
$this->assertText('Not moderated Test has been created.');
}
@@ -53,7 +53,7 @@ class ModerationStateNodeTypeTest extends ModerationStateTestBase {
$this->drupalGet('node/add/not_moderated');
$this->drupalPostForm(NULL, [
'title[0][value]' => 'Test',
- ], t('Save and publish'));
+ ], t('Save'));
$this->assertText('Not moderated Test has been created.');
// Now enable moderation state.
diff --git a/core/modules/content_moderation/tests/src/Functional/NodeAccessTest.php b/core/modules/content_moderation/tests/src/Functional/NodeAccessTest.php
index 00e847d..eeac1ce 100644
--- a/core/modules/content_moderation/tests/src/Functional/NodeAccessTest.php
+++ b/core/modules/content_moderation/tests/src/Functional/NodeAccessTest.php
@@ -46,7 +46,7 @@ class NodeAccessTest extends ModerationStateTestBase {
protected function setUp() {
parent::setUp();
$this->drupalLogin($this->adminUser);
- $this->createContentTypeFromUi('Moderated content', 'moderated_content', TRUE);
+ $this->createContentTypeFromUi('Moderated content', 'moderated_content', FALSE);
$this->grantUserPermissionToCreateContentOfType($this->adminUser, 'moderated_content');
// Rebuild permissions because hook_node_grants() is implemented by the
@@ -60,8 +60,20 @@ class NodeAccessTest extends ModerationStateTestBase {
public function testPageAccess() {
$this->drupalLogin($this->adminUser);
+ // Access the node form before moderation is enabled, the publication state
+ // should now be visible.
+ $this->drupalGet('node/add/moderated_content');
+ $this->assertSession()->fieldExists('Published');
+
+ // Now enable the workflow.
+ $this->enableModerationThroughUi('moderated_content', 'editorial');
+
+ // Access that the status field is no longer visible.
+ $this->drupalGet('node/add/moderated_content');
+ $this->assertSession()->fieldNotExists('Published');
+
// Create a node to test with.
- $this->drupalPostForm('node/add/moderated_content', [
+ $this->drupalPostForm(NULL, [
'title[0][value]' => 'moderated content',
], t('Save and Create New Draft'));
$node = $this->getNodeByTitle('moderated content');
diff --git a/core/modules/content_translation/src/Tests/ContentTranslationLanguageChangeTest.php b/core/modules/content_translation/src/Tests/ContentTranslationLanguageChangeTest.php
index bb9b223..04bd023 100644
--- a/core/modules/content_translation/src/Tests/ContentTranslationLanguageChangeTest.php
+++ b/core/modules/content_translation/src/Tests/ContentTranslationLanguageChangeTest.php
@@ -73,12 +73,12 @@ class ContentTranslationLanguageChangeTest extends NodeTestBase {
$edit = [
'title[0][value]' => 'english_title',
];
- $this->drupalPostForm(NULL, $edit, t('Save and publish'));
+ $this->drupalPostForm(NULL, $edit, t('Save'));
// Create a translation in French.
$this->clickLink('Translate');
$this->clickLink('Add');
- $this->drupalPostForm(NULL, [], t('Save and keep published (this translation)'));
+ $this->drupalPostForm(NULL, [], t('Save (this translation)'));
$this->clickLink('Translate');
// Edit English translation.
@@ -90,7 +90,7 @@ class ContentTranslationLanguageChangeTest extends NodeTestBase {
'files[field_image_field_0]' => $images->uri,
];
$this->drupalPostForm(NULL, $edit, t('Upload'));
- $this->drupalPostForm(NULL, ['field_image_field[0][alt]' => 'alternative_text'], t('Save and keep published (this translation)'));
+ $this->drupalPostForm(NULL, ['field_image_field[0][alt]' => 'alternative_text'], t('Save (this translation)'));
// Check that the translation languages are correct.
$node = $this->getNodeByTitle('english_title');
@@ -109,13 +109,13 @@ class ContentTranslationLanguageChangeTest extends NodeTestBase {
'title[0][value]' => 'english_title',
'test_field_only_en_fr' => 'node created',
];
- $this->drupalPostForm(NULL, $edit, t('Save and publish'));
+ $this->drupalPostForm(NULL, $edit, t('Save'));
$this->assertEqual('node created', \Drupal::state()->get('test_field_only_en_fr'));
// Create a translation in French.
$this->clickLink('Translate');
$this->clickLink('Add');
- $this->drupalPostForm(NULL, [], t('Save and keep published (this translation)'));
+ $this->drupalPostForm(NULL, [], t('Save (this translation)'));
$this->clickLink('Translate');
// Edit English translation.
@@ -137,7 +137,7 @@ class ContentTranslationLanguageChangeTest extends NodeTestBase {
'langcode[0][value]' => 'en',
'field_image_field[0][alt]' => 'alternative_text'
];
- $this->drupalPostForm(NULL, $edit, t('Save and keep published (this translation)'));
+ $this->drupalPostForm(NULL, $edit, t('Save (this translation)'));
// Check that the translation languages are correct.
$node = $this->getNodeByTitle('english_title');
diff --git a/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceXSSTest.php b/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceXSSTest.php
index 46986ec..b161ed6 100644
--- a/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceXSSTest.php
+++ b/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceXSSTest.php
@@ -62,7 +62,7 @@ class EntityReferenceXSSTest extends BrowserTestBase {
'title[0][value]' => $this->randomString(),
'entity_reference_test' => $referenced_node->id()
];
- $this->drupalPostForm(NULL, $edit, 'Save and publish');
+ $this->drupalPostForm(NULL, $edit, 'Save');
$this->assertEscaped($referenced_node->getTitle());
// Test the options_buttons type.
diff --git a/core/modules/file/src/Tests/FileFieldAnonymousSubmissionTest.php b/core/modules/file/src/Tests/FileFieldAnonymousSubmissionTest.php
index b59b1ba..66d87cd 100644
--- a/core/modules/file/src/Tests/FileFieldAnonymousSubmissionTest.php
+++ b/core/modules/file/src/Tests/FileFieldAnonymousSubmissionTest.php
@@ -138,7 +138,7 @@ class FileFieldAnonymousSubmissionTest extends FileFieldTestBase {
$label = 'Save';
}
else {
- $label = 'Save and publish';
+ $label = 'Save';
}
$this->drupalPostForm(NULL, $edit, $label);
$this->assertResponse(200);
diff --git a/core/modules/file/src/Tests/FileFieldDisplayTest.php b/core/modules/file/src/Tests/FileFieldDisplayTest.php
index 1b7e534..db9b7dc 100644
--- a/core/modules/file/src/Tests/FileFieldDisplayTest.php
+++ b/core/modules/file/src/Tests/FileFieldDisplayTest.php
@@ -77,7 +77,7 @@ class FileFieldDisplayTest extends FileFieldTestBase {
// Turn the "display" option off and check that the file is no longer displayed.
$edit = [$field_name . '[0][display]' => FALSE];
- $this->drupalPostForm('node/' . $nid . '/edit', $edit, t('Save and keep published'));
+ $this->drupalPostForm('node/' . $nid . '/edit', $edit, t('Save'));
$this->assertNoRaw($default_output, 'Field is hidden when "display" option is unchecked.');
@@ -87,7 +87,7 @@ class FileFieldDisplayTest extends FileFieldTestBase {
$field_name . '[0][description]' => $description,
$field_name . '[0][display]' => TRUE,
];
- $this->drupalPostForm('node/' . $nid . '/edit', $edit, t('Save and keep published'));
+ $this->drupalPostForm('node/' . $nid . '/edit', $edit, t('Save'));
$this->assertText($description);
// Ensure the filename in the link's title attribute is escaped.
@@ -167,7 +167,7 @@ class FileFieldDisplayTest extends FileFieldTestBase {
'title[0][value]' => $title,
'files[field_' . $field_name . '_0]' => drupal_realpath($file->uri),
];
- $this->drupalPostForm('node/add/' . $type_name, $edit, t('Save and publish'));
+ $this->drupalPostForm('node/add/' . $type_name, $edit, t('Save'));
$node = $this->drupalGetNodeByTitle($title);
$this->drupalGet('node/' . $node->id() . '/edit');
$this->assertText(t('The description may be used as the label of the link to the file.'));
diff --git a/core/modules/file/src/Tests/FileFieldRevisionTest.php b/core/modules/file/src/Tests/FileFieldRevisionTest.php
index 7de496f..7b7b4d3 100644
--- a/core/modules/file/src/Tests/FileFieldRevisionTest.php
+++ b/core/modules/file/src/Tests/FileFieldRevisionTest.php
@@ -63,7 +63,7 @@ class FileFieldRevisionTest extends FileFieldTestBase {
// Save a new version of the node without any changes.
// Check that the file is still the same as the previous revision.
- $this->drupalPostForm('node/' . $nid . '/edit', ['revision' => '1'], t('Save and keep published'));
+ $this->drupalPostForm('node/' . $nid . '/edit', ['revision' => '1'], t('Save'));
$node_storage->resetCache([$nid]);
$node = $node_storage->load($nid);
$node_file_r3 = File::load($node->{$field_name}->target_id);
diff --git a/core/modules/file/src/Tests/FileFieldTestBase.php b/core/modules/file/src/Tests/FileFieldTestBase.php
index 73cc96a..b7b90d7 100644
--- a/core/modules/file/src/Tests/FileFieldTestBase.php
+++ b/core/modules/file/src/Tests/FileFieldTestBase.php
@@ -227,7 +227,7 @@ abstract class FileFieldTestBase extends WebTestBase {
$edit[$name][] = $file_path;
}
}
- $this->drupalPostForm("node/$nid/edit", $edit, t('Save and keep published'));
+ $this->drupalPostForm("node/$nid/edit", $edit, t('Save'));
return $nid;
}
@@ -243,7 +243,7 @@ abstract class FileFieldTestBase extends WebTestBase {
];
$this->drupalPostForm('node/' . $nid . '/edit', [], t('Remove'));
- $this->drupalPostForm(NULL, $edit, t('Save and keep published'));
+ $this->drupalPostForm(NULL, $edit, t('Save'));
}
/**
@@ -256,7 +256,7 @@ abstract class FileFieldTestBase extends WebTestBase {
];
$this->drupalPostForm('node/' . $nid . '/edit', [], t('Remove'));
- $this->drupalPostForm(NULL, $edit, t('Save and keep published'));
+ $this->drupalPostForm(NULL, $edit, t('Save'));
}
/**
diff --git a/core/modules/file/src/Tests/FileFieldValidateTest.php b/core/modules/file/src/Tests/FileFieldValidateTest.php
index 5ddcdde..422bf68 100644
--- a/core/modules/file/src/Tests/FileFieldValidateTest.php
+++ b/core/modules/file/src/Tests/FileFieldValidateTest.php
@@ -29,7 +29,7 @@ class FileFieldValidateTest extends FileFieldTestBase {
// Try to post a new node without uploading a file.
$edit = [];
$edit['title[0][value]'] = $this->randomMachineName();
- $this->drupalPostForm('node/add/' . $type_name, $edit, t('Save and publish'));
+ $this->drupalPostForm('node/add/' . $type_name, $edit, t('Save'));
$this->assertRaw(t('@title field is required.', ['@title' => $field->getLabel()]), 'Node save failed when required file field was empty.');
// Create a new node with the uploaded file.
@@ -50,7 +50,7 @@ class FileFieldValidateTest extends FileFieldTestBase {
// Try to post a new node without uploading a file in the multivalue field.
$edit = [];
$edit['title[0][value]'] = $this->randomMachineName();
- $this->drupalPostForm('node/add/' . $type_name, $edit, t('Save and publish'));
+ $this->drupalPostForm('node/add/' . $type_name, $edit, t('Save'));
$this->assertRaw(t('@title field is required.', ['@title' => $field->getLabel()]), 'Node save failed when required multiple value file field was empty.');
// Create a new node with the uploaded file into the multivalue field.
diff --git a/core/modules/file/src/Tests/FileFieldWidgetTest.php b/core/modules/file/src/Tests/FileFieldWidgetTest.php
index ddfd8f4..ad47d77 100644
--- a/core/modules/file/src/Tests/FileFieldWidgetTest.php
+++ b/core/modules/file/src/Tests/FileFieldWidgetTest.php
@@ -121,7 +121,7 @@ class FileFieldWidgetTest extends FileFieldTestBase {
$this->assertTrue(isset($label[0]), 'Label for upload found.');
// Save the node and ensure it does not have the file.
- $this->drupalPostForm(NULL, [], t('Save and keep published'));
+ $this->drupalPostForm(NULL, [], t('Save'));
$node_storage->resetCache([$nid]);
$node = $node_storage->load($nid);
$this->assertTrue(empty($node->{$field_name}->target_id), 'File was successfully removed from the node.');
@@ -238,8 +238,7 @@ class FileFieldWidgetTest extends FileFieldTestBase {
$this->assertNoFieldByXPath('//input[@type="submit"]', t('Remove'), format_string('After removing all files, there is no "Remove" button displayed (JSMode=%type).', ['%type' => $type]));
// Save the node and ensure it does not have any files.
- $this->drupalPostForm(NULL, ['title[0][value]' => $this->randomMachineName()], t('Save and publish'));
- $matches = [];
+ $this->drupalPostForm(NULL, ['title[0][value]' => $this->randomMachineName()], t('Save'));
preg_match('/node\/([0-9]+)/', $this->getUrl(), $matches);
$nid = $matches[1];
$node_storage->resetCache([$nid]);
@@ -368,7 +367,7 @@ class FileFieldWidgetTest extends FileFieldTestBase {
$edit = [
'title[0][value]' => $this->randomMachineName(),
];
- $this->drupalPostForm('node/add/article', $edit, t('Save and publish'));
+ $this->drupalPostForm('node/add/article', $edit, t('Save'));
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
// Add a comment with a file.
@@ -402,7 +401,8 @@ class FileFieldWidgetTest extends FileFieldTestBase {
// Unpublishes node.
$this->drupalLogin($this->adminUser);
- $this->drupalPostForm('node/' . $node->id() . '/edit', [], t('Save and unpublish'));
+ $edit = ['status[value]' => FALSE];
+ $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
// Ensures normal user can no longer download the file.
$this->drupalLogin($user);
diff --git a/core/modules/file/src/Tests/FilePrivateTest.php b/core/modules/file/src/Tests/FilePrivateTest.php
index 3c9776f..7ecd485 100644
--- a/core/modules/file/src/Tests/FilePrivateTest.php
+++ b/core/modules/file/src/Tests/FilePrivateTest.php
@@ -72,10 +72,10 @@ class FilePrivateTest extends FileFieldTestBase {
// Attempt to reuse the file when editing a node.
$edit = [];
$edit['title[0][value]'] = $this->randomMachineName();
- $this->drupalPostForm('node/add/' . $type_name, $edit, t('Save and publish'));
+ $this->drupalPostForm('node/add/' . $type_name, $edit, t('Save'));
$new_node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
$edit[$field_name . '[0][fids]'] = $node_file->id();
- $this->drupalPostForm('node/' . $new_node->id() . '/edit', $edit, t('Save and keep published'));
+ $this->drupalPostForm('node/' . $new_node->id() . '/edit', $edit, t('Save'));
// Make sure the form submit failed - we stayed on the edit form.
$this->assertUrl('node/' . $new_node->id() . '/edit');
// Check that we got the expected constraint form error.
@@ -86,7 +86,7 @@ class FilePrivateTest extends FileFieldTestBase {
$edit = [];
$edit['title[0][value]'] = $this->randomMachineName();
$edit[$field_name . '[0][fids]'] = $node_file->id();
- $this->drupalPostForm('node/add/' . $type_name, $edit, t('Save and publish'));
+ $this->drupalPostForm('node/add/' . $type_name, $edit, t('Save'));
$new_node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
$this->assertTrue(empty($new_node), 'Node was not created.');
$this->assertUrl('node/add/' . $type_name);
diff --git a/core/modules/filter/tests/src/Functional/FilterHooksTest.php b/core/modules/filter/tests/src/Functional/FilterHooksTest.php
index c799460..5ff4a8a 100644
--- a/core/modules/filter/tests/src/Functional/FilterHooksTest.php
+++ b/core/modules/filter/tests/src/Functional/FilterHooksTest.php
@@ -60,7 +60,7 @@ class FilterHooksTest extends BrowserTestBase {
$edit['title[0][value]'] = $title;
$edit['body[0][value]'] = $this->randomMachineName(32);
$edit['body[0][format]'] = $format_id;
- $this->drupalPostForm("node/add/{$type->id()}", $edit, t('Save and publish'));
+ $this->drupalPostForm("node/add/{$type->id()}", $edit, t('Save'));
$this->assertText(t('@type @title has been created.', ['@type' => $type_name, '@title' => $title]));
// Disable the text format.
diff --git a/core/modules/forum/config/optional/core.entity_form_display.node.forum.default.yml b/core/modules/forum/config/optional/core.entity_form_display.node.forum.default.yml
index 6773d32..965a691 100644
--- a/core/modules/forum/config/optional/core.entity_form_display.node.forum.default.yml
+++ b/core/modules/forum/config/optional/core.entity_form_display.node.forum.default.yml
@@ -42,6 +42,13 @@ content:
weight: 15
region: content
third_party_settings: { }
+ status:
+ type: boolean_checkbox
+ settings:
+ display_label: true
+ weight: 120
+ region: content
+ third_party_settings: { }
sticky:
type: boolean_checkbox
settings:
diff --git a/core/modules/forum/tests/src/Functional/ForumBlockTest.php b/core/modules/forum/tests/src/Functional/ForumBlockTest.php
index 6c412b7..2914e94 100644
--- a/core/modules/forum/tests/src/Functional/ForumBlockTest.php
+++ b/core/modules/forum/tests/src/Functional/ForumBlockTest.php
@@ -170,7 +170,7 @@ class ForumBlockTest extends BrowserTestBase {
];
// Create the forum topic, preselecting the forum ID via a URL parameter.
- $this->drupalPostForm('node/add/forum', $edit, t('Save and publish'), ['query' => ['forum_id' => 1]]);
+ $this->drupalPostForm('node/add/forum', $edit, t('Save'), ['query' => ['forum_id' => 1]]);
$topics[] = $title;
}
diff --git a/core/modules/forum/tests/src/Functional/ForumIndexTest.php b/core/modules/forum/tests/src/Functional/ForumIndexTest.php
index 53c8364..38adb72 100644
--- a/core/modules/forum/tests/src/Functional/ForumIndexTest.php
+++ b/core/modules/forum/tests/src/Functional/ForumIndexTest.php
@@ -44,7 +44,7 @@ class ForumIndexTest extends BrowserTestBase {
$this->drupalGet("forum/$tid");
$this->clickLink(t('Add new @node_type', ['@node_type' => 'Forum topic']));
$this->assertUrl('node/add/forum', ['query' => ['forum_id' => $tid]]);
- $this->drupalPostForm(NULL, $edit, t('Save and publish'));
+ $this->drupalPostForm(NULL, $edit, t('Save'));
// Check that the node exists in the database.
$node = $this->drupalGetNodeByTitle($title);
@@ -71,7 +71,8 @@ class ForumIndexTest extends BrowserTestBase {
// Unpublish the node.
- $this->drupalPostForm('node/' . $node->id() . '/edit', [], t('Save and unpublish'));
+ $edit = ['status[value]' => FALSE];
+ $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
$this->drupalGet('node/' . $node->id());
$this->assertText(t('Access denied'), 'Unpublished node is no longer accessible.');
diff --git a/core/modules/image/src/Tests/ImageFieldDisplayTest.php b/core/modules/image/src/Tests/ImageFieldDisplayTest.php
index 9c105c1..8061526 100644
--- a/core/modules/image/src/Tests/ImageFieldDisplayTest.php
+++ b/core/modules/image/src/Tests/ImageFieldDisplayTest.php
@@ -282,7 +282,7 @@ class ImageFieldDisplayTest extends ImageFieldTestBase {
$field_name . '[0][alt]' => $image['#alt'],
$field_name . '[0][title]' => $image['#title'],
];
- $this->drupalPostForm('node/' . $nid . '/edit', $edit, t('Save and keep published'));
+ $this->drupalPostForm('node/' . $nid . '/edit', $edit, t('Save'));
$default_output = str_replace("\n", NULL, $renderer->renderRoot($image));
$this->assertRaw($default_output, 'Image displayed using user supplied alt and title attributes.');
@@ -292,7 +292,7 @@ class ImageFieldDisplayTest extends ImageFieldTestBase {
$field_name . '[0][alt]' => $this->randomMachineName($test_size),
$field_name . '[0][title]' => $this->randomMachineName($test_size),
];
- $this->drupalPostForm('node/' . $nid . '/edit', $edit, t('Save and keep published'));
+ $this->drupalPostForm('node/' . $nid . '/edit', $edit, t('Save'));
$schema = $field->getFieldStorageDefinition()->getSchema();
$this->assertRaw(t('Alternative text cannot be longer than %max characters but is currently %length characters long.', [
'%max' => $schema['columns']['alt']['length'],
@@ -314,9 +314,9 @@ class ImageFieldDisplayTest extends ImageFieldTestBase {
$edit = [
'files[' . $field_name . '_1][]' => drupal_realpath($test_image->uri),
];
- $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save and keep published'));
+ $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
// Add the required alt text.
- $this->drupalPostForm(NULL, [$field_name . '[1][alt]' => $alt], t('Save and keep published'));
+ $this->drupalPostForm(NULL, [$field_name . '[1][alt]' => $alt], t('Save'));
$this->assertText(format_string('Article @title has been updated.', ['@title' => $node->getTitle()]));
// Assert ImageWidget::process() calls FieldWidget::process().
diff --git a/core/modules/image/src/Tests/ImageFieldTestBase.php b/core/modules/image/src/Tests/ImageFieldTestBase.php
index eec7f7b..c023917 100644
--- a/core/modules/image/src/Tests/ImageFieldTestBase.php
+++ b/core/modules/image/src/Tests/ImageFieldTestBase.php
@@ -90,10 +90,10 @@ abstract class ImageFieldTestBase extends WebTestBase {
'title[0][value]' => $this->randomMachineName(),
];
$edit['files[' . $field_name . '_0]'] = drupal_realpath($image->uri);
- $this->drupalPostForm('node/add/' . $type, $edit, t('Save and publish'));
+ $this->drupalPostForm('node/add/' . $type, $edit, t('Save'));
if ($alt) {
// Add alt text.
- $this->drupalPostForm(NULL, [$field_name . '[0][alt]' => $alt], t('Save and publish'));
+ $this->drupalPostForm(NULL, [$field_name . '[0][alt]' => $alt], t('Save'));
}
// Retrieve ID of the newly created node from the current URL.
diff --git a/core/modules/image/src/Tests/ImageFieldValidateTest.php b/core/modules/image/src/Tests/ImageFieldValidateTest.php
index 7e5ee9b..e429d0b 100644
--- a/core/modules/image/src/Tests/ImageFieldValidateTest.php
+++ b/core/modules/image/src/Tests/ImageFieldValidateTest.php
@@ -119,7 +119,7 @@ class ImageFieldValidateTest extends ImageFieldTestBase {
$edit = [
'title[0][value]' => $this->randomMachineName(),
];
- $this->drupalPostForm('node/add/article', $edit, t('Save and publish'));
+ $this->drupalPostForm('node/add/article', $edit, t('Save'));
$this->assertNoText(t('Alternative text field is required.'));
$this->assertNoText(t('Title field is required.'));
@@ -132,7 +132,7 @@ class ImageFieldValidateTest extends ImageFieldTestBase {
$edit = [
'title[0][value]' => $this->randomMachineName(),
];
- $this->drupalPostForm('node/add/article', $edit, t('Save and publish'));
+ $this->drupalPostForm('node/add/article', $edit, t('Save'));
$this->assertNoText(t('Alternative text field is required.'));
$this->assertNoText(t('Title field is required.'));
diff --git a/core/modules/inline_form_errors/tests/src/FunctionalJavascript/FormErrorHandlerCKEditorTest.php b/core/modules/inline_form_errors/tests/src/FunctionalJavascript/FormErrorHandlerCKEditorTest.php
index bce4ff8..2007e1e 100644
--- a/core/modules/inline_form_errors/tests/src/FunctionalJavascript/FormErrorHandlerCKEditorTest.php
+++ b/core/modules/inline_form_errors/tests/src/FunctionalJavascript/FormErrorHandlerCKEditorTest.php
@@ -85,7 +85,7 @@ class FormErrorHandlerCKEditorTest extends JavascriptTestBase {
// Only enter a title in the node add form and leave the body field empty.
$edit = ['edit-title-0-value' => 'Test inline form error with CKEditor'];
- $this->submitForm($edit, 'Save and publish');
+ $this->submitForm($edit, 'Save');
// Add a bottom margin to the title field to be sure the body field is not
// visible. PhantomJS runs with a resolution of 1024x768px.
diff --git a/core/modules/language/tests/src/Functional/LanguageNegotiationUrlTest.php b/core/modules/language/tests/src/Functional/LanguageNegotiationUrlTest.php
index 8472ff7..07ae85d 100644
--- a/core/modules/language/tests/src/Functional/LanguageNegotiationUrlTest.php
+++ b/core/modules/language/tests/src/Functional/LanguageNegotiationUrlTest.php
@@ -70,7 +70,7 @@ class LanguageNegotiationUrlTest extends BrowserTestBase {
'title[0][value]' => 'Test',
'path[0][alias]' => '/eng/test',
];
- $this->drupalPostForm('node/add/article', $nodeValues, $this->t('Save and publish'));
+ $this->drupalPostForm('node/add/article', $nodeValues, $this->t('Save'));
$this->assertSession()->statusCodeEquals(200);
}
diff --git a/core/modules/menu_ui/src/Tests/MenuNodeTest.php b/core/modules/menu_ui/src/Tests/MenuNodeTest.php
index 3f1bba0..9990095 100644
--- a/core/modules/menu_ui/src/Tests/MenuNodeTest.php
+++ b/core/modules/menu_ui/src/Tests/MenuNodeTest.php
@@ -134,8 +134,7 @@ class MenuNodeTest extends WebTestBase {
$this->drupalGet('test-page');
$this->assertNoLink($node_title);
- // Use not only the save button, but also the two special buttons:
- // 'Save and publish' as well as 'Save and keep published'.
+ // Make sure the menu links only appear when the node is published.
// These buttons just appear for 'administer nodes' users.
$admin_user = $this->drupalCreateUser([
'access administration pages',
@@ -146,21 +145,20 @@ class MenuNodeTest extends WebTestBase {
'edit any page content',
]);
$this->drupalLogin($admin_user);
- foreach (['Save and unpublish' => FALSE, 'Save and keep unpublished' => FALSE, 'Save and publish' => TRUE, 'Save and keep published' => TRUE] as $submit => $visible) {
- $edit = [
- 'menu[enabled]' => 1,
- 'menu[title]' => $node_title,
- ];
- $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, $submit);
- // Assert that the link exists.
- $this->drupalGet('test-page');
- if ($visible) {
- $this->assertLink($node_title, 0, 'Found a menu link after submitted with ' . $submit);
- }
- else {
- $this->assertNoLink($node_title, 'Found no menu link after submitted with ' . $submit);
- }
- }
+ // Assert that the link does not exist if unpublished.
+ $edit = [
+ 'menu[enabled]' => 1,
+ 'menu[title]' => $node_title,
+ 'status[value]' => FALSE,
+ ];
+ $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, 'Save');
+ $this->drupalGet('test-page');
+ $this->assertNoLink($node_title, 'Found no menu link with the node unpublished');
+ // Assert that the link exists if published.
+ $edit['status[value]'] = TRUE;
+ $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, 'Save');
+ $this->drupalGet('test-page');
+ $this->assertLink($node_title, 0, 'Found a menu link with the node published');
// Log back in as normal user.
$this->drupalLogin($this->editor);
diff --git a/core/modules/node/node.post_update.php b/core/modules/node/node.post_update.php
new file mode 100644
index 0000000..43e3cd6
--- /dev/null
+++ b/core/modules/node/node.post_update.php
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * @file
+ * Post update functions for Node.
+ */
+
+use Drupal\Core\Entity\Entity\EntityFormDisplay;
+
+/**
+* Load all form displays for nodes, add status with these settings, save.
+*/
+function node_post_update_configure_status_field_widget() {
+ $query = \Drupal::entityQuery('entity_form_display')->condition('targetEntityType', 'node');
+ $ids = $query->execute();
+ $form_displays = EntityFormDisplay::loadMultiple($ids);
+
+ // Assign status settings for each 'node' target entity types with 'default'
+ // form mode.
+ foreach ($form_displays as $id => $form_display) {
+ /** @var \Drupal\Core\Entity\Display\EntityDisplayInterface $form_display */
+ $form_display->setComponent('status', [
+ 'type' => 'boolean_checkbox',
+ 'settings' => [
+ 'display_label' => TRUE,
+ ],
+ ])->save();
+ }
+}
diff --git a/core/modules/node/src/Entity/Node.php b/core/modules/node/src/Entity/Node.php
index 74a7c0b..e5544a2 100644
--- a/core/modules/node/src/Entity/Node.php
+++ b/core/modules/node/src/Entity/Node.php
@@ -340,6 +340,16 @@ class Node extends EditorialContentEntityBase implements NodeInterface {
])
->setDisplayConfigurable('form', TRUE);
+ $fields['status']
+ ->setDisplayOptions('form', [
+ 'type' => 'boolean_checkbox',
+ 'settings' => [
+ 'display_label' => TRUE,
+ ],
+ 'weight' => 120,
+ ])
+ ->setDisplayConfigurable('form', TRUE);
+
$fields['created'] = BaseFieldDefinition::create('created')
->setLabel(t('Authored on'))
->setDescription(t('The time that the node was created.'))
diff --git a/core/modules/node/src/NodeForm.php b/core/modules/node/src/NodeForm.php
index 203c2e6..007b7c6 100644
--- a/core/modules/node/src/NodeForm.php
+++ b/core/modules/node/src/NodeForm.php
@@ -86,7 +86,10 @@ class NodeForm extends ContentEntityForm {
$node = $this->entity;
if ($this->operation == 'edit') {
- $form['#title'] = $this->t('<em>Edit @type</em> @title', ['@type' => node_get_type_label($node), '@title' => $node->label()]);
+ $form['#title'] = $this->t('<em>Edit @type</em> @title', [
+ '@type' => node_get_type_label($node),
+ '@title' => $node->label()
+ ]);
}
// Changed must be sent to the client, for later overwrite error checking.
@@ -99,6 +102,15 @@ class NodeForm extends ContentEntityForm {
$form['advanced']['#attributes']['class'][] = 'entity-meta';
+ $form['footer'] = [
+ '#type' => 'container',
+ '#weight' => 99,
+ '#attributes' => [
+ 'class' => ['node-form-footer']
+ ]
+ ];
+ $form['status']['#group'] = 'footer';
+
// Node author information for administrators.
$form['author'] = [
'#type' => 'details',
@@ -147,8 +159,6 @@ class NodeForm extends ContentEntityForm {
$form['#attached']['library'][] = 'node/form';
- $form['#entity_builders']['update_status'] = '::updateStatus';
-
return $form;
}
@@ -165,6 +175,9 @@ class NodeForm extends ContentEntityForm {
* The current state of the form.
*
* @see \Drupal\node\NodeForm::form()
+ *
+ * @deprecated in Drupal 8.4.x, will be removed before Drupal 9.0.0.
+ * The "Publish" button was removed.
*/
public function updateStatus($entity_type_id, NodeInterface $node, array $form, FormStateInterface $form_state) {
$element = $form_state->getTriggeringElement();
@@ -183,59 +196,6 @@ class NodeForm extends ContentEntityForm {
$element['submit']['#access'] = $preview_mode != DRUPAL_REQUIRED || $form_state->get('has_been_previewed');
- // If saving is an option, privileged users get dedicated form submit
- // buttons to adjust the publishing status while saving in one go.
- // @todo This adjustment makes it close to impossible for contributed
- // modules to integrate with "the Save operation" of this form. Modules
- // need a way to plug themselves into 1) the ::submit() step, and
- // 2) the ::save() step, both decoupled from the pressed form button.
- if ($element['submit']['#access'] && \Drupal::currentUser()->hasPermission('administer nodes')) {
- // isNew | prev status » default & publish label & unpublish label
- // 1 | 1 » publish & Save and publish & Save as unpublished
- // 1 | 0 » unpublish & Save and publish & Save as unpublished
- // 0 | 1 » publish & Save and keep published & Save and unpublish
- // 0 | 0 » unpublish & Save and keep unpublished & Save and publish
-
- // 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');
- }
- else {
- $element['publish']['#value'] = $node->isPublished() ? t('Save and keep published') : t('Save and publish');
- }
- $element['publish']['#weight'] = 0;
-
- // 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');
- }
- else {
- $element['unpublish']['#value'] = !$node->isPublished() ? t('Save and keep unpublished') : t('Save and unpublish');
- }
- $element['unpublish']['#weight'] = 10;
-
- // If already published, the 'publish' button is primary.
- if ($node->isPublished()) {
- unset($element['unpublish']['#button_type']);
- }
- // Otherwise, the 'unpublish' button is primary and should come first.
- else {
- unset($element['publish']['#button_type']);
- $element['unpublish']['#weight'] = -10;
- }
-
- // Remove the "Save" button.
- $element['submit']['#access'] = FALSE;
- }
-
$element['preview'] = [
'#type' => 'submit',
'#access' => $preview_mode != DRUPAL_DISABLED && ($node->access('create') || $node->access('update')),
diff --git a/core/modules/node/src/Tests/Update/NodeUpdateTest.php b/core/modules/node/src/Tests/Update/NodeUpdateTest.php
index b8b30be..b193ea8 100644
--- a/core/modules/node/src/Tests/Update/NodeUpdateTest.php
+++ b/core/modules/node/src/Tests/Update/NodeUpdateTest.php
@@ -2,6 +2,7 @@
namespace Drupal\node\Tests\Update;
+use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\system\Tests\Update\UpdatePathTestBase;
/**
@@ -38,4 +39,29 @@ class NodeUpdateTest extends UpdatePathTestBase {
$this->assertEqual('status', $entity_type->getKey('published'));
}
+ /**
+ * Tests that the node entity form has the status checkbox.
+ *
+ * @see node_post_update_configure_status_field_widget()
+ */
+ public function testStatusCheckbox() {
+ // Run updates.
+ $this->runUpdates();
+
+ $query = \Drupal::entityQuery('entity_form_display')
+ ->condition('targetEntityType', 'node');
+ $ids = $query->execute();
+ $form_displays = EntityFormDisplay::loadMultiple($ids);
+
+ /**
+ * @var string $id
+ * @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display
+ */
+ foreach ($form_displays as $id => $form_display) {
+ $component = $form_display->getComponent('status');
+ $this->assertEqual('boolean_checkbox', $component['type']);
+ $this->assertEqual(['display_label' => TRUE], $component['settings']);
+ }
+ }
+
}
diff --git a/core/modules/node/tests/src/Functional/NodeEditFormTest.php b/core/modules/node/tests/src/Functional/NodeEditFormTest.php
index ea2a168..ce032e5 100644
--- a/core/modules/node/tests/src/Functional/NodeEditFormTest.php
+++ b/core/modules/node/tests/src/Functional/NodeEditFormTest.php
@@ -98,7 +98,7 @@ class NodeEditFormTest extends NodeTestBase {
$edit['title[0][value]'] = $this->randomMachineName(8);
$edit[$body_key] = $this->randomMachineName(16);
$edit['revision'] = TRUE;
- $this->drupalPostForm(NULL, $edit, t('Save and keep published'));
+ $this->drupalPostForm(NULL, $edit, t('Save'));
// Ensure that the node revision has been created.
$revised_node = $this->drupalGetNodeByTitle($edit['title[0][value]'], TRUE);
@@ -124,12 +124,21 @@ class NodeEditFormTest extends NodeTestBase {
$edit['created[0][value][date]'] = $this->randomMachineName(8);
// Get the current amount of open details elements.
$open_details_elements = count($this->cssSelect('details[open="open"]'));
- $this->drupalPostForm(NULL, $edit, t('Save and keep published'));
+ $this->drupalPostForm(NULL, $edit, t('Save'));
// The node author details must be open.
$this->assertRaw('<details class="node-form-author js-form-wrapper form-wrapper" data-drupal-selector="edit-author" id="edit-author" open="open">');
// Only one extra details element should now be open.
$open_details_elements++;
$this->assertEqual(count($this->cssSelect('details[open="open"]')), $open_details_elements, 'Exactly one extra open &lt;details&gt; element found.');
+
+ // Edit the same node, save it and verify it's unpublished after unchecking
+ // the 'Published' boolean_checkbox and clicking 'Save'.
+ $this->drupalGet("node/" . $node->id() . "/edit");
+ $edit = ['status[value]' => FALSE];
+ $this->drupalPostForm(NULL, $edit, t('Save'));
+ $this->nodeStorage->resetCache([$node->id()]);
+ $node = $this->nodeStorage->load($node->id());
+ $this->assertFalse($node->isPublished(), 'Node is unpublished');
}
/**
@@ -143,7 +152,7 @@ class NodeEditFormTest extends NodeTestBase {
$edit = [];
$edit['title[0][value]'] = $this->randomMachineName(8);
$edit[$body_key] = $this->randomMachineName(16);
- $this->drupalPostForm('node/add/page', $edit, t('Save and publish'));
+ $this->drupalPostForm('node/add/page', $edit, t('Save'));
// Check that the node was authored by the currently logged in user.
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
@@ -172,7 +181,7 @@ class NodeEditFormTest extends NodeTestBase {
$this->drupalLogin($this->adminUser);
// Save the node without making any changes.
- $this->drupalPostForm('node/' . $node->id() . '/edit', [], t('Save and keep published'));
+ $this->drupalPostForm('node/' . $node->id() . '/edit', [], t('Save'));
$this->nodeStorage->resetCache([$node->id()]);
$node = $this->nodeStorage->load($node->id());
$this->assertIdentical($this->webUser->id(), $node->getOwner()->id());
@@ -184,7 +193,7 @@ class NodeEditFormTest extends NodeTestBase {
// Check that saving the node without making any changes keeps the proper
// author ID.
- $this->drupalPostForm('node/' . $node->id() . '/edit', [], t('Save and keep published'));
+ $this->drupalPostForm('node/' . $node->id() . '/edit', [], t('Save'));
$this->nodeStorage->resetCache([$node->id()]);
$node = $this->nodeStorage->load($node->id());
$this->assertIdentical($this->webUser->id(), $node->getOwner()->id());
@@ -203,13 +212,13 @@ class NodeEditFormTest extends NodeTestBase {
$edit = [
$form_element_name => 'invalid-name',
];
- $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save and keep published'));
+ $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
$this->assertRaw(t('There are no entities matching "%name".', ['%name' => 'invalid-name']));
// Change the authored by field to an empty string, which should assign
// authorship to the anonymous user (uid 0).
$edit[$form_element_name] = '';
- $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save and keep published'));
+ $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
$this->nodeStorage->resetCache([$node->id()]);
$node = $this->nodeStorage->load($node->id());
$uid = $node->getOwnerId();
@@ -228,7 +237,7 @@ class NodeEditFormTest extends NodeTestBase {
// Change the authored by field to another user's name (that is not
// logged in).
$edit[$form_element_name] = $this->webUser->getUsername();
- $this->drupalPostForm(NULL, $edit, t('Save and keep published'));
+ $this->drupalPostForm(NULL, $edit, t('Save'));
$this->nodeStorage->resetCache([$node->id()]);
$node = $this->nodeStorage->load($node->id());
$this->assertIdentical($node->getOwnerId(), $this->webUser->id(), 'Node authored by normal user.');
diff --git a/core/modules/node/tests/src/Functional/NodeFormButtonsTest.php b/core/modules/node/tests/src/Functional/NodeFormButtonsTest.php
deleted file mode 100644
index 546a633..0000000
--- a/core/modules/node/tests/src/Functional/NodeFormButtonsTest.php
+++ /dev/null
@@ -1,135 +0,0 @@
-<?php
-
-namespace Drupal\Tests\node\Functional;
-
-/**
- * Tests all the different buttons on the node form.
- *
- * @group node
- */
-class NodeFormButtonsTest extends NodeTestBase {
-
- use AssertButtonsTrait;
-
- /**
- * A normal logged in user.
- *
- * @var \Drupal\user\UserInterface
- */
- protected $webUser;
-
- /**
- * A user with permission to bypass access content.
- *
- * @var \Drupal\user\UserInterface
- */
- protected $adminUser;
-
- protected function setUp() {
- parent::setUp();
-
- // Create a user that has no access to change the state of the node.
- $this->webUser = $this->drupalCreateUser(['create article content', 'edit own article content']);
- // Create a user that has access to change the state of the node.
- $this->adminUser = $this->drupalCreateUser(['administer nodes', 'bypass node access']);
- }
-
- /**
- * Tests that the right buttons are displayed for saving nodes.
- */
- public function testNodeFormButtons() {
- $node_storage = $this->container->get('entity.manager')->getStorage('node');
- // Log in as administrative user.
- $this->drupalLogin($this->adminUser);
-
- // Verify the buttons on a node add form.
- $this->drupalGet('node/add/article');
- $this->assertButtons([t('Save and publish'), t('Save as unpublished')]);
-
- // Save the node and assert it's published after clicking
- // 'Save and publish'.
- $edit = ['title[0][value]' => $this->randomString()];
- $this->drupalPostForm('node/add/article', $edit, t('Save and publish'));
-
- // Get the node.
- $node_1 = $node_storage->load(1);
- $this->assertTrue($node_1->isPublished(), 'Node is published');
-
- // Verify the buttons on a node edit form.
- $this->drupalGet('node/' . $node_1->id() . '/edit');
- $this->assertButtons([t('Save and keep published'), t('Save and unpublish')]);
-
- // Save the node and verify it's still published after clicking
- // 'Save and keep published'.
- $this->drupalPostForm(NULL, $edit, t('Save and keep published'));
- $node_storage->resetCache([1]);
- $node_1 = $node_storage->load(1);
- $this->assertTrue($node_1->isPublished(), 'Node is published');
-
- // Save the node and verify it's unpublished after clicking
- // 'Save and unpublish'.
- $this->drupalPostForm('node/' . $node_1->id() . '/edit', $edit, t('Save and unpublish'));
- $node_storage->resetCache([1]);
- $node_1 = $node_storage->load(1);
- $this->assertFalse($node_1->isPublished(), 'Node is unpublished');
-
- // Verify the buttons on an unpublished node edit screen.
- $this->drupalGet('node/' . $node_1->id() . '/edit');
- $this->assertButtons([t('Save and keep unpublished'), t('Save and publish')]);
-
- // Create a node as a normal user.
- $this->drupalLogout();
- $this->drupalLogin($this->webUser);
-
- // Verify the buttons for a normal user.
- $this->drupalGet('node/add/article');
- $this->assertButtons([t('Save')], FALSE);
-
- // Create the node.
- $edit = ['title[0][value]' => $this->randomString()];
- $this->drupalPostForm('node/add/article', $edit, t('Save'));
- $node_2 = $node_storage->load(2);
- $this->assertTrue($node_2->isPublished(), 'Node is published');
-
- // Log in as an administrator and unpublish the node that just
- // was created by the normal user.
- $this->drupalLogout();
- $this->drupalLogin($this->adminUser);
- $this->drupalPostForm('node/' . $node_2->id() . '/edit', [], t('Save and unpublish'));
- $node_storage->resetCache([2]);
- $node_2 = $node_storage->load(2);
- $this->assertFalse($node_2->isPublished(), 'Node is unpublished');
-
- // Log in again as the normal user, save the node and verify
- // it's still unpublished.
- $this->drupalLogout();
- $this->drupalLogin($this->webUser);
- $this->drupalPostForm('node/' . $node_2->id() . '/edit', [], t('Save'));
- $node_storage->resetCache([2]);
- $node_2 = $node_storage->load(2);
- $this->assertFalse($node_2->isPublished(), 'Node is still unpublished');
- $this->drupalLogout();
-
- // Set article content type default to unpublished. This will change the
- // the initial order of buttons and/or status of the node when creating
- // a node.
- $fields = \Drupal::entityManager()->getFieldDefinitions('node', 'article');
- $fields['status']->getConfig('article')
- ->setDefaultValue(FALSE)
- ->save();
-
- // Verify the buttons on a node add form for an administrator.
- $this->drupalLogin($this->adminUser);
- $this->drupalGet('node/add/article');
- $this->assertButtons([t('Save as unpublished'), t('Save and publish')]);
-
- // Verify the node is unpublished by default for a normal user.
- $this->drupalLogout();
- $this->drupalLogin($this->webUser);
- $edit = ['title[0][value]' => $this->randomString()];
- $this->drupalPostForm('node/add/article', $edit, t('Save'));
- $node_3 = $node_storage->load(3);
- $this->assertFalse($node_3->isPublished(), 'Node is unpublished');
- }
-
-}
diff --git a/core/modules/node/tests/src/Functional/NodeRevisionsUiBypassAccessTest.php b/core/modules/node/tests/src/Functional/NodeRevisionsUiBypassAccessTest.php
index a3a575d..186b694 100644
--- a/core/modules/node/tests/src/Functional/NodeRevisionsUiBypassAccessTest.php
+++ b/core/modules/node/tests/src/Functional/NodeRevisionsUiBypassAccessTest.php
@@ -67,7 +67,7 @@ class NodeRevisionsUiBypassAccessTest extends NodeTestBase {
// Uncheck the create new revision checkbox and save the node.
$edit = ['revision' => FALSE];
- $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, 'Save and keep published');
+ $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, 'Save');
$this->assertUrl($node->toUrl());
$this->assertNoLink(t('Revisions'));
@@ -78,7 +78,7 @@ class NodeRevisionsUiBypassAccessTest extends NodeTestBase {
// Submit the form without changing the checkbox.
$edit = [];
- $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, 'Save and keep published');
+ $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, 'Save');
$this->assertUrl($node->toUrl());
$this->assertLink(t('Revisions'));
diff --git a/core/modules/node/tests/src/Functional/NodeRevisionsUiTest.php b/core/modules/node/tests/src/Functional/NodeRevisionsUiTest.php
index cc364cd..a437e4a 100644
--- a/core/modules/node/tests/src/Functional/NodeRevisionsUiTest.php
+++ b/core/modules/node/tests/src/Functional/NodeRevisionsUiTest.php
@@ -55,7 +55,7 @@ class NodeRevisionsUiTest extends NodeTestBase {
// Uncheck the create new revision checkbox and save the node.
$edit = ['revision' => FALSE];
- $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save and keep published'));
+ $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
// Load the node again and check the revision is the same as before.
$node_storage->resetCache([$node->id()]);
@@ -68,7 +68,7 @@ class NodeRevisionsUiTest extends NodeTestBase {
// Submit the form without changing the checkbox.
$edit = [];
- $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save and keep published'));
+ $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
// Load the node again and check the revision is different from before.
$node_storage->resetCache([$node->id()]);
diff --git a/core/modules/node/tests/src/Functional/NodeTranslationUITest.php b/core/modules/node/tests/src/Functional/NodeTranslationUITest.php
index a8a7454..a44bea1 100644
--- a/core/modules/node/tests/src/Functional/NodeTranslationUITest.php
+++ b/core/modules/node/tests/src/Functional/NodeTranslationUITest.php
@@ -99,7 +99,9 @@ class NodeTranslationUITest extends ContentTranslationUITestBase {
'source' => $default_langcode,
'target' => $langcode
], ['language' => $language]);
- $this->drupalPostForm($add_url, $this->getEditValues($values, $langcode), t('Save and unpublish (this translation)'));
+ $edit = $this->getEditValues($values, $langcode);
+ $edit['status[value]'] = FALSE;
+ $this->drupalPostForm($add_url, $edit, t('Save (this translation)'));
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
@@ -139,18 +141,6 @@ class NodeTranslationUITest extends ContentTranslationUITestBase {
/**
* {@inheritdoc}
*/
- protected function getFormSubmitAction(EntityInterface $entity, $langcode) {
- if ($entity->getTranslation($langcode)->isPublished()) {
- return t('Save and keep published') . $this->getFormSubmitSuffix($entity, $langcode);
- }
- else {
- return t('Save and keep unpublished') . $this->getFormSubmitSuffix($entity, $langcode);
- }
- }
-
- /**
- * {@inheritdoc}
- */
protected function doTestPublishedStatus() {
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
@@ -158,18 +148,18 @@ class NodeTranslationUITest extends ContentTranslationUITestBase {
$entity = $storage->load($this->entityId);
$languages = $this->container->get('language_manager')->getLanguages();
- $actions = [
- t('Save and keep published'),
- t('Save and unpublish'),
+ $statuses = [
+ TRUE,
+ FALSE,
];
- foreach ($actions as $index => $action) {
+ foreach ($statuses as $index => $value) {
// (Un)publish the node translations and check that the translation
// statuses are (un)published accordingly.
foreach ($this->langcodes as $langcode) {
$options = ['language' => $languages[$langcode]];
$url = $entity->urlInfo('edit-form', $options);
- $this->drupalPostForm($url, [], $action . $this->getFormSubmitSuffix($entity, $langcode), $options);
+ $this->drupalPostForm($url, ['status[value]' => $value], t('Save') . $this->getFormSubmitSuffix($entity, $langcode), $options);
}
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
diff --git a/core/modules/options/tests/src/Functional/OptionsFieldUITest.php b/core/modules/options/tests/src/Functional/OptionsFieldUITest.php
index 836740d..e29a1d0 100644
--- a/core/modules/options/tests/src/Functional/OptionsFieldUITest.php
+++ b/core/modules/options/tests/src/Functional/OptionsFieldUITest.php
@@ -328,7 +328,7 @@ class OptionsFieldUITest extends FieldTestBase {
$edit = [
$this->fieldName => '1',
];
- $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save and keep published'));
+ $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
// Check the node page and see if the values are correct.
$file_formatters = ['list_default', 'list_key'];
diff --git a/core/modules/search/src/Tests/SearchConfigSettingsFormTest.php b/core/modules/search/src/Tests/SearchConfigSettingsFormTest.php
index 191e0cf..cc77f2b 100644
--- a/core/modules/search/src/Tests/SearchConfigSettingsFormTest.php
+++ b/core/modules/search/src/Tests/SearchConfigSettingsFormTest.php
@@ -47,7 +47,7 @@ class SearchConfigSettingsFormTest extends SearchTestBase {
// also needs the word "pizza" so we can use it as the search keyword.
$body_key = 'body[0][value]';
$edit[$body_key] = \Drupal::l($node->label(), $node->urlInfo()) . ' pizza sandwich';
- $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save and keep published'));
+ $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
$this->container->get('plugin.manager.search')->createInstance('node_search')->updateIndex();
search_update_totals();
diff --git a/core/modules/system/src/Tests/Menu/BreadcrumbTest.php b/core/modules/system/src/Tests/Menu/BreadcrumbTest.php
index 64c8c15..39303ff 100644
--- a/core/modules/system/src/Tests/Menu/BreadcrumbTest.php
+++ b/core/modules/system/src/Tests/Menu/BreadcrumbTest.php
@@ -206,7 +206,7 @@ class BreadcrumbTest extends MenuTestBase {
$edit = [
'menu[menu_parent]' => $link->getMenuName() . ':' . $link->getPluginId(),
];
- $this->drupalPostForm('node/' . $parent->id() . '/edit', $edit, t('Save and keep published'));
+ $this->drupalPostForm('node/' . $parent->id() . '/edit', $edit, t('Save'));
$expected = [
"node" => $link->getTitle(),
];
@@ -227,7 +227,7 @@ class BreadcrumbTest extends MenuTestBase {
$edit = [
'field_tags[target_id]' => implode(',', array_keys($tags)),
];
- $this->drupalPostForm('node/' . $parent->id() . '/edit', $edit, t('Save and keep published'));
+ $this->drupalPostForm('node/' . $parent->id() . '/edit', $edit, t('Save'));
// Put both terms into a hierarchy Drupal » Breadcrumbs. Required for both
// the menu links and the terms itself, since taxonomy_term_page() resets
diff --git a/core/modules/system/src/Tests/Update/UpdatePathRC1TestBaseFilledTest.php b/core/modules/system/src/Tests/Update/UpdatePathRC1TestBaseFilledTest.php
index 09cc586..9ee6d0c 100644
--- a/core/modules/system/src/Tests/Update/UpdatePathRC1TestBaseFilledTest.php
+++ b/core/modules/system/src/Tests/Update/UpdatePathRC1TestBaseFilledTest.php
@@ -117,7 +117,7 @@ class UpdatePathRC1TestBaseFilledTest extends UpdatePathRC1TestBaseTest {
$this->assertText('Test Article - New title');
$this->assertText('Test 1');
$this->assertRaw('0.01');
- $this->drupalPostForm('node/8/edit', [], 'Save and keep published (this translation)');
+ $this->drupalPostForm('node/8/edit', [], 'Save (this translation)');
$this->assertResponse(200);
$this->drupalGet('node/8/edit', ['language' => $spanish]);
$this->assertText('Test title Spanish');
diff --git a/core/modules/system/src/Tests/Update/UpdatePathTestBaseFilledTest.php b/core/modules/system/src/Tests/Update/UpdatePathTestBaseFilledTest.php
index fe62340..3c5aab0 100644
--- a/core/modules/system/src/Tests/Update/UpdatePathTestBaseFilledTest.php
+++ b/core/modules/system/src/Tests/Update/UpdatePathTestBaseFilledTest.php
@@ -117,7 +117,7 @@ class UpdatePathTestBaseFilledTest extends UpdatePathTestBaseTest {
$this->assertText('Test Article - New title');
$this->assertText('Test 1');
$this->assertRaw('0.01');
- $this->drupalPostForm('node/8/edit', [], 'Save and keep published (this translation)');
+ $this->drupalPostForm('node/8/edit', [], 'Save (this translation)');
$this->assertResponse(200);
$this->drupalGet('node/8/edit', ['language' => $spanish]);
$this->assertText('Test title Spanish');
diff --git a/core/modules/taxonomy/tests/src/Functional/LegacyTest.php b/core/modules/taxonomy/tests/src/Functional/LegacyTest.php
index 2894715..ac8e82d 100644
--- a/core/modules/taxonomy/tests/src/Functional/LegacyTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/LegacyTest.php
@@ -60,7 +60,7 @@ class LegacyTest extends TaxonomyTestBase {
$edit['created[0][value][time]'] = $date->format('H:i:s');
$edit['body[0][value]'] = $this->randomMachineName();
$edit['field_tags[target_id]'] = $this->randomMachineName();
- $this->drupalPostForm('node/add/article', $edit, t('Save and publish'));
+ $this->drupalPostForm('node/add/article', $edit, t('Save'));
// Checks that the node has been saved.
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
$this->assertEqual($node->getCreatedTime(), $date->getTimestamp(), 'Legacy node was saved with the right date.');
diff --git a/core/profiles/standard/config/install/core.entity_form_display.node.article.default.yml b/core/profiles/standard/config/install/core.entity_form_display.node.article.default.yml
index c94e36e..9082f2d 100644
--- a/core/profiles/standard/config/install/core.entity_form_display.node.article.default.yml
+++ b/core/profiles/standard/config/install/core.entity_form_display.node.article.default.yml
@@ -66,6 +66,13 @@ content:
weight: 15
region: content
third_party_settings: { }
+ status:
+ type: boolean_checkbox
+ settings:
+ display_label: true
+ weight: 120
+ region: content
+ third_party_settings: { }
sticky:
type: boolean_checkbox
settings:
diff --git a/core/profiles/standard/config/install/core.entity_form_display.node.page.default.yml b/core/profiles/standard/config/install/core.entity_form_display.node.page.default.yml
index 0b7ffd1..682f1a5 100644
--- a/core/profiles/standard/config/install/core.entity_form_display.node.page.default.yml
+++ b/core/profiles/standard/config/install/core.entity_form_display.node.page.default.yml
@@ -40,6 +40,13 @@ content:
weight: 15
region: content
third_party_settings: { }
+ status:
+ type: boolean_checkbox
+ settings:
+ display_label: true
+ weight: 120
+ region: content
+ third_party_settings: { }
sticky:
type: boolean_checkbox
settings:
diff --git a/core/themes/bartik/css/components/form.css b/core/themes/bartik/css/components/form.css
index 5ff46e4..672e591 100644
--- a/core/themes/bartik/css/components/form.css
+++ b/core/themes/bartik/css/components/form.css
@@ -172,6 +172,14 @@ input.form-submit:focus {
.node-form .form-wrapper {
margin-bottom: 2em;
}
+.node-form .node-form-footer,
+.node-form .field--name-status {
+ margin-bottom: 0;
+}
+.node-form .form-actions {
+ padding-top: 0;
+ margin-top: 0;
+}
/* Contact Form */
.contact-form #edit-name {
diff --git a/core/themes/seven/css/layout/node-add.css b/core/themes/seven/css/layout/node-add.css
index 22faf33..f28a1e5 100644
--- a/core/themes/seven/css/layout/node-add.css
+++ b/core/themes/seven/css/layout/node-add.css
@@ -1,3 +1,9 @@
+.layout-region-node-footer__content {
+ border-top: 1px solid #bebfb9;
+ padding-top: 0.5em;
+ margin-top: 1.5em;
+}
+
/**
* Widescreen
*
@@ -14,4 +20,7 @@
margin-top: 1em;
margin-bottom: 1em;
}
+ .layout-region-node-footer__content {
+ margin-top: 0.5em;
+ }
}
diff --git a/core/themes/seven/templates/node-edit-form.html.twig b/core/themes/seven/templates/node-edit-form.html.twig
new file mode 100644
index 0000000..cf747f9
--- /dev/null
+++ b/core/themes/seven/templates/node-edit-form.html.twig
@@ -0,0 +1,31 @@
+{#
+/**
+ * @file
+ * Theme override for a node edit form.
+ *
+ * Two column template for the node add/edit form.
+ *
+ * This template will be used when a node edit form specifies 'node_edit_form'
+ * as its #theme callback. Otherwise, by default, node add/edit forms will be
+ * themed by form.html.twig.
+ *
+ * Available variables:
+ * - form: The node add/edit form.
+ *
+ * @see seven_form_node_form_alter()
+ */
+#}
+<div class="layout-node-form clearfix">
+ <div class="layout-region layout-region-node-main">
+ {{ form|without('advanced', 'footer', 'actions') }}
+ </div>
+ <div class="layout-region layout-region-node-secondary">
+ {{ form.advanced }}
+ </div>
+ <div class="layout-region layout-region-node-footer">
+ <div class="layout-region-node-footer__content">
+ {{ form.footer }}
+ {{ form.actions }}
+ </div>
+ </div>
+</div>