/[drupal]/contributions/modules/feedapi/feedapi_aggregator/feedapi_aggregator.module
ViewVC logotype

Contents of /contributions/modules/feedapi/feedapi_aggregator/feedapi_aggregator.module

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


Revision 1.1 - (show annotations) (download) (as text)
Tue Jul 24 18:23:59 2007 UTC (2 years, 4 months ago) by aronnovak
Branch: MAIN
CVS Tags: DRUPAL-5--0-1, HEAD
Branch point for: DRUPAL-5
File MIME type: text/x-php
Initial version of feedapi_aggregator. This module mimics the core aggregator using FeedAPI. This module is written by alex_b (http://drupal.org/user/53995)
1 <?php
2 // $Id: feedapi_aggregator.module,v 1.7 2007/07/24 15:29:06 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 (!isset($feed->feedapi_aggregator)) {
621 $feed->feedapi_aggregator = db_fetch_array(db_query('SELECT * FROM {aggregator_feed} WHERE fid = %d', $feed->fid));
622 if (!is_array($feed->feedapi_aggregator)) {
623 $feed->feedapi_aggregator = array();
624 $feed->feedapi_aggregator['url'] = $feed->url;
625 }
626 }
627 return feedapi_aggregator_refresh($feed->feedapi_aggregator);
628 }
629
630
631 /**
632 * Implementation of hook_feedapi_compatible().
633 *
634 * @todo: Actually detect wether this parser could process this URL.
635 * @todo: Lots of parsers will be slow to go through compatible() because all of them
636 * will want to download and check a URL.
637 * @todo: Add this hook to parser requirements array in feedapi.
638 */
639 function feedapi_aggregator_feedapi_compatible($feed) {
640 return array_shift(feedapi_aggregator_feedapi_type());
641 }
642
643
644 function feedapi_aggregator_get_feed($fid) {
645 return db_fetch_array(db_query('SELECT * FROM {aggregator_feed} WHERE fid = %d', $fid));
646 }
647
648 function feedapi_aggregator_get_category($cid) {
649 return db_fetch_array(db_query('SELECT * FROM {aggregator_category} WHERE cid = %d', $cid));
650 }
651
652 function feedapi_aggregator_category_view() {
653 $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');
654
655 $output .= '<h3>'. t('Category overview') .'</h3>';
656
657 $header = array(t('Title'), t('Items'), t('Operations'));
658 $rows = array();
659 while ($category = db_fetch_object($result)) {
660 $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"));
661 }
662 $output .= theme('table', $header, $rows);
663
664 return $output;
665 }
666
667 /**
668 * Menu callback; displays the most recent items gathered from any feed.
669 */
670 function feedapi_aggregator_page_last() {
671 drupal_add_feed(url('aggregator/rss'), variable_get('site_name', 'Drupal') .' '. t('aggregator'));
672
673 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));
674 }
675
676 /**
677 * Menu callback; displays all the items captured from a particular feed.
678 */
679 function feedapi_aggregator_page_source() {
680 $feed = db_fetch_object(db_query('SELECT * FROM {aggregator_feed} WHERE fid = %d', arg(2)));
681 $info = theme('aggregator_feed', $feed);
682
683 return _feedapi_aggregator_page_list('SELECT * FROM {aggregator_item} WHERE fid = '. $feed->fid .' ORDER BY timestamp DESC, iid DESC', arg(3), $info);
684 }
685
686 /**
687 * Menu callback; displays all the items aggregated in a particular category.
688 */
689 function feedapi_aggregator_page_category() {
690 $category = db_fetch_object(db_query('SELECT cid, title FROM {aggregator_category} WHERE cid = %d', arg(2)));
691
692 drupal_add_feed(url('aggregator/rss/'. arg(2)), variable_get('site_name', 'Drupal') .' '. t('aggregator - @title', array('@title' => $category->title)));
693
694 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));
695 }
696
697 /**
698 * FIXME
699 */
700 function feedapi_aggregator_page_list($sql, $header, $categorize) {
701 $form['#base'] = 'feedapi_aggregator_page_list';
702 $form['header'] = array('#value' => $header);
703 $result = pager_query($sql, 20);
704 $categories = array();
705 $done = FALSE;
706 $form['items'] = array();
707 $form['categories'] = array('#tree' => TRUE);
708 while ($item = db_fetch_object($result)) {
709 $form['items'][$item->iid] = array('#value' => theme('feedapi_aggregator_page_item', $item));
710 $form['categories'][$item->iid] = array();
711 if ($categorize) {
712 $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);
713 $selected = array();
714 while ($category = db_fetch_object($categories_result)) {
715 if (!$done) {
716 $categories[$category->cid] = check_plain($category->title);
717 }
718 if ($category->iid) {
719 $selected[] = $category->cid;
720 }
721 }
722 $done = TRUE;
723 $form['categories'][$item->iid] = array(
724 '#type' => variable_get('aggregator_category_selector', 'checkboxes'),
725 '#default_value' => $selected, '#options' => $categories,
726 '#size' => 10, '#multiple' => TRUE
727 );
728 }
729 }
730 $form['submit'] = array('#type' => 'submit', '#value' => t('Save categories'));
731 $form['pager'] = array('#value' => theme('pager', NULL, 20, 0));
732
733 return $form;
734 }
735
736 /**
737 * Prints an aggregator page listing a number of feed items. Various
738 * menu callbacks use this function to print their feeds.
739 */
740 function _feedapi_aggregator_page_list($sql, $op, $header = '') {
741 $categorize = (user_access('administer news feeds') && ($op == 'categorize'));
742 $form = feedapi_aggregator_page_list($sql, $header, $categorize);
743 if ($categorize) {
744 return $form;
745 }
746 else {
747 $output = '<div id="aggregator">';
748 $output .= $header;
749 foreach ($form['items'] as $item) {
750 $output .= $item['#value'];
751 }
752 $output .= '</div>';
753 $output .= $form['pager']['#value'];
754 $output .= $form['feed_icon']['#value'];
755 return $output;
756 }
757 }
758
759 /**
760 * Menu callback; displays all the feeds used by the aggregator.
761 */
762 function feedapi_aggregator_page_sources() {
763 $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');
764 $output = "<div id=\"aggregator\">\n";
765 while ($feed = db_fetch_object($result)) {
766 $output .= '<h2>'. check_plain($feed->title) ."</h2>\n";
767
768 // Most recent items:
769 $list = array();
770 if (variable_get('aggregator_summary_items', 3)) {
771 $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));
772 while ($item = db_fetch_object($items)) {
773 $list[] = theme('feedapi_aggregator_summary_item', $item);
774 }
775 }
776 $output .= theme('item_list', $list);
777
778 $link['sources'] = array(
779 'title' => t('More'),
780 'href' => 'aggregator/sources/'. $feed->fid
781 );
782
783 $output .= '<div class="links">'. theme('links', $link) ."</div>\n";
784 }
785 $output .= theme('xml_icon', url('aggregator/opml'));
786 $output .= '</div>';
787 return $output;
788 }
789
790 /**
791 * Menu callback; displays all the categories used by the aggregator.
792 */
793 function feedapi_aggregator_page_categories() {
794 $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');
795 $output = "<div id=\"aggregator\">\n";
796
797 while ($category = db_fetch_object($result)) {
798 $output .= '<h2>'. check_plain($category->title) ."</h2>\n";
799 if (variable_get('aggregator_summary_items', 3)) { // @todo: setting for this guy
800 $list = array();
801 $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));
802 while ($item = db_fetch_object($items)) {
803 $list[] = theme('feedapi_aggregator_summary_item', $item);
804 }
805 $output .= theme('item_list', $list);
806 }
807
808 $link['categories'] = array(
809 'title' => t('More'),
810 'href' => 'aggregator/categories/'. $category->cid
811 );
812
813 $output .= '<div class="links">'. theme('links', $link) ."</div>\n";
814 }
815
816 $output .= '</div>';
817
818 return $output;
819 }
820
821 /**
822 * Return a themed item heading for summary pages located at "aggregator/sources"
823 * and "aggregator/categories".
824 *
825 * @param $item The item object from the aggregator module.
826 * @return A string containing the output.
827 *
828 * @ingroup themeable
829 */
830 function theme_feedapi_aggregator_summary_item($item) {
831 $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>';
832 if ($item->feed_link) {
833 $output .= ', <span class="source"><a href="'. check_url($item->feed_link) .'">'. check_plain($item->feed_title) .'</a></span>';
834 }
835 return $output ."\n";
836 }
837
838 /**
839 * Format an individual feed item for display on the aggregator page.
840 *
841 * @ingroup themeable
842 */
843 function theme_feedapi_aggregator_page_item($item) {
844
845 $source = '';
846 if ($item->ftitle && $item->fid) {
847 $source = l($item->ftitle, "aggregator/sources/$item->fid", array('class' => 'feed-item-source')) .' -';
848 }
849
850 if (date('Ymd', $item->timestamp) == date('Ymd')) {
851 $source_date = t('%ago ago', array('%ago' => format_interval(time() - $item->timestamp)));
852 }
853 else {
854 $source_date = format_date($item->timestamp, 'custom', variable_get('date_format_medium', 'D, m/d/Y - H:i'));
855 }
856
857 $output .= "<div class=\"feed-item\">\n";
858 $output .= '<h3 class="feed-item-title"><a href="'. check_url($item->link) .'">'. check_plain($item->title) ."</a></h3>\n";
859 $output .= "<div class=\"feed-item-meta\">$source <span class=\"feed-item-date\">$source_date</span></div>\n";
860
861 if ($item->description) {
862 $output .= '<div class="feed-item-body">'. feedapi_aggregator_filter_xss($item->description) ."</div>\n";
863 }
864
865 $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);
866 $categories = array();
867 while ($category = db_fetch_object($result)) {
868 $categories[] = l($category->title, 'aggregator/categories/'. $category->cid);
869 }
870 if ($categories) {
871 $output .= '<div class="feed-item-categories">'. t('Categories') .': '. implode(', ', $categories) ."</div>\n";
872 }
873
874 $output .= "</div>\n";
875
876 return $output;
877 }
878
879 /**
880 * Safely render HTML content, as allowed.
881 */
882 function feedapi_aggregator_filter_xss($value) {
883 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));
884 }
885
886 /**
887 * Helper function for drupal_map_assoc.
888 */
889 function _feedapi_aggregator_items($count) {
890 return format_plural($count, '1 item', '@count items');
891 }

  ViewVC Help
Powered by ViewVC 1.1.2