You are here

feedapi_aggregator.module in FeedAPI 5

File

feedapi_aggregator/feedapi_aggregator.module
View source
<?php

/**
 * @abstract This module emulates aggregator module with the feedapi framework.
 */

/**
 * Implementation of hook_help().
 */
function feedapi_aggregator_help($section) {
  switch ($section) {
    case 'admin/help#feedapi_aggregator':
      return t('Emulates the Drupal core aggregator with the FeedAPI module.');
    case 'feedapi/full_name':
      return t('FeedAPI Aggregator - classic style aggregator items');
    case 'aggregator/categories':
      if (user_access('administer feedapi')) {
        return t(' Go to !category-admin for adding or removing categories.', array(
          '!category-admin' => l(t('category management'), 'admin/content/feed/category'),
        ));
      }
  }
}

/**
 * Implementation of hook_menu().
 */
function feedapi_aggregator_menu($may_cache) {
  $items = array();
  $edit = user_access('administer feedapi');
  $view = user_access('access news feeds');
  if ($may_cache) {
    $items[] = array(
      'path' => 'admin/content/feed/category',
      'title' => t('Categories'),
      'callback' => 'feedapi_aggregator_category_view',
      'access' => $edit,
      'type' => MENU_LOCAL_TASK,
    );
    $items[] = array(
      'path' => 'admin/content/feed/category/list',
      'title' => t('List'),
      'type' => MENU_DEFAULT_LOCAL_TASK,
      'weight' => -15,
    );
    $items[] = array(
      'path' => 'admin/content/feed/category/add',
      'title' => t('Add category'),
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'feedapi_aggregator_form_category',
      ),
      'access' => $edit,
      'type' => MENU_LOCAL_TASK,
    );
    $items[] = array(
      'path' => 'aggregator',
      'title' => t('News aggregator'),
      'callback' => 'feedapi_aggregator_page_last',
      'access' => $view,
      'weight' => 5,
    );
    $items[] = array(
      'path' => 'aggregator/sources',
      'title' => t('Sources'),
      'callback' => 'feedapi_aggregator_page_sources',
      'access' => $view,
    );
    $items[] = array(
      'path' => 'aggregator/categories',
      'title' => t('Categories'),
      'callback' => 'feedapi_aggregator_page_categories',
      'access' => $view,
    );
    $items[] = array(
      'path' => 'aggregator/rss',
      'title' => t('RSS feed'),
      'callback' => 'feedapi_aggregator_page_rss',
      'access' => $view,
      'type' => MENU_CALLBACK,
    );
    $result = db_query('SELECT title, cid FROM {feedapi_aggregator_category} ORDER BY title');
    while ($category = db_fetch_array($result)) {
      $items[] = array(
        'path' => 'aggregator/categories/' . $category['cid'],
        'title' => $category['title'],
        'callback' => 'feedapi_aggregator_page_category',
        'access' => $view,
      );
    }
    $items[] = array(
      'path' => 'admin/settings/feedapi_aggregator',
      'title' => t('FeedAPI Aggregator'),
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'feedapi_aggregator_admin_settings',
      ),
      'access' => user_access('administer feedapi'),
    );
  }
  else {

    // Add the CSS for this module
    // We put this in !$may_cache so it's only added once per request
    drupal_add_css(drupal_get_path('module', 'feedapi_aggregator') . '/aggregator.css');
    if (arg(0) == 'aggregator' && is_numeric(arg(2))) {
      if (arg(1) == 'sources') {
        $feed = new stdClass();
        $feed->nid = arg(2);
        $node = node_load(arg(2));
        $feed = $node->feed;
        if ($feed) {
          $items[] = array(
            'path' => 'aggregator/sources/' . $feed->nid,
            'title' => $feed->title,
            'callback' => 'feedapi_aggregator_page_source',
            'access' => $view,
            'type' => MENU_CALLBACK,
          );
          $items[] = array(
            'path' => 'aggregator/sources/' . $feed->nid . '/view',
            'title' => t('View'),
            'type' => MENU_DEFAULT_LOCAL_TASK,
            'weight' => -10,
          );
          $items[] = array(
            'path' => 'aggregator/sources/' . $feed->nid . '/categorize',
            'title' => t('Categorize'),
            'callback' => 'drupal_get_form',
            'callback arguments' => array(
              'feedapi_aggregator_page_source',
            ),
            'access' => $edit,
            'type' => MENU_LOCAL_TASK,
          );
          $items[] = array(
            'path' => 'aggregator/sources/' . $feed->nid . '/configure',
            'title' => t('Configure'),
            'callback' => 'node_page_edit',
            'callback arguments' => array(
              $node,
            ),
            'access' => $edit,
            'type' => MENU_LOCAL_TASK,
            'weight' => 1,
          );
        }
      }
      else {
        if (arg(1) == 'categories') {
          $category = feedapi_aggregator_get_category(arg(2));
          if ($category) {
            $items[] = array(
              'path' => 'aggregator/categories/' . $category['cid'] . '/view',
              'title' => t('View'),
              'type' => MENU_DEFAULT_LOCAL_TASK,
              'weight' => -10,
            );
            $items[] = array(
              'path' => 'aggregator/categories/' . $category['cid'] . '/categorize',
              'title' => t('Categorize'),
              'callback' => 'drupal_get_form',
              'callback arguments' => array(
                'feedapi_aggregator_page_category',
              ),
              'access' => $edit,
              'type' => MENU_LOCAL_TASK,
            );
            $items[] = array(
              'path' => 'aggregator/categories/' . $category['cid'] . '/configure',
              'title' => t('Configure'),
              'callback' => 'drupal_get_form',
              'callback arguments' => array(
                'feedapi_aggregator_form_category',
                $category,
              ),
              'access' => $edit,
              'type' => MENU_LOCAL_TASK,
              'weight' => 1,
            );
          }
        }
      }
    }
  }
  return $items;
}

