Update to Drupal 8.2.0. For more information, see https://www.drupal.org/project/drupal/releases/8.2.0
This commit is contained in:
parent
2f563ab520
commit
f1c8716f57
1732 changed files with 52334 additions and 11780 deletions
|
@ -1,7 +1,8 @@
|
|||
id: d6_action_settings
|
||||
id: action_settings
|
||||
label: Action configuration
|
||||
migration_tags:
|
||||
- Drupal 6
|
||||
- Drupal 7
|
||||
source:
|
||||
plugin: variable
|
||||
variables:
|
|
@ -84,9 +84,9 @@ class EmailAction extends ConfigurableActionBase implements ContainerFactoryPlug
|
|||
* The entity manager.
|
||||
* @param \Psr\Log\LoggerInterface $logger
|
||||
* A logger instance.
|
||||
* @param \Drupal\Core\Mail\MailManagerInterface
|
||||
* @param \Drupal\Core\Mail\MailManagerInterface $mail_manager
|
||||
* The mail manager.
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* The language manager.
|
||||
* @param \Egulias\EmailValidator\EmailValidator $email_validator
|
||||
* The email validator.
|
||||
|
|
|
@ -132,7 +132,7 @@ display:
|
|||
entity_type: node
|
||||
filters:
|
||||
status:
|
||||
value: true
|
||||
value: '1'
|
||||
table: node_field_data
|
||||
field: status
|
||||
id: status
|
||||
|
|
|
@ -25,7 +25,9 @@ class ActionUninstallTest extends BrowserTestBase {
|
|||
public function testActionUninstall() {
|
||||
\Drupal::service('module_installer')->uninstall(array('action'));
|
||||
|
||||
$this->assertTrue(entity_load('action', 'user_block_user_action', TRUE), 'Configuration entity \'user_block_user_action\' still exists after uninstalling action module.' );
|
||||
$storage = $this->container->get('entity_type.manager')->getStorage('action');
|
||||
$storage->resetCache(['user_block_user_action']);
|
||||
$this->assertTrue($storage->load('user_block_user_action'), 'Configuration entity \'user_block_user_action\' still exists after uninstalling action module.' );
|
||||
|
||||
$admin_user = $this->drupalCreateUser(array('administer users'));
|
||||
$this->drupalLogin($admin_user);
|
||||
|
|
|
@ -68,7 +68,7 @@ class BulkFormTest extends BrowserTestBase {
|
|||
|
||||
// Set all nodes to sticky and check that.
|
||||
$edit += array('action' => 'node_make_sticky_action');
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply'));
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
|
||||
|
||||
foreach ($nodes as $node) {
|
||||
$changed_node = $node_storage->load($node->id());
|
||||
|
@ -82,7 +82,7 @@ class BulkFormTest extends BrowserTestBase {
|
|||
$this->assertTrue($node->isPublished(), 'The node is published.');
|
||||
|
||||
$edit = array('node_bulk_form[0]' => TRUE, 'action' => 'node_unpublish_action');
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply'));
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
|
||||
|
||||
$this->assertText('Unpublish content was applied to 1 item.');
|
||||
|
||||
|
@ -123,7 +123,7 @@ class BulkFormTest extends BrowserTestBase {
|
|||
// Check the default title.
|
||||
$this->drupalGet('test_bulk_form');
|
||||
$result = $this->xpath('//label[@for="edit-action"]');
|
||||
$this->assertEqual('With selection', $result[0]->getText());
|
||||
$this->assertEqual('Action', $result[0]->getText());
|
||||
|
||||
// Setup up a different bulk form title.
|
||||
$view = Views::getView('test_bulk_form');
|
||||
|
@ -142,7 +142,7 @@ class BulkFormTest extends BrowserTestBase {
|
|||
$edit["node_bulk_form[$i]"] = TRUE;
|
||||
}
|
||||
$edit += array('action' => 'node_delete_action');
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply'));
|
||||
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
|
||||
// Make sure we don't show an action message while we are still on the
|
||||
// confirmation page.
|
||||
$errors = $this->xpath('//div[contains(@class, "messages--status")]');
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Drupal\Tests\action\Functional;
|
||||
|
||||
use Drupal\Component\Utility\Crypt;
|
||||
use Drupal\system\Entity\Action;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
|
@ -81,7 +82,7 @@ class ConfigurationTest extends BrowserTestBase {
|
|||
$this->assertResponse(200);
|
||||
$this->assertNoText($new_action_label, "Make sure the action label does not appear on the overview page after we've deleted the action.");
|
||||
|
||||
$action = entity_load('action', $aid);
|
||||
$action = Action::load($aid);
|
||||
$this->assertFalse($action, 'Make sure the action is gone after being deleted.');
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ class MigrateActionConfigsTest extends MigrateDrupal6TestBase {
|
|||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->executeMigration('d6_action_settings');
|
||||
$this->executeMigration('action_settings');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\action\Kernel\Migrate\d7;
|
||||
|
||||
use Drupal\config\Tests\SchemaCheckTestTrait;
|
||||
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
|
||||
|
||||
/**
|
||||
* Upgrade variables to action.settings.yml.
|
||||
*
|
||||
* @group migrate_drupal_7
|
||||
*/
|
||||
class MigrateActionConfigsTest extends MigrateDrupal7TestBase {
|
||||
|
||||
use SchemaCheckTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['action'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->executeMigration('action_settings');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests migration of action variables to action.settings.yml.
|
||||
*/
|
||||
public function testActionSettings() {
|
||||
$config = $this->config('action.settings');
|
||||
$this->assertSame(28, $config->get('recursion_limit'));
|
||||
$this->assertConfigSchema(\Drupal::service('config.typed'), 'action.settings', $config->get());
|
||||
}
|
||||
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
aggregator.admin_overview:
|
||||
title: 'Feed aggregator'
|
||||
title: 'Aggregator'
|
||||
description: 'Add feeds or import OPMLs to gather external content and configure how often they are updated.'
|
||||
route_name: aggregator.admin_overview
|
||||
parent: system.admin_config_services
|
||||
weight: 10
|
||||
aggregator.page_last:
|
||||
title: 'Feed aggregator'
|
||||
title: 'Aggregator'
|
||||
weight: 5
|
||||
route_name: aggregator.page_last
|
||||
aggregator.feed_add:
|
||||
|
|
|
@ -31,11 +31,11 @@ function aggregator_help($route_name, RouteMatchInterface $route_match) {
|
|||
$output .= '<dd>' . t('Users view feed content in the <a href=":aggregator">main aggregator display</a>, or by <a href=":aggregator-sources">their source</a> (usually via an RSS feed reader). The most recent content in a feed can be displayed as a block through the <a href=":admin-block">Blocks administration page</a>.', array(':aggregator' => \Drupal::url('aggregator.page_last'), ':aggregator-sources' => $url->toString(), ':admin-block' => (\Drupal::moduleHandler()->moduleExists('block')) ? \Drupal::url('block.admin_display') : '#')) . '</dd>';
|
||||
}
|
||||
$output .= '<dt>' . t('Adding, editing, and deleting feeds') . '</dt>';
|
||||
$output .= '<dd>' . t('Administrators can add, edit, and delete feeds, and choose how often to check each feed for newly updated items on the <a href=":feededit">Feed aggregator page</a>.', array(':feededit' => \Drupal::url('aggregator.admin_overview'))) . '</dd>';
|
||||
$output .= '<dd>' . t('Administrators can add, edit, and delete feeds, and choose how often to check each feed for newly updated items on the <a href=":feededit">Aggregator administration page</a>.', array(':feededit' => \Drupal::url('aggregator.admin_overview'))) . '</dd>';
|
||||
$output .= '<dt>' . t('Configuring the display of feed items') . '</dt>';
|
||||
$output .= '<dd>' . t('Administrators can choose how many items are displayed in the listing pages, which HTML tags are allowed in the content of feed items, and whether they should be trimmed to a maximum number of characters on the <a href=":settings">Feed aggregator settings page</a>.', array(':settings' => \Drupal::url('aggregator.admin_settings'))) . '</dd>';
|
||||
$output .= '<dd>' . t('Administrators can choose how many items are displayed in the listing pages, which HTML tags are allowed in the content of feed items, and whether they should be trimmed to a maximum number of characters on the <a href=":settings">Aggregator settings page</a>.', array(':settings' => \Drupal::url('aggregator.admin_settings'))) . '</dd>';
|
||||
$output .= '<dt>' . t('Discarding old feed items') . '</dt>';
|
||||
$output .= '<dd>' . t('Administrators can choose whether to discard feed items that are older than a specified period of time on the <a href=":settings">Feed aggregator settings page</a>. This requires a correctly configured cron maintenance task (see below).', array(':settings' => \Drupal::url('aggregator.admin_settings'))) . '<dd>';
|
||||
$output .= '<dd>' . t('Administrators can choose whether to discard feed items that are older than a specified period of time on the <a href=":settings">Aggregator settings page</a>. This requires a correctly configured cron maintenance task (see below).', array(':settings' => \Drupal::url('aggregator.admin_settings'))) . '<dd>';
|
||||
|
||||
$output .= '<dt>' . t('<abbr title="Outline Processor Markup Language">OPML</abbr> integration') . '</dt>';
|
||||
// Check if the aggregator opml View is enabled.
|
||||
|
|
|
@ -2,7 +2,7 @@ aggregator.admin_overview:
|
|||
path: '/admin/config/services/aggregator'
|
||||
defaults:
|
||||
_controller: '\Drupal\aggregator\Controller\AggregatorController::adminOverview'
|
||||
_title: 'Feed aggregator'
|
||||
_title: 'Aggregator'
|
||||
requirements:
|
||||
_permission: 'administer news feeds'
|
||||
|
||||
|
@ -10,7 +10,7 @@ aggregator.admin_settings:
|
|||
path: '/admin/config/services/aggregator/settings'
|
||||
defaults:
|
||||
_form: '\Drupal\aggregator\Form\SettingsForm'
|
||||
_title: 'Feed aggregator settings'
|
||||
_title: 'Aggregator settings'
|
||||
requirements:
|
||||
_permission: 'administer news feeds'
|
||||
|
||||
|
@ -53,6 +53,6 @@ aggregator.page_last:
|
|||
path: '/aggregator'
|
||||
defaults:
|
||||
_controller: '\Drupal\aggregator\Controller\AggregatorController::pageLast'
|
||||
_title: 'Feed aggregator'
|
||||
_title: 'Aggregator'
|
||||
requirements:
|
||||
_permission: 'access news feeds'
|
||||
|
|
|
@ -127,16 +127,16 @@ class Feed extends ContentEntityBase implements FeedInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
|
||||
$fields['fid'] = BaseFieldDefinition::create('integer')
|
||||
->setLabel(t('Feed ID'))
|
||||
->setDescription(t('The ID of the aggregator feed.'))
|
||||
->setReadOnly(TRUE)
|
||||
->setSetting('unsigned', TRUE);
|
||||
/** @var \Drupal\Core\Field\BaseFieldDefinition[] $fields */
|
||||
$fields = parent::baseFieldDefinitions($entity_type);
|
||||
|
||||
$fields['uuid'] = BaseFieldDefinition::create('uuid')
|
||||
->setLabel(t('UUID'))
|
||||
->setDescription(t('The aggregator feed UUID.'))
|
||||
->setReadOnly(TRUE);
|
||||
$fields['fid']->setLabel(t('Feed ID'))
|
||||
->setDescription(t('The ID of the aggregator feed.'));
|
||||
|
||||
$fields['uuid']->setDescription(t('The aggregator feed UUID.'));
|
||||
|
||||
$fields['langcode']->setLabel(t('Language code'))
|
||||
->setDescription(t('The feed language code.'));
|
||||
|
||||
$fields['title'] = BaseFieldDefinition::create('string')
|
||||
->setLabel(t('Title'))
|
||||
|
@ -150,17 +150,6 @@ class Feed extends ContentEntityBase implements FeedInterface {
|
|||
->setDisplayConfigurable('form', TRUE)
|
||||
->addConstraint('FeedTitle');
|
||||
|
||||
$fields['langcode'] = BaseFieldDefinition::create('language')
|
||||
->setLabel(t('Language code'))
|
||||
->setDescription(t('The feed language code.'))
|
||||
->setDisplayOptions('view', array(
|
||||
'type' => 'hidden',
|
||||
))
|
||||
->setDisplayOptions('form', array(
|
||||
'type' => 'language_select',
|
||||
'weight' => 2,
|
||||
));
|
||||
|
||||
$fields['url'] = BaseFieldDefinition::create('uri')
|
||||
->setLabel(t('URL'))
|
||||
->setDescription(t('The fully-qualified URL of the feed.'))
|
||||
|
|
|
@ -47,11 +47,14 @@ class Item extends ContentEntityBase implements ItemInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
|
||||
$fields['iid'] = BaseFieldDefinition::create('integer')
|
||||
->setLabel(t('Aggregator item ID'))
|
||||
->setDescription(t('The ID of the feed item.'))
|
||||
->setReadOnly(TRUE)
|
||||
->setSetting('unsigned', TRUE);
|
||||
/** @var \Drupal\Core\Field\BaseFieldDefinition[] $fields */
|
||||
$fields = parent::baseFieldDefinitions($entity_type);
|
||||
|
||||
$fields['iid']->setLabel(t('Aggregator item ID'))
|
||||
->setDescription(t('The ID of the feed item.'));
|
||||
|
||||
$fields['langcode']->setLabel(t('Language code'))
|
||||
->setDescription(t('The feed item language code.'));
|
||||
|
||||
$fields['fid'] = BaseFieldDefinition::create('entity_reference')
|
||||
->setLabel(t('Source feed'))
|
||||
|
@ -69,10 +72,6 @@ class Item extends ContentEntityBase implements ItemInterface {
|
|||
->setLabel(t('Title'))
|
||||
->setDescription(t('The title of the feed item.'));
|
||||
|
||||
$fields['langcode'] = BaseFieldDefinition::create('language')
|
||||
->setLabel(t('Language code'))
|
||||
->setDescription(t('The feed item language code.'));
|
||||
|
||||
$fields['link'] = BaseFieldDefinition::create('uri')
|
||||
->setLabel(t('Link'))
|
||||
->setDescription(t('The link of the feed item.'))
|
||||
|
|
|
@ -21,11 +21,9 @@ class FeedAccessControlHandler extends EntityAccessControlHandler {
|
|||
switch ($operation) {
|
||||
case 'view':
|
||||
return AccessResult::allowedIfHasPermission($account, 'access news feeds');
|
||||
break;
|
||||
|
||||
default:
|
||||
return AccessResult::allowedIfHasPermission($account, 'administer news feeds');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,13 +16,16 @@ class FeedForm extends ContentEntityForm {
|
|||
*/
|
||||
public function save(array $form, FormStateInterface $form_state) {
|
||||
$feed = $this->entity;
|
||||
if ($feed->save() == SAVED_UPDATED) {
|
||||
drupal_set_message($this->t('The feed %feed has been updated.', array('%feed' => $feed->label())));
|
||||
$status = $feed->save();
|
||||
$label = $feed->label();
|
||||
$view_link = $feed->link($label, 'canonical');
|
||||
if ($status == SAVED_UPDATED) {
|
||||
drupal_set_message($this->t('The feed %feed has been updated.', array('%feed' => $view_link)));
|
||||
$form_state->setRedirectUrl($feed->urlInfo('canonical'));
|
||||
}
|
||||
else {
|
||||
$this->logger('aggregator')->notice('Feed %feed added.', array('%feed' => $feed->label(), 'link' => $this->l($this->t('View'), new Url('aggregator.admin_overview'))));
|
||||
drupal_set_message($this->t('The feed %feed has been added.', array('%feed' => $feed->label())));
|
||||
drupal_set_message($this->t('The feed %feed has been added.', array('%feed' => $view_link)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -60,17 +60,17 @@ class AggregatorTitleFormatter extends FormatterBase {
|
|||
}
|
||||
|
||||
foreach ($items as $delta => $item) {
|
||||
if ($this->getSetting('display_as_link') && $url_string) {
|
||||
$elements[$delta] = [
|
||||
if ($this->getSetting('display_as_link') && $url_string) {
|
||||
$elements[$delta] = [
|
||||
'#type' => 'link',
|
||||
'#title' => $item->value,
|
||||
'#url' => Url::fromUri($url_string),
|
||||
];
|
||||
}
|
||||
else {
|
||||
$elements[$delta] = ['#markup' => $item->value];
|
||||
}
|
||||
];
|
||||
}
|
||||
else {
|
||||
$elements[$delta] = ['#markup' => $item->value];
|
||||
}
|
||||
}
|
||||
|
||||
return $elements;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ class AggregatorAdminTest extends AggregatorTestBase {
|
|||
*/
|
||||
public function testSettingsPage() {
|
||||
$this->drupalGet('admin/config');
|
||||
$this->clickLink('Feed aggregator');
|
||||
$this->clickLink('Aggregator');
|
||||
$this->clickLink('Settings');
|
||||
// Make sure that test plugins are present.
|
||||
$this->assertText('Test fetcher');
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Drupal\aggregator\Tests;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\views\Entity\View;
|
||||
|
||||
/**
|
||||
* Tests display of aggregator items on the page.
|
||||
|
@ -90,7 +91,7 @@ class AggregatorRenderingTest extends AggregatorTestBase {
|
|||
public function testFeedPage() {
|
||||
// Increase the number of items published in the rss.xml feed so we have
|
||||
// enough articles to test paging.
|
||||
$view = entity_load('view', 'frontpage');
|
||||
$view = View::load('frontpage');
|
||||
$display = &$view->getDisplay('feed_1');
|
||||
$display['display_options']['pager']['options']['items_per_page'] = 30;
|
||||
$view->save();
|
||||
|
|
|
@ -61,7 +61,11 @@ abstract class AggregatorTestBase extends WebTestBase {
|
|||
public function createFeed($feed_url = NULL, array $edit = array()) {
|
||||
$edit = $this->getFeedEditArray($feed_url, $edit);
|
||||
$this->drupalPostForm('aggregator/sources/add', $edit, t('Save'));
|
||||
$this->assertRaw(t('The feed %name has been added.', array('%name' => $edit['title[0][value]'])), format_string('The feed @name has been added.', array('@name' => $edit['title[0][value]'])));
|
||||
$this->assertText(t('The feed @name has been added.', array('@name' => $edit['title[0][value]'])), format_string('The feed @name has been added.', array('@name' => $edit['title[0][value]'])));
|
||||
|
||||
// Verify that the creation message contains a link to a feed.
|
||||
$view_link = $this->xpath('//div[@class="messages"]//a[contains(@href, :href)]', array(':href' => 'aggregator/sources/'));
|
||||
$this->assert(isset($view_link), 'The message area contains a link to a feed');
|
||||
|
||||
$fid = db_query("SELECT fid FROM {aggregator_feed} WHERE title = :title AND url = :url", array(':title' => $edit['title[0][value]'], ':url' => $edit['url[0][value]']))->fetchField();
|
||||
$this->assertTrue(!empty($fid), 'The feed found in database.');
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
namespace Drupal\aggregator\Tests;
|
||||
|
||||
/**
|
||||
* Tests the display of a feed on the feed aggregator list page.
|
||||
* Tests the display of a feed on the Aggregator list page.
|
||||
*
|
||||
* @group aggregator
|
||||
*/
|
||||
|
|
|
@ -35,7 +35,11 @@ class UpdateFeedItemTest extends AggregatorTestBase {
|
|||
$this->assertResponse(200);
|
||||
|
||||
$this->drupalPostForm('aggregator/sources/add', $edit, t('Save'));
|
||||
$this->assertRaw(t('The feed %name has been added.', array('%name' => $edit['title[0][value]'])), format_string('The feed @name has been added.', array('@name' => $edit['title[0][value]'])));
|
||||
$this->assertText(t('The feed @name has been added.', array('@name' => $edit['title[0][value]'])), format_string('The feed @name has been added.', array('@name' => $edit['title[0][value]'])));
|
||||
|
||||
// Verify that the creation message contains a link to a feed.
|
||||
$view_link = $this->xpath('//div[@class="messages"]//a[contains(@href, :href)]', array(':href' => 'aggregator/sources/'));
|
||||
$this->assert(isset($view_link), 'The message area contains a link to a feed');
|
||||
|
||||
$fid = db_query("SELECT fid FROM {aggregator_feed} WHERE url = :url", array(':url' => $edit['url[0][value]']))->fetchField();
|
||||
$feed = Feed::load($fid);
|
||||
|
|
|
@ -24,7 +24,11 @@ class UpdateFeedTest extends AggregatorTestBase {
|
|||
$edit[$same_field] = $feed->{$same_field}->value;
|
||||
}
|
||||
$this->drupalPostForm('aggregator/sources/' . $feed->id() . '/configure', $edit, t('Save'));
|
||||
$this->assertRaw(t('The feed %name has been updated.', array('%name' => $edit['title[0][value]'])), format_string('The feed %name has been updated.', array('%name' => $edit['title[0][value]'])));
|
||||
$this->assertText(t('The feed @name has been updated.', array('@name' => $edit['title[0][value]'])), format_string('The feed %name has been updated.', array('%name' => $edit['title[0][value]'])));
|
||||
|
||||
// Verify that the creation message contains a link to a feed.
|
||||
$view_link = $this->xpath('//div[@class="messages"]//a[contains(@href, :href)]', array(':href' => 'aggregator/sources/'));
|
||||
$this->assert(isset($view_link), 'The message area contains a link to a feed');
|
||||
|
||||
// Check feed data.
|
||||
$this->assertUrl($feed->url('canonical', ['absolute' => TRUE]));
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\aggregator\Unit\Plugin {
|
||||
namespace Drupal\Tests\aggregator\Unit\Plugin;
|
||||
|
||||
use Drupal\aggregator\Form\SettingsForm;
|
||||
use Drupal\Core\Form\FormState;
|
||||
|
@ -105,11 +105,9 @@ class AggregatorPluginSettingsBaseTest extends UnitTestCase {
|
|||
|
||||
}
|
||||
|
||||
}
|
||||
// @todo Delete after https://www.drupal.org/node/2278383 is in.
|
||||
namespace Drupal\Core\Form;
|
||||
|
||||
namespace {
|
||||
// @todo Delete after https://www.drupal.org/node/1858196 is in.
|
||||
if (!function_exists('drupal_set_message')) {
|
||||
function drupal_set_message() {}
|
||||
}
|
||||
if (!function_exists('drupal_set_message')) {
|
||||
function drupal_set_message() {}
|
||||
}
|
||||
|
|
|
@ -4,3 +4,5 @@ description: 'Provides the HTTP Basic authentication provider'
|
|||
package: Web services
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- user
|
||||
|
|
|
@ -16,8 +16,7 @@ services:
|
|||
public: false
|
||||
class: \Drupal\big_pipe\Render\BigPipeResponseAttachmentsProcessor
|
||||
decorates: html_response.attachments_processor
|
||||
decoration_inner_name: html_response.attachments_processor.original
|
||||
arguments: ['@html_response.attachments_processor.original', '@asset.resolver', '@config.factory', '@asset.css.collection_renderer', '@asset.js.collection_renderer', '@request_stack', '@renderer', '@module_handler']
|
||||
arguments: ['@html_response.attachments_processor.big_pipe.inner', '@asset.resolver', '@config.factory', '@asset.css.collection_renderer', '@asset.js.collection_renderer', '@request_stack', '@renderer', '@module_handler']
|
||||
|
||||
route_subscriber.no_big_pipe:
|
||||
class: Drupal\big_pipe\EventSubscriber\NoBigPipeRouteAlterSubscriber
|
||||
|
|
|
@ -244,7 +244,7 @@ class BigPipePlaceholderTestCases {
|
|||
'command' => 'insert',
|
||||
'method' => 'replaceWith',
|
||||
'selector' => '[data-big-pipe-placeholder-id="timecurrent-timetime"]',
|
||||
'data' => '<time datetime=1991-03-14"></time>',
|
||||
'data' => '<time datetime="1991-03-14"></time>',
|
||||
'settings' => NULL,
|
||||
],
|
||||
];
|
||||
|
@ -258,7 +258,7 @@ class BigPipePlaceholderTestCases {
|
|||
],
|
||||
],
|
||||
];
|
||||
$current_time->embeddedHtmlResponse = '<time datetime=1991-03-14"></time>';
|
||||
$current_time->embeddedHtmlResponse = '<time datetime="1991-03-14"></time>';
|
||||
|
||||
|
||||
// 6. Edge case: #lazy_builder that throws an exception.
|
||||
|
|
|
@ -85,7 +85,7 @@ class BigPipeTestController {
|
|||
*/
|
||||
public static function currentTime() {
|
||||
return [
|
||||
'#markup' => '<time datetime=' . date('Y-m-d', 668948400) . '"></time>',
|
||||
'#markup' => '<time datetime="' . date('Y-m-d', 668948400) . '"></time>',
|
||||
'#cache' => ['max-age' => 0]
|
||||
];
|
||||
}
|
||||
|
|
|
@ -109,12 +109,12 @@ function block_themes_installed($theme_list) {
|
|||
*/
|
||||
function block_theme_initialize($theme) {
|
||||
// Initialize theme's blocks if none already registered.
|
||||
$has_blocks = entity_load_multiple_by_properties('block', array('theme' => $theme));
|
||||
$has_blocks = \Drupal::entityTypeManager()->getStorage('block')->loadByProperties(array('theme' => $theme));
|
||||
if (!$has_blocks) {
|
||||
$default_theme = \Drupal::config('system.theme')->get('default');
|
||||
// Apply only to new theme's visible regions.
|
||||
$regions = system_region_list($theme, REGIONS_VISIBLE);
|
||||
$default_theme_blocks = entity_load_multiple_by_properties('block', array('theme' => $default_theme));
|
||||
$default_theme_blocks = \Drupal::entityTypeManager()->getStorage('block')->loadByProperties(array('theme' => $default_theme));
|
||||
foreach ($default_theme_blocks as $default_theme_block_id => $default_theme_block) {
|
||||
if (strpos($default_theme_block_id, $default_theme . '_') === 0) {
|
||||
$id = str_replace($default_theme, $theme, $default_theme_block_id);
|
||||
|
@ -141,7 +141,7 @@ function block_rebuild() {
|
|||
if ($data->status) {
|
||||
$regions = system_region_list($theme);
|
||||
/** @var \Drupal\block\BlockInterface[] $blocks */
|
||||
$blocks = entity_load_multiple_by_properties('block', ['theme' => $theme]);
|
||||
$blocks = \Drupal::entityTypeManager()->getStorage('block')->loadByProperties(['theme' => $theme]);
|
||||
foreach ($blocks as $block_id => $block) {
|
||||
// Disable blocks in invalid regions.
|
||||
$region = $block->getRegion();
|
||||
|
|
|
@ -63,17 +63,19 @@ process:
|
|||
region:
|
||||
plugin: block_region
|
||||
source:
|
||||
- region
|
||||
- theme
|
||||
- '@theme'
|
||||
region_map:
|
||||
left: sidebar_first
|
||||
right: sidebar_second
|
||||
sidebar_first: sidebar_first
|
||||
sidebar_second: sidebar_second
|
||||
help: help
|
||||
header: header
|
||||
footer: footer
|
||||
- region
|
||||
map:
|
||||
garland:
|
||||
bartik:
|
||||
# Garland 6.x --> Bartik 8.x
|
||||
header: header
|
||||
footer: footer_fifth
|
||||
left: sidebar_first
|
||||
right: sidebar_second
|
||||
# If mapping fails, put the block in the content region.
|
||||
default_value: content
|
||||
weight: weight
|
||||
settings:
|
||||
plugin: block_settings
|
||||
|
|
|
@ -67,17 +67,24 @@ process:
|
|||
region:
|
||||
plugin: block_region
|
||||
source:
|
||||
- region
|
||||
- theme
|
||||
- '@theme'
|
||||
region_map:
|
||||
left: sidebar_first
|
||||
right: sidebar_second
|
||||
sidebar_first: sidebar_first
|
||||
sidebar_second: sidebar_second
|
||||
help: help
|
||||
header: header
|
||||
footer: footer
|
||||
- region
|
||||
map:
|
||||
bartik:
|
||||
bartik:
|
||||
# Bartik 7.x --> Bartik 8.x
|
||||
featured: featured_top
|
||||
triptych_first: featured_bottom_first
|
||||
triptych_middle: featured_bottom_second
|
||||
triptych_last: featured_bottom_third
|
||||
footer_firstcolumn: footer_first
|
||||
footer_secondcolumn: footer_second
|
||||
footer_thirdcolumn: footer_third
|
||||
footer_fourthcolumn: footer_fourth
|
||||
footer: footer_fifth
|
||||
# If mapping fails, put the block in the content region.
|
||||
default_value: content
|
||||
weight: weight
|
||||
settings:
|
||||
plugin: block_settings
|
||||
|
|
|
@ -3,15 +3,18 @@
|
|||
namespace Drupal\block;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Core\Plugin\PluginFormFactoryInterface;
|
||||
use Drupal\Core\Block\BlockPluginInterface;
|
||||
use Drupal\Core\Entity\EntityForm;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Executable\ExecutableManagerInterface;
|
||||
use Drupal\Core\Extension\ThemeHandlerInterface;
|
||||
use Drupal\Core\Form\FormState;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Form\SubformState;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\Plugin\ContextAwarePluginInterface;
|
||||
use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
|
||||
use Drupal\Core\Plugin\PluginWithFormsInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
|
@ -68,6 +71,13 @@ class BlockForm extends EntityForm {
|
|||
*/
|
||||
protected $contextRepository;
|
||||
|
||||
/**
|
||||
* The plugin form manager.
|
||||
*
|
||||
* @var \Drupal\Core\Plugin\PluginFormFactoryInterface
|
||||
*/
|
||||
protected $pluginFormFactory;
|
||||
|
||||
/**
|
||||
* Constructs a BlockForm object.
|
||||
*
|
||||
|
@ -81,13 +91,16 @@ class BlockForm extends EntityForm {
|
|||
* The language manager.
|
||||
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
|
||||
* The theme handler.
|
||||
* @param \Drupal\Core\Plugin\PluginFormFactoryInterface $plugin_form_manager
|
||||
* The plugin form manager.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager, ExecutableManagerInterface $manager, ContextRepositoryInterface $context_repository, LanguageManagerInterface $language, ThemeHandlerInterface $theme_handler) {
|
||||
public function __construct(EntityManagerInterface $entity_manager, ExecutableManagerInterface $manager, ContextRepositoryInterface $context_repository, LanguageManagerInterface $language, ThemeHandlerInterface $theme_handler, PluginFormFactoryInterface $plugin_form_manager) {
|
||||
$this->storage = $entity_manager->getStorage('block');
|
||||
$this->manager = $manager;
|
||||
$this->contextRepository = $context_repository;
|
||||
$this->language = $language;
|
||||
$this->themeHandler = $theme_handler;
|
||||
$this->pluginFormFactory = $plugin_form_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -99,7 +112,8 @@ class BlockForm extends EntityForm {
|
|||
$container->get('plugin.manager.condition'),
|
||||
$container->get('context.repository'),
|
||||
$container->get('language_manager'),
|
||||
$container->get('theme_handler')
|
||||
$container->get('theme_handler'),
|
||||
$container->get('plugin_form.factory')
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -120,7 +134,9 @@ class BlockForm extends EntityForm {
|
|||
$form_state->setTemporaryValue('gathered_contexts', $this->contextRepository->getAvailableContexts());
|
||||
|
||||
$form['#tree'] = TRUE;
|
||||
$form['settings'] = $entity->getPlugin()->buildConfigurationForm(array(), $form_state);
|
||||
$form['settings'] = [];
|
||||
$subform_state = SubformState::createForSubform($form['settings'], $form, $form_state);
|
||||
$form['settings'] = $this->getPluginForm($entity->getPlugin())->buildConfigurationForm($form['settings'], $subform_state);
|
||||
$form['visibility'] = $this->buildVisibilityInterface([], $form_state);
|
||||
|
||||
// If creating a new block, calculate a safe default machine name.
|
||||
|
@ -164,6 +180,13 @@ class BlockForm extends EntityForm {
|
|||
);
|
||||
}
|
||||
|
||||
// Hidden weight setting.
|
||||
$weight = $entity->isNew() ? $this->getRequest()->query->get('weight', 0) : $entity->getWeight();
|
||||
$form['weight'] = array(
|
||||
'#type' => 'hidden',
|
||||
'#default_value' => $weight,
|
||||
);
|
||||
|
||||
// Region settings.
|
||||
$entity_region = $entity->getRegion();
|
||||
$region = $entity->isNew() ? $this->getRequest()->query->get('region', $entity_region) : $entity_region;
|
||||
|
@ -278,13 +301,10 @@ class BlockForm extends EntityForm {
|
|||
public function validateForm(array &$form, FormStateInterface $form_state) {
|
||||
parent::validateForm($form, $form_state);
|
||||
|
||||
$form_state->setValue('weight', (int) $form_state->getValue('weight'));
|
||||
// The Block Entity form puts all block plugin form elements in the
|
||||
// settings form element, so just pass that to the block for validation.
|
||||
$settings = (new FormState())->setValues($form_state->getValue('settings'));
|
||||
// Call the plugin validate handler.
|
||||
$this->entity->getPlugin()->validateConfigurationForm($form, $settings);
|
||||
// Update the original form values.
|
||||
$form_state->setValue('settings', $settings->getValues());
|
||||
$this->getPluginForm($this->entity->getPlugin())->validateConfigurationForm($form['settings'], SubformState::createForSubform($form['settings'], $form, $form_state));
|
||||
$this->validateVisibility($form, $form_state);
|
||||
}
|
||||
|
||||
|
@ -308,11 +328,7 @@ class BlockForm extends EntityForm {
|
|||
|
||||
// Allow the condition to validate the form.
|
||||
$condition = $form_state->get(['conditions', $condition_id]);
|
||||
$condition_values = (new FormState())
|
||||
->setValues($values);
|
||||
$condition->validateConfigurationForm($form, $condition_values);
|
||||
// Update the original form values.
|
||||
$form_state->setValue(['visibility', $condition_id], $condition_values->getValues());
|
||||
$condition->validateConfigurationForm($form['visibility'][$condition_id], SubformState::createForSubform($form['visibility'][$condition_id], $form, $form_state));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -325,37 +341,17 @@ class BlockForm extends EntityForm {
|
|||
$entity = $this->entity;
|
||||
// The Block Entity form puts all block plugin form elements in the
|
||||
// settings form element, so just pass that to the block for submission.
|
||||
// @todo Find a way to avoid this manipulation.
|
||||
$settings = (new FormState())->setValues($form_state->getValue('settings'));
|
||||
|
||||
$sub_form_state = SubformState::createForSubform($form['settings'], $form, $form_state);
|
||||
// Call the plugin submit handler.
|
||||
$entity->getPlugin()->submitConfigurationForm($form, $settings);
|
||||
$block = $entity->getPlugin();
|
||||
$this->getPluginForm($block)->submitConfigurationForm($form, $sub_form_state);
|
||||
// If this block is context-aware, set the context mapping.
|
||||
if ($block instanceof ContextAwarePluginInterface && $block->getContextDefinitions()) {
|
||||
$context_mapping = $settings->getValue('context_mapping', []);
|
||||
$context_mapping = $sub_form_state->getValue('context_mapping', []);
|
||||
$block->setContextMapping($context_mapping);
|
||||
}
|
||||
// Update the original form values.
|
||||
$form_state->setValue('settings', $settings->getValues());
|
||||
|
||||
// Submit visibility condition settings.
|
||||
foreach ($form_state->getValue('visibility') as $condition_id => $values) {
|
||||
// Allow the condition to submit the form.
|
||||
$condition = $form_state->get(['conditions', $condition_id]);
|
||||
$condition_values = (new FormState())
|
||||
->setValues($values);
|
||||
$condition->submitConfigurationForm($form, $condition_values);
|
||||
if ($condition instanceof ContextAwarePluginInterface) {
|
||||
$context_mapping = isset($values['context_mapping']) ? $values['context_mapping'] : [];
|
||||
$condition->setContextMapping($context_mapping);
|
||||
}
|
||||
// Update the original form values.
|
||||
$condition_configuration = $condition->getConfiguration();
|
||||
$form_state->setValue(['visibility', $condition_id], $condition_configuration);
|
||||
// Update the visibility conditions on the block.
|
||||
$entity->getVisibilityConditions()->addInstanceId($condition_id, $condition_configuration);
|
||||
}
|
||||
$this->submitVisibility($form, $form_state);
|
||||
|
||||
// Save the settings of the plugin.
|
||||
$entity->save();
|
||||
|
@ -370,6 +366,36 @@ class BlockForm extends EntityForm {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to independently submit the visibility UI.
|
||||
*
|
||||
* @param array $form
|
||||
* A nested array form elements comprising the form.
|
||||
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||
* The current state of the form.
|
||||
*/
|
||||
protected function submitVisibility(array $form, FormStateInterface $form_state) {
|
||||
foreach ($form_state->getValue('visibility') as $condition_id => $values) {
|
||||
// Allow the condition to submit the form.
|
||||
$condition = $form_state->get(['conditions', $condition_id]);
|
||||
$condition->submitConfigurationForm($form['visibility'][$condition_id], SubformState::createForSubform($form['visibility'][$condition_id], $form, $form_state));
|
||||
|
||||
// Setting conditions' context mappings is the plugins' responsibility.
|
||||
// This code exists for backwards compatibility, because
|
||||
// \Drupal\Core\Condition\ConditionPluginBase::submitConfigurationForm()
|
||||
// did not set its own mappings until Drupal 8.2
|
||||
// @todo Remove the code that sets context mappings in Drupal 9.0.0.
|
||||
if ($condition instanceof ContextAwarePluginInterface) {
|
||||
$context_mapping = isset($values['context_mapping']) ? $values['context_mapping'] : [];
|
||||
$condition->setContextMapping($context_mapping);
|
||||
}
|
||||
|
||||
$condition_configuration = $condition->getConfiguration();
|
||||
// Update the visibility conditions on the block.
|
||||
$this->entity->getVisibilityConditions()->addInstanceId($condition_id, $condition_configuration);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a unique machine name for a block.
|
||||
*
|
||||
|
@ -402,4 +428,20 @@ class BlockForm extends EntityForm {
|
|||
return $machine_default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the plugin form for a given block and operation.
|
||||
*
|
||||
* @param \Drupal\Core\Block\BlockPluginInterface $block
|
||||
* The block plugin.
|
||||
*
|
||||
* @return \Drupal\Core\Plugin\PluginFormInterface
|
||||
* The plugin form for the block.
|
||||
*/
|
||||
protected function getPluginForm(BlockPluginInterface $block) {
|
||||
if ($block instanceof PluginWithFormsInterface) {
|
||||
return $this->pluginFormFactory->createInstance($block, 'configure');
|
||||
}
|
||||
return $block;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ use Drupal\Core\Extension\ModuleHandlerInterface;
|
|||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\Plugin\ContextAwarePluginInterface;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\block\Entity\Block;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
|
@ -186,7 +187,7 @@ class BlockViewBuilder extends EntityViewBuilder {
|
|||
* A render array with a #pre_render callback to render the block.
|
||||
*/
|
||||
public static function lazyBuilder($entity_id, $view_mode) {
|
||||
return static::buildPreRenderableBlock(entity_load('block', $entity_id), \Drupal::service('module_handler'));
|
||||
return static::buildPreRenderableBlock(Block::load($entity_id), \Drupal::service('module_handler'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,6 +8,7 @@ use Drupal\Core\Controller\ControllerBase;
|
|||
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
|
||||
use Drupal\Core\Menu\LocalActionManagerInterface;
|
||||
use Drupal\Core\Plugin\Context\LazyContextRepository;
|
||||
use Drupal\Core\Routing\RedirectDestinationInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
@ -46,6 +47,13 @@ class BlockLibraryController extends ControllerBase {
|
|||
*/
|
||||
protected $localActionManager;
|
||||
|
||||
/**
|
||||
* The redirect destination.
|
||||
*
|
||||
* @var \Drupal\Core\Routing\RedirectDestinationInterface
|
||||
*/
|
||||
protected $redirectDestination;
|
||||
|
||||
/**
|
||||
* Constructs a BlockLibraryController object.
|
||||
*
|
||||
|
@ -57,12 +65,15 @@ class BlockLibraryController extends ControllerBase {
|
|||
* The current route match.
|
||||
* @param \Drupal\Core\Menu\LocalActionManagerInterface $local_action_manager
|
||||
* The local action manager.
|
||||
* @param \Drupal\Core\Routing\RedirectDestinationInterface $redirect_destination
|
||||
* The redirect destination.
|
||||
*/
|
||||
public function __construct(BlockManagerInterface $block_manager, LazyContextRepository $context_repository, RouteMatchInterface $route_match, LocalActionManagerInterface $local_action_manager) {
|
||||
public function __construct(BlockManagerInterface $block_manager, LazyContextRepository $context_repository, RouteMatchInterface $route_match, LocalActionManagerInterface $local_action_manager, RedirectDestinationInterface $redirect_destination) {
|
||||
$this->blockManager = $block_manager;
|
||||
$this->routeMatch = $route_match;
|
||||
$this->localActionManager = $local_action_manager;
|
||||
$this->contextRepository = $context_repository;
|
||||
$this->redirectDestination = $redirect_destination;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -73,7 +84,8 @@ class BlockLibraryController extends ControllerBase {
|
|||
$container->get('plugin.manager.block'),
|
||||
$container->get('context.repository'),
|
||||
$container->get('current_route_match'),
|
||||
$container->get('plugin.manager.menu.local_action')
|
||||
$container->get('plugin.manager.menu.local_action'),
|
||||
$container->get('redirect.destination')
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -107,6 +119,7 @@ class BlockLibraryController extends ControllerBase {
|
|||
$definitions = $this->blockManager->getSortedDefinitions($definitions);
|
||||
|
||||
$region = $request->query->get('region');
|
||||
$weight = $request->query->get('weight');
|
||||
$rows = [];
|
||||
foreach ($definitions as $plugin_id => $plugin_definition) {
|
||||
$row = [];
|
||||
|
@ -132,6 +145,13 @@ class BlockLibraryController extends ControllerBase {
|
|||
if ($region) {
|
||||
$links['add']['query']['region'] = $region;
|
||||
}
|
||||
if (isset($weight)) {
|
||||
$links['add']['query']['weight'] = $weight;
|
||||
}
|
||||
$destination = $this->redirectDestination->get();
|
||||
if ($destination) {
|
||||
$links['add']['query']['destination'] = $destination;
|
||||
}
|
||||
$row['operations']['data'] = [
|
||||
'#type' => 'operations',
|
||||
'#links' => $links,
|
||||
|
|
|
@ -4,7 +4,7 @@ namespace Drupal\block\Plugin\migrate\process;
|
|||
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\ProcessPluginBase;
|
||||
use Drupal\migrate\Plugin\migrate\process\StaticMap;
|
||||
use Drupal\migrate\Row;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
|
@ -13,7 +13,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
|||
* id = "block_region"
|
||||
* )
|
||||
*/
|
||||
class BlockRegion extends ProcessPluginBase implements ContainerFactoryPluginInterface {
|
||||
class BlockRegion extends StaticMap implements ContainerFactoryPluginInterface {
|
||||
|
||||
/**
|
||||
* List of regions, keyed by theme.
|
||||
|
@ -56,7 +56,7 @@ class BlockRegion extends ProcessPluginBase implements ContainerFactoryPluginInt
|
|||
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
|
||||
// Set the destination region, based on the source region and theme as well
|
||||
// as the current destination default theme.
|
||||
list($region, $source_theme, $destination_theme) = $value;
|
||||
list($source_theme, $destination_theme, $region) = $value;
|
||||
|
||||
// Theme is the same on both source and destination, so ensure that the
|
||||
// region exists in the destination theme.
|
||||
|
@ -66,15 +66,8 @@ class BlockRegion extends ProcessPluginBase implements ContainerFactoryPluginInt
|
|||
}
|
||||
}
|
||||
|
||||
// If the source and destination theme are different, try to use the
|
||||
// mappings defined in the configuration.
|
||||
$region_map = $this->configuration['region_map'];
|
||||
if (isset($region_map[$region])) {
|
||||
return $region_map[$region];
|
||||
}
|
||||
|
||||
// Oh well, we tried. Put the block in the main content region.
|
||||
return 'content';
|
||||
// Fall back to static mapping.
|
||||
return parent::transform($value, $migrate_executable, $row, $destination_property);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,11 +24,11 @@ class BlockTheme extends ProcessPluginBase implements ContainerFactoryPluginInte
|
|||
*/
|
||||
protected $configFactory;
|
||||
|
||||
/**
|
||||
* Contains the system.theme configuration object.
|
||||
*
|
||||
* @var \Drupal\Core\Config\Config
|
||||
*/
|
||||
/**
|
||||
* Contains the system.theme configuration object.
|
||||
*
|
||||
* @var \Drupal\Core\Config\Config
|
||||
*/
|
||||
protected $themeConfig;
|
||||
|
||||
/**
|
||||
|
|
|
@ -63,7 +63,7 @@ class BlockCacheTest extends WebTestBase {
|
|||
$this->normalUserAlt->save();
|
||||
|
||||
// Enable our test block.
|
||||
$this->block = $this->drupalPlaceBlock('test_cache');
|
||||
$this->block = $this->drupalPlaceBlock('test_cache');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -25,7 +25,7 @@ class BlockFormInBlockTest extends WebTestBase {
|
|||
parent::setUp();
|
||||
|
||||
// Enable our test block.
|
||||
$this->drupalPlaceBlock('test_form_in_block');
|
||||
$this->drupalPlaceBlock('test_form_in_block');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace Drupal\block\Tests;
|
|||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\block\Entity\Block;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\user\Entity\Role;
|
||||
use Drupal\user\RoleInterface;
|
||||
|
||||
|
@ -127,6 +128,94 @@ class BlockTest extends BlockTestBase {
|
|||
$this->assertNoText($title, 'Block was not displayed to anonymous users on the front page.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests adding a block from the library page with a destination query string.
|
||||
*/
|
||||
public function testAddBlockFromLibrary() {
|
||||
$default_theme = $this->config('system.theme')->get('default');
|
||||
$help_url = Url::fromRoute('help.page', ['name' => 'block']);
|
||||
// Set up the request so we land on the block help page after creation.
|
||||
$options = [
|
||||
'query' => [
|
||||
'region' => 'sidebar_first',
|
||||
'destination' => $help_url->toString(),
|
||||
],
|
||||
];
|
||||
$this->drupalGet(Url::fromRoute('block.admin_library', ['theme' => $default_theme], $options));
|
||||
|
||||
$block_name = 'system_powered_by_block';
|
||||
$add_url = Url::fromRoute('block.admin_add', ['plugin_id' => $block_name, 'theme' => $default_theme]);
|
||||
$links = $this->xpath('//a[contains(@href, :href)]', [':href' => $add_url->toString()]);
|
||||
$this->assertEqual(1, count($links), 'Found one matching link');
|
||||
|
||||
list($path, $query_string) = explode('?', $links[0]['href'], 2);
|
||||
parse_str($query_string, $query_parts);
|
||||
$this->assertEqual(t('Place block'), (string) $links[0]);
|
||||
$this->assertEqual($help_url->toString(), $query_parts['destination'], 'Expected destination query string is in href');
|
||||
|
||||
// Create a random title for the block.
|
||||
$title = $this->randomMachineName(8);
|
||||
$block_id = strtolower($this->randomMachineName(8));
|
||||
$edit = [
|
||||
'id' => $block_id,
|
||||
'settings[label]' => $title,
|
||||
];
|
||||
// Create the block using the link parsed from the library page.
|
||||
$this->drupalPostForm($this->getAbsoluteUrl($links[0]['href']), $edit, t('Save block'));
|
||||
// Verify that we are redirected according to the original request.
|
||||
$this->assertUrl($help_url);
|
||||
|
||||
// Ensure that the block was created.
|
||||
/** @var \Drupal\block\BlockInterface $block */
|
||||
$block = Block::load($block_id);
|
||||
$this->assertEqual($title, $block->label(), 'Found the block with expected title.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests adding a block from the library page with a weight query string.
|
||||
*/
|
||||
public function testAddBlockFromLibraryWithWeight() {
|
||||
$default_theme = $this->config('system.theme')->get('default');
|
||||
// Test one positive, zero, and one negative weight.
|
||||
foreach (['7', '0', '-9'] as $weight) {
|
||||
$options = [
|
||||
'query' => [
|
||||
'region' => 'sidebar_first',
|
||||
'weight' => $weight,
|
||||
],
|
||||
];
|
||||
$this->drupalGet(Url::fromRoute('block.admin_library', ['theme' => $default_theme], $options));
|
||||
|
||||
$block_name = 'system_powered_by_block';
|
||||
$add_url = Url::fromRoute('block.admin_add', [
|
||||
'plugin_id' => $block_name,
|
||||
'theme' => $default_theme
|
||||
]);
|
||||
$links = $this->xpath('//a[contains(@href, :href)]', [':href' => $add_url->toString()]);
|
||||
$this->assertEqual(1, count($links), 'Found one matching link.');
|
||||
$this->assertEqual(t('Place block'), (string) $links[0], 'Found the expected link text.');
|
||||
|
||||
list($path, $query_string) = explode('?', $links[0]['href'], 2);
|
||||
parse_str($query_string, $query_parts);
|
||||
$this->assertEqual($weight, $query_parts['weight'], 'Found the expected weight query string.');
|
||||
|
||||
// Create a random title for the block.
|
||||
$title = $this->randomMachineName(8);
|
||||
$block_id = strtolower($this->randomMachineName(8));
|
||||
$edit = [
|
||||
'id' => $block_id,
|
||||
'settings[label]' => $title,
|
||||
];
|
||||
// Create the block using the link parsed from the library page.
|
||||
$this->drupalPostForm($this->getAbsoluteUrl($links[0]['href']), $edit, t('Save block'));
|
||||
|
||||
// Ensure that the block was created with the expected weight.
|
||||
/** @var \Drupal\block\BlockInterface $block */
|
||||
$block = Block::load($block_id);
|
||||
$this->assertEqual($weight, $block->getWeight(), 'Found the block with expected weight.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test configuring and moving a module-define block to specific regions.
|
||||
*/
|
||||
|
|
|
@ -288,4 +288,20 @@ class BlockUiTest extends WebTestBase {
|
|||
$this->assertUrl('admin/structure/block/list/classy');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if validation errors are passed plugin form to the parent form.
|
||||
*/
|
||||
public function testBlockValidateErrors() {
|
||||
$this->drupalPostForm('admin/structure/block/add/test_settings_validation/classy', ['settings[digits]' => 'abc'], t('Save block'));
|
||||
|
||||
$arguments = [':message' => 'Only digits are allowed'];
|
||||
$pattern = '//div[contains(@class,"messages messages--error")]/div[contains(text()[2],:message)]';
|
||||
$elements = $this->xpath($pattern, $arguments);
|
||||
$this->assertTrue($elements, 'Plugin error message found in parent form.');
|
||||
|
||||
$error_class_pattern = '//div[contains(@class,"form-item-settings-digits")]/input[contains(@class,"error")]';
|
||||
$error_class = $this->xpath($error_class_pattern);
|
||||
$this->assertTrue($error_class, 'Plugin error class found in parent form.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\block_test\Plugin\Block;
|
||||
|
||||
use Drupal\Core\Block\BlockBase;
|
||||
|
||||
/**
|
||||
* Provides a block with multiple forms.
|
||||
*
|
||||
* @Block(
|
||||
* id = "test_multiple_forms_block",
|
||||
* forms = {
|
||||
* "secondary" = "\Drupal\block_test\PluginForm\EmptyBlockForm"
|
||||
* },
|
||||
* admin_label = @Translation("Multiple forms test block")
|
||||
* )
|
||||
*/
|
||||
class TestMultipleFormsBlock extends BlockBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build() {
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\block_test\Plugin\Block;
|
||||
|
||||
use Drupal\Core\Block\BlockBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
|
||||
/**
|
||||
* Provides a test settings validation block.
|
||||
*
|
||||
* @Block(
|
||||
* id = "test_settings_validation",
|
||||
* admin_label = @Translation("Test settings validation block"),
|
||||
* )
|
||||
*/
|
||||
class TestSettingsValidationBlock extends BlockBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function blockForm($form, FormStateInterface $form_state) {
|
||||
return ['digits' => ['#type' => 'textfield']] + $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function blockValidate($form, FormStateInterface $form_state) {
|
||||
if (!ctype_digit($form_state->getValue('digits'))) {
|
||||
$form_state->setErrorByName('digits', $this->t('Only digits are allowed'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build() {
|
||||
return ['#markup' => 'foo'];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\block_test\PluginForm;
|
||||
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Plugin\PluginFormBase;
|
||||
|
||||
/**
|
||||
* Provides a form for a block that is empty.
|
||||
*/
|
||||
class EmptyBlockForm extends PluginFormBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
|
||||
// Intentionally empty.
|
||||
}
|
||||
|
||||
}
|
|
@ -49,6 +49,7 @@ class MigrateBlockTest extends MigrateDrupal6TestBase {
|
|||
'd6_user_role',
|
||||
'd6_block',
|
||||
]);
|
||||
block_rebuild();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -68,18 +69,21 @@ class MigrateBlockTest extends MigrateDrupal6TestBase {
|
|||
* The block label.
|
||||
* @param string $label_display
|
||||
* The block label display setting.
|
||||
* @param bool $status
|
||||
* (optional) Whether the block is expected to be enabled.
|
||||
*/
|
||||
public function assertEntity($id, $visibility, $region, $theme, $weight, $label, $label_display) {
|
||||
public function assertEntity($id, $visibility, $region, $theme, $weight, $label, $label_display, $status = TRUE) {
|
||||
$block = Block::load($id);
|
||||
$this->assertTrue($block instanceof Block);
|
||||
$this->assertIdentical($visibility, $block->getVisibility());
|
||||
$this->assertIdentical($region, $block->getRegion());
|
||||
$this->assertIdentical($theme, $block->getTheme());
|
||||
$this->assertIdentical($weight, $block->getWeight());
|
||||
$this->assertSame($visibility, $block->getVisibility());
|
||||
$this->assertSame($region, $block->getRegion());
|
||||
$this->assertSame($theme, $block->getTheme());
|
||||
$this->assertSame($weight, $block->getWeight());
|
||||
$this->assertSame($status, $block->status());
|
||||
|
||||
$config = $this->config('block.block.' . $id);
|
||||
$this->assertIdentical($label, $config->get('settings.label'));
|
||||
$this->assertIdentical($label_display, $config->get('settings.label_display'));
|
||||
$this->assertSame($label, $config->get('settings.label'));
|
||||
$this->assertSame($label_display, $config->get('settings.label_display'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -122,7 +126,7 @@ class MigrateBlockTest extends MigrateDrupal6TestBase {
|
|||
$visibility['request_path']['id'] = 'request_path';
|
||||
$visibility['request_path']['negate'] = TRUE;
|
||||
$visibility['request_path']['pages'] = '/node/1';
|
||||
$this->assertEntity('system', $visibility, 'footer', 'bartik', -5, '', '0');
|
||||
$this->assertEntity('system', $visibility, 'footer_fifth', 'bartik', -5, '', '0');
|
||||
|
||||
// Check menu blocks
|
||||
$visibility = [];
|
||||
|
@ -137,7 +141,10 @@ class MigrateBlockTest extends MigrateDrupal6TestBase {
|
|||
$visibility['request_path']['id'] = 'request_path';
|
||||
$visibility['request_path']['negate'] = FALSE;
|
||||
$visibility['request_path']['pages'] = '/node';
|
||||
$this->assertEntity('block_1', $visibility, 'sidebar_second', 'bluemarine', -4, 'Another Static Block', 'visible');
|
||||
// bluemarine does not exist in Drupal 8 and the d6_block migration defines
|
||||
// no mapping for its regions, so this block should have been defaulted
|
||||
// to the 'content' region.
|
||||
$this->assertEntity('block_1', $visibility, 'content', 'bluemarine', -4, 'Another Static Block', 'visible');
|
||||
|
||||
$visibility = [];
|
||||
$this->assertEntity('block_2', $visibility, 'right', 'test_theme', -7, '', '0');
|
||||
|
|
|
@ -13,7 +13,7 @@ use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
|
|||
*/
|
||||
class MigrateBlockTest extends MigrateDrupal7TestBase {
|
||||
|
||||
/**
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
|
@ -53,6 +53,7 @@ class MigrateBlockTest extends MigrateDrupal7TestBase {
|
|||
'd7_custom_block',
|
||||
'd7_block',
|
||||
]);
|
||||
block_rebuild();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -76,29 +77,32 @@ class MigrateBlockTest extends MigrateDrupal7TestBase {
|
|||
* The block label.
|
||||
* @param string $label_display
|
||||
* The block label display setting.
|
||||
* @param bool $status
|
||||
* (optional) Whether the block is expected to be enabled.
|
||||
*/
|
||||
public function assertEntity($id, $plugin_id, array $roles, $pages, $region, $theme, $weight, $label, $label_display) {
|
||||
public function assertEntity($id, $plugin_id, array $roles, $pages, $region, $theme, $weight, $label, $label_display, $status = TRUE) {
|
||||
$block = Block::load($id);
|
||||
$this->assertTrue($block instanceof Block);
|
||||
/** @var \Drupal\block\BlockInterface $block */
|
||||
$this->assertIdentical($plugin_id, $block->getPluginId());
|
||||
$this->assertSame($plugin_id, $block->getPluginId());
|
||||
|
||||
$visibility = $block->getVisibility();
|
||||
if ($roles) {
|
||||
$this->assertIdentical($roles, array_values($visibility['user_role']['roles']));
|
||||
$this->assertIdentical('@user.current_user_context:current_user', $visibility['user_role']['context_mapping']['user']);
|
||||
$this->assertSame($roles, array_values($visibility['user_role']['roles']));
|
||||
$this->assertSame('@user.current_user_context:current_user', $visibility['user_role']['context_mapping']['user']);
|
||||
}
|
||||
if ($pages) {
|
||||
$this->assertIdentical($pages, $visibility['request_path']['pages']);
|
||||
$this->assertSame($pages, $visibility['request_path']['pages']);
|
||||
}
|
||||
|
||||
$this->assertIdentical($region, $block->getRegion());
|
||||
$this->assertIdentical($theme, $block->getTheme());
|
||||
$this->assertIdentical($weight, $block->getWeight());
|
||||
$this->assertSame($region, $block->getRegion());
|
||||
$this->assertSame($theme, $block->getTheme());
|
||||
$this->assertSame($weight, $block->getWeight());
|
||||
$this->assertSame($status, $block->status());
|
||||
|
||||
$config = $this->config('block.block.' . $id);
|
||||
$this->assertIdentical($label, $config->get('settings.label'));
|
||||
$this->assertIdentical($label_display, $config->get('settings.label_display'));
|
||||
$this->assertSame($label, $config->get('settings.label'));
|
||||
$this->assertSame($label_display, $config->get('settings.label_display'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -108,7 +112,7 @@ class MigrateBlockTest extends MigrateDrupal7TestBase {
|
|||
$this->assertEntity('bartik_system_main', 'system_main_block', [], '', 'content', 'bartik', 0, '', '0');
|
||||
$this->assertEntity('bartik_search_form', 'search_form_block', [], '', 'sidebar_first', 'bartik', -1, '', '0');
|
||||
$this->assertEntity('bartik_user_login', 'user_login_block', [], '', 'sidebar_first', 'bartik', 0, '', '0');
|
||||
$this->assertEntity('bartik_system_powered_by', 'system_powered_by_block', [], '', 'footer', 'bartik', 10, '', '0');
|
||||
$this->assertEntity('bartik_system_powered_by', 'system_powered_by_block', [], '', 'footer_fifth', 'bartik', 10, '', '0');
|
||||
$this->assertEntity('seven_system_main', 'system_main_block', [], '', 'content', 'seven', 0, '', '0');
|
||||
$this->assertEntity('seven_user_login', 'user_login_block', [], '', 'content', 'seven', 10, '', '0');
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Drupal\Tests\block\Unit;
|
||||
|
||||
use Drupal\block\BlockForm;
|
||||
use Drupal\Core\Plugin\PluginFormFactoryInterface;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
|
@ -54,6 +55,13 @@ class BlockFormTest extends UnitTestCase {
|
|||
*/
|
||||
protected $contextRepository;
|
||||
|
||||
/**
|
||||
* The plugin form manager.
|
||||
*
|
||||
* @var \Drupal\Core\Plugin\PluginFormFactoryInterface|\Prophecy\Prophecy\ProphecyInterface
|
||||
*/
|
||||
protected $pluginFormFactory;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -71,6 +79,7 @@ class BlockFormTest extends UnitTestCase {
|
|||
->method('getStorage')
|
||||
->will($this->returnValue($this->storage));
|
||||
|
||||
$this->pluginFormFactory = $this->prophesize(PluginFormFactoryInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -99,7 +108,7 @@ class BlockFormTest extends UnitTestCase {
|
|||
->method('getQuery')
|
||||
->will($this->returnValue($query));
|
||||
|
||||
$block_form_controller = new BlockForm($this->entityManager, $this->conditionManager, $this->contextRepository, $this->language, $this->themeHandler);
|
||||
$block_form_controller = new BlockForm($this->entityManager, $this->conditionManager, $this->contextRepository, $this->language, $this->themeHandler, $this->pluginFormFactory->reveal());
|
||||
|
||||
// Ensure that the block with just one other instance gets the next available
|
||||
// name suggestion.
|
||||
|
|
|
@ -18,7 +18,7 @@ class BlockRegionTest extends UnitTestCase {
|
|||
*
|
||||
* @param array $value
|
||||
* The value to transform.
|
||||
* @param \Drupal\migrate\Row|NULL $row
|
||||
* @param \Drupal\migrate\Row|null $row
|
||||
* (optional) The mocked row.
|
||||
*
|
||||
* @return array|string
|
||||
|
@ -30,14 +30,20 @@ class BlockRegionTest extends UnitTestCase {
|
|||
$row = $this->prophesize(Row::class)->reveal();
|
||||
}
|
||||
|
||||
$regions = array(
|
||||
'bartik' => array(
|
||||
'triptych_first' => 'Triptych first',
|
||||
'triptych_second' => 'Triptych second',
|
||||
'triptych_third' => 'Triptych third',
|
||||
),
|
||||
);
|
||||
$plugin = new BlockRegion(['region_map' => []], 'block_region', [], $regions);
|
||||
$configuration = [
|
||||
'map' => [
|
||||
'bartik' => [
|
||||
'bartik' => [
|
||||
'triptych_first' => 'triptych_first',
|
||||
'triptych_middle' => 'triptych_second',
|
||||
'triptych_last' => 'triptych_third',
|
||||
],
|
||||
],
|
||||
],
|
||||
'default_value' => 'content',
|
||||
];
|
||||
|
||||
$plugin = new BlockRegion($configuration, 'block_region', [], $configuration['map']['bartik']['bartik']);
|
||||
return $plugin->transform($value, $executable, $row, 'foo');
|
||||
}
|
||||
|
||||
|
@ -48,7 +54,7 @@ class BlockRegionTest extends UnitTestCase {
|
|||
* @covers ::transform
|
||||
*/
|
||||
public function testTransformSameThemeRegionExists() {
|
||||
$this->assertSame('triptych_second', $this->transform(['triptych_second', 'bartik', 'bartik']));
|
||||
$this->assertSame('triptych_second', $this->transform(['bartik', 'bartik', 'triptych_middle']));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -58,7 +64,7 @@ class BlockRegionTest extends UnitTestCase {
|
|||
* @covers ::transform
|
||||
*/
|
||||
public function testTransformSameThemeRegionNotExists() {
|
||||
$this->assertSame('content', $this->transform(['footer', 'bartik', 'bartik']));
|
||||
$this->assertSame('content', $this->transform(['bartik', 'bartik', 'footer']));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,4 +7,5 @@ core: 8.x
|
|||
dependencies:
|
||||
- block
|
||||
- text
|
||||
- user
|
||||
configure: entity.block_content.collection
|
||||
|
|
|
@ -39,3 +39,25 @@ function block_content_update_8002() {
|
|||
// are stable.
|
||||
// @see https://www.drupal.org/node/2569469
|
||||
}
|
||||
|
||||
/**
|
||||
* Add 'revision_created' and 'revision_user' fields to 'block_content' entities.
|
||||
*/
|
||||
function block_content_update_8003() {
|
||||
$revision_created = BaseFieldDefinition::create('created')
|
||||
->setLabel(t('Revision create time'))
|
||||
->setDescription(t('The time that the current revision was created.'))
|
||||
->setRevisionable(TRUE);
|
||||
|
||||
\Drupal::entityDefinitionUpdateManager()
|
||||
->installFieldStorageDefinition('revision_created', 'block_content', 'block_content', $revision_created);
|
||||
|
||||
$revision_user = BaseFieldDefinition::create('entity_reference')
|
||||
->setLabel(t('Revision user'))
|
||||
->setDescription(t('The user ID of the author of the current revision.'))
|
||||
->setSetting('target_type', 'user')
|
||||
->setRevisionable(TRUE);
|
||||
|
||||
\Drupal::entityDefinitionUpdateManager()
|
||||
->installFieldStorageDefinition('revision_user', 'block_content', 'block_content', $revision_user);
|
||||
}
|
||||
|
|
|
@ -30,3 +30,6 @@ destination:
|
|||
migration_dependencies:
|
||||
required:
|
||||
- block_content_type
|
||||
provider:
|
||||
- block_content
|
||||
- migrate_drupal
|
||||
|
|
|
@ -17,3 +17,6 @@ process:
|
|||
label: label
|
||||
destination:
|
||||
plugin: entity:block_content_type
|
||||
provider:
|
||||
- block_content
|
||||
- migrate_drupal
|
||||
|
|
|
@ -88,7 +88,7 @@ class BlockContentForm extends ContentEntityForm {
|
|||
// Set up default values, if required.
|
||||
$block_type = $this->blockContentTypeStorage->load($block->bundle());
|
||||
if (!$block->isNew()) {
|
||||
$block->setRevisionLog(NULL);
|
||||
$block->setRevisionLogMessage(NULL);
|
||||
}
|
||||
// Always use the default revision setting.
|
||||
$block->setNewRevision($block_type->shouldCreateNewRevision());
|
||||
|
@ -170,6 +170,9 @@ class BlockContentForm extends ContentEntityForm {
|
|||
// Save as a new revision if requested to do so.
|
||||
if (!$form_state->isValueEmpty('revision')) {
|
||||
$block->setNewRevision();
|
||||
// If a new revision is created, save the current user as revision author.
|
||||
$block->setRevisionCreationTime(REQUEST_TIME);
|
||||
$block->setRevisionUserId(\Drupal::currentUser()->id());
|
||||
}
|
||||
|
||||
$insert = $block->isNew();
|
||||
|
|
|
@ -4,17 +4,21 @@ namespace Drupal\block_content;
|
|||
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\EntityChangedInterface;
|
||||
use Drupal\Core\Entity\RevisionLogInterface;
|
||||
|
||||
/**
|
||||
* Provides an interface defining a custom block entity.
|
||||
*/
|
||||
interface BlockContentInterface extends ContentEntityInterface, EntityChangedInterface {
|
||||
interface BlockContentInterface extends ContentEntityInterface, EntityChangedInterface, RevisionLogInterface {
|
||||
|
||||
/**
|
||||
* Returns the block revision log message.
|
||||
*
|
||||
* @return string
|
||||
* The revision log message.
|
||||
*
|
||||
* @deprecated in Drupal 8.2.0, will be removed before Drupal 9.0.0. Use
|
||||
* \Drupal\Core\Entity\RevisionLogInterface::getRevisionLogMessage() instead.
|
||||
*/
|
||||
public function getRevisionLog();
|
||||
|
||||
|
@ -37,6 +41,9 @@ interface BlockContentInterface extends ContentEntityInterface, EntityChangedInt
|
|||
*
|
||||
* @return \Drupal\block_content\BlockContentInterface
|
||||
* The class instance that this method is called on.
|
||||
*
|
||||
* @deprecated in Drupal 8.2.0, will be removed before Drupal 9.0.0. Use
|
||||
* \Drupal\Core\Entity\RevisionLogInterface::setRevisionLogMessage() instead.
|
||||
*/
|
||||
public function setRevisionLog($revision_log);
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ use Drupal\Core\Entity\EntityStorageInterface;
|
|||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
use Drupal\block_content\BlockContentInterface;
|
||||
use Drupal\user\UserInterface;
|
||||
|
||||
/**
|
||||
* Defines the custom block entity class.
|
||||
|
@ -119,7 +120,7 @@ class BlockContent extends ContentEntityBase implements BlockContentInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInstances() {
|
||||
return entity_load_multiple_by_properties('block', array('plugin' => 'block_content:' . $this->uuid()));
|
||||
return \Drupal::entityTypeManager()->getStorage('block')->loadByProperties(array('plugin' => 'block_content:' . $this->uuid()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -132,7 +133,7 @@ class BlockContent extends ContentEntityBase implements BlockContentInterface {
|
|||
// If we are updating an existing block_content without adding a new
|
||||
// revision and the user did not supply a revision log, keep the existing
|
||||
// one.
|
||||
$record->revision_log = $this->original->getRevisionLog();
|
||||
$record->revision_log = $this->original->getRevisionLogMessage();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,35 +151,20 @@ class BlockContent extends ContentEntityBase implements BlockContentInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
|
||||
$fields['id'] = BaseFieldDefinition::create('integer')
|
||||
->setLabel(t('Custom block ID'))
|
||||
->setDescription(t('The custom block ID.'))
|
||||
->setReadOnly(TRUE)
|
||||
->setSetting('unsigned', TRUE);
|
||||
/** @var \Drupal\Core\Field\BaseFieldDefinition[] $fields */
|
||||
$fields = parent::baseFieldDefinitions($entity_type);
|
||||
|
||||
$fields['uuid'] = BaseFieldDefinition::create('uuid')
|
||||
->setLabel(t('UUID'))
|
||||
->setDescription(t('The custom block UUID.'))
|
||||
->setReadOnly(TRUE);
|
||||
$fields['id']->setLabel(t('Custom block ID'))
|
||||
->setDescription(t('The custom block ID.'));
|
||||
|
||||
$fields['revision_id'] = BaseFieldDefinition::create('integer')
|
||||
->setLabel(t('Revision ID'))
|
||||
->setDescription(t('The revision ID.'))
|
||||
->setReadOnly(TRUE)
|
||||
->setSetting('unsigned', TRUE);
|
||||
$fields['uuid']->setDescription(t('The custom block UUID.'));
|
||||
|
||||
$fields['langcode'] = BaseFieldDefinition::create('language')
|
||||
->setLabel(t('Language'))
|
||||
->setDescription(t('The custom block language code.'))
|
||||
->setTranslatable(TRUE)
|
||||
->setRevisionable(TRUE)
|
||||
->setDisplayOptions('view', array(
|
||||
'type' => 'hidden',
|
||||
))
|
||||
->setDisplayOptions('form', array(
|
||||
'type' => 'language_select',
|
||||
'weight' => 2,
|
||||
));
|
||||
$fields['revision_id']->setDescription(t('The revision ID.'));
|
||||
|
||||
$fields['langcode']->setDescription(t('The custom block language code.'));
|
||||
|
||||
$fields['type']->setLabel(t('Block type'))
|
||||
->setDescription(t('The block type.'));
|
||||
|
||||
$fields['info'] = BaseFieldDefinition::create('string')
|
||||
->setLabel(t('Block description'))
|
||||
|
@ -193,12 +179,6 @@ class BlockContent extends ContentEntityBase implements BlockContentInterface {
|
|||
->setDisplayConfigurable('form', TRUE)
|
||||
->addConstraint('UniqueField', []);
|
||||
|
||||
|
||||
$fields['type'] = BaseFieldDefinition::create('entity_reference')
|
||||
->setLabel(t('Block type'))
|
||||
->setDescription(t('The block type.'))
|
||||
->setSetting('target_type', 'block_content_type');
|
||||
|
||||
$fields['revision_log'] = BaseFieldDefinition::create('string_long')
|
||||
->setLabel(t('Revision log message'))
|
||||
->setDescription(t('The log entry explaining the changes in this revision.'))
|
||||
|
@ -210,6 +190,17 @@ class BlockContent extends ContentEntityBase implements BlockContentInterface {
|
|||
->setTranslatable(TRUE)
|
||||
->setRevisionable(TRUE);
|
||||
|
||||
$fields['revision_created'] = BaseFieldDefinition::create('created')
|
||||
->setLabel(t('Revision create time'))
|
||||
->setDescription(t('The time that the current revision was created.'))
|
||||
->setRevisionable(TRUE);
|
||||
|
||||
$fields['revision_user'] = BaseFieldDefinition::create('entity_reference')
|
||||
->setLabel(t('Revision user'))
|
||||
->setDescription(t('The user ID of the author of the current revision.'))
|
||||
->setSetting('target_type', 'user')
|
||||
->setRevisionable(TRUE);
|
||||
|
||||
$fields['revision_translation_affected'] = BaseFieldDefinition::create('boolean')
|
||||
->setLabel(t('Revision translation affected'))
|
||||
->setDescription(t('Indicates if the last edit of a translation belongs to current revision.'))
|
||||
|
@ -224,7 +215,7 @@ class BlockContent extends ContentEntityBase implements BlockContentInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRevisionLog() {
|
||||
return $this->get('revision_log')->value;
|
||||
return $this->getRevisionLogMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -239,7 +230,63 @@ class BlockContent extends ContentEntityBase implements BlockContentInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function setRevisionLog($revision_log) {
|
||||
$this->set('revision_log', $revision_log);
|
||||
return $this->setRevisionLogMessage($revision_log);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRevisionCreationTime() {
|
||||
return $this->get('revision_created')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setRevisionCreationTime($timestamp) {
|
||||
$this->set('revision_created', $timestamp);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRevisionUser() {
|
||||
return $this->get('revision_user')->entity;
|
||||
}
|
||||
|
||||
public function setRevisionUser(UserInterface $account) {
|
||||
$this->set('revision_user', $account);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRevisionUserId() {
|
||||
return $this->get('revision_user')->entity->id();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setRevisionUserId($user_id) {
|
||||
$this->set('revision_user', $user_id);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getRevisionLogMessage() {
|
||||
return $this->get('revision_log')->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setRevisionLogMessage($revision_log_message) {
|
||||
$this->set('revision_log', $revision_log_message);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
namespace Drupal\block_content\Tests;
|
||||
|
||||
use Drupal\block_content\Entity\BlockContent;
|
||||
use Drupal\user\Entity\User;
|
||||
use Drupal\user\UserInterface;
|
||||
|
||||
/**
|
||||
* Create a block with revisions.
|
||||
|
@ -29,6 +31,9 @@ class BlockContentRevisionsTest extends BlockContentTestBase {
|
|||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
/** @var UserInterface $user */
|
||||
$user = User::load(1);
|
||||
|
||||
// Create initial block.
|
||||
$block = $this->createBlockContent('initial');
|
||||
|
||||
|
@ -43,8 +48,10 @@ class BlockContentRevisionsTest extends BlockContentTestBase {
|
|||
$revision_count = 3;
|
||||
for ($i = 0; $i < $revision_count; $i++) {
|
||||
$block->setNewRevision(TRUE);
|
||||
$block->setRevisionLog($this->randomMachineName(32));
|
||||
$logs[] = $block->getRevisionLog();
|
||||
$block->setRevisionLogMessage($this->randomMachineName(32));
|
||||
$block->setRevisionUser($this->adminUser);
|
||||
$block->setRevisionCreationTime(REQUEST_TIME);
|
||||
$logs[] = $block->getRevisionLogMessage();
|
||||
$block->save();
|
||||
$blocks[] = $block->getRevisionId();
|
||||
}
|
||||
|
@ -62,11 +69,17 @@ class BlockContentRevisionsTest extends BlockContentTestBase {
|
|||
|
||||
foreach ($blocks as $delta => $revision_id) {
|
||||
// Confirm the correct revision text appears.
|
||||
/** @var \Drupal\block_content\BlockContentInterface $loaded */
|
||||
$loaded = entity_revision_load('block_content', $revision_id);
|
||||
// Verify revision log is the same.
|
||||
$this->assertEqual($loaded->getRevisionLog(), $logs[$delta], format_string('Correct log message found for revision @revision', array(
|
||||
$this->assertEqual($loaded->getRevisionLogMessage(), $logs[$delta], format_string('Correct log message found for revision @revision', array(
|
||||
'@revision' => $loaded->getRevisionId(),
|
||||
)));
|
||||
if ($delta > 0) {
|
||||
$this->assertTrue($loaded->getRevisionUser() instanceof UserInterface, 'Revision User found.');
|
||||
$this->assertTrue(is_numeric($loaded->getRevisionUserId()), 'Revision User ID found.');
|
||||
$this->assertTrue(is_numeric($loaded->getRevisionCreationTime()), 'Revision time found.');
|
||||
}
|
||||
}
|
||||
|
||||
// Confirm that this is the default revision.
|
||||
|
|
|
@ -19,7 +19,7 @@ abstract class BlockContentTestBase extends WebTestBase {
|
|||
/**
|
||||
* Admin user
|
||||
*
|
||||
* @var object
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
|
|
|
@ -179,7 +179,10 @@ class BlockContentTranslationUITest extends ContentTranslationUITestBase {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doTestTranslationEdit() {
|
||||
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
|
||||
$storage = $this->container->get('entity_type.manager')
|
||||
->getStorage($this->entityTypeId);
|
||||
$storage->resetCache([$this->entityId]);
|
||||
$entity = $storage->load($this->entityId);
|
||||
$languages = $this->container->get('language_manager')->getLanguages();
|
||||
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\block_content\Tests;
|
||||
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
use Drupal\system\Tests\Update\UpdatePathTestBase;
|
||||
|
||||
/**
|
||||
* Tests adding revision_user and revision_created fields.
|
||||
*
|
||||
* @group Update
|
||||
*/
|
||||
class BlockContentUpdateEntityFields extends UpdatePathTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setDatabaseDumpFiles() {
|
||||
$this->databaseDumpFiles = [
|
||||
__DIR__ . '/../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that email token in status_blocked of user.mail is updated.
|
||||
*/
|
||||
public function testAddingFields() {
|
||||
$this->runUpdates();
|
||||
|
||||
$post_revision_created = \Drupal::entityDefinitionUpdateManager()->getFieldStorageDefinition('revision_created', 'block_content');
|
||||
$post_revision_user = \Drupal::entityDefinitionUpdateManager()->getFieldStorageDefinition('revision_user', 'block_content');
|
||||
$this->assertTrue($post_revision_created instanceof BaseFieldDefinition, "Revision created field found");
|
||||
$this->assertTrue($post_revision_user instanceof BaseFieldDefinition, "Revision user field found");
|
||||
|
||||
$this->assertEqual('created', $post_revision_created->getType(), "Field is type created");
|
||||
$this->assertEqual('entity_reference', $post_revision_user->getType(), "Field is type entity_reference");
|
||||
}
|
||||
|
||||
}
|
8
core/modules/block_place/block_place.info.yml
Normal file
8
core/modules/block_place/block_place.info.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
name: Place Blocks
|
||||
type: module
|
||||
description: 'Allow administrators to place blocks from any Drupal page'
|
||||
package: Core (Experimental)
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- block
|
11
core/modules/block_place/block_place.libraries.yml
Normal file
11
core/modules/block_place/block_place.libraries.yml
Normal file
|
@ -0,0 +1,11 @@
|
|||
drupal.block_place:
|
||||
version: VERSION
|
||||
css:
|
||||
theme:
|
||||
css/block-place.css: {}
|
||||
|
||||
drupal.block_place.icons:
|
||||
version: VERSION
|
||||
css:
|
||||
theme:
|
||||
css/block-place.icons.theme.css: {}
|
84
core/modules/block_place/block_place.module
Normal file
84
core/modules/block_place/block_place.module
Normal file
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Controls the placement of blocks from all pages.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*/
|
||||
function block_place_help($route_name, RouteMatchInterface $route_match) {
|
||||
switch ($route_name) {
|
||||
case 'help.page.block_place':
|
||||
$output = '<h3>' . t('About') . '</h3>';
|
||||
$output .= '<p>' . t('The Place Blocks module allows you to place blocks from every page. For more information, see the <a href=":blocks-documentation">online documentation for the Place Blocks module</a>.', [':blocks-documentation' => 'https://www.drupal.org/documentation/modules/block_place/']) . '</p>';
|
||||
$output .= '<h3>' . t('Uses') . '</h3>';
|
||||
$output .= '<p>' . t('Block placement is specific to each theme on your site. This module allows you to place blocks in the context of your content pages') . '</p>';
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_toolbar().
|
||||
*/
|
||||
function block_place_toolbar() {
|
||||
// Link to the current page with a query parameter.
|
||||
$query = \Drupal::request()->query->all();
|
||||
$wrapper_class = '';
|
||||
$status_class = '';
|
||||
$description = '';
|
||||
if (isset($query['block-place'])) {
|
||||
$status_class = 'active';
|
||||
$wrapper_class = 'is-active';
|
||||
$description = t('Exit Place block mode.');
|
||||
unset($query['block-place']);
|
||||
unset($query['destination']);
|
||||
}
|
||||
else {
|
||||
$status_class = 'inactive';
|
||||
$description = t('Show regions to Place blocks.');
|
||||
$query['block-place'] = '1';
|
||||
// Setting destination is both a work-around for the toolbar "Back to site"
|
||||
// link in escapeAdmin.js and used for the destination after picking a
|
||||
// block.
|
||||
$query['destination'] = Url::fromRoute('<current>')->toString();
|
||||
}
|
||||
|
||||
// Remove on Admin routes.
|
||||
$admin_route = \Drupal::service('router.admin_context')->isAdminRoute();
|
||||
// Remove on Block Demo page.
|
||||
$admin_demo = \Drupal::routeMatch()->getRouteName() === 'block.admin_demo';
|
||||
$access = (\Drupal::currentUser()->hasPermission('administer blocks') && !$admin_route && !$admin_demo);
|
||||
|
||||
// The 'Place Block' tab is a simple link, with no corresponding tray.
|
||||
$items['block_place'] = [
|
||||
'#cache' => [
|
||||
'contexts' => ['user.permissions', 'url.query_args'],
|
||||
],
|
||||
'#type' => 'toolbar_item',
|
||||
'tab' => [
|
||||
'#access' => $access,
|
||||
'#type' => 'link',
|
||||
'#title' => t('Place block'),
|
||||
'#url' => Url::fromRoute('<current>', [], ['query' => $query]),
|
||||
'#attributes' => [
|
||||
'title' => $description,
|
||||
'class' => ['toolbar-icon', 'toolbar-icon-place-block-' . $status_class],
|
||||
],
|
||||
],
|
||||
'#wrapper_attributes' => [
|
||||
'class' => ['toolbar-tab', 'block-place-toolbar-tab', $wrapper_class],
|
||||
],
|
||||
'#weight' => 100,
|
||||
'#attached' => [
|
||||
'library' => [
|
||||
'block_place/drupal.block_place.icons',
|
||||
],
|
||||
],
|
||||
];
|
||||
return $items;
|
||||
}
|
6
core/modules/block_place/block_place.services.yml
Normal file
6
core/modules/block_place/block_place.services.yml
Normal file
|
@ -0,0 +1,6 @@
|
|||
services:
|
||||
block_place.page_display_variant_subscriber.block:
|
||||
class: Drupal\block_place\EventSubscriber\BlockPlaceEventSubscriber
|
||||
arguments: ['@request_stack', '@current_user']
|
||||
tags:
|
||||
- { name: event_subscriber }
|
29
core/modules/block_place/css/block-place.css
Normal file
29
core/modules/block_place/css/block-place.css
Normal file
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* @file
|
||||
* Styling for block_place module regions and buttons during block placement.
|
||||
*/
|
||||
|
||||
.block-place-region {
|
||||
outline: 1px dashed rgba(0,0,0,0.5);
|
||||
box-shadow: 0 0 0 1px rgba(255,255,255,0.7);
|
||||
margin: 1em 0;
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
.block-place-region a.button {
|
||||
background: url(../../../misc/icons/bebebe/plus.svg) #ffffff center center / 16px 16px no-repeat;
|
||||
border: 1px solid #cccccc;
|
||||
box-sizing: border-box;
|
||||
font-size: 1rem;
|
||||
padding: 0;
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.block-place-region:hover a.button, .block-place-region:focus a.button {
|
||||
background-image: url(../../../misc/icons/787878/plus.svg);
|
||||
transition: all 0.25s ease;
|
||||
}
|
24
core/modules/block_place/css/block-place.icons.theme.css
Normal file
24
core/modules/block_place/css/block-place.icons.theme.css
Normal file
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* @file
|
||||
* Styling for block_place module toolbar icons.
|
||||
*/
|
||||
.toolbar .block-place-toolbar-tab.is-active {
|
||||
background-image: -webkit-linear-gradient(rgba(255, 255, 255, 0.25) 20%, transparent 200%);
|
||||
background-image: linear-gradient(rgba(255, 255, 255, 0.25) 20%, transparent 200%);
|
||||
}
|
||||
|
||||
.toolbar .toolbar-bar .block-place-toolbar-tab {
|
||||
float: right;
|
||||
}
|
||||
|
||||
[dir="rtl"] .toolbar .toolbar-bar .block-place-toolbar-tab {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.toolbar-bar .toolbar-icon-place-block-active:before {
|
||||
background-image: url(../icons/ffffff/place-block.svg);
|
||||
}
|
||||
|
||||
.toolbar-bar .toolbar-icon-place-block-inactive:before {
|
||||
background-image: url(../icons/bebebe/place-block.svg);
|
||||
}
|
1
core/modules/block_place/icons/bebebe/place-block.svg
Normal file
1
core/modules/block_place/icons/bebebe/place-block.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0" y="0" width="16" height="16" viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve"><rect x="1" y="6" fill="#bebebe" width="14" height="9"/><ellipse fill="#bebebe" cx="11.48" cy="3.68" rx="2.23" ry="0.61"/><rect x="9.25" y="3.68" fill="#bebebe" width="4.45" height="3.35"/><ellipse fill="#bebebe" cx="4.48" cy="3.68" rx="2.23" ry="0.61"/><rect x="2.25" y="3.68" fill="#bebebe" width="4.45" height="3.35"/></svg>
|
After Width: | Height: | Size: 548 B |
1
core/modules/block_place/icons/ffffff/place-block.svg
Normal file
1
core/modules/block_place/icons/ffffff/place-block.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0" y="0" width="16" height="16" viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve"><rect x="1" y="6" fill="#ffffff" width="14" height="9"/><ellipse fill="#ffffff" cx="11.48" cy="3.68" rx="2.23" ry="0.61"/><rect x="9.25" y="3.68" fill="#ffffff" width="4.45" height="3.35"/><ellipse fill="#ffffff" cx="4.48" cy="3.68" rx="2.23" ry="0.61"/><rect x="2.25" y="3.68" fill="#ffffff" width="4.45" height="3.35"/></svg>
|
After Width: | Height: | Size: 548 B |
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\block_place\EventSubscriber;
|
||||
|
||||
use Drupal\Core\Render\PageDisplayVariantSelectionEvent;
|
||||
use Drupal\Core\Render\RenderEvents;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* @see \Drupal\block_place\Plugin\DisplayVariant\PlaceBlockPageVariant
|
||||
*/
|
||||
class BlockPlaceEventSubscriber implements EventSubscriberInterface {
|
||||
|
||||
/**
|
||||
* The request stack.
|
||||
*
|
||||
* @var \Symfony\Component\HttpFoundation\RequestStack
|
||||
*/
|
||||
protected $requestStack;
|
||||
|
||||
/**
|
||||
* The current user.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
protected $account;
|
||||
|
||||
/**
|
||||
* Constructs a \Drupal\block_place\EventSubscriber\BlockPlaceEventSubscriber object.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
|
||||
* The request stack used to retrieve the current request.
|
||||
* @param \Drupal\Core\Session\AccountInterface $account
|
||||
* The current user.
|
||||
*/
|
||||
public function __construct(RequestStack $request_stack, AccountInterface $account) {
|
||||
$this->requestStack = $request_stack;
|
||||
$this->account = $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the block place override of the block page display variant.
|
||||
*
|
||||
* @param \Drupal\Core\Render\PageDisplayVariantSelectionEvent $event
|
||||
* The event to process.
|
||||
*/
|
||||
public function onBlockPageDisplayVariantSelected(PageDisplayVariantSelectionEvent $event) {
|
||||
if ($event->getPluginId() === 'block_page') {
|
||||
if ($this->requestStack->getCurrentRequest()->query->has('block-place') && $this->account->hasPermission('administer blocks')) {
|
||||
$event->setPluginId('block_place_page');
|
||||
}
|
||||
$event->addCacheContexts(['user.permissions', 'url.query_args']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents() {
|
||||
// Set a very low priority, so that it runs last.
|
||||
$events[RenderEvents::SELECT_PAGE_DISPLAY_VARIANT][] = ['onBlockPageDisplayVariantSelected', -1000];
|
||||
return $events;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\block_place\Plugin\DisplayVariant;
|
||||
|
||||
use Drupal\block\BlockRepositoryInterface;
|
||||
use Drupal\block\Plugin\DisplayVariant\BlockPageVariant;
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Core\Entity\EntityViewBuilderInterface;
|
||||
use Drupal\Core\Routing\RedirectDestinationInterface;
|
||||
use Drupal\Core\Theme\ThemeManagerInterface;
|
||||
use Drupal\Core\Link;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Allows blocks to be placed directly within a region.
|
||||
*
|
||||
* @PageDisplayVariant(
|
||||
* id = "block_place_page",
|
||||
* admin_label = @Translation("Page with blocks and place block buttons")
|
||||
* )
|
||||
*/
|
||||
class PlaceBlockPageVariant extends BlockPageVariant {
|
||||
|
||||
/**
|
||||
* The theme manager.
|
||||
*
|
||||
* @var \Drupal\Core\Theme\ThemeManagerInterface
|
||||
*/
|
||||
protected $themeManager;
|
||||
|
||||
/**
|
||||
* The redirect destination.
|
||||
*
|
||||
* @var \Drupal\Core\Routing\RedirectDestinationInterface
|
||||
*/
|
||||
protected $redirectDestination;
|
||||
|
||||
/**
|
||||
* Constructs a new PlaceBlockPageVariant.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin ID for the plugin instance.
|
||||
* @param mixed $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\block\BlockRepositoryInterface $block_repository
|
||||
* The block repository.
|
||||
* @param \Drupal\Core\Entity\EntityViewBuilderInterface $block_view_builder
|
||||
* The block view builder.
|
||||
* @param string[] $block_list_cache_tags
|
||||
* The Block entity type list cache tags.
|
||||
* @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager
|
||||
* The theme manager.
|
||||
* @param \Drupal\Core\Routing\RedirectDestinationInterface $redirect_destination
|
||||
* The redirect destination.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, $plugin_definition, BlockRepositoryInterface $block_repository, EntityViewBuilderInterface $block_view_builder, array $block_list_cache_tags, ThemeManagerInterface $theme_manager, RedirectDestinationInterface $redirect_destination) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $block_repository, $block_view_builder, $block_list_cache_tags);
|
||||
|
||||
$this->themeManager = $theme_manager;
|
||||
$this->redirectDestination = $redirect_destination;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('block.repository'),
|
||||
$container->get('entity_type.manager')->getViewBuilder('block'),
|
||||
$container->get('entity_type.manager')->getDefinition('block')->getListCacheTags(),
|
||||
$container->get('theme.manager'),
|
||||
$container->get('redirect.destination')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build() {
|
||||
$build = parent::build();
|
||||
|
||||
$active_theme = $this->themeManager->getActiveTheme();
|
||||
$theme_name = $active_theme->getName();
|
||||
$destination = $this->redirectDestination->get();
|
||||
$visible_regions = $this->getVisibleRegionNames($theme_name);
|
||||
|
||||
// Build an array of the region names in the right order.
|
||||
$build += array_fill_keys(array_keys($visible_regions), []);
|
||||
|
||||
foreach ($visible_regions as $region => $region_name) {
|
||||
$query = [
|
||||
'region' => $region,
|
||||
];
|
||||
if ($destination) {
|
||||
$query['destination'] = $destination;
|
||||
}
|
||||
$title = $this->t('<span class="visually-hidden">Place block in the %region region</span>', ['%region' => $region_name]);
|
||||
$operations['block_description'] = [
|
||||
'#type' => 'inline_template',
|
||||
'#template' => '<div class="block-place-region">{{ link }}</div>',
|
||||
'#context' => [
|
||||
'link' => Link::createFromRoute($title, 'block.admin_library', ['theme' => $theme_name], [
|
||||
'query' => $query,
|
||||
'attributes' => [
|
||||
'title' => $title,
|
||||
'class' => ['use-ajax', 'button', 'button--small'],
|
||||
'data-dialog-type' => 'modal',
|
||||
'data-dialog-options' => Json::encode([
|
||||
'width' => 700,
|
||||
]),
|
||||
],
|
||||
]),
|
||||
],
|
||||
];
|
||||
$build[$region] = ['block_place_operations' => $operations] + $build[$region];
|
||||
}
|
||||
$build['#attached']['library'][] = 'block_place/drupal.block_place';
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the human-readable list of regions keyed by machine name.
|
||||
*
|
||||
* @param string $theme
|
||||
* The name of the theme.
|
||||
*
|
||||
* @return array
|
||||
* An array of human-readable region names keyed by machine name.
|
||||
*/
|
||||
protected function getVisibleRegionNames($theme) {
|
||||
return system_region_list($theme, REGIONS_VISIBLE);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\block_place\Functional;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests the placing a block.
|
||||
*
|
||||
* @group block_place
|
||||
*/
|
||||
class BlockPlaceTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['block', 'block_place', 'toolbar'];
|
||||
|
||||
/**
|
||||
* Tests placing blocks as an admin.
|
||||
*/
|
||||
public function testPlacingBlocksAdmin() {
|
||||
// Create administrative user.
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'access administration pages',
|
||||
'access toolbar',
|
||||
'administer blocks',
|
||||
'view the administration theme',
|
||||
]));
|
||||
$this->drupalGet(Url::fromRoute('<front>'));
|
||||
$this->clickLink('Place block');
|
||||
|
||||
// Each region should have one link to place a block.
|
||||
$theme_name = $this->container->get('theme.manager')->getActiveTheme()->getName();
|
||||
$visible_regions = system_region_list($theme_name, REGIONS_VISIBLE);
|
||||
$this->assertGreaterThan(0, count($visible_regions));
|
||||
|
||||
$default_theme = $this->config('system.theme')->get('default');
|
||||
$block_library_url = Url::fromRoute('block.admin_library', ['theme' => $default_theme]);
|
||||
foreach ($visible_regions as $region => $name) {
|
||||
$block_library_url->setOption('query', ['region' => $region]);
|
||||
$links = $this->xpath('//a[contains(@href, :href)]', [':href' => $block_library_url->toString()]);
|
||||
$this->assertEquals(1, count($links));
|
||||
|
||||
list(, $query_string) = explode('?', $links[0]->getAttribute('href'), 2);
|
||||
parse_str($query_string, $query_parts);
|
||||
$this->assertNotEmpty($query_parts['destination']);
|
||||
|
||||
// Get the text inside the div->a->span->em.
|
||||
$demo_block = $this->xpath('//div[@class="block-place-region"]/a/span[text()="Place block in the "]/em[text()="' . $name . '"]');
|
||||
$this->assertEquals(1, count($demo_block));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests placing blocks as an unprivileged user.
|
||||
*/
|
||||
public function testPlacingBlocksUnprivileged() {
|
||||
// Create a user who cannot administer blocks.
|
||||
$this->drupalLogin($this->drupalCreateUser([
|
||||
'access administration pages',
|
||||
'access toolbar',
|
||||
'view the administration theme',
|
||||
]));
|
||||
$this->drupalGet(Url::fromRoute('<front>'));
|
||||
$links = $this->xpath('//a[text()=:label]', [':label' => 'Place block']);
|
||||
$this->assertEmpty($links);
|
||||
|
||||
$this->drupalGet(Url::fromRoute('block.admin_library', ['theme' => 'classy']));
|
||||
$this->assertSession()->statusCodeEquals(403);
|
||||
}
|
||||
|
||||
}
|
|
@ -3,7 +3,7 @@ administer book outlines:
|
|||
create new books:
|
||||
title: 'Create new books'
|
||||
add content to books:
|
||||
title: 'Add content and child pages to books'
|
||||
title: 'Add content and child pages to books and manage their hierarchies.'
|
||||
access printer-friendly version:
|
||||
title: 'View printer-friendly books'
|
||||
description: 'View a book page and all of its sub-pages as a single document for ease of printing. Can be performance heavy.'
|
||||
|
|
125
core/modules/book/book.views.inc
Normal file
125
core/modules/book/book.views.inc
Normal file
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provide views data for book.module.
|
||||
*
|
||||
* @ingroup views_module_handlers
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_views_data().
|
||||
*/
|
||||
function book_views_data() {
|
||||
$data = [];
|
||||
$data['book'] = [];
|
||||
$data['book']['table'] = [];
|
||||
$data['book']['table']['group'] = t('Book');
|
||||
|
||||
$data['book']['table']['join'] = [
|
||||
'node_field_data' => [
|
||||
'left_field' => 'nid',
|
||||
'field' => 'nid',
|
||||
],
|
||||
];
|
||||
|
||||
$data['book']['nid'] = [
|
||||
'title' => t('Page'),
|
||||
'help' => t('The book page node.'),
|
||||
'relationship' => [
|
||||
'base' => 'node_field_data',
|
||||
'id' => 'standard',
|
||||
'label' => t('Book Page'),
|
||||
],
|
||||
];
|
||||
|
||||
$data['book']['bid'] = [
|
||||
'title' => t('Top level book'),
|
||||
'help' => t('The book the node is in.'),
|
||||
'relationship' => [
|
||||
'base' => 'node_field_data',
|
||||
'id' => 'standard',
|
||||
'label' => t('Book'),
|
||||
],
|
||||
];
|
||||
|
||||
$data['book']['pid'] = [
|
||||
'title' => t('Parent'),
|
||||
'help' => t('The parent book node.'),
|
||||
'relationship' => [
|
||||
'base' => 'node_field_data',
|
||||
'id' => 'standard',
|
||||
'label' => t('Book Parent'),
|
||||
],
|
||||
];
|
||||
|
||||
$data['book']['has_children'] = [
|
||||
'title' => t('Page has Children'),
|
||||
'help' => t('Flag indicating whether this book page has children'),
|
||||
'field' => [
|
||||
'id' => 'boolean',
|
||||
],
|
||||
'sort' => [
|
||||
'id' => 'standard',
|
||||
],
|
||||
'filter' => [
|
||||
'id' => 'boolean',
|
||||
'label' => t('Has Children'),
|
||||
],
|
||||
'argument' => [
|
||||
'id' => 'numeric',
|
||||
],
|
||||
];
|
||||
|
||||
$data['book']['weight'] = [
|
||||
'title' => t('Weight'),
|
||||
'help' => t('The weight of the book page.'),
|
||||
'field' => [
|
||||
'id' => 'numeric',
|
||||
],
|
||||
'sort' => [
|
||||
'id' => 'standard',
|
||||
],
|
||||
];
|
||||
|
||||
$data['book']['depth'] = [
|
||||
'title' => t('Depth'),
|
||||
'help' => t('The depth of the book page in the hierarchy; top level books have a depth of 1.'),
|
||||
'field' => [
|
||||
'id' => 'numeric',
|
||||
],
|
||||
'sort' => [
|
||||
'id' => 'standard',
|
||||
],
|
||||
'filter' => [
|
||||
'id' => 'numeric',
|
||||
],
|
||||
'argument' => [
|
||||
'id' => 'standard',
|
||||
],
|
||||
];
|
||||
$parents = [
|
||||
1 => t('1st parent'),
|
||||
2 => t('2nd parent'),
|
||||
3 => t('3rd parent'),
|
||||
4 => t('4th parent'),
|
||||
5 => t('5th parent'),
|
||||
6 => t('6th parent'),
|
||||
7 => t('7th parent'),
|
||||
8 => t('8th parent'),
|
||||
9 => t('9th parent'),
|
||||
];
|
||||
foreach ($parents as $i => $parent_label) {
|
||||
$data['book']["p$i"] = [
|
||||
'title' => $parent_label,
|
||||
'help' => t('The @parent of book node.', ['@parent' => $parent_label]),
|
||||
'relationship' => [
|
||||
'base' => 'node_field_data',
|
||||
'id' => 'standard',
|
||||
'label' => t('Book @parent', ['@parent' => $parent_label]),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
|
@ -8,6 +8,6 @@ name: 'Book page'
|
|||
type: book
|
||||
description: '<em>Books</em> have a built-in hierarchical navigation. Use for handbooks or tutorials.'
|
||||
help: ''
|
||||
new_revision: false
|
||||
new_revision: true
|
||||
preview_mode: 1
|
||||
display_submitted: true
|
||||
|
|
8
core/modules/book/config/schema/book.views.schema.yml
Normal file
8
core/modules/book/config/schema/book.views.schema.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Schema for the views plugins of the Book module.
|
||||
|
||||
views.argument_default.top_level_book:
|
||||
type: sequence
|
||||
label: 'Top Level Book from current node'
|
||||
sequence:
|
||||
type: string
|
||||
label: 'Nid'
|
|
@ -138,7 +138,7 @@ class BookManager implements BookManagerInterface {
|
|||
/**
|
||||
* Determine the relative depth of the children of a given book link.
|
||||
*
|
||||
* @param array
|
||||
* @param array $book_link
|
||||
* The book link.
|
||||
*
|
||||
* @return int
|
||||
|
|
|
@ -4,7 +4,8 @@ namespace Drupal\book\Cache;
|
|||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Cache\Context\CacheContextInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerAware;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
/**
|
||||
|
@ -19,7 +20,9 @@ use Symfony\Component\HttpFoundation\RequestStack;
|
|||
* This class is container-aware to avoid initializing the 'book.manager'
|
||||
* service when it is not necessary.
|
||||
*/
|
||||
class BookNavigationCacheContext extends ContainerAware implements CacheContextInterface {
|
||||
class BookNavigationCacheContext implements CacheContextInterface, ContainerAwareInterface {
|
||||
|
||||
use ContainerAwareTrait;
|
||||
|
||||
/**
|
||||
* The request stack.
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\book\Plugin\views\argument_default;
|
||||
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\node\NodeStorageInterface;
|
||||
use Drupal\node\Plugin\views\argument_default\Node;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Default argument plugin to get the current node's top level book.
|
||||
*
|
||||
* @ViewsArgumentDefault(
|
||||
* id = "top_level_book",
|
||||
* title = @Translation("Top Level Book from current node")
|
||||
* )
|
||||
*/
|
||||
class TopLevelBook extends Node {
|
||||
|
||||
/**
|
||||
* The node storage controller.
|
||||
*
|
||||
* @var \Drupal\node\NodeStorageInterface
|
||||
*/
|
||||
protected $nodeStorage;
|
||||
|
||||
/**
|
||||
* Constructs a Drupal\book\Plugin\views\argument_default\TopLevelBook object.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin_id for the plugin instance.
|
||||
* @param array $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
|
||||
* The route match.
|
||||
* @param \Drupal\node\NodeStorageInterface $node_storage
|
||||
* The node storage controller.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, array $plugin_definition, RouteMatchInterface $route_match, NodeStorageInterface $node_storage) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $route_match);
|
||||
$this->nodeStorage = $node_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('current_route_match'),
|
||||
$container->get('entity.manager')->getStorage('node')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getArgument() {
|
||||
// Use the argument_default_node plugin to get the nid argument.
|
||||
$nid = parent::getArgument();
|
||||
if (!empty($nid)) {
|
||||
$node = $this->nodeStorage->load($nid);
|
||||
if (isset($node->book['bid'])) {
|
||||
return $node->book['bid'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -526,49 +526,49 @@ class BookTest extends WebTestBase {
|
|||
/**
|
||||
* Tests the access for deleting top-level book nodes.
|
||||
*/
|
||||
function testBookDelete() {
|
||||
$node_storage = $this->container->get('entity.manager')->getStorage('node');
|
||||
$nodes = $this->createBook();
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$edit = array();
|
||||
function testBookDelete() {
|
||||
$node_storage = $this->container->get('entity.manager')->getStorage('node');
|
||||
$nodes = $this->createBook();
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$edit = array();
|
||||
|
||||
// Test access to delete top-level and child book nodes.
|
||||
$this->drupalGet('node/' . $this->book->id() . '/outline/remove');
|
||||
$this->assertResponse('403', 'Deleting top-level book node properly forbidden.');
|
||||
$this->drupalPostForm('node/' . $nodes[4]->id() . '/outline/remove', $edit, t('Remove'));
|
||||
$node_storage->resetCache(array($nodes[4]->id()));
|
||||
$node4 = $node_storage->load($nodes[4]->id());
|
||||
$this->assertTrue(empty($node4->book), 'Deleting child book node properly allowed.');
|
||||
// Test access to delete top-level and child book nodes.
|
||||
$this->drupalGet('node/' . $this->book->id() . '/outline/remove');
|
||||
$this->assertResponse('403', 'Deleting top-level book node properly forbidden.');
|
||||
$this->drupalPostForm('node/' . $nodes[4]->id() . '/outline/remove', $edit, t('Remove'));
|
||||
$node_storage->resetCache(array($nodes[4]->id()));
|
||||
$node4 = $node_storage->load($nodes[4]->id());
|
||||
$this->assertTrue(empty($node4->book), 'Deleting child book node properly allowed.');
|
||||
|
||||
// Delete all child book nodes and retest top-level node deletion.
|
||||
foreach ($nodes as $node) {
|
||||
$nids[] = $node->id();
|
||||
}
|
||||
entity_delete_multiple('node', $nids);
|
||||
$this->drupalPostForm('node/' . $this->book->id() . '/outline/remove', $edit, t('Remove'));
|
||||
$node_storage->resetCache(array($this->book->id()));
|
||||
$node = $node_storage->load($this->book->id());
|
||||
$this->assertTrue(empty($node->book), 'Deleting childless top-level book node properly allowed.');
|
||||
// Delete all child book nodes and retest top-level node deletion.
|
||||
foreach ($nodes as $node) {
|
||||
$nids[] = $node->id();
|
||||
}
|
||||
entity_delete_multiple('node', $nids);
|
||||
$this->drupalPostForm('node/' . $this->book->id() . '/outline/remove', $edit, t('Remove'));
|
||||
$node_storage->resetCache(array($this->book->id()));
|
||||
$node = $node_storage->load($this->book->id());
|
||||
$this->assertTrue(empty($node->book), 'Deleting childless top-level book node properly allowed.');
|
||||
|
||||
// Tests directly deleting a book parent.
|
||||
$nodes = $this->createBook();
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalGet($this->book->urlInfo('delete-form'));
|
||||
$this->assertRaw(t('%title is part of a book outline, and has associated child pages. If you proceed with deletion, the child pages will be relocated automatically.', ['%title' => $this->book->label()]));
|
||||
// Delete parent, and visit a child page.
|
||||
$this->drupalPostForm($this->book->urlInfo('delete-form'), [], t('Delete'));
|
||||
$this->drupalGet($nodes[0]->urlInfo());
|
||||
$this->assertResponse(200);
|
||||
$this->assertText($nodes[0]->label());
|
||||
// The book parents should be updated.
|
||||
$node_storage = \Drupal::entityTypeManager()->getStorage('node');
|
||||
$node_storage->resetCache();
|
||||
$child = $node_storage->load($nodes[0]->id());
|
||||
$this->assertEqual($child->id(), $child->book['bid'], 'Child node book ID updated when parent is deleted.');
|
||||
// 3rd-level children should now be 2nd-level.
|
||||
$second = $node_storage->load($nodes[1]->id());
|
||||
$this->assertEqual($child->id(), $second->book['bid'], '3rd-level child node is now second level when top-level node is deleted.');
|
||||
}
|
||||
// Tests directly deleting a book parent.
|
||||
$nodes = $this->createBook();
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalGet($this->book->urlInfo('delete-form'));
|
||||
$this->assertRaw(t('%title is part of a book outline, and has associated child pages. If you proceed with deletion, the child pages will be relocated automatically.', ['%title' => $this->book->label()]));
|
||||
// Delete parent, and visit a child page.
|
||||
$this->drupalPostForm($this->book->urlInfo('delete-form'), [], t('Delete'));
|
||||
$this->drupalGet($nodes[0]->urlInfo());
|
||||
$this->assertResponse(200);
|
||||
$this->assertText($nodes[0]->label());
|
||||
// The book parents should be updated.
|
||||
$node_storage = \Drupal::entityTypeManager()->getStorage('node');
|
||||
$node_storage->resetCache();
|
||||
$child = $node_storage->load($nodes[0]->id());
|
||||
$this->assertEqual($child->id(), $child->book['bid'], 'Child node book ID updated when parent is deleted.');
|
||||
// 3rd-level children should now be 2nd-level.
|
||||
$second = $node_storage->load($nodes[1]->id());
|
||||
$this->assertEqual($child->id(), $second->book['bid'], '3rd-level child node is now second level when top-level node is deleted.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests re-ordering of books.
|
||||
|
|
156
core/modules/book/src/Tests/Views/BookRelationshipTest.php
Normal file
156
core/modules/book/src/Tests/Views/BookRelationshipTest.php
Normal file
|
@ -0,0 +1,156 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\book\Tests\Views;
|
||||
|
||||
use Drupal\views\Tests\ViewTestBase;
|
||||
use Drupal\views\Tests\ViewTestData;
|
||||
|
||||
/**
|
||||
* Tests entity reference relationship data.
|
||||
*
|
||||
* @group book
|
||||
*
|
||||
* @see book_views_data()
|
||||
*/
|
||||
class BookRelationshipTest extends ViewTestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_book_view');
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('book_test_views', 'book', 'views');
|
||||
|
||||
/**
|
||||
* A book node.
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $book;
|
||||
|
||||
/**
|
||||
* A user with permission to create and edit books.
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $bookAuthor;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create users.
|
||||
$this->bookAuthor = $this->drupalCreateUser(
|
||||
array(
|
||||
'create new books',
|
||||
'create book content',
|
||||
'edit own book content',
|
||||
'add content to books',
|
||||
)
|
||||
);
|
||||
ViewTestData::createTestViews(get_class($this), array('book_test_views'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new book with a page hierarchy.
|
||||
*/
|
||||
protected function createBook() {
|
||||
// Create new book.
|
||||
$this->drupalLogin($this->bookAuthor);
|
||||
|
||||
$this->book = $this->createBookNode('new');
|
||||
$book = $this->book;
|
||||
|
||||
$nodes = array();
|
||||
// Node 0.
|
||||
$nodes[] = $this->createBookNode($book->id());
|
||||
// Node 1.
|
||||
$nodes[] = $this->createBookNode($book->id(), $nodes[0]->book['nid']);
|
||||
// Node 2.
|
||||
$nodes[] = $this->createBookNode($book->id(), $nodes[1]->book['nid']);
|
||||
// Node 3.
|
||||
$nodes[] = $this->createBookNode($book->id(), $nodes[2]->book['nid']);
|
||||
// Node 4.
|
||||
$nodes[] = $this->createBookNode($book->id(), $nodes[3]->book['nid']);
|
||||
// Node 5.
|
||||
$nodes[] = $this->createBookNode($book->id(), $nodes[4]->book['nid']);
|
||||
// Node 6.
|
||||
$nodes[] = $this->createBookNode($book->id(), $nodes[5]->book['nid']);
|
||||
// Node 7.
|
||||
$nodes[] = $this->createBookNode($book->id(), $nodes[6]->book['nid']);
|
||||
|
||||
$this->drupalLogout();
|
||||
|
||||
return $nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a book node.
|
||||
*
|
||||
* @param int|string $book_nid
|
||||
* A book node ID or set to 'new' to create a new book.
|
||||
* @param int|null $parent
|
||||
* (optional) Parent book reference ID. Defaults to NULL.
|
||||
*
|
||||
* @return \Drupal\node\NodeInterface
|
||||
* The book node.
|
||||
*/
|
||||
protected function createBookNode($book_nid, $parent = NULL) {
|
||||
// $number does not use drupal_static as it should not be reset
|
||||
// since it uniquely identifies each call to createBookNode().
|
||||
// Used to ensure that when sorted nodes stay in same order.
|
||||
static $number = 0;
|
||||
|
||||
$edit = array();
|
||||
$edit['title[0][value]'] = $number . ' - SimpleTest test node ' . $this->randomMachineName(10);
|
||||
$edit['body[0][value]'] = 'SimpleTest test body ' . $this->randomMachineName(32) . ' ' . $this->randomMachineName(32);
|
||||
$edit['book[bid]'] = $book_nid;
|
||||
|
||||
if ($parent !== NULL) {
|
||||
$this->drupalPostForm('node/add/book', $edit, t('Change book (update list of parents)'));
|
||||
|
||||
$edit['book[pid]'] = $parent;
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
// Make sure the parent was flagged as having children.
|
||||
$parent_node = \Drupal::entityManager()->getStorage('node')->loadUnchanged($parent);
|
||||
$this->assertFalse(empty($parent_node->book['has_children']), 'Parent node is marked as having children');
|
||||
}
|
||||
else {
|
||||
$this->drupalPostForm('node/add/book', $edit, t('Save'));
|
||||
}
|
||||
|
||||
// Check to make sure the book node was created.
|
||||
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
|
||||
$this->assertNotNull(($node === FALSE ? NULL : $node), 'Book node found in database.');
|
||||
$number++;
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests using the views relationship.
|
||||
*/
|
||||
public function testRelationship() {
|
||||
|
||||
// Create new book.
|
||||
// @var \Drupal\node\NodeInterface[] $nodes
|
||||
$nodes = $this->createBook();
|
||||
for ($i = 0; $i < 8; $i++) {
|
||||
$this->drupalGet('test-book/' . $nodes[$i]->id());
|
||||
|
||||
for ($j = 0; $j < $i; $j++) {
|
||||
$this->assertLink($nodes[$j]->label());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
name: 'Book test views'
|
||||
type: module
|
||||
description: 'Provides default views for views book tests.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- book
|
||||
- views
|
File diff suppressed because it is too large
Load diff
|
@ -493,7 +493,7 @@
|
|||
* A HTML string for the button to create a name for a new button group.
|
||||
*/
|
||||
Drupal.theme.ckeditorNewButtonGroup = function () {
|
||||
return '<li class="ckeditor-add-new-group"><button role="button" aria-label="' + Drupal.t('Add a CKEditor button group to the end of this row.') + '">' + Drupal.t('Add group') + '</button></li>';
|
||||
return '<li class="ckeditor-add-new-group"><button aria-label="' + Drupal.t('Add a CKEditor button group to the end of this row.') + '">' + Drupal.t('Add group') + '</button></li>';
|
||||
};
|
||||
|
||||
})(jQuery, Drupal, drupalSettings, _);
|
||||
|
|
|
@ -71,7 +71,7 @@ interface CKEditorPluginInterface extends PluginInspectionInterface {
|
|||
* Note: this does not use a Drupal library because this uses CKEditor's API,
|
||||
* see http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.resourceManager.html#addExternal.
|
||||
*
|
||||
* @return string|FALSE
|
||||
* @return string|false
|
||||
* The Drupal root-relative path to the file, FALSE if an internal plugin.
|
||||
*/
|
||||
public function getFile();
|
||||
|
|
|
@ -115,7 +115,7 @@ class StylesCombo extends CKEditorPluginBase implements CKEditorPluginConfigurab
|
|||
*
|
||||
* @param string $styles
|
||||
* The "styles" setting.
|
||||
* @return array|FALSE
|
||||
* @return array|false
|
||||
* An array containing the "stylesSet" configuration, or FALSE when the
|
||||
* syntax is invalid.
|
||||
*/
|
||||
|
|
|
@ -55,7 +55,7 @@ class CKEditorAdminTest extends WebTestBase {
|
|||
$this->drupalGet('admin/config/content/formats/manage/filtered_html');
|
||||
|
||||
// Ensure no Editor config entity exists yet.
|
||||
$editor = entity_load('editor', 'filtered_html');
|
||||
$editor = Editor::load('filtered_html');
|
||||
$this->assertFalse($editor, 'No Editor config entity exists yet.');
|
||||
|
||||
// Verify the "Text Editor" <select> when a text editor is available.
|
||||
|
@ -111,7 +111,7 @@ class CKEditorAdminTest extends WebTestBase {
|
|||
|
||||
// Keep the "CKEditor" editor selected and click the "Configure" button.
|
||||
$this->drupalPostAjaxForm(NULL, $edit, 'editor_configure');
|
||||
$editor = entity_load('editor', 'filtered_html');
|
||||
$editor = Editor::load('filtered_html');
|
||||
$this->assertFalse($editor, 'No Editor config entity exists yet.');
|
||||
|
||||
// Ensure that drupalSettings is correct.
|
||||
|
@ -142,7 +142,7 @@ class CKEditorAdminTest extends WebTestBase {
|
|||
// Ensure an Editor object exists now, with the proper settings.
|
||||
$expected_settings = $expected_default_settings;
|
||||
$expected_settings['plugins']['stylescombo']['styles'] = '';
|
||||
$editor = entity_load('editor', 'filtered_html');
|
||||
$editor = Editor::load('filtered_html');
|
||||
$this->assertTrue($editor instanceof Editor, 'An Editor config entity exists now.');
|
||||
$this->assertEqual($expected_settings, $editor->getSettings(), 'The Editor config entity has the correct settings.');
|
||||
|
||||
|
@ -153,7 +153,7 @@ class CKEditorAdminTest extends WebTestBase {
|
|||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save configuration'));
|
||||
$expected_settings['plugins']['stylescombo']['styles'] = "h1.title|Title\np.callout|Callout\n\n";
|
||||
$editor = entity_load('editor', 'filtered_html');
|
||||
$editor = Editor::load('filtered_html');
|
||||
$this->assertTrue($editor instanceof Editor, 'An Editor config entity exists.');
|
||||
$this->assertEqual($expected_settings, $editor->getSettings(), 'The Editor config entity has the correct settings.');
|
||||
|
||||
|
@ -169,7 +169,7 @@ class CKEditorAdminTest extends WebTestBase {
|
|||
'editor[settings][toolbar][button_groups]' => json_encode($expected_settings['toolbar']['rows']),
|
||||
);
|
||||
$this->drupalPostForm(NULL, $edit, t('Save configuration'));
|
||||
$editor = entity_load('editor', 'filtered_html');
|
||||
$editor = Editor::load('filtered_html');
|
||||
$this->assertTrue($editor instanceof Editor, 'An Editor config entity exists.');
|
||||
$this->assertEqual($expected_settings, $editor->getSettings(), 'The Editor config entity has the correct settings.');
|
||||
|
||||
|
@ -197,7 +197,7 @@ class CKEditorAdminTest extends WebTestBase {
|
|||
$this->drupalGet('admin/config/content/formats/manage/filtered_html');
|
||||
$ultra_llama_mode_checkbox = $this->xpath('//input[@type="checkbox" and @name="editor[settings][plugins][llama_contextual_and_button][ultra_llama_mode]" and not(@checked)]');
|
||||
$this->assertTrue(count($ultra_llama_mode_checkbox) === 1, 'The "Ultra llama mode" checkbox exists and is not checked.');
|
||||
$editor = entity_load('editor', 'filtered_html');
|
||||
$editor = Editor::load('filtered_html');
|
||||
$this->assertTrue($editor instanceof Editor, 'An Editor config entity exists.');
|
||||
$this->assertEqual($expected_settings, $editor->getSettings(), 'The Editor config entity has the correct settings.');
|
||||
|
||||
|
@ -211,7 +211,7 @@ class CKEditorAdminTest extends WebTestBase {
|
|||
$ultra_llama_mode_checkbox = $this->xpath('//input[@type="checkbox" and @name="editor[settings][plugins][llama_contextual_and_button][ultra_llama_mode]" and @checked="checked"]');
|
||||
$this->assertTrue(count($ultra_llama_mode_checkbox) === 1, 'The "Ultra llama mode" checkbox exists and is checked.');
|
||||
$expected_settings['plugins']['llama_contextual_and_button']['ultra_llama_mode'] = TRUE;
|
||||
$editor = entity_load('editor', 'filtered_html');
|
||||
$editor = Editor::load('filtered_html');
|
||||
$this->assertTrue($editor instanceof Editor, 'An Editor config entity exists.');
|
||||
$this->assertEqual($expected_settings, $editor->getSettings());
|
||||
}
|
||||
|
@ -245,9 +245,9 @@ class CKEditorAdminTest extends WebTestBase {
|
|||
'editor[editor]' => 'ckeditor',
|
||||
);
|
||||
$this->drupalPostAjaxForm(NULL, $edit, 'editor_configure');
|
||||
$filter_format = entity_load('filter_format', 'amazing_format');
|
||||
$filter_format = FilterFormat::load('amazing_format');
|
||||
$this->assertFalse($filter_format, 'No FilterFormat config entity exists yet.');
|
||||
$editor = entity_load('editor', 'amazing_format');
|
||||
$editor = Editor::load('amazing_format');
|
||||
$this->assertFalse($editor, 'No Editor config entity exists yet.');
|
||||
|
||||
// Ensure the toolbar buttons configuration value is initialized to the
|
||||
|
@ -270,13 +270,13 @@ class CKEditorAdminTest extends WebTestBase {
|
|||
$this->drupalPostForm(NULL, $edit, t('Save configuration'));
|
||||
|
||||
// Ensure a FilterFormat object exists now.
|
||||
$filter_format = entity_load('filter_format', 'amazing_format');
|
||||
$filter_format = FilterFormat::load('amazing_format');
|
||||
$this->assertTrue($filter_format instanceof FilterFormatInterface, 'A FilterFormat config entity exists now.');
|
||||
|
||||
// Ensure an Editor object exists now, with the proper settings.
|
||||
$expected_settings = $default_settings;
|
||||
$expected_settings['plugins']['stylescombo']['styles'] = '';
|
||||
$editor = entity_load('editor', 'amazing_format');
|
||||
$editor = Editor::load('amazing_format');
|
||||
$this->assertTrue($editor instanceof Editor, 'An Editor config entity exists now.');
|
||||
$this->assertEqual($this->castSafeStrings($expected_settings), $this->castSafeStrings($editor->getSettings()), 'The Editor config entity has the correct settings.');
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ class CKEditorLoadingTest extends WebTestBase {
|
|||
$this->drupalGet('node/add/article');
|
||||
list($settings, $editor_settings_present, $editor_js_present, $body, $format_selector) = $this->getThingsToCheck();
|
||||
$ckeditor_plugin = $this->container->get('plugin.manager.editor')->createInstance('ckeditor');
|
||||
$editor = entity_load('editor', 'filtered_html');
|
||||
$editor = Editor::load('filtered_html');
|
||||
$expected = array('formats' => array('filtered_html' => array(
|
||||
'format' => 'filtered_html',
|
||||
'editor' => 'ckeditor',
|
||||
|
|
|
@ -69,7 +69,7 @@ class CKEditorTest extends KernelTestBase {
|
|||
* Tests CKEditor::getJSSettings().
|
||||
*/
|
||||
function testGetJSSettings() {
|
||||
$editor = entity_load('editor', 'filtered_html');
|
||||
$editor = Editor::load('filtered_html');
|
||||
|
||||
// Default toolbar.
|
||||
$expected_config = $this->getDefaultInternalConfig() + array(
|
||||
|
@ -217,7 +217,7 @@ class CKEditorTest extends KernelTestBase {
|
|||
* Tests CKEditor::buildToolbarJSSetting().
|
||||
*/
|
||||
function testBuildToolbarJSSetting() {
|
||||
$editor = entity_load('editor', 'filtered_html');
|
||||
$editor = Editor::load('filtered_html');
|
||||
|
||||
// Default toolbar.
|
||||
$expected = $this->getDefaultToolbarConfig();
|
||||
|
@ -248,7 +248,7 @@ class CKEditorTest extends KernelTestBase {
|
|||
* Tests CKEditor::buildContentsCssJSSetting().
|
||||
*/
|
||||
function testBuildContentsCssJSSetting() {
|
||||
$editor = entity_load('editor', 'filtered_html');
|
||||
$editor = Editor::load('filtered_html');
|
||||
|
||||
// Default toolbar.
|
||||
$expected = $this->getDefaultContentsCssConfig();
|
||||
|
@ -285,7 +285,7 @@ class CKEditorTest extends KernelTestBase {
|
|||
* Tests Internal::getConfig().
|
||||
*/
|
||||
function testInternalGetConfig() {
|
||||
$editor = entity_load('editor', 'filtered_html');
|
||||
$editor = Editor::load('filtered_html');
|
||||
$internal_plugin = $this->container->get('plugin.manager.ckeditor.plugin')->createInstance('internal');
|
||||
|
||||
// Default toolbar.
|
||||
|
@ -306,7 +306,7 @@ class CKEditorTest extends KernelTestBase {
|
|||
* Tests StylesCombo::getConfig().
|
||||
*/
|
||||
function testStylesComboGetConfig() {
|
||||
$editor = entity_load('editor', 'filtered_html');
|
||||
$editor = Editor::load('filtered_html');
|
||||
$stylescombo_plugin = $this->container->get('plugin.manager.ckeditor.plugin')->createInstance('stylescombo');
|
||||
|
||||
// Styles dropdown/button enabled: new setting should be present.
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Allows users to change the color scheme of themes.
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\color\Tests;
|
||||
namespace Drupal\Tests\color\Functional;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Ensures the color config schema is correct.
|
||||
*
|
||||
* @group color
|
||||
*/
|
||||
class ColorConfigSchemaTest extends WebTestBase {
|
||||
class ColorConfigSchemaTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
|
@ -1,16 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\color\Tests;
|
||||
namespace Drupal\Tests\color\Functional;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests sanitizing color preview loaded from theme.
|
||||
*
|
||||
* @group Theme
|
||||
* @group color
|
||||
*/
|
||||
class ColorSafePreviewTest extends WebTestBase {
|
||||
class ColorSafePreviewTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
|
@ -1,8 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\color\Tests;
|
||||
namespace Drupal\Tests\color\Functional;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Modify the Bartik theme colors and make sure the changes are reflected on the
|
||||
|
@ -10,7 +10,7 @@ use Drupal\simpletest\WebTestBase;
|
|||
*
|
||||
* @group color
|
||||
*/
|
||||
class ColorTest extends WebTestBase {
|
||||
class ColorTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
|
@ -6,6 +6,8 @@
|
|||
*/
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\StringTranslation\PluralTranslatableMarkup;
|
||||
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
|
@ -134,3 +136,46 @@ function comment_update_8001() {
|
|||
function comment_update_8002() {
|
||||
// Empty update to cause a cache flush.
|
||||
}
|
||||
|
||||
/**
|
||||
* @addtogroup updates-8.2.x
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add the 'view_mode' setting to displays having 'comment_default' formatter.
|
||||
*/
|
||||
function comment_update_8200() {
|
||||
$config_factory = \Drupal::configFactory();
|
||||
$displays = [];
|
||||
// Iterate on all entity view displays.
|
||||
foreach ($config_factory->listAll('core.entity_view_display.') as $name) {
|
||||
$changed = FALSE;
|
||||
$display = $config_factory->getEditable($name);
|
||||
$components = $display->get('content') ?: [];
|
||||
foreach ($components as $field_name => $component) {
|
||||
if (isset($component['type']) && ($component['type'] === 'comment_default')) {
|
||||
if (empty($display->get("content.{$field_name}.settings.view_mode"))) {
|
||||
$display->set("content.{$field_name}.settings.view_mode", 'default');
|
||||
$displays[] = $display->get('id');
|
||||
$changed = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($changed) {
|
||||
$display->save(TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
if ($displays) {
|
||||
return new PluralTranslatableMarkup(count($displays), '1 entity display updated: @displays.', '@count entity displays updated: @displays.', ['@displays' => implode(', ', $displays)]);
|
||||
}
|
||||
else {
|
||||
return new TranslatableMarkup('No entity view display updated.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup updates-8.2.x".
|
||||
*/
|
||||
|
|
|
@ -14,6 +14,7 @@ use Drupal\comment\CommentInterface;
|
|||
use Drupal\comment\Entity\CommentType;
|
||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
|
||||
use Drupal\Core\Entity\Entity\EntityViewMode;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
|
@ -656,7 +657,7 @@ function template_preprocess_comment(&$variables) {
|
|||
$variables['permalink'] = \Drupal::l(t('Permalink'), new Url('<front>'));
|
||||
}
|
||||
else {
|
||||
$uri = $comment->urlInfo();
|
||||
$uri = $comment->permalink();
|
||||
$attributes = $uri->getOption('attributes') ?: array();
|
||||
$attributes += array('class' => array('permalink'), 'rel' => 'bookmark');
|
||||
$uri->setOption('attributes', $attributes);
|
||||
|
@ -756,3 +757,43 @@ function comment_preprocess_field(&$variables) {
|
|||
function comment_ranking() {
|
||||
return \Drupal::service('comment.statistics')->getRankingInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_ENTITY_TYPE_presave() for entity_view_display entities.
|
||||
*/
|
||||
function comment_entity_view_display_presave(EntityViewDisplayInterface $display) {
|
||||
// Act only on comment view displays being disabled.
|
||||
if ($display->isNew() || $display->getTargetEntityTypeId() !== 'comment' || $display->status()) {
|
||||
return;
|
||||
}
|
||||
$storage = \Drupal::entityTypeManager()->getStorage('entity_view_display');
|
||||
if (!$storage->loadUnchanged($display->getOriginalId())->status()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable the comment field formatter when the used view display is disabled.
|
||||
foreach ($storage->loadMultiple() as $id => $view_display) {
|
||||
$changed = FALSE;
|
||||
/** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $view_display */
|
||||
foreach ($view_display->getComponents() as $field => $component) {
|
||||
if (isset($component['type']) && ($component['type'] === 'comment_default')) {
|
||||
if ($component['settings']['view_mode'] === $display->getMode()) {
|
||||
$view_display->removeComponent($field);
|
||||
/** @var \Drupal\Core\Entity\EntityViewModeInterface $mode */
|
||||
$mode = EntityViewMode::load($display->getTargetEntityTypeId() . '.' . $display->getMode());
|
||||
$arguments = [
|
||||
'@id' => $view_display->id(),
|
||||
'@name' => $field,
|
||||
'@display' => $mode->label(),
|
||||
'@mode' => $display->getMode(),
|
||||
];
|
||||
\Drupal::logger('system')->warning("View display '@id': Comment field formatter '@name' was disabled because it is using the comment view display '@display' (@mode) that was just disabled.", $arguments);
|
||||
$changed = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($changed) {
|
||||
$view_display->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
use Drupal\Component\Utility\UrlHelper;
|
||||
use Drupal\Core\Datetime\Entity\DateFormat;
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
use Drupal\Core\Render\BubbleableMetadata;
|
||||
|
||||
/**
|
||||
|
@ -19,15 +21,28 @@ function comment_token_info() {
|
|||
'needs-data' => 'comment',
|
||||
);
|
||||
|
||||
// @todo Make this work per field. See https://www.drupal.org/node/2031903.
|
||||
$entity['comment-count'] = array(
|
||||
'name' => t("Comment count"),
|
||||
'description' => t("The number of comments posted on an entity."),
|
||||
);
|
||||
$entity['comment-count-new'] = array(
|
||||
'name' => t("New comment count"),
|
||||
'description' => t("The number of comments posted on an entity since the reader last viewed it."),
|
||||
);
|
||||
$tokens = [];
|
||||
// Provide a integration for each entity type except comment.
|
||||
foreach (\Drupal::entityTypeManager()->getDefinitions() as $entity_type_id => $entity_type) {
|
||||
if ($entity_type_id == 'comment' || !$entity_type->isSubclassOf(ContentEntityInterface::class)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (\Drupal::service('comment.manager')->getFields($entity_type_id)) {
|
||||
// Get the correct token type.
|
||||
$token_type = ($entity_type_id == 'taxonomy_term') ? 'term' : $entity_type_id;
|
||||
|
||||
// @todo Make this work per field. See https://www.drupal.org/node/2031903.
|
||||
$tokens[$token_type]['comment-count'] = [
|
||||
'name' => t("Comment count"),
|
||||
'description' => t("The number of comments posted on an entity."),
|
||||
];
|
||||
$tokens[$token_type]['comment-count-new'] = [
|
||||
'name' => t("New comment count"),
|
||||
'description' => t("The number of comments posted on an entity since the reader last viewed it."),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Core comment tokens
|
||||
$comment['cid'] = array(
|
||||
|
@ -97,9 +112,8 @@ function comment_token_info() {
|
|||
return array(
|
||||
'types' => array('comment' => $type),
|
||||
'tokens' => array(
|
||||
'entity' => $entity,
|
||||
'comment' => $comment,
|
||||
),
|
||||
) + $tokens,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -235,9 +249,10 @@ function comment_tokens($type, $tokens, array $data, array $options, BubbleableM
|
|||
$replacements += $token_service->generate('user', $author_tokens, array('user' => $account), $options, $bubbleable_metadata);
|
||||
}
|
||||
}
|
||||
elseif ($type == 'entity' & !empty($data['entity'])) {
|
||||
// Replacement tokens for any content entities that have comment field.
|
||||
elseif (!empty($data[$type]) && $data[$type] instanceof FieldableEntityInterface) {
|
||||
/** @var $entity \Drupal\Core\Entity\FieldableEntityInterface */
|
||||
$entity = $data['entity'];
|
||||
$entity = $data[$type];
|
||||
|
||||
foreach ($tokens as $name => $original) {
|
||||
switch ($name) {
|
||||
|
|
|
@ -173,7 +173,7 @@ display:
|
|||
entity_field: changed
|
||||
filters:
|
||||
status:
|
||||
value: true
|
||||
value: '1'
|
||||
table: comment_field_data
|
||||
field: status
|
||||
id: status
|
||||
|
@ -184,7 +184,7 @@ display:
|
|||
entity_type: comment
|
||||
entity_field: status
|
||||
status_node:
|
||||
value: true
|
||||
value: '1'
|
||||
table: node_field_data
|
||||
field: status
|
||||
relationship: node
|
||||
|
|
|
@ -4,6 +4,9 @@ field.formatter.settings.comment_default:
|
|||
type: mapping
|
||||
label: 'Comment display format settings'
|
||||
mapping:
|
||||
view_mode:
|
||||
type: string
|
||||
label: 'The comment entity view mode to be used in this formatter'
|
||||
pager_id:
|
||||
type: integer
|
||||
label: 'Pager ID'
|
||||
|
|
|
@ -32,7 +32,7 @@ interface CommentInterface extends ContentEntityInterface, EntityChangedInterfac
|
|||
/**
|
||||
* Returns the parent comment entity if this is a reply to a comment.
|
||||
*
|
||||
* @return \Drupal\comment\CommentInterface|NULL
|
||||
* @return \Drupal\comment\CommentInterface|null
|
||||
* A comment entity of the parent comment or NULL if there is no parent.
|
||||
*/
|
||||
public function getParentComment();
|
||||
|
|
|
@ -79,8 +79,7 @@ class CommentController extends ControllerBase {
|
|||
* @param \Drupal\comment\CommentInterface $comment
|
||||
* A comment entity.
|
||||
*
|
||||
* @return \Symfony\Component\HttpFoundation\RedirectResponse.
|
||||
* Redirects to the permalink URL for this comment.
|
||||
* @return \Symfony\Component\HttpFoundation\RedirectResponse
|
||||
*/
|
||||
public function commentApprove(CommentInterface $comment) {
|
||||
$comment->setPublished(TRUE);
|
||||
|
@ -125,9 +124,8 @@ class CommentController extends ControllerBase {
|
|||
// Find the current display page for this comment.
|
||||
$page = $this->entityManager()->getStorage('comment')->getDisplayOrdinal($comment, $field_definition->getSetting('default_mode'), $field_definition->getSetting('per_page'));
|
||||
// @todo: Cleaner sub request handling.
|
||||
$subrequest_url = $entity->urlInfo()->toString(TRUE);
|
||||
$subrequest_url = $entity->urlInfo()->setOption('query', ['page' => $page])->toString(TRUE);
|
||||
$redirect_request = Request::create($subrequest_url->getGeneratedUrl(), 'GET', $request->query->all(), $request->cookies->all(), array(), $request->server->all());
|
||||
$redirect_request->query->set('page', $page);
|
||||
// Carry over the session to the subrequest.
|
||||
if ($session = $request->getSession()) {
|
||||
$redirect_request->setSession($session);
|
||||
|
|
|
@ -208,16 +208,18 @@ class Comment extends ContentEntityBase implements CommentInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
|
||||
$fields['cid'] = BaseFieldDefinition::create('integer')
|
||||
->setLabel(t('Comment ID'))
|
||||
->setDescription(t('The comment ID.'))
|
||||
->setReadOnly(TRUE)
|
||||
->setSetting('unsigned', TRUE);
|
||||
/** @var \Drupal\Core\Field\BaseFieldDefinition[] $fields */
|
||||
$fields = parent::baseFieldDefinitions($entity_type);
|
||||
|
||||
$fields['uuid'] = BaseFieldDefinition::create('uuid')
|
||||
->setLabel(t('UUID'))
|
||||
->setDescription(t('The comment UUID.'))
|
||||
->setReadOnly(TRUE);
|
||||
$fields['cid']->setLabel(t('Comment ID'))
|
||||
->setDescription(t('The comment ID.'));
|
||||
|
||||
$fields['uuid']->setDescription(t('The comment UUID.'));
|
||||
|
||||
$fields['comment_type']->setLabel(t('Comment Type'))
|
||||
->setDescription(t('The comment type.'));
|
||||
|
||||
$fields['langcode']->setDescription(t('The comment language code.'));
|
||||
|
||||
$fields['pid'] = BaseFieldDefinition::create('entity_reference')
|
||||
->setLabel(t('Parent ID'))
|
||||
|
@ -229,18 +231,6 @@ class Comment extends ContentEntityBase implements CommentInterface {
|
|||
->setDescription(t('The ID of the entity of which this comment is a reply.'))
|
||||
->setRequired(TRUE);
|
||||
|
||||
$fields['langcode'] = BaseFieldDefinition::create('language')
|
||||
->setLabel(t('Language'))
|
||||
->setDescription(t('The comment language code.'))
|
||||
->setTranslatable(TRUE)
|
||||
->setDisplayOptions('view', array(
|
||||
'type' => 'hidden',
|
||||
))
|
||||
->setDisplayOptions('form', array(
|
||||
'type' => 'language_select',
|
||||
'weight' => 2,
|
||||
));
|
||||
|
||||
$fields['subject'] = BaseFieldDefinition::create('string')
|
||||
->setLabel(t('Subject'))
|
||||
->setTranslatable(TRUE)
|
||||
|
@ -312,11 +302,6 @@ class Comment extends ContentEntityBase implements CommentInterface {
|
|||
->setSetting('is_ascii', TRUE)
|
||||
->setSetting('max_length', EntityTypeInterface::ID_MAX_LENGTH);
|
||||
|
||||
$fields['comment_type'] = BaseFieldDefinition::create('entity_reference')
|
||||
->setLabel(t('Comment Type'))
|
||||
->setDescription(t('The comment type.'))
|
||||
->setSetting('target_type', 'comment_type');
|
||||
|
||||
$fields['field_name'] = BaseFieldDefinition::create('string')
|
||||
->setLabel(t('Comment field name'))
|
||||
->setDescription(t('The field name through which this comment was added.'))
|
||||
|
|
|
@ -151,10 +151,10 @@ class CommentAdminOverview extends FormBase {
|
|||
'operations' => $this->t('Operations'),
|
||||
);
|
||||
$cids = $this->commentStorage->getQuery()
|
||||
->condition('status', $status)
|
||||
->tableSort($header)
|
||||
->pager(50)
|
||||
->execute();
|
||||
->condition('status', $status)
|
||||
->tableSort($header)
|
||||
->pager(50)
|
||||
->execute();
|
||||
|
||||
/** @var $comments \Drupal\comment\CommentInterface[] */
|
||||
$comments = $this->commentStorage->loadMultiple($cids);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Drupal\comment\Plugin\Field\FieldFormatter;
|
||||
|
||||
use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
|
||||
use Drupal\Core\Entity\Entity\EntityViewDisplay;
|
||||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Entity\EntityFormBuilderInterface;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
|
@ -36,6 +37,7 @@ class CommentDefaultFormatter extends FormatterBase implements ContainerFactoryP
|
|||
*/
|
||||
public static function defaultSettings() {
|
||||
return array(
|
||||
'view_mode' => 'default',
|
||||
'pager_id' => 0,
|
||||
) + parent::defaultSettings();
|
||||
}
|
||||
|
@ -167,7 +169,7 @@ class CommentDefaultFormatter extends FormatterBase implements ContainerFactoryP
|
|||
$comments_per_page = $comment_settings['per_page'];
|
||||
$comments = $this->storage->loadThread($entity, $field_name, $mode, $comments_per_page, $this->getSetting('pager_id'));
|
||||
if ($comments) {
|
||||
$build = $this->viewBuilder->viewMultiple($comments);
|
||||
$build = $this->viewBuilder->viewMultiple($comments, $this->getSetting('view_mode'));
|
||||
$build['pager']['#type'] = 'pager';
|
||||
// CommentController::commentPermalink() calculates the page number
|
||||
// where a specific comment appears and does a subrequest pointing to
|
||||
|
@ -217,6 +219,16 @@ class CommentDefaultFormatter extends FormatterBase implements ContainerFactoryP
|
|||
*/
|
||||
public function settingsForm(array $form, FormStateInterface $form_state) {
|
||||
$element = array();
|
||||
$view_modes = $this->getViewModes();
|
||||
$element['view_mode'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Comments view mode'),
|
||||
'#description' => $this->t('Select the view mode used to show the list of comments.'),
|
||||
'#default_value' => $this->getSetting('view_mode'),
|
||||
'#options' => $view_modes,
|
||||
// Only show the select element when there are more than one options.
|
||||
'#access' => count($view_modes) > 1,
|
||||
];
|
||||
$element['pager_id'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Pager ID'),
|
||||
|
@ -231,13 +243,41 @@ class CommentDefaultFormatter extends FormatterBase implements ContainerFactoryP
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function settingsSummary() {
|
||||
// Only show a summary if we're using a non-standard pager id.
|
||||
if ($this->getSetting('pager_id')) {
|
||||
return array($this->t('Pager ID: @id', array(
|
||||
'@id' => $this->getSetting('pager_id'),
|
||||
)));
|
||||
$view_mode = $this->getSetting('view_mode');
|
||||
$view_modes = $this->getViewModes();
|
||||
$view_mode_label = isset($view_modes[$view_mode]) ? $view_modes[$view_mode] : 'default';
|
||||
$summary = [$this->t('Comment view mode: @mode', ['@mode' => $view_mode_label])];
|
||||
if ($pager_id = $this->getSetting('pager_id')) {
|
||||
$summary[] = $this->t('Pager ID: @id', ['@id' => $pager_id]);
|
||||
}
|
||||
return array();
|
||||
return $summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
$dependencies = parent::calculateDependencies();
|
||||
if ($mode = $this->getSetting('view_mode')) {
|
||||
if ($bundle = $this->getFieldSetting('comment_type')) {
|
||||
/** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display */
|
||||
if ($display = EntityViewDisplay::load("comment.$bundle.$mode")) {
|
||||
$dependencies[$display->getConfigDependencyKey()][] = $display->getConfigDependencyName();
|
||||
}
|
||||
}
|
||||
}
|
||||
return $dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a list of comment view modes for the configured comment type.
|
||||
*
|
||||
* @return array
|
||||
* Associative array keyed by view mode key and having the view mode label
|
||||
* as value.
|
||||
*/
|
||||
protected function getViewModes() {
|
||||
return $this->entityManager->getViewModeOptionsByBundle('comment', $this->getFieldSetting('comment_type'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,12 +29,12 @@ class CommentActionsTest extends CommentTestBase {
|
|||
$comment = $this->postComment($this->node, $comment_text, $subject);
|
||||
|
||||
// Unpublish a comment.
|
||||
$action = entity_load('action', 'comment_unpublish_action');
|
||||
$action = Action::load('comment_unpublish_action');
|
||||
$action->execute(array($comment));
|
||||
$this->assertTrue($comment->isPublished() === FALSE, 'Comment was unpublished');
|
||||
|
||||
// Publish a comment.
|
||||
$action = entity_load('action', 'comment_publish_action');
|
||||
$action = Action::load('comment_publish_action');
|
||||
$action->execute(array($comment));
|
||||
$this->assertTrue($comment->isPublished() === TRUE, 'Comment was published');
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue