diff --git a/core/misc/icons/000000/chevron-left.svg b/core/misc/icons/000000/chevron-left.svg new file mode 100644 index 0000000000000000000000000000000000000000..19ba5807048b8f95f841b5d3f2ce0852ad3631af --- /dev/null +++ b/core/misc/icons/000000/chevron-left.svg @@ -0,0 +1 @@ + diff --git a/core/modules/block/src/Tests/BlockConfigSchemaTest.php b/core/modules/block/src/Tests/BlockConfigSchemaTest.php index 9065fa4839ac6cf25e2febcdaccddf156f9fd8fb..47e242144802e2870288c3b0090a2810374df5db 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 281ad344515649c183231c3de2d313939fac3a01..6a1798d7d3653da276b5028a8c4fe17b11d5391c 100644 --- a/core/modules/file/src/Tests/FileFieldDisplayTest.php +++ b/core/modules/file/src/Tests/FileFieldDisplayTest.php @@ -87,6 +87,7 @@ function testNodeDisplay() { $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 dc247b5e587fdb2d06cb53091e4b06175af93dee..1882a265170b6128cf1b3a17cbb97eb4163fc6a1 100644 --- a/core/modules/filter/src/Tests/FilterFormatAccessTest.php +++ b/core/modules/filter/src/Tests/FilterFormatAccessTest.php @@ -234,7 +234,7 @@ function testFormatWidgetPermissions() { $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 0000000000000000000000000000000000000000..9aca68a961753654f2741dc12b46bb1bc9d34ab2 --- /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 ab1f9357b7e074d6c19e12f29bc8b2e029c1a0ac..231687e30644bb293110cb80c8d86bc455883030 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 fefd6d5c9a13fb93d0a7393906ef4c2c79ed4a62..d96052d3200fcea8d770f47b41abfb66ea20c4ec 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'; } } @@ -1044,6 +1042,25 @@ function node_view_multiple($nodes, $view_mode = 'teaser', $langcode = NULL) { return entity_view_multiple($nodes, $view_mode, $langcode); } +/** + * 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(). * diff --git a/core/modules/node/node.pages.inc b/core/modules/node/node.pages.inc index e848f3e56f3c6d05a00a292c6209344f61cf56d9..517a5d5e9faab6eb3f12c02da182542bb7212978 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. You can insert the delimiter "<!--break-->" (without the quotes) to fine-tune where your post gets split.')); - $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 f27cbba21f2cdae348327a26498d56fe376a381c..9041f24512c51b249ae3eccb1f939e88ca2361d5 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 f9523d4c89ae4949a99322a81722e48fe815719a..26a4d07b9c6b58bbf2a6c20c85054dd554e5e940 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 85d998a6bf40e484217de65c0e40965f2f8438a9..546f47f4358c1c713af06d26ea54bf9309741b1e 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 0000000000000000000000000000000000000000..18bbcd185e52a0eaf1d2962ae4e68c5252dd06cc --- /dev/null +++ b/core/modules/node/src/Access/NodePreviewAccessCheck.php @@ -0,0 +1,58 @@ +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 0000000000000000000000000000000000000000..9fb8d13114069a05a9e89627ea7cff2a82a3f321 --- /dev/null +++ b/core/modules/node/src/Controller/NodePreviewController.php @@ -0,0 +1,73 @@ +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 0000000000000000000000000000000000000000..4c5bb913892adbc3c5bae0ff319e2217c11f9a8c --- /dev/null +++ b/core/modules/node/src/Form/NodePreviewForm.php @@ -0,0 +1,169 @@ +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 a2ec826928969dade2cfc4d497633633489f1826..327aee5d18ae067e4082e81ef0691c63bf0e03e2 100644 --- a/core/modules/node/src/NodeForm.php +++ b/core/modules/node/src/NodeForm.php @@ -13,6 +13,9 @@ 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. @@ -26,6 +29,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} */ @@ -47,6 +80,28 @@ protected function prepareEntity() { * {@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 @@ public function form(array $form, FormStateInterface $form_state) { $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 @@ public function submit(array $form, FormStateInterface $form_state) { * 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 0000000000000000000000000000000000000000..1b0f1181025e501fa9a1f1cb0c682293a4c32366 --- /dev/null +++ b/core/modules/node/src/ParamConverter/NodePreviewConverter.php @@ -0,0 +1,58 @@ +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 e387c64801388531b6ef20552f15f46217052681..fa2a32f68159319517eb091a28c6df829f9ffef3 100644 --- a/core/modules/node/src/Tests/PagePreviewTest.php +++ b/core/modules/node/src/Tests/PagePreviewTest.php @@ -116,13 +116,31 @@ function testPagePreview() { $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 @@ function testPagePreview() { $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 @@ function testPagePreview() { $this->assertLink($newterm1); $this->assertLink($newterm2); $this->assertNoLink($newterm3); - $this->drupalPostForm(NULL, $edit, t('Save')); } /** @@ -190,13 +207,14 @@ function testPagePreviewWithRevisions() { $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 1bc3441944e83b2767f028842b22c93fe0947661..0000000000000000000000000000000000000000 --- 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 %} -

{{ "Preview trimmed version"|t }}

- {{ teaser }} -

{{ "Preview full version"|t }}

-{% endif %} -{{ full }} diff --git a/core/modules/node/templates/node.html.twig b/core/modules/node/templates/node.html.twig index c80eecba6d7c10c9fcab428101882a81f7193058..a08b0643f6342019ad2359f0b538bd61ebde1a39 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 f3a74903595344a1554ef9367967d1b09bc3ac9f..7023b4fd02bf764c33aceb59e861cb0348d3bbc2 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 0836572d1ef690dccd1a0163c36d1bc5157a3e37..81f962d0da1e29c635bc1661a37c704f7c434cf0 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 82848ac8fcb39d12bf98f608ba5fe9784b6dc608..23318a0d26b3cc3679ef458c45c4b26638fbde49 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 4f7c58dcfd1b8205c439d7619f3940dcabe31eca..b2e9757a2c6bfecd1fd7c9d7c15db1c91b3854f1 100644 --- a/core/modules/taxonomy/src/Tests/TermTest.php +++ b/core/modules/taxonomy/src/Tests/TermTest.php @@ -184,9 +184,9 @@ function testTaxonomyNode() { // 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 c866079977f17f194cc03c7b3cd35e178c3aa4c0..71772b367083751f823f2001cbe310f7ffbeae72 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 ----------------- */