/**
 * Implementation of hook_perm().
 */
function feedapi_aggregator_perm() {
  return array(
    'access news feeds',
  );
}

/**
 * Settins page
 */
function feedapi_aggregator_admin_settings() {
  $items = array(
    0 => t('none'),
  ) + drupal_map_assoc(array(
    3,
    5,
    10,
    15,
    20,
    25,
  ), '_feedapi_aggregator_items');
  $form['feedapi_aggregator_summary_items'] = array(
    '#type' => 'select',
    '#title' => t('Items shown in sources and categories pages'),
    '#default_value' => variable_get('feedapi_aggregator_summary_items', 3),
    '#options' => $items,
    '#description' => t('The number of items which will be shown with each feed or category in the feed and category summary pages.'),
  );
  return system_settings_form($form);
}

/**
 * Implementation of hook_feedapi_settings_form().
 * If a module provides parsers and processors it MUST evaluate the $type variable
 * to return different forms for parsers and processors.
 * There might be a better term for parsers and processors than $type.
 */
function feedapi_aggregator_feedapi_settings_form($type) {
  switch ($type) {
    case 'processors':
      $form = array();
      $form['block'] = array(
        '#type' => 'select',
        '#title' => t('Number of news items in block'),
        '#default_value' => 3,
        '#options' => drupal_map_assoc(array(
          2,
          3,
          4,
          5,
          6,
          7,
          8,
          9,
          10,
          11,
          12,
          13,
          14,
          15,
          16,
          17,
          18,
          19,
          20,
        )),
      );
      $categories_result = db_query('SELECT cid, title FROM {feedapi_aggregator_category}');
      $categories = array();
      while ($category = db_fetch_object($categories_result)) {
        $categories[$category->cid] = check_plain($category->title);
      }
      $form['categories'] = array(
        '#title' => t('Categories'),
        '#type' => variable_get('aggregator_category_selector', 'checkboxes'),
        '#options' => $categories,
        '#size' => 10,
        '#multiple' => TRUE,
        '#default_value' => array(),
      );
      return $form;
  }
}

/**
 * Implementation of hook_nodeapi().
 */
function feedapi_aggregator_nodeapi(&$node, $op, $teaser, $page) {
  if (isset($node->feed)) {
    switch ($op) {
      case 'delete':
        if (is_array($node->feed->processors)) {
          if (in_array('feedapi_aggregator', $node->feed->processors)) {
            db_query('DELETE FROM {feedapi_aggregator_category_feed} WHERE feed_nid = %d', $node->nid);
          }
        }
    }
  }
}

/**
 * Implementation of hook_link().
 */
function feedapi_aggregator_link($type, $node = NULL, $teaser = FALSE) {
  if ($type == 'node') {
    if (count($node->feed->processors) > 0) {
      if (in_array('feedapi_aggregator', $node->feed->processors)) {
        $links['aggregator_items'] = array(
          'title' => t('News items'),
          'href' => 'aggregator/sources/' . $node->nid,
        );
      }
    }
    return $links;
  }
}

/**
 * Implementation of hook_block().
 *
 * Generates blocks for the latest news items in each category and feed.
 */
