1 | <?php |
---|
2 | |
---|
3 | /** |
---|
4 | * @file |
---|
5 | * Used to aggregate syndicated content (RSS, RDF, and Atom). |
---|
6 | */ |
---|
7 | |
---|
8 | /** |
---|
9 | * Implementation of hook_help(). |
---|
10 | */ |
---|
11 | function aggregator_help($path, $arg) { |
---|
12 | switch ($path) { |
---|
13 | case 'admin/help#aggregator': |
---|
14 | $output = '<p>'. t('The aggregator is a powerful on-site syndicator and news reader that gathers 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 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>'; |
---|
15 | $output .= '<p>'. t('Feeds contain feed items, or individual posts published by the site providing the feed. Feeds may be grouped in categories, generally by topic. Users view feed items in the <a href="@aggregator">main aggregator display</a> or by <a href="@aggregator-sources">their source</a>. Administrators can <a href="@feededit">add, edit and delete feeds</a> and choose how often to check each feed for newly updated items. The most recent items in either a feed or category can be displayed as a block through the <a href="@admin-block">blocks administration page</a>. A <a href="@aggregator-opml">machine-readable OPML file</a> of all feeds is available. A correctly configured <a href="@cron">cron maintenance task</a> is required to update feeds automatically.', array('@aggregator' => url('aggregator'), '@aggregator-sources' => url('aggregator/sources'), '@feededit' => url('admin/content/aggregator'), '@admin-block' => url('admin/build/block'), '@aggregator-opml' => url('aggregator/opml'), '@cron' => url('admin/reports/status'))) .'</p>'; |
---|
16 | $output .= '<p>'. t('For more information, see the online handbook entry for <a href="@aggregator">Aggregator module</a>.', array('@aggregator' => 'http://drupal.org/handbook/modules/aggregator/')) .'</p>'; |
---|
17 | return $output; |
---|
18 | case 'admin/content/aggregator': |
---|
19 | $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>'; |
---|
20 | $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/content/aggregator/add/feed'), '@block' => url('admin/build/block'))) .'</p>'; |
---|
21 | return $output; |
---|
22 | case 'admin/content/aggregator/add/feed': |
---|
23 | return '<p>'. t('Add a feed in RSS, RDF or Atom format. A feed may only have one entry.') .'</p>'; |
---|
24 | case 'admin/content/aggregator/add/category': |
---|
25 | 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>'; |
---|
26 | } |
---|
27 | } |
---|
28 | |
---|
29 | /** |
---|
30 | * Implementation of hook_theme() |
---|
31 | */ |
---|
32 | function aggregator_theme() { |
---|
33 | return array( |
---|
34 | 'aggregator_wrapper' => array( |
---|
35 | 'arguments' => array('content' => NULL), |
---|
36 | 'file' => 'aggregator.pages.inc', |
---|
37 | 'template' => 'aggregator-wrapper', |
---|
38 | ), |
---|
39 | 'aggregator_categorize_items' => array( |
---|
40 | 'arguments' => array('form' => NULL), |
---|
41 | 'file' => 'aggregator.pages.inc', |
---|
42 | ), |
---|
43 | 'aggregator_feed_source' => array( |
---|
44 | 'arguments' => array('feed' => NULL), |
---|
45 | 'file' => 'aggregator.pages.inc', |
---|
46 | 'template' => 'aggregator-feed-source', |
---|
47 | ), |
---|
48 | 'aggregator_block_item' => array( |
---|
49 | 'arguments' => array('item' => NULL, 'feed' => 0), |
---|
50 | ), |
---|
51 | 'aggregator_summary_items' => array( |
---|
52 | 'arguments' => array('summary_items' => NULL, 'source' => NULL), |
---|
53 | 'file' => 'aggregator.pages.inc', |
---|
54 | 'template' => 'aggregator-summary-items', |
---|
55 | ), |
---|
56 | 'aggregator_summary_item' => array( |
---|
57 | 'arguments' => array('item' => NULL), |
---|
58 | 'file' => 'aggregator.pages.inc', |
---|
59 | 'template' => 'aggregator-summary-item', |
---|
60 | ), |
---|
61 | 'aggregator_item' => array( |
---|
62 | 'arguments' => array('item' => NULL), |
---|
63 | 'file' => 'aggregator.pages.inc', |
---|
64 | 'template' => 'aggregator-item', |
---|
65 | ), |
---|
66 | 'aggregator_page_opml' => array( |
---|
67 | 'arguments' => array('feeds' => NULL), |
---|
68 | 'file' => 'aggregator.pages.inc', |
---|
69 | ), |
---|
70 | 'aggregator_page_rss' => array( |
---|
71 | 'arguments' => array('feeds' => NULL, 'category' => NULL), |
---|
72 | 'file' => 'aggregator.pages.inc', |
---|
73 | ), |
---|
74 | ); |
---|
75 | } |
---|
76 | |
---|
77 | /** |
---|
78 | * Implementation of hook_menu(). |
---|
79 | */ |
---|
80 | function aggregator_menu() { |
---|
81 | $items['admin/content/aggregator'] = array( |
---|
82 | 'title' => 'Feed aggregator', |
---|
83 | 'description' => "Configure which content your site aggregates from other sites, how often it polls them, and how they're categorized.", |
---|
84 | 'page callback' => 'aggregator_admin_overview', |
---|
85 | 'access arguments' => array('administer news feeds'), |
---|
86 | 'file' => 'aggregator.admin.inc', |
---|
87 | ); |
---|
88 | $items['admin/content/aggregator/add/feed'] = array( |
---|
89 | 'title' => 'Add feed', |
---|
90 | 'page callback' => 'drupal_get_form', |
---|
91 | 'page arguments' => array('aggregator_form_feed'), |
---|
92 | 'access arguments' => array('administer news feeds'), |
---|
93 | 'type' => MENU_LOCAL_TASK, |
---|
94 | 'parent' => 'admin/content/aggregator', |
---|
95 | 'file' => 'aggregator.admin.inc', |
---|
96 | ); |
---|
97 | $items['admin/content/aggregator/add/category'] = array( |
---|
98 | 'title' => 'Add category', |
---|
99 | 'page callback' => 'drupal_get_form', |
---|
100 | 'page arguments' => array('aggregator_form_category'), |
---|
101 | 'access arguments' => array('administer news feeds'), |
---|
102 | 'type' => MENU_LOCAL_TASK, |
---|
103 | 'parent' => 'admin/content/aggregator', |
---|
104 | 'file' => 'aggregator.admin.inc', |
---|
105 | ); |
---|
106 | $items['admin/content/aggregator/remove/%aggregator_feed'] = array( |
---|
107 | 'title' => 'Remove items', |
---|
108 | 'page callback' => 'drupal_get_form', |
---|
109 | 'page arguments' => array('aggregator_admin_remove_feed', 4), |
---|
110 | 'access arguments' => array('administer news feeds'), |
---|
111 | 'type' => MENU_CALLBACK, |
---|
112 | 'file' => 'aggregator.admin.inc', |
---|
113 | ); |
---|
114 | $items['admin/content/aggregator/update/%aggregator_feed'] = array( |
---|
115 | 'title' => 'Update items', |
---|
116 | 'page callback' => 'aggregator_admin_refresh_feed', |
---|
117 | 'page arguments' => array(4), |
---|
118 | 'access arguments' => array('administer news feeds'), |
---|
119 | 'type' => MENU_CALLBACK, |
---|
120 | 'file' => 'aggregator.admin.inc', |
---|
121 | ); |
---|
122 | $items['admin/content/aggregator/list'] = array( |
---|
123 | 'title' => 'List', |
---|
124 | 'type' => MENU_DEFAULT_LOCAL_TASK, |
---|
125 | 'weight' => -10, |
---|
126 | ); |
---|
127 | $items['admin/content/aggregator/settings'] = array( |
---|
128 | 'title' => 'Settings', |
---|
129 | 'page callback' => 'drupal_get_form', |
---|
130 | 'page arguments' => array('aggregator_admin_settings'), |
---|
131 | 'type' => MENU_LOCAL_TASK, |
---|
132 | 'weight' => 10, |
---|
133 | 'access arguments' => array('administer news feeds'), |
---|
134 | 'file' => 'aggregator.admin.inc', |
---|
135 | ); |
---|
136 | $items['aggregator'] = array( |
---|
137 | 'title' => 'Feed aggregator', |
---|
138 | 'page callback' => 'aggregator_page_last', |
---|
139 | 'access arguments' => array('access news feeds'), |
---|
140 | 'weight' => 5, |
---|
141 | 'file' => 'aggregator.pages.inc', |
---|
142 | ); |
---|
143 | $items['aggregator/sources'] = array( |
---|
144 | 'title' => 'Sources', |
---|
145 | 'page callback' => 'aggregator_page_sources', |
---|
146 | 'access arguments' => array('access news feeds'), |
---|
147 | 'file' => 'aggregator.pages.inc', |
---|
148 | ); |
---|
149 | $items['aggregator/categories'] = array( |
---|
150 | 'title' => 'Categories', |
---|
151 | 'page callback' => 'aggregator_page_categories', |
---|
152 | 'access callback' => '_aggregator_has_categories', |
---|
153 | 'file' => 'aggregator.pages.inc', |
---|
154 | ); |
---|
155 | $items['aggregator/rss'] = array( |
---|
156 | 'title' => 'RSS feed', |
---|
157 | 'page callback' => 'aggregator_page_rss', |
---|
158 | 'access arguments' => array('access news feeds'), |
---|
159 | 'type' => MENU_CALLBACK, |
---|
160 | 'file' => 'aggregator.pages.inc', |
---|
161 | ); |
---|
162 | $items['aggregator/opml'] = array( |
---|
163 | 'title' => 'OPML feed', |
---|
164 | 'page callback' => 'aggregator_page_opml', |
---|
165 | 'access arguments' => array('access news feeds'), |
---|
166 | 'type' => MENU_CALLBACK, |
---|
167 | 'file' => 'aggregator.pages.inc', |
---|
168 | ); |
---|
169 | $items['aggregator/categories/%aggregator_category'] = array( |
---|
170 | 'title callback' => '_aggregator_category_title', |
---|
171 | 'title arguments' => array(2), |
---|
172 | 'page callback' => 'aggregator_page_category', |
---|
173 | 'page arguments' => array(2), |
---|
174 | 'access callback' => 'user_access', |
---|
175 | 'access arguments' => array('access news feeds'), |
---|
176 | 'file' => 'aggregator.pages.inc', |
---|
177 | ); |
---|
178 | $items['aggregator/categories/%aggregator_category/view'] = array( |
---|
179 | 'title' => 'View', |
---|
180 | 'type' => MENU_DEFAULT_LOCAL_TASK, |
---|
181 | 'weight' => -10, |
---|
182 | ); |
---|
183 | $items['aggregator/categories/%aggregator_category/categorize'] = array( |
---|
184 | 'title' => 'Categorize', |
---|
185 | 'page callback' => 'drupal_get_form', |
---|
186 | 'page arguments' => array('aggregator_page_category', 2), |
---|
187 | 'access arguments' => array('administer news feeds'), |
---|
188 | 'type' => MENU_LOCAL_TASK, |
---|
189 | 'file' => 'aggregator.pages.inc', |
---|
190 | ); |
---|
191 | $items['aggregator/categories/%aggregator_category/configure'] = array( |
---|
192 | 'title' => 'Configure', |
---|
193 | 'page callback' => 'drupal_get_form', |
---|
194 | 'page arguments' => array('aggregator_form_category', 2), |
---|
195 | 'access arguments' => array('administer news feeds'), |
---|
196 | 'type' => MENU_LOCAL_TASK, |
---|
197 | 'weight' => 1, |
---|
198 | 'file' => 'aggregator.admin.inc', |
---|
199 | ); |
---|
200 | $items['aggregator/sources/%aggregator_feed'] = array( |
---|
201 | 'page callback' => 'aggregator_page_source', |
---|
202 | 'page arguments' => array(2), |
---|
203 | 'access arguments' => array('access news feeds'), |
---|
204 | 'type' => MENU_CALLBACK, |
---|
205 | 'file' => 'aggregator.pages.inc', |
---|
206 | ); |
---|
207 | $items['aggregator/sources/%aggregator_feed/view'] = array( |
---|
208 | 'title' => 'View', |
---|
209 | 'type' => MENU_DEFAULT_LOCAL_TASK, |
---|
210 | 'weight' => -10, |
---|
211 | ); |
---|
212 | $items['aggregator/sources/%aggregator_feed/categorize'] = array( |
---|
213 | 'title' => 'Categorize', |
---|
214 | 'page callback' => 'drupal_get_form', |
---|
215 | 'page arguments' => array('aggregator_page_source', 2), |
---|
216 | 'access arguments' => array('administer news feeds'), |
---|
217 | 'type' => MENU_LOCAL_TASK, |
---|
218 | 'file' => 'aggregator.pages.inc', |
---|
219 | ); |
---|
220 | $items['aggregator/sources/%aggregator_feed/configure'] = array( |
---|
221 | 'title' => 'Configure', |
---|
222 | 'page callback' => 'drupal_get_form', |
---|
223 | 'page arguments' => array('aggregator_form_feed', 2), |
---|
224 | 'access arguments' => array('administer news feeds'), |
---|
225 | 'type' => MENU_LOCAL_TASK, |
---|
226 | 'weight' => 1, |
---|
227 | 'file' => 'aggregator.admin.inc', |
---|
228 | ); |
---|
229 | $items['admin/content/aggregator/edit/feed/%aggregator_feed'] = array( |
---|
230 | 'title' => 'Edit feed', |
---|
231 | 'page callback' => 'drupal_get_form', |
---|
232 | 'page arguments' => array('aggregator_form_feed', 5), |
---|
233 | 'access arguments' => array('administer news feeds'), |
---|
234 | 'type' => MENU_CALLBACK, |
---|
235 | 'file' => 'aggregator.admin.inc', |
---|
236 | ); |
---|
237 | $items['admin/content/aggregator/edit/category/%aggregator_category'] = array( |
---|
238 | 'title' => 'Edit category', |
---|
239 | 'page callback' => 'drupal_get_form', |
---|
240 | 'page arguments' => array('aggregator_form_category', 5), |
---|
241 | 'access arguments' => array('administer news feeds'), |
---|
242 | 'type' => MENU_CALLBACK, |
---|
243 | 'file' => 'aggregator.admin.inc', |
---|
244 | ); |
---|
245 | |
---|
246 | return $items; |
---|
247 | } |
---|
248 | |
---|
249 | /** |
---|
250 | * Menu callback. |
---|
251 | * |
---|
252 | * @return |
---|
253 | * An aggregator category title. |
---|
254 | */ |
---|
255 | function _aggregator_category_title($category) { |
---|
256 | return $category['title']; |
---|
257 | } |
---|
258 | |
---|
259 | /** |
---|
260 | * Implementation of hook_init(). |
---|
261 | */ |
---|
262 | function aggregator_init() { |
---|
263 | drupal_add_css(drupal_get_path('module', 'aggregator') .'/aggregator.css'); |
---|
264 | } |
---|
265 | |
---|
266 | /** |
---|
267 | * Find out whether there are any aggregator categories. |
---|
268 | * |
---|
269 | * @return |
---|
270 | * TRUE if there is at least one category and the user has access to them, FALSE otherwise. |
---|
271 | */ |
---|
272 | function _aggregator_has_categories() { |
---|
273 | return user_access('access news feeds') && db_result(db_query('SELECT COUNT(*) FROM {aggregator_category}')); |
---|
274 | } |
---|
275 | |
---|
276 | /** |
---|
277 | * Implementation of hook_perm(). |
---|
278 | */ |
---|
279 | function aggregator_perm() { |
---|
280 | return array('administer news feeds', 'access news feeds'); |
---|
281 | } |
---|
282 | |
---|
283 | /** |
---|
284 | * Implementation of hook_cron(). |
---|
285 | * |
---|
286 | * Checks news feeds for updates once their refresh interval has elapsed. |
---|
287 | */ |
---|
288 | function aggregator_cron() { |
---|
289 | $result = db_query('SELECT * FROM {aggregator_feed} WHERE checked + refresh < %d', time()); |
---|
290 | while ($feed = db_fetch_array($result)) { |
---|
291 | aggregator_refresh($feed); |
---|
292 | } |
---|
293 | } |
---|
294 | |
---|
295 | /** |
---|
296 | * Implementation of hook_block(). |
---|
297 | * |
---|
298 | * Generates blocks for the latest news items in each category and feed. |
---|
299 | */ |
---|
300 | function aggregator_block($op = 'list', $delta = 0, $edit = array()) { |
---|
301 | if ($op == 'list') { |
---|
302 | $result = db_query('SELECT cid, title FROM {aggregator_category} ORDER BY title'); |
---|
303 | while ($category = db_fetch_object($result)) { |
---|
304 | $block['category-'. $category->cid]['info'] = t('!title category latest items', array('!title' => $category->title)); |
---|
305 | } |
---|
306 | $result = db_query('SELECT fid, title FROM {aggregator_feed} ORDER BY fid'); |
---|
307 | while ($feed = db_fetch_object($result)) { |
---|
308 | $block['feed-'. $feed->fid]['info'] = t('!title feed latest items', array('!title' => $feed->title)); |
---|
309 | } |
---|
310 | } |
---|
311 | else if ($op == 'configure') { |
---|
312 | list($type, $id) = explode('-', $delta); |
---|
313 | if ($type == 'category') { |
---|
314 | $value = db_result(db_query('SELECT block FROM {aggregator_category} WHERE cid = %d', $id)); |
---|
315 | } |
---|
316 | else { |
---|
317 | $value = db_result(db_query('SELECT block FROM {aggregator_feed} WHERE fid = %d', $id)); |
---|
318 | } |
---|
319 | $form['block'] = array('#type' => 'select', '#title' => t('Number of news items in block'), '#default_value' => $value, '#options' => drupal_map_assoc(array(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20))); |
---|
320 | return $form; |
---|
321 | } |
---|
322 | else if ($op == 'save') { |
---|
323 | list($type, $id) = explode('-', $delta); |
---|
324 | if ($type == 'category') { |
---|
325 | $value = db_query('UPDATE {aggregator_category} SET block = %d WHERE cid = %d', $edit['block'], $id); |
---|
326 | } |
---|
327 | else { |
---|
328 | $value = db_query('UPDATE {aggregator_feed} SET block = %d WHERE fid = %d', $edit['block'], $id); |
---|
329 | } |
---|
330 | } |
---|
331 | else if ($op == 'view') { |
---|
332 | if (user_access('access news feeds')) { |
---|
333 | list($type, $id) = explode('-', $delta); |
---|
334 | switch ($type) { |
---|
335 | case 'feed': |
---|
336 | if ($feed = db_fetch_object(db_query('SELECT fid, title, block FROM {aggregator_feed} WHERE fid = %d', $id))) { |
---|
337 | $block['subject'] = check_plain($feed->title); |
---|
338 | $result = db_query_range('SELECT * FROM {aggregator_item} WHERE fid = %d ORDER BY timestamp DESC, iid DESC', $feed->fid, 0, $feed->block); |
---|
339 | $read_more = theme('more_link', url('aggregator/sources/'. $feed->fid), t("View this feed's recent news.")); |
---|
340 | } |
---|
341 | break; |
---|
342 | |
---|
343 | case 'category': |
---|
344 | if ($category = db_fetch_object(db_query('SELECT cid, title, block FROM {aggregator_category} WHERE cid = %d', $id))) { |
---|
345 | $block['subject'] = check_plain($category->title); |
---|
346 | $result = db_query_range('SELECT i.* FROM {aggregator_category_item} ci LEFT JOIN {aggregator_item} i ON ci.iid = i.iid WHERE ci.cid = %d ORDER BY i.timestamp DESC, i.iid DESC', $category->cid, 0, $category->block); |
---|
347 | $read_more = theme('more_link', url('aggregator/categories/'. $category->cid), t("View this category's recent news.")); |
---|
348 | } |
---|
349 | break; |
---|
350 | } |
---|
351 | $items = array(); |
---|
352 | while ($item = db_fetch_object($result)) { |
---|
353 | $items[] = theme('aggregator_block_item', $item); |
---|
354 | } |
---|
355 | |
---|
356 | // Only display the block if there are items to show. |
---|
357 | if (count($items) > 0) { |
---|
358 | $block['content'] = theme('item_list', $items) . $read_more; |
---|
359 | } |
---|
360 | } |
---|
361 | } |
---|
362 | if (isset($block)) { |
---|
363 | return $block; |
---|
364 | } |
---|
365 | } |
---|
366 | |
---|
367 | /** |
---|
368 | * Add/edit/delete aggregator categories. |
---|
369 | * |
---|
370 | * @param $edit |
---|
371 | * An associative array describing the category to be added/edited/deleted. |
---|
372 | */ |
---|
373 | function aggregator_save_category($edit) { |
---|
374 | $link_path = 'aggregator/categories/'; |
---|
375 | if (!empty($edit['cid'])) { |
---|
376 | $link_path .= $edit['cid']; |
---|
377 | if (!empty($edit['title'])) { |
---|
378 | db_query("UPDATE {aggregator_category} SET title = '%s', description = '%s' WHERE cid = %d", $edit['title'], $edit['description'], $edit['cid']); |
---|
379 | $op = 'update'; |
---|
380 | } |
---|
381 | else { |
---|
382 | db_query('DELETE FROM {aggregator_category} WHERE cid = %d', $edit['cid']); |
---|
383 | // Make sure there is no active block for this category. |
---|
384 | db_query("DELETE FROM {blocks} WHERE module = '%s' AND delta = '%s'", 'aggregator', 'category-' . $edit['cid']); |
---|
385 | $edit['title'] = ''; |
---|
386 | $op = 'delete'; |
---|
387 | } |
---|
388 | } |
---|
389 | else if (!empty($edit['title'])) { |
---|
390 | // A single unique id for bundles and feeds, to use in blocks |
---|
391 | db_query("INSERT INTO {aggregator_category} (title, description, block) VALUES ('%s', '%s', 5)", $edit['title'], $edit['description']); |
---|
392 | $link_path .= db_last_insert_id('aggregator_category', 'cid'); |
---|
393 | $op = 'insert'; |
---|
394 | } |
---|
395 | if (isset($op)) { |
---|
396 | menu_link_maintain('aggregator', $op, $link_path, $edit['title']); |
---|
397 | } |
---|
398 | } |
---|
399 | |
---|
400 | /** |
---|
401 | * Add/edit/delete an aggregator feed. |
---|
402 | * |
---|
403 | * @param $edit |
---|
404 | * An associative array describing the feed to be added/edited/deleted. |
---|
405 | */ |
---|
406 | function aggregator_save_feed($edit) { |
---|
407 | if (!empty($edit['fid'])) { |
---|
408 | // An existing feed is being modified, delete the category listings. |
---|
409 | db_query('DELETE FROM {aggregator_category_feed} WHERE fid = %d', $edit['fid']); |
---|
410 | } |
---|
411 | if (!empty($edit['fid']) && !empty($edit['title'])) { |
---|
412 | db_query("UPDATE {aggregator_feed} SET title = '%s', url = '%s', refresh = %d WHERE fid = %d", $edit['title'], $edit['url'], $edit['refresh'], $edit['fid']); |
---|
413 | } |
---|
414 | else if (!empty($edit['fid'])) { |
---|
415 | $items = array(); |
---|
416 | $result = db_query('SELECT iid FROM {aggregator_item} WHERE fid = %d', $edit['fid']); |
---|
417 | while ($item = db_fetch_object($result)) { |
---|
418 | $items[] = "iid = $item->iid"; |
---|
419 | } |
---|
420 | if (!empty($items)) { |
---|
421 | db_query('DELETE FROM {aggregator_category_item} WHERE '. implode(' OR ', $items)); |
---|
422 | } |
---|
423 | db_query('DELETE FROM {aggregator_feed} WHERE fid = %d', $edit['fid']); |
---|
424 | db_query('DELETE FROM {aggregator_item} WHERE fid = %d', $edit['fid']); |
---|
425 | // Make sure there is no active block for this feed. |
---|
426 | db_query("DELETE FROM {blocks} WHERE module = '%s' AND delta = '%s'", 'aggregator', 'feed-' . $edit['fid']); |
---|
427 | } |
---|
428 | else if (!empty($edit['title'])) { |
---|
429 | db_query("INSERT INTO {aggregator_feed} (title, url, refresh, block, description, image) VALUES ('%s', '%s', %d, 5, '', '')", $edit['title'], $edit['url'], $edit['refresh']); |
---|
430 | // A single unique id for bundles and feeds, to use in blocks. |
---|
431 | $edit['fid'] = db_last_insert_id('aggregator_feed', 'fid'); |
---|
432 | } |
---|
433 | if (!empty($edit['title'])) { |
---|
434 | // The feed is being saved, save the categories as well. |
---|
435 | if (!empty($edit['category'])) { |
---|
436 | foreach ($edit['category'] as $cid => $value) { |
---|
437 | if ($value) { |
---|
438 | db_query('INSERT INTO {aggregator_category_feed} (fid, cid) VALUES (%d, %d)', $edit['fid'], $cid); |
---|
439 | } |
---|
440 | } |
---|
441 | } |
---|
442 | } |
---|
443 | } |
---|
444 | |
---|
445 | /** |
---|
446 | * Removes all items from a feed. |
---|
447 | * |
---|
448 | * @param $feed |
---|
449 | * An associative array describing the feed to be cleared. |
---|
450 | */ |
---|
451 | function aggregator_remove($feed) { |
---|
452 | $result = db_query('SELECT iid FROM {aggregator_item} WHERE fid = %d', $feed['fid']); |
---|
453 | while ($item = db_fetch_object($result)) { |
---|
454 | $items[] = "iid = $item->iid"; |
---|
455 | } |
---|
456 | if (!empty($items)) { |
---|
457 | db_query('DELETE FROM {aggregator_category_item} WHERE '. implode(' OR ', $items)); |
---|
458 | } |
---|
459 | db_query('DELETE FROM {aggregator_item} WHERE fid = %d', $feed['fid']); |
---|
460 | db_query("UPDATE {aggregator_feed} SET checked = 0, etag = '', modified = 0 WHERE fid = %d", $feed['fid']); |
---|
461 | drupal_set_message(t('The news items from %site have been removed.', array('%site' => $feed['title']))); |
---|
462 | } |
---|
463 | |
---|
464 | /** |
---|
465 | * Call-back function used by the XML parser. |
---|
466 | */ |
---|
467 | function aggregator_element_start($parser, $name, $attributes) { |
---|
468 | global $item, $element, $tag, $items, $channel; |
---|
469 | |
---|
470 | switch ($name) { |
---|
471 | case 'IMAGE': |
---|
472 | case 'TEXTINPUT': |
---|
473 | case 'CONTENT': |
---|
474 | case 'SUMMARY': |
---|
475 | case 'TAGLINE': |
---|
476 | case 'SUBTITLE': |
---|
477 | case 'LOGO': |
---|
478 | case 'INFO': |
---|
479 | $element = $name; |
---|
480 | break; |
---|
481 | case 'ID': |
---|
482 | if ($element != 'ITEM') { |
---|
483 | $element = $name; |
---|
484 | } |
---|
485 | case 'LINK': |
---|
486 | if (!empty($attributes['REL']) && $attributes['REL'] == 'alternate') { |
---|
487 | if ($element == 'ITEM') { |
---|
488 | $items[$item]['LINK'] = $attributes['HREF']; |
---|
489 | } |
---|
490 | else { |
---|
491 | $channel['LINK'] = $attributes['HREF']; |
---|
492 | } |
---|
493 | } |
---|
494 | break; |
---|
495 | case 'ITEM': |
---|
496 | $element = $name; |
---|
497 | $item += 1; |
---|
498 | break; |
---|
499 | case 'ENTRY': |
---|
500 | $element = 'ITEM'; |
---|
501 | $item += 1; |
---|
502 | break; |
---|
503 | } |
---|
504 | |
---|
505 | $tag = $name; |
---|
506 | } |
---|
507 | |
---|
508 | /** |
---|
509 | * Call-back function used by the XML parser. |
---|
510 | */ |
---|
511 | function aggregator_element_end($parser, $name) { |
---|
512 | global $element; |
---|
513 | |
---|
514 | switch ($name) { |
---|
515 | case 'IMAGE': |
---|
516 | case 'TEXTINPUT': |
---|
517 | case 'ITEM': |
---|
518 | case 'ENTRY': |
---|
519 | case 'CONTENT': |
---|
520 | case 'INFO': |
---|
521 | $element = ''; |
---|
522 | break; |
---|
523 | case 'ID': |
---|
524 | if ($element == 'ID') { |
---|
525 | $element = ''; |
---|
526 | } |
---|
527 | } |
---|
528 | } |
---|
529 | |
---|
530 | /** |
---|
531 | * Call-back function used by the XML parser. |
---|
532 | */ |
---|
533 | function aggregator_element_data($parser, $data) { |
---|
534 | global $channel, $element, $items, $item, $image, $tag; |
---|
535 | $items += array($item => array()); |
---|
536 | switch ($element) { |
---|
537 | case 'ITEM': |
---|
538 | $items[$item] += array($tag => ''); |
---|
539 | $items[$item][$tag] .= $data; |
---|
540 | break; |
---|
541 | case 'IMAGE': |
---|
542 | case 'LOGO': |
---|
543 | $image += array($tag => ''); |
---|
544 | $image[$tag] .= $data; |
---|
545 | break; |
---|
546 | case 'LINK': |
---|
547 | if ($data) { |
---|
548 | $items[$item] += array($tag => ''); |
---|
549 | $items[$item][$tag] .= $data; |
---|
550 | } |
---|
551 | break; |
---|
552 | case 'CONTENT': |
---|
553 | $items[$item] += array('CONTENT' => ''); |
---|
554 | $items[$item]['CONTENT'] .= $data; |
---|
555 | break; |
---|
556 | case 'SUMMARY': |
---|
557 | $items[$item] += array('SUMMARY' => ''); |
---|
558 | $items[$item]['SUMMARY'] .= $data; |
---|
559 | break; |
---|
560 | case 'TAGLINE': |
---|
561 | case 'SUBTITLE': |
---|
562 | $channel += array('DESCRIPTION' => ''); |
---|
563 | $channel['DESCRIPTION'] .= $data; |
---|
564 | break; |
---|
565 | case 'INFO': |
---|
566 | case 'ID': |
---|
567 | case 'TEXTINPUT': |
---|
568 | // The sub-element is not supported. However, we must recognize |
---|
569 | // it or its contents will end up in the item array. |
---|
570 | break; |
---|
571 | default: |
---|
572 | $channel += array($tag => ''); |
---|
573 | $channel[$tag] .= $data; |
---|
574 | } |
---|
575 | } |
---|
576 | |
---|
577 | /** |
---|
578 | * Checks a news feed for new items. |
---|
579 | * |
---|
580 | * @param $feed |
---|
581 | * An associative array describing the feed to be refreshed. |
---|
582 | */ |
---|
583 | function aggregator_refresh($feed) { |
---|
584 | global $channel, $image; |
---|
585 | |
---|
586 | // Generate conditional GET headers. |
---|
587 | $headers = array(); |
---|
588 | if ($feed['etag']) { |
---|
589 | $headers['If-None-Match'] = $feed['etag']; |
---|
590 | } |
---|
591 | if ($feed['modified']) { |
---|
592 | $headers['If-Modified-Since'] = gmdate('D, d M Y H:i:s', $feed['modified']) .' GMT'; |
---|
593 | } |
---|
594 | |
---|
595 | // Request feed. |
---|
596 | $result = drupal_http_request($feed['url'], $headers); |
---|
597 | |
---|
598 | // Process HTTP response code. |
---|
599 | switch ($result->code) { |
---|
600 | case 304: |
---|
601 | db_query('UPDATE {aggregator_feed} SET checked = %d WHERE fid = %d', time(), $feed['fid']); |
---|
602 | drupal_set_message(t('There is no new syndicated content from %site.', array('%site' => $feed['title']))); |
---|
603 | break; |
---|
604 | case 301: |
---|
605 | $feed['url'] = $result->redirect_url; |
---|
606 | watchdog('aggregator', 'Updated URL for feed %title to %url.', array('%title' => $feed['title'], '%url' => $feed['url'])); |
---|
607 | // Deliberate no break. |
---|
608 | case 200: |
---|
609 | case 302: |
---|
610 | case 307: |
---|
611 | // Filter the input data: |
---|
612 | if (aggregator_parse_feed($result->data, $feed)) { |
---|
613 | $modified = empty($result->headers['Last-Modified']) ? 0 : strtotime($result->headers['Last-Modified']); |
---|
614 | |
---|
615 | // Prepare the channel data. |
---|
616 | foreach ($channel as $key => $value) { |
---|
617 | $channel[$key] = trim($value); |
---|
618 | } |
---|
619 | |
---|
620 | // Prepare the image data (if any). |
---|
621 | foreach ($image as $key => $value) { |
---|
622 | $image[$key] = trim($value); |
---|
623 | } |
---|
624 | |
---|
625 | if (!empty($image['LINK']) && !empty($image['URL']) && !empty($image['TITLE'])) { |
---|
626 | // Note, we should really use theme_image() here but that only works with local images it won't work with images fetched with a URL unless PHP version > 5 |
---|
627 | $image = '<a href="'. check_url($image['LINK']) .'" class="feed-image"><img src="'. check_url($image['URL']) .'" alt="'. check_plain($image['TITLE']) .'" /></a>'; |
---|
628 | } |
---|
629 | else { |
---|
630 | $image = NULL; |
---|
631 | } |
---|
632 | |
---|
633 | $etag = empty($result->headers['ETag']) ? '' : $result->headers['ETag']; |
---|
634 | // Update the feed data. |
---|
635 | db_query("UPDATE {aggregator_feed} SET url = '%s', checked = %d, link = '%s', description = '%s', image = '%s', etag = '%s', modified = %d WHERE fid = %d", $feed['url'], time(), $channel['LINK'], $channel['DESCRIPTION'], $image, $etag, $modified, $feed['fid']); |
---|
636 | |
---|
637 | // Clear the cache. |
---|
638 | cache_clear_all(); |
---|
639 | |
---|
640 | watchdog('aggregator', 'There is new syndicated content from %site.', array('%site' => $feed['title'])); |
---|
641 | drupal_set_message(t('There is new syndicated content from %site.', array('%site' => $feed['title']))); |
---|
642 | } |
---|
643 | break; |
---|
644 | default: |
---|
645 | watchdog('aggregator', 'The feed from %site seems to be broken, due to "%error".', array('%site' => $feed['title'], '%error' => $result->code .' '. $result->error), WATCHDOG_WARNING); |
---|
646 | drupal_set_message(t('The feed from %site seems to be broken, because of error "%error".', array('%site' => $feed['title'], '%error' => $result->code .' '. $result->error))); |
---|
647 | } |
---|
648 | } |
---|
649 | |
---|
650 | /** |
---|
651 | * Parse the W3C date/time format, a subset of ISO 8601. PHP date parsing |
---|
652 | * functions do not handle this format. |
---|
653 | * See http://www.w3.org/TR/NOTE-datetime for more information. |
---|
654 | * Originally from MagpieRSS (http://magpierss.sourceforge.net/). |
---|
655 | * |
---|
656 | * @param $date_str |
---|
657 | * A string with a potentially W3C DTF date. |
---|
658 | * @return |
---|
659 | * A timestamp if parsed successfully or FALSE if not. |
---|
660 | */ |
---|
661 | function aggregator_parse_w3cdtf($date_str) { |
---|
662 | if (preg_match('/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(:(\d{2}))?(?:([-+])(\d{2}):?(\d{2})|(Z))?/', $date_str, $match)) { |
---|
663 | list($year, $month, $day, $hours, $minutes, $seconds) = array($match[1], $match[2], $match[3], $match[4], $match[5], $match[6]); |
---|
664 | // calc epoch for current date assuming GMT |
---|
665 | $epoch = gmmktime($hours, $minutes, $seconds, $month, $day, $year); |
---|
666 | if ($match[10] != 'Z') { // Z is zulu time, aka GMT |
---|
667 | list($tz_mod, $tz_hour, $tz_min) = array($match[8], $match[9], $match[10]); |
---|
668 | // zero out the variables |
---|
669 | if (!$tz_hour) { |
---|
670 | $tz_hour = 0; |
---|
671 | } |
---|
672 | if (!$tz_min) { |
---|
673 | $tz_min = 0; |
---|
674 | } |
---|
675 | $offset_secs = (($tz_hour * 60) + $tz_min) * 60; |
---|
676 | // is timezone ahead of GMT? then subtract offset |
---|
677 | if ($tz_mod == '+') { |
---|
678 | $offset_secs *= -1; |
---|
679 | } |
---|
680 | $epoch += $offset_secs; |
---|
681 | } |
---|
682 | return $epoch; |
---|
683 | } |
---|
684 | else { |
---|
685 | return FALSE; |
---|
686 | } |
---|
687 | } |
---|
688 | |
---|
689 | /** |
---|
690 | * Parse a feed and store its items. |
---|
691 | * |
---|
692 | * @param $data |
---|
693 | * The feed data. |
---|
694 | * @param $feed |
---|
695 | * An associative array describing the feed to be parsed. |
---|
696 | * @return |
---|
697 | * 0 on error, 1 otherwise. |
---|
698 | */ |
---|
699 | function aggregator_parse_feed(&$data, $feed) { |
---|
700 | global $items, $image, $channel; |
---|
701 | |
---|
702 | // Unset the global variables before we use them: |
---|
703 | unset($GLOBALS['element'], $GLOBALS['item'], $GLOBALS['tag']); |
---|
704 | $items = array(); |
---|
705 | $image = array(); |
---|
706 | $channel = array(); |
---|
707 | |
---|
708 | // parse the data: |
---|
709 | $xml_parser = drupal_xml_parser_create($data); |
---|
710 | xml_set_element_handler($xml_parser, 'aggregator_element_start', 'aggregator_element_end'); |
---|
711 | xml_set_character_data_handler($xml_parser, 'aggregator_element_data'); |
---|
712 | |
---|
713 | if (!xml_parse($xml_parser, $data, 1)) { |
---|
714 | watchdog('aggregator', 'The feed from %site seems to be broken, due to an error "%error" on line %line.', array('%site' => $feed['title'], '%error' => xml_error_string(xml_get_error_code($xml_parser)), '%line' => xml_get_current_line_number($xml_parser)), WATCHDOG_WARNING); |
---|
715 | drupal_set_message(t('The feed from %site seems to be broken, because of error "%error" on line %line.', array('%site' => $feed['title'], '%error' => xml_error_string(xml_get_error_code($xml_parser)), '%line' => xml_get_current_line_number($xml_parser))), 'error'); |
---|
716 | return 0; |
---|
717 | } |
---|
718 | xml_parser_free($xml_parser); |
---|
719 | |
---|
720 | // We reverse the array such that we store the first item last, and the last |
---|
721 | // item first. In the database, the newest item should be at the top. |
---|
722 | $items = array_reverse($items); |
---|
723 | |
---|
724 | // Initialize variables. |
---|
725 | $title = $link = $author = $description = $guid = NULL; |
---|
726 | foreach ($items as $item) { |
---|
727 | unset($title, $link, $author, $description, $guid); |
---|
728 | |
---|
729 | // Prepare the item: |
---|
730 | foreach ($item as $key => $value) { |
---|
731 | $item[$key] = trim($value); |
---|
732 | } |
---|
733 | |
---|
734 | // Resolve the item's title. If no title is found, we use up to 40 |
---|
735 | // characters of the description ending at a word boundary but not |
---|
736 | // splitting potential entities. |
---|
737 | if (!empty($item['TITLE'])) { |
---|
738 | $title = $item['TITLE']; |
---|
739 | } |
---|
740 | elseif (!empty($item['DESCRIPTION'])) { |
---|
741 | $title = preg_replace('/^(.*)[^\w;&].*?$/', "\\1", truncate_utf8($item['DESCRIPTION'], 40)); |
---|
742 | } |
---|
743 | else { |
---|
744 | $title = ''; |
---|
745 | } |
---|
746 | |
---|
747 | // Resolve the items link. |
---|
748 | if (!empty($item['LINK'])) { |
---|
749 | $link = $item['LINK']; |
---|
750 | } |
---|
751 | else { |
---|
752 | $link = $feed['link']; |
---|
753 | } |
---|
754 | |
---|
755 | // Atom feeds use ID rather than GUID. |
---|
756 | if (isset($item['GUID'])) { |
---|
757 | $guid = $item['GUID']; |
---|
758 | } |
---|
759 | elseif (isset($item['ID'])) { |
---|
760 | $guid = $item['ID']; |
---|
761 | } |
---|
762 | else { |
---|
763 | $guid = ''; |
---|
764 | } |
---|
765 | |
---|
766 | // Atom feeds have a CONTENT and/or SUMMARY tag instead of a DESCRIPTION tag. |
---|
767 | if (!empty($item['CONTENT:ENCODED'])) { |
---|
768 | $item['DESCRIPTION'] = $item['CONTENT:ENCODED']; |
---|
769 | } |
---|
770 | else if (!empty($item['SUMMARY'])) { |
---|
771 | $item['DESCRIPTION'] = $item['SUMMARY']; |
---|
772 | } |
---|
773 | else if (!empty($item['CONTENT'])) { |
---|
774 | $item['DESCRIPTION'] = $item['CONTENT']; |
---|
775 | } |
---|
776 | |
---|
777 | // Try to resolve and parse the item's publication date. If no date is |
---|
778 | // found, we use the current date instead. |
---|
779 | $date = 'now'; |
---|
780 | foreach (array('PUBDATE', 'DC:DATE', 'DCTERMS:ISSUED', 'DCTERMS:CREATED', 'DCTERMS:MODIFIED', 'ISSUED', 'CREATED', 'MODIFIED', 'PUBLISHED', 'UPDATED') as $key) { |
---|
781 | if (!empty($item[$key])) { |
---|
782 | $date = $item[$key]; |
---|
783 | break; |
---|
784 | } |
---|
785 | } |
---|
786 | |
---|
787 | $timestamp = strtotime($date); // As of PHP 5.1.0, strtotime returns FALSE on failure instead of -1. |
---|
788 | if ($timestamp <= 0) { |
---|
789 | $timestamp = aggregator_parse_w3cdtf($date); // Returns FALSE on failure |
---|
790 | if (!$timestamp) { |
---|
791 | $timestamp = time(); // better than nothing |
---|
792 | } |
---|
793 | } |
---|
794 | |
---|
795 | // Resolve dc:creator tag as the item author if author tag is not set. |
---|
796 | if (empty($item['AUTHOR']) && !empty($item['DC:CREATOR'])) { |
---|
797 | $item['AUTHOR'] = $item['DC:CREATOR']; |
---|
798 | } |
---|
799 | |
---|
800 | // Save this item. Try to avoid duplicate entries as much as possible. If |
---|
801 | // we find a duplicate entry, we resolve it and pass along its ID is such |
---|
802 | // that we can update it if needed. |
---|
803 | if (!empty($guid)) { |
---|
804 | $entry = db_fetch_object(db_query("SELECT iid FROM {aggregator_item} WHERE fid = %d AND guid = '%s'", $feed['fid'], $guid)); |
---|
805 | } |
---|
806 | else if ($link && $link != $feed['link'] && $link != $feed['url']) { |
---|
807 | $entry = db_fetch_object(db_query("SELECT iid FROM {aggregator_item} WHERE fid = %d AND link = '%s'", $feed['fid'], $link)); |
---|
808 | } |
---|
809 | else { |
---|
810 | $entry = db_fetch_object(db_query("SELECT iid FROM {aggregator_item} WHERE fid = %d AND title = '%s'", $feed['fid'], $title)); |
---|
811 | } |
---|
812 | $item += array('AUTHOR' => '', 'DESCRIPTION' => ''); |
---|
813 | aggregator_save_item(array('iid' => (isset($entry->iid) ? $entry->iid: ''), 'fid' => $feed['fid'], 'timestamp' => $timestamp, 'title' => $title, 'link' => $link, 'author' => $item['AUTHOR'], 'description' => $item['DESCRIPTION'], 'guid' => $guid)); |
---|
814 | } |
---|
815 | |
---|
816 | // Remove all items that are older than flush item timer. |
---|
817 | $age = time() - variable_get('aggregator_clear', 9676800); |
---|
818 | $result = db_query('SELECT iid FROM {aggregator_item} WHERE fid = %d AND timestamp < %d', $feed['fid'], $age); |
---|
819 | |
---|
820 | $items = array(); |
---|
821 | $num_rows = FALSE; |
---|
822 | while ($item = db_fetch_object($result)) { |
---|
823 | $items[] = $item->iid; |
---|
824 | $num_rows = TRUE; |
---|
825 | } |
---|
826 | if ($num_rows) { |
---|
827 | db_query('DELETE FROM {aggregator_category_item} WHERE iid IN ('. implode(', ', $items) .')'); |
---|
828 | db_query('DELETE FROM {aggregator_item} WHERE fid = %d AND timestamp < %d', $feed['fid'], $age); |
---|
829 | } |
---|
830 | |
---|
831 | return 1; |
---|
832 | } |
---|
833 | |
---|
834 | /** |
---|
835 | * Add/edit/delete an aggregator item. |
---|
836 | * |
---|
837 | * @param $edit |
---|
838 | * An associative array describing the item to be added/edited/deleted. |
---|
839 | */ |
---|
840 | function aggregator_save_item($edit) { |
---|
841 | if ($edit['iid'] && $edit['title']) { |
---|
842 | db_query("UPDATE {aggregator_item} SET title = '%s', link = '%s', author = '%s', description = '%s', guid = '%s', timestamp = %d WHERE iid = %d", $edit['title'], $edit['link'], $edit['author'], $edit['description'], $edit['guid'], $edit['timestamp'], $edit['iid']); |
---|
843 | } |
---|
844 | else if ($edit['iid']) { |
---|
845 | db_query('DELETE FROM {aggregator_item} WHERE iid = %d', $edit['iid']); |
---|
846 | db_query('DELETE FROM {aggregator_category_item} WHERE iid = %d', $edit['iid']); |
---|
847 | } |
---|
848 | else if ($edit['title'] && $edit['link']) { |
---|
849 | db_query("INSERT INTO {aggregator_item} (fid, title, link, author, description, timestamp, guid) VALUES (%d, '%s', '%s', '%s', '%s', %d, '%s')", $edit['fid'], $edit['title'], $edit['link'], $edit['author'], $edit['description'], $edit['timestamp'], $edit['guid']); |
---|
850 | $edit['iid'] = db_last_insert_id('aggregator_item', 'iid'); |
---|
851 | // file the items in the categories indicated by the feed |
---|
852 | $categories = db_query('SELECT cid FROM {aggregator_category_feed} WHERE fid = %d', $edit['fid']); |
---|
853 | while ($category = db_fetch_object($categories)) { |
---|
854 | db_query('INSERT INTO {aggregator_category_item} (cid, iid) VALUES (%d, %d)', $category->cid, $edit['iid']); |
---|
855 | } |
---|
856 | } |
---|
857 | } |
---|
858 | |
---|
859 | /** |
---|
860 | * Load an aggregator feed. |
---|
861 | * |
---|
862 | * @param $fid |
---|
863 | * The feed id. |
---|
864 | * @return |
---|
865 | * An associative array describing the feed. |
---|
866 | */ |
---|
867 | function aggregator_feed_load($fid) { |
---|
868 | static $feeds; |
---|
869 | if (!isset($feeds[$fid])) { |
---|
870 | $feeds[$fid] = db_fetch_array(db_query('SELECT * FROM {aggregator_feed} WHERE fid = %d', $fid)); |
---|
871 | } |
---|
872 | return $feeds[$fid]; |
---|
873 | } |
---|
874 | |
---|
875 | /** |
---|
876 | * Load an aggregator category. |
---|
877 | * |
---|
878 | * @param $cid |
---|
879 | * The category id. |
---|
880 | * @return |
---|
881 | * An associative array describing the category. |
---|
882 | */ |
---|
883 | function aggregator_category_load($cid) { |
---|
884 | static $categories; |
---|
885 | if (!isset($categories[$cid])) { |
---|
886 | $categories[$cid] = db_fetch_array(db_query('SELECT * FROM {aggregator_category} WHERE cid = %d', $cid)); |
---|
887 | } |
---|
888 | return $categories[$cid]; |
---|
889 | } |
---|
890 | |
---|
891 | /** |
---|
892 | * Format an individual feed item for display in the block. |
---|
893 | * |
---|
894 | * @param $item |
---|
895 | * The item to be displayed. |
---|
896 | * @param $feed |
---|
897 | * Not used. |
---|
898 | * @return |
---|
899 | * The item HTML. |
---|
900 | * @ingroup themeable |
---|
901 | */ |
---|
902 | function theme_aggregator_block_item($item, $feed = 0) { |
---|
903 | global $user; |
---|
904 | |
---|
905 | $output = ''; |
---|
906 | if ($user->uid && module_exists('blog') && user_access('create blog entries')) { |
---|
907 | if ($image = theme('image', 'misc/blog.png', t('blog it'), t('blog it'))) { |
---|
908 | $output .= '<div class="icon">'. l($image, 'node/add/blog', array('attributes' => array('title' => t('Comment on this news item in your personal blog.'), 'class' => 'blog-it'), 'query' => "iid=$item->iid", 'html' => TRUE)) .'</div>'; |
---|
909 | } |
---|
910 | } |
---|
911 | |
---|
912 | // Display the external link to the item. |
---|
913 | $output .= '<a href="'. check_url($item->link) .'">'. check_plain($item->title) ."</a>\n"; |
---|
914 | |
---|
915 | return $output; |
---|
916 | } |
---|
917 | |
---|
918 | /** |
---|
919 | * Safely render HTML content, as allowed. |
---|
920 | * |
---|
921 | * @param $value |
---|
922 | * The content to be filtered. |
---|
923 | * @return |
---|
924 | * The filtered content. |
---|
925 | */ |
---|
926 | function aggregator_filter_xss($value) { |
---|
927 | 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)); |
---|
928 | } |
---|
929 | |
---|
930 | /** |
---|
931 | * Helper function for drupal_map_assoc. |
---|
932 | * |
---|
933 | * @param $count |
---|
934 | * Items count. |
---|
935 | * @return |
---|
936 | * Plural-formatted "@count items" |
---|
937 | */ |
---|
938 | function _aggregator_items($count) { |
---|
939 | return format_plural($count, '1 item', '@count items'); |
---|
940 | } |
---|