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

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

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


Revision 1.23 - (show annotations) (download) (as text)
Tue Jul 24 16:17:40 2007 UTC (2 years, 4 months ago) by aronnovak
Branch: MAIN
CVS Tags: DRUPAL-5--0-1, HEAD
Branch point for: DRUPAL-5
Changes since 1.22: +2 -2 lines
File MIME type: text/x-php
Fix a missing processor sanitizing stage at feedapi_invoke_feedapi
1 <?php
2 // $Id: feedapi.module,v 1.22 2007/07/24 14:03:47 aronnovak Exp $
3
4 /**
5 * @file
6 * Handle the submodules (for feed and item processing)
7 * Provide a basic management of feeds
8 */
9
10 /**
11 * Implementation of hook_help().
12 */
13 function feedapi_help($section) {
14 switch ($section) {
15 case 'admin/help#feedapi':
16 return t('Provides feed management interface and handle underlying processors and parsers for any type of feeds.');
17 }
18 }
19
20 /**
21 * Implementation of hook_menu().
22 */
23 function feedapi_menu($may_cache) {
24 if ($may_cache) {
25 $items[] = array(
26 'path' => 'admin/content/feed',
27 'title' => t('Feed'),
28 'callback' => 'feedapi_management_page',
29 'access' => user_access('administer feedapi'),
30 );
31 $items[] = array(
32 'path' => 'admin/content/feed/list',
33 'title' => t('List'),
34 'type' => MENU_DEFAULT_LOCAL_TASK,
35 'weight' => -15,
36 );
37 $items[] = array(
38 'path' => 'feed/add',
39 'title' => t('Create feed'),
40 'callback' => 'drupal_get_form',
41 'callback arguments' => array('feedapi_add_page'),
42 'access' => user_access('handle own feeds') || user_access('administer feedapi'),
43 );
44 $items[] = array('path' => 'admin/settings/feedapi',
45 'title' => t('FeedAPI settings'),
46 'callback' => 'drupal_get_form',
47 'callback arguments' => array('feedapi_admin_settings'),
48 'type' => MENU_NORMAL_ITEM,
49 'access' => user_access('administer feedapi'),
50 );
51 }
52 else if (arg(0) == 'feed' && is_numeric(arg(1))) {
53 global $user;
54 $feed = new stdClass();
55 $feed->fid = arg(1);
56 feedapi_invoke_feedapi("load", $feed);
57 $own_feed = $feed->uid == $user->uid && user_access('handle own feeds') ? TRUE : FALSE;
58 $items[] = array('path' => 'feed/'. arg(1) .'/delete', 'title' => t('Delete'),
59 'callback' => 'drupal_get_form',
60 'callback arguments' => array('feedapi_delete_confirm', $feed),
61 'access' => user_access('administer feedapi') || $own_feed,
62 'type' => MENU_CALLBACK
63 );
64 $items[] = array('path' => 'feed/'. arg(1) .'/refresh', 'title' => t('Refresh'),
65 'callback' => 'feedapi_invoke_feedapi',
66 'callback arguments' => array("refresh", $feed),
67 'access' => user_access('administer feedapi') || $own_feed,
68 'type' => MENU_CALLBACK
69 );
70 $items[] = array('path' => 'feed/'. arg(1) .'/edit', 'title' => t('Edit'),
71 'callback' => 'drupal_get_form',
72 'callback arguments' => array('feedapi_edit_page', $feed),
73 'access' => user_access('administer feedapi') || $own_feed,
74 'type' => MENU_CALLBACK
75 );
76 }
77 return $items;
78 }
79
80 /**
81 * Implementation of hook_perm().
82 */
83 function feedapi_perm() {
84 return array('administer feedapi', 'handle own feeds');
85 }
86
87 /**
88 * Do various things with feed. Handle the core data and call the
89 * underyling modules (parsers/processors) too
90 *
91 * @param $op
92 * "load" Load the feed. $param can be:
93 * "no" - the feed object won't have items member
94 * "info" - only the nid, fid, fiid data members will put ito each item
95 * "full" - the whole stored structure will be fetched into items
96 * "refresh" Re-download the feed and process newly arrived item
97 * "purge" Delete the feed and all connected things (eg. items)
98 * "save" Save the feed
99 * "update" Update the feed
100 *
101 * FIXME: maybe split into private functions
102 *
103 * @param $feed
104 * A feed object. If only the ID is known, you should pass something like this: $feed->fid = X
105 * @param $param
106 * Depends on the $op value.
107 */
108 function feedapi_invoke_feedapi($op, &$feed, $param = NULL) {
109 if (!is_object($feed)) {
110 return FALSE;
111 }
112 $user = isset($feed->user) || isset($feed->uid) ? user_load(array('name' => $feed->user, 'uid' => $feed->uid)) : NULL;
113 if (!isset($user->uid) || $user->uid == 0) {
114 global $user;
115 }
116 _feedapi_sanitize_processors($feed);
117 switch ($op) {
118 case 'load':
119 $load_items = $param;
120 $feed = db_fetch_object(db_query("SELECT * FROM {feedapi} WHERE fid = %d", $feed->fid));
121 $feed->parsers = unserialize($feed->parsers);
122 $feed->processors_item = unserialize($feed->processors_item);
123 $feed->processors_feed = unserialize($feed->processors_feed);
124 _feedapi_sanitize_processors($feed);
125 $feed->options = new stdClass();
126 // Load additional elements provided by the processors
127 foreach ($feed->processors_feed as $processor) {
128 $feed = module_invoke($processor, "feedapi_load", $feed);
129 }
130 if ($load_items == "info" || $load_items == "full") {
131 $feed->items = array();
132 foreach ($feed->processors_item as $processor) {
133 $items = module_invoke($processor, "feedapi_item_fetch_items", $feed);
134 $feed->items += is_array($items) ? $items : array();
135 }
136 }
137 if ($load_items == "full") {
138 foreach ($feed->items as $key => $item) {
139 foreach ($feed->processors_item as $processor) {
140 $item = module_invoke($processor, "feedapi_item_load", $item);
141 $feed->items[$key] = $item;
142 }
143 }
144 }
145 module_invoke_all('feedapi_after_load', $feed);
146 break;
147
148 case 'refresh':
149 $cron = $param;
150 $goto = user_access('administer feedapi') ? 'admin/content/feed' : drupal_init_path();
151 feedapi_invoke_feedapi("load", $feed, "info");
152 if (!is_array($feed->processors_item) || count($feed->processors_item) == 0) {
153 if (!$cron) {
154 drupal_set_message(t("This feed (%url) doesn't have any item processor. It's not possible to refresh the feed.", array('%url' => $feed->url)), "error");
155 drupal_goto($goto);
156 }
157 return;
158 }
159 if (!is_array($feed->processors_feed) || count($feed->processors_feed) == 0) {
160 if (!$cron) {
161 drupal_set_message(t("This feed (%url) doesn't have any feed processor. It's not possible to refresh the feed.", array('%url' => $feed->url)), "error");
162 drupal_goto($goto);
163 }
164 return;
165 }
166 $processors_item = $feed->processors_item;
167 $processors_feed = $feed->processors_feed;
168 // Force the processors to delete old items and determine the max. create elements
169 $max_new_items = variable_get('feedapi_refresh_once', 10);
170 foreach ($feed->processors_feed as $processor) {
171 module_invoke($processor, 'feedapi_expire', $feed, $cron === TRUE ? FALSE : TRUE);
172 }
173 $feed = _feedapi_call_parsers($feed, $feed->parsers);
174 $feed->processors_item = $processors_item;
175 $feed->processors_feed = $processors_feed;
176 $items = array_reverse($feed->items);
177 $updated = 0;
178 $new = 0;
179 // Walk through the items
180 foreach ($items as $item) {
181 // Call each item parser
182 $is_updated = FALSE;
183 $is_new = FALSE;
184 foreach ($feed->processors_item as $processor) {
185 if (!module_invoke($processor, 'feedapi_item_unique', $item, $feed->fid)) {
186 module_invoke($processor, 'feedapi_item_update', $item, $feed->fid);
187 $is_updated = TRUE;
188 }
189 else {
190 module_invoke($processor, 'feedapi_item_save', $item, $feed->fid);
191 $is_new = TRUE;
192 }
193 }
194 $new = $is_new ? $new + 1 : $new;
195 $updated = ($is_updated && !$is_new) ? $updated + 1 : $updated;
196 // If we consumed the max new item limit -> stop
197 if ($max_new_items == $new) {
198 break;
199 }
200 }
201 db_query("UPDATE {feedapi} SET checked = %d WHERE fid = %d", time(), $feed->fid);
202 module_invoke_all('feedapi_after_refresh', $feed);
203 if (!$cron) {
204 drupal_set_message(t("%new feed item(s) are created and %updated feed items are updated.", array("%new" => $new, "%updated" => $updated)));
205 drupal_goto($goto);
206 }
207 break;
208
209 case 'purge':
210 feedapi_invoke_feedapi("load", $feed, "info");
211 // Delete items from the processors
212 foreach ($feed->items as $item) {
213 foreach($feed->processors_item as $processor) {
214 module_invoke($processor, 'feedapi_item_delete', $item);
215 }
216 }
217 // Delete feed from the processors
218 foreach($feed->processors_feed as $processor) {
219 module_invoke($processor, 'feedapi_delete', $feed);
220 }
221 // Delete core data
222 db_query("DELETE FROM {feedapi} WHERE fid = %d", $feed->fid);
223 module_invoke_all('feedapi_after_purge', $feed);
224 drupal_goto("admin/content/feed");
225 break;
226
227 case 'save':
228 db_query("INSERT INTO {feedapi} (
229 url, feed_type, processors_item, processors_feed,
230 parsers, refresh, checked, uid) VALUES
231 ('%s', '%s', '%s', '%s', '%s', %d, %d, %d)",
232 $feed->url, $feed->feed_type, serialize($feed->processors_item),
233 serialize($feed->processors_feed), serialize($feed->parsers),
234 $feed->refresh, 0, // Means that the feed have never been refreshed yet
235 $user->uid
236 );
237 $feed->fid = db_result(db_query("SELECT fid FROM {feedapi} WHERE url = '%s'", $feed->url));
238 // Parse the feed with the parsers
239 $title = $feed->title;
240 $description = $feed->description;
241 $feed = _feedapi_call_parsers($feed, $feed->parsers);
242 // If the user provided a title and a description, drop the parsers' extracted
243 $feed->title = !empty($title) ? $title : $feed->title;
244 $feed->description = !empty($description) ? $description : $feed->description;
245 foreach ($feed->processors_feed as $processor) {
246 $feed = module_invoke($processor, 'feedapi_save', $feed);
247 }
248 module_invoke_all('feedapi_after_save', $feed);
249 drupal_set_message(t('The %feed feed (%url) was saved successfully.', array('%feed' => $feed->title, '%url' => $feed->url)));
250 // Tell the user if the feed is currently not usable
251 if (strlen($feed->feed_type) < 1
252 || !module_exists($feed->parsers['primary'])
253 || !module_exists($feed->processors_feed[0])
254 || !module_exists($feed->processors_item[0])) {
255 drupal_set_message(t('The feed is not usable now, because there is no suitable processors and parser in the system'));
256 }
257 break;
258
259 case 'update':
260 // Store the common things
261 db_query("UPDATE {feedapi} SET
262 feed_type = '%s', url = '%s', processors_item = '%s',
263 processors_feed = '%s', parsers = '%s', refresh = %d,
264 checked = %d, uid = %d WHERE fid = %d",
265 $feed->feed_type, $feed->url, serialize($feed->processors_item),
266 serialize($feed->processors_feed), serialize($feed->parsers), $feed->refresh,
267 $feed->checked, $user->uid, $feed->fid
268 );
269 // Parse the feed with the parsers
270 $title = $feed->title;
271 $description = $feed->description;
272 $feed = _feedapi_call_parsers($feed, $feed->parsers);
273 // If the user provided a title and a description, drop the parsers' extracted
274 $feed->title = !empty($title) ? $title : $feed->title;
275 $feed->description = !empty($description) ? $description : $feed->description;
276 // Call the enabled feed processors
277 foreach ($feed->processors_feed as $processor) {
278 // Check if the feed processor has this feed or not
279 $feed_try_load = new stdClass();
280 $feed_try_load->options = new stdClass();
281 $feed_try_load->fid = $feed->fid;
282 if (module_invoke($processor, 'feedapi_load', $feed_try_load) === FALSE) {
283 $feed = module_invoke($processor, 'feedapi_save', $feed);
284 }
285 else {
286 $feed = module_invoke($processor, 'feedapi_update', $feed);
287 }
288 }
289 module_invoke_all('feedapi_after_update', $feed);
290 drupal_set_message(t('The %feed feed (%url) was updated successfully.', array('%feed' => $feed->title, '%url' => $feed->url)));
291 break;
292 }
293 }
294
295 /**
296 * Implementation of hook_cron().
297 */
298 function feedapi_cron() {
299 $result = db_query_range("SELECT fid FROM {feedapi} ORDER BY checked ASC", 0, variable_get('feedapi_cron_max', 5));
300 while ($fid = db_result($result, $row++)) {
301 $feed = new stdClass();
302 $feed->fid = $fid;
303 feedapi_invoke_feedapi('load', $feed);
304 feedapi_invoke_feedapi('refresh', $feed, TRUE);
305 }
306 }
307
308 /**
309 * Menu callback -- ask for confirmation of feed deletion
310 */
311 function feedapi_delete_confirm($feed) {
312 $form['fid'] = array('#type' => 'value', '#value' => $feed->fid);
313 return confirm_form($form,
314 t('Are you sure you want to delete %title?', array('%title' => $feed->title)),
315 'admin/content/feed',
316 t('This action cannot be undone.'),
317 t('Delete'), t('Cancel'));
318 }
319
320 /**
321 * Execute feed deletion
322 */
323 function feedapi_delete_confirm_submit($form_id, $form_values) {
324 if ($form_values['confirm']) {
325 $feed = new stdClass();
326 $feed->fid = $form_values['fid'];
327 feedapi_invoke_feedapi("purge", $feed);
328 }
329 }
330
331 /**
332 * Provide a UI for overviewing the existing feeds
333 */
334 function feedapi_management_page() {
335 $header = array(t('URL'),
336 t('Refresh interval'),
337 t('Last refresh'),
338 t('Title'),
339 t('Commands')
340 );
341 $rows = array();
342 $result = db_query("SELECT fid from {feedapi}");
343 while ($fid = db_result($result, $row++)) {
344 $commands = array(l(t('Delete'), 'feed/'. $fid . '/delete'),
345 l(t('Refresh'), 'feed/'. $fid . '/refresh'),
346 l(t('Edit'), 'feed/'. $fid . '/edit'),
347 );
348 $ext_commands = module_invoke_all('feedapi_edit_option', $fid);
349 if (count($ext_commands) > 0) {
350 foreach ($ext_commands as $command) {
351 $commands[] = l($command['name'], $command['link']);
352 }
353 }
354 $feed = new stdClass();
355 $feed->fid = $fid;
356 feedapi_invoke_feedapi("load", $feed);
357 $rows[] = array($feed->url,
358 format_interval($feed->refresh),
359 $feed->checked == 0 ? t('Never') : format_interval(time() - $feed->checked) ." ". t("ago"),
360 $feed->title,
361 theme_item_list($commands)
362 );
363 }
364 return theme_table($header, $rows);
365 }
366
367 /**
368 * Provide UI for edit a feed
369 */
370 function feedapi_edit_page($feed) {
371 $form = _feedapi_common_form($feed);
372 foreach (array('fid', 'feed_type', 'checked') as $value) {
373 $form[$value] = array('#type' => 'hidden', '#value' => $feed->{$value});
374 }
375 // Select from the parsers and the processors
376 $all = strlen($feed->feed_type) < 1 ? TRUE : FALSE;
377 $form['processors_feed'] = array('#type' => 'checkboxes',
378 '#options' => _feedapi_suitable_feed_processors($feed->feed_type, $all),
379 '#default_value' => $feed->processors_feed,
380 '#title' => t('Feed processor'),
381 );
382 $form['processors_item'] = array('#type' => 'checkboxes',
383 '#options' => _feedapi_suitable_item_processors($feed->feed_type, $all),
384 '#default_value' => $feed->processors_item,
385 '#title' => t('Feed item processor'),
386 );
387 $parsers = _feedapi_suitable_parsers($feed->feed_type, $all);
388 $form['primary'] = array('#type' => 'radios',
389 '#title' => t('Primary parser'),
390 '#options' => $parsers,
391 '#default_value' => $feed->parsers['primary'],
392 '#required' => TRUE,
393 );
394 $form['secondary'] = array('#type' => 'checkboxes',
395 '#title' => t('Secondary parsers'),
396 '#options' => $parsers,
397 '#default_value' => $feed->parsers['secondary'],
398 );
399 $form['processors_settings'] = array('#type' => 'fieldset',
400 '#title' => t('The currently enabled processors\' settings'),
401 '#collapsible' => TRUE,
402 '#collapsed' => FALSE,
403 );
404 $form['submit'] = array('#type' => 'submit', '#value' => t('Submit'));
405 return $form;
406 }
407
408 /**
409 * Validate as the submission
410 */
411 function feedapi_edit_page_validate($form_id, $form_values) {
412 $feed = new stdClass();
413 $feed->url = $form_values['url'];
414 foreach ($form_values['processors_feed'] as $processor) {
415 $feed = module_invoke($processor, 'feedapi_validate', $form_id, $form_values, $feed);
416 }
417 // Check if the selected parser-processor configuration has a common type
418 $type = module_invoke($form_values['primary'], 'feedapi_type');
419 $type = is_array($type) ? $type : array();
420 foreach ($form_values['processors_item'] as $processor => $turned) {
421 if ($turned && !empty($processor)) {
422 $type = array_intersect($type, module_invoke($processor, 'feedapi_type'));
423 }
424 }
425 foreach ($form_values['processors_feed'] as $processor => $turned) {
426 if ($turned && !empty($processor)) {
427 $type = array_intersect($type, module_invoke($processor, 'feedapi_type'));
428 }
429 }
430 if (count($type) == 0) {
431 form_set_error('feedapi', t('You have selected an invalid parser-processor configuration.'), 'error');
432 }
433 }
434
435 /**
436 * Construct parsers and processors data structures and call update
437 */
438 function feedapi_edit_page_submit($form_id, $form_values) {
439 $feed = new stdClass();
440 $feed->parsers = array();
441 $feed->parsers['primary'] = $form_values['primary'];
442 $feed->parsers['secondary'] = array();
443 foreach ($form_values['secondary'] as $parser => $turned) {
444 if ($turned) {
445 $feed->parsers['secondary'][] = $parser;
446 }
447 }
448 if (strlen($form_values['feed_type']) < 1) {
449 // Check the feed against the main parser to get the type
450 $form_values['feed_type'] = module_invoke($feed->parsers['primary'], 'feed_compatible', $form_values['url']);
451 }
452 $feed->processors_item = array_values($form_values['processors_item']);
453 $feed->processors_feed = array_values($form_values['processors_feed']);
454 $feed = (object) array_merge($form_values, (array) $feed);
455 feedapi_invoke_feedapi("update", $feed);
456 drupal_goto('admin/content/feed');
457 }
458
459 /**
460 * Provide a UI for create a feed
461 */
462 function feedapi_add_page() {
463 $form = _feedapi_common_form();
464 $form['advanced'] = array('#type' => 'checkbox',
465 '#title' => t('Show me the advanced parser/processor configuration'),
466 '#default_value' => FALSE,
467 '#description' => t('After the feed will be created, you will be able to edit the assigned parsers and processors.'),
468 );
469 $form['submit'] = array('#type' => 'submit', '#value' => t('Submit'));
470 return $form;
471 }
472
473 /**
474 * Let the feed processor allow or reject the given URL
475 */
476 function feedapi_add_page_validate($form_id, $form_values) {
477 $feed = new stdClass();
478 $feed->url = $form_values['url'];
479 $assigned = _feedapi_assign_parser_processor($feed->url);
480 $feed = module_invoke($assigned['processor_feed'], 'feedapi_validate', $form_id, $form_values, $feed);
481 if (db_result(db_query("SELECT fid FROM {feedapi} WHERE url = '%s'", $feed->url))) {
482 form_set_error('feedapi', t('The feed URL provided already exists. Please use a different URL.'));
483 }
484 }
485
486 /**
487 * Create the feed and invoke the the assigned processor
488 */
489 function feedapi_add_page_submit($form_id, $form_values) {
490 // Assign one parser and one processor to the URL
491 $assigned = _feedapi_assign_parser_processor($form_values['url']);
492 $feed = new stdClass();
493 $feed = (object) array_merge($form_values, (array) $feed);
494 $feed->parsers = array();
495 // We store it in such structures because the user will be able to assign more parsers and processors to one feed later
496 $feed->parsers['primary'] = $assigned['parser'];
497 $feed->parsers['secondary'] = array();
498 $feed->feed_type = $assigned['type'];
499 $feed->processors_item = array($assigned['processor_item']);
500 $feed->processors_feed = array($assigned['processor_feed']);
501 // Store the common things
502 feedapi_invoke_feedapi('save', $feed);
503 drupal_goto(($form_values['advanced'] == TRUE) ? 'feed/'. $feed->fid. '/edit' : drupal_init_path());
504 }
505
506 /**
507 * Settings:
508 * Allowed HTML tags, number of feeds refreshed in one round
509 */
510 function feedapi_admin_settings() {
511 $form['feedapi_allowed_html_tags'] = array(
512 '#type' => 'textfield', '#title' => t('Allowed HTML tags'), '#size' => 80, '#maxlength' => 255,
513 '#default_value' => variable_get('feedapi_allowed_html_tags', '<a> <b> <br> <dd> <dl> <dt> <em> <i> <li> <ol> <p> <strong> <u> <ul>'),
514 '#description' => t('The list of tags which are allowed in feeds, i.e., which will not be removed by Drupal.')
515 );
516 $form['feedapi_cron_max'] = array(
517 '#type' => 'textfield', '#title' => t('Per-cron feeds refresh'), '#size' => 3, '#maxlength' => 10,
518 '#default_value' => variable_get('feedapi_cron_max', 5),
519 '#description' => t('How much feed should be refreshed in one cron run maximally')
520 );
521
522 $form['feedapi_refresh_once'] = array(
523 '#type' => 'textfield', '#title' => t('Maximal number of items updated/created in one refresh round'), '#size' => 5, '#maxlength' => 7,
524 '#default_value' => variable_get('feedapi_refresh_once', 10),
525 );
526 $form['advanced'] = array('#type' => 'fieldset',
527 '#title' => t('Default parser and processor configuration'),
528 '#description' => t('These will be used first when the detection mechanism try to assign a suitable parser and processor to the feed.
529 If these values are set to the most common situation, the feed-creation can be much faster.'),
530 '#collapsible' => TRUE,
531 '#collapsed' => TRUE,
532 );
533
534 $form['advanced']['parser'] = array('#type' => 'fieldset',
535 '#title' => t('Parsers order'),
536 '#description' => t('The lighter parser will be tried out earlier.'),
537 '#collapsible' => TRUE,
538 '#collapsed' => TRUE,
539 );
540 // Walk through all the possible processors - not just for one feed type's
541 $parsers = _feedapi_suitable_parsers('', TRUE);
542 foreach ($parsers as $parser) {
543 $form['advanced']['parser']['feedapi_weight_parser_'. $parser] = array(
544 '#type' => 'weight',
545 '#title' => $parser,
546 '#delta' => 10,
547 '#default_value' => variable_get('feedapi_weight_parser_'. $parser, 0),
548 );
549 }
550 $form['advanced']['feed_processor'] = array('#type' => 'fieldset',
551 '#title' => t('Feed processors order'),
552 '#description' => t('The lighter feed processor will be tried out earlier.'),
553 '#collapsible' => TRUE,
554 '#collapsed' => TRUE,
555 );
556 // Walk through all the possible processors - not just for one feed type's
557 $processors = _feedapi_suitable_feed_processors('', TRUE);
558 foreach ($processors as $feed_processor) {
559 $form['advanced']['feed_processor']['feedapi_weight_feed_processor_'. $feed_processor] = array(
560 '#type' => 'weight',
561 '#title' => $feed_processor,
562 '#delta' => 10,
563 '#default_value' => variable_get('feedapi_weight_feed_processor_'. $feed_processor, 0),
564 );
565 }
566
567 $form['advanced']['item_processor'] = array('#type' => 'fieldset',
568 '#title' => t('Feed item processors order'),
569 '#description' => t('The lighter feed item processor will be tried out earlier.'),
570 '#collapsible' => TRUE,
571 '#collapsed' => TRUE,
572 );
573 // Walk through all the possible item processors - not just for one feed type's
574 $processors = _feedapi_suitable_item_processors('', TRUE);
575 foreach ($processors as $item_processor) {
576 $form['advanced']['item_processor']['feedapi_weight_item_processor_'. $item_processor] = array(
577 '#type' => 'weight',
578 '#title' => $item_processor,
579 '#delta' => 10,
580 '#default_value' => variable_get('feedapi_weight_item_processor_'. $item_processor, 0),
581 );
582 }
583 return system_settings_form($form);
584 }
585
586 /**
587 * Get the list of the parsers for the given feed type
588 *
589 * @param $type
590 * The descriptor of the feed type. For eg. "XML feed"
591 * @param $all
592 * If you set this to TRUE, all the parsers will be returned (not check the type)
593 * @return
594 * An associative array with the names of the suitable parsers
595 */
596 function _feedapi_suitable_parsers($type, $all = FALSE) {
597 $req = array('feedapi_type', 'feedapi_parse');
598 return _feedapi_check_requirement($type, $req, $all);
599 }
600
601 /**
602 * Execute the enabled parsers and create an unified output
603 *
604 * @param $feed
605 * Feed object
606 * @param $parsers
607 * Structure: array(
608 * "primary" => "parser_primary",
609 * "secondary" => array("parser1", "parser2", "parserN")
610 * );
611 * @return
612 * The object of the parser data
613 */
614 function _feedapi_call_parsers($feed, $parsers) {
615 $fid = $feed->fid;
616 if (module_exists($parsers["primary"])) {
617 $parser_output = module_invoke($parsers["primary"], "feedapi_parse", $feed);
618 $feed = (object) array_merge((array) $feed, (array) $parser_output);
619 }
620 // Call the turned on parsers, create an union of returned options
621 foreach ($parsers["secondary"] as $parser) {
622 $feed_ext = module_invoke($parser, "feedapi_parse", $feed);
623 $feed->options = (object) ((array) $feed->options + (array) $feed_ext->options);
624 }
625 $feed->fid = $fid;
626 // Filter bad or not allowed tags
627 $allowed = preg_split('/\s+|<|>/', variable_get('feedapi_allowed_html_tags', '<a> <b> <br> <dd> <dl> <dt> <em> <i> <li> <ol> <p> <strong> <u> <ul>'), -1, PREG_SPLIT_NO_EMPTY);
628 foreach (array('title', 'description') as $property) {
629 if (isset($feed->{$property})) {
630 if (is_string($feed->{$property})) {
631 $feed->{$property} = filter_xss($feed->{$property}, $allowed);
632 }
633 }
634 }
635 for ($i = 0; $i < count($feed->items); $i++) {
636 $feed->items[$i]->title = filter_xss($feed->items[$i]->title, $allowed);
637 $feed->items[$i]->description = filter_xss($feed->items[$i]->description, $allowed);
638 }
639 return $feed;
640 }
641
642 /**
643 * Auto-detect one possible parser and one feed and item processor to the given URL
644 * It follows the order of the modules from the module's settings page
645 *
646 * @param $url
647 * The fully-qualified URL of the feed
648 */
649 function _feedapi_assign_parser_processor($url) {
650 $parsers = _feedapi_suitable_parsers('', TRUE);
651 // Get the parsers' weight
652 $sorted_parsers = array();
653 foreach ($parsers as $parser) {
654 $sorted_parsers[variable_get('feedapi_weight_parser_'. $parser, 0)][] = $parser;
655 }
656 $success = FALSE;
657 $keys = array_keys($sorted_parsers);
658 if (count($keys) > 0) {
659 $low = min($keys);
660 $max = max($keys);
661 // Call the ordered parsers to find one compatible
662 for ($i = $low; $i <= $max; $i++) {
663 if (isset($sorted_parsers[$i])) {
664 foreach ($sorted_parsers[$i] as $parser) {
665 $type = module_invoke($parser, 'feedapi_compatible', $url);
666 $i = is_string($type) ? $max + 2: $i;
667 $success = is_string($type) ? TRUE : $success;
668 }
669 }
670 }
671 }
672 if (!$success) {
673 // We can't found a suitable parser at all
674 return array('type' => '', 'parser' => '', 'processor_feed' => '', 'processor_item' => '');
675 }
676 // Get the feed processor's weight
677 $processors_feed = _feedapi_suitable_feed_processors($type);
678 foreach ($processors_feed as $processor) {
679 $sorted_feed_processors[variable_get('feedapi_weight_feed_processor_'. $processor, 0)][] = $processor;
680 }
681 // Get the feed item processor's weight
682 $processors_item = _feedapi_suitable_item_processors($type);
683 foreach ($processors_item as $processor) {
684 $sorted_item_processors[variable_get('feedapi_weight_item_processor_'. $processor, 0)][] = $processor;
685 }
686 return array(
687 'type' => $type,
688 'parser' => $parser,
689 'processor_feed' => array_shift($sorted_feed_processors[min(array_keys($sorted_feed_processors))]), // lightest item processor
690 'processor_item' => array_shift($sorted_item_processors[min(array_keys($sorted_item_processors))]) // lightest feed processor
691 );
692 }
693
694 /**
695 * Collect a list of an arrays that fulfill the given requirements (hooks and type)
696 *
697 * @param $type
698 * The descriptor of the feed type. For eg. "XML feed"
699 * @param $all
700 * If you set this to TRUE, all the feed processors will be returned (not check the type)
701 * @return
702 * An associative array with the names of the suitable feed processors
703 */
704 function _feedapi_check_requirement($type, $hooks, $all) {
705 $possible = array();
706 $suitable = array();
707 for ($i = count($hooks) - 1; $i >= 0; $i--) {
708 $hooks[$i] = module_implements($hooks[$i]);
709 }
710 $possible = call_user_func_array('array_intersect', $hooks);
711 foreach ($possible as $parser) {
712 $types = module_invoke($parser, "feedapi_type");
713 if (in_array($type, $types) || $all) {
714 $suitable[] = $parser;
715 }
716 }
717 return drupal_map_assoc($suitable);
718 }
719
720 /**
721 * Get the list of the feed processors for the given feed type
722 *
723 * @param $type
724 * The descriptor of the feed type. For eg. "XML feed"
725 * @param $all
726 * If you set this to TRUE, all the feed processors will be returned (not check the type)
727 * @return
728 * An associative array with the names of the suitable feed processors
729 */
730 function _feedapi_suitable_feed_processors($type, $all = FALSE) {
731 $req = array('feedapi_type', 'feedapi_save', 'feedapi_update',
732 'feedapi_delete', 'feedapi_expire', 'feedapi_load',
733 'feedapi_validate', 'feedapi_get',
734 );
735 return _feedapi_check_requirement($type, $req, $all);
736 }
737
738 /**
739 * Get the list of the item processors for the given feed type
740 *
741 * @param $type
742 * The descriptor of the feed type. For eg. "XML feed"
743 * @param $all
744 * If you set this to TRUE, all the item processors will be returned (not check the type)
745 * @return
746 * An associative array with the names of the suitable feed processors
747 */
748 function _feedapi_suitable_item_processors($type, $all = FALSE) {
749 $req = array('feedapi_type', 'feedapi_item_save', 'feedapi_item_update',
750 'feedapi_item_delete', 'feedapi_item_load',
751 'feedapi_item_unique', 'feedapi_item_fetch_items',
752 );
753 return _feedapi_check_requirement($type, $req, $all);
754 }
755
756 /**
757 * Common form parts of editing/creating a feed
758 */
759 function _feedapi_common_form($feed = FALSE) {
760 $period = drupal_map_assoc(array(900, 1800, 3600, 7200, 10800, 21600, 32400, 43200, 64800, 86400, 172800, 259200, 604800, 1209600, 2419200), 'format_interval');
761 $form['url'] = array('#type' => 'textfield',
762 '#title' => t('URL'),
763 '#description' => t('The fully-qualified URL of the feed.'),
764 '#maxlength' => 255,
765 '#required' => TRUE,
766 );
767 $form['title'] = array('#type' => 'textfield',
768 '#title' => t('Title'),
769 '#maxlength' => 64,
770 '#description' => 'If you fill this field, the title from the feed will be overwritten by this',
771 '#required' => FALSE,
772 );
773 $form['description'] = array('#type' => 'textarea',
774 '#title' => t('Description'),
775 '#description' => 'If you fill this field, the description from the feed will be overwritten by this',
776 );
777 $form['refresh'] = array('#type' => 'select',
778 '#title' => t('Update interval'),
779 '#options' => $period,
780 '#description' => t('The refresh interval indicating how often you want to update this feed. Requires crontab.'),
781 );
782 if (user_access('administer feedapi')) {
783 $form['user'] = array(
784 '#type' => 'textfield',
785 '#title' => t('The owner of the feed'),
786 '#autocomplete_path' => 'user/autocomplete',
787 );
788 }
789 if (is_object($feed)) {
790 $form['url']['#default_value'] = isset($feed->url) ? $feed->url : '';
791 $form['title']['#default_value'] = isset($feed->title) ? $feed->title : '';
792 $form['description']['#default_value'] = isset($feed->description) ? $feed->description : '';
793 $form['refresh']['#default_value'] = isset($feed->refresh) ? $feed->refresh : 900;
794 if (user_access('administer feedapi')) {
795 $user = user_load(array('uid' => $feed->uid));
796 $form['user']['#default_value'] = $user->name;
797 }
798 }
799 return $form;
800 }
801
802 /**
803 * Remove non-existing processors from the processors arrays
804 */
805 function _feedapi_sanitize_processors(&$feed) {
806 foreach (array('processors_feed', 'processors_item') as $proc) {
807 if (is_array($feed->{$proc})) {
808 foreach ($feed->{$proc} as $key => $processor) {
809 if (!module_exists($processor)) {
810 unset($feed->{$proc}[$key]);
811 }
812 }
813 }
814 }
815 }

  ViewVC Help
Powered by ViewVC 1.1.2