' . t('About') . ''; $output .= '

' . t('The Aggregator module is an on-site syndicator and news reader that gathers and displays fresh content from RSS-, RDF-, and Atom-based feeds made available across the web. Thousands of sites (particularly news sites and blogs) publish their latest headlines in feeds, using a number of standardized XML-based formats. For more information, see the online handbook entry for Aggregator module.', array('@aggregator-module' => 'http://drupal.org/documentation/modules/aggregator', '@aggregator' => url('aggregator'))) . '

'; $output .= '

' . t('Uses') . '

'; $output .= '
'; $output .= '
' . t('Viewing feeds') . '
'; $output .= '
' . t('Feeds contain published content. Users view feed content in the main aggregator display, or by their source (usually via an RSS feed reader). The most recent content in a feed can be displayed as a block through the Blocks administration page.', array('@aggregator' => url('aggregator'), '@aggregator-sources' => url('aggregator/sources'), '@admin-block' => url('admin/structure/block'))) . '
'; $output .= '
' . t('Adding, editing, and deleting feeds') . '
'; $output .= '
' . t('Administrators can add, edit, and delete feeds, and choose how often to check each feed for newly updated items on the Feed aggregator administration page.', array('@feededit' => url('admin/config/services/aggregator'))) . '
'; $output .= '
' . t('OPML integration') . '
'; $output .= '
' . t('A machine-readable OPML file of all feeds is available. OPML is an XML-based file format used to share outline-structured information such as a list of RSS feeds. Feeds can also be imported via an OPML file.', array('@aggregator-opml' => url('aggregator/opml'), '@import-opml' => url('admin/config/services/aggregator'))) . '
'; $output .= '
' . t('Configuring cron') . '
'; $output .= '
' . t('A correctly configured cron maintenance task is required to update feeds automatically.', array('@cron' => 'http://drupal.org/cron')) . '
'; $output .= '
'; return $output; case 'admin/config/services/aggregator': $output = '

' . t('Thousands of sites (particularly news sites and blogs) publish their latest headlines and posts in feeds, using a number of standardized XML-based formats. Formats supported by the aggregator include RSS, RDF, and Atom.', array('@rss' => 'http://cyber.law.harvard.edu/rss/', '@rdf' => 'http://www.w3.org/RDF/', '@atom' => 'http://www.atomenabled.org')) . '

'; $output .= '

' . t('Current feeds are listed below, and new feeds may be added. For each feed, the latest items block may be enabled at the blocks administration page.', array('@addfeed' => url('admin/config/services/aggregator/add/feed'), '@block' => url('admin/structure/block'))) . '

'; return $output; case 'admin/config/services/aggregator/add/feed': return '

' . t('Add a feed in RSS, RDF or Atom format. A feed may only have one entry.') . '

'; case 'admin/config/services/aggregator/add/opml': return '

' . t('OPML is an XML format used to exchange multiple feeds between aggregators. A single OPML document may contain a collection of many feeds. Drupal can parse such a file and import all feeds at once, saving you the effort of adding them manually. You may either upload a local file from your computer or enter a URL where Drupal can download it.') . '

