Parent Directory
|
Revision Log
|
Revision Graph
Aggregator2: - don't set feed->image to empty string if it's already set to some value.
| 1 | <?php |
| 2 | /* $Id: aggregator2.module,v 1.42 2006/07/20 10:46:58 ahwayakchih Exp $ */ |
| 3 | |
| 4 | /** |
| 5 | * @file |
| 6 | * Used to aggregate syndicated content (RSS, RDF, Atom). |
| 7 | * Sponsored by Sandro Feuillet. |
| 8 | * Sponsored by John Bransford. |
| 9 | * Sponsored by Development Seed. |
| 10 | */ |
| 11 | |
| 12 | /* |
| 13 | Copyright (C) 2005 by Marcin Konicki <ahwayakchih@gmail.com> and Sandro Feuillet <feuillet aat fastmail ddot fm> |
| 14 | Based on parts of Node Aggregator module by Bèr Kessels <ber aat webschuur ddot com>, |
| 15 | and Aggregator module by Drupal team - http://www.drupal.org |
| 16 | Also depends on other modules from Drupal basic distribution and, in some cases, contains parts of their code. |
| 17 | |
| 18 | This program is free software; you can redistribute it and/or modify |
| 19 | it under the terms of the GNU General Public License. |
| 20 | This program is distributed in the hope that it will be useful, |
| 21 | but WITHOUT ANY WARRANTY. |
| 22 | |
| 23 | See the LICENSE file for more details. |
| 24 | */ |
| 25 | |
| 26 | /** |
| 27 | * Some definitions, to make code a bit more readable, and easier to maintain |
| 28 | */ |
| 29 | define("AGGREGATOR2_PERM_CREATE_FEED", "create feeds"); |
| 30 | define("AGGREGATOR2_PERM_EDIT_OWN_FEED", "edit own feeds"); |
| 31 | define("AGGREGATOR2_PERM_EDIT_OWN_ITEM", "edit own feed items"); |
| 32 | define("AGGREGATOR2_PERM_REFRESH_OWN_FEED", "refresh own feed items"); |
| 33 | |
| 34 | define("AGGREGATOR2_PERM_ACCESS_FEED", "access feeds"); |
| 35 | define("AGGREGATOR2_PERM_ACCESS_ITEM", "access feed items"); |
| 36 | |
| 37 | define("AGGREGATOR2_ITEM_DATE_SNIFFED", 0); |
| 38 | define("AGGREGATOR2_ITEM_DATE_CURRENT", 1); |
| 39 | |
| 40 | define("AGGREGATOR2_SHOW_LINK_ALWAYS", 0); |
| 41 | define("AGGREGATOR2_SHOW_LINK_NEVER", 1); |
| 42 | define("AGGREGATOR2_SHOW_LINK_TEASER_ONLY", 2); |
| 43 | define("AGGREGATOR2_SHOW_LINK_PAGE_ONLY", 3); |
| 44 | |
| 45 | // OR'ed deleting mode |
| 46 | define("AGGREGATOR2_ITEM_DELETE_ANY", 0); |
| 47 | define("AGGREGATOR2_ITEM_DELETE_UNPUBLISHED", 1); |
| 48 | |
| 49 | |
| 50 | static $AGGREGATOR2_REFRESH_FEED_RUNNING = FALSE; |
| 51 | |
| 52 | /** |
| 53 | * Implementation of hook_help(). |
| 54 | */ |
| 55 | function aggregator2_help($section) { |
| 56 | switch ($section) { |
| 57 | case 'admin/help#aggregator2': |
| 58 | return t(' |
| 59 | <h3>Background</h3> |
| 60 | Thousands of sites (particularly news sites and weblogs) publish their latest headlines and/or stories in a machine-readable format so that other sites can easily link to them. This content is usually in the form of an <a href="http://blogs.law.harvard.edu/tech/rss">RSS</a> feed (which is an XML-based syndication standard). Aggregator2 module can download such feeds, and add news from them to Your site.<br /> |
| 61 | <h3>Setting up Aggregator2</h3> |
| 62 | <p><b>1.</b> First You need to setup permissions for aggregator2 module on %admin->%access page: |
| 63 | <ul> |
| 64 | <li><b>Access news</b> - User can view news.</li> |
| 65 | <li><b>Administer News Feed</b> - User can contribute/administer Aggregator2 News feed.</li> |
| 66 | <li><b>Administer News Items</b> - User can contribute/adminster News Items.</li> |
| 67 | </ul> |
| 68 | </p> |
| 69 | <p><b>2.</b> Next, if You want to associate categories with aggregator2 content, go to %admin->%categories page and edit one of already exiting vocabularies or create new one, and make sure under "types" that aggregator2 news feed and aggregator2 news feed item are checked.</p> |
| 70 | <h3>Creating Content</h3> |
| 71 | <p>After setting up Aggregator2 module, You can start generating content with it. For that You have to create %feed. |
| 72 | <ul> |
| 73 | <li><b>Title</b> - Title of the News Feed.</li> |
| 74 | <li><b>Categories</b> - Select categories that you want the feed itself to be categorized in.</li> |
| 75 | <li><b>URL</b> - url of RSS feed.</li> |
| 76 | <li><b>Update interval</b> - Amount of time before news feed is updated.</li> |
| 77 | <li><b>Discard Feed Items older than</b> - Whether or not you want the feed items to be discarded after a certain time interval.</li> |
| 78 | <li><b>Item Categories</b> - Category or Categories you want the aggregated feed items to be associated with.</li> |
| 79 | </ul> |
| 80 | For the feed to update automatically you must run %cron on a regular basis. |
| 81 | </p> |
| 82 | <h3>Administrating Content</h3> |
| 83 | <p>You can administer aggregator2 feeds and aggregator2 items as You administer any other drupal content. Just go to %admin->%content and use "edit" link at node You want to edit. Or You can use "edit" tab, when viewing specific node.</p> |
| 84 | ', array('%admin' => l(t('Administer'), 'admin'), '%access' => l(t('access control'), 'admin/access'), '%categories' => l(t('categories'), 'admin/taxonomy'), '%feed' => l(t('aggregator2 news feed'), 'node/add/aggregator2_feed'), '%cron' => l('cron.php', 'admin/help/system#cron'), '%content' => l(t('content'), 'admin/node'))); |
| 85 | case 'admin/modules#description': |
| 86 | return t('Aggregates syndicated content (RSS and ATOM formats) as regular Drupal content.'); |
| 87 | case 'admin/aggregator2': |
| 88 | return '<p>' . t('Thousands of sites (particularly news sites and weblogs) publish their latest headlines and/or stories in a machine-readable format so that other sites can easily link to them. This content is usually in the form of an %rssurl feed (which is an XML-based syndication standard).', array('%rssurl' => '<a href="http://blogs.law.harvard.edu/tech/rss">RSS</a>') ) . '</p><p>'. l(t('create news feed'), 'node/add/aggregator2_feed') .'</p>'; |
| 89 | case 'node/add#aggregator2_feed': |
| 90 | return t('A news feed is a source of news from other site(s). If you add one from this page aggregator2 module will automatically add news feed items (nodes) in configured intervals. You will also be able to edit those items later. The URL is the full path to the RSS feed file. For the feed to update automatically you must run "cron.php" on a regular basis. 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.'); |
| 91 | case 'node/add#aggregator2_item': |
| 92 | return t('A news feed item is an item that is part of a feed. They are added automatically when feed is updated.'); |
| 93 | } |
| 94 | } |
| 95 | |
| 96 | /** |
| 97 | * Implementation of hook_perm(). |
| 98 | */ |
| 99 | function aggregator2_perm() { |
| 100 | return array(AGGREGATOR2_PERM_CREATE_FEED, AGGREGATOR2_PERM_EDIT_OWN_FEED, AGGREGATOR2_PERM_EDIT_OWN_ITEM, AGGREGATOR2_PERM_REFRESH_OWN_FEED, AGGREGATOR2_PERM_ACCESS_FEED, AGGREGATOR2_PERM_ACCESS_ITEM); |
| 101 | } |
| 102 | |
| 103 | /** |
| 104 | * Implementation of hook_node_info(). |
| 105 | */ |
| 106 | function aggregator2_node_info() { |
| 107 | return array( |
| 108 | 'aggregator2_feed' => array('name' => t('feed'), 'base' => 'aggregator2_feed'), |
| 109 | 'aggregator2_item' => array('name' => t('feed item'), 'base' => 'aggregator2_item') |
| 110 | ); |
| 111 | } |
| 112 | |
| 113 | /** |
| 114 | * Implementation of hook_menu(). |
| 115 | */ |
| 116 | function aggregator2_menu($may_cache) { |
| 117 | $items = array(); |
| 118 | |
| 119 | if ($may_cache) { |
| 120 | $items[] = array('path' => 'node/add/aggregator2_feed', 'title' => t('feed'), |
| 121 | 'access' => user_access(AGGREGATOR2_PERM_CREATE_FEED)); |
| 122 | |
| 123 | $items[] = array('path' => 'aggregator2/sources', 'title' => t('aggregator2'), |
| 124 | 'callback' => 'aggregator2_page_default', 'access' => user_access(AGGREGATOR2_PERM_ACCESS_FEED), |
| 125 | 'type' => MENU_CALLBACK); |
| 126 | |
| 127 | $items[] = array('path' => 'admin/aggregator2', 'title' => t('aggregator2'), |
| 128 | 'callback' => 'aggregator2_admin_overview', 'access' => user_access(AGGREGATOR2_PERM_CREATE_FEED)); |
| 129 | |
| 130 | // TODO: find a nice way to allow refresh only to feed owner |
| 131 | $items[] = array('path' => 'admin/aggregator2/refresh', 'title' => t('aggregator2'), |
| 132 | 'callback' => 'aggregator2_admin_refresh_feed', 'access' => user_access(AGGREGATOR2_PERM_REFRESH_OWN_FEED), |
| 133 | 'type' => MENU_CALLBACK); |
| 134 | |
| 135 | // TODO: find a nice way to allow remove only to items owner? |
| 136 | $items[] = array('path' => 'admin/aggregator2/remove', 'title' => t('remove items'), |
| 137 | 'callback' => 'aggregator2_admin_remove_feed_items', 'access' => user_access(AGGREGATOR2_PERM_EDIT_OWN_ITEM), |
| 138 | 'type' => MENU_CALLBACK); |
| 139 | |
| 140 | $items[] = array('path' => 'aggregator2/opml', 'title' => t('opml'), |
| 141 | 'callback' => 'aggregator2_page_opml', 'access' => user_access(AGGREGATOR2_PERM_ACCESS_FEED), |
| 142 | 'type' => MENU_CALLBACK); |
| 143 | } |
| 144 | |
| 145 | return $items; |
| 146 | } |
| 147 | |
| 148 | /** |
| 149 | * Implementation of hook_settings(). |
| 150 | */ |
| 151 | function aggregator2_settings() { |
| 152 | $form = array(); |
| 153 | |
| 154 | $form['agg2_create_feed_blocks'] = array( |
| 155 | '#type' => 'checkbox', |
| 156 | '#title' => t('Create drupal blocks for each feed'), |
| 157 | '#default_value' => variable_get('agg2_create_feed_blocks', 0), |
| 158 | '#description' => t('If enabled, aggragator2 will create block for each feed. Such block still needs to be enabled on %link page.', array('%link' => l('admin/block', 'admin/block'))) |
| 159 | ); |
| 160 | $form['agg2_show_feed_link'] = array( |
| 161 | '#type' => 'checkbox', |
| 162 | '#title' => t('Show link to feed with each item'), |
| 163 | '#default_value' => variable_get('agg2_show_feed_link', 0), |
| 164 | '#description' => t('If enabled, aggragator2 will show "source" link with each item. It will point to item\'s feed node.') |
| 165 | ); |
| 166 | $form['agg2_show_item_link'] = array( |
| 167 | '#type' => 'checkbox', |
| 168 | '#title' => t('Show link to items with each feed'), |
| 169 | '#default_value' => variable_get('agg2_show_item_link', 0), |
| 170 | '#description' => t('If enabled, aggragator2 will show "items" link with each feed. It will point to feed node list of all items.') |
| 171 | ); |
| 172 | $form['agg2_original_links'] = array( |
| 173 | '#type' => 'checkbox', |
| 174 | '#title' => t('Use link to origin source whenever possible'), |
| 175 | '#default_value' => variable_get('agg2_original_links', 0), |
| 176 | '#description' => t('If enabled, aggragator2 will use data from "source" tags instead of "link" tags. That will make "full article" link point to site which first published article, instead to site from which article was aggregated. Unfortunetly many sites do not use "source" tags, so often links will still point to site from which feeed was aggregated.') |
| 177 | ); |
| 178 | |
| 179 | // how many feeds to update at one cron run |
| 180 | $feed_count = drupal_map_assoc(array(0, 1, 2, 3, 4, 5, 10, 15, 20, 25, 50, 100)); |
| 181 | $feed_count['9999999'] = t('All'); |
| 182 | $form['agg2_cron_feed_count'] = array ( |
| 183 | '#type' => 'select', |
| 184 | '#title' => t('Number of feeds to update at a time'), |
| 185 | '#default_value' => variable_get('agg2_cron_feed_count', 10), |
| 186 | '#options' => $feed_count, |
| 187 | '#description' => t('Select how many feeds can be updated at one cron run.') |
| 188 | ); |
| 189 | |
| 190 | // how long intervals to use between node_save()/node_delete() calls |
| 191 | $sleep_interval = drupal_map_assoc(array(0, 1, 2, 3, 4, 5)); |
| 192 | $form['agg2_sleep_interval'] = array ( |
| 193 | '#type' => 'select', |
| 194 | '#title' => t('Interval between node updates'), |
| 195 | '#default_value' => variable_get('agg2_sleep_interval', 3), |
| 196 | '#options' => $sleep_interval, |
| 197 | '#description' => t('Select how many seconds aggregator2 should wait before trying to save/delete next node.') |
| 198 | ); |
| 199 | |
| 200 | $form['agg2_blacklist_url'] = array ( |
| 201 | '#type' => 'textarea', |
| 202 | '#title' => t('Blacklist URLs'), |
| 203 | '#default_value' => variable_get('agg2_blacklist_url', ''), |
| 204 | '#rows' => 5, |
| 205 | '#description' => t('One entry per line. You can enter full URLs or domain names only. You can also enter regular expression (find out more about what it is at %link. more examples can be found also at %link2). For example "http://some.url.com/some/page.html" will blacklist that specific URL. ".url.com" will blacklist all URLs from url.com domain, and all it\'s subdomains. "some.url.com" will blacklist all URLs from "some" subdomain. "/^ftp\:\/\//" will blacklist any ftp:// URL. Feed which has URL which matches any of the rules on blacklist will be blocked. Items which have link pointing to URL which matches any of the rules from blacklist will not be created.', array('%link' => l('http://www.php.net/manual/en/reference.pcre.pattern.syntax.php', 'http://www.php.net/manual/en/reference.pcre.pattern.syntax.php'), '%link2' => l('http://www.php.net/manual/en/function.preg-match.php', 'http://www.php.net/manual/en/function.preg-match.php'))) |
| 206 | ); |
| 207 | |
| 208 | // Globally change settings for all feeds - useful if one wants to change setting without need to edit each feed |
| 209 | $form['once_click_change'] = array( |
| 210 | '#type' => 'fieldset', |
| 211 | '#title' => t('Change all news feeds with one click'), |
| 212 | '#collapsible' => TRUE, |
| 213 | '#collapsed' => TRUE |
| 214 | ); |
| 215 | if ($clear_items = variable_get('agg2_clear_items', 0)) { |
| 216 | db_query("UPDATE {aggregator2_feed} SET clear_items = %d", $clear_items); |
| 217 | variable_set('agg2_clear_items', 0); |
| 218 | } |
| 219 | $period = drupal_map_assoc(array(3600, 10800, 21600, 32400, 43200, 86400, 172800, 259200, 604800, 1209600, 2419200, 3628800, 4838400, 7257600, 15724800, 31536000), 'format_interval'); |
| 220 | $period['0'] = t('Do not change'); |
| 221 | $period['1000000000'] = t('Never'); |
| 222 | $form['once_click_change']['agg2_clear_items'] = array ( |
| 223 | '#type' => 'select', |
| 224 | '#title' => t('Discard feed items older than'), |
| 225 | '#default_value' => 0, |
| 226 | '#options' => $period, |
| 227 | '#description' => t('The time feed items should be kept. Older items will be automatically discarded. Requires crontab.') |
| 228 | ); |
| 229 | |
| 230 | return $form; |
| 231 | } |
| 232 | |
| 233 | /** |
| 234 | * Implementation of hook_form_alter(). |
| 235 | */ |
| 236 | function aggregator2_form_alter($form_id, &$form) { |
| 237 | if (isset($form['type']) && $form['type']['#value'] .'_node_settings' == $form_id && $form['type']['#value'] == 'aggregator2_feed') { |
| 238 | $form['workflow']['agg2_feed_defs'] = array( |
| 239 | '#type' => 'fieldset', |
| 240 | '#title' => t('Default feed options'), |
| 241 | '#collapsible' => TRUE, |
| 242 | '#collapsed' => FALSE |
| 243 | ); |
| 244 | $form['workflow']['agg2_feed_defs']['agg2_feed_freezed'] = array( |
| 245 | '#type' => 'checkbox', |
| 246 | '#title' => t('Freeze'), |
| 247 | '#default_value' => variable_get('agg2_feed_freezed', 0) |
| 248 | ); |
| 249 | $form['workflow']['agg2_feed_defs']['agg2_feed_apply_old'] = array( |
| 250 | '#type' => 'checkbox', |
| 251 | '#title' => t('Apply changes to already existing items after feed is re-edited'), |
| 252 | '#default_value' => variable_get('agg2_feed_apply_old', 0) |
| 253 | ); |
| 254 | $form['workflow']['agg2_feed_defs']['agg2_update_items'] = array( |
| 255 | '#type' => 'checkbox', |
| 256 | '#title' => t('Update existing items'), |
| 257 | '#default_value' => variable_get('agg2_update_items', 1) |
| 258 | ); |
| 259 | $form['workflow']['agg2_feed_defs']['agg2_item_status'] = array( |
| 260 | '#type' => 'checkbox', |
| 261 | '#title' => t('Publish new items'), |
| 262 | '#default_value' => variable_get('agg2_item_status', 1) |
| 263 | ); |
| 264 | $form['workflow']['agg2_feed_defs']['agg2_item_delete_mode'] = array( |
| 265 | '#type' => 'checkbox', |
| 266 | '#title' => t('Discard only items not published currently'), |
| 267 | '#default_value' => variable_get('agg2_item_delete_mode', AGGREGATOR2_ITEM_DELETE_UNPUBLISHED) |
| 268 | ); |
| 269 | $form['workflow']['agg2_feed_defs']['agg2_guid_items'] = array( |
| 270 | '#type' => 'checkbox', |
| 271 | '#title' => t('Create GUID for items'), |
| 272 | '#default_value' => variable_get('agg2_guid_items', 1) |
| 273 | ); |
| 274 | $promoted = drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30)); |
| 275 | $promoted['0'] = t('None'); |
| 276 | $promoted['1000000000'] = t('All'); |
| 277 | $form['workflow']['agg2_feed_defs']['agg2_promoted_items'] = array ( |
| 278 | '#type' => 'select', |
| 279 | '#title' => t('By default promote items'), |
| 280 | '#default_value' => variable_get('agg2_promoted_items', 0), |
| 281 | '#options' => $promoted, |
| 282 | '#description' => t('Select how many of aggregated items should be promoted to front page. When new items are created old ones will be taken out from front page.') |
| 283 | ); |
| 284 | $period = drupal_map_assoc(array(3600, 10800, 21600, 32400, 43200, 86400, 172800, 259200, 604800, 1209600, 2419200, 3628800, 4838400, 7257600, 15724800, 31536000), 'format_interval'); |
| 285 | $period['1000000000'] = t('Never'); |
| 286 | $form['workflow']['agg2_feed_defs']['agg2_clear_items'] = array ( |
| 287 | '#type' => 'select', |
| 288 | '#title' => t('By default discard feed items older than'), |
| 289 | '#default_value' => variable_get('agg2_clear_items', 86400), |
| 290 | '#options' => $period, |
| 291 | '#description' => t('The time feed items should be kept. Older items will be automatically discarded. Requires crontab.') |
| 292 | ); |
| 293 | $form['workflow']['agg2_feed_defs']['agg2_item_date_source'] = array ( |
| 294 | '#type' => 'select', |
| 295 | '#title' => t('Item date source'), |
| 296 | '#default_value' => variable_get('agg2_item_date_source', AGGREGATOR2_ITEM_DATE_SNIFFED), |
| 297 | '#options' => array( |
| 298 | AGGREGATOR2_ITEM_DATE_SNIFFED => t('Feed'), |
| 299 | AGGREGATOR2_ITEM_DATE_CURRENT => t('Current') |
| 300 | ), |
| 301 | '#description' => t('Select which date will be used for aggregated items. If "Feed" is selected, Aggregator2 module will try to find date in feed, and if not found, current date will be used. If "Current" is selected, date of creation of items will always be set to the one at which items are aggregated.') |
| 302 | ); |
| 303 | $form['workflow']['agg2_feed_defs']['agg2_item_show_link'] = array ( |
| 304 | '#type' => 'select', |
| 305 | '#title' => t('Show "full article"/"visit site" link'), |
| 306 | '#default_value' => variable_get('agg2_item_show_link', AGGREGATOR2_SHOW_LINK_PAGE_ONLY), |
| 307 | '#options' => array( |
| 308 | AGGREGATOR2_SHOW_LINK_ALWAYS => t('Always'), |
| 309 | AGGREGATOR2_SHOW_LINK_NEVER => t('Do not display'), |
| 310 | AGGREGATOR2_SHOW_LINK_TEASER_ONLY => t('Only with teaser'), |
| 311 | AGGREGATOR2_SHOW_LINK_PAGE_ONLY => t('Only on full page') |
| 312 | ), |
| 313 | '#description' => t('Select place(s) where link to full article (for news items) or visit site (for news feeds) will be shown.') |
| 314 | ); |
| 315 | return $form; |
| 316 | } |
| 317 | else if (isset($form['type']) && $form['type']['#value'] .'_node_settings' == $form_id && $form['type']['#value'] == 'aggregator2_item') { |
| 318 | // nothing needed here for now ;] |
| 319 | } |
| 320 | } |
| 321 | |
| 322 | /** |
| 323 | * Implementation of hook_block(). |
| 324 | * |
| 325 | * Generates news feeds blocks for display. |
| 326 | */ |
| 327 | function aggregator2_block($op = 'list', $delta = 0) { |
| 328 | if (variable_get('agg2_create_feed_blocks', 0) == 1) { |
| 329 | if ($op == 'list') { |
| 330 | $result = db_query(db_rewrite_sql('SELECT n.nid, n.title FROM {node} n WHERE n.type = \'aggregator2_feed\' AND n.status = 1')); |
| 331 | while ($block = db_fetch_object($result)) { |
| 332 | $blocks[$block->nid]['info'] = $block->title; |
| 333 | } |
| 334 | $blocks['sources']['info'] = t('Latest sources'); |
| 335 | return $blocks; |
| 336 | } |
| 337 | else if ($op == 'view') { |
| 338 | if ($block = cache_get('aggregator2:block:'.$delta)) { |
| 339 | return unserialize($block->data); |
| 340 | } |
| 341 | else if ($delta != 'sources') { |
| 342 | $feed = db_fetch_object(db_query('SELECT n.nid, n.title FROM {node} n WHERE n.nid = %d', $delta)); |
| 343 | if ($feed->nid) { |
| 344 | $block = array(); |
| 345 | $block['subject'] = $feed->title; |
| 346 | $items = db_query('SELECT n.nid, n.title FROM {node} n, {aggregator2_item} a WHERE n.nid = a.nid AND a.fid = %d ORDER BY n.created DESC, n.title LIMIT 10', $feed->nid); |
| 347 | $block['content'] = node_title_list($items); |
| 348 | cache_set('aggregator2:block:'.$delta, serialize($block)); |
| 349 | return $block; |
| 350 | } |
| 351 | } |
| 352 | else { |
| 353 | $block = array(); |
| 354 | $items = array(); |
| 355 | $block['subject'] = t('Latest sources'); |
| 356 | $result = db_query('SELECT n.nid, n.title FROM {node} n WHERE n.type = \'aggregator2_feed\' AND n.status = 1 ORDER BY n.changed DESC LIMIT 10'); |
| 357 | while ($temp = db_fetch_object($result)) { |
| 358 | $items[] = l($temp->title, 'aggregator2/sources/'.$temp->nid); |
| 359 | } |
| 360 | $block['content'] = theme('node_list', $items, NULL); |
| 361 | $block['content'] .= l(t('all sources'), 'aggregator2/sources'); |
| 362 | cache_set('aggregator2:block:'.$delta, serialize($block)); |
| 363 | return $block; |
| 364 | } |
| 365 | } |
| 366 | } |
| 367 | } |
| 368 | |
| 369 | /** |
| 370 | * Implementation of hook_nodeapi(). |
| 371 | */ |
| 372 | function aggregator2_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) { |
| 373 | switch ($op) { |
| 374 | case 'rss item': |
| 375 | if ($node->type == 'aggregator2_item') { |
| 376 | return array(array('key' => 'source', |
| 377 | 'attributes' => array('url' => ($node->source_xml ? $node->source_xml : $node->feed_url)), |
| 378 | 'value' => check_plain(($node->source_title ? $node->source_title : $node->feed_title))), |
| 379 | array('key' => 'dc:source', |
| 380 | 'value' => ($node->source_link ? $node->source_link : $node->link))); |
| 381 | } |
| 382 | if ($node->type == 'aggregator2_feed') { |
| 383 | return array(array('key' => 'source', |
| 384 | 'attributes' => array('url' => $node->url), |
| 385 | 'value' => check_plain($node->title)), |
| 386 | array('key' => 'dc:source', |
| 387 | 'value' => $node->link)); |
| 388 | } |
| 389 | break; |
| 390 | } |
| 391 | } |
| 392 | |
| 393 | /** |
| 394 | * Implementation of hook_link(). |
| 395 | */ |
| 396 | function aggregator2_link($type, $node = NULL, $teaser = FALSE) { |
| 397 | $links = array(); |
| 398 | |
| 399 | if ($type == 'node' && $node != NULL) { |
| 400 | if (($node->item_show_link == AGGREGATOR2_SHOW_LINK_ALWAYS) || |
| 401 | ($teaser && $node->item_show_link == AGGREGATOR2_SHOW_LINK_TEASER_ONLY) || |
| 402 | (!$teaser && $node->item_show_link == AGGREGATOR2_SHOW_LINK_PAGE_ONLY)) { |
| 403 | if ($node->type == 'aggregator2_item') { |
| 404 | $links[] = theme('aggregator2_link_full_article', $node); |
| 405 | } |
| 406 | else if ($node->type == 'aggregator2_feed') { |
| 407 | $links[] = theme('aggregator2_link_visit_site', $node); |
| 408 | } |
| 409 | } |
| 410 | global $user; |
| 411 | if ($node->type == 'aggregator2_feed') { |
| 412 | if ((user_access(AGGREGATOR2_PERM_REFRESH_OWN_FEED) && ($user->uid == $node->uid)) || user_access('administer nodes')) { |
| 413 | $links[] = l(t('refresh items'), "admin/aggregator2/refresh/{$node->nid}"); |
| 414 | } |
| 415 | if ((user_access(AGGREGATOR2_PERM_EDIT_OWN_ITEM) && ($user->uid == $node->uid)) || user_access('administer nodes')) { |
| 416 | $links[] = l(t('remove items'), "admin/aggregator2/remove/{$node->nid}"); |
| 417 | } |
| 418 | if (variable_get('agg2_show_item_link', 0)) { |
| 419 | $links[] = l(t('view items'), "aggregator2/sources/{$node->nid}"); |
| 420 | } |
| 421 | } |
| 422 | else if ($node->type == 'aggregator2_item' && variable_get('agg2_show_feed_link', 0)) { |
| 423 | $links[] = l(t('source'), "aggregator2/sources/{$node->fid}"); |
| 424 | } |
| 425 | } |
| 426 | |
| 427 | return $links; |
| 428 | } |
| 429 | |
| 430 | /** |
| 431 | * Implementation of hook_cron(). |
| 432 | * |
| 433 | * Checks news feeds for updates once their refresh interval has elapsed. |
| 434 | */ |
| 435 | function aggregator2_cron() { |
| 436 | global $user; |
| 437 | $old_user = $user; |
| 438 | |
| 439 | // check how many feed nodew we can update at a time |
| 440 | $limit = variable_get('aggregator2_cron_feed_count', 10); |
| 441 | if (is_numeric($limit) && $limit > -1) { |
| 442 | $limit = 'LIMIT '. $limit; |
| 443 | } |
| 444 | else { |
| 445 | $limit = ''; |
| 446 | } |
| 447 | |
| 448 | $updated_feeds = array(); |
| 449 | $result = db_query('SELECT nid FROM {aggregator2_feed} WHERE freezed = 0 AND checked + refresh < %d ORDER BY checked ASC '. $limit, time()); |
| 450 | while ($temp = db_fetch_array($result)) { |
| 451 | $feed = node_load($temp['nid']); |
| 452 | // Fake login |
| 453 | if ($feed->uid != $user->uid) { |
| 454 | $user = user_load(array('uid' => $feed->uid, 'status' => 1)); |
| 455 | } |
| 456 | // Check again if it's correct uid and only then refresh feed |
| 457 | if ($feed->uid == $user->uid) { |
| 458 | aggregator2_refresh($feed); |
| 459 | $updated_feeds[$feed->nid] = array($feed->clear_items, $feed->item_delete_mode); |
| 460 | } |
| 461 | } |
| 462 | |
| 463 | // Now delete old items as admin (to make it faster - we don't really need to delete node as owner) |
| 464 | if ($user->uid != 1) { |
| 465 | $user = user_load(array('uid' => 1)); |
| 466 | } |
| 467 | foreach ($updated_feeds as $nid => $args) { |
| 468 | aggregator2_remove_old_items($nid, $args[0], $args[1]); |
| 469 | } |
| 470 | |
| 471 | // Now "logout" |
| 472 | if ($user->uid != $old_user->uid) { |
| 473 | $user = $old_user; |
| 474 | } |
| 475 | } |
| 476 | |
| 477 | /** |
| 478 | * Implementation of hook_prepare(). |
| 479 | */ |
| 480 | function aggregator2_feed_prepare(&$node, $teaser = FALSE) { |
| 481 | if (!$node->nid) { |
| 482 | $node->refresh = 3600; |
| 483 | $node->clear_items = variable_get('agg2_clear_items', 86400); |
| 484 | } |
| 485 | |
| 486 | // Remove "empty" terms |
| 487 | $temp = array(); |
| 488 | if (is_array($node->feed_item_taxonomy)) { |
| 489 | foreach ($node->feed_item_taxonomy as $tid) { |
| 490 | if ($tid) { |
| 491 | $temp[] = $tid; |
| 492 | } |
| 493 | } |
| 494 | } |
| 495 | if (count($temp) > 0) { |
| 496 | $node->feed_item_taxonomy = $temp; |
| 497 | } |
| 498 | |
| 499 | // Overwrite only if it's not saved from cron run, so it not gets freezed after each update :) |
| 500 | // TODO: if there will be some other module saving nodes, it will trigger overwriting values. Find way to workaround that? |
| 501 | global $AGGREGATOR2_REFRESH_FEED_RUNNING; |
| 502 | if (!$AGGREGATOR2_REFRESH_FEED_RUNNING && (!user_access('administer nodes') || !$node->nid)) { |
| 503 | $node->update_items = variable_get('agg2_update_items', 1); |
| 504 | $node->item_status = variable_get('agg2_item_status', 1); |
| 505 | $node->item_delete_mode = variable_get('agg2_item_delete_mode', AGGREGATOR2_ITEM_DELETE_UNPUBLISHED); |
| 506 | $node->clear_items = variable_get('agg2_clear_items', 86400); |
| 507 | $node->promoted_items = variable_get('agg2_promoted_items', 0); |
| 508 | $node->freezed = variable_get('agg2_feed_freezed', 0); |
| 509 | $node->change_existing_items = variable_get('agg2_feed_apply_old', 0); |
| 510 | $node->guid_items = variable_get('agg2_guid_items', 1); |
| 511 | $node->item_date_source = variable_get('agg2_item_date_source', AGGREGATOR2_ITEM_DATE_SNIFFED); |
| 512 | $node->item_show_link = variable_get('agg2_item_show_link', AGGREGATOR2_SHOW_LINK_PAGE_ONLY); |
| 513 | } |
| 514 | |
| 515 | return $node; |
| 516 | } |
| 517 | |
| 518 | /** |
| 519 | * Implementation of hook_form(). |
| 520 | */ |
| 521 | function aggregator2_feed_form(&$node) { |
| 522 | $form = array(); |
| 523 | |
| 524 | if (user_access('administer nodes')) { |
| 525 | $form['admin'] = array( |
| 526 | '#type' => 'fieldset', |
| 527 | '#title' => t('Feed options'), |
| 528 | '#collapsible' => TRUE, |
| 529 | '#collapsed' => FALSE, |
| 530 | '#weight' => 0 |
| 531 | ); |
| 532 | $form['admin']['freezed'] = array( |
| 533 | '#type' => 'checkbox', |
| 534 | '#title' => t('Freeze'), |
| 535 | '#default_value' => $node->freezed, |
| 536 | '#description' => t('If set, aggragator2 will not create new items, or update old ones, for this feed.') |
| 537 | ); |
| 538 | $form['admin']['change_existing_items'] = array( |
| 539 | '#type' => 'checkbox', |
| 540 | '#title' => t('Apply changes to already existing items after feed is re-edited'), |
| 541 | '#default_value' => $node->change_existing_items, |
| 542 | '#description' => t('If set, changes to input format and item categories will be applied also to already existing items.') |
| 543 | ); |
| 544 | $form['admin']['update_items'] = array( |
| 545 | '#type' => 'checkbox', |
| 546 | '#title' => t('Update existing items'), |
| 547 | '#default_value' => $node->update_items, |
| 548 | '#description' => t('If enabled, aggragator2 will update already existing items, overwriting any changes done between cron runs.') |
| 549 | ); |
| 550 | $form['admin']['item_status'] = array( |
| 551 | '#type' => 'checkbox', |
| 552 | '#title' => t('Publish new items'), |
| 553 | '#default_value' => $node->item_status, |
| 554 | '#description' => t('If enabled, aggragator2 will mark each new item as published.') |
| 555 | ); |
| 556 | $form['admin']['item_delete_mode'] = array( |
| 557 | '#type' => 'checkbox', |
| 558 | '#title' => t('Discard only items not published currently'), |
| 559 | '#default_value' => $node->item_delete_mode, |
| 560 | '#description' => t('The time feed items should be kept. Older items will be automatically discarded. Requires crontab.') |
| 561 | ); |
| 562 | $form['admin']['guid_items'] = array( |
| 563 | '#type' => 'checkbox', |
| 564 | '#title' => t('Create GUID for items'), |
| 565 | '#default_value' => $node->guid_items, |
| 566 | '#description' => t('If enabled, aggragator2 will try to generate GUID for each item. Use this ONLY if aggregated items do not contain GUID tag and their LINK is not unique (ie. more than one item has the same link).') |
| 567 | ); |
| 568 | $promoted = drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30)); |
| 569 | $promoted['0'] = t('None'); |
| 570 | $promoted['1000000000'] = t('All'); |
| 571 | $form['admin']['promoted_items'] = array ( |
| 572 | '#type' => 'select', |
| 573 | '#title' => t('By default promote items'), |
| 574 | '#default_value' => $node->promoted_items, |
| 575 | '#options' => $promoted, |
| 576 | '#description' => t('Select how many of aggregated items should be promoted to front page. When new items are created old ones will be taken out from front page.') |
| 577 | ); |
| 578 | $period = drupal_map_assoc(array(3600, 10800, 21600, 32400, 43200, 86400, 172800, 259200, 604800, 1209600, 2419200, 3628800, 4838400, 7257600, 15724800, 31536000), 'format_interval'); |
| 579 | $period['1000000000'] = t('Never'); |
| 580 | $form['admin']['clear_items'] = array ( |
| 581 | '#type' => 'select', |
| 582 | '#title' => t('By default discard feed items older than'), |
| 583 | '#default_value' => $node->clear_items, |
| 584 | '#options' => $period, |
| 585 | '#description' => t('The time feed items should be kept. Older items will be automatically discarded. Requires crontab.') |
| 586 | ); |
| 587 | $form['admin']['item_date_source'] = array ( |
| 588 | '#type' => 'select', |
| 589 | '#title' => t('Item date source'), |
| 590 | '#default_value' => $node->item_date_source, |
| 591 | '#options' => array( |
| 592 | AGGREGATOR2_ITEM_DATE_SNIFFED => t('Feed'), |
| 593 | AGGREGATOR2_ITEM_DATE_CURRENT => t('Current') |
| 594 | ), |
| 595 | '#description' => t('Select which date will be used for aggregated items. If "Feed" is selected, Aggregator2 module will try to find date in feed, and if not found, current date will be used. If "Current" is selected, date of creation of items will always be set to the one at which items are aggregated.') |
| 596 | ); |
| 597 | $form['admin']['item_show_link'] = array ( |
| 598 | '#type' => 'select', |
| 599 | '#title' => t('Show "full article"/"visit site" link'), |
| 600 | '#default_value' => $node->item_show_link, |
| 601 | '#options' => array( |
| 602 | AGGREGATOR2_SHOW_LINK_ALWAYS => t('Always'), |
| 603 | AGGREGATOR2_SHOW_LINK_NEVER => t('Do not display'), |
| 604 | AGGREGATOR2_SHOW_LINK_TEASER_ONLY => t('Only with teaser'), |
| 605 | AGGREGATOR2_SHOW_LINK_PAGE_ONLY => t('Only on full page') |
| 606 | ), |
| 607 | '#description' => t('Select place(s) where link to full article (for news items) or visit site (for news feeds) will be shown.') |
| 608 | ); |
| 609 | |
| 610 | // don't allow regular user's to "steal fame" :) |
| 611 | $form['original_author'] = array( |
| 612 | '#type' => 'textfield', |
| 613 | '#title' => t('Original author'), |
| 614 | '#default_value' => $node->original_author, |
| 615 | '#size' => 60, |
| 616 | '#maxlength' => 60, |
| 617 | '#weight' => -101 |
| 618 | ); |
| 619 | // don't allow user to setup logo |
| 620 | $form['image'] = array( |
| 621 | '#type' => 'textfield', |
| 622 | '#title' => t('Logo-link HTML'), |
| 623 | '#default_value' => $node->image, |
| 624 | '#size' => 60, |
| 625 | '#maxlength' => 1024, |
| 626 | '#description' => t('Leave it blank to allow aggregator2 to auto-generate it. Use only full URL (including "http://" part too) to image so it does not break RSS/ATOM feed compatibility.'), |
| 627 | '#weight' => -96 |
| 628 | ); |
| 629 | } |
| 630 | else { |
| 631 | // don't allow regular user's to "steal fame" :) |
| 632 | $form['original_author'] = array( |
| 633 | '#type' => 'hidden', |
| 634 | '#value' => $node->original_author |
| 635 | ); |
| 636 | // don't allow user to setup logo |
| 637 | $form['image'] = array( |
| 638 | '#type' => 'hidden', |
| 639 | '#value' => $node->image |
| 640 | ); |
| 641 | } |
| 642 | |
| 643 | $form['url'] = array( |
| 644 | '#type' => 'textfield', |
| 645 | '#title' => t('Feed URL'), |
| 646 | '#default_value' => $node->url, |
| 647 | '#size' => 60, |
| 648 | '#maxlength' => 250, |
| 649 | '#required' => TRUE, |
| 650 | '#weight' => -99 |
| 651 | ); |
| 652 | $period = drupal_map_assoc(array(900, 1800, 3600, 7200, 10800, 21600, 32400, 43200, 64800, 86400, 172800, 259200, 604800, 1209600, 2419200), 'format_interval'); |
| 653 | $form['refresh'] = array ( |
| 654 | '#type' => 'select', |
| 655 | '#title' => t('Update interval'), |
| 656 | '#default_value' => $node->refresh, |
| 657 | '#options' => $period, |
| 658 | '#description' => t('The refresh interval indicating how often you want to update this feed. Requires crontab.'), |
| 659 | '#weight' => -97 |
| 660 | ); |
| 661 | |
| 662 | $form['title'] = array( |
| 663 | '#type' => 'textfield', |
| 664 | '#title' => t('Title'), |
| 665 | '#default_value' => $node->title, |
| 666 | '#required' => TRUE, |
| 667 | '#weight' => -100 |
| 668 | ); |
| 669 | $form['body'] = array( |
| 670 | '#type' => 'textarea', |
| 671 | '#title' => t('Description'), |
| 672 | '#default_value' => $node->body, |
| 673 | '#description' => t('Leave it blank to allow aggregator2 to use aggregated content for description.'), |
| 674 | '#weight' => -98 |
| 675 | ); |
| 676 | $form['format'] = filter_form($node->format); |
| 677 | $form['format']['#weight'] = 0; |
| 678 | |
| 679 | |
| 680 | // Now fake form to get taxonomy for items |
| 681 | $form['item_setup'] = array( |
| 682 | '#type' => 'fieldset', |
| 683 | '#title' => t('Item categories'), |
| 684 | '#collapsible' => TRUE, |
| 685 | '#collapsed' => FALSE, |
| 686 | '#weight' => 0 |
| 687 | ); |
| 688 | $fakeform = array('type' => array('#value' => 'aggregator2_item'), '#node' => new StdClass()); |
| 689 | $fakeform['#node']->type = 'aggregator2_item'; |
| 690 | $fakeform['#node']->taxonomy = $node->feed_item_taxonomy; |
| 691 | if (is_array($fakeform['#node']->taxonomy)) { |
| 692 | foreach ($fakeform['#node']->taxonomy as $id => $term) { |
| 693 | if (is_array($term)) { |
| 694 | foreach ($term as $vid => $value) { |
| 695 | $fakeform['#node']->taxonomy['tags'][$vid] = $value; |
| 696 | } |
| 697 | unset($fakeform['#node']->taxonomy[$id]); |
| 698 | } |
| 699 | } |
| 700 | } |
| 701 | taxonomy_form_alter('aggregator2_item_node_form', $fakeform); |
| 702 | $form['item_setup']['feed_item_taxonomy'] = $fakeform['taxonomy']; |
| 703 | unset($fakeform); |
| 704 | |
| 705 | return $form; |
| 706 | } |
| 707 | |
| 708 | /** |
| 709 | * Implementation of hook_form(). |
| 710 | */ |
| 711 | function aggregator2_item_form(&$node) { |
| 712 | $form = array(); |
| 713 | |
| 714 | if (user_access('administer nodes')) { |
| 715 | // don't allow regular user's to "steal fame" :) |
| 716 | $form['original_author'] = array( |
| 717 | '#type' => 'textfield', |
| 718 | '#title' => t('Original author'), |
| 719 | '#default_value' => $node->original_author, |
| 720 | '#size' => 60, |
| 721 | '#maxlength' => 60, |
| 722 | '#weight' => -101 |
| 723 | ); |
| 724 | $form['link'] = array( |
| 725 | '#type' => 'textfield', |
| 726 | '#title' => t('Link'), |
| 727 | '#default_value' => $node->link, |
| 728 | '#size' => 60, |
| 729 | '#maxlength' => 250, |
| 730 | '#weight' => -99 |
| 731 | ); |
| 732 | $form['source_link'] = array( |
| 733 | '#type' => 'textfield', |
| 734 | '#title' => t('Source Link'), |
| 735 | '#default_value' => $node->source_link, |
| 736 | '#size' => 60, |
| 737 | '#maxlength' => 250, |
| 738 | '#weight' => -95 |
| 739 | ); |
| 740 | $form['source_xml'] = array( |
| 741 | '#type' => 'textfield', |
| 742 | '#title' => t('Source XML'), |
| 743 | '#default_value' => $node->source_xml, |
| 744 | '#size' => 60, |
| 745 | '#maxlength' => 250, |
| 746 | '#weight' => -95 |
| 747 | ); |
| 748 | $form['source_title'] = array( |
| 749 | '#type' => 'textfield', |
| 750 | '#title' => t('Source Title'), |
| 751 | '#default_value' => $node->source_title, |
| 752 | '#size' => 60, |
| 753 | '#maxlength' => 250, |
| 754 | '#weight' => -94 |
| 755 | ); |
| 756 | } |
| 757 | else { |
| 758 | // don't allow regular user's to "steal fame" :) |
| 759 | $form['original_author'] = array( |
| 760 | '#type' => 'hidden', |
| 761 | '#value' => $node->original_author |
| 762 | ); |
| 763 | $form['link'] = array( |
| 764 | '#type' => 'hidden', |
| 765 | '#value' => $node->link |
| 766 | ); |
| 767 | $form['source_link'] = array( |
| 768 | '#type' => 'hidden', |
| 769 | '#value' => $node->source_link |
| 770 | ); |
| 771 | $form['source_xml'] = array( |
| 772 | '#type' => 'hidden', |
| 773 | '#value' => $node->source_xml |
| 774 | ); |
| 775 | $form['source_title'] = array( |
| 776 | '#type' => 'hidden', |
| 777 | '#value' => $node->source_title |
| 778 | ); |
| 779 | } |
| 780 | |
| 781 | $form['title'] = array( |
| 782 | '#type' => 'textfield', |
| 783 | '#title' => t('Title'), |
| 784 | '#default_value' => $node->title, |
| 785 | '#required' => TRUE, |
| 786 | '#weight' => -100 |
| 787 | ); |
| 788 | |
| 789 | $feeds = array(); |
| 790 | $result = db_query(db_rewrite_sql('SELECT n.nid, n.title FROM {node} n, {aggregator2_feed} af WHERE af.nid = n.nid')); |
| 791 | while ($temp = db_fetch_array($result)) { |
| 792 | $feeds[$temp['nid']] = $temp['title']; |
| 793 | } |
| 794 | $form['fid'] = array ( |
| 795 | '#type' => 'select', |
| 796 | '#title' => t('Feed Name'), |
| 797 | '#default_value' => $node->fid, |
| 798 | '#options' => $feeds, |
| 799 | '#required' => TRUE, |
| 800 | '#description' => t('The RSS/ATOM feed which this item belongs to.'), |
| 801 | '#weight' => -98 |
| 802 | ); |
| 803 | |
| 804 | $form['body'] = array( |
| 805 | '#type' => 'textarea', |
| 806 | '#title' => t('Description'), |
| 807 | '#default_value' => $node->body, |
| 808 | '#weight' => -97 |
| 809 | ); |
| 810 | $form['format'] = filter_form($node->format); |
| 811 | $form['format']['#weight'] = 0; |
| 812 | |
| 813 | return $form; |
| 814 | } |
| 815 | |
| 816 | /** |
| 817 | * Implementation of hook_access(). |
| 818 | */ |
| 819 | function aggregator2_feed_access($op, $node) { |
| 820 | global $AGGREGATOR2_REFRESH_FEED_RUNNING; |
| 821 | global $user; |
| 822 | |
| 823 | switch ($op) { |
| 824 | case 'create': |
| 825 | return user_access(AGGREGATOR2_PERM_CREATE_FEED); |
| 826 | break; |
| 827 | case 'update': |
| 828 | case 'delete': |
| 829 | if ($AGGREGATOR2_REFRESH_FEED_RUNNING || (user_access(AGGREGATOR2_PERM_EDIT_OWN_FEED) && ($user->uid == $node->uid))) { |
| 830 | return TRUE; |
| 831 | } |
| 832 | break; |
| 833 | case 'view': |
| 834 | return user_access(AGGREGATOR2_PERM_ACCESS_FEED); |
| 835 | break; |
| 836 | } |
| 837 | } |
| 838 | |
| 839 | /** |
| 840 | * Implementation of hook_access(). |
| 841 | */ |
| 842 | function aggregator2_item_access($op, $node) { |
| 843 | global $AGGREGATOR2_REFRESH_FEED_RUNNING; |
| 844 | global $user; |
| 845 | |
| 846 | switch ($op) { |
| 847 | case 'create': |
| 848 | if ($AGGREGATOR2_REFRESH_FEED_RUNNING) { |
| 849 | return TRUE; |
| 850 | } |
| 851 | break; |
| 852 | case 'update': |
| 853 | case 'delete': |
| 854 | if ($AGGREGATOR2_REFRESH_FEED_RUNNING || (user_access(AGGREGATOR2_PERM_EDIT_OWN_ITEM) && ($user->uid == $node->uid))) { |
| 855 | return TRUE; |
| 856 | } |
| 857 | break; |
| 858 | case 'view': |
| 859 | return user_access(AGGREGATOR2_PERM_ACCESS_ITEM); |
| 860 | break; |
| 861 | } |
| 862 | } |
| 863 | |
| 864 | /** |
| 865 | * Implementation of hook_validate(). |
| 866 | */ |
| 867 | function aggregator2_feed_validate($node) { |
| 868 | if (isset($node->url)) { |
| 869 | if (trim($node->url) == '') { |
| 870 | form_set_error('url', t('URL field may not be empty, without it aggregator2 will not know from where to aggregate items.')); |
| 871 | } |
| 872 | $result = db_query("SELECT af.nid, n.title FROM {node} n, {aggregator2_feed} af WHERE af.url = '%s' AND af.nid = n.nid", $node->url); |
| 873 | while ($feed = db_fetch_object($result)) { |
| 874 | if ($feed->nid != $node->nid) { |
| 875 | $link = l($feed->title, "node/{$feed->nid}"); |
| 876 | form_set_error('url', t('Duplicated URL: %link already uses that URL.', array('%link' => $link))); |
| 877 | break; |
| 878 | } |
| 879 | } |
| 880 | if (!aggregator2_is_valid_url($node->url)) { |
| 881 | form_set_error('url', t('That URL is not allowed.')); |
| 882 | } |
| 883 | } |
| 884 | } |
| 885 | |
| 886 | /** |
| 887 | * Implementation of hook_validate(). |
| 888 | */ |
| 889 | function aggregator2_item_validate($node) { |
| 890 | if (!isset($node->fid) || $node->fid < 1) { |
| 891 | form_set_error('fid', t('Invalid feed selected')); |
| 892 | } |
| 893 | if (!aggregator2_is_valid_url($node->link)) { |
| 894 | form_set_error('url', t('That URL is not allowed.')); |
| 895 | } |
| 896 | } |
| 897 | |
| 898 | /** |
| 899 | * Implementation of hook_insert(). |
| 900 | */ |
| 901 | function aggregator2_feed_insert($node) { |
| 902 | db_query("INSERT INTO {aggregator2_feed} (nid, author, url, freezed, refresh, clear_items, update_items, guid_items, promoted_items, item_status, item_taxonomy, item_date_source, item_show_link, item_delete_mode) VALUES (%d, '%s', '%s', %d, %d, %d, %d, %d, %d, %d, '%s', %d, %d, %d)", $node->nid, $node->author, $node->url, $node->freezed, $node->refresh, $node->clear_items, $node->update_items, $node->guid_items, $node->promoted_items, $node->item_status, serialize($node->feed_item_taxonomy), $node->item_date_source, $node->item_show_link, $node->item_delete_mode); |
| 903 | cache_clear_all('aggregator2:block:sources'); |
| 904 | } |
| 905 | |
| 906 | /** |
| 907 | * Implementation of hook_insert(). |
| 908 | */ |
| 909 | function aggregator2_item_insert($node) { |
| 910 | db_query("INSERT INTO {aggregator2_item} (nid, fid, author, link, guid, source_link, source_xml, source_title) VALUES (%d, %d, '%s', '%s', '%s', '%s', '%s', '%s')", $node->nid, $node->fid, $node->author, $node->link, $node->guid, $node->source_link, $node->source_xml, $node->source_title); |
| 911 | } |
| 912 | |
| 913 | /** |
| 914 | * Implementation of hook_update(). |
| 915 | */ |
| 916 | function aggregator2_feed_update($node) { |
| 917 | db_query("UPDATE {aggregator2_feed} SET author = '%s', url = '%s', freezed = %d, refresh = %d, clear_items = %d, update_items = %d, guid_items = %d, promoted_items = %d, checked = %d, link = '%s', image = '%s', etag = '%s', modified = %d, item_status = %d, item_taxonomy = '%s', item_date_source = %d, item_show_link = %d, item_delete_mode = %d WHERE nid = %d", $node->author, $node->url, $node->freezed, $node->refresh, $node->clear_items, $node->update_items, $node->guid_items, $node->promoted_items, $node->checked, $node->link, $node->image, $node->etag, $node->modified, $node->item_status, serialize($node->feed_item_taxonomy), $node->item_date_source, $node->item_show_link, $node->item_delete_mode, $node->nid); |
| 918 | // update taxonomy for already existing nodes, it may take a while... |
| 919 | // TODO: find a way to split work and run it at cron run? |
| 920 | // maybe store serialized array as drupal variable (for example: aggregator2_update_items_[FEED->NID]) |
| 921 | // and at cron run, at feed update time, load it and update X items form it, then save what's left for next cron run? |
| 922 | if (function_exists('taxonomy_node_save') && $node->change_existing_items == 1) { |
| 923 | $result = db_query("SELECT ai.nid FROM {aggregator2_item} ai WHERE ai.fid = '%d'", $node->nid); |
| 924 | $items = array(); |
| 925 | while ($temp = db_fetch_object($result)) { |
| 926 | $items[] = $temp->nid; |
| 927 | // TODO: this removes previous categories, including those from autotaxonomy :( Find a way to remove only those we don't want? |
| 928 | // maybe setting by which vocabularies should change and which not? then load taxonomy, and remove only those terms which are from vocabularies allowed to change? |
| 929 | taxonomy_node_save($temp->nid, $node->feed_item_taxonomy); |
| 930 | } |
| 931 | if (count($items) > 0) { |
| 932 | // Update filter format of items |
| 933 | db_query('UPDATE {node_revisions} SET format = %d WHERE nid IN(%s)', $node->format, implode(',', $items)); |
| 934 | drupal_set_message(t('Updated existing items')); |
| 935 | } |
| 936 | } |
| 937 | // Clear cache |
| 938 | cache_clear_all('aggregator2:block:sources'); |
| 939 | cache_clear_all('aggregator2:block:'.$node->nid); |
| 940 | } |
| 941 | |
| 942 | |
| 943 | /** |
| 944 | * Implementation of hook_update(). |
| 945 | */ |
| 946 | function aggregator2_item_update($node) { |
| 947 | db_query("UPDATE {aggregator2_item} SET link = '%s', author = '%s', fid = %d WHERE nid = %d", $node->link, $node->author, $node->fid, $node->nid); |
| 948 | } |
| 949 | |
| 950 | /** |
| 951 | * Implementation of hook_delete(). |
| 952 | */ |
| 953 | function aggregator2_feed_delete(&$node) { |
| 954 | db_query('DELETE FROM {aggregator2_feed} WHERE nid = %d', $node->nid); |
| 955 | // Clear cache |
| 956 | cache_clear_all('aggregator2:block:'.$node->nid); |
| 957 | cache_clear_all('aggregator2:block:sources'); |
| 958 | } |
| 959 | |
| 960 | /** |
| 961 | * Implementation of hook_delete(). |
| 962 | */ |
| 963 | function aggregator2_item_delete(&$node) { |
| 964 | db_query('DELETE FROM {aggregator2_item} WHERE nid = %d', $node->nid); |
| 965 | cache_clear_all('aggregator2:block:'.$node->fid); |
| 966 | } |
| 967 | |
| 968 | /** |
| 969 | * Implementation of hook_load(). |
| 970 | */ |
| 971 | function aggregator2_feed_load($node) { |
| 972 | $temp = db_fetch_object(db_query('SELECT * FROM {aggregator2_feed} WHERE nid = %d', $node->nid)); |
| 973 | $temp->feed_item_taxonomy = unserialize($temp->item_taxonomy); |
| 974 | unset($temp->item_taxonomy); |
| 975 | return $temp; |
| 976 | } |
| 977 | |
| 978 | /** |
| 979 | * Implementation of hook_load(). |
| 980 | */ |
| 981 | function aggregator2_item_load($node) { |
| 982 | return db_fetch_object(db_query('SELECT ai.fid, ai.link, ai.source_link, ai.source_xml, ai.source_title, ai.author AS author, n.title AS feed_title, af.url AS feed_url, af.item_show_link AS item_show_link FROM {aggregator2_item} ai LEFT JOIN {aggregator2_feed} af ON af.nid = ai.fid LEFT JOIN {node} n ON n.nid = ai.fid WHERE ai.nid = %d', $node->nid)); |
| 983 | } |
| 984 | |
| 985 | /** |
| 986 | * Implementation of hook_view(). |
| 987 | */ |
| 988 | function aggregator2_feed_view(&$node, $teaser = FALSE, $page = FALSE) { |
| 989 | // Provide some statistics for feed nodes |
| 990 | $rows = array(); |
| 991 | $items_count = db_result(db_query("SELECT COUNT(ai.nid) FROM {aggregator2_item} ai WHERE ai.fid = '%d'", $node->nid)); |
| 992 | $rows[] = array(t('Feed hosted at'), $node->url); |
| 993 | $rows[] = array(t('Feed currently contains'), format_plural($items_count, '1 item', '%count items')); |
| 994 | $rows[] = array(t('Last checked feed host'), ($node->checked ? t('%time ago', array('%time' => format_interval(time() - $node->checked))) : t('never')) ); |
| 995 | $rows[] = array(t('Time until next refresh'), ($node->checked ? t('%time left', array('%time' => format_interval($node->checked + $node->refresh - time()))) : t('never')) ); |
| 996 | |
| 997 | $output .= theme('table', array(), $rows); |
| 998 | $node->body .= $output; |
| 999 | |
| 1000 | $node = node_prepare($node, $teaser); |
| 1001 | } |
| 1002 | |
| 1003 | |
| 1004 | |
| 1005 | |
| 1006 | |
| 1007 | /** |
| 1008 | * Menu callback; displays the aggregator administration page. |
| 1009 | */ |
| 1010 | function aggregator2_admin_overview() { |
| 1011 | global $user; |
| 1012 | if (!user_access('administer nodes')) { |
| 1013 | $uid = ' WHERE n.uid = '. $user->uid .' '; |
| 1014 | $can_edit = user_access(AGGREGATOR2_PERM_EDIT_OWN_FEED); |
| 1015 | $can_remove = user_access(AGGREGATOR2_PERM_EDIT_OWN_ITEM); |
| 1016 | $can_refresh = user_access(AGGREGATOR2_PERM_REFRESH_OWN_FEED); |
| 1017 | } |
| 1018 | else { |
| 1019 | $uid = ''; |
| 1020 | $can_edit = TRUE; |
| 1021 | $can_remove = TRUE; |
| 1022 | $can_refresh = TRUE; |
| 1023 | } |
| 1024 | |
| 1025 | $result = db_query("SELECT n.nid, n.title, af.checked, af.refresh, af.freezed FROM {node} n INNER JOIN {aggregator2_feed} af ON n.nid = af.nid $uid ORDER BY n.title ASC"); |
| 1026 | |
| 1027 | $output = '<h3>'. t('Feed overview') .'</h3>'; |
| 1028 | |
| 1029 | $header = array(t('Title'), t('Items'), t('Last update' |