function feedapi_aggregator_block($op = 'list', $delta = 0, $edit = array()) {
  if (user_access('access news feeds')) {
    if ($op == 'list') {
      $result = db_query('SELECT cid, title FROM {feedapi_aggregator_category} ORDER BY title');
      while ($category = db_fetch_object($result)) {
        $block['category-' . $category->cid]['info'] = t('!title category latest items', array(
          '!title' => $category->title,
        ));
      }
      $result = db_query('SELECT nid FROM {feedapi} ORDER BY nid');
      while ($node = db_fetch_object($result)) {
        $node = node_load($node->nid);
        if (is_array($node->feed->processors)) {
          if (in_array('feedapi_aggregator', $node->feed->processors)) {
            $block['feed-' . $node->feed->nid]['info'] = t('!title feed latest items', array(
              '!title' => $node->title,
            ));
          }
        }
      }
    }
    else {
      if ($op == 'configure') {
        list($type, $id) = explode('-', $delta);
        if ($type == 'category') {
          $value = db_result(db_query('SELECT block FROM {feedapi_aggregator_category} WHERE cid = %d', $id));
          $form['block'] = array(
            '#type' => 'select',
            '#title' => t('Number of news items in block'),
            '#default_value' => $value,
            '#options' => drupal_map_assoc(array(
              2,
              3,
              4,
              5,
              6,
              7,
              8,
              9,
              10,
              11,
              12,
              13,
              14,
              15,
              16,
              17,
              18,
              19,
              20,
            )),
          );
          return $form;
        }
      }
      else {
        if ($op == 'save') {
          list($type, $id) = explode('-', $delta);
          if ($type == 'category') {
            $value = db_query('UPDATE {feedapi_aggregator_category} SET block = %d WHERE cid = %d', $edit['block'], $id);
          }
        }
        else {
          if ($op == 'view') {
            list($type, $id) = explode('-', $delta);
            switch ($type) {
              case 'feed':
                if ($node = node_load($id)) {
                  $block['subject'] = check_plain($node->title);
                  $elems_in_block = is_numeric($node->feed->settings['processors']['feedapi_aggregator']['block']) ? $node->feed->settings['processors']['feedapi_aggregator']['block'] : 3;
                  $result = db_query_range('SELECT * FROM {feedapi_aggregator_item} WHERE feed_nid = %d ORDER BY timestamp DESC, iid DESC', $node->nid, 0, $elems_in_block);
                  $read_more = '<div class="more-link">' . l(t('more'), 'aggregator/sources/' . $node->nid, array(
                    'title' => t("View this feed's recent news."),
                  )) . '</div>';
                }
                break;
              case 'category':
                if ($category = db_fetch_object(db_query('SELECT cid, title, block FROM {feedapi_aggregator_category} WHERE cid = %d', $id))) {
                  $block['subject'] = check_plain($category->title);
                  $result = db_query_range('SELECT i.* FROM {feedapi_aggregator_category_item} ci LEFT JOIN {feedapi_aggregator_item} i ON ci.iid = i.iid WHERE ci.cid = %d ORDER BY i.timestamp DESC, i.iid DESC', $category->cid, 0, $category->block);
                  $read_more = '<div class="more-link">' . l(t('more'), 'aggregator/categories/' . $category->cid, array(
                    'title' => t("View this category's recent news."),
                  )) . '</div>';
                }
                break;
            }
            $items = array();
            while ($item = db_fetch_object($result)) {
              $items[] = theme('feedapi_aggregator_block_item', $item);
            }

            // Only display the block if there are items to show.
            if (count($items) > 0) {
              $block['content'] = theme('item_list', $items) . $read_more;
            }
          }
        }
      }
    }
    return $block;
  }
}

/**
 * Generate a form to add/edit/delete aggregator categories.
 */
function feedapi_aggregator_form_category($edit = array()) {
  $form['title'] = array(
    '#type' => 'textfield',
    '#title' => t('Title'),
    '#default_value' => $edit['title'],
    '#maxlength' => 64,
    '#required' => TRUE,
  );
  $form['description'] = array(
    '#type' => 'textarea',
    '#title' => t('Description'),
    '#default_value' => $edit['description'],
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Submit'),
  );
  if ($edit['cid']) {
    $form['delete'] = array(
      '#type' => 'submit',
      '#value' => t('Delete'),
    );
    $form['cid'] = array(
      '#type' => 'hidden',
      '#value' => $edit['cid'],
    );
  }
  return $form;
}

/**
 * Validate feedapi_aggregator_form_feed form submissions.
 */
function feedapi_aggregator_form_category_validate($form_id, $form_values) {
  if ($form_values['op'] == t('Submit')) {

    // Check for duplicate titles
    if (isset($form_values['cid'])) {
      $category = db_fetch_object(db_query("SELECT cid FROM {feedapi_aggregator_category} WHERE title = '%s' AND cid <> %d", $form_values['title'], $form_values['cid']));
    }
    else {
      $category = db_fetch_object(db_query("SELECT cid FROM {feedapi_aggregator_category} WHERE title = '%s'", $form_values['title']));
    }
    if ($category) {
      form_set_error('title', t('A category named %category already exists. Please enter a unique title.', array(
        '%category' => $form_values['title'],
      )));
    }
  }
}

/**
 * Process feedapi_aggregator_form_category form submissions.
 * @todo Add delete confirmation dialog.
 */
function feedapi_aggregator_form_category_submit($form_id, $form_values) {
  if ($form_values['op'] == t('Delete')) {
    $title = $form_values['title'];

    // Unset the title:
    unset($form_values['title']);
  }
  feedapi_aggregator_save_category($form_values);
  menu_rebuild();
  if (isset($form_values['cid'])) {
    if (isset($form_values['title'])) {
      drupal_set_message(t('The category %category has been updated.', array(
        '%category' => $form_values['title'],
      )));
      if (arg(0) == 'admin') {
        return 'admin/content/feed/category/';
      }
      else {
        return 'aggregator/categories/' . $form_values['cid'];
      }
    }
    else {
      watchdog('aggregator', t('Category %category deleted.', array(
        '%category' => $title,
      )));
      drupal_set_message(t('The category %category has been deleted.', array(
        '%category' => $title,
      )));
      if (arg(0) == 'admin') {
        return 'admin/content/feed/category/';
      }
      else {
        return 'aggregator/categories/';
      }
    }
  }
  else {
    watchdog('aggregator', t('Category %category added.', array(
      '%category' => $form_values['title'],
    )), WATCHDOG_NOTICE, l(t('view'), 'admin/content/aggregator'));
    drupal_set_message(t('The category %category has been added.', array(
      '%category' => $form_values['title'],
    )));
  }
}

/**
 * Add/edit/delete aggregator categories.
 */
function feedapi_aggregator_save_category($edit) {
  if ($edit['cid'] && $edit['title']) {
    db_query("UPDATE {feedapi_aggregator_category} SET title = '%s', description = '%s' WHERE cid = %d", $edit['title'], $edit['description'], $edit['cid']);
  }
  else {
    if ($edit['cid']) {
      db_query('DELETE FROM {feedapi_aggregator_category} WHERE cid = %d', $edit['cid']);
      db_query('DELETE FROM {feedapi_aggregator_category_feed} WHERE cid = %d', $edit['cid']);
      db_query('DELETE FROM {feedapi_aggregator_category_item} WHERE cid = %d', $edit['cid']);
    }
    else {
      if ($edit['title']) {
        db_query("INSERT INTO {feedapi_aggregator_category} (title, description, block) VALUES ('%s', '%s', 5)", $edit['title'], $edit['description']);
      }
    }
  }
}

