Drupal PHP Cross Reference Content Management Systems

Source: /modules/aggregator/aggregator.module - 782 lines - 28677 bytes - Summary - Text - Print

   1  <?php
   2  
   3  /**
   4   * @file
   5   * Used to aggregate syndicated content (RSS, RDF, and Atom).
   6   */
   7  
   8  /**
   9   * Denotes that a feed's items should never expire.
  10   */
  11  define('AGGREGATOR_CLEAR_NEVER', 0);
  12  
  13  /**
  14   * Implements hook_help().
  15   */
  16  function aggregator_help($path, $arg) {
  17    switch ($path) {
  18      case 'admin/help#aggregator':
  19        $output = '';
  20        $output .= '<h3>' . t('About') . '</h3>';
  21        $output .= '<p>' . 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 <a href="@aggregator-module">Aggregator module</a>.', array('@aggregator-module' => 'http://drupal.org/documentation/modules/aggregator', '@aggregator' => url('aggregator'))) . '</p>';
  22        $output .= '<h3>' . t('Uses') . '</h3>';
  23        $output .= '<dl>';
  24        $output .= '<dt>' . t('Viewing feeds') . '</dt>';
  25        $output .= '<dd>' . t('Feeds contain published content, and may be grouped in categories, generally by topic. Users view feed content in the <a href="@aggregator">main aggregator display</a>, or by <a href="@aggregator-sources">their source</a> (usually via an RSS feed reader). The most recent content in a feed or category can be displayed as a block through the <a href="@admin-block">Blocks administration page</a>.', array('@aggregator' => url('aggregator'), '@aggregator-sources' => url('aggregator/sources'), '@admin-block' => url('admin/structure/block'))) . '</a></dd>';
  26        $output .= '<dt>' . t('Adding, editing, and deleting feeds') . '</dt>';
  27        $output .= '<dd>' . t('Administrators can add, edit, and delete feeds, and choose how often to check each feed for newly updated items on the <a href="@feededit">Feed aggregator administration page</a>.', array('@feededit' => url('admin/config/services/aggregator'))) . '</dd>';
  28        $output .= '<dt>' . t('OPML integration') . '</dt>';
  29        $output .= '<dd>' . t('A <a href="@aggregator-opml">machine-readable OPML file</a> 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 <a href="@import-opml">imported via an OPML file</a>.', array('@aggregator-opml' => url('aggregator/opml'), '@import-opml' => url('admin/config/services/aggregator'))) . '</dd>';
  30        $output .= '<dt>' . t('Configuring cron') . '</dt>';
  31        $output .= '<dd>' . t('A correctly configured <a href="@cron">cron maintenance task</a> is required to update feeds automatically.', array('@cron' => 'http://drupal.org/cron')) . '</dd>';
  32        $output .= '</dl>';
  33        return $output;
  34      case 'admin/config/services/aggregator':
  35        $output = '<p>' . 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 <a href="@rss">RSS</a>, <a href="@rdf">RDF</a>, and <a href="@atom">Atom</a>.', array('@rss' => 'http://cyber.law.harvard.edu/rss/', '@rdf' => 'http://www.w3.org/RDF/', '@atom' => 'http://www.atomenabled.org')) . '</p>';
  36        $output .= '<p>' . t('Current feeds are listed below, and <a href="@addfeed">new feeds may be added</a>. For each feed or feed category, the <em>latest items</em> block may be enabled at the <a href="@block">blocks administration page</a>.', array('@addfeed' => url('admin/config/services/aggregator/add/feed'), '@block' => url('admin/structure/block'))) . '</p>';
  37        return $output;
  38      case 'admin/config/services/aggregator/add/feed':
  39        return '<p>' . t('Add a feed in RSS, RDF or Atom format. A feed may only have one entry.') . '</p>';
  40      case 'admin/config/services/aggregator/add/category':
  41        return '<p>' . t('Categories allow feed items from different feeds to be grouped together. For example, several sport-related feeds may belong to a category named <em>Sports</em>. Feed items may be grouped automatically (by selecting a category when creating or editing a feed) or manually (via the <em>Categorize</em> page available from feed item listings). Each category provides its own feed page and block.') . '</p>';
  42      case 'admin/config/services/aggregator/add/opml':
  43        return '<p>' . t('<acronym title="Outline Processor Markup Language">OPML</acronym> 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.') . '</p>';
  44    }
  45  }
  46  
  47  /**
  48   * Implements hook_theme().
  49   */
  50  function aggregator_theme() {
  51    return array(
  52      'aggregator_wrapper' => array(
  53        'variables' => array('content' => NULL),
  54        'file' => 'aggregator.pages.inc',
  55        'template' => 'aggregator-wrapper',
  56      ),
  57      'aggregator_categorize_items' => array(
  58        'render element' => 'form',
  59        'file' => 'aggregator.pages.inc',
  60      ),
  61      'aggregator_feed_source' => array(
  62        'variables' => array('feed' => NULL),
  63        'file' => 'aggregator.pages.inc',
  64        'template' => 'aggregator-feed-source',
  65      ),
  66      'aggregator_block_item' => array(
  67        'variables' => array('item' => NULL, 'feed' => 0),
  68      ),
  69      'aggregator_summary_items' => array(
  70        'variables' => array('summary_items' => NULL, 'source' => NULL),
  71        'file' => 'aggregator.pages.inc',
  72        'template' => 'aggregator-summary-items',
  73      ),
  74      'aggregator_summary_item' => array(
  75        'variables' => array('item' => NULL),
  76        'file' => 'aggregator.pages.inc',
  77        'template' => 'aggregator-summary-item',
  78      ),
  79      'aggregator_item' => array(
  80        'variables' => array('item' => NULL),
  81        'file' => 'aggregator.pages.inc',
  82        'template' => 'aggregator-item',
  83      ),
  84      'aggregator_page_opml' => array(
  85        'variables' => array('feeds' => NULL),
  86        'file' => 'aggregator.pages.inc',
  87      ),
  88      'aggregator_page_rss' => array(
  89        'variables' => array('feeds' => NULL, 'category' => NULL),
  90        'file' => 'aggregator.pages.inc',
  91      ),
  92    );
  93  }
  94  
  95  /**
  96   * Implements hook_menu().
  97   */
  98  function aggregator_menu() {
  99    $items['admin/config/services/aggregator'] = array(
 100      'title' => 'Feed aggregator',
 101      'description' => "Configure which content your site aggregates from other sites, how often it polls them, and how they're categorized.",
 102      'page callback' => 'aggregator_admin_overview',
 103      'access arguments' => array('administer news feeds'),
 104      'weight' => 10,
 105      'file' => 'aggregator.admin.inc',
 106    );
 107    $items['admin/config/services/aggregator/add/feed'] = array(
 108      'title' => 'Add feed',
 109      'page callback' => 'drupal_get_form',
 110      'page arguments' => array('aggregator_form_feed'),
 111      'access arguments' => array('administer news feeds'),
 112      'type' => MENU_LOCAL_ACTION,
 113      'file' => 'aggregator.admin.inc',
 114    );
 115    $items['admin/config/services/aggregator/add/category'] = array(
 116      'title' => 'Add category',
 117      'page callback' => 'drupal_get_form',
 118      'page arguments' => array('aggregator_form_category'),
 119      'access arguments' => array('administer news feeds'),
 120      'type' => MENU_LOCAL_ACTION,
 121      'file' => 'aggregator.admin.inc',
 122    );
 123    $items['admin/config/services/aggregator/add/opml'] = array(
 124      'title' => 'Import OPML',
 125      'page callback' => 'drupal_get_form',
 126      'page arguments' => array('aggregator_form_opml'),
 127      'access arguments' => array('administer news feeds'),
 128      'type' => MENU_LOCAL_ACTION,
 129      'file' => 'aggregator.admin.inc',
 130    );
 131    $items['admin/config/services/aggregator/remove/%aggregator_feed'] = array(
 132      'title' => 'Remove items',
 133      'page callback' => 'drupal_get_form',
 134      'page arguments' => array('aggregator_admin_remove_feed', 5),
 135      'access arguments' => array('administer news feeds'),
 136      'file' => 'aggregator.admin.inc',
 137    );
 138    $items['admin/config/services/aggregator/update/%aggregator_feed'] = array(
 139      'title' => 'Update items',
 140      'page callback' => 'aggregator_admin_refresh_feed',
 141      'page arguments' => array(5),
 142      'access arguments' => array('administer news feeds'),
 143      'file' => 'aggregator.admin.inc',
 144    );
 145    $items['admin/config/services/aggregator/list'] = array(
 146      'title' => 'List',
 147      'type' => MENU_DEFAULT_LOCAL_TASK,
 148      'weight' => -10,
 149    );
 150    $items['admin/config/services/aggregator/settings'] = array(
 151      'title' => 'Settings',
 152      'description' => 'Configure the behavior of the feed aggregator, including when to discard feed items and how to present feed items and categories.',
 153      'page callback' => 'drupal_get_form',
 154      'page arguments' => array('aggregator_admin_form'),
 155      'access arguments' => array('administer news feeds'),
 156      'type' => MENU_LOCAL_TASK,
 157      'file' => 'aggregator.admin.inc',
 158    );
 159    $items['aggregator'] = array(
 160      'title' => 'Feed aggregator',
 161      'page callback' => 'aggregator_page_last',
 162      'access arguments' => array('access news feeds'),
 163      'weight' => 5,
 164      'file' => 'aggregator.pages.inc',
 165    );
 166    $items['aggregator/sources'] = array(
 167      'title' => 'Sources',
 168      'page callback' => 'aggregator_page_sources',
 169      'access arguments' => array('access news feeds'),
 170      'file' => 'aggregator.pages.inc',
 171    );
 172    $items['aggregator/categories'] = array(
 173      'title' => 'Categories',
 174      'page callback' => 'aggregator_page_categories',
 175      'access callback' => '_aggregator_has_categories',
 176      'file' => 'aggregator.pages.inc',
 177    );
 178    $items['aggregator/rss'] = array(
 179      'title' => 'RSS feed',
 180      'page callback' => 'aggregator_page_rss',
 181      'access arguments' => array('access news feeds'),
 182      'type' => MENU_CALLBACK,
 183      'file' => 'aggregator.pages.inc',
 184    );
 185    $items['aggregator/opml'] = array(
 186      'title' => 'OPML feed',
 187      'page callback' => 'aggregator_page_opml',
 188      'access arguments' => array('access news feeds'),
 189      'type' => MENU_CALLBACK,
 190      'file' => 'aggregator.pages.inc',
 191    );
 192    $items['aggregator/categories/%aggregator_category'] = array(
 193      'title callback' => '_aggregator_category_title',
 194      'title arguments' => array(2),
 195      'page callback' => 'aggregator_page_category',
 196      'page arguments' => array(2),
 197      'access arguments' => array('access news feeds'),
 198      'file' => 'aggregator.pages.inc',
 199    );
 200    $items['aggregator/categories/%aggregator_category/view'] = array(
 201      'title' => 'View',
 202      'type' => MENU_DEFAULT_LOCAL_TASK,
 203      'weight' => -10,
 204    );
 205    $items['aggregator/categories/%aggregator_category/categorize'] = array(
 206      'title' => 'Categorize',
 207      'page callback' => 'drupal_get_form',
 208      'page arguments' => array('aggregator_page_category_form', 2),
 209      'access arguments' => array('administer news feeds'),
 210      'type' => MENU_LOCAL_TASK,
 211      'file' => 'aggregator.pages.inc',
 212    );
 213    $items['aggregator/categories/%aggregator_category/configure'] = array(
 214      'title' => 'Configure',
 215      'page callback' => 'drupal_get_form',
 216      'page arguments' => array('aggregator_form_category', 2),
 217      'access arguments' => array('administer news feeds'),
 218      'type' => MENU_LOCAL_TASK,
 219      'weight' => 1,
 220      'file' => 'aggregator.admin.inc',
 221    );
 222    $items['aggregator/sources/%aggregator_feed'] = array(
 223      'page callback' => 'aggregator_page_source',
 224      'page arguments' => array(2),
 225      'access arguments' => array('access news feeds'),
 226      'file' => 'aggregator.pages.inc',
 227    );
 228    $items['aggregator/sources/%aggregator_feed/view'] = array(
 229      'title' => 'View',
 230      'type' => MENU_DEFAULT_LOCAL_TASK,
 231      'weight' => -10,
 232    );
 233    $items['aggregator/sources/%aggregator_feed/categorize'] = array(
 234      'title' => 'Categorize',
 235      'page callback' => 'drupal_get_form',
 236      'page arguments' => array('aggregator_page_source_form', 2),
 237      'access arguments' => array('administer news feeds'),
 238      'type' => MENU_LOCAL_TASK,
 239      'file' => 'aggregator.pages.inc',
 240    );
 241    $items['aggregator/sources/%aggregator_feed/configure'] = array(
 242      'title' => 'Configure',
 243      'page callback' => 'drupal_get_form',
 244      'page arguments' => array('aggregator_form_feed', 2),
 245      'access arguments' => array('administer news feeds'),
 246      'type' => MENU_LOCAL_TASK,
 247      'weight' => 1,
 248      'file' => 'aggregator.admin.inc',
 249    );
 250    $items['admin/config/services/aggregator/edit/feed/%aggregator_feed'] = array(
 251      'title' => 'Edit feed',
 252      'page callback' => 'drupal_get_form',
 253      'page arguments' => array('aggregator_form_feed', 6),
 254      'access arguments' => array('administer news feeds'),
 255      'file' => 'aggregator.admin.inc',
 256    );
 257    $items['admin/config/services/aggregator/edit/category/%aggregator_category'] = array(
 258      'title' => 'Edit category',
 259      'page callback' => 'drupal_get_form',
 260      'page arguments' => array('aggregator_form_category', 6),
 261      'access arguments' => array('administer news feeds'),
 262      'file' => 'aggregator.admin.inc',
 263    );
 264  
 265    return $items;
 266  }
 267  
 268  /**
 269   * Title callback: Returns a title for aggregator category pages.
 270   *
 271   * @param $category
 272   *   An aggregator category.
 273   *
 274   * @return
 275   *   A string with the aggregator category title.
 276   */
 277  function _aggregator_category_title($category) {
 278    return $category['title'];
 279  }
 280  
 281  /**
 282   * Determines whether there are any aggregator categories.
 283   *
 284   * @return
 285   *   TRUE if there is at least one category and the user has access to them;
 286   *   FALSE otherwise.
 287   */
 288  function _aggregator_has_categories() {
 289    return user_access('access news feeds') && (bool) db_query_range('SELECT 1 FROM {aggregator_category}', 0, 1)->fetchField();
 290  }
 291  
 292  /**
 293   * Implements hook_permission().
 294   */
 295  function aggregator_permission() {
 296    return array(
 297      'administer news feeds' => array(
 298        'title' => t('Administer news feeds'),
 299      ),
 300      'access news feeds' => array(
 301        'title' => t('View news feeds'),
 302      ),
 303    );
 304  }
 305  
 306  /**
 307   * Implements hook_cron().
 308   *
 309   * Queues news feeds for updates once their refresh interval has elapsed.
 310   */
 311  function aggregator_cron() {
 312    $result = db_query('SELECT * FROM {aggregator_feed} WHERE queued = 0 AND checked + refresh < :time AND refresh <> :never', array(
 313      ':time' => REQUEST_TIME,
 314      ':never' => AGGREGATOR_CLEAR_NEVER
 315    ));
 316    $queue = DrupalQueue::get('aggregator_feeds');
 317    foreach ($result as $feed) {
 318      if ($queue->createItem($feed)) {
 319        // Add timestamp to avoid queueing item more than once.
 320        db_update('aggregator_feed')
 321          ->fields(array('queued' => REQUEST_TIME))
 322          ->condition('fid', $feed->fid)
 323          ->execute();
 324      }
 325    }
 326  
 327    // Remove queued timestamp after 6 hours assuming the update has failed.
 328    db_update('aggregator_feed')
 329      ->fields(array('queued' => 0))
 330      ->condition('queued', REQUEST_TIME - (3600 * 6), '<')
 331      ->execute();
 332  }
 333  
 334  /**
 335   * Implements hook_cron_queue_info().
 336   */
 337  function aggregator_cron_queue_info() {
 338    $queues['aggregator_feeds'] = array(
 339      'worker callback' => 'aggregator_refresh',
 340      'time' => 60,
 341    );
 342    return $queues;
 343  }
 344  
 345  /**
 346   * Implements hook_block_info().
 347   */
 348  function aggregator_block_info() {
 349    $blocks = array();
 350    $result = db_query('SELECT cid, title FROM {aggregator_category} ORDER BY title');
 351    foreach ($result as $category) {
 352      $blocks['category-' . $category->cid]['info'] = t('!title category latest items', array('!title' => $category->title));
 353    }
 354    $result = db_query('SELECT fid, title FROM {aggregator_feed} WHERE block <> 0 ORDER BY fid');
 355    foreach ($result as $feed) {
 356      $blocks['feed-' . $feed->fid]['info'] = t('!title feed latest items', array('!title' => $feed->title));
 357    }
 358    return $blocks;
 359  }
 360  
 361  /**
 362   * Implements hook_block_configure().
 363   */
 364  function aggregator_block_configure($delta = '') {
 365    list($type, $id) = explode('-', $delta);
 366    if ($type == 'category') {
 367      $value = db_query('SELECT block FROM {aggregator_category} WHERE cid = :cid', array(':cid' => $id))->fetchField();
 368      $form['block'] = array(
 369        '#type' => 'select',
 370        '#title' => t('Number of news items in block'),
 371        '#default_value' => $value,
 372        '#options' => drupal_map_assoc(range(2, 20)),
 373      );
 374      return $form;
 375    }
 376  }
 377  
 378  /**
 379   * Implements hook_block_save().
 380   */
 381  function aggregator_block_save($delta = '', $edit = array()) {
 382    list($type, $id) = explode('-', $delta);
 383    if ($type == 'category') {
 384      db_update('aggregator_category')
 385        ->fields(array('block' => $edit['block']))
 386        ->condition('cid', $id)
 387        ->execute();
 388    }
 389  }
 390  
 391  /**
 392   * Implements hook_block_view().
 393   *
 394   * Generates blocks for the latest news items in each category and feed.
 395   */
 396  function aggregator_block_view($delta = '') {
 397    if (user_access('access news feeds')) {
 398      $block = array();
 399      list($type, $id) = explode('-', $delta);
 400      $result = FALSE;
 401      switch ($type) {
 402        case 'feed':
 403          if ($feed = db_query('SELECT fid, title, block FROM {aggregator_feed} WHERE block <> 0 AND fid = :fid', array(':fid' => $id))->fetchObject()) {
 404            $block['subject'] = check_plain($feed->title);
 405            $result = db_query_range("SELECT * FROM {aggregator_item} WHERE fid = :fid ORDER BY timestamp DESC, iid DESC", 0, $feed->block, array(':fid' => $id));
 406            $read_more = theme('more_link', array('url' => 'aggregator/sources/' . $feed->fid, 'title' => t("View this feed's recent news.")));
 407          }
 408          break;
 409  
 410        case 'category':
 411          if ($category = db_query('SELECT cid, title, block FROM {aggregator_category} WHERE cid = :cid', array(':cid' => $id))->fetchObject()) {
 412            $block['subject'] = check_plain($category->title);
 413            $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));
 414            $read_more = theme('more_link', array('url' => 'aggregator/categories/' . $category->cid, 'title' => t("View this category's recent news.")));
 415          }
 416          break;
 417      }
 418  
 419      $items = array();
 420      if (!empty($result)) {
 421        foreach ($result as $item) {
 422          $items[] = theme('aggregator_block_item', array('item' => $item));
 423        }
 424      }
 425  
 426      // Only display the block if there are items to show.
 427      if (count($items) > 0) {
 428        $block['content'] = theme('item_list', array('items' => $items)) . $read_more;
 429      }
 430      return $block;
 431    }
 432  }
 433  
 434  /**
 435   * Adds/edits/deletes aggregator categories.
 436   *
 437   * @param $edit
 438   *   An associative array describing the category to be added/edited/deleted.
 439   */
 440  function aggregator_save_category($edit) {
 441    $link_path = 'aggregator/categories/';
 442    if (!empty($edit['cid'])) {
 443      $link_path .= $edit['cid'];
 444      if (!empty($edit['title'])) {
 445        db_merge('aggregator_category')
 446          ->key(array('cid' => $edit['cid']))
 447          ->fields(array(
 448            'title' => $edit['title'],
 449            'description' => $edit['description'],
 450          ))
 451          ->execute();
 452        $op = 'update';
 453      }
 454      else {
 455        db_delete('aggregator_category')
 456          ->condition('cid', $edit['cid'])
 457          ->execute();
 458        // Make sure there is no active block for this category.
 459        if (module_exists('block')) {
 460          db_delete('block')
 461            ->condition('module', 'aggregator')
 462            ->condition('delta', 'category-' . $edit['cid'])
 463            ->execute();
 464        }
 465        $edit['title'] = '';
 466        $op = 'delete';
 467      }
 468    }
 469    elseif (!empty($edit['title'])) {
 470      // A single unique id for bundles and feeds, to use in blocks.
 471      $link_path .= db_insert('aggregator_category')
 472        ->fields(array(
 473          'title' => $edit['title'],
 474          'description' => $edit['description'],
 475          'block' => 5,
 476        ))
 477        ->execute();
 478      $op = 'insert';
 479    }
 480    if (isset($op)) {
 481      menu_link_maintain('aggregator', $op, $link_path, $edit['title']);
 482    }
 483  }
 484  
 485  /**
 486   * Add/edit/delete an aggregator feed.
 487   *
 488   * @param $edit
 489   *   An associative array describing the feed to be added/edited/deleted.
 490   */
 491  function aggregator_save_feed($edit) {
 492    if (!empty($edit['fid'])) {
 493      // An existing feed is being modified, delete the category listings.
 494      db_delete('aggregator_category_feed')
 495        ->condition('fid', $edit['fid'])
 496        ->execute();
 497    }
 498    if (!empty($edit['fid']) && !empty($edit['title'])) {
 499      db_update('aggregator_feed')
 500        ->condition('fid', $edit['fid'])
 501        ->fields(array(
 502          'title' => $edit['title'],
 503          'url' => $edit['url'],
 504          'refresh' => $edit['refresh'],
 505          'block' => $edit['block'],
 506        ))
 507        ->execute();
 508    }
 509    elseif (!empty($edit['fid'])) {
 510      $iids = db_query('SELECT iid FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $edit['fid']))->fetchCol();
 511      if ($iids) {
 512        db_delete('aggregator_category_item')
 513          ->condition('iid', $iids, 'IN')
 514          ->execute();
 515      }
 516      db_delete('aggregator_feed')->
 517        condition('fid', $edit['fid'])
 518        ->execute();
 519      db_delete('aggregator_item')
 520        ->condition('fid', $edit['fid'])
 521        ->execute();
 522      // Make sure there is no active block for this feed.
 523      if (module_exists('block')) {
 524        db_delete('block')
 525          ->condition('module', 'aggregator')
 526          ->condition('delta', 'feed-' . $edit['fid'])
 527          ->execute();
 528      }
 529    }
 530    elseif (!empty($edit['title'])) {
 531      $edit['fid'] = db_insert('aggregator_feed')
 532        ->fields(array(
 533          'title' => $edit['title'],
 534          'url' => $edit['url'],
 535          'refresh' => $edit['refresh'],
 536          'block' => $edit['block'],
 537          'link' => '',
 538          'description' => '',
 539          'image' => '',
 540        ))
 541        ->execute();
 542  
 543    }
 544    if (!empty($edit['title'])) {
 545      // The feed is being saved, save the categories as well.
 546      if (!empty($edit['category'])) {
 547        foreach ($edit['category'] as $cid => $value) {
 548          if ($value) {
 549            db_insert('aggregator_category_feed')
 550              ->fields(array(
 551                'fid' => $edit['fid'],
 552                'cid' => $cid,
 553              ))
 554              ->execute();
 555          }
 556        }
 557      }
 558    }
 559  }
 560  
 561  /**
 562   * Removes all items from a feed.
 563   *
 564   * @param $feed
 565   *   An object describing the feed to be cleared.
 566   */
 567  function aggregator_remove($feed) {
 568    _aggregator_get_variables();
 569    // Call hook_aggregator_remove() on all modules.
 570    module_invoke_all('aggregator_remove', $feed);
 571    // Reset feed.
 572    db_update('aggregator_feed')
 573      ->condition('fid', $feed->fid)
 574      ->fields(array(
 575        'checked' => 0,
 576        'hash' => '',
 577        'etag' => '',
 578        'modified' => 0,
 579      ))
 580      ->execute();
 581  }
 582  
 583  /**
 584   * Gets the fetcher, parser, and processors.
 585   *
 586   * @return
 587   *   An array containing the fetcher, parser, and processors.
 588   */
 589  function _aggregator_get_variables() {
 590    // Fetch the feed.
 591    $fetcher = variable_get('aggregator_fetcher', 'aggregator');
 592    if ($fetcher == 'aggregator') {
 593      include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'aggregator') . '/aggregator.fetcher.inc';
 594    }
 595    $parser = variable_get('aggregator_parser', 'aggregator');
 596    if ($parser == 'aggregator') {
 597      include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'aggregator') . '/aggregator.parser.inc';
 598    }
 599    $processors = variable_get('aggregator_processors', array('aggregator'));
 600    if (in_array('aggregator', $processors)) {
 601      include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'aggregator') . '/aggregator.processor.inc';
 602    }
 603    return array($fetcher, $parser, $processors);
 604  }
 605  
 606  /**
 607   * Checks a news feed for new items.
 608   *
 609   * @param $feed
 610   *   An object describing the feed to be refreshed.
 611   */
 612  function aggregator_refresh($feed) {
 613    // Store feed URL to track changes.
 614    $feed_url = $feed->url;
 615  
 616    // Fetch the feed.
 617    list($fetcher, $parser, $processors) = _aggregator_get_variables();
 618    $success = module_invoke($fetcher, 'aggregator_fetch', $feed);
 619  
 620    // We store the hash of feed data in the database. When refreshing a
 621    // feed we compare stored hash and new hash calculated from downloaded
 622    // data. If both are equal we say that feed is not updated.
 623    $hash = hash('sha256', $feed->source_string);
 624  
 625    if ($success && ($feed->hash != $hash)) {
 626      // Parse the feed.
 627      if (module_invoke($parser, 'aggregator_parse', $feed)) {
 628        // Update feed with parsed data.
 629        db_merge('aggregator_feed')
 630          ->key(array('fid' => $feed->fid))
 631          ->fields(array(
 632            'url' => $feed->url,
 633            'link' => empty($feed->link) ? $feed->url : $feed->link,
 634            'description' => empty($feed->description) ? '' : $feed->description,
 635            'image' => empty($feed->image) ? '' : $feed->image,
 636            'hash' => $hash,
 637            'etag' => empty($feed->etag) ? '' : $feed->etag,
 638            'modified' => empty($feed->modified) ? 0 : $feed->modified,
 639          ))
 640          ->execute();
 641  
 642        // Log if feed URL has changed.
 643        if ($feed->url != $feed_url) {
 644          watchdog('aggregator', 'Updated URL for feed %title to %url.', array('%title' => $feed->title, '%url' => $feed->url));
 645        }
 646  
 647        watchdog('aggregator', 'There is new syndicated content from %site.', array('%site' => $feed->title));
 648        drupal_set_message(t('There is new syndicated content from %site.', array('%site' => $feed->title)));
 649  
 650        // If there are items on the feed, let all enabled processors do their work on it.
 651        if (@count($feed->items)) {
 652          foreach ($processors as $processor) {
 653            module_invoke($processor, 'aggregator_process', $feed);
 654          }
 655        }
 656      }
 657    }
 658    else {
 659      drupal_set_message(t('There is no new syndicated content from %site.', array('%site' => $feed->title)));
 660    }
 661  
 662    // Regardless of successful or not, indicate that this feed has been checked.
 663    db_update('aggregator_feed')
 664      ->fields(array('checked' => REQUEST_TIME, 'queued' => 0))
 665      ->condition('fid', $feed->fid)
 666      ->execute();
 667  
 668    // Expire old feed items.
 669    if (function_exists('aggregator_expire')) {
 670      aggregator_expire($feed);
 671    }
 672  }
 673  
 674  /**
 675   * Loads an aggregator feed.
 676   *
 677   * @param $fid
 678   *   The feed id.
 679   *
 680   * @return
 681   *   An object describing the feed.
 682   */
 683  function aggregator_feed_load($fid) {
 684    $feeds = &drupal_static(__FUNCTION__);
 685    if (!isset($feeds[$fid])) {
 686      $feeds[$fid] = db_query('SELECT * FROM {aggregator_feed} WHERE fid = :fid', array(':fid' => $fid))->fetchObject();
 687    }
 688  
 689    return $feeds[$fid];
 690  }
 691  
 692  /**
 693   * Loads an aggregator category.
 694   *
 695   * @param $cid
 696   *   The category id.
 697   *
 698   * @return
 699   *   An associative array describing the category.
 700   */
 701  function aggregator_category_load($cid) {
 702    $categories = &drupal_static(__FUNCTION__);
 703    if (!isset($categories[$cid])) {
 704      $categories[$cid] = db_query('SELECT * FROM {aggregator_category} WHERE cid = :cid', array(':cid' => $cid))->fetchAssoc();
 705    }
 706  
 707    return $categories[$cid];
 708  }
 709  
 710  /**
 711   * Returns HTML for an individual feed item for display in the block.
 712   *
 713   * @param $variables
 714   *   An associative array containing:
 715   *   - item: The item to be displayed.
 716   *   - feed: Not used.
 717   *
 718   * @ingroup themeable
 719   */
 720  function theme_aggregator_block_item($variables) {
 721    // Display the external link to the item.
 722    return '<a href="' . check_url($variables['item']->link) . '">' . check_plain($variables['item']->title) . "</a>\n";
 723  }
 724  
 725  /**
 726   * Renders the HTML content safely, as allowed.
 727   *
 728   * @param $value
 729   *   The content to be filtered.
 730   *
 731   * @return
 732   *   The filtered content.
 733   */
 734  function aggregator_filter_xss($value) {
 735    return filter_xss($value, preg_split('/\s+|<|>/', variable_get('aggregator_allowed_html_tags', '<a> <b> <br> <dd> <dl> <dt> <em> <i> <li> <ol> <p> <strong> <u> <ul>'), -1, PREG_SPLIT_NO_EMPTY));
 736  }
 737  
 738  /**
 739   * Checks and sanitizes the aggregator configuration.
 740   *
 741   * Goes through all fetchers, parsers and processors and checks whether they
 742   * are available. If one is missing, resets to standard configuration.
 743   *
 744   * @return
 745   *   TRUE if this function resets the configuration; FALSE if not.
 746   */
 747  function aggregator_sanitize_configuration() {
 748    $reset = FALSE;
 749    list($fetcher, $parser, $processors) = _aggregator_get_variables();
 750    if (!module_exists($fetcher)) {
 751      $reset = TRUE;
 752    }
 753    if (!module_exists($parser)) {
 754      $reset = TRUE;
 755    }
 756    foreach ($processors as $processor) {
 757      if (!module_exists($processor)) {
 758        $reset = TRUE;
 759        break;
 760      }
 761    }
 762    if ($reset) {
 763      variable_del('aggregator_fetcher');
 764      variable_del('aggregator_parser');
 765      variable_del('aggregator_processors');
 766      return TRUE;
 767    }
 768    return FALSE;
 769  }
 770  
 771  /**
 772   * Helper function for drupal_map_assoc.
 773   *
 774   * @param $count
 775   *   Items count.
 776   *
 777   * @return
 778   *   A string that is plural-formatted as "@count items".
 779   */
 780  function _aggregator_items($count) {
 781    return format_plural($count, '1 item', '@count items');
 782  }

title

Description

title

Description

title

Description

title

title

Body