Index: modules/aggregator/aggregator.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/aggregator/aggregator.admin.inc,v retrieving revision 1.18 diff -u -r1.18 aggregator.admin.inc --- modules/aggregator/aggregator.admin.inc 22 Oct 2008 18:29:28 -0000 1.18 +++ modules/aggregator/aggregator.admin.inc 6 Nov 2008 21:54:42 -0000 @@ -389,33 +389,36 @@ * @see system_settings_form() */ function aggregator_admin_settings() { - $items = array(0 => t('none')) + drupal_map_assoc(array(3, 5, 10, 15, 20, 25), '_aggregator_items'); - $period = drupal_map_assoc(array(3600, 10800, 21600, 32400, 43200, 86400, 172800, 259200, 604800, 1209600, 2419200, 4838400, 9676800), 'format_interval'); - - $form['aggregator_allowed_html_tags'] = array( - '#type' => 'textfield', '#title' => t('Allowed HTML tags'), '#size' => 80, '#maxlength' => 255, - '#default_value' => variable_get('aggregator_allowed_html_tags', '
      • '), - '#description' => t('A space-separated list of HTML tags allowed in the content of feed items. (Tags in this list are not removed by Drupal.)'), - ); - - $form['aggregator_summary_items'] = array( - '#type' => 'select', '#title' => t('Items shown in sources and categories pages') , - '#default_value' => variable_get('aggregator_summary_items', 3), '#options' => $items, - '#description' => t('Number of feed items displayed in feed and category summary pages.'), - ); - - $form['aggregator_clear'] = array( - '#type' => 'select', '#title' => t('Discard items older than'), - '#default_value' => variable_get('aggregator_clear', 9676800), '#options' => $period, - '#description' => t('The length of time to retain feed items before discarding. (Requires a correctly configured cron maintenance task.)', array('@cron' => url('admin/reports/status'))), - ); - - $form['aggregator_category_selector'] = array( - '#type' => 'radios', '#title' => t('Category selection type'), '#default_value' => variable_get('aggregator_category_selector', 'checkboxes'), - '#options' => array('checkboxes' => t('checkboxes'), 'select' => t('multiple selector')), - '#description' => t('The type of category selection widget displayed on categorization pages. (For a small number of categories, checkboxes are easier to use, while a multiple selector works well with large numbers of categories.)'), + $period = array('-1' => t('none')); + $period += drupal_map_assoc(array(900, 1800, 3600, 7200, 10800, 21600, 32400, 43200, 64800, 86400, 172800, 259200, 604800, 1209600, 2419200), 'format_interval'); + $parsers = module_implements('aggregator_parse'); + foreach ($parsers as $k => $v) { + $info = module_invoke($v, 'aggregator_parse', 'info'); + unset($parsers[$k]); + $parsers[$v] = $info['title'] . ' ' . $info['description'] .''; + } + $processors = module_implements('aggregator_process'); + foreach ($processors as $k => $v) { + $info = module_invoke($v, 'aggregator_process', 'info'); + unset($processors[$k]); + $processors[$v] = $info['title'] . ' ' . $info['description'] .''; + } + $form['aggregator_parser'] = array( + '#type' => 'radios', + '#title' => t('Parser'), + '#description' => t('Parsers retrieve and parse feed data. Choose one suitable for the type of feeds you would like to aggregate.'), + '#options' => $parsers, + '#default_value' => variable_get('aggregator_parser', ''), + ); + $form['aggregator_processors'] = array( + '#type' => 'checkboxes', + '#title' => t('Processors'), + '#description' => t('Processors act on parsed feed data, for example they store feed items. Pick the processors suitable for your task.'), + '#options' => $processors, + '#default_value' => variable_get('aggregator_processors', array()), ); - + $form['modules'] = array(); + return system_settings_form($form); } @@ -507,3 +510,50 @@ drupal_set_message(t('The category %category has been added.', array('%category' => $form_state['values']['title']))); } } + +/** + * Implementation of hook_form_alter(). + */ +function aggregator_form_alter(&$form, $form_state, $form_id) { + if ($form_id == 'aggregator_admin_settings') { + if (in_array('aggregator', aggregator_get_enabled_processors())) { + $types = node_get_types(); + $types_select = array(); + foreach ($types as $type) { + // Do not allow a content-type for both the items and the feeds + if (!variable_get('aggregator_feed_' . $type->type, FALSE)) { + $types_select[$type->type] = $type->name; + } + } + $info = module_invoke('aggregator', 'aggregator_process', 'info'); + $period = drupal_map_assoc(array(3600, 10800, 21600, 32400, 43200, 86400, 172800, 259200, 604800, 1209600, 2419200, 4838400, 9676800), 'format_interval'); + $items = array(0 => t('none')) + drupal_map_assoc(array(3, 5, 10, 15, 20, 25), '_aggregator_items'); + + $form['modules']['aggregator'] = array( + '#type' => 'fieldset', + '#title' => t('Advanced Aggregator Light settings'), + '#description' => $info['description'], + '#collapsible' => TRUE, + '#collapsed' => !in_array('aggregator', aggregator_get_enabled_processors()), + ); + + $form['modules']['aggregator']['aggregator_summary_items'] = array( + '#type' => 'select', '#title' => t('Items shown in sources and categories pages') , + '#default_value' => variable_get('aggregator_summary_items', 3), '#options' => $items, + '#description' => t('Number of feed items displayed in feed and category summary pages.'), + ); + + $form['modules']['aggregator']['aggregator_clear'] = array( + '#type' => 'select', '#title' => t('Discard items older than'), + '#default_value' => variable_get('aggregator_clear', 9676800), '#options' => $period, + '#description' => t('The length of time to retain feed items before discarding. (Requires a correctly configured cron maintenance task.)', array('@cron' => url('admin/reports/status'))), + ); + + $form['modules']['aggregator']['aggregator_category_selector'] = array( + '#type' => 'radios', '#title' => t('Category selection type'), '#default_value' => variable_get('aggregator_category_selector', 'checkboxes'), + '#options' => array('checkboxes' => t('checkboxes'), 'select' => t('multiple selector')), + '#description' => t('The type of category selection widget displayed on categorization pages. (For a small number of categories, checkboxes are easier to use, while a multiple selector works well with large numbers of categories.)'), + ); + } + } +} Index: modules/aggregator/aggregator.install =================================================================== RCS file: /cvs/drupal/drupal/modules/aggregator/aggregator.install,v retrieving revision 1.17 diff -u -r1.17 aggregator.install --- modules/aggregator/aggregator.install 12 Aug 2008 07:00:48 -0000 1.17 +++ modules/aggregator/aggregator.install 6 Nov 2008 21:54:42 -0000 @@ -7,6 +7,9 @@ function aggregator_install() { // Create tables. drupal_install_schema('aggregator'); + // Enable default parser and processors. + variable_set('aggregator_parser', 'aggregator'); + variable_set('aggregator_processors', array('aggregator')); } /** Index: modules/aggregator/aggregator.module =================================================================== RCS file: /cvs/drupal/drupal/modules/aggregator/aggregator.module,v retrieving revision 1.398 diff -u -r1.398 aggregator.module --- modules/aggregator/aggregator.module 1 Nov 2008 19:51:06 -0000 1.398 +++ modules/aggregator/aggregator.module 6 Nov 2008 21:54:42 -0000 @@ -1,5 +1,5 @@ REQUEST_TIME)); - foreach ($result as $feed) { - aggregator_refresh((array)$feed); + $ready = FALSE; + if (drupal_function_exists('_aggregator_light_delete_expired')) { + _aggregator_light_delete_expired(); + } + // Query the feeds which should be refreshed and do the refresh. + $start = REQUEST_TIME; + while(!$ready || _aggregator_cron_time()) { + $result = db_query_range('SELECT * FROM {aggregator_feed} WHERE checked < :start AND (:start - checked) > refresh ORDER BY checked', array(':start' => $start), 0, 2); + $feed_count = 0; + foreach ($result as $feed) { + aggregator_refresh((array)$feed); + ++$feed_count; + } + if ($feed_count == 0) { + $ready = TRUE; + } } } @@ -368,6 +381,126 @@ } /** + * Implementation of hook_aggregator_process(). + * + * @param $op + * 'unique' Determine which of the items in $feed['items'] are unique. + * 'save' Save feed items in $feed['items']. + * 'info' Metadata about the processor. + * @param $feed + * Feed associative array. Structure: @see aggregator_parse() + */ +function aggregator_aggregator_process($op, $feed = NULL) { + switch ($op) { + case 'unique': + foreach ($feed['items'] as $k => $item) { + if (!empty($item['guid'])) { + $entry = db_query("SELECT iid, timestamp FROM {aggregator_item} WHERE fid = :fid AND guid = :guid", array(':fid' => $feed['fid'], ':guid' => $item['guid']))->fetchObject(); + } + elseif ($item['link'] && $item['link'] != $feed['link'] && $item['link'] != $feed['url']) { + $entry = db_query("SELECT iid, timestamp FROM {aggregator_item} WHERE fid = :fid AND link = :link", array(':fid' => $feed['fid'], ':link' => $item['link']))->fetchObject(); + } + elseif (isset($item['title'])) { + $entry = db_query("SELECT iid, timestamp FROM {aggregator_item} WHERE fid = :fid AND title = :title", array(':fid' => $feed['fid'], ':title' => $item['title']))->fetchObject(); + } + if (!isset($feed['items'][$k]['unique'])) { + $feed['items'][$k]['unique'] = array(); + } + $feed['items'][$k]['unique']['aggregator'] = (!isset($entry->iid) ? TRUE : $entry->iid); + } + return $feed; + case 'save': + $new = FALSE; + foreach ($feed['items'] as $k => $item) { + $new = ($new || is_numeric($item['unique']['aggregator']) ? FALSE : TRUE); + if (isset($item['description']) && isset($item['title'])) { + aggregator_save_item(array('iid' => $item['unique']['aggregator'], 'fid' => $feed['fid'], 'timestamp' => $item['timestamp'], 'title' => $item['title'], 'link' => $item['link'], 'author' => $item['author'], 'description' => $item['description'], 'guid' => $item['guid'])); + } + } + if ($new) { + watchdog('aggregator', 'There is new syndicated content from %site.', array('%site' => $feed['title'])); + drupal_set_message(t('There is new syndicated content from %site.', array('%site' => $feed['title']))); + } + return $feed; + case 'info': + return array( + 'title' => t('Aggregator Light'), + 'description' => t('Creates lightweight records of feed items.'), + ); + } +} + +/** + * Implementation of hook_aggregator_parse(). + * + * @param $op + * 'parse' Parse the feed-nodes + * 'info' Metadata about the processor + * @param $feed + * Feed associative array come from {aggregator} table + */ +function aggregator_aggregator_parse($op, $feed = NULL) { + switch ($op) { + case 'parse': + // Generate conditional GET headers. + $headers = array(); + if ($feed['etag']) { + $headers['If-None-Match'] = $feed['etag']; + } + if ($feed['modified']) { + $headers['If-Modified-Since'] = gmdate('D, d M Y H:i:s', $feed['modified']) . ' GMT'; + } + + // Request feed. + $result = drupal_http_request($feed['url'], $headers); + + // Process HTTP response code. + switch ($result->code) { + case 304: + return TRUE; + break; + case 301: + $feed['url'] = $result->redirect_url; + + if (isset($result->redirect_url)) { + watchdog('aggregator', 'Updated URL for feed %title to %url.', array('%title' => $feed['title'], '%url' => $feed['url'])); + } + // Do not break here. + case 200: + case 302: + case 307: + // We store the md5 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. + $feed['md5'] = md5($result->data); + if ($feed['hash'] == $feed['md5']) { + return TRUE; + } + return array_merge( + array( + 'md5' => $feed['md5'], + 'modified' => empty($result->headers['Last-Modified']) ? 0 : strtotime($result->headers['Last-Modified']), + 'etag' => empty($result->headers['ETag']) ? '' : $result->headers['ETag'], + ), + aggregator_parse_feed($result->data, $feed)); + break; + default: + watchdog('aggregator', 'The feed from %site seems to be broken, due to "%error".', array('%site' => $feed['title'], '%error' => $result->code . ' ' . $result->error), WATCHDOG_WARNING); + drupal_set_message(t('The feed from %site seems to be broken, because of error "%error".', array('%site' => $feed['title'], '%error' => $result->code . ' ' . $result->error))); + module_invoke('system', 'check_http_request'); + return FALSE; + } + break; + case 'info': + return array( + 'title' => t('Built-in Parser'), + 'description' => t('Default parser for RSS, Atom and RDF feeds.'), + ); + + } +} + +/** * Add/edit/delete aggregator categories. * * @param $edit @@ -545,7 +678,7 @@ $items[$item]['LINK'] = $attributes['HREF']; } else { - $channel['LINK'] = $attributes['HREF']; + $channel['link'] = $attributes['HREF']; } } break; @@ -616,8 +749,8 @@ break; case 'TAGLINE': case 'SUBTITLE': - $channel += array('DESCRIPTION' => ''); - $channel['DESCRIPTION'] .= $data; + $channel += array('description' => ''); + $channel['description'] .= $data; break; case 'INFO': case 'ID': @@ -626,8 +759,8 @@ // it or its contents will end up in the item array. break; default: - $channel += array($tag => ''); - $channel[$tag] .= $data; + $channel += array(strtolower($tag) => ''); + $channel[strtolower($tag)] .= $data; } } @@ -638,100 +771,67 @@ * An associative array describing the feed to be refreshed. */ function aggregator_refresh($feed) { - global $channel, $image; - // Generate conditional GET headers. - $headers = array(); - if ($feed['etag']) { - $headers['If-None-Match'] = $feed['etag']; - } - if ($feed['modified']) { - $headers['If-Modified-Since'] = gmdate('D, d M Y H:i:s', $feed['modified']) . ' GMT'; - } - - // Request feed. - $result = drupal_http_request($feed['url'], $headers); - - // Process HTTP response code. - switch ($result->code) { - case 304: - db_update('aggregator_feed') - ->fields(array('checked' => REQUEST_TIME)) - ->condition('fid', $feed['fid']) - ->execute(); - drupal_set_message(t('There is no new syndicated content from %site.', array('%site' => $feed['title']))); - break; - case 301: - $feed['url'] = $result->redirect_url; - // Do not break here. - case 200: - case 302: - case 307: - // We store the md5 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. - $md5 = md5($result->data); - if ($feed['hash'] == $md5) { - db_update('aggregator_feed') - ->condition('fid', $feed['fid']) - ->fields(array('checked' => REQUEST_TIME)) - ->execute(); - drupal_set_message(t('There is no new syndicated content from %site.', array('%site' => $feed['title']))); - break; + $parser = variable_get('aggregator_parser', 'aggregator'); + $channel = module_invoke($parser, 'aggregator_parse', 'parse', $feed); + if ($channel === TRUE) { + db_update('aggregator_feed') + ->fields(array('checked' => REQUEST_TIME)) + ->condition('fid', $feed['fid']) + ->execute(); + drupal_set_message(t('There is no new syndicated content from %site.', array('%site' => $feed['title']))); + return; + } + if ($channel === FALSE) { + return; + } + if (is_array($channel)) { + $channel['fid'] = $feed['fid']; + $channel['url'] = $feed['url']; + // Let implementing modules process feed items. + if (is_array($channel['items'])) { + $processors = aggregator_get_enabled_processors(); + foreach ($processors as $processor) { + $channel = module_invoke($processor, 'aggregator_process', 'unique', $channel); } + foreach ($processors as $processor) { + $channel = module_invoke($processor, 'aggregator_process', 'save', $channel); + } + } + + $image = isset($channel['image']) ? $channel['image'] : FALSE; - // Filter the input data. - if (aggregator_parse_feed($result->data, $feed)) { - $modified = empty($result->headers['Last-Modified']) ? 0 : strtotime($result->headers['Last-Modified']); - - // Prepare the channel data. - foreach ($channel as $key => $value) { - $channel[$key] = trim($value); - } - - // Prepare the image data (if any). - foreach ($image as $key => $value) { - $image[$key] = trim($value); - } - - if (!empty($image['LINK']) && !empty($image['URL']) && !empty($image['TITLE'])) { - $image = l(theme('image', $image['URL'], $image['TITLE']), $image['LINK'], array('html' => TRUE)); - } - else { - $image = ''; - } - - $etag = empty($result->headers['ETag']) ? '' : $result->headers['ETag']; - // Update the feed data. - db_merge('aggregator_feed') - ->key(array('fid' => $feed['fid'])) - ->fields(array( - 'url' => $feed['url'], - 'checked' => REQUEST_TIME, - 'link' => $channel['LINK'], - 'description' => $channel['DESCRIPTION'], - 'image' => $image, - 'hash' => $md5, - 'etag' => $etag, - 'modified' => $modified, - )) - ->execute(); + // Prepare the image data (if any). + if (is_array($image)) { + foreach ($image as $key => $value) { + $image[$key] = trim($value); + } + } - // Clear the cache. - cache_clear_all(); + if (!empty($image['LINK']) && !empty($image['URL']) && !empty($image['TITLE'])) { + $image = l(theme('image', $image['URL'], $image['TITLE']), $image['LINK'], array('html' => TRUE)); + } + else { + $image = ''; + } - if (isset($result->redirect_url)) { - watchdog('aggregator', 'Updated URL for feed %title to %url.', array('%title' => $feed['title'], '%url' => $feed['url'])); - } + // Update the feed data. + db_merge('aggregator_feed') + ->key(array('fid' => $feed['fid'])) + ->fields(array( + 'url' => $feed['url'], + 'checked' => REQUEST_TIME, + 'link' => $channel['link'], + 'description' => $channel['description'], + 'image' => $image, + 'hash' => $channel['md5'], + 'etag' => $channel['etag'], + 'modified' => $channel['modified'], + )) + ->execute(); - watchdog('aggregator', 'There is new syndicated content from %site.', array('%site' => $feed['title'])); - drupal_set_message(t('There is new syndicated content from %site.', array('%site' => $feed['title']))); - } - break; - default: - watchdog('aggregator', 'The feed from %site seems to be broken, due to "%error".', array('%site' => $feed['title'], '%error' => $result->code . ' ' . $result->error), WATCHDOG_WARNING); - drupal_set_message(t('The feed from %site seems to be broken, because of error "%error".', array('%site' => $feed['title'], '%error' => $result->code . ' ' . $result->error))); - module_invoke('system', 'check_http_request'); + // Clear the cache. + cache_clear_all(); } } @@ -783,9 +883,9 @@ * @param $feed * An associative array describing the feed to be parsed. * @return - * FALSE on error, TRUE otherwise. + * FALSE on error, the parsed feed otherwise */ -function aggregator_parse_feed(&$data, $feed) { +function aggregator_parse_feed($data, $feed) { global $items, $image, $channel; // Unset the global variables before we use them. @@ -793,6 +893,7 @@ $items = array(); $image = array(); $channel = array(); + $channel['items'] = array(); // Parse the data. $xml_parser = drupal_xml_parser_create($data); @@ -810,97 +911,61 @@ // item first. In the database, the newest item should be at the top. $items = array_reverse($items); - // Initialize variables. - $title = $link = $author = $description = $guid = NULL; foreach ($items as $item) { - unset($title, $link, $author, $description, $guid); // Prepare the item: foreach ($item as $key => $value) { - $item[$key] = trim($value); + $item[strtolower($key)] = trim($value); } // Resolve the item's title. If no title is found, we use up to 40 // characters of the description ending at a word boundary, but not // splitting potential entities. - if (!empty($item['TITLE'])) { - $title = $item['TITLE']; - } - elseif (!empty($item['DESCRIPTION'])) { - $title = preg_replace('/^(.*)[^\w;&].*?$/', "\\1", truncate_utf8($item['DESCRIPTION'], 40)); - } - else { - $title = ''; + if (empty($item['title']) && !empty($item['description'])) { + $item['title'] = preg_replace('/^(.*)[^\w;&].*?$/', "\\1", truncate_utf8($item['description'], 40)); } // Resolve the items link. - if (!empty($item['LINK'])) { - $link = $item['LINK']; + if (empty($item['link'])) { + $item['link'] = $feed['link']; } - else { - $link = $feed['link']; - } - $guid = isset($item['GUID']) ? $item['GUID'] : ''; + $item['guid'] = isset($item['guid']) ? $item['guid'] : ''; // Atom feeds have a CONTENT and/or SUMMARY tag instead of a DESCRIPTION tag. - if (!empty($item['CONTENT:ENCODED'])) { - $item['DESCRIPTION'] = $item['CONTENT:ENCODED']; + if (!empty($item['content:encoded'])) { + $item['description'] = $item['content:encoded']; } - elseif (!empty($item['SUMMARY'])) { - $item['DESCRIPTION'] = $item['SUMMARY']; + elseif (!empty($item['summary'])) { + $item['description'] = $item['summary']; } - elseif (!empty($item['CONTENT'])) { - $item['DESCRIPTION'] = $item['CONTENT']; + elseif (!empty($item['content'])) { + $item['description'] = $item['content']; } // Try to resolve and parse the item's publication date. $date = ''; - foreach (array('PUBDATE', 'DC:DATE', 'DCTERMS:ISSUED', 'DCTERMS:CREATED', 'DCTERMS:MODIFIED', 'ISSUED', 'CREATED', 'MODIFIED', 'PUBLISHED', 'UPDATED') as $key) { + foreach (array('pubdate', 'dc:date', 'dcterms:issued', 'dcterms:created', 'dcterms:modified', 'issued', 'created', 'modified', 'published', 'updated') as $key) { if (!empty($item[$key])) { $date = $item[$key]; break; } } - $timestamp = strtotime($date); - - if ($timestamp === FALSE) { - $timestamp = aggregator_parse_w3cdtf($date); // Aggregator_parse_w3cdtf() returns FALSE on failure. - } + $item['timestamp'] = strtotime($date); - // Save this item. Try to avoid duplicate entries as much as possible. If - // we find a duplicate entry, we resolve it and pass along its ID is such - // that we can update it if needed. - if (!empty($guid)) { - $entry = db_query("SELECT iid, timestamp FROM {aggregator_item} WHERE fid = :fid AND guid = :guid", array(':fid' => $feed['fid'], ':guid' => $guid))->fetchObject(); - } - elseif ($link && $link != $feed['link'] && $link != $feed['url']) { - $entry = db_query("SELECT iid, timestamp FROM {aggregator_item} WHERE fid = :fid AND link = :link", array(':fid' => $feed['fid'], ':link' => $link))->fetchObject(); + if ($item['timestamp'] === FALSE) { + $item['timestamp'] = aggregator_parse_w3cdtf($date); // Aggregator_parse_w3cdtf() returns FALSE on failure. } else { - $entry = db_query("SELECT iid, timestamp FROM {aggregator_item} WHERE fid = :fid AND title = :title", array(':fid' => $feed['fid'], ':title' => $title))->fetchObject(); - } - - if (!$timestamp) { - $timestamp = isset($entry->timestamp) ? $entry->timestamp : REQUEST_TIME; + $item['timestamp'] = REQUEST_TIME; } - $item += array('AUTHOR' => '', 'DESCRIPTION' => ''); - aggregator_save_item(array('iid' => (isset($entry->iid) ? $entry->iid : ''), 'fid' => $feed['fid'], 'timestamp' => $timestamp, 'title' => $title, 'link' => $link, 'author' => $item['AUTHOR'], 'description' => $item['DESCRIPTION'], 'guid' => $guid)); + $item += array('author' => ''); + $channel['items'][] = $item; } - - // Remove all items that are older than flush item timer. - $age = REQUEST_TIME - variable_get('aggregator_clear', 9676800); - $iids = db_query('SELECT iid FROM {aggregator_item} WHERE fid = :fid AND timestamp < :timestamp', array(':fid' => $feed['fid'], ':timestamp' => $age))->fetchCol(); - if ($iids) { - db_delete('aggregator_category_item') - ->condition('iid', $iids, 'IN') - ->execute(); - db_delete('aggregator_item') - ->condition('iid', $iids, 'IN') - ->execute(); + if (!empty($image)) { + $channel['image'] = $image; } - - return TRUE; + return $channel; } /** @@ -910,7 +975,7 @@ * An associative array describing the item to be added/edited/deleted. */ function aggregator_save_item($edit) { - if ($edit['title'] && empty($edit['iid'])) { + if ($edit['title'] && $edit['iid'] === TRUE) { $edit['iid'] = db_insert('aggregator_item') ->fields(array( 'title' => $edit['title'], @@ -963,6 +1028,34 @@ } /** + * Returns the enabled parser. + * + * @return + * A string that is the name of the module that implements the currently + * enabled parser. + */ +function aggregator_get_enabled_parser() { + return variable_get('aggregator_parser', ''); +} + +/** + * Returns enabled processors. + * + * @return + * An array of strings that are the names of the modules that implement the + * currently enabled processors. + */ +function aggregator_get_enabled_processors() { + $processors = variable_get('aggregator_processors', array()); + foreach ($processors as $k => $v) { + if ($v === 0) { + unset($processors[$k]); + } + } + return $processors; +} + +/** * Load an aggregator category. * * @param $cid @@ -991,7 +1084,7 @@ * @ingroup themeable */ function theme_aggregator_block_item($item, $feed = 0) { - + // Display the external link to the item. $output .= '' . check_plain($item->title) . "\n"; @@ -1021,3 +1114,28 @@ function _aggregator_items($count) { return format_plural($count, '1 item', '@count items'); } + +/** + * Checks for time limits in cron processing. + */ +function _aggregator_cron_time() { + static $time_limit; + $execute_percentage = 0.5; + if (!$time_limit) { + $time_limit = REQUEST_TIME + ($execute_percentage / 100) * ini_get('max_execution_time'); + // However, check for left time, maybe some other cron processing already occured. + $time_limit = min($time_limit, variable_get('cron_semaphore', 0) + ini_get('max_execution_time')); + } + return max($time_limit - time(), 0); +} + +function _aggregator_light_delete_expired() { + // Remove all items that are older than flush item timer. + $age = REQUEST_TIME - variable_get('aggregator_clear', 9676800); + $iids = db_query('SELECT iid FROM {aggregator_item} WHERE timestamp < :timestamp', array(':timestamp' => $age))->fetchCol(); + if ($iids) { + db_delete('aggregator_category_item') + ->condition('iid', $iids, 'IN') + ->execute(); + } +}