/**
 * Implementation of hook_feedapi_item().
 */
function feedapi_aggregator_feedapi_item($op) {
  switch ($op) {
    case 'type':
      return array(
        "XML feed",
      );
    default:
      if (function_exists('_feedapi_aggregator_' . $op)) {
        $args = array_slice(func_get_args(), 1);
        return call_user_func_array('_feedapi_aggregator_' . $op, $args);
      }
  }
}

/**
 * Save the news item as an aggregator item
 */
function _feedapi_aggregator_save($feed_item, $feed_nid, $settings = array()) {
  if (empty($feed_item->options->original_url)) {
    $feed_item->options->original_url = $feed_item->options->guid;
  }
  if ($feed_item->title && $feed_item->options->original_url) {
    $feed_item->fiid = db_next_id('{feedapi_aggregator_item}_iid');
    db_query("INSERT INTO {feedapi_aggregator_item} (iid, feed_nid, title, link, author, description, timestamp, guid) VALUES (%d, %d, '%s', '%s', '%s', '%s', %d, '%s')", $feed_item->fiid, $feed_nid, $feed_item->title, $feed_item->options->original_url, is_object($feed_item->options->original_author) ? $feed_item->options->original_author->name : $feed_item->options->original_author, $feed_item->description, $feed_item->options->timestamp, $feed_item->options->guid);

    // file the items in the categories indicated by the feed
    $categories = db_query('SELECT cid FROM {feedapi_aggregator_category_feed} WHERE feed_nid = %d', $feed_nid);
    while ($category = db_fetch_object($categories)) {
      db_query('INSERT INTO {feedapi_aggregator_category_item} (cid, iid) VALUES (%d, %d)', $category->cid, $feed_item->fiid);
    }
  }
  return $feed_item;
}

/**
 * Update news items data
 */
function _feedapi_aggregator_update($feed_item, $feed_nid, $settings = array()) {
  if ($feed_item->options->guid) {
    $feed_item->fiid = db_result(db_query("SELECT iid FROM {feedapi_aggregator_item} WHERE feed_nid = %d AND guid = '%s'", $feed_nid, $feed_item->options->guid));
  }
  else {
    if ($feed_item->options->original_url) {
      $feed_item->fiid = db_result(db_query("SELECT iid FROM {feedapi_aggregator_item} WHERE feed_nid = %d AND link = '%s'", $feed_nid, $feed_item->options->original_url));
    }
    else {
      $feed_item->fiid = db_result(db_query("SELECT iid FROM {feedapi_aggregator_item} WHERE feed_nid = %d AND title = '%s'", $feed_nid, $feed_item->title));
    }
  }
  if (is_numeric($feed_item->fiid) && isset($feed_item->title)) {
    db_query("UPDATE {feedapi_aggregator_item} SET title = '%s', link = '%s', author = '%s', description = '%s', guid = '%s', timestamp = %d WHERE iid = %d", $feed_item->title, $feed_item->options->original_url, is_object($feed_item->options->original_author) ? $feed_item->options->original_author->name : $feed_item->options->original_author, $feed_item->description, $feed_item->options->guid, $feed_item->options->timestamp, $feed_item->fiid);
    $categories = db_query('SELECT cid FROM {feedapi_aggregator_category_feed} WHERE feed_nid = %d', $feed_nid);
    while ($category = db_fetch_object($categories)) {
      if (0 == db_result(db_query("SELECT COUNT(*) FROM {feedapi_aggregator_category_item} WHERE cid = %d AND iid = %d", $category->cid, $feed_item->fiid))) {
        db_query('INSERT INTO {feedapi_aggregator_category_item} (cid, iid) VALUES (%d, %d)', $category->cid, $feed_item->fiid);
      }
    }
  }
  return $feed_item;
}

/**
 * Delete item and category data
 */
function _feedapi_aggregator_delete($feed_item, $feed_nid, $settings = array()) {
  db_query('DELETE FROM {feedapi_aggregator_category_item} WHERE iid = %d' . $feed_item->fiid);
  db_query('DELETE FROM {feedapi_aggregator_item} WHERE iid = %d' . $feed_item->fiid);
}

/**
 * Load item data into the $feed_item object
 */
function _feedapi_aggregator_load($feed_item, $feed_nid, $settings = array()) {
  $item = db_fetch_array(db_query("SELECT * FROM {feedapi_aggregator_item} WHERE iid = %d", $feed_item->fiid));
  $feed_item->fiid = $item['iid'];
  $feed_item->feed_nid = $item['feed_nid'];
  $feed_item->arrived = 0;

  // no arrived time
  $feed_item->options->original_url = $item['link'];
  $feed_item->options->guid = $item['guid'];
  $feed_item->options->timestamp = $item['timestamp'];
  $feed_item->description = $item['description'];
  $feed_item->title = $item['title'];
  return $feed_item;
}

/**
 * Return all items related to a feed
 */
