diff --git a/core/modules/block/lib/Drupal/block/Plugin/views/display/Block.php b/core/modules/block/lib/Drupal/block/Plugin/views/display/Block.php index 121d2de..047d402 100644 --- a/core/modules/block/lib/Drupal/block/Plugin/views/display/Block.php +++ b/core/modules/block/lib/Drupal/block/Plugin/views/display/Block.php @@ -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,10 +47,35 @@ 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' => TRUE), + 'more_link' => array('default' => TRUE), + ), + ); + 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'; + $settings['more_link'] = 'none'; + return $settings; + } + + /** * The display block handler returns the structure necessary for a block. */ public function execute() { @@ -87,6 +116,15 @@ public function optionsSummary(&$categories, &$options) { 'value' => views_ui_truncate($block_description, 24), ); + $allow = $this->getOption('allow'); + $filtered_allow = array_filter($allow); + + $options['allow'] = array( + 'category' => 'block', + 'title' => t('Allow settings'), + 'value' => empty($filtered_allow) ? t('None') : ($allow === $filtered_allow ? t('All') : t('Some')), + ); + $types = $this->blockCachingModes(); $options['block_caching'] = array( 'category' => 'other', @@ -155,6 +193,22 @@ public function buildOptionsForm(&$form, &$form_state) { '#markup' => '
' . t('Exposed filters in block displays require "Use AJAX" to be set to work correctly.') . '
', ); } + break; + case 'allow': + $form['#title'] .= t('Allow settings in the block configuration'); + + $options = array( + 'items_per_page' => t('Items per page'), + 'more_link' => t('More link'), + ); + + $allow = array_filter($this->getOption('allow')); + $form['allow'] = array( + '#type' => 'checkboxes', + '#default_value' => $allow, + '#options' => $options, + ); + break; } } @@ -166,15 +220,141 @@ 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 and + * more_link settings. + * + * @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')); + + if (!empty($allow_settings)) { + $form['override'] = array( + '#type' => 'details', + '#title' => t('Override settings'), + '#open' => TRUE, + '#weight' => 8, + ); + } + + $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; + case 'more_link': + $form['override']['more_link'] = array( + '#type' => 'select', + '#title' => t('More link'), + '#options' => array( + 'none' => t('Use default settings'), + 0 => t('No more link'), + 1 => t('Display more link'), + ), + '#default_value' => $block_configuration['more_link'], + ); + 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']); + } + if (isset($form_state['values']['override']['more_link'])) { + $block->setConfig('more_link', $form_state['values']['override']['more_link']); + } + } + + /** + * 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']); + } + + if ($config['more_link'] !== 'none') { + if ((int) $config['more_link'] == 0) { + $this->setOption('use_more', FALSE); + } + elseif ((int) $config['more_link'] == 1) { + $this->setOption('use_more', TRUE); + } + } + } + + /** * Block views use exposed widgets only if AJAX is set. */ public function usesExposed() { diff --git a/core/modules/block/lib/Drupal/block/Tests/Views/DisplayBlockTest.php b/core/modules/block/lib/Drupal/block/Tests/Views/DisplayBlockTest.php index ec3d500..b51354c 100644 --- a/core/modules/block/lib/Drupal/block/Tests/Views/DisplayBlockTest.php +++ b/core/modules/block/lib/Drupal/block/Tests/Views/DisplayBlockTest.php @@ -127,6 +127,29 @@ public function testViewsBlockForm() { // expected machine name. $this->assertTrue(!empty($block), 'The expected block was loaded.'); } + + // Tests the override capability of items per page and the more link. + $this->drupalGet('admin/structure/block/add/views_block:test_view_block-block_1/' . $default_theme); + $edit = array(); + $edit['settings[override][items_per_page]'] = 10; + $edit['settings[override][more_link]'] = 1; + + $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."); + $this->assertEqual(1, $config['more_link'], "'More links' are properly saved."); + + $edit['settings[override][items_per_page]'] = 5; + $edit['settings[override][more_link]'] = 0; + $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."); + $this->assertEqual(0, $config['more_link'], "'More links' are properly saved."); } /** diff --git a/core/modules/block/tests/lib/Drupal/block/Tests/Plugin/views/display/BlockTest.php b/core/modules/block/tests/lib/Drupal/block/Tests/Plugin/views/display/BlockTest.php new file mode 100644 index 0000000..837308d --- /dev/null +++ b/core/modules/block/tests/lib/Drupal/block/Tests/Plugin/views/display/BlockTest.php @@ -0,0 +1,108 @@ + ' Views: Display Block', + 'description' => 'Tests the block display 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->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(); + } + + /** + * Test 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', 'more_link' => 'none'))); + + $this->blockDisplay->preBlockBuild($this->blockPlugin); + + $this->assertFalse((bool) $this->blockDisplay->getOption('use_more')); + } + + /** + * Tests the build method with overriding items per page and the more link. + */ + 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, 'more_link' => 1))); + + $this->blockDisplay->preBlockBuild($this->blockPlugin); + + $this->assertTrue((bool) $this->blockDisplay->getOption('use_more')); + } + +} diff --git a/core/modules/views/lib/Drupal/views/Plugin/Block/ViewsBlock.php b/core/modules/views/lib/Drupal/views/Plugin/Block/ViewsBlock.php index 8a0c919..c3fb441 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/Block/ViewsBlock.php +++ b/core/modules/views/lib/Drupal/views/Plugin/Block/ViewsBlock.php @@ -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,39 @@ class ViewsBlock extends BlockBase { protected $displayID; /** - * Overrides \Drupal\Component\Plugin\PluginBase::__construct(). + * 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) { - parent::__construct($configuration, $plugin_id, $plugin_definition); - + 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); + + 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 +94,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 +102,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()); @@ -87,6 +117,41 @@ public function build() { } /** + * {@inheritdoc} + */ + public function settings() { + $settings = array(); + if ($this->view->setDisplay($this->displayID)) { + return $this->view->display_handler->blockSettings($settings); + } + return $settings; + } + + /** + * {@inheritdoc} + */ + public function blockForm($form, &$form_state) { + $this->view->setDisplay($this->displayID); + return $this->view->display_handler->blockForm($this, $form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function blockValidate($form, &$form_state) { + $this->view->setDisplay($this->displayID); + $this->view->display_handler->blockValidate($this, $form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function blockSubmit($form, &$form_state) { + $this->view->setDisplay($this->displayID); + $this->view->display_handler->blockSubmit($this, $form, $form_state); + } + + /** * Converts Views block content to a renderable array with contextual links. * * @param string|array $output diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php index b0cbb7b..974f001 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php @@ -32,7 +32,7 @@ /** * The top object of a view. * - * @var Drupal\views\ViewExecutable + * @var \Drupal\views\ViewExecutable */ var $view = NULL; @@ -523,7 +523,7 @@ protected function defineOptions() { 'bool' => TRUE, ), 'use_more_always' => array( - 'default' => FALSE, + 'default' => TRUE, 'bool' => TRUE, ), 'use_more_text' => array( diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/ViewsBlockTest.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/ViewsBlockTest.php index 759351a..b427262 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Plugin/ViewsBlockTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/ViewsBlockTest.php @@ -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'); diff --git a/core/modules/views/tests/Drupal/views/Tests/Plugin/Block/ViewsBlockTest.php b/core/modules/views/tests/Drupal/views/Tests/Plugin/Block/ViewsBlockTest.php new file mode 100644 index 0000000..5257f67 --- /dev/null +++ b/core/modules/views/tests/Drupal/views/Tests/Plugin/Block/ViewsBlockTest.php @@ -0,0 +1,168 @@ + ' 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)); + } + + /** + * Test 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)); + + $block_id = 'views_block:test_view-block_1'; + $config = array(); + $definition = array(); + $definition['module'] = 'views'; + $plugin = new ViewsBlock($config, $block_id, $definition, $this->executableFactory, $this->storageController); + + $this->assertEquals($build, $plugin->build()); + } + + /** + * Test the build method with a failed execution. + * + * @see \Drupal\views\Plugin\block\ViewsBlock::build() + */ + public function testBuildFailed() { + $output = FALSE; + $this->executable->expects($this->once()) + ->method('executeDisplay') + ->with($this->equalTo('block_1')) + ->will($this->returnValue($output)); + + $block_id = 'views_block:test_view-block_1'; + $config = array(); + $definition = array(); + $definition['module'] = 'views'; + $plugin = new ViewsBlock($config, $block_id, $definition, $this->executableFactory, $this->storageController); + + $this->assertEquals(array(), $plugin->build()); + } + +} + +} + +// @todo Remove this once https://drupal.org/node/2018411 is in. +namespace { + if (!function_exists('t')) { + function t($string) { + return $string; + } + } + // @todo replace views_add_contextual_links() + if (!function_exists('views_add_contextual_links')) { + function views_add_contextual_links() { + } + } +}