diff options
Diffstat (limited to 'modules/aggregator.module')
-rw-r--r-- | modules/aggregator.module | 393 |
1 files changed, 189 insertions, 204 deletions
diff --git a/modules/aggregator.module b/modules/aggregator.module index 1983718bc..91752bf8a 100644 --- a/modules/aggregator.module +++ b/modules/aggregator.module @@ -1,13 +1,10 @@ <?php -// $Id$ - - -function aggregator_help($section = "admin/help#aggregator") { - $output = ""; +/* $Id$ */ +function aggregator_help($section) { switch ($section) { case 'admin/help#aggregator': - $output .= "<p>Thousands of web sites, especially news sites and weblogs, syndicate their most recent site content for others to display. The syndicated content always includes titles, also known as headlines, for the newest published stories. Each headline acts as a direct link to the stories on the remote site. Along with the headline, most sites typically provide either the first few paragraphs of the story or a short summary. Many individuals use client-based news aggregators on their personal computer to aggregate content, such as %amphetadesk</p>"; + $output = "<p>Thousands of web sites, especially news sites and weblogs, syndicate their most recent site content for others to display. The syndicated content always includes titles, also known as headlines, for the newest published stories. Each headline acts as a direct link to the stories on the remote site. Along with the headline, most sites typically provide either the first few paragraphs of the story or a short summary. Many individuals use client-based news aggregators on their personal computer to aggregate content, such as %amphetadesk</p>"; $output .= "<p>Drupal also has a news aggregator built in as a standard feature. With it, you can subscribe to feeds from other sites and display their content for your site users. Simply enable the aggregator module in site administration and enter the feeds that you choose.</p>"; $output .= "<h3>What do I need to subscribe to a feed?</h3>"; $output .= "<p>The standard method of syndication is using the XML-based %rss (RSS). To syndicate a site's content, obtain the full URL of the RSS page providing syndication. Common file tags for RSS pages are .rss, .xml and .rdf. Example: %slashdot-rss.</p>"; @@ -50,84 +47,77 @@ function aggregator_help($section = "admin/help#aggregator") { $output .= "</ul>"; $output .= "<h3>RSS feed blocks</h3>"; $output .= "<p>In addition to providing subscribed content through the news aggregator, Drupal automatically creates a block for each subscribed feed and every bundle created. Beside each headline in each block, Drupal includes an icon which acts a blog it link. Enable any or all of the blocks using block management.</p>"; - $output = t($output, array("%amphetadesk" => "<a href=\"http://www.disobey.com/amphetadesk/\">AmphetaDesk</a>", "%rss" => "<a href=\"http://groups.yahoo.com/group/rss-dev/files/specification.html\">Rich Site Summary</a>", "%slashdot-rss" => "<a href=\"http://slashdot.org/slashdot.rdf\">http://slashdot.org/slashdot.rdf</a>", "%syndic8" => "<a href=\"http://www.syndic8.com/\">Syndic8</a>", "%rss-what" => "<a href=\"http://www.xml.com/pub/a/2002/12/18/dive-into-xml.html\">What is RSS</a>", "%rss-evolution" => "<a href=\"http://www.webreference.com/authoring/languages/xml/rss/1/\">The Evolution of RSS</a>", "%admin-news" => l(t("RSS/RDF"), "admin/node/syndication/news"), "%new-feed" => l(t("new feed"), "admin/node/syndication/news/add/feed"), "%update-items" => l(t("update items"), "admin/node/syndication/news"))); - break; + $output .= "<h3>Subscription list</h3>"; + $output .= "<p>Drupal automatically generates an OPML feed file that is available by selecting the XML icon on the News Sources page.</p>"; + $output .= "<h3>Technical details</h3>"; + $output .= "<p>When fetching feeds Drupal supports conditional GETs, this reduces the bandwidth usage for feeds that have not been updated since the last check.</p>"; + $output .= "<p>If a feed is permanently moved to a new location Drupal will automatically update the feed URL to the new address.</p>"; + return t($output, array("%amphetadesk" => "<a href=\"http://www.disobey.com/amphetadesk/\">AmphetaDesk</a>", "%rss" => "<a href=\"http://groups.yahoo.com/group/rss-dev/files/specification.html\">Rich Site Summary</a>", "%slashdot-rss" => "<a href=\"http://slashdot.org/slashdot.rdf\">http://slashdot.org/slashdot.rdf</a>", "%syndic8" => "<a href=\"http://www.syndic8.com/\">Syndic8</a>", "%rss-what" => "<a href=\"http://www.xml.com/pub/a/2002/12/18/dive-into-xml.html\">What is RSS</a>", "%rss-evolution" => "<a href=\"http://www.webreference.com/authoring/languages/xml/rss/1/\">The Evolution of RSS</a>", "%admin-news" => l(t("RSS/RDF"), "admin/node/syndication/news"), "%new-feed" => l(t("new feed"), "admin/node/syndication/news/add/feed"), "%update-items" => l(t("update items"), "admin/node/syndication/news"))); case 'admin/system/modules#description': - $output = t("Used to aggregate syndicated content (RSS and RDF)."); - break; + return t("Used to aggregate syndicated content (RSS and RDF)."); case 'admin/system/modules/aggregator': - $output = t("Drupal's news aggregator controls how many RSS/RDF items from a single source are displayed in a \"Block\", and on the page that goes with that block."); - break; + return t("Drupal's news aggregator controls how many RSS/RDF items from a single source are displayed in a \"Block\", and on the page that goes with that block."); case 'admin/node/syndication/news': - $output = t("Several web sites, especially news related sites, syndicate parts of their site's content for other web sites to display. Usually, the syndicated content includes the latest headlines with a direct link to that story on the remote site. Some syndicated content also includes a description of the headline. The standard method of syndication is using the XML based Rich Site Summary (RSS). To get a feed to work you <strong>must</strong> run \"cron.php\". To display the feed in a block you must turn on the %block. <br /><ul><li>To delete a feed choose \"edit feed\"</li><li>To clear all of the entries from a feed choose \"Remove items\"</li><li>To check whether a feed is working, and to get new items <strong>now</strong> click on \"update items\"</li></ul><ul><li>To delete a bundle choose \"edit bundle\".</li></ul>", array("%block" => l(t("feed's block"), "admin/system/block"))); - break; + return t("Several web sites, especially news related sites, syndicate parts of their site's content for other web sites to display. Usually, the syndicated content includes the latest headlines with a direct link to that story on the remote site. Some syndicated content also includes a description of the headline. The standard method of syndication is using the XML based Rich Site Summary (RSS). To get a feed to work you <strong>must</strong> run \"cron.php\". To display the feed in a block you must turn on the %block. <br /><ul><li>To delete a feed choose \"edit feed\"</li><li>To clear all of the entries from a feed choose \"Remove items\"</li><li>To check whether a feed is working, and to get new items <strong>now</strong> click on \"update items\"</li></ul><ul><li>To delete a bundle choose \"edit bundle\".</li></ul>", array("%block" => l(t("feed's block"), "admin/system/block"))); case 'admin/node/syndication/news/add/feed': - $output = t("Add a site that has an RSS/RDF feed. The URL is the full path to the RSS feed file. For the feed to update automatically you must run \"cron.php\". The \"Attributes\" are used to bundle this feed with other feeds (See %bundle), and to tag articles from this feed.<br />Note: If you already have a feed with the URL you are planning to use, the system will not accept another feed with the same URL.", array("%bundle" => l(t("add new bundle"), "admin/node/syndication/news/add/bundle"))); - break; + return t("Add a site that has an RSS/RDF feed. The URL is the full path to the RSS feed file. For the feed to update automatically you must run \"cron.php\". The \"Attributes\" are used to bundle this feed with other feeds (See %bundle), and to tag articles from this feed.<br />Note: If you already have a feed with the URL you are planning to use, the system will not accept another feed with the same URL.", array("%bundle" => l(t("add new bundle"), "admin/node/syndication/news/add/bundle"))); case 'admin/node/syndication/news/add/bundle': - $output = t("Bundles provide a generalized way of creating composite feeds. They allow you, for example, to combine various sport-related feeds into one bundle called <i>Sport</i>. If an article from a feed has been \"tag\"-ged (See %tag too look at and change tags.) with a matching \"Attribute\" then it will be added to the bundle.", array("%tag" => l(t("tag news item"), "admin/node/syndication/news/tag"))); - break; + return t("Bundles provide a generalized way of creating composite feeds. They allow you, for example, to combine various sport-related feeds into one bundle called <i>Sport</i>. If an article from a feed has been \"tag\"-ged (See %tag too look at and change tags.) with a matching \"Attribute\" then it will be added to the bundle.", array("%tag" => l(t("tag news item"), "admin/node/syndication/news/tag"))); case 'admin/node/syndication/news/tag': - $output = t("This allows you to see and change an news item's \"tag\". All articles are originally tagged with the \"Attributes\" of their feed."); - break; + return t("This allows you to see and change an news item's \"tag\". All articles are originally tagged with the \"Attributes\" of their feed."); } - - return $output; } function aggregator_help_page() { - print theme("page", aggregator_help()); + print theme('page', aggregator_help('admin/help#aggregator')); } function aggregator_settings() { $number = array(5 => 5, 10 => 10, 15 => 15, 20 => 20, 25 => 25, 30 => 30, 35 => 35, 40 => 40, 45 => 45, 50 => 50, 55 => 55, 60 => 60, 65 => 65, 70 => 70, 75 => 75, 80 => 80, 85 => 85, 90 => 90, 95 => 95, 100 => 100); - $output .= form_select(t("Items per block"), "aggregator_block_limit", variable_get("aggregator_block_limit", 15), $number, t("The maximum number of news items displayed in one block.")); - $output .= form_select(t("Items per page"), "aggregator_page_limit", variable_get("aggregator_page_limit", 75), $number, t("The maximum number of news items displayed on one page.")); + + $output = form_select(t('Items per block'), 'aggregator_block_limit', variable_get('aggregator_block_limit', 15), $number, t('The maximum number of news items displayed in one block.')); + $output .= form_select(t('Items per page'), 'aggregator_page_limit', variable_get('aggregator_page_limit', 75), $number, t('The maximum number of news items displayed on one page.')); return $output; } function aggregator_perm() { - return array("administer news feeds", "access news feeds"); + return array('administer news feeds', 'access news feeds'); } function aggregator_link($type) { - - $links = array(); - - if ($type == "page" && user_access("access news feeds")) { - $links[] = l(t("news feeds"), "aggregator", array("title" => t("Read the latest news from syndicated web sites."))); - } - - if ($type == "system") { - if (user_access("administer news feeds")) { - menu("admin/node/syndication", t("syndication"), NULL, 5); - menu("admin/node/syndication/news", t("RSS/RDF"), "aggregator_admin"); - menu("admin/node/syndication/news/add/feed", t("new feed"), "aggregator_admin", 2); - menu("admin/node/syndication/news/add/bundle", t("new bundle"), "aggregator_admin", 3); - menu("admin/node/syndication/news/tag", t("tag items"), "aggregator_admin", 4); - menu("admin/node/syndication/news/help", t("help"), "aggregator_help_page", 9); + if ($type == 'page' && user_access('access news feeds')) { + return array(l(t('news feeds'), 'aggregator', array('title' => t('Read the latest news from syndicated web sites.')))); + } + + if ($type == 'system') { + if (user_access('administer news feeds')) { + menu('admin/node/syndication', t('syndication'), NULL, 5); + menu('admin/node/syndication/news', t('RSS/RDF'), 'aggregator_admin'); + menu('admin/node/syndication/news/add/feed', t('new feed'), 'aggregator_admin', 2); + menu('admin/node/syndication/news/add/bundle', t('new bundle'), 'aggregator_admin', 3); + menu('admin/node/syndication/news/tag', t('tag items'), 'aggregator_admin', 4); + menu('admin/node/syndication/news/help', t('help'), 'aggregator_help_page', 9); } - if (user_access("access news feeds")) { - menu("aggregator", t('news aggregator'), 'aggregator_page', 5); - menu("aggregator/feeds", t('news by source'), 'aggregator_page'); - menu("aggregator/bundles", t('news by topic'), 'aggregator_page'); - menu("aggregator/sources", t('news sources'), 'aggregator_page'); + + if (user_access('access news feeds')) { + menu('aggregator', t('news aggregator'), 'aggregator_page', 5); + menu('aggregator/feeds', t('news by source'), 'aggregator_page'); + menu('aggregator/bundles', t('news by topic'), 'aggregator_page'); + menu('aggregator/sources', t('news sources'), 'aggregator_page'); } } - - return $links; } function aggregator_cron() { - $result = db_query("SELECT * FROM {feed} WHERE timestamp + refresh < ". time()); + $result = db_query("SELECT * FROM {feed} WHERE checked + refresh < %d", time()); while ($feed = db_fetch_array($result)) { aggregator_refresh($feed); } } function aggregator_update() { - $result = db_query("SELECT * FROM {feed} "); + $result = db_query("SELECT * FROM {feed}"); while ($feed = db_fetch_array($result)) { aggregator_refresh($feed); } @@ -147,12 +137,13 @@ function theme_aggregator_format_item($item, $feed = 0) { } function aggregator_bundle_block($attributes) { - if ($attributes) { $keys = explode(",", $attributes); - foreach ($keys as $key) $where[] = "attributes LIKE '%". trim($key) ."%'"; + foreach ($keys as $key) { + $where[] = "attributes LIKE '%". check_query(trim($key)) ."%'"; + } - $result = db_query_range("SELECT * FROM {item} WHERE ". implode(" OR ", $where) ." ORDER BY iid DESC", 0, variable_get("aggregator_block_limit", 15)); + $result = db_query_range("SELECT * FROM {item} WHERE ". implode(" OR ", $where) ." ORDER BY timestamp DESC", 0, variable_get("aggregator_block_limit", 15)); } $items = array(); @@ -164,7 +155,7 @@ function aggregator_bundle_block($attributes) { } function aggregator_feed_block($feed) { - $result = db_query_range("SELECT * FROM {item} WHERE fid = %d ORDER BY iid DESC ", $feed->fid, 0, variable_get("aggregator_block_limit", 15)); + $result = db_query_range("SELECT * FROM {item} WHERE fid = %d ORDER BY timestamp DESC ", $feed->fid, 0, variable_get("aggregator_block_limit", 15)); $items = array(); while ($item = db_fetch_object($result)) { @@ -175,18 +166,18 @@ function aggregator_feed_block($feed) { } function aggregator_block($op, $delta) { - if (user_access("access news feeds")) { + if (user_access('access news feeds')) { if ($op == "list") { $result = db_query("SELECT * FROM {bundle} ORDER BY title"); while ($bundle = db_fetch_object($result)) { $block["bundle:$bundle->bid"]["info"] = "$bundle->title bundle"; } - + $result = db_query("SELECT * FROM {feed} ORDER BY fid"); while ($feed = db_fetch_object($result)) { $block["feed:$feed->fid"]["info"] = "$feed->title feed"; } - + return $block; } else { @@ -197,21 +188,19 @@ function aggregator_block($op, $delta) { $block["subject"] = $feed->title; $block["content"] .= aggregator_feed_block($feed) ."<div class=\"more-link\">". l(t("more"), "aggregator/feed/$feed->fid", array("title" => t("View this feed's recent news."))) ."</div>"; break; - case "bundle": $bundle = db_fetch_object(db_query("SELECT * FROM {bundle} WHERE bid = %d", $id)); $block["subject"] = $bundle->title; $block["content"] .= aggregator_bundle_block($bundle->attributes) ."<div class=\"more-link\">". l(t("more"), "aggregator/bundle/$bundle->bid", array("title" => t("View this bundle's recent news."))) ."</div>"; break; } - + return $block; } } } function aggregator_get_bundles($attributes = 0) { - $block = array(); $result = db_query("SELECT * FROM {bundle} ORDER BY title"); @@ -225,7 +214,6 @@ function aggregator_get_bundles($attributes = 0) { } function aggregator_get_feeds($attributes = 0) { - $block = array(); $result = db_query("SELECT * FROM {feed} ORDER BY fid"); @@ -264,7 +252,7 @@ function aggregator_element_start($parser, $name, $attributes) { function aggregator_element_end($parser, $name) { global $element; - switch ($name) { + switch ($name) { case "IMAGE": case "TEXTINPUT": case "ITEM": @@ -294,129 +282,145 @@ function aggregator_element_data($parser, $data) { } function aggregator_refresh($feed) { + // 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 reponse code. + switch ($result->code) { + case 304: + db_query("UPDATE {feed} SET checked = %d WHERE fid = %d", time(), $feed['fid']); + return t("no new syndicated content from '%site'.", array('%site' => $feed['title'])); + case 301: + $feed['url'] = $result->redirect_url; + watchdog('special', "aggregator: updated URL for feed '$feed[title]' to $feed[url]"); + case 200: + case 302: + case 307: + // Filter the input data: + if (!valid_input_data($result->data)) { + return t("failed to parse RSS feed '%site': suspicious input data.", array("%site" => $feed["title"])); + } - // unset the global variables before we use them: - unset($GLOBALS["channel"], $GLOBALS["element"], $GLOBALS["item"], $GLOBALS["items"], $GLOBALS["tag"]); + $channel = aggregator_parse_feed($result->data, $feed); - // after we unset the variables, we can global them again: - global $items, $channel; + if ($result->headers['Last-Modified']) { + $modified = strtotime($result->headers['Last-Modified']); + } - /* - ** Check whether the feed is properly configured: - */ + db_query("UPDATE {feed} SET url = '%s', checked = %d, link = '%s', description = '%s', etag = '%s', modified = %d WHERE fid = %d", $feed['url'], time(), $channel["LINK"], $channel["DESCRIPTION"], $result->headers['ETag'], $modified, $feed["fid"]); - if (!ereg("^http://|ftp://", $feed["url"])) { - return t("failed to parse RSS feed '%site': incorrect or missing URL.", array("%site" => $feed["title"])); + return t("syndicated content from '%site'.", array("%site" => $feed["title"])); + default: + return t("failed to parse RSS feed '%site': %error.", array('%site' => $feed['title'], '%error' => $result->code .' '. $result->error)); } +} - /* - ** Grab the news items: - */ - - if ($fp = @fopen($feed["url"], "r")) { - // fetch data: - while (!feof($fp)) { - $data .= fgets($fp, 128); - } - fclose($fp); - - // filter the input data: - if (!valid_input_data($data)) { - return t("failed to parse RSS feed '%site': suspicious input data.", array("%site" => $feed["title"])); - } - - // parse the data: - $xml_parser = drupal_xml_parser_create($data); - xml_set_element_handler($xml_parser, "aggregator_element_start", "aggregator_element_end"); - xml_set_character_data_handler($xml_parser, "aggregator_element_data"); +function aggregator_parse_feed(&$data, $feed) { + global $items, $channel; - if (!xml_parse($xml_parser, $data, 1)) { - return t("failed to parse RSS feed '%site': %error at line %line.", array("%site" => $feed["title"], "%error" => xml_error_string(xml_get_error_code($xml_parser)), "%line" => xml_get_current_line_number($xml_parser))); - } - xml_parser_free($xml_parser); + // Unset the global variables before we use them: + unset($GLOBALS["element"], $GLOBALS["item"], $GLOBALS["tag"]); + $items = array(); + $channel = array(); - // initialize the translation table: - $tt = array_flip(get_html_translation_table(HTML_ENTITIES)); - $tt["'"] = "'"; + // parse the data: + $xml_parser = drupal_xml_parser_create($data); + xml_set_element_handler($xml_parser, "aggregator_element_start", "aggregator_element_end"); + xml_set_character_data_handler($xml_parser, "aggregator_element_data"); - db_query("UPDATE {feed} SET timestamp = %d, link = '%s', description = '%s' WHERE fid = %d", time(), $channel["LINK"], $channel["DESCRIPTION"], $feed["fid"]); + if (!xml_parse($xml_parser, $data, 1)) { + return t("failed to parse RSS feed '%site': %error at line %line.", array("%site" => $feed["title"], "%error" => xml_error_string(xml_get_error_code($xml_parser)), "%line" => xml_get_current_line_number($xml_parser))); + } + xml_parser_free($xml_parser); - /* - ** We reverse the array such that we store the first item last, - ** and the last item first. In the database, the newest item - ** should be at the top. - */ + // initialize the translation table: + $tt = array_flip(get_html_translation_table(HTML_ENTITIES)); + $tt["'"] = "'"; - $items = array_reverse($items); + /* + ** We reverse the array such that we store the first item last, + ** and the last item first. In the database, the newest item + ** should be at the top. + */ - foreach ($items as $item) { - unset($title, $link, $author, $description); + $items = array_reverse($items); - // Prepare the item: - foreach ($item as $key => $value) { - $item[$key] = filter_default(strtr(trim($value), $tt)); - } + foreach ($items as $item) { + unset($title, $link, $author, $description); - if ($item["TITLE"]) { - $title = $item["TITLE"]; - } - else { - /* - ** Use up to 40 characters of the description, ending at - ** word boundary, but don't split potential entities. - */ - $title = preg_replace('/^(.*)[^\w;&].*?$/', "\\1", substr($item["DESCRIPTION"], 0, 40)); - } - - if ($item["LINK"]) { - $link = $item["LINK"]; - } - elseif ($item["GUID"] && (strncmp($item["GUID"], "http://", 7) == 0)) { - $link = $item["GUID"]; - } - else { - $link = $feed["link"]; - } + // Prepare the item: + foreach ($item as $key => $value) { + $item[$key] = filter_default(strtr(trim($value), $tt)); + } + if ($item["TITLE"]) { + $title = $item["TITLE"]; + } + else { /* - ** Save this item. Try to avoid duplicate entries as much as - ** possible. If we find a duplicate entry, we resolve it and - ** pass along it's ID such that we can update it if needed. - */ + ** Use up to 40 characters of the description, ending at + ** word boundary, but don't split potential entities. + */ + $title = preg_replace('/^(.*)[^\w;&].*?$/', "\\1", substr($item["DESCRIPTION"], 0, 40)); + } - if ($link && $link != $feed["link"] && $link != $feed["url"]) { - $entry = db_fetch_object(db_query("SELECT iid FROM {item} WHERE fid = %d AND link = '%s'", $feed["fid"], $link)); - } - else { - $entry = db_fetch_object(db_query("SELECT iid FROM {item} WHERE fid = %d AND title = '%s'", $feed["fid"], $title)); - } + if ($item["LINK"]) { + $link = $item["LINK"]; + } + elseif ($item["GUID"] && (strncmp($item["GUID"], "http://", 7) == 0)) { + $link = $item["GUID"]; + } + else { + $link = $feed["link"]; + } - aggregator_save_item(array(iid => $entry->iid, fid => $feed["fid"], title => $title, link => $link, author => $item["AUTHOR"], description => $item["DESCRIPTION"], attributes => $feed["attributes"])); + if (!$timestamp = strtotime($item['PUBDATE'])) { + $timestamp = time(); } /* - ** Remove all the old, expired items: + ** Save this item. Try to avoid duplicate entries as much as + ** possible. If we find a duplicate entry, we resolve it and + ** pass along it's ID such that we can update it if needed. */ - unset($items); + if ($link && $link != $feed["link"] && $link != $feed["url"]) { + $entry = db_fetch_object(db_query("SELECT iid FROM {item} WHERE fid = %d AND link = '%s'", $feed["fid"], $link)); + } + else { + $entry = db_fetch_object(db_query("SELECT iid FROM {item} WHERE fid = %d AND title = '%s'", $feed["fid"], $title)); + } - $result = db_query("SELECT iid FROM {item} WHERE fid = %d ORDER BY timestamp", $feed["fid"]); + aggregator_save_item(array('iid' => $entry->iid, 'fid' => $feed["fid"], 'timestamp' => $timestamp, 'title' => $title, 'link' => $link, 'author' => $item["AUTHOR"], 'description' => $item["DESCRIPTION"], 'attributes' => $feed["attributes"])); + } + /* + ** Remove all the old, expired items: + */ - while ($item = db_fetch_object($result)) { - $items[] = "iid = '$item->iid'"; - } + unset($items); - if (sizeof($items) > 50) { - db_query("DELETE FROM {item} WHERE ". implode(" OR ", array_slice($items, 0, - 50))); - } + $result = db_query("SELECT iid FROM {item} WHERE fid = %d ORDER BY timestamp", $feed["fid"]); - cache_clear_all(); + while ($item = db_fetch_object($result)) { + $items[] = "iid = '$item->iid'"; } - else { - return t("failed to parse RSS feed '%site': no data.", array("%site" => $feed["tite"])); + + if (sizeof($items) > 50) { + db_query("DELETE FROM {item} WHERE ". implode(" OR ", array_slice($items, 0, - 50))); } - return t("syndicated content from '%site'.", array("%site" => $feed["title"])); + cache_clear_all(); + + return $channel; } function aggregator_save_item($edit) { @@ -427,13 +431,12 @@ function aggregator_save_item($edit) { db_query("DELETE FROM {item} WHERE iid = %d", $edit["iid"]); } else if ($edit["title"] && $edit["link"]) { - db_query("INSERT INTO {item} (fid, title, link, author, description, attributes, timestamp) VALUES (%d, '%s', '%s', '%s', '%s', '%s', %d)", $edit["fid"], $edit["title"], $edit["link"], $edit["author"], $edit["description"], $edit["attributes"], time()); + db_query("INSERT INTO {item} (fid, title, link, author, description, attributes, timestamp) VALUES (%d, '%s', '%s', '%s', '%s', '%s', %d)", $edit["fid"], $edit["title"], $edit["link"], $edit["author"], $edit["description"], $edit["attributes"], $edit["timestamp"]); } } function aggregator_form_bundle($edit = array()) { - - $form .= form_textfield(t("Title"), "title", $edit["title"], 50, 64, t("The name of the bundle.")); + $form = form_textfield(t("Title"), "title", $edit["title"], 50, 64, t("The name of the bundle.")); $form .= form_textfield(t("Attributes"), "attributes", $edit["attributes"], 50, 128, t("A comma-separated list of keywords describing the bundle.")); $form .= form_submit(t("Submit")); @@ -461,7 +464,6 @@ function aggregator_save_bundle($edit) { } function aggregator_form_feed($edit = array()) { - $period = array(900 => format_interval(900), 1800 => format_interval(1800), 3600 => format_interval(3600), 7200 => format_interval(7200), 10800 => format_interval(10800), 21600 => format_interval(21600), 32400 => format_interval(32400), 43200 => format_interval(43200), 64800 => format_interval(64800), 86400 => format_interval(86400), 172800 => format_interval(172800), 259200 => format_interval(259200), 604800 => format_interval(604800), 1209600 => format_interval(1209600), 2419200 => format_interval(2419200)); if ($edit["refresh"] == "") { @@ -515,14 +517,14 @@ function aggregator_get_bundle($bid) { } function aggregator_view() { - $result = db_query("SELECT f.*, COUNT(i.iid) AS items FROM {feed} f LEFT JOIN {item} i ON f.fid = i.fid GROUP BY f.fid, f.title, f.url, f.refresh, f.timestamp, f.attributes, f.link, f.description ORDER BY f.title"); + $result = db_query("SELECT f.*, COUNT(i.iid) AS items FROM {feed} f LEFT JOIN {item} i ON f.fid = i.fid GROUP BY f.fid, f.title, f.url, f.refresh, f.checked, f.attributes, f.link, f.description ORDER BY f.title"); $output .= "<h3>". t("Feed overview") ."</h3>"; $header = array(t("title"), t("attributes"), t("items"), t("last update"), t("next update"), array("data" => t("operations"), "colspan" => 3)); - unset($rows); + $rows = array(); while ($feed = db_fetch_object($result)) { - $rows[] = array($feed->title, $feed->attributes, format_plural($feed->items, "1 item", "%count items"), ($feed->timestamp ? t("%time ago", array("%time" => format_interval(time() - $feed->timestamp))) : t("never")), ($feed->timestamp ? t("%time left", array("%time" => format_interval($feed->timestamp + $feed->refresh - time()))) : t("never")), l(t("edit feed"), "admin/node/syndication/news/edit/feed/$feed->fid"), l(t("remove items"), "admin/node/syndication/news/remove/$feed->fid"), l(t("update items"), "admin/node/syndication/news/update/$feed->fid")); + $rows[] = array($feed->title, $feed->attributes, format_plural($feed->items, "1 item", "%count items"), ($feed->checked ? t("%time ago", array("%time" => format_interval(time() - $feed->checked))) : t("never")), ($feed->checked ? t("%time left", array("%time" => format_interval($feed->checked + $feed->refresh - time()))) : t("never")), l(t("edit feed"), "admin/node/syndication/news/edit/feed/$feed->fid"), l(t("remove items"), "admin/node/syndication/news/remove/$feed->fid"), l(t("update items"), "admin/node/syndication/news/update/$feed->fid")); } $output .= theme("table", $header, $rows); @@ -531,7 +533,7 @@ function aggregator_view() { $output .= "<h3>". t("Bundle overview") ."</h3>"; $header = array(t("title"), t("attributes"), t("operations")); - unset($rows); + $rows = array(); while ($bundle = db_fetch_object($result)) { $rows[] = array($bundle->title, $bundle->attributes, l(t("edit bundle"), "admin/node/syndication/news/edit/bundle/$bundle->bid")); } @@ -541,8 +543,7 @@ function aggregator_view() { } function aggregator_tag() { - - $result = db_query_range("SELECT i.*, f.title AS feed FROM {item} i INNER JOIN {feed} f ON i.fid = f.fid ORDER BY i.iid DESC", 0, 50); + $result = db_query_range("SELECT i.*, f.title AS feed FROM {item} i INNER JOIN {feed} f ON i.fid = f.fid ORDER BY i.timestamp DESC", 0, 50); $header = array(t("date"), t("feed"), t("news item")); while ($item = db_fetch_object($result)) { @@ -556,16 +557,11 @@ function aggregator_tag() { } function aggregator_admin() { - $op = $_POST["op"]; $edit = $_POST["edit"]; if (user_access("administer news feeds")) { - if (empty($op)) { - $op = arg(4); - } - - switch ($op) { + switch ($_POST["op"] ? $_POST["op"] : arg(4)) { case "add": if (arg(5) == "bundle") { $output = aggregator_form_bundle(); @@ -619,12 +615,11 @@ function aggregator_admin() { } function aggregator_page_last() { + $result = db_query_range("SELECT i.*, f.title AS ftitle, f.link AS flink FROM {item} i INNER JOIN {feed} f ON i.fid = f.fid ORDER BY i.timestamp DESC", 0, variable_get("aggregator_page_limit", 75)); - - $result = db_query_range("SELECT i.*, f.title AS ftitle, f.link AS flink FROM {item} i INNER JOIN {feed} f ON i.fid = f.fid ORDER BY i.iid DESC", 0, variable_get("aggregator_page_limit", 75)); - - $output .= "<table border=\"0\" cellpadding=\"4\" cellspacing=\"2\">"; + $output = "<table border=\"0\" cellpadding=\"4\" cellspacing=\"2\">"; while ($item = db_fetch_object($result)) { + $links = array(format_date($item->timestamp)); if (module_exist("blog") && user_access("maintain personal blog")) { $links[] = l(t("blog it"), "node/add/blog", array("title" => t("Comment on this news item in your personal blog.")), "iid=$item->iid"); } @@ -637,8 +632,6 @@ function aggregator_page_last() { if ($item->description) { $output .= "<tr><td colspan=\"2\"><div style=\"margin-left: 20px;\">$item->description</div><br /></td></tr>"; } - - unset($links); } $output .= "</table>\n"; @@ -646,18 +639,17 @@ function aggregator_page_last() { } function aggregator_page_feed($fid) { - - $feed = db_fetch_object(db_query("SELECT * FROM {feed} WHERE fid = %d", $fid)); - $header .= "<p><strong>". t("Website") .":</strong><div style=\"margin-left: 20px;\"><a href=\"$feed->link\">$feed->link</a></div></p>"; + $header = "<p><strong>". t("Website") .":</strong><div style=\"margin-left: 20px;\"><a href=\"$feed->link\">$feed->link</a></div></p>"; $header .= "<p><strong>". t("Description") .":</strong><div style=\"margin-left: 20px;\">$feed->description</div></p>"; - $header .= "<p><strong>". t("Last update") .":</strong><div style=\"margin-left: 20px; text-align: right;\">". t("%time ago", array("%time" => format_interval(time() - $feed->timestamp))) ." <a href=\"$feed->url\"><img src=\"". theme("image", "xml.gif") ."\" width=\"36\" height=\"14\" style=\"border: 0px;\" alt=\"\" title=\"\" /></a><br /><br /></div></p>\n"; + $header .= "<p><strong>". t("Last update") .":</strong><div style=\"margin-left: 20px; text-align: right;\">". t("%time ago", array("%time" => format_interval(time() - $feed->checked))) ." <a href=\"$feed->url\"><img src=\"". theme("image", "xml.gif") ."\" width=\"36\" height=\"14\" style=\"border: 0px;\" alt=\"\" title=\"\" /></a><br /><br /></div></p>\n"; - $result = db_query_range("SELECT * FROM {item} WHERE fid = %d ORDER BY iid DESC", $fid, 0, variable_get("aggregator_page_limit", 75)); + $result = db_query_range("SELECT * FROM {item} WHERE fid = %d ORDER BY timestamp DESC", $fid, 0, variable_get("aggregator_page_limit", 75)); $output .= "<table border=\"0\" cellpadding=\"4\" cellspacing=\"2\">"; while ($item = db_fetch_object($result)) { + $links = array(); if (module_exist("blog") && user_access("maintain personal blog")) { $links[] = l(t("blog it"), "node/add/blog", array("title" => t("Comment on this news item in your personal blog.")), "iid=$item->iid"); } @@ -669,8 +661,6 @@ function aggregator_page_feed($fid) { if ($item->description) { $output .= "<tr><td colspan=\"2\"><div style=\"margin-left: 20px;\">$item->description</div><br /></td></tr>"; } - - unset($links); } $output .= "</table>\n"; @@ -681,8 +671,6 @@ function aggregator_page_feed($fid) { } function aggregator_page_bundle($bid) { - - $bundle = db_fetch_object(db_query("SELECT * FROM {bundle} WHERE bid = %d", $bid)); $header .= "<p><strong>". t("Website") .":</strong><div style=\"margin-left: 20px;\">". l($bundle->title, "aggregator/bundle/$bundle->bid") ."</div></p>"; @@ -690,7 +678,7 @@ function aggregator_page_bundle($bid) { $keys = explode(",", $bundle->attributes); foreach ($keys as $key) $where[] = "i.attributes LIKE '%". trim($key) ."%'"; - $result = db_query_range("SELECT i.*, f.title AS ftitle, f.link AS flink FROM {item} i, {feed} f WHERE (". implode(" OR ", $where) .") AND i.fid = f.fid ORDER BY iid DESC", 0, variable_get("aggregator_page_limit", 75)); + $result = db_query_range("SELECT i.*, f.title AS ftitle, f.link AS flink FROM {item} i, {feed} f WHERE (". implode(" OR ", $where) .") AND i.fid = f.fid ORDER BY timestamp DESC", 0, variable_get("aggregator_page_limit", 75)); $output .= "<table border=\"0\" cellpadding=\"4\" cellspacing=\"2\">"; while ($item = db_fetch_object($result)) { @@ -716,12 +704,9 @@ function aggregator_page_bundle($bid) { print theme("box", $bundle->title, $header); print theme("box", t("Latest news"), $output); print theme("footer"); - } function aggregator_page_sources() { - - $result = db_query("SELECT * FROM {feed} ORDER BY title"); while ($feed = db_fetch_object($result)) { @@ -729,26 +714,28 @@ function aggregator_page_sources() { $output .= "<div style=\"margin-left: 20px;\">$feed->description</div><br />"; } - $output .= "<div style=\"xml-icon\">". l("<img src=\"". theme("image", "xml.gif") ."\" width=\"36\" height=\"14\" style=\"border: 0px;\" />", "aggregator/fd", array("title" => t("View the list of syndicated web sites in XML format."))) ."</div><br />"; + $output .= "<div style=\"xml-icon\">". l("<img src=\"". theme("image", "xml.gif") ."\" width=\"36\" height=\"14\" style=\"border: 0px;\" />", "aggregator/opml", array("title" => t("View the list of syndicated web sites in XML format."))) ."</div><br />"; print theme("page", $output); } -function aggregator_page_fd() { - +function aggregator_page_opml() { $result = db_query("SELECT * FROM {feed} ORDER BY title"); - $output .= "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n"; - $output .= "<rssfeeds version=\"0.1\">\n\n"; + $output = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"; + $output .= "<opml version=\"1.1\">\n"; + $output .= "<head>\n"; + $output .= "<title>". drupal_specialchars(variable_get('site_name', 'Drupal')) ."</title>\n"; + $output .= "<dateModified>". gmdate('r') ."</dateModified>\n"; + $output .= "</head>\n"; + $output .= "<body>\n"; while ($feed = db_fetch_object($result)) { - $output .= "<channel>\n"; - $output .= " <title>". drupal_specialchars($feed->title) ."</title>\n"; - $output .= " <link>". drupal_specialchars($feed->url) ."</link>\n"; - $output .= "</channel>\n\n"; + $output .= '<outline text="'. drupal_specialchars($feed->title) .'" xmlUrl="'. drupal_specialchars($feed->url) ."\" />\n"; } - $output .= "</rssfeeds>\n"; + $output .= "</body>\n"; + $output .= "</opml>\n"; header("Content-Type: text/xml"); @@ -764,8 +751,6 @@ function aggregator_page_feeds() { } function aggregator_page_blocks($blocks) { - - print theme("header"); print "<table cellpadding=\"0\" cellspacing=\"5\" border=\"0\" style=\"width: 100%;\">\n"; print " <tr>\n"; @@ -806,8 +791,8 @@ function aggregator_page() { case "sources": aggregator_page_sources(); break; - case "fd": - aggregator_page_fd(); + case "opml": + aggregator_page_opml(); break; default: aggregator_page_last(); |