function _feedapi_aggregator_fetch($feed, $settings = array()) {
  $result = db_query("SELECT iid AS fiid, feed_nid, timestamp, link FROM {feedapi_aggregator_item} WHERE feed_nid = %d", $feed->nid);
  $items = array();
  while ($item = db_fetch_object($result)) {
    $items[] = $item;
  }
  return $items;
}

/**
 * Is this feed item created?
 */
function _feedapi_aggregator_unique($feed_item, $feed_nid, $settings = array()) {
  $entry = FALSE;
  if ($feed_item->options->guid) {
    $entry = db_fetch_object(db_query("SELECT iid FROM {feedapi_aggregator_item} WHERE feed_nid = %d AND guid = '%s'", $feed_nid, $feed_item->options->guid));
  }
  else {
    if ($feed_item->options->original_url) {
      $entry = db_fetch_object(db_query("SELECT iid FROM {feedapi_aggregator_item} WHERE feed_nid = %d AND link = '%s'", $feed_nid, $feed_item->options->original_url));
    }
    else {
      $entry = db_fetch_object(db_query("SELECT iid FROM {feedapi_aggregator_item} WHERE feed_nid = %d AND title = '%s'", $feed_nid, $feed_item->title));
    }
  }
  return is_object($entry) ? FALSE : TRUE;
}

/**
 * Check for expired items, pass them to the item_expire function
 * 
 * @TO DO Add cron timeout checking here, there may be too many items (nodes) to delete
 * 
 * We implement the same logic as a db query. The old code is
 * 
 *     if (isset($item->arrived) || isset($item->timestamp)) {
 *       $diff = abs(time() - (isset($item->timestamp) ? $item->timestamp : $item->timestamp));
 *       if ($diff > $settings['items_delete']) {
 *       ................
 *       }
 *     }
 */
function _feedapi_aggregator_expire($feed, $settings) {
  $count = 0;
  if ($settings['items_delete'] > FEEDAPI_NEVER_DELETE_OLD) {
    $timexpire = time() - $settings['items_delete'];

    // @ TODO Review this query conditions
    $result = db_query("SELECT * FROM {feedapi_aggregator_item} WHERE feed_nid = %d AND timestamp > 0 AND timestamp < %d", $feed->nid, $timexpire);
    while ($item = db_fetch_object($result)) {

      // We callback feedapi for deleting
      $item->fiid = $item->iid;
      feedapi_expire_item($feed, $item);
      $count++;
    }
  }
  return $count;
}

/**
 * Implementation of hook_feedapi_after_settings().
 * Store per-feed categorizing
 */
function feedapi_aggregator_feedapi_after_settings($nid, $settings) {
  db_query('DELETE FROM {feedapi_aggregator_category_feed} WHERE feed_nid = %d', $nid);
  if (isset($settings['processors']['feedapi_aggregator']['categories'])) {
    foreach ($settings['processors']['feedapi_aggregator']['categories'] as $cid => $value) {
      if ($value) {
        db_query('INSERT INTO {feedapi_aggregator_category_feed} (feed_nid, cid) VALUES (%d, %d)', $nid, $cid);
      }
    }
  }
}
function feedapi_aggregator_get_category($cid) {
  return db_fetch_array(db_query('SELECT * FROM {feedapi_aggregator_category} WHERE cid = %d', $cid));
}

/**
 * Provide a UI for category management
 */
function feedapi_aggregator_category_view() {
  $result = db_query('SELECT c.cid, c.title, count(ci.iid) as items FROM {feedapi_aggregator_category} c LEFT JOIN {feedapi_aggregator_category_item} ci ON c.cid = ci.cid GROUP BY c.cid, c.title ORDER BY title');
  $output = '<h3>' . t('Category overview') . '</h3>';
  $header = array(
    t('Title'),
    t('Items'),
    t('Operations'),
  );
  $rows = array();
  while ($category = db_fetch_object($result)) {
    $rows[] = array(
      l($category->title, "aggregator/categories/{$category->cid}"),
      format_plural($category->items, '1 item', '@count items'),
      l(t('edit'), "aggregator/categories/{$category->cid}/configure"),
    );
  }
  $output .= theme('table', $header, $rows);
  return $output;
}

/**
 * Menu callback; displays the most recent items gathered from any feed.
 */
function feedapi_aggregator_page_last() {
  drupal_add_feed(url('aggregator/rss'), variable_get('site_name', 'Drupal') . ' ' . t('aggregator'));
  return _feedapi_aggregator_page_list('SELECT i.*, f.nid FROM {feedapi_aggregator_item} i INNER JOIN {feedapi} f ON i.feed_nid = f.nid ORDER BY i.timestamp DESC, i.iid DESC', arg(1));
}

/**
 * Menu callback; displays all the items captured from a particular feed.
 */
function feedapi_aggregator_page_source() {
  $node = node_load(arg(2));
  drupal_set_title(t("News items from !feed", array(
    "!feed" => $node->title,
  )));
  $info = theme('aggregator_feed', $node->feed);
  return _feedapi_aggregator_page_list('SELECT * FROM {feedapi_aggregator_item} WHERE feed_nid = ' . $node->nid . ' ORDER BY timestamp DESC, iid DESC', arg(3), $info);
}

/**
 * Store the per-item categorization
 */
