Commit 6e7b1219 authored by Dries's avatar Dries

Issue #1535868 by EclipseGc, tim.plunkett, xjm, Jody Lynn, sdboyer, naxoc,...

Issue #1535868 by EclipseGc, tim.plunkett, xjm, Jody Lynn, sdboyer, naxoc, tizzo, effulgentsia, dawehner, disasm, beejeebus: Convert all blocks into plugins.
parent a6c393ef
......@@ -65,6 +65,11 @@ public function getDefinitions() {
protected function getDerivatives(array $base_plugin_definitions) {
$plugin_definitions = array();
foreach ($base_plugin_definitions as $base_plugin_id => $plugin_definition) {
// @todo Remove this check once http://drupal.org/node/1780396 is resolved.
if (isset($plugin_definition['module']) && !module_exists($plugin_definition['module'])) {
continue;
}
$derivative_fetcher = $this->getDerivativeFetcher($base_plugin_id, $plugin_definition);
if ($derivative_fetcher) {
$derivative_definitions = $derivative_fetcher->getDerivativeDefinitions($plugin_definition);
......
......@@ -24,9 +24,10 @@ interface MapperInterface {
* An array of options that can be used to determine a suitable plugin to
* instantiate and how to configure it.
*
* @return object
* @return object|false
* A fully configured plugin instance. The interface of the plugin instance
* will depends on the plugin type.
* will depends on the plugin type. If no instance can be retrieved, FALSE
* will be returned.
*/
public function getInstance(array $options);
......
<?php
/**
* @file
* Contains \Drupal\Core\Plugin\Mapper\ConfigMapper.
*/
namespace Drupal\Core\Plugin\Mapper;
use Drupal\Component\Plugin\Mapper\MapperInterface;
use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Component\Plugin\Exception\PluginException;
/**
* Retrieves plugin instances from the configuration system.
*/
class ConfigMapper implements MapperInterface {
/**
* The plugin manager instance used by this mapper.
*
* @var \Drupal\Component\Plugin\PluginManagerInterface
*/
protected $manager;
/**
* Constructs a \Drupal\Core\Plugin\Mapper\ConfigMapper object.
*
* @param \Drupal\Component\Plugin\PluginManagerInterface $manager
* The plugin manager instance to use for this mapper.
*/
public function __construct(PluginManagerInterface $manager) {
$this->manager = $manager;
}
/**
* Implements \Drupal\Component\Plugin\Mapper\MapperInterface::getInstance().
*/
public function getInstance(array $options) {
$config = config($options['config']);
if ($config) {
$plugin_id = $config->get('id');
$settings = $config->get();
$settings['config_id'] = $options['config'];
// Attempt to create an instance with this plugin ID and settings.
try {
return $this->manager->createInstance($plugin_id, $settings);
}
catch (PluginException $e) {
return FALSE;
}
}
return FALSE;
}
}
......@@ -202,6 +202,11 @@ function aggregator_form_feed_validate($form, &$form_state) {
* @todo Add delete confirmation dialog.
*/
function aggregator_form_feed_submit($form, &$form_state) {
// @todo Replicate this cache invalidation when these ops are separated.
// Invalidate the block cache to update aggregator feed-based derivatives.
if (module_exists('block')) {
drupal_container()->get('plugin.manager.block')->clearCachedDefinitions();
}
if ($form_state['values']['op'] == t('Delete')) {
$title = $form_state['values']['title'];
// Unset the title.
......@@ -639,6 +644,11 @@ function aggregator_form_category_validate($form, &$form_state) {
* @todo Add delete confirmation dialog.
*/
function aggregator_form_category_submit($form, &$form_state) {
// @todo Replicate this cache invalidation when these ops are separated.
// Invalidate the block cache to update aggregator category-based derivatives.
if (module_exists('block')) {
drupal_container()->get('plugin.manager.block')->clearCachedDefinitions();
}
if ($form_state['values']['op'] == t('Delete')) {
$title = $form_state['values']['title'];
// Unset the title.
......
......@@ -346,95 +346,6 @@ function aggregator_queue_info() {
return $queues;
}
/**
* Implements hook_block_info().
*/
function aggregator_block_info() {
$blocks = array();
$result = db_query('SELECT cid, title FROM {aggregator_category} ORDER BY title');
foreach ($result as $category) {
$blocks['category-' . $category->cid]['info'] = t('!title category latest items', array('!title' => $category->title));
}
$result = db_query('SELECT fid, title FROM {aggregator_feed} WHERE block <> 0 ORDER BY fid');
foreach ($result as $feed) {
$blocks['feed-' . $feed->fid]['info'] = t('!title feed latest items', array('!title' => $feed->title));
}
return $blocks;
}
/**
* Implements hook_block_configure().
*/
function aggregator_block_configure($delta = '') {
list($type, $id) = explode('-', $delta);
if ($type == 'category') {
$value = db_query('SELECT block FROM {aggregator_category} WHERE cid = :cid', array(':cid' => $id))->fetchField();
$form['block'] = array(
'#type' => 'select',
'#title' => t('Number of news items in block'),
'#default_value' => $value,
'#options' => drupal_map_assoc(range(2, 20)),
);
return $form;
}
}
/**
* Implements hook_block_save().
*/
function aggregator_block_save($delta = '', $edit = array()) {
list($type, $id) = explode('-', $delta);
if ($type == 'category') {
db_update('aggregator_category')
->fields(array('block' => $edit['block']))
->condition('cid', $id)
->execute();
}
}
/**
* Implements hook_block_view().
*
* Generates blocks for the latest news items in each category and feed.
*/
function aggregator_block_view($delta = '') {
if (user_access('access news feeds')) {
$block = array();
list($type, $id) = explode('-', $delta);
$result = FALSE;
switch ($type) {
case 'feed':
if ($feed = db_query('SELECT fid, title, block FROM {aggregator_feed} WHERE block <> 0 AND fid = :fid', array(':fid' => $id))->fetchObject()) {
$block['subject'] = check_plain($feed->title);
$result = db_query_range("SELECT * FROM {aggregator_item} WHERE fid = :fid ORDER BY timestamp DESC, iid DESC", 0, $feed->block, array(':fid' => $id));
$read_more = theme('more_link', array('url' => 'aggregator/sources/' . $feed->fid, 'title' => t("View this feed's recent news.")));
}
break;
case 'category':
if ($category = db_query('SELECT cid, title, block FROM {aggregator_category} WHERE cid = :cid', array(':cid' => $id))->fetchObject()) {
$block['subject'] = check_plain($category->title);
$result = db_query_range('SELECT i.* FROM {aggregator_category_item} ci LEFT JOIN {aggregator_item} i ON ci.iid = i.iid WHERE ci.cid = :cid ORDER BY i.timestamp DESC, i.iid DESC', 0, $category->block, array(':cid' => $category->cid));
$read_more = theme('more_link', array('url' => 'aggregator/categories/' . $category->cid, 'title' => t("View this category's recent news.")));
}
break;
}
$items = array();
if (!empty($result)) {
foreach ($result as $item) {
$items[] = theme('aggregator_block_item', array('item' => $item));
}
}
// Only display the block if there are items to show.
if (count($items) > 0) {
$block['content'] = theme('item_list', array('items' => $items)) . $read_more;
}
return $block;
}
}
/**
* Adds/edits/deletes aggregator categories.
*
......@@ -460,11 +371,12 @@ function aggregator_save_category($edit) {
->condition('cid', $edit['cid'])
->execute();
// Make sure there is no active block for this category.
if (module_exists('block')) {
db_delete('block')
->condition('module', 'aggregator')
->condition('delta', 'category-' . $edit['cid'])
->execute();
$block_configs = config_get_storage_names_with_prefix('plugin.core.block');
foreach ($block_configs as $config_id) {
$config = config($config_id);
if ($config->get('id') == 'aggregator_category_block:' . $edit['cid']) {
$config->delete();
}
}
$edit['title'] = '';
$op = 'delete';
......@@ -527,11 +439,12 @@ function aggregator_save_feed($edit) {
->condition('fid', $edit['fid'])
->execute();
// Make sure there is no active block for this feed.
if (module_exists('block')) {
db_delete('block')
->condition('module', 'aggregator')
->condition('delta', 'feed-' . $edit['fid'])
->execute();
$block_configs = config_get_storage_names_with_prefix('plugin.core.block');
foreach ($block_configs as $config_id) {
$config = config($config_id);
if ($config->get('id') == 'aggregator_feed_block:' . $edit['fid']) {
$config->delete();
}
}
}
elseif (!empty($edit['title'])) {
......
<?php
/**
* @file
* Contains \Drupal\aggregator\Plugin\Derivative\AggregatorCategoryBlock.
*/
namespace Drupal\aggregator\Plugin\Derivative;
use Drupal\Component\Plugin\Derivative\DerivativeInterface;
/**
* Provides block plugin definitions for aggregator categories.
*
* @see \Drupal\aggregator\Plugin\block\block\AggregatorCategoryBlock
*/
class AggregatorCategoryBlock implements DerivativeInterface {
/**
* List of derivative definitions.
*
* @var array
*/
protected $derivatives = array();
/**
* Implements \Drupal\Component\Plugin\Derivative\DerivativeInterface::getDerivativeDefinition().
*/
public function getDerivativeDefinition($derivative_id, array $base_plugin_definition) {
if (!empty($this->derivatives) && !empty($this->derivatives[$derivative_id])) {
return $this->derivatives[$derivative_id];
}
$result = db_query('SELECT cid, title FROM {aggregator_category} ORDER BY title WHERE cid = :cid', array(':cid' => $derivative_id))->fetchObject();
$this->derivatives[$derivative_id] = $base_plugin_definition;
$this->derivatives[$derivative_id]['delta'] = $result->cid;
$this->derivatives[$derivative_id]['subject'] = t('@title category latest items', array('@title' => $result->title));
return $this->derivatives[$derivative_id];
}
/**
* Implements \Drupal\Component\Plugin\Derivative\DerivativeInterface::getDerivativeDefinitions().
*/
public function getDerivativeDefinitions(array $base_plugin_definition) {
// Provide a block plugin definition for each aggregator category.
$result = db_query('SELECT cid, title FROM {aggregator_category} ORDER BY title');
foreach ($result as $category) {
$this->derivatives[$category->cid] = $base_plugin_definition;
$this->derivatives[$category->cid]['delta'] = $category->cid;
$this->derivatives[$category->cid]['subject'] = t('@title category latest items', array('@title' => $category->title));
}
return $this->derivatives;
}
}
<?php
/**
* @file
* Contains \Drupal\aggregator\Plugin\Derivative\AggregatorFeedBlock.
*/
namespace Drupal\aggregator\Plugin\Derivative;
use Drupal\Component\Plugin\Derivative\DerivativeInterface;
/**
* Provides block plugin definitions for aggregator feeds.
*
* @see \Drupal\aggregator\Plugin\block\block\AggregatorFeedBlock
*/
class AggregatorFeedBlock implements DerivativeInterface {
/**
* List of derivative definitions.
*
* @var array
*/
protected $derivatives = array();
/**
* Implements \Drupal\Component\Plugin\Derivative\DerivativeInterface::getDerivativeDefinition().
*/
public function getDerivativeDefinition($derivative_id, array $base_plugin_definition) {
if (!empty($this->derivatives) && !empty($this->derivatives[$derivative_id])) {
return $this->derivatives[$derivative_id];
}
$result = db_query('SELECT fid, title, block FROM {aggregator_feed} WHERE block <> 0 AND fid = :fid', array(':fid' => $derivative_id))->fetchObject();
$this->derivatives[$derivative_id] = $base_plugin_definition;
$this->derivatives[$derivative_id]['delta'] = $result->fid;
$this->derivatives[$derivative_id]['subject'] = t('@title feed latest items', array('@title' => $result->title));
return $this->derivatives[$derivative_id];
}
/**
* Implements \Drupal\Component\Plugin\Derivative\DerivativeInterface::getDerivativeDefinitions().
*/
public function getDerivativeDefinitions(array $base_plugin_definition) {
// Add a block plugin definition for each feed.
$result = db_query('SELECT fid, title FROM {aggregator_feed} WHERE block <> 0 ORDER BY fid');
foreach ($result as $feed) {
$this->derivatives[$feed->fid] = $base_plugin_definition;
$this->derivatives[$feed->fid]['delta'] = $feed->fid;
$this->derivatives[$feed->fid]['subject'] = t('@title feed latest items', array('@title' => $feed->title));
}
return $this->derivatives;
}
}
<?php
/**
* @file
* Contains \Drupal\aggregator\Plugin\block\block\AggregatorCategoryBlock.
*/
namespace Drupal\aggregator\Plugin\block\block;
use Drupal\block\BlockBase;
use Drupal\Core\Annotation\Plugin;
use Drupal\Core\Annotation\Translation;
/**
* Provides an 'Aggregator category' block for the latest items in a category.
*
* @Plugin(
* id = "aggregator_category_block",
* subject = @Translation("Aggregator category"),
* module = "aggregator",
* derivative = "Drupal\aggregator\Plugin\Derivative\AggregatorCategoryBlock"
* )
*/
class AggregatorCategoryBlock extends BlockBase {
/**
* Overrides \Drupal\block\BlockBase::blockSettings().
*/
public function blockSettings() {
// By default, the block will contain 10 feed items.
return array(
'block_count' => 10,
);
}
/**
* Overrides \Drupal\block\BlockBase::blockAccess().
*/
public function blockAccess() {
// Only grant access to users with the 'access news feeds' permission.
return user_access('access news feeds');
}
/**
* Overrides \Drupal\block\BlockBase::blockForm().
*/
public function blockForm($form, &$form_state) {
$form['block_count'] = array(
'#type' => 'select',
'#title' => t('Number of news items in block'),
'#default_value' => $this->configuration['block_count'],
'#options' => drupal_map_assoc(range(2, 20)),
);
return $form;
}
/**
* Overrides \Drupal\block\BlockBase::blockSubmit().
*/
public function blockSubmit($form, &$form_state) {
$this->configuration['block_count'] = $form_state['values']['block_count'];
}
/**
* Implements \Drupal\block\BlockBase::blockBuild().
*/
public function blockBuild() {
$id = $this->getPluginId();
if ($category = db_query('SELECT cid, title, block FROM {aggregator_category} WHERE cid = :cid', array(':cid' => $id))->fetchObject()) {
$result = db_query_range('SELECT i.* FROM {aggregator_category_item} ci LEFT JOIN {aggregator_item} i ON ci.iid = i.iid WHERE ci.cid = :cid ORDER BY i.timestamp DESC, i.iid DESC', 0, $this->configuration['block_count'], array(':cid' => $category->cid));
$read_more = theme('more_link', array('url' => 'aggregator/categories/' . $category->cid, 'title' => t("View this category's recent news.")));
$items = array();
foreach ($result as $item) {
$items[] = theme('aggregator_block_item', array('item' => $item));
}
// Only display the block if there are items to show.
if (count($items) > 0) {
return array(
'#children' => theme('item_list', array('items' => $items)) . $read_more,
);
}
return array();
}
}
}
<?php
/**
* @file
* Contains \Drupal\aggregator\Plugin\block\block\AggregatorFeedBlock.
*/
namespace Drupal\aggregator\Plugin\block\block;
use Drupal\block\BlockBase;
use Drupal\Core\Annotation\Plugin;
use Drupal\Core\Annotation\Translation;
/**
* Provides an 'Aggregator feed' block with the latest items from the feed.
*
* @Plugin(
* id = "aggregator_feed_block",
* subject = @Translation("Aggregator feed"),
* module = "aggregator",
* derivative = "Drupal\aggregator\Plugin\Derivative\AggregatorFeedBlock"
* )
*/
class AggregatorFeedBlock extends BlockBase {
/**
* Overrides \Drupal\block\BlockBase::blockSettings().
*/
public function blockSettings() {
// By default, the block will contain 10 feed items.
return array(
'block_count' => 10,
);
}
/**
* Overrides \Drupal\block\BlockBase::blockAccess().
*/
public function blockAccess() {
// Only grant access to users with the 'access news feeds' permission.
return user_access('access news feeds');
}
/**
* Overrides \Drupal\block\BlockBase::blockForm().
*/
public function blockForm($form, &$form_state) {
$form['block_count'] = array(
'#type' => 'select',
'#title' => t('Number of news items in block'),
'#default_value' => $this->configuration['block_count'],
'#options' => drupal_map_assoc(range(2, 20)),
);
return $form;
}
/**
* Overrides \Drupal\block\BlockBase::blockSubmit().
*/
public function blockSubmit($form, &$form_state) {
$this->configuration['block_count'] = $form_state['values']['block_count'];
}
/**
* Implements \Drupal\block\BlockBase::blockBuild().
*/
public function blockBuild() {
// Plugin IDs look something like this: aggregator_feed_block:1.
list(, $id) = explode(':', $this->getPluginId());
if ($feed = db_query('SELECT fid, title, block FROM {aggregator_feed} WHERE block <> 0 AND fid = :fid', array(':fid' => $id))->fetchObject()) {
$result = db_query_range("SELECT * FROM {aggregator_item} WHERE fid = :fid ORDER BY timestamp DESC, iid DESC", 0, $this->configuration['block_count'], array(':fid' => $id));
$read_more = theme('more_link', array('url' => 'aggregator/sources/' . $feed->fid, 'title' => t("View this feed's recent news.")));
$items = array();
foreach ($result as $item) {
$items[] = theme('aggregator_block_item', array('item' => $item));
}
// Only display the block if there are items to show.
if (count($items) > 0) {
return array(
'#children' => theme('item_list', array('items' => $items)) . $read_more,
);
}
}
}
}
......@@ -8,6 +8,14 @@
namespace Drupal\aggregator\Tests;
class AggregatorRenderingTest extends AggregatorTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('block');
public static function getInfo() {
return array(
'name' => 'Checks display of aggregator items',
......@@ -27,31 +35,26 @@ public function testBlockLinks() {
$feed = $this->createFeed();
$this->updateFeedItems($feed, $this->getDefaultFeedItemCount());
// Place block on page (@see block.test:moveBlockToRegion())
// Need admin user to be able to access block admin.
$this->admin_user = $this->drupalCreateUser(array(
$admin_user = $this->drupalCreateUser(array(
'administer blocks',
'access administration pages',
'administer news feeds',
'access news feeds',
));
$this->drupalLogin($this->admin_user);
$this->drupalLogin($admin_user);
// Prepare to use the block admin form.
$current_theme = variable_get('theme_default', 'stark');
$machine_name = 'test_aggregator_feed_block';
$block = array(
'module' => 'aggregator',
'delta' => 'feed-' . $feed->fid,
'title' => $feed->title,
'machine_name' => $machine_name,
'region' => 'footer',
'title' => 'feed-' . $feed->title,
'block_count' => 2,
);
$region = 'footer';
$edit = array();
$edit['blocks[' . $block['module'] . '_' . $block['delta'] . '][region]'] = $region;
// Check the feed block is available in the block list form.
$this->drupalGet('admin/structure/block');
$this->assertFieldByName('blocks[' . $block['module'] . '_' . $block['delta'] . '][region]', '', 'Aggregator feed block is available for positioning.');
// Position it.
$this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
$this->assertText(t('The block settings have been updated.'), format_string('Block successfully moved to %region_name region.', array( '%region_name' => $region)));
$this->drupalPost("admin/structure/block/manage/aggregator_feed_block:{$feed->fid}/$current_theme", $block, t('Save block'));
$this->assertText(t('The block configuration has been saved.'), 'Block was saved.');
// Confirm that the block is now being displayed on pages.
$this->drupalGet('node');
$this->assertText(t($block['title']), 'Feed block is displayed on the page.');
......@@ -70,8 +73,6 @@ public function testBlockLinks() {
// up.
$feed->block = 0;
aggregator_save_feed((array) $feed);
// It is nescessary to flush the cache after saving the number of items.
$this->resetAll();
// Check that the block is no longer displayed.
$this->drupalGet('node');
$this->assertNoText(t($block['title']), 'Feed block is not displayed on the page when number of items is set to 0.');
......
......@@ -19,7 +19,7 @@ abstract class AggregatorTestBase extends WebTestBase {
*
* @var array
*/
public static $modules = array('node', 'block', 'aggregator', 'aggregator_test');
public static $modules = array('node', 'aggregator', 'aggregator_test');
function setUp() {
parent::setUp();
......
......@@ -8,6 +8,14 @@
namespace Drupal\aggregator\Tests;
class ImportOpmlTest extends AggregatorTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('block');
public static function getInfo() {
return array(
'name' => 'Import feeds from OPML functionality',
......@@ -16,6 +24,13 @@ public static function getInfo() {
);
}