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