/[drupal]/contributions/sandbox/alex_b/feedapi_aggregator/feedapi_aggregator.module
ViewVC logotype

Contents of /contributions/sandbox/alex_b/feedapi_aggregator/feedapi_aggregator.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.7 - (show annotations) (download) (as text)
Tue Jul 24 15:29:06 2007 UTC (2 years, 4 months ago) by alexb
Branch: MAIN
CVS Tags: HEAD
Changes since 1.6: +47 -3 lines
File MIME type: text/x-php
Added: list out sources nicely
1 <?php
2 // $Id: feedapi_aggregator.module,v 1.6 2007/07/24 13:57:57 alexb Exp $
3
4 /**
5 * @abstract This module emulates aggregator module with the feedapi framework.
6 * @todo
7 * - Remove duplicate datastructure between feedapi and feedapi_aggregator.
8 * - Now, the old datastructure is held in $feed_item->feedapi_aggregator or
9 * $feed->feedapi_aggregator as an array. This might prove superfluent.
10 */
11
12 /**
13 * Implementation of function feedapi_aggregator_help().
14 */
15 function feedapi_aggregator_help() {
16 switch ($section) {
17 case 'admin/help#feedapi_aggregator':
18 return t('Emulates the Drupal core aggregator with the FeedAPI module.');
19 }
20 }
21
22 /**
23 * Implementation of hook_menu().
24 */
25 function feedapi_aggregator_menu($may_cache) {
26 $items = array();
27 $edit = user_access('administer feedapi');
28 $view = user_access('access news feeds'); // @todo
29
30 if ($may_cache) {
31 $items[] = array(
32 'path' => 'admin/content/feed/category',
33 'title' => t('Categories'),
34 'callback' => 'feedapi_aggregator_category_view',
35 'access' => $edit,
36 'type' => MENU_LOCAL_TASK,
37 );
38 $items[] = array(
39 'path' => 'admin/content/feed/category/list',
40 'title' => t('List'),
41 'type' => MENU_DEFAULT_LOCAL_TASK,
42 'weight' => -15,
43 );
44 $items[] = array('path' => 'admin/content/feed/category/add',
45 'title' => t('Add category'),
46 'callback' => 'drupal_get_form',
47 'callback arguments' => array('feedapi_aggregator_form_category'),
48 'access' => $edit,
49 'type' => MENU_LOCAL_TASK);
50
51 $items[] = array('path' => 'aggregator',
52 'title' => t('News aggregator'),
53 'callback' => 'feedapi_aggregator_page_last',
54 'access' => $view,
55 'weight' => 5);
56 $items[] = array('path' => 'aggregator/sources',
57 'title' => t('Sources'),
58 'callback' => 'feedapi_aggregator_page_sources',
59 'access' => $view);
60 $items[] = array('path' => 'aggregator/categories',
61 'title' => t('Categories'),
62 'callback' => 'feedapi_aggregator_page_categories',
63 'access' => $view,
64 'type' => MENU_ITEM_GROUPING);
65 $items[] = array('path' => 'aggregator/rss',
66 'title' => t('RSS feed'),
67 'callback' => 'feedapi_aggregator_page_rss',
68 'access' => $view,
69 'type' => MENU_CALLBACK);
70 $items[] = array('path' => 'aggregator/opml',
71 'title' => t('OPML feed'),
72 'callback' => 'feedapi_aggregator_page_opml',
73 'access' => $view,
74 'type' => MENU_CALLBACK);
75
76 $result = db_query('SELECT title, cid FROM {aggregator_category} ORDER BY title');
77 while ($category = db_fetch_array($result)) {
78 $items[] = array('path' => 'aggregator/categories/'. $category['cid'],
79 'title' => $category['title'],
80 'callback' => 'feedapi_aggregator_page_category',
81 'access' => $view);
82 }
83 }
84 else {
85 // Add the CSS for this module
86 // We put this in !$may_cache so it's only added once per request
87 drupal_add_css(drupal_get_path('module', 'feedapi_aggregator') .'/aggregator.css');
88
89 if (arg(0) == 'aggregator' && is_numeric(arg(2))) {
90 if (arg(1) == 'sources') {
91 $feed = feedapi_aggregator_get_feed(arg(2));
92 if ($feed) {
93 $items[] = array('path' => 'aggregator/sources/'. $feed['fid'],
94 'title' => $feed['title'],
95 'callback' => 'feedapi_aggregator_page_source',
96 'access' => $view,
97 'type' => MENU_CALLBACK);
98 $items[] = array('path' => 'aggregator/sources/'. $feed['fid'] .'/view',
99 'title' => t('View'),
100 'type' => MENU_DEFAULT_LOCAL_TASK,
101 'weight' => -10);
102 $items[] = array('path' => 'aggregator/sources/'. $feed['fid'] .'/categorize',
103 'title' => t('Categorize'),
104 'callback' => 'drupal_get_form',
105 'callback arguments' => array('feedapi_aggregator_page_source'),
106 'access' => $edit,
107 'type' => MENU_LOCAL_TASK);
108 $items[] = array('path' => 'aggregator/sources/'. $feed['fid'] .'/configure',
109 'title' => t('Configure'),
110 'callback' => 'drupal_get_form',
111 'callback arguments' => array('feedapi_aggregator_form_feed', $feed),
112 'access' => $edit,
113 'type' => MENU_LOCAL_TASK,
114 'weight' => 1);
115 }
116 }
117 else if (arg(1) == 'categories') {
118 $category = feedapi_aggregator_get_category(arg(2));
119 if ($category) {
120 $items[] = array('path' => 'aggregator/categories/'. $category['cid'] .'/view',
121 'title' => t('View'),
122 'type' => MENU_DEFAULT_LOCAL_TASK,
123 'weight' => -10);
124 $items[] = array('path' => 'aggregator/categories/'. $category['cid'] .'/categorize',
125 'title' => t('Categorize'),
126 'callback' => 'drupal_get_form',
127 'callback arguments' => array('feedapi_aggregator_page_category'),
128 'access' => $edit,
129 'type' => MENU_LOCAL_TASK);
130 $items[] = array('path' => 'aggregator/categories/'. $category['cid'] .'/configure',
131 'title' => t('Configure'),
132 'callback' => 'drupal_get_form',
133 'callback arguments' => array('feedapi_aggregator_form_category', $category),
134 'access' => $edit,
135 'type' => MENU_LOCAL_TASK,
136 'weight' => 1);
137 }
138 }
139 }
140 else if (arg(2) == 'aggregator' && is_numeric(arg(5))) {
141 if (arg(4) == 'feed') {
142 $feed = feedapi_aggregator_get_feed(arg(5));
143 if ($feed) {
144 $items[] = array('path' => 'admin/content/aggregator/edit/feed/'. $feed['fid'],
145 'title' => t('Edit feed'),
146 'callback' => 'drupal_get_form',
147 'callback arguments' => array('feedapi_aggregator_form_feed', $feed),
148 'access' => $edit,
149 'type' => MENU_CALLBACK);
150 }
151 }
152 else {
153 $category = feedapi_aggregator_get_category(arg(5));
154 if ($category) {
155 $items[] = array('path' => 'admin/content/aggregator/edit/category/'. $category['cid'],
156 'title' => t('Edit category'),
157 'callback' => 'drupal_get_form',
158 'callback arguments' => array('feedapi_aggregator_form_category', $category),
159 'access' => $edit,
160 'type' => MENU_CALLBACK);
161 }
162 }
163 }
164 }
165
166 return $items;
167 }
168
169 function feedapi_aggregator_admin_settings() {
170 $items = array(0 => t('none')) + drupal_map_assoc(array(3, 5, 10, 15, 20, 25), '_aggregator_items');
171 $period = drupal_map_assoc(array(3600, 10800, 21600, 32400, 43200, 86400, 172800, 259200, 604800, 1209600, 2419200, 4838400, 9676800), 'format_interval');
172
173 $form['feedapi_aggregator_summary_items'] = array(
174 '#type' => 'select', '#title' => t('Items shown in sources and categories pages') ,
175 '#default_value' => variable_get('aggregator_summary_items', 3), '#options' => $items,
176 '#description' => t('The number of items which will be shown with each feed or category in the feed and category summary pages.')
177 );
178
179 $form['feedapi_aggregator_category_selector'] = array(
180 '#type' => 'radios', '#title' => t('Category selection type'), '#default_value' => variable_get('aggregator_category_selector', 'checkboxes'),
181 '#options' => array('checkboxes' => t('checkboxes'), 'select' => t('multiple selector')),
182 '#description' => t('The type of category selection widget which is shown on categorization pages. Checkboxes are easier to use; a multiple selector is good for working with large numbers of categories.')
183 );
184
185 return system_settings_form($form);
186 }
187
188 /**
189 * Implementation of hook_block().
190 *
191 * Generates blocks for the latest news items in each category and feed.
192 */
193 function feedapi_aggregator_block($op = 'list', $delta = 0, $edit = array()) {
194 if (user_access('access news feeds')) {
195 if ($op == 'list') {
196 $result = db_query('SELECT cid, title FROM {aggregator_category} ORDER BY title');
197 while ($category = db_fetch_object($result)) {
198 $block['category-'. $category->cid]['info'] = t('!title category latest items', array('!title' => $category->title));
199 }
200 $result = db_query('SELECT fid, title FROM {aggregator_feed} ORDER BY fid');
201 while ($feed = db_fetch_object($result)) {
202 $block['feed-'. $feed->fid]['info'] = t('!title feed latest items', array('!title' => $feed->title));
203 }
204 }
205 else if ($op == 'configure') {
206 list($type, $id) = explode('-', $delta);
207 if ($type == 'category') {
208 $value = db_result(db_query('SELECT block FROM {aggregator_category} WHERE cid = %d', $id));
209 }
210 else {
211 $value = db_result(db_query('SELECT block FROM {aggregator_feed} WHERE fid = %d', $id));
212 }
213 $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)));
214 return $form;
215 }
216 else if ($op == 'save') {
217 list($type, $id) = explode('-', $delta);
218 if ($type == 'category') {
219 $value = db_query('UPDATE {aggregator_category} SET block = %d WHERE cid = %d', $edit['block'], $id);
220 }
221 else {
222 $value = db_query('UPDATE {aggregator_feed} SET block = %d WHERE fid = %d', $edit['block'], $id);
223 }
224 }
225 else if ($op == 'view') {
226 list($type, $id) = explode('-', $delta);
227 switch ($type) {
228 case 'feed':
229 if ($feed = db_fetch_object(db_query('SELECT fid, title, block FROM {aggregator_feed} WHERE fid = %d', $id))) {
230 $block['subject'] = check_plain($feed->title);
231 $result = db_query_range('SELECT * FROM {aggregator_item} WHERE fid = %d ORDER BY timestamp DESC, iid DESC', $feed->fid, 0, $feed->block);
232 $read_more = '<div class="more-link">'. l(t('more'), 'aggregator/sources/'. $feed->fid, array('title' => t("View this feed's recent news."))) .'</div>';
233 }
234 break;
235
236 case 'category':
237 if ($category = db_fetch_object(db_query('SELECT cid, title, block FROM {aggregator_category} WHERE cid = %d', $id))) {
238 $block['subject'] = check_plain($category->title);
239 $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);
240 $read_more = '<div class="more-link">'. l(t('more'), 'aggregator/categories/'. $category->cid, array('title' => t("View this category's recent news."))) .'</div>';
241 }
242 break;
243 }
244 $items = array();
245 while ($item = db_fetch_object($result)) {
246 $items[] = theme('feedapi_aggregator_block_item', $item);
247 }
248
249 // Only display the block if there are items to show.
250 if (count($items) > 0) {
251 $block['content'] = theme('item_list', $items) . $read_more;
252 }
253 }
254 return $block;
255 }
256 }
257
258
259 /**
260 * Generate a form to add/edit/delete aggregator categories.
261 */
262 function feedapi_aggregator_form_category($edit = array()) {
263 $form['title'] = array('#type' => 'textfield',
264 '#title' => t('Title'),
265 '#default_value' => $edit['title'],
266 '#maxlength' => 64,
267 '#required' => TRUE,
268 );
269 $form['description'] = array('#type' => 'textarea',
270 '#title' => t('Description'),
271 '#default_value' => $edit['description'],
272 );
273 $form['submit'] = array('#type' => 'submit', '#value' => t('Submit'));
274
275 if ($edit['cid']) {
276 $form['delete'] = array('#type' => 'submit', '#value' => t('Delete'));
277 $form['cid'] = array('#type' => 'hidden', '#value' => $edit['cid']);
278 }
279
280 return $form;
281 }
282
283 /**
284 * Validate feedapi_aggregator_form_feed form submissions.
285 */
286 function feedapi_aggregator_form_category_validate($form_id, $form_values) {
287 if ($form_values['op'] == t('Submit')) {
288 // Check for duplicate titles
289 if (isset($form_values['cid'])) {
290 $category = db_fetch_object(db_query("SELECT cid FROM {aggregator_category} WHERE title = '%s' AND cid != %d", $form_values['title'], $form_values['cid']));
291 }
292 else {
293 $category = db_fetch_object(db_query("SELECT cid FROM {aggregator_category} WHERE title = '%s'", $form_values['title']));
294 }
295 if ($category) {
296 form_set_error('title', t('A category named %category already exists. Please enter a unique title.', array('%category' => $form_values['title'])));
297 }
298 }
299 }
300
301 /**
302 * Process feedapi_aggregator_form_category form submissions.
303 * @todo Add delete confirmation dialog.
304 */
305 function feedapi_aggregator_form_category_submit($form_id, $form_values) {
306 if ($form_values['op'] == t('Delete')) {
307 $title = $form_values['title'];
308 // Unset the title:
309 unset($form_values['title']);
310 }
311 feedapi_aggregator_save_category($form_values);
312 menu_rebuild();
313 if (isset($form_values['cid'])) {
314 if (isset($form_values['title'])) {
315 drupal_set_message(t('The category %category has been updated.', array('%category' => $form_values['title'])));
316 if (arg(0) == 'admin') {
317 return 'admin/content/feed/category/';
318 }
319 else {
320 return 'aggregator/categories/'. $form_values['cid'];
321 }
322 }
323 else {
324 watchdog('aggregator', t('Category %category deleted.', array('%category' => $title)));
325 drupal_set_message(t('The category %category has been deleted.', array('%category' => $title)));
326 if (arg(0) == 'admin') {
327 return 'admin/content/feed/category/';
328 }
329 else {
330 return 'aggregator/categories/';
331 }
332 }
333 }
334 else {
335 watchdog('aggregator', t('Category %category added.', array('%category' => $form_values['title'])), WATCHDOG_NOTICE, l(t('view'), 'admin/content/aggregator'));
336 drupal_set_message(t('The category %category has been added.', array('%category' => $form_values['title'])));
337 }
338 }
339
340 /**
341 * Add/edit/delete aggregator categories.
342 */
343 function feedapi_aggregator_save_category($edit) {
344 if ($edit['cid'] && $edit['title']) {
345 db_query("UPDATE {aggregator_category} SET title = '%s', description = '%s' WHERE cid = %d", $edit['title'], $edit['description'], $edit['cid']);
346 }
347 else if ($edit['cid']) {
348 db_query('DELETE FROM {aggregator_category} WHERE cid = %d', $edit['cid']);
349 }
350 else if ($edit['title']) {
351 // A single unique id for bundles and feeds, to use in blocks
352 $next_id = db_next_id('{aggregator_category}_cid');
353 db_query("INSERT INTO {aggregator_category} (cid, title, description, block) VALUES (%d, '%s', '%s', 5)", $next_id, $edit['title'], $edit['description']);
354 }
355 }
356
357 /**
358 * Implementation of hook_form_alter().
359 */
360 function feedapi_aggregator_form_alter($form_id, &$form) {
361 if ($form_id == 'feedapi_edit_page') {
362 // Handling of categories:
363 $options = array();
364 $values = array();
365 $categories = db_query('SELECT c.cid, c.title, f.fid FROM {aggregator_category} c LEFT JOIN {aggregator_category_feed} f ON c.cid = f.cid AND f.fid = %d ORDER BY title', $form['fid']['#value']);
366 while ($category = db_fetch_object($categories)) {
367 $options[$category->cid] = check_plain($category->title);
368 if ($category->fid) $values[] = $category->cid;
369 }
370 if ($options) {
371 $form['processors_settings']['feedapi_aggregator_category'] = array('#type' => 'checkboxes',
372 '#title' => t('Feed API Aggregator - Categorize news items'),
373 '#default_value' => $values,
374 '#options' => $options,
375 '#description' => t('New items in this feed will be automatically filed in the checked categories as they are received.'),
376 '#weight' => 0,
377 );
378 }
379 }
380 }
381
382 /**
383 * Implementation of hook_feed_save().
384 */
385 function feedapi_aggregator_feedapi_save($feed) {
386 if ($feed->update) {
387 // An existing feed is being modified, delete the category listings.
388 db_query('DELETE FROM {aggregator_category_feed} WHERE fid = %d', $feed->fid);
389 db_query("UPDATE {aggregator_feed} SET title = '%s', url = '%s', refresh = %d WHERE fid = %d", $feed->title, $feed->url, $feed->refresh, $feed->fid);
390 }
391 else {
392 db_query("INSERT INTO {aggregator_feed} (fid, title, url, refresh, block) VALUES (%d, '%s', '%s', %d, 5)", $feed->fid, $feed->title, $feed->url, $feed->refresh);
393 }
394 // The feed is being saved, save the categories as well.
395 if ($feed->feedapi_aggregator_category) {
396 foreach ($feed->feedapi_aggregator_category as $cid => $value) {
397 if ($value) {
398 db_query('INSERT INTO {aggregator_category_feed} (fid, cid) VALUES (%d, %d)', $feed->fid, $cid);
399 }
400 }
401 }
402 return $feed;
403 }
404
405 /**
406 * Implementation of hook_feedapi_update().
407 */
408 function feedapi_aggregator_feedapi_update($feed) {
409 $feed->update = TRUE;
410 $feed = feedapi_aggregator_feedapi_save($feed);
411 return $feed;
412 }
413
414 /**
415 * Implementation of hook_feedapi_delete().
416 */
417 function feedapi_aggregator_feedapi_delete(&$feed) {
418 $result = db_query('SELECT iid FROM {aggregator_item} WHERE fid = %d', $feed->fid);
419 while ($item = db_fetch_object($result)) {
420 $items[] = "iid = $item->iid";
421 }
422 if ($items) {
423 db_query('DELETE FROM {aggregator_category_item} WHERE '. implode(' OR ', $items));
424 }
425 db_query('DELETE FROM {aggregator_item} WHERE fid = %d', $feed->fid);
426 db_query('DELETE FROM {aggregator_feed} WHERE fid = %d', $feed->fid);
427 }
428
429 /**
430 * Implementation of hook_feedapi_load().
431 */
432 function feedapi_aggregator_feedapi_load($feed) {
433 $aggregator_feed = db_fetch_array(db_query('SELECT * FROM {aggregator_feed} WHERE fid = %d', $feed->fid));
434
435 $feed->title = $aggregator_feed['title'];
436 $feed->description = $aggregator_feed['description'];
437 $feed->options->original_url = $aggregator_feed['link'];
438 $feed->feedapi_aggregator = $aggregator_feed;
439 return $feed;
440 }
441
442 /**
443 * Implementation of hook_feed_validate().
444 * @todo editing feeds does not work (most likely - see isset($form_values['fid']) and further)
445 * - test and implement.
446 * @question Should we move duplicate feed checking to feedapi?
447 */
448 function feedapi_aggregator_feedapi_validate($form_id, $form_values, $feed) {
449 // Check for duplicate titles
450 if (isset($form_values['fid'])) {
451 $result = db_query("SELECT title, url FROM {aggregator_feed} WHERE (title = '%s' OR url='%s') AND fid != %d", $form_values['title'], $form_values['url'], $form_values['fid']);
452 }
453 else {
454 $result = db_query("SELECT title, url FROM {aggregator_feed} WHERE title = '%s' OR url='%s'", $form_values['title'], $form_values['url']);
455 }
456 while ($feed = db_fetch_object($result)) {
457 if (strcasecmp($feed->title, $form_values['title']) == 0) {
458 form_set_error('title', t('A feed named %feed already exists. Please enter a unique title.', array('%feed' => $form_values['title'])));
459 }
460 if (strcasecmp($feed->url, $form_values['url']) == 0) {
461 form_set_error('url', t('A feed with this URL %url already exists. Please enter a unique URL.', array('%url' => $form_values['url'])));
462 }
463 }
464 if ($form_values['feedapi_aggregator_category']) {
465 $feed->categories = $form_values['feedapi_aggregator_category'];
466 }
467 return $feed;
468 }
469
470 function feedapi_aggregator_feedapi_type() {
471 return array("XML feed");
472 }
473
474 /**
475 * Implementation of hook_feedapi_get().
476 */
477 function feedapi_aggregator_feedapi_get($url, $username = NULL, $password = NULL) {
478 $method = 'GET';
479 $follow = 3;
480 $data = NULL;
481 $timeout = 15;
482
483 if (!function_exists('curl_init')) {
484 // Conventional way - use drupal internal download mechanism
485 $headers = array();
486 if ($username != NULL && $password != NULL) {
487 $headers['Authorization'] = 'Basic: '. base64_encode("$username:$password");
488 }
489 $result = drupal_http_request($url, $headers, $method, $data, $follow);
490 return $result->data;
491 }
492
493 // Solid way - use cURL
494 $ch = curl_init();
495 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
496 curl_setopt($ch, CURLOPT_URL, $url);
497 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
498 curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
499 curl_setopt($ch, CURLOPT_USERAGENT, 'Drupal');
500
501 if (strlen($username) > 0 && strlen($password) > 0) {
502 curl_setopt($ch, CURLOPT_USERPWD, "$username:$password");
503 }
504 $result = curl_exec($ch);
505
506 if (curl_errno($ch) != 0) {
507 watchdog("feedapi", $url ." : ". curl_error($ch), WATCHDOG_WARNING);
508 curl_close($ch);
509 unset($ch);
510 return FALSE;
511 }
512 //
513 return $result;
514
515 }
516
517 function feedapi_aggregator_feedapi_expire($feed, $force = FALSE) {
518 /*
519 ** Remove all items that are older than flush item timer:
520 */
521
522 $age = time() - variable_get('aggregator_clear', 9676800);
523 $result = db_query('SELECT iid FROM {aggregator_item} WHERE fid = %d AND timestamp < %d', $feed->fid, $age);
524
525 if (db_num_rows($result)) {
526 $items = array();
527 while ($item = db_fetch_object($result)) {
528 $items[] = $item->iid;
529 }
530 db_query('DELETE FROM {aggregator_category_item} WHERE iid IN ('. implode(', ', $items) .')');
531 db_query('DELETE FROM {aggregator_item} WHERE fid = %d AND timestamp < %d', $feed->fid, $age);
532 }
533 }
534
535 function feedapi_aggregator_feedapi_item_save($feed_item, $fid) {
536 if ($feed_item->title && $feed_item->options->original_url) {
537 $feed_item->fiid = db_next_id('{aggregator_item}_iid');
538 $feed_item->feedapi_aggregator['iid'] = $feed_item->fiid;
539 db_query("INSERT INTO {aggregator_item} (iid, fid, title, link, author, description, timestamp, guid) VALUES (%d, %d, '%s', '%s', '%s', '%s', %d, '%s')", $feed_item->fiid, $fid, $feed_item->title, $feed_item->options->original_url, $feed_item->options->author, $feed_item->description, $feed_item->options->timestamp, $feed_item->options->guid);
540 // file the items in the categories indicated by the feed
541 $categories = db_query('SELECT cid FROM {aggregator_category_feed} WHERE fid = %d', $fid);
542 while ($category = db_fetch_object($categories)) {
543 db_query('INSERT INTO {aggregator_category_item} (cid, iid) VALUES (%d, %d)', $category->cid, $feed_item->fiid);
544 }
545 }
546 return $feed_item;
547 }
548
549 function feedapi_aggregator_feedapi_item_update($feed_item, $fid) {
550 if ($feed_item->fiid && $feed_item->title) {
551 db_query("UPDATE {aggregator_item} SET title = '%s', link = '%s', author = '%s', description = '%s', guid = '%s', timestamp = %d WHERE iid = %d", $feed_item->title, $feed_item->options->original_url, $feed_item->options->author, $feed_item->description, $feed_item->options->guid, $feed_item->options->timestamp, $feed_item->fiid);
552 }
553 return $feed_item;
554 }
555
556 /**
557 * Implementation of hook_feedapi_item_delete().
558 */
559 function feedapi_aggregator_feedapi_item_delete($feed_item) {
560 db_query('DELETE FROM {aggregator_category_item} WHERE iid = %d'. $feed_item->fiid);
561 db_query('DELETE FROM {aggregator_item} WHERE iid = %d'. $feed_item->fiid);
562 }
563
564 /**
565 * Implementation of hook_feedapi_item_load().
566 */
567 function feedapi_aggregator_feedapi_item_load(&$feed_item) {
568 $item = db_fetch_array(db_query("SELECT * FROM {aggregator_item} WHERE iid = %d", $feed_item->fiid));
569 $feed_item->fiid = $item['iid'];
570 $feed_item->fid = $item['fid'];
571 $feed_item->arrived = 0; // no arrived time
572 $feed_item->options->original_url = $item['link'];
573 $feed_item->options->guid = $item['guid'];
574 $feed_item->options->timestamp = $item['timestamp'];
575 $feed_item->description = $item['description'];
576 $feed_item->title = $item['title'];
577 $feed_item->feedapi_aggregator = $item;
578 return $feed_item;
579 }
580
581 /**
582 * Implementation of hook_feedapi_item_fetch_items().
583 */
584 function feedapi_aggregator_feedapi_item_fetch_items($feed) {
585 $result = db_query(
586 "SELECT * FROM {aggregator_item} ai WHERE fid = %d", $feed->fid);
587 $items = array();
588 while ($item = db_fetch_object($result)) {
589 $items[] = $item;
590 }
591 return $items;
592 }
593
594 /**
595 * Implementation of hook_feedapi_item_unique().
596 */
597 function feedapi_aggregator_feedapi_item_unique($feed_item, $fid) {
598 if ($feed_item->options->guid) {
599 $entry = db_fetch_object(db_query("SELECT iid FROM {aggregator_item} WHERE fid = %d AND guid = '%s'", $fid, $feed_item->options->guid));
600 }
601 // We cannot test against $feed URL because it is not available.
602 // else if ($link && $link != $feed['link'] && $link != $feed['url']) {
603 else if ($feed_item->options->original_url) {
604 $entry = db_fetch_object(db_query("SELECT iid FROM {aggregator_item} WHERE fid = %d AND link = '%s'", $fid, $feed_item->options->original_url));
605 }
606 else {
607 $entry = db_fetch_object(db_query("SELECT iid FROM {aggregator_item} WHERE fid = %d AND title = '%s'", $fid, $feed_item->title));
608 }
609 if ($entry) {
610 return FALSE;
611 }
612 return TRUE;
613 }
614
615 /**
616 * Implementation of hook_feedapi_parse().
617 */
618 function feedapi_aggregator_feedapi_parse($feed) {
619 include_once './'. drupal_get_path('module', 'feedapi_aggregator') .'/feedapi_aggregator_parser.inc';
620 if (!$feed->feedapi_aggregator) {
621 $feed->feedapi_aggregator = db_fetch_array(db_query('SELECT * FROM {aggregator_feed} WHERE fid = %d', $feed->fid));
622 }
623 return feedapi_aggregator_refresh($feed->feedapi_aggregator);
624 }
625
626
627 /**
628 * Implementation of hook_feedapi_compatible().
629 *
630 * @todo: Actually detect wether this parser could process this URL.
631 * @todo: Lots of parsers will be slow to go through compatible() because all of them
632 * will want to download and check a URL.
633 * @todo: Add this hook to parser requirements array in feedapi.
634 */
635 function feedapi_aggregator_feedapi_compatible($feed) {
636 return array_shift(feedapi_aggregator_feedapi_type());
637 }
638
639
640 function feedapi_aggregator_get_feed($fid) {
641 return db_fetch_array(db_query('SELECT * FROM {aggregator_feed} WHERE fid = %d', $fid));
642 }
643
644 function feedapi_aggregator_get_category($cid) {
645 return db_fetch_array(db_query('SELECT * FROM {aggregator_category} WHERE cid = %d', $cid));
646 }
647
648 function feedapi_aggregator_category_view() {
649 $result = db_query('SELECT c.cid, c.title, count(ci.iid) as items FROM {aggregator_category} c LEFT JOIN {aggregator_category_item} ci ON c.cid = ci.cid GROUP BY c.cid, c.title ORDER BY title');
650
651 $output .= '<h3>'. t('Category overview') .'</h3>';
652
653 $header = array(t('Title'), t('Items'), t('Operations'));
654 $rows = array();
655 while ($category = db_fetch_object($result)) {
656 $rows[] = array(l($category->title, "aggregator/categories/$category->cid"), format_plural($category->items, '1 item', '@count items'), l(t('edit'), "admin/content/aggregator/edit/category/$category->cid"));
657 }
658 $output .= theme('table', $header, $rows);
659
660 return $output;
661 }
662
663 /**
664 * Menu callback; displays the most recent items gathered from any feed.
665 */
666 function feedapi_aggregator_page_last() {
667 drupal_add_feed(url('aggregator/rss'), variable_get('site_name', 'Drupal') .' '. t('aggregator'));
668
669 return _feedapi_aggregator_page_list('SELECT i.*, f.title AS ftitle, f.link AS flink FROM {aggregator_item} i INNER JOIN {aggregator_feed} f ON i.fid = f.fid ORDER BY i.timestamp DESC, i.iid DESC', arg(1));
670 }
671
672 /**
673 * Menu callback; displays all the items captured from a particular feed.
674 */
675 function feedapi_aggregator_page_source() {
676 $feed = db_fetch_object(db_query('SELECT * FROM {aggregator_feed} WHERE fid = %d', arg(2)));
677 $info = theme('aggregator_feed', $feed);
678
679 return _feedapi_aggregator_page_list('SELECT * FROM {aggregator_item} WHERE fid = '. $feed->fid .' ORDER BY timestamp DESC, iid DESC', arg(3), $info);
680 }
681
682 /**
683 * Menu callback; displays all the items aggregated in a particular category.
684 */
685 function feedapi_aggregator_page_category() {
686 $category = db_fetch_object(db_query('SELECT cid, title FROM {aggregator_category} WHERE cid = %d', arg(2)));
687
688 drupal_add_feed(url('aggregator/rss/'. arg(2)), variable_get('site_name', 'Drupal') .' '. t('aggregator - @title', array('@title' => $category->title)));
689
690 return _feedapi_aggregator_page_list('SELECT i.*, f.title AS ftitle, f.link AS flink FROM {aggregator_category_item} c LEFT JOIN {aggregator_item} i ON c.iid = i.iid LEFT JOIN {aggregator_feed} f ON i.fid = f.fid WHERE cid = '. $category->cid .' ORDER BY timestamp DESC, iid DESC', arg(3));
691 }
692
693 /**
694 * FIXME
695 */
696 function feedapi_aggregator_page_list($sql, $header, $categorize) {
697 $form['#base'] = 'feedapi_aggregator_page_list';
698 $form['header'] = array('#value' => $header);
699 $result = pager_query($sql, 20);
700 $categories = array();
701 $done = FALSE;
702 $form['items'] = array();
703 $form['categories'] = array('#tree' => TRUE);
704 while ($item = db_fetch_object($result)) {
705 $form['items'][$item->iid] = array('#value' => theme('feedapi_aggregator_page_item', $item));
706 $form['categories'][$item->iid] = array();
707 if ($categorize) {
708 $categories_result = db_query('SELECT c.cid, c.title, ci.iid FROM {aggregator_category} c LEFT JOIN {aggregator_category_item} ci ON c.cid = ci.cid AND ci.iid = %d', $item->iid);
709 $selected = array();
710 while ($category = db_fetch_object($categories_result)) {
711 if (!$done) {
712 $categories[$category->cid] = check_plain($category->title);
713 }
714 if ($category->iid) {
715 $selected[] = $category->cid;
716 }
717 }
718 $done = TRUE;
719 $form['categories'][$item->iid] = array(
720 '#type' => variable_get('aggregator_category_selector', 'checkboxes'),
721 '#default_value' => $selected, '#options' => $categories,
722 '#size' => 10, '#multiple' => TRUE
723 );
724 }
725 }
726 $form['submit'] = array('#type' => 'submit', '#value' => t('Save categories'));
727 $form['pager'] = array('#value' => theme('pager', NULL, 20, 0));
728
729 return $form;
730 }
731
732 /**
733 * Prints an aggregator page listing a number of feed items. Various
734 * menu callbacks use this function to print their feeds.
735 */
736 function _feedapi_aggregator_page_list($sql, $op, $header = '') {
737 $categorize = (user_access('administer news feeds') && ($op == 'categorize'));
738 $form = feedapi_aggregator_page_list($sql, $header, $categorize);
739 if ($categorize) {
740 return $form;
741 }
742 else {
743 $output = '<div id="aggregator">';
744 $output .= $header;
745 foreach ($form['items'] as $item) {
746 $output .= $item['#value'];
747 }
748 $output .= '</div>';
749 $output .= $form['pager']['#value'];
750 $output .= $form['feed_icon']['#value'];
751 return $output;
752 }
753 }
754
755 /**
756 * Menu callback; displays all the feeds used by the aggregator.
757 */
758 function feedapi_aggregator_page_sources() {
759 $result = db_query('SELECT f.fid, f.title, f.description, f.image, MAX(i.timestamp) AS last FROM {aggregator_feed} f LEFT JOIN {aggregator_item} i ON f.fid = i.fid GROUP BY f.fid, f.title, f.description, f.image ORDER BY last DESC, f.title');
760 $output = "<div id=\"aggregator\">\n";
761 while ($feed = db_fetch_object($result)) {
762 $output .= '<h2>'. check_plain($feed->title) ."</h2>\n";
763
764 // Most recent items:
765 $list = array();
766 if (variable_get('aggregator_summary_items', 3)) {
767 $items = db_query_range('SELECT i.title, i.timestamp, i.link FROM {aggregator_item} i WHERE i.fid = %d ORDER BY i.timestamp DESC', $feed->fid, 0, variable_get('aggregator_summary_items', 3));
768 while ($item = db_fetch_object($items)) {
769 $list[] = theme('feedapi_aggregator_summary_item', $item);
770 }
771 }
772 $output .= theme('item_list', $list);
773
774 $link['sources'] = array(
775 'title' => t('More'),
776 'href' => 'aggregator/sources/'. $feed->fid
777 );
778
779 $output .= '<div class="links">'. theme('links', $link) ."</div>\n";
780 }
781 $output .= theme('xml_icon', url('aggregator/opml'));
782 $output .= '</div>';
783 return $output;
784 }
785
786 /**
787 * Menu callback; displays all the categories used by the aggregator.
788 */
789 function feedapi_aggregator_page_categories() {
790 $result = db_query('SELECT c.cid, c.title, c.description FROM {aggregator_category} c LEFT JOIN {aggregator_category_item} ci ON c.cid = ci.cid LEFT JOIN {aggregator_item} i ON ci.iid = i.iid GROUP BY c.cid, c.title, c.description');
791 $output = "<div id=\"aggregator\">\n";
792
793 while ($category = db_fetch_object($result)) {
794 $output .= '<h2>'. check_plain($category->title) ."</h2>\n";
795 if (variable_get('aggregator_summary_items', 3)) { // @todo: setting for this guy
796 $list = array();
797 $items = db_query_range('SELECT i.title, i.timestamp, i.link, f.title as feed_title, f.link as feed_link FROM {aggregator_category_item} ci LEFT JOIN {aggregator_item} i ON i.iid = ci.iid LEFT JOIN {aggregator_feed} f ON i.fid = f.fid WHERE ci.cid = %d ORDER BY i.timestamp DESC', $category->cid, 0, variable_get('aggregator_summary_items', 3));
798 while ($item = db_fetch_object($items)) {
799 $list[] = theme('feedapi_aggregator_summary_item', $item);
800 }
801 $output .= theme('item_list', $list);
802 }
803
804 $link['categories'] = array(
805 'title' => t('More'),
806 'href' => 'aggregator/categories/'. $category->cid
807 );
808
809 $output .= '<div class="links">'. theme('links', $link) ."</div>\n";
810 }
811
812 $output .= '</div>';
813
814 return $output;
815 }
816
817 /**
818 * Return a themed item heading for summary pages located at "aggregator/sources"
819 * and "aggregator/categories".
820 *
821 * @param $item The item object from the aggregator module.
822 * @return A string containing the output.
823 *
824 * @ingroup themeable
825 */
826 function theme_feedapi_aggregator_summary_item($item) {
827 $output = '<a href="'. check_url($item->link) .'">'. check_plain($item->title) .'</a> <span class="age">'. t('%age old', array('%age' => format_interval(time() - $item->timestamp))) .'</span>';
828 if ($item->feed_link) {
829 $output .= ', <span class="source"><a href="'. check_url($item->feed_link) .'">'. check_plain($item->feed_title) .'</a></span>';
830 }
831 return $output ."\n";
832 }
833
834 /**
835 * Format an individual feed item for display on the aggregator page.
836 *
837 * @ingroup themeable
838 */
839 function theme_feedapi_aggregator_page_item($item) {
840
841 $source = '';
842 if ($item->ftitle && $item->fid) {
843 $source = l($item->ftitle, "aggregator/sources/$item->fid", array('class' => 'feed-item-source')) .' -';
844 }
845
846 if (date('Ymd', $item->timestamp) == date('Ymd')) {
847 $source_date = t('%ago ago', array('%ago' => format_interval(time() - $item->timestamp)));
848 }
849 else {
850 $source_date = format_date($item->timestamp, 'custom', variable_get('date_format_medium', 'D, m/d/Y - H:i'));
851 }
852
853 $output .= "<div class=\"feed-item\">\n";
854 $output .= '<h3 class="feed-item-title"><a href="'. check_url($item->link) .'">'. check_plain($item->title) ."</a></h3>\n";
855 $output .= "<div class=\"feed-item-meta\">$source <span class=\"feed-item-date\">$source_date</span></div>\n";
856
857 if ($item->description) {
858 $output .= '<div class="feed-item-body">'. feedapi_aggregator_filter_xss($item->description) ."</div>\n";
859 }
860
861 $result = db_query('SELECT c.title, c.cid FROM {aggregator_category_item} ci LEFT JOIN {aggregator_category} c ON ci.cid = c.cid WHERE ci.iid = %d ORDER BY c.title', $item->iid);
862 $categories = array();
863 while ($category = db_fetch_object($result)) {
864 $categories[] = l($category->title, 'aggregator/categories/'. $category->cid);
865 }
866 if ($categories) {
867 $output .= '<div class="feed-item-categories">'. t('Categories') .': '. implode(', ', $categories) ."</div>\n";
868 }
869
870 $output .= "</div>\n";
871
872 return $output;
873 }
874
875 /**
876 * Safely render HTML content, as allowed.
877 */
878 function feedapi_aggregator_filter_xss($value) {
879 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));
880 }
881
882 /**
883 * Helper function for drupal_map_assoc.
884 */
885 function _feedapi_aggregator_items($count) {
886 return format_plural($count, '1 item', '@count items');
887 }

  ViewVC Help
Powered by ViewVC 1.1.2