summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorDries2013-06-19 20:29:36 (GMT)
committerDries2013-06-19 20:29:36 (GMT)
commite4c24df2f3a2853f57dfe6828b1331746b011fd8 (patch)
tree5c58a0b3acc172b2ba6e72d625322348b682204b /core
parent6c3dae99d2afcbdfe88dfc26edcdd62e8c2e130d (diff)
Issue #1821844 by dawehner, jibran, damiankloip, xjm, ParisLiakos, wamilton, olli: Aggregator views integration.
Diffstat (limited to 'core')
-rw-r--r--core/modules/aggregator/aggregator.views.inc324
-rw-r--r--core/modules/aggregator/lib/Drupal/aggregator/Plugin/Core/Entity/Item.php4
-rw-r--r--core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/argument/CategoryCid.php67
-rw-r--r--core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/argument/Fid.php69
-rw-r--r--core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/argument/Iid.php69
-rw-r--r--core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/field/Category.php84
-rw-r--r--core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/field/TitleLink.php88
-rw-r--r--core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/field/Xss.php29
-rw-r--r--core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/filter/CategoryCid.php65
-rw-r--r--core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/row/Rss.php109
-rw-r--r--core/modules/aggregator/lib/Drupal/aggregator/Tests/Views/IntegrationTest.php114
-rw-r--r--core/modules/aggregator/tests/modules/aggregator_test_views/aggregator_test_views.info.yml9
-rw-r--r--core/modules/aggregator/tests/modules/aggregator_test_views/aggregator_test_views.module1
-rwxr-xr-xcore/modules/aggregator/tests/modules/aggregator_test_views/test_views/views.view.test_aggregator_items.yml168
14 files changed, 1199 insertions, 1 deletions
diff --git a/core/modules/aggregator/aggregator.views.inc b/core/modules/aggregator/aggregator.views.inc
new file mode 100644
index 0000000..5d87034
--- /dev/null
+++ b/core/modules/aggregator/aggregator.views.inc
@@ -0,0 +1,324 @@
+<?php
+
+/**
+ * @file
+ * Provides views data for aggregator.module.
+ *
+ * @ingroup views_module_handlers
+ */
+
+/**
+ * Implements hook_views_data().
+ */
+function aggregator_views_data() {
+ $data = array();
+
+ $data['aggregator_item']['table']['group'] = t('Aggregator');
+
+ $data['aggregator_item']['table']['base'] = array(
+ 'field' => 'iid',
+ 'title' => t('Aggregator item'),
+ 'help' => t('Aggregator items are imported from external RSS and Atom news feeds.'),
+ );
+ $data['aggregator_item']['table']['entity type'] = 'aggregator_item';
+
+ $data['aggregator_item']['iid'] = array(
+ 'title' => t('Item ID'),
+ 'help' => t('The unique ID of the aggregator item.'),
+ 'field' => array(
+ 'id' => 'numeric',
+ ),
+ 'argument' => array(
+ 'id' => 'aggregator_iid',
+ 'name field' => 'title',
+ 'numeric' => TRUE,
+ ),
+ 'filter' => array(
+ 'id' => 'numeric',
+ ),
+ 'sort' => array(
+ 'id' => 'standard',
+ ),
+ );
+
+ $data['aggregator_item']['title'] = array(
+ 'title' => t('Title'),
+ 'help' => t('The title of the aggregator item.'),
+ 'field' => array(
+ 'id' => 'aggregator_title_link',
+ 'extra' => array('link'),
+ ),
+ 'argument' => array(
+ 'id' => 'string',
+ ),
+ 'sort' => array(
+ 'id' => 'standard',
+ ),
+ 'filter' => array(
+ 'id' => 'string',
+ ),
+ );
+
+ $data['aggregator_item']['link'] = array(
+ 'title' => t('Link'),
+ 'help' => t('The link to the original source URL of the item.'),
+ 'field' => array(
+ 'id' => 'url',
+ ),
+ 'argument' => array(
+ 'id' => 'string',
+ ),
+ 'sort' => array(
+ 'id' => 'standard',
+ ),
+ 'filter' => array(
+ 'id' => 'string',
+ ),
+ );
+
+ $data['aggregator_item']['author'] = array(
+ 'title' => t('Author'),
+ 'help' => t('The author of the original imported item.'),
+ 'field' => array(
+ 'id' => 'aggregator_xss',
+ ),
+ 'argument' => array(
+ 'id' => 'string',
+ ),
+ 'sort' => array(
+ 'id' => 'standard',
+ ),
+ 'filter' => array(
+ 'id' => 'string',
+ ),
+ );
+
+ $data['aggregator_item']['guid'] = array(
+ 'title' => t('GUID'),
+ 'help' => t('The guid of the original imported item.'),
+ 'field' => array(
+ 'id' => 'standard',
+ ),
+ 'argument' => array(
+ 'id' => 'string',
+ ),
+ 'sort' => array(
+ 'id' => 'standard',
+ ),
+ 'filter' => array(
+ 'id' => 'string',
+ ),
+ );
+
+ $data['aggregator_item']['description'] = array(
+ 'title' => t('Body'),
+ 'help' => t('The actual content of the imported item.'),
+ 'field' => array(
+ 'id' => 'aggregator_xss',
+ 'click sortable' => FALSE,
+ ),
+ 'argument' => array(
+ 'id' => 'string',
+ ),
+ 'filter' => array(
+ 'id' => 'string',
+ ),
+ 'sort' => array(
+ 'id' => 'standard',
+ ),
+ );
+
+ $data['aggregator_item']['timestamp'] = array(
+ 'title' => t('Timestamp'),
+ 'help' => t('The date the original feed item was posted. (With some feeds, this will be the date it was imported.)'),
+ 'field' => array(
+ 'id' => 'date',
+ ),
+ 'sort' => array(
+ 'id' => 'date',
+ ),
+ 'filter' => array(
+ 'id' => 'date',
+ ),
+ 'argument' => array(
+ 'id' => 'date',
+ ),
+ );
+
+ $data['aggregator_feed']['table']['group'] = t('Aggregator feed');
+
+ $data['aggregator_feed']['table']['base'] = array(
+ 'field' => 'fid',
+ 'title' => t('Aggregator feed'),
+ );
+
+ $data['aggregator_feed']['table']['entity type'] = 'aggregator_feed';
+
+ $data['aggregator_feed']['table']['join'] = array(
+ 'aggregator_item' => array(
+ 'left_field' => 'fid',
+ 'field' => 'fid',
+ ),
+ );
+
+ $data['aggregator_feed']['fid'] = array(
+ 'title' => t('Feed ID'),
+ 'help' => t('The unique ID of the aggregator feed.'),
+ 'field' => array(
+ 'id' => 'numeric',
+ ),
+ 'argument' => array(
+ 'id' => 'aggregator_fid',
+ 'name field' => 'title',
+ 'numeric' => TRUE,
+ ),
+ 'filter' => array(
+ 'id' => 'numeric',
+ ),
+ 'sort' => array(
+ 'id' => 'standard',
+ ),
+ );
+
+ $data['aggregator_feed']['title'] = array(
+ 'title' => t('Title'),
+ 'help' => t('The title of the aggregator feed.'),
+ 'field' => array(
+ 'id' => 'aggregator_title_link',
+ 'extra' => array('link'),
+ ),
+ 'sort' => array(
+ 'id' => 'standard',
+ ),
+ 'filter' => array(
+ 'id' => 'string',
+ ),
+ 'argument' => array(
+ 'id' => 'string',
+ ),
+ );
+
+ $data['aggregator_feed']['link'] = array(
+ 'title' => t('Link'),
+ 'help' => t('The link to the source URL of the feed.'),
+ 'field' => array(
+ 'id' => 'url',
+ ),
+ 'argument' => array(
+ 'id' => 'string',
+ ),
+ 'sort' => array(
+ 'id' => 'standard',
+ ),
+ 'filter' => array(
+ 'id' => 'string',
+ ),
+ );
+
+ $data['aggregator_feed']['checked'] = array(
+ 'title' => t('Last checked'),
+ 'help' => t('The date the feed was last checked for new content.'),
+ 'field' => array(
+ 'id' => 'date',
+ ),
+ 'sort' => array(
+ 'id' => 'date',
+ ),
+ 'filter' => array(
+ 'id' => 'date',
+ ),
+ 'argument' => array(
+ 'id' => 'date',
+ ),
+ );
+
+ $data['aggregator_feed']['description'] = array(
+ 'title' => t('Description'),
+ 'help' => t('The description of the aggregator feed.'),
+ 'field' => array(
+ 'id' => 'xss',
+ 'click sortable' => FALSE,
+ ),
+ 'filter' => array(
+ 'id' => 'string',
+ ),
+ 'argument' => array(
+ 'id' => 'string',
+ ),
+ 'sort' => array(
+ 'id' => 'standard',
+ ),
+ );
+
+ $data['aggregator_feed']['modified'] = array(
+ 'title' => t('Last modified'),
+ 'help' => t('The date of the most recent new content on the feed.'),
+ 'field' => array(
+ 'id' => 'date',
+ ),
+ 'sort' => array(
+ 'id' => 'date',
+ ),
+ 'filter' => array(
+ 'id' => 'date',
+ ),
+ 'argument' => array(
+ 'id' => 'date',
+ ),
+ );
+
+ $data['aggregator_category_feed']['table']['join'] = array(
+ 'aggregator_item' => array(
+ 'left_field' => 'fid',
+ 'field' => 'fid',
+ ),
+ );
+
+ $data['aggregator_category']['table']['group'] = t('Aggregator category');
+
+ $data['aggregator_category']['table']['join'] = array(
+ 'aggregator_item' => array(
+ 'left_table' => 'aggregator_category_feed',
+ 'left_field' => 'cid',
+ 'field' => 'cid',
+ ),
+ );
+
+ $data['aggregator_category']['cid'] = array(
+ 'title' => t('Category ID'),
+ 'help' => t('The unique ID of the aggregator category.'),
+ 'field' => array(
+ 'id' => 'numeric',
+ ),
+ 'argument' => array(
+ 'id' => 'aggregator_category_cid',
+ 'name field' => 'title',
+ 'numeric' => TRUE,
+ ),
+ 'filter' => array(
+ 'id' => 'aggregator_category_cid',
+ ),
+ 'sort' => array(
+ 'id' => 'standard',
+ ),
+ );
+
+ $data['aggregator_category']['title'] = array(
+ 'title' => t('Category'),
+ 'help' => t('The title of the aggregator category.'),
+ 'field' => array(
+ 'id' => 'aggregator_category',
+ ),
+ 'argument' => array(
+ 'id' => 'string',
+ ),
+ 'sort' => array(
+ 'id' => 'standard',
+ ),
+ 'filter' => array(
+ 'id' => 'string',
+ ),
+ );
+
+ return $data;
+}
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Core/Entity/Item.php b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Core/Entity/Item.php
index b527752..6da20f3 100644
--- a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Core/Entity/Item.php
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Core/Entity/Item.php
@@ -136,7 +136,9 @@ class Item extends EntityNG implements ItemInterface {
* {@inheritdoc}
*/
public function postCreate(EntityStorageControllerInterface $storage_controller) {
- $this->timestamp->value = REQUEST_TIME;
+ if (!isset($this->timestamp->value)) {
+ $this->timestamp->value = REQUEST_TIME;
+ }
}
/**
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/argument/CategoryCid.php b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/argument/CategoryCid.php
new file mode 100644
index 0000000..fb55a40
--- /dev/null
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/argument/CategoryCid.php
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\aggregator\Plugin\views\argument\CategoryCid.
+ */
+
+namespace Drupal\aggregator\Plugin\views\argument;
+
+use Drupal\views\Plugin\views\argument\Numeric;
+use Drupal\Component\Annotation\PluginID;
+use Drupal\Component\Utility\String;
+use Drupal\Core\Database\Connection;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Argument handler to accept an aggregator category id.
+ *
+ * @ingroup views_argument_handlers
+ *
+ * @PluginID("aggregator_category_cid")
+ */
+class CategoryCid extends Numeric {
+
+ /**
+ * Database Service Object.
+ *
+ * @var \Drupal\Core\Database\Connection
+ */
+ protected $database;
+
+ /**
+ * 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\Core\Database\Connection $database
+ * Database Service Object.
+ */
+ public function __construct(array $configuration, $plugin_id, array $plugin_definition, Connection $database) {
+ parent::__construct($configuration, $plugin_id, $plugin_definition);
+ $this->database = $database;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) {
+ return new static($configuration, $plugin_id, $plugin_definition, $container->get('database'));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function titleQuery() {
+ $titles = $this->database->query("SELECT title FROM {aggregator_category} where cid IN (:cid)", array(':cid' => $this->value))->fetchCol();
+
+ return array_map(function ($title) {
+ return String::checkPlain($title);
+ }, $titles);
+ }
+
+}
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/argument/Fid.php b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/argument/Fid.php
new file mode 100644
index 0000000..f77fb02
--- /dev/null
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/argument/Fid.php
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\aggregator\Plugin\views\argument\Fid.
+ */
+
+namespace Drupal\aggregator\Plugin\views\argument;
+
+use Drupal\views\Plugin\views\argument\Numeric;
+use Drupal\Component\Annotation\PluginID;
+use Drupal\Component\Utility\String;
+use Drupal\Core\Entity\EntityManager;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Argument handler to accept an aggregator feed id.
+ *
+ * @ingroup views_argument_handlers
+ *
+ * @PluginID("aggregator_fid")
+ */
+class Fid extends Numeric {
+
+ /**
+ * The entity manager service
+ *
+ * @var \Drupal\Core\Entity\EntityManager
+ */
+ protected $entityManager;
+
+ /**
+ * 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\Core\Entity\EntityManager $entity_manager
+ * The entity manager.
+ */
+ public function __construct(array $configuration, $plugin_id, array $plugin_definition, EntityManager $entity_manager) {
+ parent::__construct($configuration, $plugin_id, $plugin_definition);
+ $this->entityManager = $entity_manager;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) {
+ return new static($configuration, $plugin_id, $plugin_definition, $container->get('plugin.manager.entity'));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function titleQuery() {
+ $titles = array();
+
+ $feeds = $this->entityManager->getStorageController('aggregator_feed')->load($this->value);
+ foreach ($feeds as $feed) {
+ $titles[] = String::checkPlain($feed->label());
+ }
+ return $titles;
+ }
+
+}
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/argument/Iid.php b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/argument/Iid.php
new file mode 100644
index 0000000..1ce1864
--- /dev/null
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/argument/Iid.php
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\aggregator\Plugin\views\argument\Iid.
+ */
+
+namespace Drupal\aggregator\Plugin\views\argument;
+
+use Drupal\views\Plugin\views\argument\Numeric;
+use Drupal\Component\Annotation\PluginID;
+use Drupal\Component\Utility\String;
+use Drupal\Core\Entity\EntityManager;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Argument handler to accept an aggregator item id.
+ *
+ * @ingroup views_argument_handlers
+ *
+ * @PluginID("aggregator_iid")
+ */
+class Iid extends Numeric {
+
+ /**
+ * The entity manager service
+ *
+ * @var \Drupal\Core\Entity\EntityManager
+ */
+ protected $entityManager;
+
+ /**
+ * 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\Core\Entity\EntityManager $entity_manager
+ * The entity manager.
+ */
+ public function __construct(array $configuration, $plugin_id, array $plugin_definition, EntityManager $entity_manager) {
+ parent::__construct($configuration, $plugin_id, $plugin_definition);
+ $this->entityManager = $entity_manager;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) {
+ return new static($configuration, $plugin_id, $plugin_definition, $container->get('plugin.manager.entity'));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function titleQuery() {
+ $titles = array();
+
+ $items = $this->entityManager->getStorageController('aggregator_item')->load($this->value);
+ foreach ($items as $feed) {
+ $titles[] = String::checkPlain($feed->label());
+ }
+ return $titles;
+ }
+
+}
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/field/Category.php b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/field/Category.php
new file mode 100644
index 0000000..8291422
--- /dev/null
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/field/Category.php
@@ -0,0 +1,84 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\aggregator\Plugin\views\field\Category.
+ */
+
+namespace Drupal\aggregator\Plugin\views\field;
+
+use Drupal\views\Plugin\views\display\DisplayPluginBase;
+use Drupal\views\Plugin\views\field\FieldPluginBase;
+use Drupal\views\ViewExecutable;
+use Drupal\Component\Annotation\PluginID;
+
+/**
+ * Defines a simple renderer that allows linking to an aggregator category.
+ *
+ * @ingroup views_field_handlers
+ *
+ * @PluginID("aggregator_category")
+ */
+class Category extends FieldPluginBase {
+
+ /**
+ * {@inheritdoc}
+ */
+ public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
+ parent::init($view, $display, $options);
+
+ $this->additional_fields['cid'] = 'cid';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+
+ $options['link_to_category'] = array('default' => FALSE);
+ return $options;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function buildOptionsForm(&$form, &$form_state) {
+ $form['link_to_category'] = array(
+ '#title' => t('Link this field to its aggregator category page'),
+ '#description' => t('This will override any other link you have set.'),
+ '#type' => 'checkbox',
+ '#default_value' => !empty($this->options['link_to_category']),
+ );
+ parent::buildOptionsForm($form, $form_state);
+ }
+
+ /**
+ * Render whatever the data is as a link to the category.
+ *
+ * @param string $data
+ * The XSS safe string for the link text.
+ * @param object $values
+ * The values retrieved from the database.
+ *
+ * @return data
+ * Returns string for the link text.
+ */
+ protected function render_link($data, $values) {
+ $cid = $this->getValue($values, 'cid');
+ if (!empty($this->options['link_to_category']) && !empty($cid) && $data !== NULL && $data !== '') {
+ $this->options['alter']['make_link'] = TRUE;
+ $this->options['alter']['path'] = "aggregator/categories/$cid";
+ }
+ return $data;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function render($values) {
+ $value = $this->getValue($values);
+ return $this->render_link($this->sanitizeValue($value), $values);
+ }
+
+}
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/field/TitleLink.php b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/field/TitleLink.php
new file mode 100644
index 0000000..3eae377
--- /dev/null
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/field/TitleLink.php
@@ -0,0 +1,88 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\aggregator\Plugin\views\field\TitleLink.
+ */
+
+namespace Drupal\aggregator\Plugin\views\field;
+
+use Drupal\views\Plugin\views\display\DisplayPluginBase;
+use Drupal\views\Plugin\views\field\FieldPluginBase;
+use Drupal\views\ViewExecutable;
+use Drupal\Component\Annotation\PluginID;
+
+/**
+ * Defines a field handler that turns an item's title into a clickable link to
+ * the original source article.
+ *
+ * @ingroup views_field_handlers
+ *
+ * @PluginID("aggregator_title_link")
+ */
+class TitleLink extends FieldPluginBase {
+
+ /**
+ * {@inheritdoc}
+ */
+ public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
+ parent::init($view, $display, $options);
+
+ $this->additional_fields['link'] = 'link';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+
+ $options['display_as_link'] = array('default' => TRUE);
+
+ return $options;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function buildOptionsForm(&$form, &$form_state) {
+ $form['display_as_link'] = array(
+ '#title' => t('Display as link'),
+ '#type' => 'checkbox',
+ '#default_value' => !empty($this->options['display_as_link']),
+ );
+ parent::buildOptionsForm($form, $form_state);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function render($values) {
+ $value = $this->getValue($values);
+ return $this->render_link($this->sanitizeValue($value), $values);
+ }
+
+ /**
+ * Renders aggregator item's title as link.
+ *
+ * @param string $data
+ * The XSS safe string for the link text.
+ * @param object $values
+ * The values retrieved from the database.
+ *
+ * @return data
+ * Returns string for the link text.
+ */
+ protected function render_link($data, $values) {
+ $link = $this->getValue($values, 'link');
+ if (!empty($this->options['display_as_link'])) {
+ $this->options['alter']['make_link'] = TRUE;
+ $this->options['alter']['path'] = $link;
+ $this->options['alter']['html'] = TRUE;
+ $this->options['alter']['absolute'] = TRUE;
+ }
+
+ return $data;
+ }
+
+}
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/field/Xss.php b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/field/Xss.php
new file mode 100644
index 0000000..5b755ff
--- /dev/null
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/field/Xss.php
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\aggregator\Plugin\views\field\Xss.
+ */
+
+namespace Drupal\aggregator\Plugin\views\field;
+
+use Drupal\views\Plugin\views\field\Xss as XssBase;
+use Drupal\Component\Annotation\PluginID;
+
+/**
+ * Filters htmls tags from item.
+ *
+ * @ingroup views_field_handlers
+ *
+ * @PluginID("aggregator_xss")
+ */
+class Xss extends XssBase {
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sanitizeValue($value, $type = NULL) {
+ return aggregator_filter_xss($value);
+ }
+
+}
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/filter/CategoryCid.php b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/filter/CategoryCid.php
new file mode 100644
index 0000000..bb5f705
--- /dev/null
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/filter/CategoryCid.php
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\aggregator\Plugin\views\filter\CategoryCid.
+ */
+
+namespace Drupal\aggregator\Plugin\views\filter;
+
+use Drupal\views\Plugin\views\filter\InOperator;
+use Drupal\Component\Annotation\PluginID;
+
+/**
+ * Defines a filter handler that filters by aggregator category cid.
+ *
+ * @ingroup views_filter_handlers
+ *
+ * @PluginID("aggregator_category_cid")
+ */
+class CategoryCid extends InOperator {
+
+ /**
+ * Database Service Object.
+ *
+ * @var \Drupal\Core\Database\Connection
+ */
+ protected $database;
+
+ /**
+ * 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\Core\Database\Connection $database
+ * Database Service Object.
+ */
+ public function __construct(array $configuration, $plugin_id, array $plugin_definition, Connection $database) {
+ parent::__construct($configuration, $plugin_id, $plugin_definition);
+ $this->database = $database;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) {
+ return new static($configuration, $plugin_id, $plugin_definition, $container->get('database'));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function getValueOptions() {
+ if (isset($this->value_options)) {
+ return;
+ }
+
+ $this->value_options = array();
+ $this->value_options = $this->database->query('SELECT cid, title FROM {aggregator_category} ORDER BY title')->fetchAllKeyed();
+ }
+
+}
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/row/Rss.php b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/row/Rss.php
new file mode 100644
index 0000000..7e55034
--- /dev/null
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/views/row/Rss.php
@@ -0,0 +1,109 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\aggregator\Plugin\views\row\Rss.
+ */
+
+namespace Drupal\aggregator\Plugin\views\row;
+
+use Drupal\views\Plugin\views\row\RowPluginBase;
+use Drupal\Component\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Defines a row plugin which loads an aggregator item and renders as RSS.
+ *
+ * @Plugin(
+ * id = "aggregator_rss",
+ * module = "aggregator",
+ * theme = "views_view_row_rss",
+ * title = @Translation("Aggregator item"),
+ * help = @Translation("Display the aggregator item using the data from the original source."),
+ * base = {"aggregator_item"},
+ * display_types = {"feed"}
+ * )
+ */
+class Rss extends RowPluginBase {
+
+ /**
+ * The table the aggregator item is using for storage.
+ *
+ * @var string
+ */
+ public $base_table = 'aggregator_item';
+
+ /**
+ * The actual field which is used to identify a aggregator item.
+ *
+ * @var string
+ */
+ public $base_field = 'iid';
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+
+ $options['item_length'] = array('default' => 'default');
+
+ return $options;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function buildOptionsForm(&$form, &$form_state) {
+ $form['item_length'] = array(
+ '#type' => 'select',
+ '#title' => t('Display type'),
+ '#options' => array(
+ 'fulltext' => t('Full text'),
+ 'teaser' => t('Title plus teaser'),
+ 'title' => t('Title only'),
+ 'default' => t('Use default RSS settings'),
+ ),
+ '#default_value' => $this->options['item_length'],
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ function render($row) {
+ $entity = $row->_entity;
+
+ $item = new \stdClass();
+ foreach ($entity->getProperties() as $name => $value) {
+ // views_view_row_rss takes care about the escaping.
+ $item->{$name} = $value->value;
+ }
+
+ $item->elements = array(
+ array(
+ 'key' => 'pubDate',
+ // views_view_row_rss takes care about the escaping.
+ 'value' => gmdate('r', $entity->timestamp->value),
+ ),
+ array(
+ 'key' => 'dc:creator',
+ // views_view_row_rss takes care about the escaping.
+ 'value' => $entity->author->value,
+ ),
+ array(
+ 'key' => 'guid',
+ // views_view_row_rss takes care about the escaping.
+ 'value' => $entity->guid->value,
+ 'attributes' => array('isPermaLink' => 'false'),
+ ),
+ );
+
+ return theme($this->themeFunctions(), array(
+ 'view' => $this->view,
+ 'options' => $this->options,
+ 'row' => $item,
+ ));
+ }
+
+}
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Tests/Views/IntegrationTest.php b/core/modules/aggregator/lib/Drupal/aggregator/Tests/Views/IntegrationTest.php
new file mode 100644
index 0000000..da85242
--- /dev/null
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Tests/Views/IntegrationTest.php
@@ -0,0 +1,114 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\aggregator\Tests\Views\IntegrationTest.
+ */
+
+namespace Drupal\aggregator\Tests\Views;
+
+use Drupal\views\Tests\ViewTestData;
+use Drupal\views\Tests\ViewUnitTestBase;
+
+/**
+ * Tests basic views integration of aggregator module.
+ */
+class IntegrationTest extends ViewUnitTestBase {
+
+ /**
+ * Modules to enable.
+ *
+ * @var array
+ */
+ public static $modules = array('aggregator', 'aggregator_test_views', 'system', 'field');
+
+ /**
+ * Views used by this test.
+ *
+ * @var array
+ */
+ public static $testViews = array('test_aggregator_items');
+
+ /**
+ * The entity storage controller for aggregator items.
+ *
+ * @var \Drupal\aggregator\ItemStorageController
+ */
+ protected $itemStorageController;
+
+ /**
+ * The entity storage controller for aggregator feeds.
+ *
+ * @var \Drupal\aggregator\FeedStorageController
+ */
+ protected $feedStorageController;
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Aggregator: Integration tests',
+ 'description' => 'Tests basic integration of views data from the aggregator module.',
+ 'group' => 'Views module integration',
+ );
+ }
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->installSchema('aggregator', array('aggregator_item', 'aggregator_feed', 'aggregator_category_feed', 'aggregator_category', 'aggregator_category_item'));
+
+ ViewTestData::importTestViews(get_class($this), array('aggregator_test_views'));
+
+ $this->itemStorageController = $this->container->get('plugin.manager.entity')->getStorageController('aggregator_item');
+ $this->feedStorageController = $this->container->get('plugin.manager.entity')->getStorageController('aggregator_feed');
+ }
+
+ /**
+ * Tests basic aggregator_item view.
+ */
+ public function testAggregatorItemView() {
+ $items = array();
+ $expected = array();
+ for ($i = 0; $i < 10; $i++) {
+ $values = array();
+ $values['timestamp'] = mt_rand(REQUEST_TIME - 10, REQUEST_TIME + 10);
+ $values['title'] = $this->randomName();
+ $values['description'] = $this->randomName();
+ // Add a image to ensure that the sanitizing can be tested below.
+ $values['author'] = $this->randomName() . '<img src="http://example.com/example.png" \>"';
+ $values['link'] = 'http://drupal.org/node/' . mt_rand(1000, 10000);
+
+ $aggregator_item = $this->itemStorageController->create($values);
+ $aggregator_item->save();
+ $items[$aggregator_item->id()] = $aggregator_item;
+
+ $values['iid'] = $aggregator_item->id();
+ $expected[] = $values;
+ }
+
+ $view = views_get_view('test_aggregator_items');
+ $this->executeView($view);
+
+ $column_map = array(
+ 'iid' => 'iid',
+ 'aggregator_item_title' => 'title',
+ 'aggregator_item_timestamp' => 'timestamp',
+ 'aggregator_item_description' => 'description',
+ 'aggregator_item_author' => 'author',
+ );
+ $this->assertIdenticalResultset($view, $expected, $column_map);
+
+ // Ensure that the rendering of the linked title works as expected.
+ foreach ($view->result as $row) {
+ $iid = $view->field['iid']->getValue($row);
+ $expected_link = l($items[$iid]->title->value, $items[$iid]->link->value, array('absolute' => TRUE));
+ $this->assertEqual($view->field['title']->advancedRender($row), $expected_link, 'Ensure the right link is generated');
+
+ $expected_author = aggregator_filter_xss($items[$iid]->author->value);
+ $this->assertEqual($view->field['author']->advancedRender($row), $expected_author, 'Ensure the author got filtered');
+
+ $expected_description = aggregator_filter_xss($items[$iid]->description->value);
+ $this->assertEqual($view->field['description']->advancedRender($row), $expected_description, 'Ensure the author got filtered');
+ }
+ }
+
+}
diff --git a/core/modules/aggregator/tests/modules/aggregator_test_views/aggregator_test_views.info.yml b/core/modules/aggregator/tests/modules/aggregator_test_views/aggregator_test_views.info.yml
new file mode 100644
index 0000000..2165bd7
--- /dev/null
+++ b/core/modules/aggregator/tests/modules/aggregator_test_views/aggregator_test_views.info.yml
@@ -0,0 +1,9 @@
+name: 'Aggregator test views'
+description: 'Provides default views for views aggregator tests.'
+package: Testing
+version: VERSION
+core: 8.x
+dependencies:
+ - aggregator
+ - views
+hidden: true
diff --git a/core/modules/aggregator/tests/modules/aggregator_test_views/aggregator_test_views.module b/core/modules/aggregator/tests/modules/aggregator_test_views/aggregator_test_views.module
new file mode 100644
index 0000000..b3d9bbc
--- /dev/null
+++ b/core/modules/aggregator/tests/modules/aggregator_test_views/aggregator_test_views.module
@@ -0,0 +1 @@
+<?php
diff --git a/core/modules/aggregator/tests/modules/aggregator_test_views/test_views/views.view.test_aggregator_items.yml b/core/modules/aggregator/tests/modules/aggregator_test_views/test_views/views.view.test_aggregator_items.yml
new file mode 100755
index 0000000..c4e51f7
--- /dev/null
+++ b/core/modules/aggregator/tests/modules/aggregator_test_views/test_views/views.view.test_aggregator_items.yml
@@ -0,0 +1,168 @@
+base_field: iid
+base_table: aggregator_item
+core: 8.x
+description: ''
+status: '1'
+display:
+ default:
+ display_plugin: default
+ id: default
+ display_title: Master
+ position: ''
+ display_options:
+ access:
+ type: none
+ cache:
+ type: none
+ query:
+ type: views_query
+ exposed_form:
+ type: basic
+ pager:
+ type: full
+ style:
+ type: default
+ row:
+ type: fields
+ fields:
+ iid:
+ table: aggregator_item
+ field: iid
+ id: iid
+ plugin_id: numeric
+ title:
+ table: aggregator_item
+ field: title
+ id: title
+ plugin_id: aggregator_title_link
+ alter:
+ alter_text: '0'
+ text: ''
+ make_link: '0'
+ path: ''
+ absolute: '0'
+ external: '0'
+ replace_spaces: '0'
+ path_case: none
+ trim_whitespace: '0'
+ alt: ''
+ rel: ''
+ link_class: ''
+ prefix: ''
+ suffix: ''
+ target: ''
+ nl2br: '0'
+ max_length: ''
+ word_boundary: '1'
+ ellipsis: '1'
+ more_link: '0'
+ more_link_text: ''
+ more_link_path: ''
+ strip_tags: '0'
+ trim: '0'
+ preserve_tags: ''
+ html: '0'
+ timestamp:
+ table: aggregator_item
+ field: timestamp
+ id: timestamp
+ plugin_id: date
+ author:
+ table: aggregator_item
+ field: author
+ id: author
+ plugin_id: aggregator_xss
+ alter:
+ alter_text: '0'
+ text: ''
+ make_link: '0'
+ path: ''
+ absolute: '0'
+ external: '0'
+ replace_spaces: '0'
+ path_case: none
+ trim_whitespace: '0'
+ alt: ''
+ rel: ''
+ link_class: ''
+ prefix: ''
+ suffix: ''
+ target: ''
+ nl2br: '0'
+ max_length: ''
+ word_boundary: '1'
+ ellipsis: '1'
+ more_link: '0'
+ more_link_text: ''
+ more_link_path: ''
+ strip_tags: '0'
+ trim: '0'
+ preserve_tags: ''
+ html: '0'
+ description:
+ id: description
+ table: aggregator_item
+ field: description
+ relationship: none
+ group_type: group
+ admin_label: ''
+ label: Body
+ exclude: '0'
+ alter:
+ alter_text: '0'
+ text: ''
+ make_link: '0'
+ path: ''
+ absolute: '0'
+ external: '0'
+ replace_spaces: '0'
+ path_case: none
+ trim_whitespace: '0'
+ alt: ''
+ rel: ''
+ link_class: ''
+ prefix: ''
+ suffix: ''
+ target: ''
+ nl2br: '0'
+ max_length: ''
+ word_boundary: '1'
+ ellipsis: '1'
+ more_link: '0'
+ more_link_text: ''
+ more_link_path: ''
+ strip_tags: '0'
+ trim: '0'
+ preserve_tags: ''
+ html: '0'
+ element_type: ''
+ element_class: ''
+ element_label_type: ''
+ element_label_class: ''
+ element_label_colon: '1'
+ element_wrapper_type: ''
+ element_wrapper_class: ''
+ element_default_classes: '1'
+ empty: ''
+ hide_empty: '0'
+ empty_zero: '0'
+ hide_alter_empty: '1'
+ plugin_id: aggregator_xss
+ filters: { }
+ sorts: { }
+ feed_1:
+ display_plugin: feed
+ id: feed_1
+ display_title: Feed
+ position: ''
+ display_options:
+ path: test-aggregator-items-feed
+ row:
+ type: aggregator_rss
+ options:
+ item_length: default
+label: test_aggregator_items
+module: views
+id: test_aggregator_items
+tag: ''
+langcode: en