updated core from 8.4 to 8.5 : bug with login_destination

This commit is contained in:
Bachir Soussi Chiadmi
2018-03-13 14:01:21 +01:00
parent 0d78da249b
commit e668535a4e
3486 changed files with 89604 additions and 33283 deletions
+3 -3
View File
@@ -6,8 +6,8 @@ package: Core
# core: 8.x
configure: entity.action.collection
# Information added by Drupal.org packaging script on 2018-01-03
version: '8.4.4'
# Information added by Drupal.org packaging script on 2018-03-07
version: '8.5.0'
core: '8.x'
project: 'drupal'
datestamp: 1515021228
datestamp: 1520457826
@@ -3,6 +3,7 @@ label: Action configuration
migration_tags:
- Drupal 6
- Drupal 7
- Configuration
source:
plugin: variable
variables:
@@ -2,6 +2,7 @@ id: d6_action
label: Actions
migration_tags:
- Drupal 6
- Configuration
source:
plugin: action
process:
@@ -23,6 +24,12 @@ process:
imagecache_flush_action: 0
imagecache_generate_all_action: 0
imagecache_generate_action: 0
comment_publish_action: entity:publish_action:comment
comment_unpublish_action: entity:unpublish_action:comment
comment_save_action: entity:save_action:comment
node_publish_action: entity:publish_action:node
node_unpublish_action: entity:unpublish_action:node
node_save_action: entity:save_action:node
bypass: true
-
plugin: skip_on_empty
@@ -2,6 +2,7 @@ id: d7_action
label: Actions
migration_tags:
- Drupal 7
- Configuration
source:
plugin: action
process:
@@ -20,6 +21,12 @@ process:
system_send_email_action: action_send_email_action
system_message_action: action_message_action
system_block_ip_action: 0
comment_publish_action: entity:publish_action:comment
comment_unpublish_action: entity:unpublish_action:comment
comment_save_action: entity:save_action:comment
node_publish_action: entity:publish_action:node
node_unpublish_action: entity:unpublish_action:node
node_save_action: entity:save_action:node
bypass: true
-
plugin: skip_on_empty
+9 -48
View File
@@ -2,67 +2,28 @@
namespace Drupal\action;
use Drupal\Component\Utility\Crypt;
use Drupal\Core\Action\ActionManager;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a form for action add forms.
*
* @internal
*/
class ActionAddForm extends ActionFormBase {
/**
* The action manager.
*
* @var \Drupal\Core\Action\ActionManager
*/
protected $actionManager;
/**
* Constructs a new ActionAddForm.
*
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
* The action storage.
* @param \Drupal\Core\Action\ActionManager $action_manager
* The action plugin manager.
*/
public function __construct(EntityStorageInterface $storage, ActionManager $action_manager) {
parent::__construct($storage);
$this->actionManager = $action_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity.manager')->getStorage('action'),
$container->get('plugin.manager.action')
);
}
/**
* {@inheritdoc}
*
* @param string $action_id
* The hashed version of the action ID.
* The action ID.
*/
public function buildForm(array $form, FormStateInterface $form_state, $action_id = NULL) {
// In \Drupal\action\Form\ActionAdminManageForm::buildForm() the action
// are hashed. Here we have to decrypt it to find the desired action ID.
foreach ($this->actionManager->getDefinitions() as $id => $definition) {
$key = Crypt::hashBase64($id);
if ($key === $action_id) {
$this->entity->setPlugin($id);
// Derive the label and type from the action definition.
$this->entity->set('label', $definition['label']);
$this->entity->set('type', $definition['type']);
break;
}
}
$this->entity->setPlugin($action_id);
// Derive the label and type from the action definition.
$definition = $this->entity->getPluginDefinition();
$this->entity->set('label', $definition['label']);
$this->entity->set('type', $definition['type']);
return parent::buildForm($form, $form_state);
}
@@ -4,6 +4,8 @@ namespace Drupal\action;
/**
* Provides a form for action edit forms.
*
* @internal
*/
class ActionEditForm extends ActionFormBase {
+27 -24
View File
@@ -13,13 +13,6 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
*/
abstract class ActionFormBase extends EntityForm {
/**
* The action plugin being configured.
*
* @var \Drupal\Core\Action\ActionInterface
*/
protected $plugin;
/**
* The action storage.
*
@@ -27,6 +20,13 @@ abstract class ActionFormBase extends EntityForm {
*/
protected $storage;
/**
* The action entity.
*
* @var \Drupal\system\ActionConfigEntityInterface
*/
protected $entity;
/**
* Constructs a new action form.
*
@@ -46,14 +46,6 @@ abstract class ActionFormBase extends EntityForm {
);
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$this->plugin = $this->entity->getPlugin();
return parent::buildForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
@@ -85,8 +77,8 @@ abstract class ActionFormBase extends EntityForm {
'#value' => $this->entity->getType(),
];
if ($this->plugin instanceof PluginFormInterface) {
$form += $this->plugin->buildConfigurationForm($form, $form_state);
if ($plugin = $this->getPlugin()) {
$form += $plugin->buildConfigurationForm($form, $form_state);
}
return parent::form($form, $form_state);
@@ -96,7 +88,7 @@ abstract class ActionFormBase extends EntityForm {
* Determines if the action already exists.
*
* @param string $id
* The action ID
* The action ID.
*
* @return bool
* TRUE if the action exists, FALSE otherwise.
@@ -120,9 +112,8 @@ abstract class ActionFormBase extends EntityForm {
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
parent::validateForm($form, $form_state);
if ($this->plugin instanceof PluginFormInterface) {
$this->plugin->validateConfigurationForm($form, $form_state);
if ($plugin = $this->getPlugin()) {
$plugin->validateConfigurationForm($form, $form_state);
}
}
@@ -131,9 +122,8 @@ abstract class ActionFormBase extends EntityForm {
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
parent::submitForm($form, $form_state);
if ($this->plugin instanceof PluginFormInterface) {
$this->plugin->submitConfigurationForm($form, $form_state);
if ($plugin = $this->getPlugin()) {
$plugin->submitConfigurationForm($form, $form_state);
}
}
@@ -147,4 +137,17 @@ abstract class ActionFormBase extends EntityForm {
$form_state->setRedirect('entity.action.collection');
}
/**
* Gets the action plugin while ensuring it implements configuration form.
*
* @return \Drupal\Core\Action\ActionInterface|\Drupal\Core\Plugin\PluginFormInterface|null
* The action plugin, or NULL if it does not implement configuration forms.
*/
protected function getPlugin() {
if ($this->entity->getPlugin() instanceof PluginFormInterface) {
return $this->entity->getPlugin();
}
return NULL;
}
}
@@ -108,12 +108,12 @@ class ActionListBuilder extends ConfigEntityListBuilder {
* {@inheritdoc}
*/
public function render() {
$build['action_admin_manage_form'] = \Drupal::formBuilder()->getForm('Drupal\action\Form\ActionAdminManageForm');
$build['action_header']['#markup'] = '<h3>' . $this->t('Available actions:') . '</h3>';
$build['action_table'] = parent::render();
if (!$this->hasConfigurableActions) {
unset($build['action_table']['table']['#header']['operations']);
}
$build['action_admin_manage_form'] = \Drupal::formBuilder()->getForm('Drupal\action\Form\ActionAdminManageForm');
return $build;
}
@@ -3,13 +3,14 @@
namespace Drupal\action\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Component\Utility\Crypt;
use Drupal\Core\Action\ActionManager;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a configuration form for configurable actions.
*
* @internal
*/
class ActionAdminManageForm extends FormBase {
@@ -53,10 +54,10 @@ class ActionAdminManageForm extends FormBase {
$actions = [];
foreach ($this->manager->getDefinitions() as $id => $definition) {
if (is_subclass_of($definition['class'], '\Drupal\Core\Plugin\PluginFormInterface')) {
$key = Crypt::hashBase64($id);
$actions[$key] = $definition['label'] . '...';
$actions[$id] = $definition['label'];
}
}
asort($actions);
$form['parent'] = [
'#type' => 'details',
'#title' => $this->t('Create an advanced action'),
@@ -68,7 +69,7 @@ class ActionAdminManageForm extends FormBase {
'#title' => $this->t('Action'),
'#title_display' => 'invisible',
'#options' => $actions,
'#empty_option' => $this->t('Choose an advanced action'),
'#empty_option' => $this->t('- Select -'),
];
$form['parent']['actions'] = [
'#type' => 'actions'
@@ -7,6 +7,8 @@ use Drupal\Core\Url;
/**
* Builds a form to delete an action.
*
* @internal
*/
class ActionDeleteForm extends EntityDeleteForm {
@@ -9,8 +9,8 @@ dependencies:
- views
- node
# Information added by Drupal.org packaging script on 2018-01-03
version: '8.4.4'
# Information added by Drupal.org packaging script on 2018-03-07
version: '8.5.0'
core: '8.x'
project: 'drupal'
datestamp: 1515021228
datestamp: 1520457826
@@ -0,0 +1,13 @@
name: action_form_ajax_test
type: module
description: 'module used for testing ajax in action config entity forms.'
package: Core
# version: VERSION
# core: 8.x
hidden: true
# Information added by Drupal.org packaging script on 2018-03-07
version: '8.5.0'
core: '8.x'
project: 'drupal'
datestamp: 1520457826
@@ -0,0 +1,7 @@
action.configuration.action_form_ajax_test:
type: action_configuration_default
label: 'action_form_ajax_test action'
mapping:
party_time:
type: string
label: 'The time of the party.'
@@ -0,0 +1,89 @@
<?php
namespace Drupal\action_form_ajax_test\Plugin\Action;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Action\ConfigurableActionBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
/**
* Plugin used for testing AJAX in action config entity forms.
*
* @Action(
* id = "action_form_ajax_test",
* label = @Translation("action_form_ajax_test"),
* type = "system"
* )
*/
class ActionAjaxTest extends ConfigurableActionBase {
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return [
'party_time' => '',
];
}
/**
* {@inheritdoc}
*/
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
$result = AccessResult::allowed();
return $return_as_object ? $result : $result->isAllowed();
}
/**
* {@inheritdoc}
*/
public function execute() {
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$having_a_party = $form_state->getValue('having_a_party', !empty($this->configuration['party_time']));
$form['having_a_party'] = [
'#type' => 'checkbox',
'#title' => $this->t('Are we having a party?'),
'#ajax' => [
'wrapper' => 'party-container',
'callback' => [$this, 'partyCallback'],
],
'#default_value' => $having_a_party,
];
$form['container'] = [
'#type' => 'container',
'#prefix' => '<div id="party-container">',
'#suffix' => '</div>',
];
if ($having_a_party) {
$form['container']['party_time'] = [
'#type' => 'textfield',
'#title' => $this->t('Party time'),
'#default_value' => $this->configuration['party_time'],
];
}
return $form;
}
/**
* Callback for party checkbox.
*/
public function partyCallback(array $form, FormStateInterface $form_state) {
return $form['container'];
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$this->configuration['party_time'] = $form_state->getValue('party_time');
}
}
@@ -2,7 +2,6 @@
namespace Drupal\Tests\action\Functional;
use Drupal\Component\Utility\Crypt;
use Drupal\system\Entity\Action;
use Drupal\Tests\BrowserTestBase;
@@ -31,7 +30,7 @@ class ConfigurationTest extends BrowserTestBase {
// Make a POST request to admin/config/system/actions.
$edit = [];
$edit['action'] = Crypt::hashBase64('action_goto_action');
$edit['action'] = 'action_goto_action';
$this->drupalPostForm('admin/config/system/actions', $edit, t('Create'));
$this->assertResponse(200);
@@ -41,17 +40,18 @@ class ConfigurationTest extends BrowserTestBase {
$edit['label'] = $action_label;
$edit['id'] = strtolower($action_label);
$edit['url'] = 'admin';
$this->drupalPostForm('admin/config/system/actions/add/' . Crypt::hashBase64('action_goto_action'), $edit, t('Save'));
$this->drupalPostForm('admin/config/system/actions/add/action_goto_action', $edit, t('Save'));
$this->assertResponse(200);
$action_id = $edit['id'];
// Make sure that the new complex action was saved properly.
$this->assertText(t('The action has been successfully saved.'), "Make sure we get a confirmation that we've successfully saved the complex action.");
$this->assertText($action_label, "Make sure the action label appears on the configuration page after we've saved the complex action.");
// Make another POST request to the action edit page.
$this->clickLink(t('Configure'));
preg_match('|admin/config/system/actions/configure/(.+)|', $this->getUrl(), $matches);
$aid = $matches[1];
$edit = [];
$new_action_label = $this->randomMachineName();
$edit['label'] = $new_action_label;
@@ -73,7 +73,7 @@ class ConfigurationTest extends BrowserTestBase {
$this->clickLink(t('Delete'));
$this->assertResponse(200);
$edit = [];
$this->drupalPostForm("admin/config/system/actions/configure/$aid/delete", $edit, t('Delete'));
$this->drupalPostForm(NULL, $edit, t('Delete'));
$this->assertResponse(200);
// Make sure that the action was actually deleted.
@@ -82,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 = Action::load($aid);
$action = Action::load($action_id);
$this->assertFalse($action, 'Make sure the action is gone after being deleted.');
}
@@ -0,0 +1,70 @@
<?php
namespace Drupal\Tests\action\FunctionalJavascript;
use Drupal\Core\Url;
use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
use Drupal\system\Entity\Action;
/**
* Tests action plugins using Javascript.
*
* @group action
*/
class ActionFormAjaxTest extends JavascriptTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['action', 'action_form_ajax_test'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$user = $this->drupalCreateUser(['administer actions']);
$this->drupalLogin($user);
}
/**
* Tests action plugins with AJAX save their configuration.
*/
public function testActionConfigurationWithAjax() {
$url = Url::fromRoute('action.admin_add', ['action_id' => 'action_form_ajax_test']);
$this->drupalGet($url);
$this->assertSession()->statusCodeEquals(200);
$page = $this->getSession()->getPage();
$id = 'test_plugin';
$page->find('css', '[name="id"]')
->setValue($id);
$page->find('css', '[name="having_a_party"]')
->check();
$this->assertSession()->waitForElement('css', '[name="party_time"]');
$party_time = 'Evening';
$page->find('css', '[name="party_time"]')
->setValue($party_time);
$page->find('css', '[value="Save"]')
->click();
$url = Url::fromRoute('entity.action.collection');
$this->assertSession()->pageTextContains('The action has been successfully saved.');
$this->assertSession()->addressEquals($url);
$this->assertSession()->statusCodeEquals(200);
// Check storage.
$instance = Action::load($id);
$configuration = $instance->getPlugin()->getConfiguration();
$this->assertEquals(['party_time' => $party_time], $configuration);
// Configuration should be shown in edit form.
$this->drupalGet($instance->toUrl('edit-form'));
$this->assertSession()->checkboxChecked('having_a_party');
$this->assertSession()->fieldValueEquals('party_time', $party_time);
}
}
@@ -15,7 +15,7 @@ class ActionTest extends MigrateSqlSourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['action', 'migrate_drupal'];
public static $modules = ['action', 'migrate_drupal', 'system'];
/**
* {@inheritdoc}
+3 -3
View File
@@ -9,8 +9,8 @@ dependencies:
- file
- options
# Information added by Drupal.org packaging script on 2018-01-03
version: '8.4.4'
# Information added by Drupal.org packaging script on 2018-03-07
version: '8.5.0'
core: '8.x'
project: 'drupal'
datestamp: 1515021228
datestamp: 1520457826
@@ -1,7 +1,9 @@
id: d6_aggregator_feed
label: Aggregator feeds
audit: true
migration_tags:
- Drupal 6
- Content
source:
plugin: aggregator_feed
process:
@@ -1,7 +1,9 @@
id: d6_aggregator_item
label: Aggregator items
audit: true
migration_tags:
- Drupal 6
- Content
source:
plugin: aggregator_item
process:
@@ -2,6 +2,7 @@ id: d6_aggregator_settings
label: Aggregator configuration
migration_tags:
- Drupal 6
- Configuration
source:
plugin: variable
variables:
@@ -1,7 +1,9 @@
id: d7_aggregator_feed
label: Aggregator feeds
audit: true
migration_tags:
- Drupal 7
- Content
source:
plugin: aggregator_feed
process:
@@ -1,7 +1,9 @@
id: d7_aggregator_item
label: Aggregator items
audit: true
migration_tags:
- Drupal 7
- Content
source:
plugin: aggregator_item
process:
@@ -2,6 +2,7 @@ id: d7_aggregator_settings
label: Aggregator configuration
migration_tags:
- Drupal 7
- Configuration
source:
plugin: variable
variables:
@@ -93,7 +93,7 @@ class AggregatorController extends ControllerBase {
$message = $aggregator_feed->refreshItems()
? $this->t('There is new syndicated content from %site.', ['%site' => $aggregator_feed->label()])
: $this->t('There is no new syndicated content from %site.', ['%site' => $aggregator_feed->label()]);
drupal_set_message($message);
$this->messenger()->addStatus($message);
return $this->redirect('aggregator.admin_overview');
}
+2
View File
@@ -8,6 +8,8 @@ use Drupal\Core\Url;
/**
* Form handler for the aggregator feed edit forms.
*
* @internal
*/
class FeedForm extends ContentEntityForm {
@@ -7,6 +7,8 @@ use Drupal\Core\Url;
/**
* Provides a form for deleting a feed.
*
* @internal
*/
class FeedDeleteForm extends ContentEntityDeleteForm {
@@ -8,6 +8,8 @@ use Drupal\Core\Url;
/**
* Provides a deletion confirmation form for items that belong to a feed.
*
* @internal
*/
class FeedItemsDeleteForm extends ContentEntityConfirmFormBase {
@@ -12,6 +12,8 @@ use GuzzleHttp\ClientInterface;
/**
* Imports feeds from OPML.
*
* @internal
*/
class OpmlFeedAdd extends FormBase {
@@ -13,6 +13,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Configures aggregator settings for this site.
*
* @internal
*/
class SettingsForm extends ConfigFormBase {
@@ -167,8 +167,10 @@ class AggregatorFeedBlock extends BlockBase implements ContainerFactoryPluginInt
*/
public function getCacheTags() {
$cache_tags = parent::getCacheTags();
$feed = $this->feedStorage->load($this->configuration['feed']);
return Cache::mergeTags($cache_tags, $feed->getCacheTags());
if ($feed = $this->feedStorage->load($this->configuration['feed'])) {
$cache_tags = Cache::mergeTags($cache_tags, $feed->getCacheTags());
}
return $cache_tags;
}
}
@@ -5,8 +5,8 @@ package: Testing
# version: VERSION
# core: 8.x
# Information added by Drupal.org packaging script on 2018-01-03
version: '8.4.4'
# Information added by Drupal.org packaging script on 2018-03-07
version: '8.5.0'
core: '8.x'
project: 'drupal'
datestamp: 1515021228
datestamp: 1520457826
@@ -8,8 +8,8 @@ dependencies:
- aggregator
- views
# Information added by Drupal.org packaging script on 2018-01-03
version: '8.4.4'
# Information added by Drupal.org packaging script on 2018-03-07
version: '8.5.0'
core: '8.x'
project: 'drupal'
datestamp: 1515021228
datestamp: 1520457826
@@ -2,7 +2,6 @@
namespace Drupal\Tests\aggregator\Kernel\Migrate;
use Drupal\migrate\MigrateException;
use Drupal\Tests\migrate_drupal\Kernel\MigrateDrupalTestBase;
use Drupal\migrate_drupal\Tests\StubTestTrait;
@@ -40,18 +39,6 @@ class MigrateAggregatorStubTest extends MigrateDrupalTestBase {
* Tests creation of aggregator feed items.
*/
public function testItemStub() {
try {
// We expect an exception, because there's no feed to reference.
$this->performStubTest('aggregator_item');
$this->fail('Expected exception has not been thrown.');
}
catch (MigrateException $e) {
$this->assertIdentical($e->getMessage(),
'Stubbing failed, unable to generate value for field fid');
}
// The stub should pass when there's a feed to point to.
$this->createStub('aggregator_feed');
$this->performStubTest('aggregator_item');
}
@@ -6,8 +6,8 @@ package: Core
# core: 8.x
configure: system.cron_settings
# Information added by Drupal.org packaging script on 2018-01-03
version: '8.4.4'
# Information added by Drupal.org packaging script on 2018-03-07
version: '8.5.0'
core: '8.x'
project: 'drupal'
datestamp: 1515021228
datestamp: 1520457826
+3 -3
View File
@@ -6,8 +6,8 @@ package: Core
# core: 8.x
configure: ban.admin_page
# Information added by Drupal.org packaging script on 2018-01-03
version: '8.4.4'
# Information added by Drupal.org packaging script on 2018-03-07
version: '8.5.0'
core: '8.x'
project: 'drupal'
datestamp: 1515021228
datestamp: 1520457826
@@ -2,6 +2,7 @@ id: d7_blocked_ips
label: Blocked IPs
migration_tags:
- Drupal 7
- Content
source:
plugin: d7_blocked_ips
process:
+2
View File
@@ -10,6 +10,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Displays banned IP addresses.
*
* @internal
*/
class BanAdmin extends FormBase {
+2
View File
@@ -11,6 +11,8 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Provides a form to unban IP addresses.
*
* @internal
*/
class BanDelete extends ConfirmFormBase {
+3 -3
View File
@@ -7,8 +7,8 @@ package: Web services
dependencies:
- user
# Information added by Drupal.org packaging script on 2018-01-03
version: '8.4.4'
# Information added by Drupal.org packaging script on 2018-03-07
version: '8.5.0'
core: '8.x'
project: 'drupal'
datestamp: 1515021228
datestamp: 1520457826
@@ -5,12 +5,13 @@ namespace Drupal\basic_auth\Authentication\Provider;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Authentication\AuthenticationProviderInterface;
use Drupal\Core\Authentication\AuthenticationProviderChallengeInterface;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Flood\FloodInterface;
use Drupal\Core\Http\Exception\CacheableUnauthorizedHttpException;
use Drupal\user\UserAuthInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
/**
* HTTP Basic authentication provider.
@@ -126,11 +127,35 @@ class BasicAuth implements AuthenticationProviderInterface, AuthenticationProvid
* {@inheritdoc}
*/
public function challengeException(Request $request, \Exception $previous) {
$site_name = $this->configFactory->get('system.site')->get('name');
$site_config = $this->configFactory->get('system.site');
$site_name = $site_config->get('name');
$challenge = SafeMarkup::format('Basic realm="@realm"', [
'@realm' => !empty($site_name) ? $site_name : 'Access restricted',
]);
return new UnauthorizedHttpException((string) $challenge, 'No authentication credentials provided.', $previous);
// A 403 is converted to a 401 here, but it doesn't matter what the
// cacheability was of the 403 exception: what matters here is that
// authentication credentials are missing, i.e. that this request was made
// as the anonymous user.
// Therefore, all we must do, is make this response:
// 1. vary by whether the current user has the 'anonymous' role or not. This
// works fine because:
// - Thanks to \Drupal\basic_auth\PageCache\DisallowBasicAuthRequests,
// Page Cache never caches a response whose request has Basic Auth
// credentials.
// - Dynamic Page Cache will cache a different result for when the
// request is unauthenticated (this 401) versus authenticated (some
// other response)
// 2. have the 'config:user.role.anonymous' cache tag, because the only
// reason this 401 would no longer be a 401 is if permissions for the
// 'anonymous' role change, causing that cache tag to be invalidated.
// @see \Drupal\Core\EventSubscriber\AuthenticationSubscriber::onExceptionSendChallenge()
// @see \Drupal\Core\EventSubscriber\ClientErrorResponseSubscriber()
// @see \Drupal\Core\EventSubscriber\FinishResponseSubscriber::onAllResponds()
$cacheability = CacheableMetadata::createFromObject($site_config)
->addCacheTags(['config:user.role.anonymous'])
->addCacheContexts(['user.roles:anonymous']);
return new CacheableUnauthorizedHttpException($cacheability, (string) $challenge, 'No authentication credentials provided.', $previous);
}
}
@@ -2,7 +2,7 @@
namespace Drupal\basic_auth\Tests;
@trigger_error(__FILE__ . ' is deprecated in Drupal 8.3.0 and will be removed before Drupal 9.0.0. Use \Drupal\Tests\basic_auth\Traits\BasicAuthTestTrait instead. See https://www.drupal.org/node/2862800.', E_USER_DEPRECATED);
@trigger_error(__NAMESPACE__ . '\BasicAuthTestTrait is deprecated in Drupal 8.3.0 and will be removed before Drupal 9.0.0. Use \Drupal\Tests\basic_auth\Traits\BasicAuthTestTrait instead. See https://www.drupal.org/node/2862800.', E_USER_DEPRECATED);
/**
* Provides common functionality for Basic Authentication test classes.
@@ -5,8 +5,8 @@ package: Testing
# version: VERSION
# core: 8.x
# Information added by Drupal.org packaging script on 2018-01-03
version: '8.4.4'
# Information added by Drupal.org packaging script on 2018-03-07
version: '8.5.0'
core: '8.x'
project: 'drupal'
datestamp: 1515021228
datestamp: 1520457826
@@ -7,6 +7,7 @@ use Drupal\Core\Url;
use Drupal\Tests\basic_auth\Traits\BasicAuthTestTrait;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\Tests\BrowserTestBase;
use Drupal\user\Entity\Role;
/**
* Tests for BasicAuth authentication provider.
@@ -180,6 +181,47 @@ class BasicAuthTest extends BrowserTestBase {
$this->assertText('Access denied', "A user friendly access denied message is displayed");
}
/**
* Tests the cacheability of Basic Auth's 401 response.
*
* @see \Drupal\basic_auth\Authentication\Provider\BasicAuth::challengeException()
*/
public function testCacheabilityOf401Response() {
$session = $this->getSession();
$url = Url::fromRoute('router_test.11');
$assert_response_cacheability = function ($expected_page_cache_header_value, $expected_dynamic_page_cache_header_value) use ($session, $url) {
$this->drupalGet($url);
$this->assertSession()->statusCodeEquals(401);
$this->assertSame($expected_page_cache_header_value, $session->getResponseHeader('X-Drupal-Cache'));
$this->assertSame($expected_dynamic_page_cache_header_value, $session->getResponseHeader('X-Drupal-Dynamic-Cache'));
};
// 1. First request: cold caches, both Page Cache and Dynamic Page Cache are
// now primed.
$assert_response_cacheability('MISS', 'MISS');
// 2. Second request: Page Cache HIT, we don't even hit Dynamic Page Cache.
// This is going to keep happening.
$assert_response_cacheability('HIT', 'MISS');
// 3. Third request: after clearing Page Cache, we now see that Dynamic Page
// Cache is a HIT too.
$this->container->get('cache.page')->deleteAll();
$assert_response_cacheability('MISS', 'HIT');
// 4. Fourth request: warm caches.
$assert_response_cacheability('HIT', 'HIT');
// If the permissions of the 'anonymous' role change, it may no longer be
// necessary to be authenticated to access this route. Therefore the cached
// 401 responses should be invalidated.
$this->grantPermissions(Role::load(Role::ANONYMOUS_ID), [$this->randomMachineName()]);
$assert_response_cacheability('MISS', 'MISS');
$assert_response_cacheability('HIT', 'MISS');
// Idem for when the 'system.site' config changes.
$this->config('system.site')->save();
$assert_response_cacheability('MISS', 'MISS');
$assert_response_cacheability('HIT', 'MISS');
}
/**
* Tests if the controller is called before authentication.
*
+3 -3
View File
@@ -5,8 +5,8 @@ package: Core
# version: VERSION
# core: 8.x
# Information added by Drupal.org packaging script on 2018-01-03
version: '8.4.4'
# Information added by Drupal.org packaging script on 2018-03-07
version: '8.5.0'
core: '8.x'
project: 'drupal'
datestamp: 1515021228
datestamp: 1520457826
+30 -7
View File
@@ -20,16 +20,17 @@
// Ignore any placeholders that are not in the known placeholder list. Used
// to avoid someone trying to XSS the site via the placeholdering mechanism.
if (typeof drupalSettings.bigPipePlaceholderIds[placeholderId] !== 'undefined') {
const response = mapTextContentToAjaxResponse(content);
// If we try to parse the content too early (when the JSON containing Ajax
// commands is still arriving), textContent will be empty which will cause
// JSON.parse() to fail. Remove once so that it can be processed again
// later.
// @see bigPipeProcessDocument()
if (content === '') {
// commands is still arriving), textContent will be empty or incomplete.
if (response === false) {
/**
* Mark as unprocessed so this will be retried later.
* @see bigPipeProcessDocument()
*/
$(this).removeOnce('big-pipe');
}
else {
const response = JSON.parse(content);
// Create a Drupal.Ajax object without associating an element, a
// progress indicator or a URL.
const ajaxObject = Drupal.ajax({
@@ -45,6 +46,28 @@
}
}
/**
* Maps textContent of <script type="application/vnd.drupal-ajax"> to an AJAX response.
*
* @param {string} content
* The text content of a <script type="application/vnd.drupal-ajax"> DOM node.
* @return {Array|boolean}
* The parsed Ajax response containing an array of Ajax commands, or false in
* case the DOM node hasn't fully arrived yet.
*/
function mapTextContentToAjaxResponse(content) {
if (content === '') {
return false;
}
try {
return JSON.parse(content);
}
catch (e) {
return false;
}
}
/**
* Processes a streamed HTML document receiving placeholder replacements.
*
@@ -89,7 +112,7 @@
// The frequency with which to check for newly arrived BigPipe placeholders.
// Hence 50 ms means we check 20 times per second. Setting this to 100 ms or
// more would cause the user to see content appear noticeably slower.
var interval = drupalSettings.bigPipeInterval || 50;
const interval = drupalSettings.bigPipeInterval || 50;
// The internal ID to contain the watcher service.
let timeoutID;
+15 -3
View File
@@ -11,11 +11,11 @@
var content = this.textContent.trim();
if (typeof drupalSettings.bigPipePlaceholderIds[placeholderId] !== 'undefined') {
if (content === '') {
var response = mapTextContentToAjaxResponse(content);
if (response === false) {
$(this).removeOnce('big-pipe');
} else {
var response = JSON.parse(content);
var ajaxObject = Drupal.ajax({
url: '',
base: false,
@@ -28,6 +28,18 @@
}
}
function mapTextContentToAjaxResponse(content) {
if (content === '') {
return false;
}
try {
return JSON.parse(content);
} catch (e) {
return false;
}
}
function bigPipeProcessDocument(context) {
if (!context.querySelector('script[data-big-pipe-event="start"]')) {
return false;
+3 -3
View File
@@ -420,7 +420,7 @@ class BigPipe {
}
$placeholder = $fragment;
assert('isset($no_js_placeholders[$placeholder])');
assert(isset($no_js_placeholders[$placeholder]));
$token = Crypt::randomBytesBase64(55);
// Render the placeholder, but include the cumulative settings assets, so
@@ -629,7 +629,7 @@ EOF;
* AJAX page state.
*/
protected function filterEmbeddedResponse(Request $fake_request, Response $embedded_response) {
assert('$embedded_response instanceof \Drupal\Core\Render\HtmlResponse || $embedded_response instanceof \Drupal\Core\Ajax\AjaxResponse');
assert($embedded_response instanceof HtmlResponse || $embedded_response instanceof AjaxResponse);
return $this->filterResponse($fake_request, HttpKernelInterface::SUB_REQUEST, $embedded_response);
}
@@ -649,7 +649,7 @@ EOF;
* The filtered response.
*/
protected function filterResponse(Request $request, $request_type, Response $response) {
assert('$request_type === \Symfony\Component\HttpKernel\HttpKernelInterface::MASTER_REQUEST || $request_type === \Symfony\Component\HttpKernel\HttpKernelInterface::SUB_REQUEST');
assert($request_type === HttpKernelInterface::MASTER_REQUEST || $request_type === HttpKernelInterface::SUB_REQUEST);
$this->requestStack->push($request);
$event = new FilterResponseEvent($this->httpKernel, $request, $request_type, $response);
$this->eventDispatcher->dispatch(KernelEvents::RESPONSE, $event);
@@ -9,6 +9,7 @@ use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\EnforcedResponseException;
use Drupal\Core\Render\AttachmentsInterface;
use Drupal\Core\Render\AttachmentsResponseProcessorInterface;
use Drupal\Core\Render\HtmlResponse;
use Drupal\Core\Render\HtmlResponseAttachmentsProcessor;
use Drupal\Core\Render\RendererInterface;
use Symfony\Component\HttpFoundation\RequestStack;
@@ -57,7 +58,7 @@ class BigPipeResponseAttachmentsProcessor extends HtmlResponseAttachmentsProcess
* {@inheritdoc}
*/
public function processAttachments(AttachmentsInterface $response) {
assert('$response instanceof \Drupal\Core\Render\HtmlResponse');
assert($response instanceof HtmlResponse);
// First, render the actual placeholders; this will cause the BigPipe
// placeholder strategy to generate BigPipe placeholders. We need those to
@@ -180,7 +180,7 @@ class BigPipeStrategy implements PlaceholderStrategyInterface {
* a placeholder for a HTML attribute value or a subset of it).
*/
protected static function placeholderIsAttributeSafe($placeholder) {
assert('is_string($placeholder)');
assert(is_string($placeholder));
return $placeholder[0] !== '<' || $placeholder !== Html::normalize($placeholder);
}
@@ -5,8 +5,8 @@ package: Testing
# version: VERSION
# core: 8.x
# Information added by Drupal.org packaging script on 2018-01-03
version: '8.4.4'
# Information added by Drupal.org packaging script on 2018-03-07
version: '8.5.0'
core: '8.x'
project: 'drupal'
datestamp: 1515021228
datestamp: 1520457826
@@ -5,8 +5,8 @@ package: Testing
# version: VERSION
# core: 8.x
# Information added by Drupal.org packaging script on 2018-01-03
version: '8.4.4'
# Information added by Drupal.org packaging script on 2018-03-07
version: '8.5.0'
core: '8.x'
project: 'drupal'
datestamp: 1515021228
datestamp: 1520457826
@@ -5,6 +5,11 @@ namespace Drupal\big_pipe_test\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Form to test BigPipe.
*
* @internal
*/
class BigPipeTestForm extends FormBase {
/**
@@ -4,8 +4,8 @@ description: 'Theme for testing BigPipe edge cases.'
# version: VERSION
# core: 8.x
# Information added by Drupal.org packaging script on 2018-01-03
version: '8.4.4'
# Information added by Drupal.org packaging script on 2018-03-07
version: '8.5.0'
core: '8.x'
project: 'drupal'
datestamp: 1515021228
datestamp: 1520457826
+3 -3
View File
@@ -6,8 +6,8 @@ package: Core
# core: 8.x
configure: block.admin_display
# Information added by Drupal.org packaging script on 2018-01-03
version: '8.4.4'
# Information added by Drupal.org packaging script on 2018-03-07
version: '8.5.0'
core: '8.x'
project: 'drupal'
datestamp: 1515021228
datestamp: 1520457826
@@ -30,6 +30,3 @@ block.block.*:
sequence:
type: condition.plugin.[id]
label: 'Visibility Condition'
block.settings.*:
type: block_settings
+7 -7
View File
@@ -24,7 +24,7 @@
attach(context, settings) {
const $input = $('input.block-filter-text').once('block-filter-text');
const $table = $($input.attr('data-element'));
let $filter_rows;
let $filterRows;
/**
* Filters the block list.
@@ -52,24 +52,24 @@
// Filter if the length of the query is at least 2 characters.
if (query.length >= 2) {
$filter_rows.each(toggleBlockEntry);
$filterRows.each(toggleBlockEntry);
Drupal.announce(
Drupal.formatPlural(
$table.find('tr:visible').length - 1,
'1 block is available in the modified list.',
'@count blocks are available in the modified list.'
)
'@count blocks are available in the modified list.',
),
);
}
else {
$filter_rows.each(function (index) {
$filterRows.each(function (index) {
$(this).parent().parent().show();
});
}
}
if ($table.length) {
$filter_rows = $table.find('div.block-filter-text-source');
$filterRows = $table.find('div.block-filter-text-source');
$input.on('keyup', debounce(filterBlockList, 200));
}
},
@@ -91,7 +91,7 @@
// Just scrolling the document.body will not work in Firefox. The html
// element is needed as well.
$('html, body').animate({
scrollTop: $('.js-block-placed').offset().top - $container.offset().top + $container.scrollTop(),
scrollTop: ($('.js-block-placed').offset().top - $container.offset().top) + $container.scrollTop(),
}, 500);
});
}
+4 -4
View File
@@ -10,7 +10,7 @@
attach: function attach(context, settings) {
var $input = $('input.block-filter-text').once('block-filter-text');
var $table = $($input.attr('data-element'));
var $filter_rows = void 0;
var $filterRows = void 0;
function filterBlockList(e) {
var query = $(e.target).val().toLowerCase();
@@ -23,17 +23,17 @@
}
if (query.length >= 2) {
$filter_rows.each(toggleBlockEntry);
$filterRows.each(toggleBlockEntry);
Drupal.announce(Drupal.formatPlural($table.find('tr:visible').length - 1, '1 block is available in the modified list.', '@count blocks are available in the modified list.'));
} else {
$filter_rows.each(function (index) {
$filterRows.each(function (index) {
$(this).parent().parent().show();
});
}
}
if ($table.length) {
$filter_rows = $table.find('div.block-filter-text-source');
$filterRows = $table.find('div.block-filter-text-source');
$input.on('keyup', debounce(filterBlockList, 200));
}
}
+6 -6
View File
@@ -195,14 +195,14 @@
// Find the correct region and insert the row as the last in the
// region.
tableDrag.rowObject = new tableDrag.row(row[0]);
const region_message = table.find(`.region-${select[0].value}-message`);
const region_items = region_message.nextUntil('.region-message, .region-title');
if (region_items.length) {
region_items.last().after(row);
const regionMessage = table.find(`.region-${select[0].value}-message`);
const regionItems = regionMessage.nextUntil('.region-message, .region-title');
if (regionItems.length) {
regionItems.last().after(row);
}
// We found that region_message is the last row.
// We found that regionMessage is the last row.
else {
region_message.after(row);
regionMessage.after(row);
}
updateBlockWeights(table, select[0].value);
// Modify empty regions with added or removed fields.
+5 -5
View File
@@ -119,12 +119,12 @@
var select = $(this);
tableDrag.rowObject = new tableDrag.row(row[0]);
var region_message = table.find('.region-' + select[0].value + '-message');
var region_items = region_message.nextUntil('.region-message, .region-title');
if (region_items.length) {
region_items.last().after(row);
var regionMessage = table.find('.region-' + select[0].value + '-message');
var regionItems = regionMessage.nextUntil('.region-message, .region-title');
if (regionItems.length) {
regionItems.last().after(row);
} else {
region_message.after(row);
regionMessage.after(row);
}
updateBlockWeights(table, select[0].value);
@@ -2,6 +2,7 @@ id: d6_block
label: Blocks
migration_tags:
- Drupal 6
- Configuration
source:
plugin: block
process:
@@ -2,6 +2,7 @@ id: d7_block
label: Blocks
migration_tags:
- Drupal 7
- Configuration
source:
plugin: block
process:
+2
View File
@@ -19,6 +19,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides form for block instance forms.
*
* @internal
*/
class BlockForm extends EntityForm {
@@ -105,6 +105,10 @@ class BlockLibraryController extends ControllerBase {
$definitions = $this->blockManager->getDefinitionsForContexts($this->contextRepository->getAvailableContexts());
// Order by category, and then by admin label.
$definitions = $this->blockManager->getSortedDefinitions($definitions);
// Filter out definitions that are not intended to be placed by the UI.
$definitions = array_filter($definitions, function (array $definition) {
return empty($definition['_block_ui_hidden']);
});
$region = $request->query->get('region');
$weight = $request->query->get('weight');
@@ -7,6 +7,8 @@ use Drupal\Core\Url;
/**
* Provides a deletion confirmation form for the block instance deletion form.
*
* @internal
*/
class BlockDeleteForm extends EntityDeleteForm {
@@ -7,8 +7,8 @@ package: Testing
dependencies:
- block
# Information added by Drupal.org packaging script on 2018-01-03
version: '8.4.4'
# Information added by Drupal.org packaging script on 2018-03-07
version: '8.5.0'
core: '8.x'
project: 'drupal'
datestamp: 1515021228
datestamp: 1520457826
@@ -5,6 +5,11 @@ namespace Drupal\block_test\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Form that performs favorite animal test.
*
* @internal
*/
class FavoriteAnimalTestForm extends FormBase {
/**
@@ -5,6 +5,11 @@ namespace Drupal\block_test\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Form that performs base block form test.
*
* @internal
*/
class TestForm extends FormBase {
/**
@@ -6,8 +6,8 @@ regions:
content: Content
help: Help
# Information added by Drupal.org packaging script on 2018-01-03
version: '8.4.4'
# Information added by Drupal.org packaging script on 2018-03-07
version: '8.5.0'
core: '8.x'
project: 'drupal'
datestamp: 1515021228
datestamp: 1520457826
@@ -15,8 +15,8 @@ regions_hidden:
- sidebar_first
- sidebar_second
# Information added by Drupal.org packaging script on 2018-01-03
version: '8.4.4'
# Information added by Drupal.org packaging script on 2018-03-07
version: '8.5.0'
core: '8.x'
project: 'drupal'
datestamp: 1515021228
datestamp: 1520457826
@@ -8,8 +8,8 @@ dependencies:
- block
- views
# Information added by Drupal.org packaging script on 2018-01-03
version: '8.4.4'
# Information added by Drupal.org packaging script on 2018-03-07
version: '8.5.0'
core: '8.x'
project: 'drupal'
datestamp: 1515021228
datestamp: 1520457826
@@ -3,6 +3,7 @@
namespace Drupal\Tests\block\Unit;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Tests\Core\Plugin\Fixtures\TestConfigurablePlugin;
use Drupal\Tests\UnitTestCase;
@@ -20,11 +21,11 @@ class BlockConfigEntityUnitTest extends UnitTestCase {
protected $entityType;
/**
* The entity manager used for testing.
* The entity type manager used for testing.
*
* @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
* @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $entityManager;
protected $entityTypeManager;
/**
* The ID of the type of the entity under test.
@@ -51,8 +52,8 @@ class BlockConfigEntityUnitTest extends UnitTestCase {
->method('getProvider')
->will($this->returnValue('block'));
$this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface');
$this->entityManager->expects($this->any())
$this->entityTypeManager = $this->getMock(EntityTypeManagerInterface::class);
$this->entityTypeManager->expects($this->any())
->method('getDefinition')
->with($this->entityTypeId)
->will($this->returnValue($this->entityType));
@@ -60,7 +61,7 @@ class BlockConfigEntityUnitTest extends UnitTestCase {
$this->uuid = $this->getMock('\Drupal\Component\Uuid\UuidInterface');
$container = new ContainerBuilder();
$container->set('entity.manager', $this->entityManager);
$container->set('entity_type.manager', $this->entityTypeManager);
$container->set('uuid', $this->uuid);
\Drupal::setContainer($container);
}
@@ -3,6 +3,8 @@
namespace Drupal\Tests\block\Unit;
use Drupal\block\BlockForm;
use Drupal\block\Entity\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Plugin\PluginFormFactoryInterface;
use Drupal\Tests\UnitTestCase;
@@ -82,6 +84,32 @@ class BlockFormTest extends UnitTestCase {
$this->pluginFormFactory = $this->prophesize(PluginFormFactoryInterface::class);
}
/**
* Mocks a block with a block plugin.
*
* @param string $machine_name
* The machine name of the block plugin.
*
* @return \Drupal\block\BlockInterface|\PHPUnit_Framework_MockObject_MockObject
* The mocked block.
*/
protected function getBlockMockWithMachineName($machine_name) {
$plugin = $this->getMockBuilder(BlockBase::class)
->disableOriginalConstructor()
->getMock();
$plugin->expects($this->any())
->method('getMachineNameSuggestion')
->will($this->returnValue($machine_name));
$block = $this->getMockBuilder(Block::class)
->disableOriginalConstructor()
->getMock();
$block->expects($this->any())
->method('getPlugin')
->will($this->returnValue($plugin));
return $block;
}
/**
* Tests the unique machine name generator.
*
@@ -49,6 +49,7 @@ class BlockPageVariantTest extends UnitTestCase {
$container = new Container();
$cache_context_manager = $this->getMockBuilder('Drupal\Core\Cache\CacheContextsManager')
->disableOriginalConstructor()
->setMethods(['assertValidTokens'])
->getMock();
$container->set('cache_contexts_manager', $cache_context_manager);
$cache_context_manager->expects($this->any())
@@ -209,9 +210,6 @@ class BlockPageVariantTest extends UnitTestCase {
$title_block_plugin = $this->getMock('Drupal\Core\Block\TitleBlockPluginInterface');
foreach ($blocks_config as $block_id => $block_config) {
$block = $this->getMock('Drupal\block\BlockInterface');
$block->expects($this->any())
->method('getContexts')
->willReturn([]);
$block->expects($this->atLeastOnce())
->method('getPlugin')
->willReturn($block_config[1] ? $main_content_block_plugin : ($block_config[2] ? $messages_block_plugin : ($block_config[3] ? $title_block_plugin : $block_plugin)));
@@ -10,8 +10,8 @@ dependencies:
- user
configure: entity.block_content.collection
# Information added by Drupal.org packaging script on 2018-01-03
version: '8.4.4'
# Information added by Drupal.org packaging script on 2018-03-07
version: '8.5.0'
core: '8.x'
project: 'drupal'
datestamp: 1515021228
datestamp: 1520457826
@@ -6,6 +6,23 @@
*/
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\StringTranslation\TranslatableMarkup;
/**
* Implements hook_update_dependencies().
*/
function block_content_update_dependencies() {
// The update function that adds the status field must run after
// content_translation_update_8400() which fixes NULL values for the
// 'content_translation_status' field.
if (\Drupal::moduleHandler()->moduleExists('content_translation')) {
$dependencies['block_content'][8400] = [
'content_translation' => 8400,
];
return $dependencies;
}
}
/**
* Add 'revision_translation_affected' field to 'block_content' entities.
@@ -70,5 +87,54 @@ function block_content_update_8300() {
$entity_type = $definition_update_manager->getEntityType('block_content');
$entity_type->set('revision_data_table', 'block_content_field_revision');
$definition_update_manager->updateEntityType($entity_type);
}
/**
* Add a publishing status field for block_content entities.
*/
function block_content_update_8400() {
$definition_update_manager = \Drupal::entityDefinitionUpdateManager();
// Add the published entity key to the block_content entity type.
$entity_type = $definition_update_manager->getEntityType('block_content');
$entity_keys = $entity_type->getKeys();
$entity_keys['published'] = 'status';
$entity_type->set('entity_keys', $entity_keys);
$definition_update_manager->updateEntityType($entity_type);
// Add the publishing status field to the block_content entity type.
$status = BaseFieldDefinition::create('boolean')
->setLabel(new TranslatableMarkup('Publishing status'))
->setDescription(new TranslatableMarkup('A boolean indicating the published state.'))
->setRevisionable(TRUE)
->setTranslatable(TRUE)
->setDefaultValue(TRUE);
$has_content_translation_status_field = \Drupal::moduleHandler()->moduleExists('content_translation') && $definition_update_manager->getFieldStorageDefinition('content_translation_status', 'block_content');
if ($has_content_translation_status_field) {
$status->setInitialValueFromField('content_translation_status');
}
else {
$status->setInitialValue(TRUE);
}
$definition_update_manager->installFieldStorageDefinition('status', 'block_content', 'block_content', $status);
// Uninstall the 'content_translation_status' field if needed.
$database = \Drupal::database();
if ($has_content_translation_status_field) {
// First we have to remove the field data.
$database->update($entity_type->getDataTable())
->fields(['content_translation_status' => NULL])
->execute();
// A site may have disabled revisionability for this entity type.
if ($entity_type->isRevisionable()) {
$database->update($entity_type->getRevisionDataTable())
->fields(['content_translation_status' => NULL])
->execute();
}
$content_translation_status = $definition_update_manager->getFieldStorageDefinition('content_translation_status', 'block_content');
$definition_update_manager->uninstallFieldStorageDefinition($content_translation_status);
}
}
@@ -445,7 +445,7 @@ display:
admin_label: ''
empty: true
tokenize: false
content: 'There are no custom blocks available. '
content: 'There are no custom blocks available.'
plugin_id: text_custom
block_content_listing_empty:
admin_label: ''
@@ -3,6 +3,7 @@ label: Block content body field configuration
migration_tags:
- Drupal 6
- Drupal 7
- Configuration
source:
plugin: embedded_data
data_rows:
@@ -3,6 +3,7 @@ label: Body field display configuration
migration_tags:
- Drupal 6
- Drupal 7
- Configuration
source:
plugin: embedded_data
data_rows:
@@ -3,6 +3,7 @@ label: Body field form display configuration
migration_tags:
- Drupal 6
- Drupal 7
- Configuration
source:
plugin: embedded_data
data_rows:
@@ -3,6 +3,7 @@ label: Block content type
migration_tags:
- Drupal 6
- Drupal 7
- Configuration
source:
plugin: embedded_data
data_rows:
@@ -1,7 +1,9 @@
id: d6_custom_block
label: Custom blocks
audit: true
migration_tags:
- Drupal 6
- Content
source:
plugin: d6_box
process:
@@ -1,7 +1,9 @@
id: d7_custom_block
label: Custom blocks
audit: true
migration_tags:
- Drupal 7
- Content
source:
plugin: d7_block_custom
process:
@@ -19,7 +19,8 @@ class BlockContentAccessControlHandler extends EntityAccessControlHandler {
*/
protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
if ($operation === 'view') {
return AccessResult::allowed();
return AccessResult::allowedIf($entity->isPublished())->addCacheableDependency($entity)
->orIf(AccessResult::allowedIfHasPermission($account, 'administer blocks'));
}
return parent::checkAccess($entity, $operation, $account);
}
@@ -8,6 +8,8 @@ use Drupal\Core\Form\FormStateInterface;
/**
* Form handler for the custom block edit forms.
*
* @internal
*/
class BlockContentForm extends ContentEntityForm {
@@ -4,12 +4,13 @@ namespace Drupal\block_content;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityChangedInterface;
use Drupal\Core\Entity\EntityPublishedInterface;
use Drupal\Core\Entity\RevisionLogInterface;
/**
* Provides an interface defining a custom block entity.
*/
interface BlockContentInterface extends ContentEntityInterface, EntityChangedInterface, RevisionLogInterface {
interface BlockContentInterface extends ContentEntityInterface, EntityChangedInterface, RevisionLogInterface, EntityPublishedInterface {
/**
* Returns the block revision log message.
@@ -4,7 +4,6 @@ namespace Drupal\block_content;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityListBuilder;
use Drupal\Core\Routing\RedirectDestinationTrait;
/**
* Defines a class to build a listing of custom block entities.
@@ -13,8 +12,6 @@ use Drupal\Core\Routing\RedirectDestinationTrait;
*/
class BlockContentListBuilder extends EntityListBuilder {
use RedirectDestinationTrait;
/**
* {@inheritdoc}
*/
@@ -31,15 +28,4 @@ class BlockContentListBuilder extends EntityListBuilder {
return $row + parent::buildRow($entity);
}
/**
* {@inheritdoc}
*/
public function getDefaultOperations(EntityInterface $entity) {
$operations = parent::getDefaultOperations($entity);
if (isset($operations['edit'])) {
$operations['edit']['query']['destination'] = $this->getRedirectDestination()->get();
}
return $operations;
}
}
@@ -8,7 +8,9 @@ use Drupal\Core\Form\FormStateInterface;
use Drupal\language\Entity\ContentLanguageSettings;
/**
* Base form for category edit forms.
* The block content type entity form.
*
* @internal
*/
class BlockContentTypeForm extends BundleEntityFormBase {
@@ -2,7 +2,6 @@
namespace Drupal\block_content;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityViewBuilder;
@@ -41,18 +40,4 @@ class BlockContentViewBuilder extends EntityViewBuilder {
return $build;
}
/**
* {@inheritdoc}
*/
protected function alterBuild(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
parent::alterBuild($build, $entity, $display, $view_mode);
// Add contextual links for this custom block.
if (!$entity->isNew()) {
$build['#contextual_links']['block_content'] = [
'route_parameters' => ['block_content' => $entity->id()],
'metadata' => ['changed' => $entity->getChangedTime()],
];
}
}
}
@@ -2,8 +2,7 @@
namespace Drupal\block_content\Entity;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityChangedTrait;
use Drupal\Core\Entity\EditorialContentEntityBase;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
@@ -51,7 +50,8 @@ use Drupal\user\UserInterface;
* "bundle" = "type",
* "label" = "info",
* "langcode" = "langcode",
* "uuid" = "uuid"
* "uuid" = "uuid",
* "published" = "status",
* },
* revision_metadata_keys = {
* "revision_user" = "revision_user",
@@ -68,9 +68,7 @@ use Drupal\user\UserInterface;
* caching.
* See https://www.drupal.org/node/2284917#comment-9132521 for more information.
*/
class BlockContent extends ContentEntityBase implements BlockContentInterface {
use EntityChangedTrait;
class BlockContent extends EditorialContentEntityBase implements BlockContentInterface {
/**
* The theme the block is being created in.
@@ -174,6 +172,8 @@ class BlockContent extends ContentEntityBase implements BlockContentInterface {
$fields['type']->setLabel(t('Block type'))
->setDescription(t('The block type.'));
$fields['revision_log']->setDescription(t('The log entry explaining the changes in this revision.'));
$fields['info'] = BaseFieldDefinition::create('string')
->setLabel(t('Block description'))
->setDescription(t('A brief description of your block.'))
@@ -187,35 +187,12 @@ class BlockContent extends ContentEntityBase implements BlockContentInterface {
->setDisplayConfigurable('form', TRUE)
->addConstraint('UniqueField', []);
$fields['revision_log'] = BaseFieldDefinition::create('string_long')
->setLabel(t('Revision log message'))
->setDescription(t('The log entry explaining the changes in this revision.'))
->setRevisionable(TRUE)
->setDisplayOptions('form', [
'type' => 'string_textarea',
'weight' => 25,
'settings' => [
'rows' => 4,
],
]);
$fields['changed'] = BaseFieldDefinition::create('changed')
->setLabel(t('Changed'))
->setDescription(t('The time that the custom block was last edited.'))
->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);
return $fields;
}
@@ -7,6 +7,8 @@ use Drupal\Core\Form\FormStateInterface;
/**
* Provides a confirmation form for deleting a custom block entity.
*
* @internal
*/
class BlockContentDeleteForm extends ContentEntityDeleteForm {
@@ -7,6 +7,8 @@ use Drupal\Core\Form\FormStateInterface;
/**
* Provides a confirmation form for deleting a custom block type entity.
*
* @internal
*/
class BlockContentTypeDeleteForm extends EntityDeleteForm {
@@ -7,8 +7,8 @@ package: Testing
dependencies:
- block_content
# Information added by Drupal.org packaging script on 2018-01-03
version: '8.4.4'
# Information added by Drupal.org packaging script on 2018-03-07
version: '8.5.0'
core: '8.x'
project: 'drupal'
datestamp: 1515021228
datestamp: 1520457826
@@ -8,8 +8,8 @@ dependencies:
- block_content
- views
# Information added by Drupal.org packaging script on 2018-01-03
version: '8.4.4'
# Information added by Drupal.org packaging script on 2018-03-07
version: '8.5.0'
core: '8.x'
project: 'drupal'
datestamp: 1515021228
datestamp: 1520457826
@@ -0,0 +1,37 @@
<?php
namespace Drupal\Tests\block_content\Functional;
/**
* Tests views contextual links on block content.
*
* @group block_content
*/
class BlockContentContextualLinksTest extends BlockContentTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'contextual',
];
/**
* Tests contextual links.
*/
public function testBlockContentContextualLinks() {
$block_content = $this->createBlockContent();
$block = $this->placeBlock('block_content:' . $block_content->uuid());
$user = $this->drupalCreateUser([
'administer blocks',
'access contextual links',
]);
$this->drupalLogin($user);
$this->drupalGet('<front>');
$this->assertSession()->elementAttributeContains('css', 'div[data-contextual-id]', 'data-contextual-id', 'block:block=' . $block->id() . ':langcode=en|block_content:block_content=' . $block_content->id() . ':');
}
}
@@ -63,7 +63,9 @@ class BlockContentCreationTest extends BlockContentTestBase {
$this->assertNoFieldByXPath('//select[@name="settings[view_mode]"]', NULL, 'View mode setting hidden because only one exists');
// Check that the block exists in the database.
$blocks = entity_load_multiple_by_properties('block_content', ['info' => $edit['info[0][value]']]);
$blocks = \Drupal::entityTypeManager()
->getStorage('block_content')
->loadByProperties(['info' => $edit['info[0][value]']]);
$block = reset($blocks);
$this->assertTrue($block, 'Custom Block found in database.');
@@ -143,7 +145,9 @@ class BlockContentCreationTest extends BlockContentTestBase {
$this->assertFieldByXPath('//select[@name="settings[view_mode]"]/option[@selected="selected"]', 'test_view_mode', 'View mode changed to Test View Mode');
// Check that the block exists in the database.
$blocks = entity_load_multiple_by_properties('block_content', ['info' => $edit['info[0][value]']]);
$blocks = \Drupal::entityTypeManager()
->getStorage('block_content')
->loadByProperties(['info' => $edit['info[0][value]']]);
$block = reset($blocks);
$this->assertTrue($block, 'Custom Block found in database.');
@@ -178,7 +182,9 @@ class BlockContentCreationTest extends BlockContentTestBase {
]), 'Basic block created.');
// Check that the block exists in the database.
$blocks = entity_load_multiple_by_properties('block_content', ['info' => $edit['info[0][value]']]);
$blocks = \Drupal::entityTypeManager()
->getStorage('block_content')
->loadByProperties(['info' => $edit['info[0][value]']]);
$block = reset($blocks);
$this->assertTrue($block, 'Default Custom Block found in database.');
}
@@ -0,0 +1,47 @@
<?php
namespace Drupal\Tests\block_content\Functional;
use Drupal\block_content\Entity\BlockContent;
use Drupal\simpletest\BlockCreationTrait;
use Drupal\Tests\BrowserTestBase;
/**
* Tests unpublishing of block_content entities.
*
* @group block_content
*/
class UnpublishedBlockTest extends BrowserTestBase {
use BlockCreationTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['block_content'];
/**
* Tests unpublishing of block_content entities.
*/
public function testViewShowsCorrectStates() {
$block_content = BlockContent::create([
'info' => 'Test block',
'type' => 'basic',
]);
$block_content->save();
$this->placeBlock('block_content:' . $block_content->uuid());
$this->drupalGet('<front>');
$page = $this->getSession()->getPage();
$this->assertTrue($page->has('css', '.block-block-content' . $block_content->uuid()));
$block_content->setPublished(FALSE);
$block_content->save();
$this->drupalGet('<front>');
$page = $this->getSession()->getPage();
$this->assertFalse($page->has('css', '.block-block-content' . $block_content->uuid()));
}
}
@@ -43,4 +43,27 @@ class BlockContentUpdateTest extends UpdatePathTestBase {
$this->assertEqual('block_content_field_revision', $entity_type->getRevisionDataTable());
}
/**
* Tests adding a status field to the block content entity type.
*
* @see block_content_update_8400()
*/
public function testStatusFieldAddition() {
$schema = \Drupal::database()->schema();
$entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager();
// Run updates.
$this->runUpdates();
// Check that the field exists and has the correct label.
$updated_field = $entity_definition_update_manager->getFieldStorageDefinition('status', 'block_content');
$this->assertEqual('Publishing status', $updated_field->getLabel());
$content_translation_status = $entity_definition_update_manager->getFieldStorageDefinition('content_translation_status', 'block_content');
$this->assertNull($content_translation_status);
$this->assertFalse($schema->fieldExists('block_content_field_revision', 'content_translation_status'));
$this->assertFalse($schema->fieldExists('block_content_field_data', 'content_translation_status'));
}
}
@@ -8,8 +8,8 @@ hidden: true
dependencies:
- block
# Information added by Drupal.org packaging script on 2018-01-03
version: '8.4.4'
# Information added by Drupal.org packaging script on 2018-03-07
version: '8.5.0'
core: '8.x'
project: 'drupal'
datestamp: 1515021228
datestamp: 1520457826
+4 -4
View File
@@ -4,8 +4,8 @@
*/
.block-place-region {
outline: 1px dashed rgba(0,0,0,0.5);
box-shadow: 0 0 0 1px rgba(255,255,255,0.7);
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;
@@ -14,8 +14,8 @@
.block-place-region a.button {
position: relative;
background: url(../../../misc/icons/bebebe/plus.svg) #ffffff center center / 16px 16px no-repeat;
border: 1px solid #cccccc;
background: url(../../../misc/icons/bebebe/plus.svg) #fff center center / 16px 16px no-repeat;
border: 1px solid #ccc;
box-sizing: border-box;
font-size: 1rem;
padding: 0;

Some files were not shown because too many files have changed in this diff Show More