summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwebchick2014-08-23 19:03:35 (GMT)
committerwebchick2014-08-23 19:03:35 (GMT)
commitd72c0f98ec41bd2dfe43e5bf7c0e0821cd8bee6f (patch)
treef82907fd2d095895190673fc7fbc0a8def599f7a
parent7fde4cf6e6d408043a178339c5bd31d3c6413782 (diff)
Issue #1510544 by swentel, Bojhan, Gábor Hojtsy, merlinofchaos, Cottser, Wim Leers, plopesc, aspilicious, sannejanssen, larowlan, tim.plunkett, nod_: Fixed Show previews in front-end theme, able to select different view modes.
-rw-r--r--core/misc/icons/000000/chevron-left.svg1
-rw-r--r--core/modules/block/src/Tests/BlockConfigSchemaTest.php1
-rw-r--r--core/modules/file/src/Tests/FileFieldDisplayTest.php1
-rw-r--r--core/modules/filter/src/Tests/FilterFormatAccessTest.php2
-rw-r--r--core/modules/node/css/node.preview.css17
-rw-r--r--core/modules/node/node.libraries.yml4
-rw-r--r--core/modules/node/node.module31
-rw-r--r--core/modules/node/node.pages.inc65
-rw-r--r--core/modules/node/node.preview.js20
-rw-r--r--core/modules/node/node.routing.yml13
-rw-r--r--core/modules/node/node.services.yml10
-rw-r--r--core/modules/node/src/Access/NodePreviewAccessCheck.php58
-rw-r--r--core/modules/node/src/Controller/NodePreviewController.php73
-rw-r--r--core/modules/node/src/Form/NodePreviewForm.php169
-rw-r--r--core/modules/node/src/NodeForm.php76
-rw-r--r--core/modules/node/src/ParamConverter/NodePreviewConverter.php58
-rw-r--r--core/modules/node/src/Tests/PagePreviewTest.php32
-rw-r--r--core/modules/node/templates/node-preview.html.twig23
-rw-r--r--core/modules/node/templates/node.html.twig1
-rw-r--r--core/modules/system/css/system.theme.css3
-rw-r--r--core/modules/system/src/Tests/Database/RegressionTest.php2
-rw-r--r--core/modules/system/src/Tests/Entity/EntityTypedDataDefinitionTest.php2
-rw-r--r--core/modules/taxonomy/src/Tests/TermTest.php6
-rw-r--r--core/themes/bartik/css/style.css42
24 files changed, 580 insertions, 130 deletions
diff --git a/core/misc/icons/000000/chevron-left.svg b/core/misc/icons/000000/chevron-left.svg
new file mode 100644
index 0000000..19ba580
--- /dev/null
+++ b/core/misc/icons/000000/chevron-left.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#000000" d="M7.951 7.645c-.193.196-.193.516 0 .71l3.258 3.29c.193.193.191.519-.002.709l-1.371 1.371c-.193.192-.512.191-.707 0l-5.335-5.371c-.194-.194-.194-.514 0-.708l5.335-5.369c.195-.195.514-.195.707-.001l1.371 1.371c.193.194.195.513.002.709l-3.258 3.289z"/></svg>
diff --git a/core/modules/block/src/Tests/BlockConfigSchemaTest.php b/core/modules/block/src/Tests/BlockConfigSchemaTest.php
index 9065fa4..47e2421 100644
--- a/core/modules/block/src/Tests/BlockConfigSchemaTest.php
+++ b/core/modules/block/src/Tests/BlockConfigSchemaTest.php
@@ -35,6 +35,7 @@ class BlockConfigSchemaTest extends KernelTestBase {
// BlockManager->getModuleName() calls system_get_info().
'system',
'taxonomy',
+ 'user',
);
/**
diff --git a/core/modules/file/src/Tests/FileFieldDisplayTest.php b/core/modules/file/src/Tests/FileFieldDisplayTest.php
index 281ad34..6a1798d 100644
--- a/core/modules/file/src/Tests/FileFieldDisplayTest.php
+++ b/core/modules/file/src/Tests/FileFieldDisplayTest.php
@@ -87,6 +87,7 @@ class FileFieldDisplayTest extends FileFieldTestBase {
$edit[$field_name . '[0][display]'] = FALSE;
$edit[$field_name . '[1][display]'] = FALSE;
$this->drupalPostForm("node/$nid/edit", $edit, t('Preview'));
+ $this->clickLink(t('Back to content editing'));
$this->assertRaw($field_name . '[0][display]', 'First file appears as expected.');
$this->assertRaw($field_name . '[1][display]', 'Second file appears as expected.');
}
diff --git a/core/modules/filter/src/Tests/FilterFormatAccessTest.php b/core/modules/filter/src/Tests/FilterFormatAccessTest.php
index dc247b5..1882a26 100644
--- a/core/modules/filter/src/Tests/FilterFormatAccessTest.php
+++ b/core/modules/filter/src/Tests/FilterFormatAccessTest.php
@@ -234,7 +234,7 @@ class FilterFormatAccessTest extends WebTestBase {
$this->assertText($edit[$body_value_key], 'Old body found in preview.');
// Save and verify that only the title was changed.
- $this->drupalPostForm(NULL, $new_edit, t('Save'));
+ $this->drupalPostForm('node/' . $node->id() . '/edit', $new_edit, t('Save'));
$this->assertNoText($edit['title[0][value]'], 'Old title not found.');
$this->assertText($new_edit['title[0][value]'], 'New title found.');
$this->assertText($edit[$body_value_key], 'Old body found.');
diff --git a/core/modules/node/css/node.preview.css b/core/modules/node/css/node.preview.css
new file mode 100644
index 0000000..9aca68a
--- /dev/null
+++ b/core/modules/node/css/node.preview.css
@@ -0,0 +1,17 @@
+/**
+ * @file
+ * Styles for node preview page.
+ */
+
+.node-preview-container {
+ position: fixed;
+ z-index: 499;
+ width: 100%;
+ padding: 10px;
+}
+
+@media only screen and (min-width: 36em) {
+ .node-preview-container .form-type-select {
+ margin-left: 25%;
+ }
+}
diff --git a/core/modules/node/node.libraries.yml b/core/modules/node/node.libraries.yml
index ab1f935..231687e 100644
--- a/core/modules/node/node.libraries.yml
+++ b/core/modules/node/node.libraries.yml
@@ -10,12 +10,16 @@ drupal.node:
drupal.node.preview:
version: VERSION
+ css:
+ theme:
+ css/node.preview.css: {}
js:
node.preview.js: {}
dependencies:
- core/jquery
- core/jquery.once
- core/drupal
+ - core/drupal.form
drupal.content_types:
version: VERSION
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index fefd6d5..d96052d 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -169,11 +169,6 @@ function node_theme() {
'file' => 'node.pages.inc',
'template' => 'node-add-list',
),
- 'node_preview' => array(
- 'variables' => array('node' => NULL),
- 'file' => 'node.pages.inc',
- 'template' => 'node-preview',
- ),
'node_edit_form' => array(
'render element' => 'form',
'template' => 'node-edit-form',
@@ -627,7 +622,10 @@ function template_preprocess_node(&$variables) {
));
$variables['label'] = $variables['elements']['title'];
unset($variables['elements']['title']);
- $variables['page'] = $variables['view_mode'] == 'full' && node_is_page($node);
+ // The 'page' variable is set to TRUE in two occasions:
+ // - The view mode is 'full' and we are on the 'node.view' route.
+ // - The node is in preview and view mode is either 'full' or 'default'.
+ $variables['page'] = ($variables['view_mode'] == 'full' && (node_is_page($node)) || (isset($node->in_preview) && in_array($node->preview_view_mode, array('full', 'default'))));
// Helpful $content variable for templates.
$variables += array('content' => array());
@@ -669,7 +667,7 @@ function template_preprocess_node(&$variables) {
if ($variables['view_mode']) {
$variables['attributes']['class'][] = drupal_html_class('node--view-mode-' . $variables['view_mode']);
}
- if (isset($variables['preview'])) {
+ if (isset($node->preview)) {
$variables['attributes']['class'][] = 'node--preview';
}
}
@@ -1045,6 +1043,25 @@ function node_view_multiple($nodes, $view_mode = 'teaser', $langcode = NULL) {
}
/**
+ * Implements hook_page_build().
+ */
+function node_page_build(&$page) {
+ // Add 'Back to content editing' link on preview page.
+ $route_match = \Drupal::routeMatch();
+ if ($route_match->getRouteName() == 'entity.node.preview') {
+ $page['page_top']['node_preview'] = array(
+ '#type' => 'container',
+ '#attributes' => array(
+ 'class' => array('node-preview-container', 'container-inline')
+ ),
+ );
+
+ $form = \Drupal::formBuilder()->getForm('\Drupal\node\Form\NodePreviewForm', $route_match->getParameter('node_preview'));
+ $page['page_top']['node_preview']['view_mode'] = $form;
+ }
+}
+
+/**
* Implements hook_form_FORM_ID_alter().
*
* Alters the System module's site information settings form to add a global
diff --git a/core/modules/node/node.pages.inc b/core/modules/node/node.pages.inc
index e848f3e..517a5d5 100644
--- a/core/modules/node/node.pages.inc
+++ b/core/modules/node/node.pages.inc
@@ -37,68 +37,3 @@ function template_preprocess_node_add_list(&$variables) {
}
}
}
-
-/**
- * Generates a node preview.
- *
- * @param \Drupal\node\NodeInterface $node
- * The node to preview.
- *
- * @return
- * An HTML-formatted string of a node preview.
- *
- * @see node_form_build_preview()
- */
-function node_preview(NodeInterface $node, FormStateInterface $form_state) {
- if ($node->access('create') || $node->access('update')) {
-
- $node->changed = REQUEST_TIME;
-
- // Display a preview of the node.
- if (!form_get_errors($form_state)) {
- $node->in_preview = TRUE;
- $node_preview = array(
- '#theme' => 'node_preview',
- '#node' => $node,
- );
- $output = drupal_render($node_preview);
- unset($node->in_preview);
- }
-
- return $output;
- }
-}
-
-/**
- * Prepares variables for node preview templates.
- *
- * Default template: node-preview.html.twig.
- *
- * @param array $variables
- * An associative array containing:
- * - node: The node entity which is being previewed.
- *
- * @see NodeForm::preview()
- * @see node_preview()
- */
-function template_preprocess_node_preview(&$variables) {
- $node = $variables['node'];
-
- // Render trimmed teaser version of the post.
- $node_teaser = node_view($node, 'teaser');
- $node_teaser['#attached']['library'][] = 'node/drupal.node.preview';
- $variables['teaser'] = $node_teaser;
- // Render full version of the post.
- $node_full = node_view($node, 'full');
- $variables['full'] = $node_full;
-
- // Display a preview of the teaser only if the content of the teaser is
- // different to the full post.
- if ($variables['teaser'] != $variables['full']) {
- drupal_set_message(t('The trimmed version of your post shows what your post looks like when promoted to the main page or when exported for syndication.<span class="no-js"> You can insert the delimiter "&lt;!--break--&gt;" (without the quotes) to fine-tune where your post gets split.</span>'));
- $variables['preview_teaser'] = TRUE;
- }
- else {
- $variables['preview_teaser'] = FALSE;
- }
-}
diff --git a/core/modules/node/node.preview.js b/core/modules/node/node.preview.js
index f27cbba..9041f24 100644
--- a/core/modules/node/node.preview.js
+++ b/core/modules/node/node.preview.js
@@ -8,16 +8,16 @@
*/
Drupal.behaviors.nodePreviewDestroyLinks = {
attach: function (context) {
- var $preview = $(context).find('.node').once('node-preview');
+ var $preview = $(context).find('.page-node-preview').once('node-preview');
if ($preview.length) {
- $preview.on('click.preview', 'a:not([href^=#])', function (e) {
+ $preview.on('click.preview', 'a:not([href^=#], #edit-backlink, #toolbar-administration a)', function (e) {
e.preventDefault();
});
}
},
detach: function (context, settings, trigger) {
if (trigger === 'unload') {
- var $preview = $(context).find('.node').removeOnce('node-preview');
+ var $preview = $(context).find('.page-node-preview').removeOnce('node-preview');
if ($preview.length) {
$preview.off('click.preview');
}
@@ -25,4 +25,18 @@
}
};
+ /**
+ * Switch view mode.
+ */
+ Drupal.behaviors.nodePreviewSwitchViewMode = {
+ attach: function (context) {
+ var $autosubmit = $(context).find('[data-drupal-autosubmit]').once('autosubmit');
+ if ($autosubmit.length) {
+ $autosubmit.on('formUpdated.preview', function() {
+ $(this.form).trigger('submit');
+ });
+ }
+ }
+ };
+
})(jQuery, Drupal);
diff --git a/core/modules/node/node.routing.yml b/core/modules/node/node.routing.yml
index f9523d4..26a4d07 100644
--- a/core/modules/node/node.routing.yml
+++ b/core/modules/node/node.routing.yml
@@ -1,4 +1,3 @@
-
node.multiple_delete_confirm:
path: '/admin/content/node/delete'
defaults:
@@ -37,6 +36,18 @@ node.add:
options:
_node_operation_route: TRUE
+entity.node.preview:
+ path: '/node/preview/{node_preview}/{view_mode_id}'
+ defaults:
+ _content: '\Drupal\node\Controller\NodePreviewController::view'
+ _title_callback: '\Drupal\node\Controller\NodePreviewController::title'
+ requirements:
+ _node_preview_access: '{node_preview}'
+ options:
+ parameters:
+ node_preview:
+ type: 'node_preview'
+
entity.node.canonical:
path: '/node/{node}'
defaults:
diff --git a/core/modules/node/node.services.yml b/core/modules/node/node.services.yml
index 85d998a..546f47f 100644
--- a/core/modules/node/node.services.yml
+++ b/core/modules/node/node.services.yml
@@ -19,8 +19,18 @@ services:
arguments: ['@entity.manager']
tags:
- { name: access_check, applies_to: _node_add_access }
+ access_check.node.preview:
+ class: Drupal\node\Access\NodePreviewAccessCheck
+ arguments: ['@entity.manager']
+ tags:
+ - { name: access_check, applies_to: _node_preview_access }
node.admin_path.route_subscriber:
class: Drupal\node\EventSubscriber\NodeAdminRouteSubscriber
arguments: ['@config.factory']
tags:
- { name: event_subscriber }
+ node_preview:
+ class: Drupal\node\ParamConverter\NodePreviewConverter
+ arguments: ['@user.tempstore']
+ tags:
+ - { name: paramconverter }
diff --git a/core/modules/node/src/Access/NodePreviewAccessCheck.php b/core/modules/node/src/Access/NodePreviewAccessCheck.php
new file mode 100644
index 0000000..18bbcd1
--- /dev/null
+++ b/core/modules/node/src/Access/NodePreviewAccessCheck.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\node\Access\NodePreviewAccessCheck.
+ */
+
+namespace Drupal\node\Access;
+
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Routing\Access\AccessInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\node\NodeInterface;
+
+/**
+ * Determines access to node previews.
+ */
+class NodePreviewAccessCheck implements AccessInterface {
+
+ /**
+ * The entity manager.
+ *
+ * @var \Drupal\Core\Entity\EntityManagerInterface
+ */
+ protected $entityManager;
+
+ /**
+ * Constructs a EntityCreateAccessCheck object.
+ *
+ * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+ * The entity manager.
+ */
+ public function __construct(EntityManagerInterface $entity_manager) {
+ $this->entityManager = $entity_manager;
+ }
+
+ /**
+ * Checks access to the node preview page.
+ *
+ * @param \Drupal\Core\Session\AccountInterface $account
+ * The currently logged in account.
+ * @param \Drupal\node\NodeInterface $node_preview
+ * The node that is being previewed.
+ *
+ * @return string
+ * A \Drupal\Core\Access\AccessInterface constant value.
+ */
+ public function access(AccountInterface $account, NodeInterface $node_preview) {
+ if ($node_preview->isNew()) {
+ $access_controller = $this->entityManager->getAccessControlHandler('node');
+ return $access_controller->createAccess($node_preview->bundle(), $account) ? static::ALLOW : static::DENY;
+ }
+ else {
+ return $node_preview->access('update', $account) ? static::ALLOW : static::DENY;
+ }
+ }
+
+}
diff --git a/core/modules/node/src/Controller/NodePreviewController.php b/core/modules/node/src/Controller/NodePreviewController.php
new file mode 100644
index 0000000..9fb8d13
--- /dev/null
+++ b/core/modules/node/src/Controller/NodePreviewController.php
@@ -0,0 +1,73 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\node\Controller\NodePreviewController.
+ */
+
+namespace Drupal\node\Controller;
+
+use Drupal\Component\Utility\String;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\Controller\EntityViewController;
+
+/**
+ * Defines a controller to render a single node in preview.
+ */
+class NodePreviewController extends EntityViewController {
+
+ /**
+ * {@inheritdoc}
+ */
+ public function view(EntityInterface $node_preview, $view_mode_id = 'full', $langcode = NULL) {
+ // Do not cache this page.
+ drupal_page_is_cacheable(FALSE);
+
+ $node_preview->preview_view_mode = $view_mode_id;
+ $build = array('nodes' => parent::view($node_preview, $view_mode_id));
+
+ $build['#attached']['library'][] = 'node/drupal.node.preview';
+
+ $build['#title'] = $build['nodes']['#title'];
+ unset($build['nodes']['#title']);
+
+ // Don't render cache previews.
+ unset($build['nodes']['#cache']);
+
+ foreach ($node_preview->uriRelationships() as $rel) {
+ // Set the node path as the canonical URL to prevent duplicate content.
+ $build['#attached']['drupal_add_html_head_link'][] = array(
+ array(
+ 'rel' => $rel,
+ 'href' => $node_preview->url($rel),
+ )
+ , TRUE);
+
+ if ($rel == 'canonical') {
+ // Set the non-aliased canonical path as a default shortlink.
+ $build['#attached']['drupal_add_html_head_link'][] = array(
+ array(
+ 'rel' => 'shortlink',
+ 'href' => $node_preview->url($rel, array('alias' => TRUE)),
+ )
+ , TRUE);
+ }
+ }
+
+ return $build;
+ }
+
+ /**
+ * The _title_callback for the page that renders a single node in preview.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $node_preview
+ * The current node.
+ *
+ * @return string
+ * The page title.
+ */
+ public function title(EntityInterface $node_preview) {
+ return String::checkPlain($this->entityManager->getTranslationFromContext($node_preview)->label());
+ }
+
+}
diff --git a/core/modules/node/src/Form/NodePreviewForm.php b/core/modules/node/src/Form/NodePreviewForm.php
new file mode 100644
index 0000000..4c5bb91
--- /dev/null
+++ b/core/modules/node/src/Form/NodePreviewForm.php
@@ -0,0 +1,169 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\node\Form\NodePreviewForm.
+ */
+
+namespace Drupal\node\Form;
+
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Contains a form for switching the view mode of a node during preview.
+ */
+class NodePreviewForm extends FormBase implements ContainerInjectionInterface {
+
+ /**
+ * The entity manager service.
+ *
+ * @var \Drupal\Core\Entity\EntityManagerInterface
+ */
+ protected $entityManager;
+
+ /**
+ * The config factory.
+ *
+ * @var \Drupal\Core\Config\ConfigFactoryInterface
+ */
+ protected $configFactory;
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container) {
+ return new static($container->get('entity.manager'), $container->get('config.factory'));
+ }
+
+ /**
+ * Constructs a new NodePreviewForm.
+ *
+ * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+ * The entity manager service.
+ * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+ * The configuration factory.
+ */
+ public function __construct(EntityManagerInterface $entity_manager, ConfigFactoryInterface $config_factory) {
+ $this->entityManager = $entity_manager;
+ $this->configFactory = $config_factory;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getFormId() {
+ return 'node_preview_form_select';
+ }
+
+ /**
+ * Form constructor.
+ *
+ * @param array $form
+ * An associative array containing the structure of the form.
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ * The current state of the form.
+ * @param \Drupal\Core\Entity\EntityInterface $node
+ * The node being previews
+ *
+ * @return array
+ * The form structure.
+ */
+ public function buildForm(array $form, FormStateInterface $form_state, EntityInterface $node = NULL) {
+ $view_mode = $node->preview_view_mode;
+
+ $query_options = $node->isNew() ? array('query' => array('uuid' => $node->uuid())) : array();
+ $form['backlink'] = array(
+ '#type' => 'link',
+ '#title' => $this->t('Back to content editing'),
+ '#href' => $node->isNew() ? 'node/add/' . $node->bundle() : 'node/' . $node->id() . '/edit',
+ '#options' => array('attributes' => array('class' => array('node-preview-backlink'))) + $query_options,
+ );
+
+ $view_mode_options = $this->getViewModeOptions($node);
+
+ $form['uuid'] = array(
+ '#type' => 'value',
+ '#value' => $node->uuid(),
+ );
+
+ $form['view_mode'] = array(
+ '#type' => 'select',
+ '#title' => $this->t('View mode'),
+ '#options' => $view_mode_options,
+ '#default_value' => $view_mode,
+ '#attributes' => array(
+ 'data-drupal-autosubmit' => TRUE,
+ )
+ );
+
+ $form['submit'] = array(
+ '#type' => 'submit',
+ '#value' => $this->t('Switch'),
+ '#attributes' => array(
+ 'class' => array('js-hide'),
+ ),
+ );
+
+ return $form;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function submitForm(array &$form, FormStateInterface $form_state) {
+ $form_state->setRedirect('entity.node.preview', array(
+ 'node_preview' => $form_state['values']['uuid'],
+ 'view_mode_id' => $form_state['values']['view_mode'],
+ ));
+ }
+
+ /**
+ * Retrieves the list of available view modes for the current node.
+ *
+ * @param EntityInterface $node
+ * The node being previewed.
+ *
+ * @return array
+ * List of available view modes for the current node.
+ */
+ protected function getViewModeOptions(EntityInterface $node) {
+ $load_ids = array();
+ $view_mode_options = array();
+
+ // Load all the node's view modes.
+ $view_modes = $this->entityManager->getViewModes('node');
+
+ // Get the list of available view modes for the current node's bundle.
+ $ids = $this->configFactory->listAll('entity.view_display.node.' . $node->bundle());
+ foreach ($ids as $id) {
+ $config_id = str_replace('entity.view_display' . '.', '', $id);
+ $load_ids[] = $config_id;
+ }
+ $displays = entity_load_multiple('entity_view_display', $load_ids);
+
+ // Generate the display options array.
+ foreach ($displays as $display) {
+
+ $view_mode_name = $display->get('mode');
+
+ // Skip view modes that are not used in the front end.
+ if (in_array($view_mode_name, array('rss', 'search_index'))) {
+ continue;
+ }
+
+ if ($display->status()) {
+ $view_mode_options[$view_mode_name] = ($view_mode_name == 'default') ? t('Default') : $view_modes[$view_mode_name]['label'];
+ }
+ }
+
+ return $view_mode_options;
+ }
+
+}
diff --git a/core/modules/node/src/NodeForm.php b/core/modules/node/src/NodeForm.php
index a2ec826..327aee5 100644
--- a/core/modules/node/src/NodeForm.php
+++ b/core/modules/node/src/NodeForm.php
@@ -13,6 +13,9 @@ use Drupal\Core\Entity\ContentEntityForm;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Component\Utility\String;
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\user\TempStoreFactory;
+use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Form controller for the node edit forms.
@@ -27,6 +30,36 @@ class NodeForm extends ContentEntityForm {
protected $settings;
/**
+ * The tempstore factory.
+ *
+ * @var \Drupal\user\TempStoreFactory
+ */
+ protected $tempStoreFactory;
+
+ /**
+ * Constructs a ContentEntityForm object.
+ *
+ * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+ * The entity manager.
+ * @param \Drupal\user\TempStoreFactory $temp_store_factory
+ * The factory for the temp store object.
+ */
+ public function __construct(EntityManagerInterface $entity_manager, TempStoreFactory $temp_store_factory) {
+ parent::__construct($entity_manager);
+ $this->tempStoreFactory = $temp_store_factory;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container) {
+ return new static(
+ $container->get('entity.manager'),
+ $container->get('user.tempstore')
+ );
+ }
+
+ /**
* {@inheritdoc}
*/
protected function prepareEntity() {
@@ -47,6 +80,28 @@ class NodeForm extends ContentEntityForm {
* {@inheritdoc}
*/
public function form(array $form, FormStateInterface $form_state) {
+
+ // Try to restore from temp store.
+ $uuid = $this->entity->uuid();
+ $store = $this->tempStoreFactory->get('node_preview');
+
+ // If the user is creating a new node, the UUID is passed in the request.
+ if ($request_uuid = \Drupal::request()->query->get('uuid')) {
+ $uuid = $request_uuid;
+ }
+
+ if ($preview = $store->get($uuid)) {
+ $form_state = $preview;
+
+ // Rebuild the form.
+ $form_state['rebuild'] = TRUE;
+ $this->entity = $preview['controller']->getEntity();
+ unset($this->entity->in_preview);
+
+ // Remove the entry from the temp store.
+ $store->delete($uuid);
+ }
+
/** @var \Drupal\node\NodeInterface $node */
$node = $this->entity;
@@ -56,15 +111,6 @@ class NodeForm extends ContentEntityForm {
$current_user = \Drupal::currentUser();
$user_config = \Drupal::config('user.settings');
- // Some special stuff when previewing a node.
- if (isset($form_state['node_preview'])) {
- $form['#prefix'] = $form_state['node_preview'];
- $node->in_preview = TRUE;
- $form['#title'] = $this->t('Preview');
- }
- else {
- unset($node->in_preview);
- }
// Override the default CSS class name, since the user-defined node type
// name in 'TYPE-node-form' potentially clashes with third-party class
@@ -366,11 +412,13 @@ class NodeForm extends ContentEntityForm {
* The current state of the form.
*/
public function preview(array $form, FormStateInterface $form_state) {
- // @todo Remove this: we should not have explicit includes in autoloaded
- // classes.
- module_load_include('inc', 'node', 'node.pages');
- $form_state['node_preview'] = node_preview($this->entity, $form_state);
- $form_state['rebuild'] = TRUE;
+ $store = $this->tempStoreFactory->get('node_preview');
+ $this->entity->in_preview = TRUE;
+ $store->set($this->entity->uuid(), $form_state);
+ $form_state->setRedirect('entity.node.preview', array(
+ 'node_preview' => $this->entity->uuid(),
+ 'view_mode_id' => 'default',
+ ));
}
/**
diff --git a/core/modules/node/src/ParamConverter/NodePreviewConverter.php b/core/modules/node/src/ParamConverter/NodePreviewConverter.php
new file mode 100644
index 0000000..1b0f118
--- /dev/null
+++ b/core/modules/node/src/ParamConverter/NodePreviewConverter.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\node\ParamConverter\NodePreviewConverter.
+ */
+
+namespace Drupal\node\ParamConverter;
+
+use Drupal\Core\Entity\EntityManagerInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Route;
+use Drupal\Core\ParamConverter\ParamConverterInterface;
+use Drupal\user\TempStoreFactory;
+
+/**
+ * Provides upcasting for a node entity in preview.
+ */
+class NodePreviewConverter implements ParamConverterInterface {
+
+ /**
+ * Stores the tempstore factory.
+ *
+ * @var \Drupal\user\TempStoreFactory
+ */
+ protected $tempStoreFactory;
+
+ /**
+ * Constructs a new NodePreviewConverter.
+ *
+ * @param \Drupal\user\TempStoreFactory $temp_store_factory
+ * The factory for the temp store object.
+ */
+ public function __construct(TempStoreFactory $temp_store_factory) {
+ $this->tempStoreFactory = $temp_store_factory;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function convert($value, $definition, $name, array $defaults, Request $request) {
+ $store = $this->tempStoreFactory->get('node_preview');
+ if ($form_state = $store->get($value)) {
+ return $form_state['controller']->getEntity();
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function applies($definition, $name, Route $route) {
+ if (!empty($definition['type']) && $definition['type'] == 'node_preview') {
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+}
diff --git a/core/modules/node/src/Tests/PagePreviewTest.php b/core/modules/node/src/Tests/PagePreviewTest.php
index e387c64..fa2a32f 100644
--- a/core/modules/node/src/Tests/PagePreviewTest.php
+++ b/core/modules/node/src/Tests/PagePreviewTest.php
@@ -116,13 +116,31 @@ class PagePreviewTest extends NodeTestBase {
$this->drupalPostForm('node/add/page', $edit, t('Preview'));
// Check that the preview is displaying the title, body and term.
- $this->assertTitle(t('Preview | Drupal'), 'Basic page title is preview.');
+ $this->assertTitle(t('@title | Drupal', array('@title' => $edit[$title_key])), 'Basic page title is preview.');
$this->assertText($edit[$title_key], 'Title displayed.');
$this->assertText($edit[$body_key], 'Body displayed.');
$this->assertText($edit[$term_key], 'Term displayed.');
+ $this->assertLink(t('Back to content editing'));
+
+ // Get the UUID.
+ $url = parse_url($this->getUrl());
+ $paths = explode('/', $url['path']);
+ $view_mode = array_pop($paths);
+ $uuid = array_pop($paths);
+
+ // Switch view mode. We'll remove the body from the teaser view mode.
+ entity_get_display('node', 'page', 'teaser')
+ ->removeComponent('body')
+ ->save();
+
+ $view_mode_edit = array('view_mode' => 'teaser');
+ $this->drupalPostForm('node/preview/' . $uuid . '/default', $view_mode_edit, t('Switch'));
+ $this->assertRaw('view-mode-teaser', 'View mode teaser class found.');
+ $this->assertNoText($edit[$body_key], 'Body not displayed.');
// Check that the title, body and term fields are displayed with the
- // correct values.
+ // values after going back to the content edit page.
+ $this->clickLink(t('Back to content editing'));
$this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.');
$this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.');
$this->assertFieldByName($term_key, $edit[$term_key], 'Term field displayed.');
@@ -153,7 +171,7 @@ class PagePreviewTest extends NodeTestBase {
$this->assertNoLink($newterm1);
$this->assertNoLink($newterm2);
- $this->drupalPostForm(NULL, $edit, t('Save'));
+ $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
// Check with one more new term, keeping old terms, removing the existing
// one.
@@ -168,7 +186,6 @@ class PagePreviewTest extends NodeTestBase {
$this->assertLink($newterm1);
$this->assertLink($newterm2);
$this->assertNoLink($newterm3);
- $this->drupalPostForm(NULL, $edit, t('Save'));
}
/**
@@ -190,13 +207,14 @@ class PagePreviewTest extends NodeTestBase {
$this->drupalPostForm('node/add/page', $edit, t('Preview'));
// Check that the preview is displaying the title, body and term.
- $this->assertTitle(t('Preview | Drupal'), 'Basic page title is preview.');
+ $this->assertTitle(t('@title | Drupal', array('@title' => $edit[$title_key])), 'Basic page title is preview.');
$this->assertText($edit[$title_key], 'Title displayed.');
$this->assertText($edit[$body_key], 'Body displayed.');
$this->assertText($edit[$term_key], 'Term displayed.');
- // Check that the title, body and term fields are displayed with the correct values.
- $this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.');
+ // Check that the title and body fields are displayed with the correct
+ // values after going back to the content edit page.
+ $this->clickLink(t('Back to content editing')); $this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.');
$this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.');
$this->assertFieldByName($term_key, $edit[$term_key], 'Term field displayed.');
diff --git a/core/modules/node/templates/node-preview.html.twig b/core/modules/node/templates/node-preview.html.twig
deleted file mode 100644
index 1bc3441..0000000
--- a/core/modules/node/templates/node-preview.html.twig
+++ /dev/null
@@ -1,23 +0,0 @@
-{#
-/**
- * @file
- * Default theme implementation for a node preview.
- *
- * This display may be used during node creation and editing.
- *
- * Available variables:
- * - preview_teaser: Flag indicating whether to show a trimmed teaser version.
- * - teaser: Trimmed teaser version of the node.
- * - full: Full version of the node.
- *
- * @see template_preprocess_node_preview()
- *
- * @ingroup themeable
- */
-#}
-{% if preview_teaser %}
- <h3>{{ "Preview trimmed version"|t }}</h3>
- {{ teaser }}
- <h3>{{ "Preview full version"|t }}</h3>
-{% endif %}
-{{ full }}
diff --git a/core/modules/node/templates/node.html.twig b/core/modules/node/templates/node.html.twig
index c80eecb..a08b064 100644
--- a/core/modules/node/templates/node.html.twig
+++ b/core/modules/node/templates/node.html.twig
@@ -39,7 +39,6 @@
* - node--view-mode-[view_mode]: The View Mode of the node; for example, a
* teaser would result in: "node--view-mode-teaser", and
* full: "node--view-mode-full".
- * - node--preview: Whether a node is in preview mode.
* The following are controlled through the node publishing options.
* - node--promoted: Appears on nodes promoted to the front page.
* - node--sticky: Appears on nodes ordered above other non-sticky nodes in
diff --git a/core/modules/system/css/system.theme.css b/core/modules/system/css/system.theme.css
index f3a7490..7023b4f 100644
--- a/core/modules/system/css/system.theme.css
+++ b/core/modules/system/css/system.theme.css
@@ -9,9 +9,6 @@
.node--unpublished {
background-color: #fff4f4;
}
-.node--preview {
- background-color: #ffffea;
-}
/**
* Markup generated by theme_tablesort_indicator().
diff --git a/core/modules/system/src/Tests/Database/RegressionTest.php b/core/modules/system/src/Tests/Database/RegressionTest.php
index 0836572..81f962d 100644
--- a/core/modules/system/src/Tests/Database/RegressionTest.php
+++ b/core/modules/system/src/Tests/Database/RegressionTest.php
@@ -19,7 +19,7 @@ class RegressionTest extends DatabaseTestBase {
*
* @var array
*/
- public static $modules = array('node');
+ public static $modules = array('node', 'user');
/**
* Ensures that non-ASCII UTF-8 data is stored in the database properly.
diff --git a/core/modules/system/src/Tests/Entity/EntityTypedDataDefinitionTest.php b/core/modules/system/src/Tests/Entity/EntityTypedDataDefinitionTest.php
index 82848ac..23318a0 100644
--- a/core/modules/system/src/Tests/Entity/EntityTypedDataDefinitionTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityTypedDataDefinitionTest.php
@@ -36,7 +36,7 @@ class EntityTypedDataDefinitionTest extends DrupalUnitTestBase {
*
* @var array
*/
- public static $modules = array('filter', 'text', 'node');
+ public static $modules = array('filter', 'text', 'node', 'user');
protected function setUp() {
parent::setup();
diff --git a/core/modules/taxonomy/src/Tests/TermTest.php b/core/modules/taxonomy/src/Tests/TermTest.php
index 4f7c58d..b2e9757 100644
--- a/core/modules/taxonomy/src/Tests/TermTest.php
+++ b/core/modules/taxonomy/src/Tests/TermTest.php
@@ -184,9 +184,9 @@ class TermTest extends TaxonomyTestBase {
// Preview the node.
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Preview'));
- $this->assertNoUniqueText($term2->getName(), 'Term is displayed when previewing the node.');
- $this->drupalPostForm(NULL, NULL, t('Preview'));
- $this->assertNoUniqueText($term2->getName(), 'Term is displayed when previewing the node again.');
+ $this->assertUniqueText($term2->getName(), 'Term is displayed when previewing the node.');
+ $this->drupalPostForm('node/' . $node->id() . '/edit', NULL, t('Preview'));
+ $this->assertUniqueText($term2->getName(), 'Term is displayed when previewing the node again.');
}
/**
diff --git a/core/themes/bartik/css/style.css b/core/themes/bartik/css/style.css
index c866079..71772b3 100644
--- a/core/themes/bartik/css/style.css
+++ b/core/themes/bartik/css/style.css
@@ -847,6 +847,48 @@ ul.links {
border-left: 1px solid #fff4f4;
border-right: 1px solid #fff4f4;
}
+.node-preview-container {
+ background: #d1e8f5;
+ background-image: linear-gradient(to bottom, #d1e8f5, #d3e8f4);
+ font-family: Arial, sans-serif;
+ box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.3333);
+ position: fixed;
+ z-index: 499;
+ width: 100%;
+ padding: 10px;
+}
+.node-preview-backlink {
+ background-color: #419ff1;
+ background: url(../../../misc/icons/000000/chevron-left.svg) left no-repeat, linear-gradient(to bottom, #419ff1, #1076d5);
+ border: 1px solid #0048c8;
+ border-radius: .4em;
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, .4);
+ color: #fff;
+ font-size: 0.9em;
+ line-height: normal;
+ margin: 0;
+ padding: 4px 1em 4px 0.6em;
+ text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.5);
+}
+.node-preview-backlink:focus,
+.node-preview-backlink:hover {
+ background-color: #419cf1;
+ background: url(../../../misc/icons/000000/chevron-left.svg) left no-repeat, linear-gradient(to bottom, #59abf3, #2a90ef);
+ border: 1px solid #0048c8;
+ text-decoration: none;
+ color: #fff;
+}
+.node-preview-backlink:active {
+ background-color: #0e69be;
+ background: url(../../../misc/icons/000000/chevron-left.svg) left no-repeat, linear-gradient(to bottom, #0e69be, #2a93ef);
+ border: 1px solid #0048c8;
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, .25);
+}
+.node-preview-backlink::before {
+ content: '';
+ width: 10px;
+ display: inline-block;
+}
/* ----------------- Comments ----------------- */