aggregator.module

  1. drupal
    1. 4.6 modules/aggregator.module
    2. 4.7 modules/aggregator.module
    3. 5 modules/aggregator/aggregator.module
    4. 6 modules/aggregator/aggregator.module
    5. 7 modules/aggregator/aggregator.module
    6. 8 core/modules/aggregator/aggregator.module

Used to aggregate syndicated content (RSS, RDF, and Atom).

Functions & methods

NameDescription
aggregator_block_configureImplements hook_block_configure().
aggregator_block_infoImplements hook_block_info().
aggregator_block_saveImplements hook_block_save().
aggregator_block_viewImplements hook_block_view().
aggregator_category_loadLoads an aggregator category.
aggregator_cronImplements hook_cron().
aggregator_feed_loadLoads an aggregator feed.
aggregator_filter_xssSafely renders HTML content, as allowed.
aggregator_helpImplements hook_help().
aggregator_menuImplements hook_menu().
aggregator_permissionImplements hook_permission().
aggregator_preprocess_blockImplements hook_preprocess_HOOK() for block.tpl.php.
aggregator_queue_infoImplements hook_queue_info().
aggregator_refreshChecks a news feed for new items.
aggregator_removeRemoves all items from a feed.
aggregator_sanitize_configurationChecks and sanitizes the aggregator configuration.
aggregator_save_categoryAdds/edits/deletes aggregator categories.
aggregator_save_feedAdds/edits/deletes an aggregator feed.
aggregator_themeImplements hook_theme().
theme_aggregator_block_itemReturns HTML for an individual feed item for display in the block.
_aggregator_category_titleTitle callback: Returns a title for aggregatory category pages.
_aggregator_get_variablesGets the fetcher, parser, and processors.
_aggregator_has_categoriesAccess callback: Determines whether there are any aggregator categories.
_aggregator_itemsHelper function for drupal_map_assoc.

Constants

NameDescription
AGGREGATOR_CLEAR_NEVERDenotes that a feed's items should never expire.

File

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