diff --git a/panelizer_quickedit/README.txt b/panelizer_quickedit/README.txt new file mode 100644 index 0000000..284bb68 --- /dev/null +++ b/panelizer_quickedit/README.txt @@ -0,0 +1,5 @@ +Panelizer Quick Edit +--------- +This module contains customizations which allow Panelized Entities to be edited +inline with Quick Edit. It is separate from the Panelizer project as not all +users need this functionality. diff --git a/panelizer_quickedit/panelizer_quickedit.info.yml b/panelizer_quickedit/panelizer_quickedit.info.yml new file mode 100644 index 0000000..e39f9f4 --- /dev/null +++ b/panelizer_quickedit/panelizer_quickedit.info.yml @@ -0,0 +1,9 @@ +type: module +name: Panelizer Quick Edit +description: 'Enables Quick Edit to function normally when using Panelizer.' +package: Layout +version: VERSION +core: 8.x +dependencies: + - panelizer + - quickedit diff --git a/panelizer_quickedit/panelizer_quickedit.module b/panelizer_quickedit/panelizer_quickedit.module new file mode 100644 index 0000000..44c19f0 --- /dev/null +++ b/panelizer_quickedit/panelizer_quickedit.module @@ -0,0 +1,59 @@ +getTranslationFromContext($entity, $langcode); + + // Grab the information required to re-render the entity_field block. + $temp = str_replace('panelizer-', '', $view_mode_id); + list($view_mode, $block_id) = explode('-block-id-', $temp); + + // Load the Panelizer display. + /** @var \Drupal\panelizer\PanelizerInterface $panelizer */ + $panelizer = \Drupal::service('panelizer'); + $display = $panelizer->getPanelsDisplay($entity, $view_mode); + + /** @var \Drupal\ctools_block\Plugin\Block\EntityField $plugin */ + $plugin = $display->getBlock($block_id); + + // Set the appropriate Entity context and build the plugin. + $plugin->setContextValue('entity', $entity); + $build = $plugin->build(); + + // Add our custom field view-mode in case the user wants to edit again. + $build['field']['#view_mode'] = $view_mode_id; + + return $build; +} + +/** + * Implements hook_entity_view_alter(). + */ +function panelizer_quickedit_entity_view_alter(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display) { + // Explicitly add support for ctools_block by attaching a custom view-mode to + // every Block that's about to be rendered. + if (isset($build['#panels_display'])) { + // We only support known Display Builders. + $supported_plugins = ['ipe', 'standard']; + if (in_array($build['#panels_display']->getBuilder()->getPluginId(), $supported_plugins)) { + $region_names = Element::getVisibleChildren($build['content']); + foreach ($region_names as $region_name) { + $block_ids = Element::getVisibleChildren($build['content'][$region_name]); + foreach ($block_ids as $block_id) { + $block = &$build['content'][$region_name][$block_id]; + if (isset($block['#base_plugin_id']) && $block['#base_plugin_id'] === 'entity_field') { + $view_mode = 'panelizer-' . $build['#view_mode'] . '-block-id-' . $block_id; + $block['content']['field']['#view_mode'] = $view_mode; + } + } + } + } + } +} diff --git a/panelizer_quickedit/tests/src/FunctionalJavascript/PanelizerQuickEditTest.php b/panelizer_quickedit/tests/src/FunctionalJavascript/PanelizerQuickEditTest.php new file mode 100644 index 0000000..9097043 --- /dev/null +++ b/panelizer_quickedit/tests/src/FunctionalJavascript/PanelizerQuickEditTest.php @@ -0,0 +1,153 @@ +drupalCreateContentType(['type' => 'article', 'name' => 'Article']); + + // Add a plain text field for this content type. + FieldStorageConfig::create([ + 'field_name' => 'test_field', + 'entity_type' => 'node', + 'type' => 'string', + ])->save(); + + FieldConfig::create([ + 'field_name' => 'test_field', + 'label' => 'Test Field', + 'entity_type' => 'node', + 'bundle' => 'article', + 'required' => FALSE, + 'settings' => [], + 'description' => '', + ])->save(); + + /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $entity_form_display */ + $entity_form_display = \Drupal::entityTypeManager() + ->getStorage('entity_form_display') + ->load('node.article.default'); + $entity_form_display->setComponent('test_field')->save(); + + /** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $entity_display */ + $entity_display = \Drupal::entityTypeManager() + ->getStorage('entity_view_display') + ->load('node.article.default'); + $entity_display->setComponent('test_field')->save(); + + // Create a privileged user. + $user = $this->drupalCreateUser([ + 'access contextual links', + 'access in-place editing', + 'access content', + 'administer node display', + 'administer panelizer', + 'create article content', + 'edit any article content', + ]); + $this->drupalLogin($user); + + // Enable Panelizer for Articles. + $this->drupalPostForm('admin/structure/types/manage/article/display', [ + 'panelizer[enable]' => TRUE, + ], 'Save'); + } + + /** + * Tests Quick Editing a Panelized Node. + */ + public function testPanelizerQuickEdit() { + /** @var \Drupal\panelizer\PanelizerInterface $panelizer */ + $panelizer = \Drupal::service('panelizer'); + $displays = $panelizer->getDefaultPanelsDisplays('node', 'article', 'default'); + $display = $displays['default']; + + // Find the "test_field" block. + $block_id = FALSE; + foreach ($display->getConfiguration()['blocks'] as $block) { + if ($block['id'] === 'entity_field:node:test_field') { + $block_id = $block['uuid']; + } + } + + // Make sure we found a valid UUID. + $this->assertNotFalse($block_id); + + // Create an Article. + $node = $this->drupalCreateNode([ + 'type' => 'article', + 'test_field' => [ + 'value' => 'Change me', + ] + ]); + + // Visit the new node. + $this->drupalGet('node/' . $node->id()); + + // This is the unique ID we append to normal Quick Edit field IDs. + $panelizer_id = 'panelizer-full-block-id-' . $block_id; + + // Assemble common CSS selectors. + $entity_selector = '[data-quickedit-entity-id="node/' . $node->id() . '"]'; + $field_selector = '[data-quickedit-field-id="node/' . $node->id() . '/test_field/' . $node->language()->getId() . '/' . $panelizer_id . '"]'; + + // Wait until Quick Edit loads. + $condition = "jQuery('" . $entity_selector . " .quickedit').length > 0"; + $this->assertJsCondition($condition, 10000); + + // Initiate Quick Editing. + $this->triggerClick($entity_selector . ' [data-contextual-id] > button'); + $this->click($entity_selector . ' [data-contextual-id] .quickedit > a'); + $this->triggerClick($field_selector); + $this->assertSession()->assertWaitOnAjaxRequest(); + + // Trigger an edit with Javascript (this is a "contenteditable" element). + $this->getSession()->executeScript("jQuery('" . $field_selector . "').text('Hello world').trigger('keyup');"); + + // To prevent 403s on save, we re-set our request (cookie) state. + $this->prepareRequest(); + + // Save the change. + $this->triggerClick('.quickedit-button.action-save'); + $this->assertSession()->assertWaitOnAjaxRequest(); + + // Re-visit the node to make sure the edit worked. + $this->drupalGet('node/' . $node->id()); + $this->assertSession()->pageTextContains('Hello world'); + } + + /** + * Clicks the element with the given CSS selector using event triggers. + * + * @todo Remove when https://github.com/jcalderonzumba/gastonjs/issues/19 + * is fixed. Currently clicking anchors/buttons with nested elements is not + * possible. + * + * @param string $css_selector + * The CSS selector identifying the element to click. + */ + protected function triggerClick($css_selector) { + $this->getSession()->executeScript("jQuery('" . $css_selector . "')[0].click()"); + } + +} diff --git a/src/PanelizerEntityViewBuilder.php b/src/PanelizerEntityViewBuilder.php index 3a15689..39e25ea 100644 --- a/src/PanelizerEntityViewBuilder.php +++ b/src/PanelizerEntityViewBuilder.php @@ -353,6 +353,13 @@ class PanelizerEntityViewBuilder implements EntityViewBuilderInterface, EntityHa foreach ($entities as $id => $entity) { $panels_display = $this->panelizer->getPanelsDisplay($entity, $view_mode, $displays[$entity->bundle()]); $build[$id] = $this->buildPanelized($entity, $panels_display, $view_mode, $langcode); + + // Allow modules to modify the render array. + $alter_types = array( + "{$this->entityTypeId}_view", + 'entity_view', + ); + $this->moduleHandler->alter($alter_types, $build[$id], $entity, $displays[$entity->bundle()]); } return $build;