Stripping CVS keywords
[project/feeds.git] / feeds.module
CommitLineData
dcaa933e 1<?php
eabfe21e 2
dcaa933e
AB
3/**
4 * @file
eabfe21e
AB
5 * Feeds - basic API functions and hook implementations.
6 */
7
f47efc7d 8// Common request time, use as point of reference and to avoid calls to time().
eabfe21e
AB
9define('FEEDS_REQUEST_TIME', time());
10// Do not schedule a feed for refresh.
11define('FEEDS_SCHEDULE_NEVER', -1);
12// Never expire feed items.
eabfe21e 13define('FEEDS_EXPIRE_NEVER', -1);
f47efc7d 14// An object that is not persistent. Compare EXPORT_IN_DATABASE, EXPORT_IN_CODE.
eabfe21e 15define('FEEDS_EXPORT_NONE', 0x0);
475305c0
AB
16// Status of batched operations.
17define('FEEDS_BATCH_COMPLETE', 1);
18define('FEEDS_BATCH_ACTIVE', 0);
eabfe21e
AB
19
20/**
c1cbe130 21 * @defgroup hooks Hook and callback implementations
eabfe21e 22 * @{
dcaa933e
AB
23 */
24
25/**
9f20738f 26 * Implements hook_cron().
dcaa933e
AB
27 */
28function feeds_cron() {
f544c313
AB
29 if ($importers = feeds_reschedule()) {
30 foreach ($importers as $id) {
31 feeds_importer($id)->schedule();
88f92d3b 32 $rows = db_query("SELECT feed_nid FROM {feeds_source} WHERE id = :id", array(':id' => $id));
681a5658 33 foreach ($rows as $row) {
f544c313
AB
34 feeds_source($id, $row->feed_nid)->schedule();
35 }
36 }
37 feeds_reschedule(FALSE);
38 return;
39 }
eabfe21e
AB
40}
41
42/**
9f20738f 43 * Implements hook_cron_queue_info().
fe86b16a 44 *
eabfe21e
AB
45 * Invoked by drupal_queue module if present.
46 */
47function feeds_cron_queue_info() {
48 $queues = array();
f544c313
AB
49 $queues['feeds_source_import'] = array(
50 'worker callback' => 'feeds_source_import',
51 'time' => variable_get('feeds_worker_time', 15),
52 );
53 $queues['feeds_importer_expire'] = array(
54 'worker callback' => 'feeds_importer_expire',
78df7b6d 55 'time' => variable_get('feeds_worker_time', 15),
eabfe21e
AB
56 );
57 return $queues;
dcaa933e
AB
58}
59
60/**
f544c313
AB
61 * Scheduler callback for importing from a source.
62 */
63function feeds_source_import($job) {
64 $source = feeds_source($job['type'], $job['id']);
65 try {
66 $source->existing()->import();
67 }
68 catch (Exception $e) {
69 watchdog('feeds_source_import()', $e->getMessage(), array(), WATCHDOG_ERROR);
70 }
71 $source->schedule();
72}
73
74/**
75 * Scheduler callback for expiring content.
76 */
77function feeds_importer_expire($job) {
78 $importer = feeds_importer($job['type']);
79 try {
80 $importer->existing()->expire();
81 }
82 catch (Exception $e) {
83 watchdog('feeds_importer_expire()', $e->getMessage(), array(), WATCHDOG_ERROR);
84 }
85 $importer->schedule();
86}
87
88/**
89 * Reschedule one or all importers.
90 *
91 * Note: feeds_reschedule() is used in update hook feeds_update_6012() and as
92 * such must be maintained as part of the upgrade path from pre 6.x 1.0 beta 6
93 * versions of Feeds.
94 *
95 * @param $importer_id
96 * If TRUE, all importers will be rescheduled, if FALSE, no importers will
97 * be rescheduled, if an importer id, only importer of that id will be
98 * rescheduled.
99 *
100 * @return
101 * TRUE if all importers need rescheduling. FALSE if no rescheduling is
102 * required. An array of importers that need rescheduling.
103 */
104function feeds_reschedule($importer_id = NULL) {
105 $reschedule = variable_get('feeds_reschedule', FALSE);
106 if ($importer_id === TRUE || $importer_id === FALSE) {
107 $reschedule = $importer_id;
108 }
109 elseif (is_string($importer_id) && $reschedule !== TRUE) {
110 $reschedule = is_array($reschedule) ? $reschedule : array();
111 $reschedule[$importer_id] = $importer_id;
112 }
113 variable_set('feeds_reschedule', $reschedule);
114 if ($reschedule === TRUE) {
115 return feeds_enabled_importers();
116 }
117 return $reschedule;
118}
119
120/**
9f20738f 121 * Implements feeds_permission().
dcaa933e 122 */
d7af3ea3
AB
123function feeds_permission() {
124 $perms = array(
c0426510
AB
125 'administer feeds' => array(
126 'title' => t('Administer Feeds'),
d6daf6bc 127 'description' => t('Create, update, delete importers, execute import and delete tasks on any importer.')
c0426510 128 ),
d7af3ea3 129 );
eabfe21e 130 foreach (feeds_importer_load_all() as $importer) {
d7af3ea3 131 $perms[] = array(
c0426510
AB
132 "import $importer->id feeds" => array(
133 'title' => t('Import @name feeds', array('@name' => $importer->config['name'])),
134 ),
d7af3ea3
AB
135 );
136 $perms[] = array(
c0426510
AB
137 "clear $importer->id feeds" => array(
138 'title' => t('Delete items from @name feeds', array('@name' => $importer->config['name'])),
139 ),
d7af3ea3 140 );
eabfe21e
AB
141 }
142 return $perms;
143}
144
145/**
9f20738f 146 * Implements hook_forms().
90cf9016
AB
147 *
148 * Declare form callbacks for all known classes derived from FeedsConfigurable.
eabfe21e
AB
149 */
150function feeds_forms() {
eabfe21e 151 $forms = array();
b422863d 152 $forms['FeedsImporter_feeds_form']['callback'] = 'feeds_form';
725e6aeb 153 $plugins = FeedsPlugin::all();
eabfe21e 154 foreach ($plugins as $plugin) {
b422863d 155 $forms[$plugin['handler']['class'] .'_feeds_form']['callback'] = 'feeds_form';
eabfe21e
AB
156 }
157 return $forms;
158}
159
160/**
9f20738f 161 * Implements hook_menu().
eabfe21e
AB
162 */
163function feeds_menu() {
164 // Register a callback for all feed configurations that are not attached to a content type.
165 $items = array();
166 foreach (feeds_importer_load_all() as $importer) {
167 if (empty($importer->config['content_type'])) {
168 $items['import/'. $importer->id] = array(
169 'title' => $importer->config['name'],
170 'page callback' => 'drupal_get_form',
171 'page arguments' => array('feeds_import_form', 1),
172 'access callback' => 'feeds_access',
173 'access arguments' => array('import', $importer->id),
174 'file' => 'feeds.pages.inc',
175 );
176 $items['import/'. $importer->id .'/import'] = array(
177 'title' => 'Import',
178 'type' => MENU_DEFAULT_LOCAL_TASK,
179 'weight' => -10,
180 );
181 $items['import/'. $importer->id .'/delete-items'] = array(
182 'title' => 'Delete items',
183 'page callback' => 'drupal_get_form',
184 'page arguments' => array('feeds_delete_tab_form', 1),
185 'access callback' => 'feeds_access',
186 'access arguments' => array('clear', $importer->id),
187 'file' => 'feeds.pages.inc',
188 'type' => MENU_LOCAL_TASK,
189 );
190 }
191 else {
192 $items['node/%node/import'] = array(
193 'title' => 'Import',
194 'page callback' => 'drupal_get_form',
195 'page arguments' => array('feeds_import_tab_form', 1),
196 'access callback' => 'feeds_access',
197 'access arguments' => array('import', 1),
198 'file' => 'feeds.pages.inc',
199 'type' => MENU_LOCAL_TASK,
200 'weight' => 10,
201 );
202 $items['node/%node/delete-items'] = array(
203 'title' => 'Delete items',
204 'page callback' => 'drupal_get_form',
205 'page arguments' => array('feeds_delete_tab_form', NULL, 1),
206 'access callback' => 'feeds_access',
207 'access arguments' => array('clear', 1),
208 'file' => 'feeds.pages.inc',
209 'type' => MENU_LOCAL_TASK,
210 'weight' => 11,
211 );
212 }
2c769fda 213 $items += $importer->fetcher->menuItem();
eabfe21e
AB
214 }
215 if (count($items)) {
216 $items['import'] = array(
217 'title' => 'Import',
218 'page callback' => 'feeds_page',
219 'access callback' => 'feeds_page_access',
220 'file' => 'feeds.pages.inc',
221 );
222 }
223 return $items;
224}
225
226/**
227 * Menu loader callback.
eabfe21e
AB
228 */
229function feeds_importer_load($id) {
230 return feeds_importer($id);
231}
232
233/**
9f20738f 234 * Implements hook_theme().
eabfe21e
AB
235 */
236function feeds_theme() {
237 return array(
b483268b
AB
238 'feeds_upload' => array(
239 'file' => 'feeds.pages.inc',
5a544038 240 'render element' => 'element',
eabfe21e
AB
241 ),
242 );
243}
244
245/**
246 * Menu access callback.
247 *
248 * @param $action
249 * One of 'import' or 'clear'.
250 * @param $param
251 * Node object or FeedsImporter id.
252 */
253function feeds_access($action, $param) {
254 if (is_string($param)) {
255 $importer_id = $param;
256 }
257 elseif ($param->type) {
4fb825e9 258 $importer_id = feeds_get_importer_id($param->type);
eabfe21e
AB
259 }
260
261 // Check for permissions if feed id is present, otherwise return FALSE.
4fb825e9 262 if ($importer_id) {
eabfe21e
AB
263 if (user_access('administer feeds') || user_access($action .' '. $importer_id .' feeds')) {
264 return TRUE;
265 }
266 }
267 return FALSE;
268}
269
270/**
271 * Menu access callback.
eabfe21e
AB
272 */
273function feeds_page_access() {
274 if (user_access('administer feeds')) {
275 return TRUE;
276 }
616f2ca8 277 foreach (feeds_enabled_importers() as $id) {
a3968487 278 if (user_access("import $id feeds")) {
eabfe21e
AB
279 return TRUE;
280 }
281 }
282 return FALSE;
283}
284
285/**
9f20738f 286 * Implements hook_views_api().
eabfe21e
AB
287 */
288function feeds_views_api() {
289 return array(
290 'api' => '2.0',
291 'path' => drupal_get_path('module', 'feeds') .'/views',
292 );
293}
294
295/**
9f20738f 296 * Implements hook_ctools_plugin_api().
eabfe21e
AB
297 */
298function feeds_ctools_plugin_api($owner, $api) {
299 if ($owner == 'feeds' && $api == 'plugins') {
300 return array('version' => 1);
301 }
302}
303
304/**
9f20738f 305 * Implements hook_ctools_plugin_type().
0619a600 306 */
8cd0d86c 307function feeds_ctools_plugin_type() {
0619a600 308 return array(
8cd0d86c
AB
309 'plugins' => array(
310 'cache' => TRUE,
311 'use hooks' => TRUE,
312 'classes' => array('handler'),
313 ),
0619a600
AB
314 );
315}
316
317/**
9f20738f 318 * Implements hook_feeds_plugins().
eabfe21e
AB
319 */
320function feeds_feeds_plugins() {
321 module_load_include('inc', 'feeds', 'feeds.plugins');
322 return _feeds_feeds_plugins();
323}
324
325/**
48cfdeab 326 * Implements hook_node_load().
eabfe21e 327 */
59a81b7e
AB
328function feeds_node_load($nodes) {
329 _feeds_node_processor_node_load($nodes);
48cfdeab 330}
5549eea7 331
48cfdeab
AB
332/**
333 * Implements hook_node_validate().
334 */
335function feeds_node_validate($node, $form, &$form_state) {
336 if (!$importer_id = feeds_get_importer_id($node->type)) {
337 return;
338 }
339 // Keep a copy of the title for subsequent node creation stages.
340 // @todo: revisit whether $node still looses all of its properties
341 // between validate and insert stage.
342 $last_title = &drupal_static('feeds_node_last_title');
343 $last_feeds = &drupal_static('feeds_node_last_feeds');
eabfe21e 344
48cfdeab
AB
345 // On validation stage we are working with a FeedsSource object that is
346 // not tied to a nid - when creating a new node there is no
347 // $node->nid at this stage.
348 $source = feeds_source($importer_id);
eabfe21e 349
725e6aeb
AB
350 // Node module magically moved $form['feeds'] to $node->feeds :P.
351 // configFormValidate may modify $last_feed, smuggle it to update/insert stage
352 // through a static variable.
48cfdeab
AB
353 $last_feeds = $node->feeds;
354 $source->configFormValidate($last_feeds);
355
356 // If node title is empty, try to retrieve title from feed.
357 if (trim($node->title) == '') {
358 try {
359 $source->addConfig($last_feeds);
360 if (!$last_title = $source->preview()->getTitle()) {
361 throw new Exception();
362 }
363 }
364 catch (Exception $e) {
365 drupal_set_message($e->getMessage(), 'error');
366 form_set_error('title', t('Could not retrieve title from feed.'), 'error');
eabfe21e
AB
367 }
368 }
369}
370
371/**
725e6aeb
AB
372 * Implements hook_node_presave().
373 */
374function feeds_node_presave($node) {
375 // Populate $node->title and $node->feed from result of validation phase.
376 $last_title = &drupal_static('feeds_node_last_title');
377 $last_feeds = &drupal_static('feeds_node_last_feeds');
378 if (empty($node->title) && !empty($last_title)) {
379 $node->title = $last_title;
380 }
381 if (!empty($last_feeds)) {
382 $node->feeds = $last_feeds;
383 }
384 $last_title = NULL;
385 $last_feeds = NULL;
386}
387
388/**
48cfdeab 389 * Implements hook_node_insert().
eabfe21e 390 */
48cfdeab
AB
391function feeds_node_insert($node) {
392 _feeds_node_processor_node_insert($node);
393 feeds_node_update($node);
725e6aeb
AB
394 if ($importer_id = feeds_get_importer_id($node->type)) {
395 // Start import if requested.
396 if (feeds_importer($importer_id)->config['import_on_create'] && !isset($node->feeds['suppress_import'])) {
397 feeds_batch_set(t('Importing'), 'import', $importer_id, $node->nid);
398 }
399 // Schedule source and importer.
400 feeds_source($importer_id, $node->nid)->schedule();
401 feeds_importer($importer_id)->schedule();
402 }
48cfdeab
AB
403}
404
405/**
406 * Implements hook_node_update().
407 */
408function feeds_node_update($node) {
409 _feeds_node_processor_node_update($node);
410 if (!$importer_id = feeds_get_importer_id($node->type)) {
411 return;
412 }
48cfdeab
AB
413 // Add configuration to feed source and save.
414 $source = feeds_source($importer_id, $node->nid);
725e6aeb 415 $source->addConfig($node->feeds);
48cfdeab 416 $source->save();
48cfdeab
AB
417}
418
419/**
420 * Implements hook_node_delete().
421 */
422function feeds_node_delete($node) {
423 _feeds_node_processor_node_delete($node);
424 if ($importer_id = feeds_get_importer_id($node->type)) {
425 feeds_source($importer_id, $node->nid)->delete();
426 }
427}
428
429/**
430 * FeedsNodeProcessor's hook_node_load().
431 */
59a81b7e 432function _feeds_node_processor_node_load($nodes) {
0be4cf20 433 if ($items = db_query("SELECT nid, imported, guid, url, feed_nid FROM {feeds_node_item} WHERE nid IN (:nids)", array(':nids' => array_keys($nodes)))) {
59a81b7e 434 foreach ($items as $item) {
725e6aeb 435 $nodes[$item->nid]->feeds_node_item = $item;
59a81b7e 436 }
48cfdeab
AB
437 }
438}
439
440/**
441 * FeedsNodeProcessor's hook_node_insert().
442 */
443function _feeds_node_processor_node_insert($node) {
444 if (isset($node->feeds_node_item)) {
445 $node->feeds_node_item->nid = $node->nid;
446 drupal_write_record('feeds_node_item', $node->feeds_node_item);
447 }
448}
449
450/**
451 * FeedsNodeProcessor's hook_node_update().
452 */
453function _feeds_node_processor_node_update($node) {
454 if (isset($node->feeds_node_item)) {
455 $node->feeds_node_item->nid = $node->nid;
456 drupal_write_record('feeds_node_item', $node->feeds_node_item, 'nid');
457 }
458}
459
460/**
461 * FeedsNodeProcessor's hook_node_delete().
462 */
463function _feeds_node_processor_node_delete($node) {
725e6aeb
AB
464 db_delete('feeds_node_item')
465 ->condition('nid', $node->nid)
466 ->execute();
dcaa933e
AB
467}
468
469/**
59a81b7e
AB
470 * Implements hook_taxonomy_term_update().
471 */
472function feeds_taxonomy_term_update($term) {
473 if (isset($term->feeds_importer_id)) {
474 db_delete('feeds_term_item')
475 ->condition('tid', $term->tid)
476 ->execute();
477 }
478}
479
480/**
481 * Implements hook_taxonomy_term_insert().
482 */
483function feeds_taxonomy_term_insert($term) {
e49c8cdd 484 if (isset($term->feeds_importer_id)) {
59a81b7e
AB
485 $record = array(
486 'id' => $term->feeds_importer_id,
487 'tid' => $term->tid,
725e6aeb 488 'feed_nid' => $term->feed_nid,
59a81b7e
AB
489 );
490 drupal_write_record('feeds_term_item', $record);
491 }
492}
493
494/**
59a81b7e
AB
495 * Implements hook_taxonomy_delete().
496 */
497function feeds_taxonomy_term_delete($term) {
498 db_delete('feeds_term_item')
499 ->condition('tid', $term->tid)
500 ->execute();
501}
502
503/**
9f20738f 504 * Implements hook_form_alter().
af015f3d
AB
505 */
506function feeds_form_alter(&$form, $form_state, $form_id) {
725e6aeb 507 if (!empty($form['#node_edit_form'])) {
4fb825e9 508 if ($importer_id = feeds_get_importer_id($form['type']['#value'])) {
eabfe21e
AB
509 // Set title to not required, try to retrieve it from feed.
510 $form['title']['#required'] = FALSE;
5549eea7
WW
511 // Enable uploads.
512 $form['#attributes']['enctype'] = 'multipart/form-data';
eabfe21e
AB
513
514 // Build form.
4fb825e9 515 $source = feeds_source($importer_id, empty($form['nid']['#value']) ? 0 : $form['nid']['#value']);
eabfe21e
AB
516 $form['feeds'] = array(
517 '#type' => 'fieldset',
518 '#title' => t('Feed'),
519 '#tree' => TRUE,
520 );
521 $form['feeds'] += $source->configForm($form_state);
4fb825e9 522 $form['#feed_id'] = $importer_id;
eabfe21e
AB
523 }
524 }
525}
526
527/**
4f40f554 528 * @}
eabfe21e
AB
529 */
530
531/**
c1cbe130 532 * @defgroup batch Batch functions
9e4acf98
AB
533 */
534
535/**
536 * Batch helper.
537 *
538 * @param $title
539 * Title to show to user when executing batch.
540 * @param $method
541 * Method to execute on importer; one of 'import', 'clear' or 'expire'.
542 * @param $importer_id
543 * Identifier of a FeedsImporter object.
544 * @param $feed_nid
545 * If importer is attached to content type, feed node id identifying the
546 * source to be imported.
547 */
548function feeds_batch_set($title, $method, $importer_id, $feed_nid = 0) {
549 $batch = array(
550 'title' => $title,
551 'operations' => array(
552 array('feeds_batch', array($method, $importer_id, $feed_nid)),
553 ),
554 'progress_message' => '',
555 );
556 batch_set($batch);
557}
558
559/**
560 * Batch callback.
561 *
562 * @param $method
563 * Method to execute on importer; one of 'import' or 'clear'.
564 * @param $importer_id
565 * Identifier of a FeedsImporter object.
566 * @param $feed_nid
567 * If importer is attached to content type, feed node id identifying the
568 * source to be imported.
569 * @param $context
570 * Batch context.
571 */
572function feeds_batch($method, $importer_id, $feed_nid = 0, &$context) {
573 $context['finished'] = 1;
574 try {
96ec6459 575 $context['finished'] = feeds_source($importer_id, $feed_nid)->$method();
9e4acf98
AB
576 }
577 catch (Exception $e) {
578 drupal_set_message($e->getMessage(), 'error');
579 }
580}
581
582/**
4f40f554 583 * @}
9e4acf98
AB
584 */
585
586/**
eabfe21e
AB
587 * @defgroup utility Utility functions
588 * @{
589 */
590
591/**
fe86b16a 592 * Loads all importers.
eabfe21e 593 *
3fa50248
AB
594 * @param $load_disabled
595 * Pass TRUE to load all importers, enabled or disabled, pass FALSE to only
596 * retrieve enabled importers.
85d695f9 597 *
eabfe21e
AB
598 * @return
599 * An array of all feed configurations available.
600 */
85d695f9 601function feeds_importer_load_all($load_disabled = FALSE) {
eabfe21e
AB
602 $feeds = array();
603 // This function can get called very early in install process through
604 // menu_router_rebuild(). Do not try to include CTools if not available.
605 if (function_exists('ctools_include')) {
606 ctools_include('export');
607 $configs = ctools_export_load_object('feeds_importer', 'all');
608 foreach ($configs as $config) {
c6479f64 609 if (!empty($config->id) && ($load_disabled || empty($config->disabled))) {
eabfe21e 610 $feeds[$config->id] = feeds_importer($config->id);
af015f3d
AB
611 }
612 }
613 }
eabfe21e 614 return $feeds;
af015f3d
AB
615}
616
617/**
fe86b16a 618 * Gets an array of enabled importer ids.
87c79ba1
AB
619 *
620 * @return
616f2ca8 621 * An array where the values contain ids of enabled importers.
87c79ba1 622 */
616f2ca8
AB
623function feeds_enabled_importers() {
624 return array_keys(_feeds_importer_digest());
87c79ba1
AB
625}
626
627/**
fe86b16a 628 * Gets an enabled importer configuration by content type.
eabfe21e 629 *
eabfe21e
AB
630 * @param $content_type
631 * A node type string.
632 *
633 * @return
4fb825e9
AB
634 * A FeedsImporter id if there is an importer for the given content type,
635 * FALSE otherwise.
fd1dc51f 636 */
4fb825e9 637function feeds_get_importer_id($content_type) {
616f2ca8
AB
638 $importers = array_flip(_feeds_importer_digest());
639 return isset($importers[$content_type]) ? $importers[$content_type] : FALSE;
640}
641
642/**
643 * Helper function for feeds_get_importer_id() and feeds_enabled_importers().
644 */
645function _feeds_importer_digest() {
7fa4efd0 646 $importers = &drupal_static(__FUNCTION__);
616f2ca8
AB
647 if ($importers === NULL) {
648 if ($cache = cache_get(__FUNCTION__)) {
649 $importers = $cache->data;
650 }
651 else {
652 $importers = array();
653 foreach (feeds_importer_load_all() as $importer) {
13b6962e 654 $importers[$importer->id] = isset($importer->config['content_type']) ? $importer->config['content_type'] : '';
eabfe21e 655 }
616f2ca8 656 cache_set(__FUNCTION__, $importers);
eabfe21e 657 }
fd1dc51f 658 }
616f2ca8
AB
659 return $importers;
660}
661
662/**
fe86b16a 663 * Resets importer caches. Call when enabling/disabling importers.
616f2ca8
AB
664 */
665function feeds_cache_clear($rebuild_menu = TRUE) {
666 cache_clear_all('_feeds_importer_digest', 'cache');
7fa4efd0 667 drupal_static_reset('_feeds_importer_digest');
616f2ca8
AB
668 ctools_include('export');
669 ctools_export_load_object_reset('feeds_importer');
0164b5c4 670 drupal_static_reset('_node_types_build');
616f2ca8
AB
671 if ($rebuild_menu) {
672 menu_rebuild();
673 }
fd1dc51f
AB
674}
675
676/**
fe86b16a 677 * Exports a FeedsImporter configuration to code.
dcaa933e 678 */
eabfe21e
AB
679function feeds_export($importer_id, $indent = '') {
680 ctools_include('export');
681 $result = ctools_export_load_object('feeds_importer', 'names', array('id' => $importer_id));
682 if (isset($result[$importer_id])) {
683 return ctools_export_object('feeds_importer', $result[$importer_id], $indent);
684 }
685}
686
687/**
fe86b16a 688 * Logs to a file like /mytmp/feeds_my_domain_org.log in temporary directory.
2c769fda
AB
689 */
690function feeds_dbg($msg) {
691 if (variable_get('feeds_debug', false)) {
692 if (!is_string($msg)) {
693 $msg = var_export($msg, true);
694 }
695 $filename = trim(str_replace('/', '_', $_SERVER['HTTP_HOST'] . base_path()), '_');
696 $handle = fopen(file_directory_temp() ."/feeds_$filename.log", 'a');
697 fwrite($handle, date('c') ."\t$msg\n");
698 fclose($handle);
699 }
700}
701
702/**
4f40f554 703 * @}
eabfe21e
AB
704 */
705
706/**
707 * @defgroup instantiators Instantiators
708 * @{
709 */
710
711/**
fe86b16a 712 * Gets an importer instance.
eabfe21e
AB
713 *
714 * @param $id
715 * The unique id of the importer object.
716 *
717 * @return
718 * A FeedsImporter object or an object of a class defined by the Drupal
719 * variable 'feeds_importer_class'. There is only one importer object
720 * per $id system-wide.
721 */
722function feeds_importer($id) {
eabfe21e
AB
723 return FeedsConfigurable::instance(variable_get('feeds_importer_class', 'FeedsImporter'), $id);
724}
725
726/**
fe86b16a 727 * Gets an instance of a source object.
eabfe21e 728 *
4fb825e9
AB
729 * @param $importer_id
730 * A FeedsImporter id.
eabfe21e
AB
731 * @param $feed_nid
732 * The node id of a feed node if the source is attached to a feed node.
733 *
734 * @return
735 * A FeedsSource object or an object of a class defiend by the Drupal
736 * variable 'source_class'.
737 */
4fb825e9 738function feeds_source($importer_id, $feed_nid = 0) {
4fb825e9 739 return FeedsSource::instance($importer_id, $feed_nid);
eabfe21e
AB
740}
741
742/**
fe86b16a 743 * Gets an instance of a class for a given plugin and id.
eabfe21e
AB
744 *
745 * @param $plugin
746 * A string that is the key of the plugin to load.
747 * @param $id
748 * A string that is the id of the object.
749 *
750 * @return
751 * A FeedsPlugin object.
752 *
753 * @throws Exception
754 * If plugin can't be instantiated.
dcaa933e 755 */
725e6aeb 756function feeds_plugin($plugin, $id) {
eabfe21e
AB
757 ctools_include('plugins');
758 if ($class = ctools_plugin_load_class('feeds', 'plugins', $plugin, 'handler')) {
759 return FeedsConfigurable::instance($class, $id);
dcaa933e 760 }
ca91e96f
AB
761 $args = array( '%plugin' => $plugin);
762 if (user_access('administer feeds')) {
040ab157 763 $args['!link'] = l($id, 'admin/structure/feeds/edit/' . $id);
ca91e96f
AB
764 drupal_set_message(t('Missing Feeds plugin %plugin. See !link. Check whether all required libraries and modules are installed properly.', $args), 'warning');
765 }
766 else {
767 drupal_set_message(t('Missing Feeds plugin %plugin. Please contact your site administrator.', $args), 'warning');
768 }
a75b733c
AB
769 $class = ctools_plugin_load_class('feeds', 'plugins', 'FeedsMissingPlugin', 'handler');
770 return FeedsConfigurable::instance($class, $id);
dcaa933e
AB
771}
772
773/**
4f40f554 774 * @}
eabfe21e
AB
775 */
776
777/**
778 * @defgroup include Funtions for loading libraries
779 * @{
780 */
781
782/**
fe86b16a 783 * Includes a library file.
eabfe21e
AB
784 *
785 * @param $file
786 * The filename to load from.
787 * @param $library
788 * The name of the library. If libraries module is installed,
789 * feeds_include_library() will look for libraries with this name managed by
790 * libraries module.
791 */
792function feeds_include_library($file, $library) {
793 static $included = array();
794 if (!isset($included[$file])) {
795 // Try first whether libraries module is present and load the file from
796 // there. If this fails, require the library from the local path.
d17e69a2 797 if (module_exists('libraries') && file_exists(libraries_get_path($library) . "/$file")) {
eabfe21e
AB
798 require libraries_get_path($library) ."/$file";
799 }
800 else {
d17e69a2 801 require DRUPAL_ROOT . '/' . drupal_get_path('module', 'feeds') . "/libraries/$file";
eabfe21e
AB
802 }
803 }
dcaa933e
AB
804 $included[$file] = TRUE;
805}
eabfe21e
AB
806
807/**
808 * Checks whether a library is present.
809 *
810 * @param $file
811 * The filename to load from.
812 * @param $library
813 * The name of the library. If libraries module is installed,
814 * feeds_library_exists() will look for libraries with this name managed by
815 * libraries module.
816 */
817function feeds_library_exists($file, $library) {
d17e69a2 818 if (module_exists('libraries') && file_exists(libraries_get_path($library) . "/$file")) {
eabfe21e
AB
819 return TRUE;
820 }
d17e69a2 821 elseif (file_exists(DRUPAL_ROOT . '/' . drupal_get_path('module', 'feeds') . "/libraries/$file")) {
eabfe21e
AB
822 return TRUE;
823 }
824 return FALSE;
825}
826
827/**
725e6aeb
AB
828 * Simplified drupal_alter().
829 *
830 * - None of that 'multiple parameters by ref' crazyness.
831 * - Don't use module_implements() to allow hot including on behalf
832 * implementations (see mappers/).
833 */
834function feeds_alter($type, &$data) {
835 $args = array(&$data);
836 $additional_args = func_get_args();
837 array_shift($additional_args);
838 array_shift($additional_args);
839 $args = array_merge($args, $additional_args);
840
841 $list = module_list();
842 foreach (module_list() as $module) {
843 $function = $module .'_'. $type .'_alter';
844 if (function_exists($function)) {
845 call_user_func_array($function, $args);
846 }
847 }
848}
849
850/**
4f40f554 851 * @}
eabfe21e 852 */