'; } } /** * Implements hook_theme(). */ function aggregator_theme() { return array( 'aggregator_feed_source' => array( 'variables' => array('aggregator_feed' => NULL, 'view_mode' => NULL), 'file' => 'aggregator.pages.inc', 'template' => 'aggregator-feed-source', ), 'aggregator_block_item' => array( 'variables' => array('item' => NULL, 'feed' => 0), 'template' => 'aggregator-block-item', ), 'aggregator_summary_items' => array( 'variables' => array('summary_items' => NULL, 'source' => NULL), 'file' => 'aggregator.pages.inc', 'template' => 'aggregator-summary-items', ), 'aggregator_summary_item' => array( 'variables' => array('aggregator_item' => NULL, 'view_mode' => NULL), 'file' => 'aggregator.pages.inc', 'template' => 'aggregator-summary-item', ), 'aggregator_item' => array( 'variables' => array('aggregator_item' => NULL, 'view_mode' => NULL), 'file' => 'aggregator.pages.inc', 'template' => 'aggregator-item', ), 'aggregator_page_opml' => array( 'variables' => array('feeds' => NULL), 'file' => 'aggregator.pages.inc', ), 'aggregator_page_rss' => array( 'variables' => array('feeds' => NULL), 'file' => 'aggregator.pages.inc', ), ); } /** * Implements hook_menu(). */ function aggregator_menu() { $items['admin/config/services/aggregator'] = array( 'title' => 'Feed aggregator', 'description' => "Configure which content your site aggregates from other sites, and how often it polls them.", 'route_name' => 'aggregator.admin_overview', 'weight' => 10, ); $items['admin/config/services/aggregator/remove/%aggregator_feed'] = array( 'title' => 'Remove items', 'route_name' => 'aggregator.feed_items_delete', ); $items['admin/config/services/aggregator/update/%aggregator_feed'] = array( 'title' => 'Update items', 'route_name' => 'aggregator.feed_refresh', ); $items['aggregator'] = array( 'title' => 'Feed aggregator', 'weight' => 5, 'route_name' => 'aggregator.page_last', ); $items['aggregator/sources'] = array( 'title' => 'Sources', 'route_name' => 'aggregator.sources', ); $items['aggregator/sources/%aggregator_feed'] = array( 'route_name' => 'aggregator.feed_view', ); $items['admin/config/services/aggregator/edit/feed/%aggregator_feed'] = array( 'title' => 'Edit feed', 'route_name' => 'aggregator.feed_edit', ); $items['admin/config/services/aggregator/delete/feed/%aggregator_feed'] = array( 'title' => 'Delete feed', 'route_name' => 'aggregator.feed_delete', ); return $items; } /** * Implements hook_permission(). */ function aggregator_permission() { return array( 'administer news feeds' => array( 'title' => t('Administer news feeds'), ), 'access news feeds' => array( 'title' => t('View news feeds'), ), ); } /** * Implements hook_cron(). * * Queues news feeds for updates once their refresh interval has elapsed. */ function aggregator_cron() { $result = db_query('SELECT fid FROM {aggregator_feed} WHERE queued = 0 AND checked + refresh < :time AND refresh <> :never', array( ':time' => REQUEST_TIME, ':never' => AGGREGATOR_CLEAR_NEVER )); $queue = \Drupal::queue('aggregator_feeds'); foreach ($result->fetchCol() as $fid) { $feed = aggregator_feed_load($fid); if ($queue->createItem($feed)) { // Add timestamp to avoid queueing item more than once. $feed->queued->value = REQUEST_TIME; $feed->save(); } } // Remove queued timestamp after 6 hours assuming the update has failed. db_update('aggregator_feed') ->fields(array('queued' => 0)) ->condition('queued', REQUEST_TIME - (3600 * 6), '<') ->execute(); } /** * Implements hook_queue_info(). */ function aggregator_queue_info() { $queues['aggregator_feeds'] = array( 'title' => t('Aggregator refresh'), 'worker callback' => 'aggregator_refresh', 'cron' => array( 'time' => 60, ), ); return $queues; } /** * Checks a news feed for new items. * * @param \Drupal\aggregator\Entity\Feed $feed * An object describing the feed to be refreshed. */ function aggregator_refresh(Feed $feed) { // Store feed URL to track changes. $feed_url = $feed->url->value; $config = \Drupal::config('aggregator.settings'); // Fetch the feed. $fetcher_manager = \Drupal::service('plugin.manager.aggregator.fetcher'); try { $success = $fetcher_manager->createInstance($config->get('fetcher'))->fetch($feed); } catch (PluginException $e) { $success = FALSE; watchdog_exception('aggregator', $e); } // Retrieve processor manager now. $processor_manager = \Drupal::service('plugin.manager.aggregator.processor'); // Store instances in an array so we dont have to instantiate new objects. $processor_instances = array(); foreach ($config->get('processors') as $processor) { try { $processor_instances[$processor] = $processor_manager->createInstance($processor); } catch (PluginException $e) { watchdog_exception('aggregator', $e); } } // We store the hash of feed data in the database. When refreshing a // feed we compare stored hash and new hash calculated from downloaded // data. If both are equal we say that feed is not updated. $hash = hash('sha256', $feed->source_string); if ($success && ($feed->hash->value != $hash)) { // Parse the feed. $parser_manager = \Drupal::service('plugin.manager.aggregator.parser'); try { if ($parser_manager->createInstance($config->get('parser'))->parse($feed)) { if (empty($feed->link->value)) { $feed->link->value = $feed->url->value; } $feed->hash->value = $hash; // Update feed with parsed data. $feed->save(); // Log if feed URL has changed. if ($feed->url->value != $feed_url) { watchdog('aggregator', 'Updated URL for feed %title to %url.', array('%title' => $feed->label(), '%url' => $feed->url->value)); } watchdog('aggregator', 'There is new syndicated content from %site.', array('%site' => $feed->label())); drupal_set_message(t('There is new syndicated content from %site.', array('%site' => $feed->label()))); // If there are items on the feed, let enabled processors process them. if (!empty($feed->items)) { foreach ($processor_instances as $instance) { $instance->process($feed); } } } } catch (PluginException $e) { watchdog_exception('aggregator', $e); } } else { drupal_set_message(t('There is no new syndicated content from %site.', array('%site' => $feed->label()))); } // Regardless of successful or not, indicate that this feed has been checked. $feed->checked->value = REQUEST_TIME; $feed->queued->value = 0; $feed->save(); // Processing is done, call postProcess on enabled processors. foreach ($processor_instances as $instance) { $instance->postProcess($feed); } } /** * Loads an aggregator feed. * * @param int $fid * The feed id. * * @return \Drupal\aggregator\Entity\Feed * An object describing the feed. */ function aggregator_feed_load($fid) { return entity_load('aggregator_feed', $fid); } /** * Prepares variables for individual feed item block templates. * * Default template: aggregator-block-item.html.twig. * * @param array $variables * An associative array containing: * - item: The item to be displayed. * - feed: Not used. */ function template_preprocess_aggregator_block_item(&$variables) { // Display the external link to the item. $variables['url'] = check_url($variables['item']->link); $variables['title'] = check_plain($variables['item']->title); } /** * Renders the HTML content safely, as allowed. * * @param $value * The content to be filtered. * * @return * The filtered content. */ function aggregator_filter_xss($value) { return filter_xss($value, preg_split('/\s+|<|>/', \Drupal::config('aggregator.settings')->get('items.allowed_html'), -1, PREG_SPLIT_NO_EMPTY)); } /** * Implements hook_preprocess_HOOK() for block templates. */ function aggregator_preprocess_block(&$variables) { if ($variables['configuration']['module'] == 'aggregator') { $variables['attributes']['role'] = 'complementary'; } }