function feedapi_aggregator_page_source_submit($form_id, $form_values) {

  // Delete items' current categorization
  $items = array_keys($form_values['categories']);
  if (count($items) > 0) {
    db_query("DELETE FROM {feedapi_aggregator_category_item} WHERE iid IN(" . implode(", ", $items) . ")");
    foreach ($items as $item) {
      if (is_array($form_values['categories'][$item])) {
        foreach (array_keys($form_values['categories'][$item]) as $category) {
          if ($form_values['categories'][$item][$category] == TRUE) {
            db_query("INSERT INTO {feedapi_aggregator_category_item} VALUES (%d, %d)", $item, $category);
          }
        }
      }
    }
  }
}

/**
 * Menu callback; displays all the items aggregated in a particular category.
 */
function feedapi_aggregator_page_category() {
  $category = db_fetch_object(db_query('SELECT cid, title FROM {feedapi_aggregator_category} WHERE cid = %d', arg(2)));
  drupal_add_feed(url('aggregator/rss/' . arg(2)), variable_get('site_name', 'Drupal') . ' ' . t('aggregator - @title', array(
    '@title' => $category->title,
  )));
  return _feedapi_aggregator_page_list('SELECT i.*, f.nid FROM {feedapi_aggregator_category_item} c LEFT JOIN {feedapi_aggregator_item} i ON c.iid = i.iid LEFT JOIN {feedapi} f ON i.feed_nid = f.nid WHERE cid = ' . $category->cid . ' ORDER BY timestamp DESC, iid DESC', arg(3));
}

/**
 * Show the list of the feed items
 */
function feedapi_aggregator_page_list($sql, $header, $categorize) {
  if ($categorize && db_result(db_query('SELECT COUNT(*) FROM {feedapi_aggregator_category}')) == 0) {
    $form['warning'] = array(
      '#type' => 'item',
      '#title' => t('Warning'),
      '#value' => t('Please create a new category before try categorizing items.') . ' ' . l(t('You can create categories here.'), 'admin/content/feed/category'),
    );
    return $form;
  }
  $form['#base'] = 'feedapi_aggregator_page_list';
  $form['header'] = array(
    '#value' => $header,
  );
  $result = pager_query($sql, 20);
  $categories = array();
  $done = FALSE;
  $form['items'] = array();
  $form['categories'] = array(
    '#tree' => TRUE,
  );
  while ($item = db_fetch_object($result)) {
    $form['items'][$item->iid] = array(
      '#value' => theme('feedapi_aggregator_page_item', $item),
    );
    $form['categories'][$item->iid] = array();
    if ($categorize) {
      $categories_result = db_query('SELECT c.cid, c.title, ci.iid FROM {feedapi_aggregator_category} c LEFT JOIN {feedapi_aggregator_category_item} ci ON c.cid = ci.cid AND ci.iid = %d', $item->iid);
      $selected = array();
      while ($category = db_fetch_object($categories_result)) {
        if (!$done) {
          $categories[$category->cid] = check_plain($category->title);
        }
        if ($category->iid) {
          $selected[] = $category->cid;
        }
      }
      $done = TRUE;
      $form['categories'][$item->iid] = array(
        '#title' => $item->title,
        '#description' => $item->description,
        '#type' => variable_get('aggregator_category_selector', 'checkboxes'),
        '#default_value' => $selected,
        '#options' => $categories,
        '#size' => 10,
        '#multiple' => TRUE,
      );
    }
  }
  if ($categorize) {
    unset($form['items']);
  }
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Save categories'),
  );
  $form['pager'] = array(
    '#value' => theme('pager', NULL, 20, 0),
  );
  return $form;
}

/**
 * Menu callback; generate an RSS 0.92 feed of aggregator items or categories.
 */
function feedapi_aggregator_page_rss() {

  // arg(2) is the passed cid, only select for that category
  $result = NULL;
  if (arg(2)) {
    $category = db_fetch_object(db_query('SELECT cid, title FROM {feedapi_aggregator_category} WHERE cid = %d', arg(2)));
    $url = '/categories/' . $category->cid;
    $title = ' ' . t('in category') . ' ' . $category->title;
    $sql = 'SELECT i.*, n.title AS ftitle, f.url AS flink FROM {feedapi_aggregator_category_item} c LEFT JOIN {feedapi_aggregator_item} i ON c.iid = i.iid LEFT JOIN {feedapi} f ON i.feed_nid = f.nid LEFT JOIN {node} n ON f.nid = n.nid WHERE cid = %d ORDER BY timestamp DESC, iid DESC';
    $result = db_query_range($sql, $category->cid, 0, variable_get('feed_default_items', 10));
  }
  else {
    $sql = 'SELECT i.*, n.title AS ftitle, f.link AS flink FROM {feedapi_aggregator_item} i INNER JOIN {feedapi} f ON i.feed_nid = f.nid LEFT JOIN {node} n ON f.nid = n.nid ORDER BY i.timestamp DESC, i.iid DESC';
    $result = db_query_range($sql, 0, variable_get('feed_default_items', 10));
  }
  while ($item = db_fetch_object($result)) {
    switch (variable_get('feed_item_length', 'teaser')) {
      case 'teaser':
        $teaser = node_teaser($item->description);
        if ($teaser != $item->description) {
          $teaser .= '<p><a href="' . check_url($item->link) . '">' . t('read more') . "</a></p>\n";
        }
        $item->description = $teaser;
        break;
      case 'title':
        $item->description = '';
        break;
    }
    $items .= format_rss_item($item->ftitle . ': ' . $item->title, $item->link, $item->description, array(
      'pubDate' => date('r', $item->timestamp),
    ));
  }
  $output .= "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
  $output .= "<rss version=\"2.0\">\n";
  $output .= format_rss_channel(variable_get('site_name', 'Drupal') . ' ' . t('aggregator'), url('aggregator' . $url, NULL, NULL, TRUE), variable_get('site_name', 'Drupal') . ' - ' . t('aggregated feeds') . "{$title}", $items, 'en');
  $output .= "</rss>\n";
  drupal_set_header('Content-Type: application/rss+xml; charset=utf-8');
  print $output;
}

