/[drupal]/contributions/modules/simplefeed/simplefeed.module
ViewVC logotype

Contents of /contributions/modules/simplefeed/simplefeed.module

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


Revision 1.46 - (show annotations) (download) (as text)
Fri Feb 8 00:48:16 2008 UTC (21 months, 2 weeks ago) by csevb10
Branch: MAIN
CVS Tags: DRUPAL-6--1-0, HEAD
Branch point for: DRUPAL-6--1
Changes since 1.45: +5 -5 lines
File MIME type: text/x-php
Purely aesthetic modifications to fix breaks with standard Drupal coding practice.
1 <?php
2 // $Id: simplefeed.module,v 1.45 2008/02/05 01:54:03 csevb10 Exp $
3
4 /**
5 * @file
6 * Parse feeds into nodes.
7 */
8
9 /**
10 * Implementation of hook_init().
11 */
12 function simplefeed_init() {
13 // ensure we are not serving a cached page
14 if (function_exists('drupal_set_content')) {
15 // we don't do this in hook_menu to ensure the files are already included when
16 // views_menu is executed
17 if (module_exists('views')) {
18 include_once('./'. drupal_get_path('module', 'simplefeed') .'/simplefeed_views.inc');
19 }
20 if (module_exists('token')) {
21 include_once('./'. drupal_get_path('module', 'simplefeed') .'/simplefeed_token.inc');
22 }
23 }
24 }
25
26
27 /**
28 * Implementation of hook_menu().
29 */
30 function simplefeed_menu($may_cache) {
31 $items = array();
32
33 if ($may_cache) {
34 $items[] = array(
35 'path' => 'admin/settings/simplefeed',
36 'title' => t('SimpleFeed'),
37 'description' => t('Configure how feeds are parsed.'),
38 'callback' => 'drupal_get_form',
39 'callback arguments' => array('simplefeed_admin_settings'),
40 'access' => user_access('administer feeds'),
41 );
42 }
43 else {
44 $items[] = array(
45 'path' => 'feed/refresh/'. arg(2),
46 'title' => t('Refresh feed'),
47 'callback' => 'simplefeed_feed_refresh',
48 'callback arguments' => array(arg(2)),
49 'access' => user_access('administer feeds'),
50 'type' => MENU_CALLBACK
51 );
52
53 $items[] = array(
54 'path' => 'feed/empty/'. arg(2),
55 'title' => t('Empty feed'),
56 'callback' => 'simplefeed_feed_empty',
57 'callback arguments' => array(arg(2)),
58 'access' => user_access('administer feeds'),
59 'type' => MENU_CALLBACK
60 );
61 }
62
63 return $items;
64 }
65
66
67 /**
68 * Implementation of hook_perm().
69 */
70 function simplefeed_perm() {
71 return array('create feeds', 'edit own feeds', 'edit feeds', 'administer feeds');
72 }
73
74
75 /**
76 * Implementation of hook_access().
77 */
78 function simplefeed_access($op, $node) {
79 global $user;
80
81 switch ($op) {
82 case 'create':
83 return user_access('create feeds');
84 break;
85
86 case 'update':
87 case 'delete':
88 // users who create a feed may edit or delete it later, assuming they have the necessary permissions
89 if ((user_access('edit own feeds') && ($user->uid == $node->uid)) || user_access('edit feeds')) {
90 return TRUE;
91 }
92 break;
93 }
94 }
95
96
97 /**
98 * Implementation of hook_node_info().
99 */
100 function simplefeed_node_info() {
101 return array(
102 'feed' => array(
103 'name' => t('Feed'),
104 'module' => 'simplefeed',
105 'description' => t('Aggregate an RSS or Atom syndication feed.'),
106 'body_label' => t('Description'),
107 ),
108 );
109 }
110
111
112 /**
113 * Implementation of hook_form().
114 */
115 function simplefeed_form(&$node) {
116 $type = node_get_types('type', $node);
117
118 $form['title'] = array(
119 '#type' => 'textfield',
120 '#title' => check_plain($type->title_label),
121 '#required' => TRUE,
122 '#default_value' => $node->title,
123 '#weight' => -5,
124 );
125 $form['body_filter']['body'] = array(
126 '#type' => 'textarea',
127 '#title' => check_plain($type->body_label),
128 '#default_value' => $node->body,
129 '#rows' => 3,
130 '#required' => TRUE,
131 );
132 $form['body_filter']['format'] = filter_form($node->format);
133
134 $form['url'] = array(
135 '#type' => 'textfield',
136 '#title' => t('URL'),
137 '#description' => t('The URL for this feed.'),
138 '#default_value' => isset($node->url) ? $node->url : 'http://www.',
139 '#maxlength' => 255,
140 '#required' => TRUE,
141 );
142
143
144 if (user_access('administer feeds')) {
145 $form['feed_settings'] = array(
146 '#type' => 'fieldset',
147 '#title' => t('Feed settings'),
148 '#collapsible' => TRUE,
149 '#collapsed' => TRUE,
150 );
151
152 $period = array(0 => t('Never')) + drupal_map_assoc(array(3600, 10800, 21600, 32400, 43200, 86400, 172800, 259200, 604800, 1209600, 2419200, 4838400, 9676800), 'format_interval');
153 $form['feed_settings']['expires'] = array(
154 '#type' => 'select',
155 '#title' => t('Discard feed items older than'),
156 '#default_value' => isset($node->expires) ? $node->expires : variable_get('simplefeed_expires', 86400),
157 '#options' => $period,
158 '#description' => t('Older feed items will be automatically discarded. Requires !cron to be running.', array('!cron' => l('cron', 'admin/logs/status'))),
159 );
160
161 $period = array(0 => t('Never'), 1 => t('Manual')) + drupal_map_assoc(array(900, 1800, 3600, 7200, 10800, 21600, 32400, 43200, 64800, 86400, 172800, 259200, 604800), 'format_interval');
162 $form['feed_settings']['refresh'] = array(
163 '#type' => 'select',
164 '#title' => t('Check feed every'),
165 '#description' => t('How often should a feed be checked for new content? If you select "manual", you can update a feed by clicking "refresh this feed" below the feed node. If you select a time, !cron must be running.', array('!cron' => l('cron', 'admin/logs/status'))),
166 '#options' => $period,
167 '#default_value' => isset($node->refresh) ? $node->refresh : variable_get('simplefeed_refresh', 3600),
168 );
169 }
170 else {
171 $form['feed_settings']['expires'] = array('#type' => 'value', '#value' => variable_get('simplefeed_expires', 86400));
172 $form['feed_settings']['refresh'] = array('#type' => 'value', '#value' => variable_get('simplefeed_refresh', 3600));
173 }
174
175 return $form;
176 }
177
178
179 /**
180 * Implementation of hook_validate().
181 */
182 function simplefeed_validate($node) {
183 $valid_url = "`(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?`";
184 $url = trim($node->url);
185
186 if (preg_match($valid_url, $url)) {
187 $duplicate = db_result(db_query("SELECT nid FROM {simplefeed_feed} WHERE url = '%s'", $node->url));
188
189 // make sure this URL does not exist already
190 // if this node is being updated make sure it doesn't trigger a duplicate warning
191 if ($duplicate != '' && $duplicate != $node->nid) {
192 form_set_error('url', t('The URL entered already exists. Please try a different URL.'));
193 }
194 }
195 else {
196 form_set_error('url', t('The URL entered is not valid. It should be in the format of: %url_format', array('%url_format' => 'http://www.example.com/rss.xml')));
197 }
198 }
199
200
201 /**
202 * Implementation of hook_load().
203 */
204 function simplefeed_load($node) {
205 $additions = db_fetch_object(db_query('SELECT url, expires, refresh, checked FROM {simplefeed_feed} WHERE vid = %d', $node->vid));
206 return $additions;
207 }
208
209
210 /**
211 * Implementation of hook_insert().
212 */
213 function simplefeed_insert($node) {
214 db_query("INSERT INTO {simplefeed_feed} (vid, nid, url, expires, refresh) VALUES (%d, %d, '%s', %d, %d)", $node->vid, $node->nid, $node->url, $node->expires, $node->refresh);
215 }
216
217
218 /**
219 * Implementation of hook_update().
220 */
221 function simplefeed_update($node) {
222 // if this is a new node or we're adding a new revision
223 if ($node->revision) {
224 simplefeed_insert($node);
225 }
226 else {
227 db_query("UPDATE {simplefeed_feed} SET url = '%s', expires = %d, refresh = %d WHERE vid = %d", $node->url, $node->expires, $node->refresh, $node->vid);
228 }
229 }
230
231
232 /**
233 * Implementation of hook_delete().
234 */
235 function simplefeed_delete($node) {
236 // delete the feed
237 db_query('DELETE FROM {simplefeed_feed} WHERE nid = %d', $node->nid);
238
239 // delete all feed items associated with this feed
240 $feed_items = db_query('SELECT nid FROM {simplefeed_feed_item} WHERE fid = %d', $node->nid);
241
242 while ($feed_item = db_fetch_object($feed_items)) {
243 node_delete($feed_item->nid);
244 }
245
246 db_query('DELETE FROM {simplefeed_feed_item} WHERE fid = %d', $node->nid);
247 }
248
249
250 /**
251 * Implementation of hook_nodeapi().
252 */
253
254 function simplefeed_nodeapi(&$node, $op, $teaser, $page) {
255 switch ($op) {
256 case 'delete revision':
257 db_query('DELETE FROM {simplefeed_feed} WHERE vid = %d', $node->vid);
258 break;
259 }
260 }
261
262
263 /**
264 * Implementation of hook_view().
265 */
266 function simplefeed_view($node, $teaser = FALSE, $page = FALSE) {
267 $node = node_prepare($node, $teaser);
268
269 $node->content['simplefeed']['#theme'] = 'simplefeed_node_view';
270 $node->content['simplefeed']['#weight'] = 2;
271 $node->content['simplefeed']['url'] = array(
272 '#value' => check_url($node->url),
273 '#weight' => 1,
274 );
275
276 // since only administrators can edit when a feed expires, only admins can see what this value is
277 if (user_access('administer feeds')) {
278 $node->content['simplefeed']['expires'] = array(
279 '#value' => format_interval($node->expires),
280 );
281 $node->content['simplefeed']['refresh'] = array(
282 '#value' => format_interval($node->refresh),
283 );
284 $node->content['simplefeed']['checked'] = array(
285 '#value' => $node->checked > 0 ? format_interval(time() - $node->checked) . t(' ago') : t('Never'),
286 );
287 }
288
289 return $node;
290 }
291
292
293 /**
294 * Theme the display of a feed node.
295 */
296 function theme_simplefeed_node_view($values) {
297 // needs to be a space so default values aren't outputed
298 $output = ' ';
299
300 // since only administrators can edit when a feed expires, only admins can see what this value is
301 if (user_access('administer feeds')) {
302 drupal_add_css(drupal_get_path('module', 'simplefeed') .'/simplefeed.css');
303
304 $output .= '<ul class="simplefeed">';
305 $output .= '<li><strong>Expires:</strong> '. $values['expires']['#value'] .'</li>';
306 $output .= '<li><strong>Refresh:</strong> '. $values['refresh']['#value'] .'</li>';
307 $output .= '<li><strong>Checked:</strong> '. $values['checked']['#value'] .'</li>';
308 $output .= '</ul>';
309 }
310
311 return $output;
312 }
313
314
315 /**
316 * Implementation of hook_link().
317 */
318 function simplefeed_link($type, $node = NULL, $teaser = FALSE) {
319 $links = array();
320
321 if ($type == 'node' && $node->type == 'feed') {
322 if (module_exists('views')) {
323 $links['simplefeed_view_feed_items'] = array(
324 'title' => t('View feed items'),
325 'href' => 'feeds/'. $node->nid,
326 'attributes' => array('title' => t('View feed items')),
327 );
328 }
329 // only admins can force a feed to update or delete all feed items
330 if (user_access('administer feeds')) {
331 // don't show if the feed is marked not to refresh, since the link won't do anything in this case
332 if ($node->refresh > 0) {
333 $links['simplefeed_refresh'] = array(
334 'title' => t('Refresh this feed'),
335 'href' => 'feed/refresh/'. $node->vid,
336 );
337 }
338 $links['simplefeed_delete'] = array(
339 'title' => t('Empty this feed'),
340 'href' => 'feed/empty/'. $node->nid,
341 'attributes' => array("title" => "Delete all feed items associated with this feed")
342 );
343 }
344 }
345
346 return $links;
347 }
348
349
350 /**
351 * Administrative settings.
352 */
353 function simplefeed_admin_settings() {
354 $form['feed_settings'] = array(
355 '#type' => 'fieldset',
356 '#title' => t('Default feed settings'),
357 '#description' => t('Default settings for each feed that is added. Note, that some of these settings can be overriden by editing any specific feed node.'),
358 '#collapsible' => TRUE,
359 '#access' => user_access('administer feeds'),
360 );
361
362 $period = array(0 => t('Never')) + drupal_map_assoc(array(3600, 10800, 21600, 32400, 43200, 86400, 172800, 259200, 604800, 1209600, 2419200, 4838400, 9676800), 'format_interval');
363 $form['feed_settings']['simplefeed_expires'] = array(
364 '#type' => 'select',
365 '#title' => t('Discard feed items older than'),
366 '#default_value' => variable_get('simplefeed_expires', 86400),
367 '#options' => $period,
368 '#description' => t('Older feed items will be automatically discarded. Requires !cron to be running.', array('!cron' => l('cron', 'admin/logs/status'))),
369 );
370
371 $period = array(0 => t('Never'), 1 => t('Manual')) + drupal_map_assoc(array(900, 1800, 3600, 7200, 10800, 21600, 32400, 43200, 64800, 86400, 172800, 259200, 604800), 'format_interval');
372 $form['feed_settings']['simplefeed_refresh'] = array(
373 '#type' => 'select',
374 '#title' => t('Check feeds every'),
375 '#description' => t('How often should a feed be checked for new content? If you select "manual", you can update a feed by clicking "refresh this feed" below the feed node. If you select a time, !cron must be running.', array('!cron' => l('cron', 'admin/logs/status'))),
376 '#options' => $period,
377 '#default_value' => variable_get('simplefeed_refresh', 3600),
378 );
379
380 foreach (filter_formats() as $id => $format) {
381 $formats[$id] = $format->name;
382 }
383 $form['simplefeed_format'] = array(
384 '#type' => 'select',
385 '#title' => t('Default input format'),
386 '#description' => t('The default input format for feeds (e.g., which HTML tags to not strip out).'),
387 '#options' => $formats,
388 '#default_value' => variable_get('simplefeed_format', 1),
389 );
390
391 // only if taxonomy is enabled will these options work
392 if (module_exists('taxonomy')) {
393 $vocabularies = taxonomy_get_vocabularies();
394 $vocabularies_list = array(0 => 'None');
395 foreach ($vocabularies as $vocabulary) {
396 $vocabularies_list[$vocabulary->vid] = check_plain($vocabulary->name);
397 }
398
399 $form['simplefeed_vocab'] = array(
400 '#type' => 'select',
401 '#title' => t('Vocabulary'),
402 '#description' => t("Select a vocabulary that both feeds and feed items share to have feed items automatically inherit their feed parent's terms when created. Select none to disable this feature."),
403 '#options' => $vocabularies_list,
404 '#default_value' => variable_get('simplefeed_vocab', 0),
405 );
406 $form['#validate']['simplefeed_settings_validate'] = array();
407
408 $form['simplefeed_categories'] = array(
409 '#type' => 'checkbox',
410 '#title' => t('Automatically add categories set by external feeds to the vocabulary above.'),
411 '#description' => t('This is useful when external feeds provide their own categories. These categories can then be merged into the main vocabulary defined above.'),
412 '#default_value' => variable_get('simplefeed_categories', 0),
413 );
414 }
415
416 $throttle = drupal_map_assoc(array(5, 10, 15, 20, 25, 50, 100, 'unlimited'));
417 $form['simplefeed_cron_throttle'] = array(
418 '#type' => 'select',
419 '#title' => t('Cron throttle'),
420 '#description' => t('The number of feeds to parse each cron run.'),
421 '#options' => $throttle,
422 '#default_value' => variable_get('simplefeed_cron_throttle', 50),
423 );
424
425 return system_settings_form($form);
426 }
427
428
429 /**
430 * Validate the SimpleFeed settings.
431 */
432 function simplefeed_settings_validate($form_id, $form_values) {
433 // ensure that the vocab selected has been selected to be used by both feed and feed item node types
434 if ($form_values['simplefeed_vocab']) {
435 $check = array();
436 $types = db_query('SELECT type FROM {vocabulary_node_types} WHERE vid = %d', $form_values['simplefeed_vocab']);
437 $tags = db_result(db_query('SELECT tags FROM {vocabulary} WHERE vid = %d', $form_values['simplefeed_vocab']));
438 while ($type = db_fetch_object($types)) {
439 $check[] = $type->type;
440 }
441
442 if (!$tags) {
443 form_set_error('simplefeed_vocab', t('Automatic term inheritance from parent feed to child feed items requires that this vocabulary has <em>free tagging</em> enabled.'));
444 }
445
446 if (!in_array('feed', $check) || !in_array('feed_item', $check)) {
447 form_set_error('simplefeed_vocab', t('If you want to use this vocabulary, you need to associate it with both the <em>feed</em> and <em>feed item</em> node types.'));
448 }
449 }
450
451 // you can't add categories to a non-existent vocab
452 if ($form_values['simplefeed_categories'] && !$form_values['simplefeed_vocab']) {
453 form_set_error('simplefeed_categories', t('You must specify a valid vocabulary if you want to automatically add categories.'));
454 }
455 }
456
457
458 /**
459 * Implementation of hook_cron().
460 */
461 function simplefeed_cron() {
462 simplefeed_feed_refresh();
463 simplefeed_item_feed_expire();
464 }
465
466
467 /**
468 * Refresh a feed, downloading any new feed items and creating nodes for them.
469 */
470 function simplefeed_feed_refresh($nid = NULL) {
471 // figure out which feeds to update
472 if (is_numeric($nid)) {
473 // refresh a specific feed
474 // since a $vid is specified, we are looking at a specific feed node so we allow either "manual" or anytime option to be a valid value to push a refresh
475 $process_feeds = db_query('SELECT n.nid FROM {node} n INNER JOIN {simplefeed_feed} s ON s.vid = n.vid WHERE n.nid = %d AND n.status = 1 AND s.refresh > 0', $nid);
476 }
477 else {
478 // refresh all feeds
479 $limit = variable_get('simplefeed_cron_throttle', 50);
480 // limit in ASC order so we grab the top N that haven't been checked recently
481 // we can only update feed nodes that have a valid refresh time set; ignore "never" and "manual" options
482 $query = 'SELECT s.nid FROM {simplefeed_feed} s INNER JOIN {node} n ON s.vid = n.vid WHERE ((%d - s.checked) >= s.refresh) AND n.status = 1 AND s.refresh > 1 ORDER BY s.checked ASC';
483 if ($limit != 'unlimited') {
484 $process_feeds = db_query_range($query, time(), 0, $limit);
485 }
486 else {
487 $process_feeds = db_query($query, time());
488 }
489 }
490
491 // build cache path
492 $cache_path = file_create_path('cache_simplefeed');
493 file_check_directory($cache_path, FILE_CREATE_DIRECTORY);
494
495 // process each feed
496 while ($process_feed = db_fetch_object($process_feeds)) {
497 $process_feed = node_load($process_feed->nid);
498 // above we define hook_view() which then performs check_url() on the $url in the feed node
499 // the problem is check_url() calls filter_xss_bad_protocol() which does it thing to prevent XSS
500 // but it returns the string through check_plain() which calls htmlspecialchars()
501 // this converts & in a url to &amp; and then causes SimplePie not to be able to parse it
502 // because of this, we decode this URL since we are passing it directly to SimplePie
503 // it is still encoded everywhere else it is output to prevent XSS
504 $process_feed->url = htmlspecialchars_decode($process_feed->url, ENT_QUOTES);
505 $process_feed->tags = '';
506 $process_feed->vocab = 0;
507
508 // gather tags for feed to pass to feed item children
509 if ($process_feed->vocab = variable_get('simplefeed_vocab', 0)) {
510 $terms = taxonomy_node_get_terms_by_vocabulary($process_feed->nid, $process_feed->vocab);
511 $taxonomy = array();
512 if (!empty($terms)) {
513 foreach ($terms as $term) {
514 $taxonomy[] = $term->name;
515 }
516 }
517 $process_feed->tags = implode($taxonomy, ', ');
518 }
519
520 // turn each feed item into a node
521 simplefeed_item_feed_parse($process_feed, $cache_path);
522
523 // finished processing this feed so we can mark it checked
524 db_query('UPDATE {simplefeed_feed} SET checked = %d WHERE vid = %d', time(), $process_feed->vid);
525 }
526
527 // if you're manually refreshing a feed, redirect back to the feed node
528 if ($nid) {
529 drupal_set_message(t('Feed refreshed.'));
530 drupal_goto('node/'. $nid);
531 }
532 }
533
534
535 /**
536 * Delete all feed items associated with a feed.
537 */
538 function simplefeed_feed_empty($nid) {
539 if (is_numeric($nid)) {
540 $nodes = db_query("SELECT nid FROM {simplefeed_feed_item} WHERE fid = %d", $nid);
541 while ($node = db_fetch_object($nodes)) {
542 node_delete($node->nid);
543 }
544 drupal_set_message(t('Feed emptied.'));
545 drupal_goto('node/'. $nid);
546 }
547 else {
548 drupal_not_found();
549 exit;
550 }
551 }
552
553 /**
554 * Implementation of hook_requirements().
555 */
556 function simplefeed_requirements($phase) {
557 $requirements = array();
558 // Ensure translations don't break at install time.
559 $t = get_t();
560
561 if ($phase == 'runtime') {
562 $path = drupal_get_path('module', 'simplefeed');
563
564 if (!file_exists($path .'/simplepie.inc')) {
565 $requirements['simplepie'] = array(
566 'title' => $t('SimpleFeed'),
567 'value' => $t('SimplePie library missing'),
568 'severity' => REQUIREMENT_ERROR,
569 'description' => $t('SimpleFeed requires the <a href="http://simplepie.org/">SimplePie</a> library to properly parse feeds. Please download either the 1.0 or 1.1 development (recommended) version and place simplepie.inc in your SimpleFeed module directory.'),
570 );
571 }
572 }
573 return $requirements;
574 }
575
576 /**
577 * This is a PHP4 compatibility function -- not needed since this is already in PHP5
578 */
579 if (!function_exists("htmlspecialchars_decode")) {
580 function htmlspecialchars_decode($string, $quote_style = ENT_COMPAT) {
581 return strtr($string, array_flip(get_html_translation_table(HTML_SPECIALCHARS, $quote_style)));
582 }
583 }

  ViewVC Help
Powered by ViewVC 1.1.2