Commit 9dd98eaa authored by alexpott's avatar alexpott

Issue #1957346 by dawehner, olli, damiankloip, jibran, ParisLiakos: Add some...

Issue #1957346 by dawehner, olli, damiankloip, jibran, ParisLiakos: Add some settings on the block display to allow overrides on the block instance configuration.
parent 7ec526e9
......@@ -10,6 +10,7 @@
use Drupal\Component\Annotation\Plugin;
use Drupal\Core\Annotation\Translation;
use Drupal\views\Plugin\Block\ViewsBlock;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
/**
......@@ -27,6 +28,9 @@
* contextual_links_locations = {"block"},
* admin = @Translation("Block")
* )
*
* @see \Drupal\views\Plugin\block\block\ViewsBlock
* @see \Drupal\views\Plugin\Derivative\ViewsBlock
*/
class Block extends DisplayPluginBase {
......@@ -43,9 +47,32 @@ protected function defineOptions() {
$options['block_description'] = array('default' => '', 'translatable' => TRUE);
$options['block_caching'] = array('default' => DRUPAL_NO_CACHE);
$options['allow'] = array(
'contains' => array(
'items_per_page' => array('default' => 'items_per_page'),
),
);
return $options;
}
/**
* Returns plugin-specific settings for the block.
*
* @param array $settings
* The settings of the block.
*
* @return array
* An array of block-specific settings to override the defaults provided in
* \Drupal\views\Plugin\Block\ViewsBlock::settings().
*
* @see \Drupal\views\Plugin\Block\ViewsBlock::settings().
*/
public function blockSettings(array $settings) {
$settings['items_per_page'] = 'none';
return $settings;
}
/**
* The display block handler returns the structure necessary for a block.
*/
......@@ -87,6 +114,14 @@ public function optionsSummary(&$categories, &$options) {
'value' => views_ui_truncate($block_description, 24),
);
$filtered_allow = array_filter($this->getOption('allow'));
$options['allow'] = array(
'category' => 'block',
'title' => t('Allow settings'),
'value' => empty($filtered_allow) ? t('None') : t('Items per page'),
);
$types = $this->blockCachingModes();
$options['block_caching'] = array(
'category' => 'other',
......@@ -155,6 +190,21 @@ public function buildOptionsForm(&$form, &$form_state) {
'#markup' => '<div class="messages messages--warning">' . t('Exposed filters in block displays require "Use AJAX" to be set to work correctly.') . '</div>',
);
}
break;
case 'allow':
$form['#title'] .= t('Allow settings in the block configuration');
$options = array(
'items_per_page' => t('Items per page'),
);
$allow = array_filter($this->getOption('allow'));
$form['allow'] = array(
'#type' => 'checkboxes',
'#default_value' => $allow,
'#options' => $options,
);
break;
}
}
......@@ -166,14 +216,106 @@ public function submitOptionsForm(&$form, &$form_state) {
parent::submitOptionsForm($form, $form_state);
switch ($form_state['section']) {
case 'block_description':
$this->setOption('block_description', $form_state['values']['block_description']);
break;
case 'block_caching':
$this->setOption('block_caching', $form_state['values']['block_caching']);
case 'allow':
$this->setOption($form_state['section'], $form_state['values'][$form_state['section']]);
break;
}
}
/**
* Adds the configuration form elements specific to this views block plugin.
*
* This method allows block instances to override the views items_per_page.
*
* @param \Drupal\views\Plugin\Block\ViewsBlock $block
* The ViewsBlock plugin.
* @param array $form
* The form definition array for the block configuration form.
* @param array $form_state
* An array containing the current state of the configuration form.
*
* @return array $form
* The renderable form array representing the entire configuration form.
*
* @see \Drupal\views\Plugin\Block\ViewsBlock::blockForm()
*/
public function blockForm(ViewsBlock $block, array &$form, array &$form_state) {
$allow_settings = array_filter($this->getOption('allow'));
$block_configuration = $block->getConfig();
foreach ($allow_settings as $type => $enabled) {
if (empty($enabled)) {
continue;
}
switch ($type) {
case 'items_per_page':
$form['override']['items_per_page'] = array(
'#type' => 'select',
'#title' => t('Items per page'),
'#options' => array(
'none' => t('Use default settings'),
5 => 5,
10 => 10,
20 => 20,
40 => 40,
),
'#default_value' => $block_configuration['items_per_page'],
);
break;
}
}
return $form;
}
/**
* Handles form validation for the views block configuration form.
*
* @param \Drupal\views\Plugin\Block\ViewsBlock $block
* The ViewsBlock plugin.
* @param array $form
* The form definition array for the block configuration form.
* @param array $form_state
* An array containing the current state of the configuration form.
*
* @see \Drupal\views\Plugin\Block\ViewsBlock::blockValidate()
*/
public function blockValidate(ViewsBlock $block, array $form, array &$form_state) {
}
/**
* Handles form submission for the views block configuration form.
*
* @param \Drupal\views\Plugin\Block\ViewsBlock $block
* The ViewsBlock plugin.
* @param array $form
* The form definition array for the full block configuration form.
* @param array $form_state
* An array containing the current state of the configuration form.
*
* * @see \Drupal\views\Plugin\Block\ViewsBlock::blockSubmit()
*/
public function blockSubmit(ViewsBlock $block, $form, &$form_state) {
if (isset($form_state['values']['override']['items_per_page'])) {
$block->setConfig('items_per_page', $form_state['values']['override']['items_per_page']);
}
}
/**
* Allows to change the display settings right before executing the block.
*
* @param \Drupal\views\Plugin\Block\ViewsBlock $block
* The block plugin for views displays.
*/
public function preBlockBuild(ViewsBlock $block) {
$config = $block->getConfig();
if ($config['items_per_page'] !== 'none') {
$this->view->setItemsPerPage($config['items_per_page']);
}
}
/**
* Block views use exposed widgets only if AJAX is set.
*/
......
......@@ -23,7 +23,7 @@ public static function getInfo() {
}
/**
* Test block visibility.
* Tests block visibility.
*/
function testBlockVisibility() {
$block_name = 'system_powered_by_block';
......
......@@ -127,6 +127,25 @@ public function testViewsBlockForm() {
// expected machine name.
$this->assertTrue(!empty($block), 'The expected block was loaded.');
}
// Tests the override capability of items per page.
$this->drupalGet('admin/structure/block/add/views_block:test_view_block-block_1/' . $default_theme);
$edit = array();
$edit['settings[override][items_per_page]'] = 10;
$this->drupalPost('admin/structure/block/add/views_block:test_view_block-block_1/' . $default_theme, $edit, t('Save block'));
$block = $storage->load('stark.views_block__test_view_block_block_1_4');
$config = $block->getPlugin()->getConfig();
$this->assertEqual(10, $config['items_per_page'], "'Items per page' is properly saved.");
$edit['settings[override][items_per_page]'] = 5;
$this->drupalPost('admin/structure/block/manage/stark.views_block__test_view_block_block_1_4', $edit, t('Save block'));
$block = $storage->load('stark.views_block__test_view_block_block_1_4');
$config = $block->getPlugin()->getConfig();
$this->assertEqual(5, $config['items_per_page'], "'Items per page' is properly saved.");
}
/**
......
<?php
/**
* @file
* Contains \Drupal\block\Tests\Plugin\views\display\BlockTest.
*/
namespace Drupal\block\Tests\Plugin\views\display;
use Drupal\Tests\UnitTestCase;
/**
* Tests the block display plugin.
*
* @see \Drupal\block\Plugin\views\display\Block
*/
class BlockTest extends UnitTestCase {
/**
* The view executable.
*
* @var \Drupal\views\ViewExecutable|\PHPUnit_Framework_MockObject_MockObject
*/
protected $executable;
/**
* The views block plugin.
*
* @var \Drupal\views\Plugin\Block\ViewsBlock|\PHPUnit_Framework_MockObject_MockObject
*/
protected $blockPlugin;
/**
* The tested block display plugin.
*
* @var \Drupal\block\Plugin\views\display\Block|\PHPUnit_Framework_MockObject_MockObject
*/
protected $blockDisplay;
public static function getInfo() {
return array(
'name' => ' Views: Display Block',
'description' => 'Tests the block display plugin.',
'group' => 'Views module integration',
);
}
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->executable = $this->getMockBuilder('Drupal\views\ViewExecutable')
->disableOriginalConstructor()
->setMethods(array('executeDisplay', 'setDisplay', 'setItemsPerPage'))
->getMock();
$this->executable->expects($this->any())
->method('setDisplay')
->with('block_1')
->will($this->returnValue(TRUE));
$this->blockDisplay = $this->executable->display_handler = $this->getMockBuilder('Drupal\block\Plugin\views\display\Block')
->disableOriginalConstructor()
->setMethods(NULL)
->getMock();
$this->blockDisplay->view = $this->executable;
$this->blockPlugin = $this->getMockBuilder('Drupal\views\Plugin\Block\ViewsBlock')
->disableOriginalConstructor()
->getMock();
}
/**
* Tests the build method with no overriding.
*/
public function testBuildNoOverride() {
$this->executable->expects($this->never())
->method('setItemsPerPage');
$this->blockPlugin->expects($this->once())
->method('getConfig')
->will($this->returnValue(array('items_per_page' => 'none')));
$this->blockDisplay->preBlockBuild($this->blockPlugin);
}
/**
* Tests the build method with overriding items per page.
*/
public function testBuildOverride() {
$this->executable->expects($this->once())
->method('setItemsPerPage')
->with(5);
$this->blockPlugin->expects($this->once())
->method('getConfig')
->will($this->returnValue(array('items_per_page' => 5)));
$this->blockDisplay->preBlockBuild($this->blockPlugin);
}
}
......@@ -12,6 +12,9 @@
use Drupal\Core\Annotation\Translation;
use Drupal\Core\Entity\EntityStorageControllerInterface;
use Drupal\Component\Utility\Xss;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\views\ViewExecutableFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a generic Views block.
......@@ -23,7 +26,7 @@
* derivative = "Drupal\views\Plugin\Derivative\ViewsBlock"
* )
*/
class ViewsBlock extends BlockBase {
class ViewsBlock extends BlockBase implements ContainerFactoryPluginInterface {
/**
* The View executable object.
......@@ -40,15 +43,47 @@ class ViewsBlock extends BlockBase {
protected $displayID;
/**
* Overrides \Drupal\Component\Plugin\PluginBase::__construct().
* Indicates whether the display was successfully set.
*
* @var bool
*/
public function __construct(array $configuration, $plugin_id, array $plugin_definition) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
protected $displaySet;
/**
* Constructs a Drupal\Component\Plugin\PluginBase 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\views\ViewExecutableFactory $executable_factory
* The view executable factory.
* @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage_controller
* The views storage controller.
*/
public function __construct(array $configuration, $plugin_id, array $plugin_definition, ViewExecutableFactory $executable_factory, EntityStorageControllerInterface $storage_controller) {
$this->pluginId = $plugin_id;
list($plugin, $delta) = explode(':', $this->getPluginId());
list($name, $this->displayID) = explode('-', $delta, 2);
// Load the view.
$this->view = views_get_view($name);
$view = $storage_controller->load($name);
$this->view = $executable_factory->get($view);
$this->displaySet = $this->view->setDisplay($this->displayID);
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) {
return new static(
$configuration, $plugin_id, $plugin_definition,
$container->get('views.executable'),
$container->get('plugin.manager.entity')->getStorageController('view')
);
}
/**
......@@ -67,6 +102,7 @@ public function form($form, &$form_state) {
// Set the default label to '' so the views internal title is used.
$form['label']['#default_value'] = '';
$form['label']['#access'] = FALSE;
return $form;
}
......@@ -74,6 +110,8 @@ public function form($form, &$form_state) {
* {@inheritdoc}
*/
public function build() {
$this->view->display_handler->preBlockBuild($this);
if ($output = $this->view->executeDisplay($this->displayID)) {
// Set the label to the title configured in the view.
$this->configuration['label'] = Xss::filterAdmin($this->view->getTitle());
......@@ -86,6 +124,46 @@ public function build() {
return array();
}
/**
* {@inheritdoc}
*/
public function settings() {
$settings = array();
if ($this->displaySet) {
return $this->view->display_handler->blockSettings($settings);
}
return $settings;
}
/**
* {@inheritdoc}
*/
public function blockForm($form, &$form_state) {
if ($this->displaySet) {
return $this->view->display_handler->blockForm($this, $form, $form_state);
}
}
/**
* {@inheritdoc}
*/
public function blockValidate($form, &$form_state) {
if ($this->displaySet) {
$this->view->display_handler->blockValidate($this, $form, $form_state);
}
}
/**
* {@inheritdoc}
*/
public function blockSubmit($form, &$form_state) {
if ($this->displaySet) {
$this->view->display_handler->blockSubmit($this, $form, $form_state);
}
}
/**
* Converts Views block content to a renderable array with contextual links.
*
......@@ -121,7 +199,6 @@ protected function addContextualLinks(&$output, $block_type = 'block') {
* The new block instance ID.
*/
public function generateBlockInstanceID(EntityStorageControllerInterface $manager) {
$this->view->setDisplay($this->displayID);
$original_id = 'views_block__' . $this->view->storage->id() . '_' . $this->view->current_display;
// Get an array of block IDs without the theme prefix.
......
......@@ -32,7 +32,7 @@ abstract class DisplayPluginBase extends PluginBase {
/**
* The top object of a view.
*
* @var Drupal\views\ViewExecutable
* @var \Drupal\views\ViewExecutable
*/
var $view = NULL;
......
......@@ -59,7 +59,7 @@ public function testGenerateBlockInstanceID() {
'module' => 'views',
);
$plugin_id = 'views_block:test_view_block-block_1';
$views_block = new ViewsBlock(array(), $plugin_id, $plugin_definition);
$views_block = ViewsBlock::create($this->container, array(), $plugin_id, $plugin_definition);
$storage_controller = $this->container->get('plugin.manager.entity')->getStorageController('block');
......
<?php
/**
* @file
* Contains \Drupal\views\Tests\Plugin\Block\ViewsBlockTest.
*/
namespace Drupal\views\Tests\Plugin\Block {
use Drupal\Tests\UnitTestCase;
use Drupal\views\Plugin\Block\ViewsBlock;
use Drupal\block\Plugin\views\display\Block;
// @todo Remove this once the constant got converted.
if (!defined('DRUPAL_CORE_COMPATIBILITY')) {
define('DRUPAL_CORE_COMPATIBILITY', '8.x');
}
if (!defined('BLOCK_LABEL_VISIBLE')) {
define('BLOCK_LABEL_VISIBLE', 'visible');
}
if (!defined('DRUPAL_NO_CACHE')) {
define('DRUPAL_NO_CACHE', -1);
}
/**
* Tests the views block plugin.
*
* @see \Drupal\views\Plugin\block\ViewsBlock
*/
class ViewsBlockTest extends UnitTestCase {
/**
* The view executable.
*
* @var \Drupal\views\ViewExecutable|\PHPUnit_Framework_MockObject_MockObject
*/
protected $executable;
/**
* The view executable factory.
*
* @var \Drupal\views\ViewExecutableFactory|\PHPUnit_Framework_MockObject_MockObject
*/
protected $executableFactory;
/**
* The view entity.
*
* @var \Drupal\views\ViewStorageInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $view;
/**
* The view storage controller.
*
* @var \Drupal\Core\Entity\EntityStorageControllerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $storageController;
public static function getInfo() {
return array(
'name' => ' Block: Views block',
'description' => 'Tests the views block plugin.',
'group' => 'Views module integration',
);
}
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp(); // TODO: Change the autogenerated stub
$this->executable = $this->getMockBuilder('Drupal\views\ViewExecutable')
->disableOriginalConstructor()
->setMethods(array('executeDisplay', 'setDisplay', 'setItemsPerPage'))
->getMock();
$this->executable->expects($this->any())
->method('setDisplay')
->with('block_1')
->will($this->returnValue(TRUE));
$this->executable->display_handler = $this->getMockBuilder('Drupal\block\Plugin\views\display\Block')
->disableOriginalConstructor()
->setMethods(NULL)
->getMock();
$this->view = $this->getMockBuilder('Drupal\views\Plugin\Core\Entity\View')
->disableOriginalConstructor()
->getMock();
$this->executableFactory = $this->getMockBuilder('Drupal\views\ViewExecutableFactory')
->getMock();
$this->executableFactory->staticExpects($this->any())
->method('get')
->with($this->view)
->will($this->returnValue($this->executable));
$this->storageController = $this->getMockBuilder('Drupal\views\ViewStorageController')
->disableOriginalConstructor()
->getMock();
$this->storageController->expects($this->any())
->method('load')
->with('test_view')
->will($this->returnValue($this->view));
}
/**
* Tests the build method.
*
* @see \Drupal\views\Plugin\block\ViewsBlock::build()
*/
public function testBuild() {
$output = $this->randomName(100);
$build = array('#markup' => $output);
$this->executable->expects($this->once())
->method('executeDisplay')
->with($this->equalTo('block_1'))
->will($this->returnValue($build));