/**
 * Prints an aggregator page listing a number of feed items. Various
 * menu callbacks use this function to print their feeds.
 */
function _feedapi_aggregator_page_list($sql, $op, $header = '') {
  $categorize = user_access('administer feedapi') && $op == 'categorize';
  $form = feedapi_aggregator_page_list($sql, $header, $categorize);
  if ($categorize) {
    return $form;
  }
  else {
    $output = '<div id="aggregator">';
    $output .= $header;
    if (count($form['items']) != 0) {
      foreach ($form['items'] as $item) {
        $output .= $item['#value'];
      }
    }
    else {
      $output .= t('Currently there is no news item in this category/feed.');
    }
    $output .= '</div>';
    $output .= $form['pager']['#value'];
    $output .= $form['feed_icon']['#value'];
    return $output;
  }
}

/**
 * Menu callback; displays all the feeds used by the aggregator.
 */
function feedapi_aggregator_page_sources() {
  $result = db_query('SELECT f.nid, MAX(i.timestamp) AS last FROM {feedapi} f LEFT JOIN {feedapi_aggregator_item} i ON f.nid = i.feed_nid GROUP BY f.nid ORDER BY last DESC');
  $output = "<div id=\"aggregator\">\n";
  while ($feed = db_fetch_object($result)) {
    $node = node_load($feed->nid);
    if (count($node->feed->processors) > 0) {
      if (in_array('feedapi_aggregator', $node->feed->processors)) {
        $output .= '<h2>' . check_plain($node->title) . "</h2>\n";

        // Most recent items:
        $list = array();
        if (variable_get('feedapi_aggregator_summary_items', 3)) {
          $items = db_query_range('SELECT i.title, i.timestamp, i.link FROM {feedapi_aggregator_item} i WHERE i.feed_nid= %d ORDER BY i.timestamp DESC', $node->nid, 0, variable_get('feedapi_aggregator_summary_items', 3));
          while ($item = db_fetch_object($items)) {
            $list[] = theme('feedapi_aggregator_summary_item', $item);
          }
        }
        $output .= theme('item_list', $list);
        $link['sources'] = array(
          'title' => t('More'),
          'href' => 'aggregator/sources/' . $feed->nid,
        );
        $output .= '<div class="links">' . theme('links', $link) . "</div>\n";
      }
    }
  }
  $output .= '</div>';
  return $output;
}

/**
 * Menu callback; displays all the categories used by the aggregator.
 */
function feedapi_aggregator_page_categories() {
  $result = db_query('SELECT c.cid, c.title, c.description FROM {feedapi_aggregator_category} c LEFT JOIN {feedapi_aggregator_category_item} ci ON c.cid = ci.cid LEFT JOIN {feedapi_aggregator_item} i ON ci.iid = i.iid GROUP BY c.cid, c.title, c.description');
  $output = "<div id=\"aggregator\">\n";
  while ($category = db_fetch_object($result)) {
    $output .= '<h2>' . check_plain($category->title) . "</h2>\n";
    if (variable_get('feedapi_aggregator_summary_items', 3)) {
      $list = array();
      $items = db_query_range('SELECT i.title, i.timestamp, i.link, f.nid FROM {feedapi_aggregator_category_item} ci LEFT JOIN {feedapi_aggregator_item} i ON i.iid = ci.iid LEFT JOIN {feedapi} f ON i.feed_nid = f.nid WHERE ci.cid = %d ORDER BY i.timestamp DESC', $category->cid, 0, variable_get('feedapi_aggregator_summary_items', 3));
      while ($item = db_fetch_object($items)) {
        $list[] = theme('feedapi_aggregator_summary_item', $item);
      }
      $output .= theme('item_list', $list);
    }
    $link['categories'] = array(
      'title' => t('More'),
      'href' => 'aggregator/categories/' . $category->cid,
    );
    $output .= '<div class="links">' . theme('links', $link) . "</div>\n";
  }
  $output .= '</div>';
  return $output;
}

/**
 * Return a themed item heading for summary pages located at "aggregator/sources"
 * and "aggregator/categories".
 *
 * @param $item The item object from the aggregator module.
 * @return A string containing the output.
 *
 * @ingroup themeable
 */
function theme_feedapi_aggregator_summary_item($item) {
  $output = '<a href="' . check_url($item->link) . '">' . check_plain($item->title) . '</a> <span class="age">' . t('%age old', array(
    '%age' => format_interval(time() - $item->timestamp),
  )) . '</span>';
  if ($item->feed_link) {
    $output .= ', <span class="source"><a href="' . check_url($item->feed_link) . '">' . check_plain($item->feed_title) . '</a></span>';
  }
  return $output . "\n";
}

/**
 * Format an individual feed item for display on the aggregator page.
 *
 * @ingroup themeable
 */
function theme_feedapi_aggregator_page_item($item) {
  $source = '';
  if ($item->ftitle && $item->feed_nid) {
    $source = l($item->ftitle, "aggregator/sources/{$item->feed_nid}", array(
      'class' => 'feed-item-source',
    )) . ' -';
  }
  if (date('Ymd', $item->timestamp) == date('Ymd')) {
    $source_date = t('%ago ago', array(
      '%ago' => format_interval(time() - $item->timestamp),
    ));
  }
  else {
    $source_date = format_date($item->timestamp, 'custom', variable_get('date_format_medium', 'D, m/d/Y - H:i'));
  }
  $output .= "<div class=\"feed-item\">\n";
  $output .= '<h3 class="feed-item-title"><a href="' . check_url($item->link) . '">' . check_plain($item->title) . "</a></h3>\n";
  $output .= "<div class=\"feed-item-meta\">{$source} <span class=\"feed-item-date\">{$source_date}</span></div>\n";
  if ($item->description) {
    $output .= '<div class="feed-item-body">' . $item->description . "</div>\n";
  }
  $result = db_query('SELECT c.title, c.cid FROM {feedapi_aggregator_category_item} ci LEFT JOIN {feedapi_aggregator_category} c ON ci.cid = c.cid WHERE ci.iid = %d ORDER BY c.title', $item->iid);
  $categories = array();
  while ($category = db_fetch_object($result)) {
    if (strlen($category->title) > 0) {
      $categories[] = l($category->title, 'aggregator/categories/' . $category->cid);
    }
  }
  if (count($categories) > 0) {
    $output .= '<div class="feed-item-categories">' . t('Categories') . ': ' . implode(', ', $categories) . "</div>\n";
  }
  $output .= "</div>\n";
  return $output;
}

/**
 * Format an individual feed item for display in the block.
 *
 * @ingroup themeable
 */
function theme_feedapi_aggregator_block_item($item, $feed = 0) {
  global $user;
  if ($user->uid && module_exists('blog') && user_access('edit own blog')) {
    if ($image = theme('image', 'misc/blog.png', t('blog it'), t('blog it'))) {
      $output .= '<div class="icon">' . l($image, 'node/add/blog', array(
        'title' => t('Comment on this news item in your personal blog.'),
        'class' => 'blog-it',
      ), "iid={$item->iid}", NULL, FALSE, TRUE) . '</div>';
    }
  }

  // Display the external link to the item.
  $output .= '<a href="' . check_url($item->link) . '">' . check_plain($item->title) . "</a>\n";
  return $output;
}

/**
 * Helper function for drupal_map_assoc.
 */
function _feedapi_aggregator_items($count) {
  return format_plural($count, '1 item', '@count items');
}

Functions

Namesort descending Description
feedapi_aggregator_admin_settings Settins page
feedapi_aggregator_block Implementation of hook_block().
feedapi_aggregator_category_view Provide a UI for category management
feedapi_aggregator_feedapi_after_settings Implementation of hook_feedapi_after_settings(). Store per-feed categorizing
feedapi_aggregator_feedapi_item Implementation of hook_feedapi_item().
feedapi_aggregator_feedapi_settings_form Implementation of hook_feedapi_settings_form(). If a module provides parsers and processors it MUST evaluate the $type variable to return different forms for parsers and processors. There might be a better term for parsers and processors than $type.
feedapi_aggregator_form_category Generate a form to add/edit/delete aggregator categories.
feedapi_aggregator_form_category_submit Process feedapi_aggregator_form_category form submissions. @todo Add delete confirmation dialog.
feedapi_aggregator_form_category_validate Validate feedapi_aggregator_form_feed form submissions.
feedapi_aggregator_get_category
feedapi_aggregator_help Implementation of hook_help().
feedapi_aggregator_link Implementation of hook_link().
feedapi_aggregator_menu Implementation of hook_menu().
feedapi_aggregator_nodeapi Implementation of hook_nodeapi().
feedapi_aggregator_page_categories Menu callback; displays all the categories used by the aggregator.
feedapi_aggregator_page_category Menu callback; displays all the items aggregated in a particular category.
feedapi_aggregator_page_last Menu callback; displays the most recent items gathered from any feed.
feedapi_aggregator_page_list Show the list of the feed items
feedapi_aggregator_page_rss Menu callback; generate an RSS 0.92 feed of aggregator items or categories.
feedapi_aggregator_page_source Menu callback; displays all the items captured from a particular feed.
feedapi_aggregator_page_sources Menu callback; displays all the feeds used by the aggregator.
feedapi_aggregator_page_source_submit Store the per-item categorization
feedapi_aggregator_perm Implementation of hook_perm().
feedapi_aggregator_save_category Add/edit/delete aggregator categories.
theme_feedapi_aggregator_block_item Format an individual feed item for display in the block.
theme_feedapi_aggregator_page_item Format an individual feed item for display on the aggregator page.
theme_feedapi_aggregator_summary_item Return a themed item heading for summary pages located at "aggregator/sources" and "aggregator/categories".
_feedapi_aggregator_delete Delete item and category data
_feedapi_aggregator_expire Check for expired items, pass them to the item_expire function
_feedapi_aggregator_fetch Return all items related to a feed
_feedapi_aggregator_items Helper function for drupal_map_assoc.
_feedapi_aggregator_load Load item data into the $feed_item object
_feedapi_aggregator_page_list Prints an aggregator page listing a number of feed items. Various menu callbacks use this function to print their feeds.
_feedapi_aggregator_save Save the news item as an aggregator item
_feedapi_aggregator_unique Is this feed item created?
_feedapi_aggregator_update Update news items data