From 725e6aeb96ce77e47483f0a63eb3d1b3bcd45475 Mon Sep 17 00:00:00 2001
From: Alex Barth <alex_b@53995.no-reply.drupal.org>
Date: Wed, 29 Sep 2010 23:26:19 +0000
Subject: [PATCH] Merge in changes from
 http://github.com/lxbarth/Feeds/commits/DRUPAL-7--1-0-alpha1.

---
 CHANGELOG.txt                                 |  12 +-
 feeds.api.php                                 |  91 +---
 feeds.info                                    |   3 +
 feeds.module                                  | 212 +++-----
 feeds.pages.inc                               |  15 +-
 feeds_news/feeds_news.features.inc            |  12 +-
 .../feeds_news.feeds_importer_default.inc     |  30 +-
 feeds_news/feeds_news.info                    |   7 +-
 feeds_news/feeds_news.views_default.inc       | 504 ++++++------------
 feeds_ui/feeds_ui.admin.inc                   |  11 +-
 includes/FeedsBatch.inc                       |  21 +-
 includes/FeedsConfigurable.inc                |   2 +-
 includes/FeedsImporter.inc                    |   8 +-
 includes/FeedsSource.inc                      |  20 +-
 libraries/http_request.inc                    |   2 +-
 mappers/content.inc                           |  59 --
 mappers/field.inc                             |  71 +++
 mappers/file.inc                              |  86 +++
 mappers/filefield.inc                         |  79 ---
 mappers/taxonomy.inc                          | 170 +++---
 plugins/FeedsFileFetcher.inc                  |  64 ++-
 plugins/FeedsNodeProcessor.inc                |  13 +-
 plugins/FeedsParser.inc                       |  77 +--
 plugins/FeedsPlugin.inc                       | 109 +++-
 plugins/FeedsProcessor.inc                    |  11 +-
 plugins/FeedsSimplePieParser.inc              |   2 +-
 plugins/FeedsTermProcessor.inc                |  69 ++-
 plugins/FeedsUserProcessor.inc                |   3 +-
 tests/feeds.test.inc                          |  41 +-
 tests/feeds_processor_node.test               | 165 +++---
 tests/feeds_processor_term.test               |  13 +-
 31 files changed, 900 insertions(+), 1082 deletions(-)
 delete mode 100644 mappers/content.inc
 create mode 100644 mappers/field.inc
 create mode 100644 mappers/file.inc
 delete mode 100644 mappers/filefield.inc

diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index e9a55794..c1dfb31b 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -3,7 +3,17 @@
 Feeds 7.x XXXXXXXXXXXXXXXXXXXXXX
 --------------------------------
 
-
+- Expire files returned by FeedsImportBatch after DRUPAL_MAXIMUM_TEMP_FILE_AGE
+  seconds.
+- FeedsFileFetcher: track uploaded files, delete unused files.
+- yhahn: Upgrade FeedsTaxonomyProcessor.
+- Remove handling of target items that are array. All target items must be
+  objects now.
+- Upgrade file and image mapper.
+- Upgrade taxonomy mapper.
+- Upgrade field mapper.
+- Move plugin handling into FeedsPlugin class.
+- Base level upgrade.
 
 Feeds 6.x 1.0 Beta 6, 2010-09-16
 --------------------------------
diff --git a/feeds.api.php b/feeds.api.php
index 2d250dac..1a4640c8 100644
--- a/feeds.api.php
+++ b/feeds.api.php
@@ -151,7 +151,7 @@ function hook_feeds_parser_sources_alter(&$sources, $content_type) {
 }
 
 /**
- * Callback specified in hook_feeds_parser_sources_alter().
+ * Example callback specified in hook_feeds_parser_sources_alter().
  *
  * To be invoked on mapping time.
  *
@@ -173,25 +173,7 @@ function my_source_get_source(FeedsImportBatch $batch, $key) {
 }
 
 /**
- * Alter mapping targets for users. Use this hook to add additional target
- * options to the mapping form of User processors.
- *
- * For an example implementation, see mappers/profile.inc
- *
- * @param: &$targets
- *  Array containing the targets to be offered to the user. Add to this array
- *  to expose additional options. Remove from this array to suppress options.
- */
-function hook_feeds_user_processor_targets_alter(&$targets) {
-  $targets['my_user_field'] = array(
-    'name' => t('My custom user field'),
-    'description' => t('Description of what my custom user field does.'),
-    'callback' => 'my_callback',
-  );
-}
-
-/**
- * Alter mapping targets for nodes. Use this hook to add additional target
+ * Alter mapping targets for entities. Use this hook to add additional target
  * options to the mapping form of Node processors.
  *
  * If the key in $targets[] does not correspond to the actual key on the node
@@ -203,61 +185,40 @@ function hook_feeds_user_processor_targets_alter(&$targets) {
  *   Array containing the targets to be offered to the user. Add to this array
  *   to expose additional options. Remove from this array to suppress options.
  *   Remove with caution.
+ * @param $entity_type
+ *   The entity type of the target, for instance a 'node' entity.
  * @param $content_type
  *   The content type of the target node.
  */
-function hook_feeds_node_processor_targets_alter(&$targets, $content_type) {
-  $targets['my_node_field'] = array(
-    'name' => t('My custom node field'),
-    'description' => t('Description of what my custom node field does.'),
-    'callback' => 'my_callback',
-  );
-  $targets['my_node_field2'] = array(
-    'name' => t('My Second custom node field'),
-    'description' => t('Description of what my second custom node field does.'),
-    'callback' => 'my_callback2',
-    'real_target' => 'my_node_field_two', // Specify real target field on node.
-  );
-}
-
-/**
- * Alter mapping targets for taxonomy terms. Use this hook to add additional
- * target options to the mapping form of Taxonomy term processor.
- *
- * For an example implementation, look at geotaxnomy module.
- * http://drupal.org/project/geotaxonomy
- *
- * @param &$targets
- *   Array containing the targets to be offered to the user. Add to this array
- *   to expose additional options. Remove from this array to suppress options.
- *   Remove with caution.
- * @param $vid
- *   The vocabulary id
- */
-function hook_feeds_term_processor_targets_alter(&$targets, $vid) {
-  if (variable_get('mymodule_vocabulary_'. $vid, 0)) {
-    $targets['lat'] = array(
-      'name' => t('Latitude'),
-      'description' => t('Latitude of the term.'),
+function hook_feeds_processor_targets_alter(&$targets, $entity_type, $content_type) {
+  if ($entity_type == 'node') {
+    $targets['my_node_field'] = array(
+      'name' => t('My custom node field'),
+      'description' => t('Description of what my custom node field does.'),
+      'callback' => 'my_module_set_target',
     );
-    $targets['lon'] = array(
-      'name' => t('Longitude'),
-      'description' => t('Longitude of the term.'),
+    $targets['my_node_field2'] = array(
+      'name' => t('My Second custom node field'),
+      'description' => t('Description of what my second custom node field does.'),
+      'callback' => 'my_module_set_target2',
+      'real_target' => 'my_node_field_two', // Specify real target field on node.
     );
   }
 }
 
 /**
- * Alter mapping targets for Data table entries. Use this hook to add additional
- * target options to the mapping form of Data processor.
+ * Example callback specified in hook_feeds_processor_targets_alter().
+ *
+ * @param $entity
+ *   An entity object, for instance a node object.
+ * @param $target
+ *   A string identifying the target on the node.
+ * @param $value
+ *   The value to populate the target with.
+ *
  */
-function hook_feeds_data_processor_targets_alter(&$fields, $data_table) {
-  if ($data_table == mymodule_base_table()) {
-    $fields['mytable:category'] = array(
-      'name' => t('Category'),
-      'description' => t('One or more category terms.'),
-    );
-  }
+function my_module_set_target($entity, $target, $value) {
+  $entity->$target['und'][0]['value'] = $value;
 }
 
 /**
diff --git a/feeds.info b/feeds.info
index 4d9e9b98..b34fc115 100644
--- a/feeds.info
+++ b/feeds.info
@@ -10,5 +10,8 @@ files[] = includes/FeedsBatch.inc
 files[] = includes/FeedsConfigurable.inc
 files[] = includes/FeedsImporter.inc
 files[] = includes/FeedsSource.inc
+files[] = tests/feeds.test.inc
+files[] = tests/feeds_processor_node.test
+files[] = tests/feeds_processor_term.test
 core = 7.x
 php = 5.2
diff --git a/feeds.module b/feeds.module
index 4008031a..871fce7d 100644
--- a/feeds.module
+++ b/feeds.module
@@ -151,7 +151,7 @@ function feeds_permission() {
 function feeds_forms() {
   $forms = array();
   $forms['FeedsImporter_feeds_form']['callback'] = 'feeds_form';
-  $plugins = feeds_get_plugins();
+  $plugins = FeedsPlugin::all();
   foreach ($plugins as $plugin) {
     $forms[$plugin['handler']['class'] .'_feeds_form']['callback'] = 'feeds_form';
   }
@@ -348,7 +348,9 @@ function feeds_node_validate($node, $form, &$form_state) {
   // $node->nid at this stage.
   $source = feeds_source($importer_id);
 
-  // Node module magically moved $form['feeds'] to $node->feeds :P
+  // Node module magically moved $form['feeds'] to $node->feeds :P.
+  // configFormValidate may modify $last_feed, smuggle it to update/insert stage
+  // through a static variable.
   $last_feeds = $node->feeds;
   $source->configFormValidate($last_feeds);
 
@@ -367,12 +369,38 @@ function feeds_node_validate($node, $form, &$form_state) {
   }
 }
 
+/**
+ * Implements hook_node_presave().
+ */
+function feeds_node_presave($node) {
+  // Populate $node->title and $node->feed from result of validation phase.
+  $last_title = &drupal_static('feeds_node_last_title');
+  $last_feeds = &drupal_static('feeds_node_last_feeds');
+  if (empty($node->title) && !empty($last_title)) {
+    $node->title = $last_title;
+  }
+  if (!empty($last_feeds)) {
+    $node->feeds = $last_feeds;
+  }
+  $last_title = NULL;
+  $last_feeds = NULL;
+}
+
 /**
  * Implements hook_node_insert().
  */
 function feeds_node_insert($node) {
   _feeds_node_processor_node_insert($node);
   feeds_node_update($node);
+  if ($importer_id = feeds_get_importer_id($node->type)) {
+    // Start import if requested.
+    if (feeds_importer($importer_id)->config['import_on_create'] && !isset($node->feeds['suppress_import'])) {
+      feeds_batch_set(t('Importing'), 'import', $importer_id, $node->nid);
+    }
+    // Schedule source and importer.
+    feeds_source($importer_id, $node->nid)->schedule();
+    feeds_importer($importer_id)->schedule();
+  }
 }
 
 /**
@@ -383,33 +411,10 @@ function feeds_node_update($node) {
   if (!$importer_id = feeds_get_importer_id($node->type)) {
     return;
   }
-  $last_title = &drupal_static('feeds_node_last_title');
-  $last_feeds = &drupal_static('feeds_node_last_feeds');
-  // Populate title from result of validation phase.
-  if (!empty($last_title)) {
-    $node->title = $last_title;
-  }
-  $last_title = NULL;
-  // A node may not have been validated, make sure $last_feeds is present.
-  if (empty($last_feeds)) {
-    $last_feeds = $node->feeds;
-  }
   // Add configuration to feed source and save.
   $source = feeds_source($importer_id, $node->nid);
-  $source->addConfig($last_feeds);
+  $source->addConfig($node->feeds);
   $source->save();
-
-  // Refresh feed if import on create is selected and suppress_import is
-  // not set.
-  if ($op == 'insert' && feeds_importer($importer_id)->config['import_on_create'] && !isset($last_feeds['suppress_import'])) {
-    feeds_batch_set(t('Importing'), 'import', $importer_id, $node->nid);
-  }
-  // Add source to schedule, make sure importer is scheduled, too.
-  if ($op == 'insert') {
-    $source->schedule();
-    $source->importer->schedule();
-  }
-  $last_feeds = NULL;
 }
 
 /**
@@ -428,7 +433,7 @@ function feeds_node_delete($node) {
 function _feeds_node_processor_node_load($nodes) {
   if ($items = db_query("SELECT nid, imported, guid, url, feed_nid FROM {feeds_node_item} WHERE nid IN (:nids)", array(':nids' => array_keys($nodes)))) {
     foreach ($items as $item) {
-      $node->{$item->nid} = $item;
+      $nodes[$item->nid]->feeds_node_item = $item;
     }
   }
 }
@@ -457,11 +462,9 @@ function _feeds_node_processor_node_update($node) {
  * FeedsNodeProcessor's hook_node_delete().
  */
 function _feeds_node_processor_node_delete($node) {
-  if (isset($node->feeds_node_item)) {
-    db_delete('feeds_node_item')
-      ->condition('nid', $node->nid)
-      ->execute();
-  }
+  db_delete('feeds_node_item')
+    ->condition('nid', $node->nid)
+    ->execute();
 }
 
 /**
@@ -483,6 +486,7 @@ function feeds_taxonomy_term_insert($term) {
     $record = array(
       'id' => $term->feeds_importer_id,
       'tid' => $term->tid,
+      'feed_nid' => $term->feed_nid,
     );
     drupal_write_record('feeds_term_item', $record);
   }
@@ -501,7 +505,7 @@ function feeds_taxonomy_term_delete($term) {
  * Implements hook_form_alter().
  */
 function feeds_form_alter(&$form, $form_state, $form_id) {
-  if ($form['#id'] == 'node-form') {
+  if (!empty($form['#node_edit_form'])) {
     if ($importer_id = feeds_get_importer_id($form['type']['#value'])) {
       // Set title to not required, try to retrieve it from feed.
       $form['title']['#required'] = FALSE;
@@ -736,72 +740,9 @@ function feeds_source($importer_id, $feed_nid = 0) {
   return FeedsSource::instance($importer_id, $feed_nid);
 }
 
-/**
- * @}
- */
-
-/**
- * @defgroup plugins Plugin functions
- * @{
- *
- * @todo Encapsulate this in a FeedsPluginHandler class, move it to includes/
- * and only load it if we're manipulating plugins.
- */
-
-/**
- * Gets all available plugins. Does not list hidden plugins.
- *
- * @return
- *   An array where the keys are the plugin keys and the values
- *   are the plugin info arrays as defined in hook_feeds_plugins().
- */
-function feeds_get_plugins() {
-  ctools_include('plugins');
-  $plugins = ctools_get_plugins('feeds', 'plugins');
-
-  $result = array();
-  foreach ($plugins as $key => $info) {
-    if (!empty($info['hidden'])) {
-      continue;
-    }
-    $result[$key] = $info;
-  }
-
-  // Sort plugins by name and return.
-  uasort($result, 'feeds_plugin_compare');
-  return $result;
-}
-
-/**
- * Sort callback for feeds_get_plugins().
- */
-function feeds_plugin_compare($a, $b) {
-  return strcasecmp($a['name'], $b['name']);
-}
-
-/**
- * Gets all available plugins of a particular type.
- *
- * @param $type
- *   'fetcher', 'parser' or 'processor'
- */
-function feeds_get_plugins_by_type($type) {
-  $plugins = feeds_get_plugins();
-
-  $result = array();
-  foreach ($plugins as $key => $info) {
-    if ($type == feeds_plugin_type($key)) {
-      $result[$key] = $info;
-    }
-  }
-  return $result;
-}
-
 /**
  * Gets an instance of a class for a given plugin and id.
  *
- * @todo Collapse into FeedsPlugin::instance().
- *
  * @param $plugin
  *   A string that is the key of the plugin to load.
  * @param $id
@@ -813,7 +754,7 @@ function feeds_get_plugins_by_type($type) {
  * @throws Exception
  *   If plugin can't be instantiated.
  */
-function feeds_plugin_instance($plugin, $id) {
+function feeds_plugin($plugin, $id) {
   ctools_include('plugins');
   if ($class = ctools_plugin_load_class('feeds', 'plugins', $plugin, 'handler')) {
     return FeedsConfigurable::instance($class, $id);
@@ -830,60 +771,6 @@ function feeds_plugin_instance($plugin, $id) {
   return FeedsConfigurable::instance($class, $id);
 }
 
-/**
- * Determines whether given plugin is derived from given base plugin.
- *
- * @param $plugin_key
- *   String that identifies a Feeds plugin key.
- * @param $parent_plugin
- *   String that identifies a Feeds plugin key to be tested against.
- *
- * @return
- *   TRUE if $parent_plugin is directly *or indirectly* a parent of $plugin,
- *   FALSE otherwise.
- */
-function feeds_plugin_child($plugin_key, $parent_plugin) {
-  ctools_include('plugins');
-  $plugins = ctools_get_plugins('feeds', 'plugins');
-  $info = $plugins[$plugin_key];
-
-  if (empty($info['handler']['parent'])) {
-    return FALSE;
-  }
-  elseif ($info['handler']['parent'] == $parent_plugin) {
-    return TRUE;
-  }
-  else {
-    return feeds_plugin_child($info['handler']['parent'], $parent_plugin);
-  }
-}
-
-/**
- * Determines the type of a plugin.
- *
- * @param $plugin_key
- *   String that identifies a Feeds plugin key.
- *
- * @return
- *   One of the following values:
- *   'fetcher' if the plugin is a fetcher
- *   'parser' if the plugin is a parser
- *   'processor' if the plugin is a processor
- *   FALSE otherwise.
- */
-function feeds_plugin_type($plugin_key) {
-  if (feeds_plugin_child($plugin_key, 'FeedsFetcher')) {
-    return 'fetcher';
-  }
-  elseif (feeds_plugin_child($plugin_key, 'FeedsParser')) {
-    return 'parser';
-  }
-  elseif (feeds_plugin_child($plugin_key, 'FeedsProcessor')) {
-    return 'processor';
-  }
-  return FALSE;
-}
-
 /**
  * @}
  */
@@ -938,6 +825,29 @@ function feeds_library_exists($file, $library) {
   return FALSE;
 }
 
+/**
+ * Simplified drupal_alter().
+ *
+ * - None of that 'multiple parameters by ref' crazyness.
+ * - Don't use module_implements() to allow hot including on behalf
+ *   implementations (see mappers/).
+ */
+function feeds_alter($type, &$data) {
+  $args = array(&$data);
+  $additional_args = func_get_args();
+  array_shift($additional_args);
+  array_shift($additional_args);
+  $args = array_merge($args, $additional_args);
+
+  $list = module_list();
+  foreach (module_list() as $module) {
+    $function = $module .'_'. $type .'_alter';
+    if (function_exists($function)) {
+      call_user_func_array($function, $args);
+    }
+  }
+}
+
 /**
  * @}
  */
diff --git a/feeds.pages.inc b/feeds.pages.inc
index 44b244ad..e58bd401 100644
--- a/feeds.pages.inc
+++ b/feeds.pages.inc
@@ -159,19 +159,18 @@ function theme_feeds_upload($variables) {
   _form_set_class($element, array('form-file'));
   $description = '';
   if (!empty($element['#file_info'])) {
-    $info = $element['#file_info'];
+    $file = $element['#file_info'];
+    $wrapper = file_stream_wrapper_get_instance_by_uri($file->uri);
     $description .= '<div class="file-info">';
     $description .= '<div class="file-name">';
-    $description .= l(basename($info['path']), $info['path']);
+    $description .= l($file->filename, $wrapper->getExternalUrl());
     $description .= '</div>';
     $description .= '<div class="file-size">';
-    $description .= format_size($info['size']);
+    $description .= format_size($file->filesize);
+    $description .= '</div>';
+    $description .= '<div class="file-mime">';
+    $description .= check_plain($file->filemime);
     $description .= '</div>';
-    if (isset($info['mime'])) {
-      $description .= '<div class="file-mime">';
-      $description .= check_plain($info['mime']);
-      $description .= '</div>';
-    }
     $description .= '</div>';
   }
   $description .= '<div class="file-upload">';
diff --git a/feeds_news/feeds_news.features.inc b/feeds_news/feeds_news.features.inc
index 810c7621..1b7873f8 100644
--- a/feeds_news/feeds_news.features.inc
+++ b/feeds_news/feeds_news.features.inc
@@ -17,24 +17,18 @@ function feeds_news_node_info() {
   $items = array(
     'feed' => array(
       'name' => t('Feed'),
-      'module' => 'node',
+      'base' => 'node_content',
       'description' => t('Subscribe to RSS or Atom feeds. Creates nodes of the content type "Feed item" from feed content.'),
       'has_title' => '1',
       'title_label' => t('Title'),
-      'has_body' => '1',
-      'body_label' => t('Body'),
-      'min_word_count' => '0',
       'help' => '',
     ),
     'feed_item' => array(
       'name' => t('Feed item'),
-      'module' => 'node',
+      'base' => 'node_content',
       'description' => t('This content type is being used for automatically aggregated content from feeds.'),
       'has_title' => '1',
       'title_label' => t('Title'),
-      'has_body' => '1',
-      'body_label' => t('Body'),
-      'min_word_count' => '0',
       'help' => '',
     ),
   );
@@ -46,6 +40,6 @@ function feeds_news_node_info() {
  */
 function feeds_news_views_api() {
   return array(
-    'api' => '2',
+    'api' => '3.0-alpha1',
   );
 }
diff --git a/feeds_news/feeds_news.feeds_importer_default.inc b/feeds_news/feeds_news.feeds_importer_default.inc
index 91642ba4..4fe041c7 100644
--- a/feeds_news/feeds_news.feeds_importer_default.inc
+++ b/feeds_news/feeds_news.feeds_importer_default.inc
@@ -15,8 +15,8 @@ function feeds_news_feeds_importer_default() {
     'fetcher' => array(
       'plugin_key' => 'FeedsHTTPFetcher',
       'config' => array(
-        'auto_detect_feeds' => FALSE,
-        'use_pubsubhubbub' => FALSE,
+        'auto_detect_feeds' => 1,
+        'use_pubsubhubbub' => 0,
         'designated_hub' => '',
       ),
     ),
@@ -31,30 +31,30 @@ function feeds_news_feeds_importer_default() {
         'update_existing' => 0,
         'expire' => '-1',
         'mappings' => array(
-          '0' => array(
+          0 => array(
             'source' => 'title',
             'target' => 'title',
             'unique' => FALSE,
           ),
-          '1' => array(
-            'source' => 'description',
-            'target' => 'body',
-            'unique' => FALSE,
-          ),
-          '2' => array(
+          1 => array(
             'source' => 'timestamp',
             'target' => 'created',
             'unique' => FALSE,
           ),
-          '3' => array(
+          2 => array(
             'source' => 'url',
             'target' => 'url',
-            'unique' => TRUE,
+            'unique' => 1,
           ),
-          '4' => array(
+          3 => array(
             'source' => 'guid',
             'target' => 'guid',
-            'unique' => TRUE,
+            'unique' => 1,
+          ),
+          4 => array(
+            'source' => 'description',
+            'target' => 'field_body',
+            'unique' => FALSE,
           ),
         ),
         'input_format' => 0,
@@ -92,12 +92,12 @@ function feeds_news_feeds_importer_default() {
         'content_type' => 'feed',
         'update_existing' => 0,
         'mappings' => array(
-          '0' => array(
+          0 => array(
             'source' => 'title',
             'target' => 'title',
             'unique' => FALSE,
           ),
-          '1' => array(
+          1 => array(
             'source' => 'xmlurl',
             'target' => 'source',
             'unique' => 1,
diff --git a/feeds_news/feeds_news.info b/feeds_news/feeds_news.info
index c6f6d7d6..bc35836d 100644
--- a/feeds_news/feeds_news.info
+++ b/feeds_news/feeds_news.info
@@ -1,14 +1,17 @@
-core = "6.x"
+core = "7.x"
+dependencies[] = "features"
 dependencies[] = "feeds"
 dependencies[] = "views"
 description = "A news aggregator built with feeds, creates nodes from imported feed items. With OPML import."
 features[ctools][] = "feeds:feeds_importer_default:1"
 features[feeds_importer][] = "feed"
 features[feeds_importer][] = "opml"
+features[field][] = "node-feed_item-field_body"
 features[node][] = "feed"
 features[node][] = "feed_item"
 features[views][] = "feeds_defaults_feed_items"
-features[views_api][] = "api:2"
+features[views_api][] = "api:3.0-alpha1"
+files[] = "feeds_news.module"
 name = "Feeds News"
 package = "Feeds"
 project = "Feeds"
diff --git a/feeds_news/feeds_news.views_default.inc b/feeds_news/feeds_news.views_default.inc
index 62c68d40..97a10ee4 100644
--- a/feeds_news/feeds_news.views_default.inc
+++ b/feeds_news/feeds_news.views_default.inc
@@ -14,353 +14,169 @@ function feeds_news_views_default_views() {
   $view->view_php = '';
   $view->base_table = 'node';
   $view->is_cacheable = FALSE;
-  $view->api_version = 2;
+  $view->api_version = '3.0-alpha1';
   $view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
+  
+/* Display: Defaults */
   $handler = $view->new_display('default', 'Defaults', 'default');
-  $handler->override_option('relationships', array(
-    'feed_nid' => array(
-      'label' => 'Owner feed',
-      'required' => 1,
-      'id' => 'feed_nid',
-      'table' => 'feeds_node_item',
-      'field' => 'feed_nid',
-      'override' => array(
-        'button' => 'Override',
-      ),
-      'relationship' => 'none',
-    ),
-  ));
-  $handler->override_option('fields', array(
-    'url' => array(
-      'label' => '',
-      'alter' => array(
-        'alter_text' => 0,
-        'text' => '',
-        'make_link' => 0,
-        'path' => '',
-        'link_class' => '',
-        'alt' => '',
-        'prefix' => '',
-        'suffix' => '',
-        'target' => '',
-        'help' => '',
-        'trim' => 0,
-        'max_length' => '',
-        'word_boundary' => 1,
-        'ellipsis' => 1,
-        'html' => 0,
-        'strip_tags' => 0,
-      ),
-      'empty' => '',
-      'hide_empty' => 0,
-      'empty_zero' => 0,
-      'display_as_link' => 1,
-      'exclude' => 1,
-      'id' => 'url',
-      'table' => 'feeds_node_item',
-      'field' => 'url',
-      'override' => array(
-        'button' => 'Override',
-      ),
-      'relationship' => 'none',
-    ),
-    'title' => array(
-      'label' => '',
-      'alter' => array(
-        'alter_text' => 0,
-        'text' => '',
-        'make_link' => 1,
-        'path' => '[url]',
-        'link_class' => '',
-        'alt' => '',
-        'prefix' => '<h2>',
-        'suffix' => '</h2>',
-        'target' => '',
-        'help' => '',
-        'trim' => 0,
-        'max_length' => '',
-        'word_boundary' => 1,
-        'ellipsis' => 1,
-        'html' => 0,
-        'strip_tags' => 0,
-      ),
-      'empty' => '',
-      'hide_empty' => 0,
-      'empty_zero' => 0,
-      'link_to_node' => 0,
-      'exclude' => 0,
-      'id' => 'title',
-      'table' => 'node',
-      'field' => 'title',
-      'override' => array(
-        'button' => 'Override',
-      ),
-      'relationship' => 'none',
-    ),
-    'title_1' => array(
-      'label' => '',
-      'alter' => array(
-        'alter_text' => 0,
-        'text' => '',
-        'make_link' => 0,
-        'path' => '',
-        'link_class' => '',
-        'alt' => '',
-        'prefix' => '',
-        'suffix' => '',
-        'target' => '',
-        'help' => '',
-        'trim' => 0,
-        'max_length' => '',
-        'word_boundary' => 1,
-        'ellipsis' => 1,
-        'html' => 0,
-        'strip_tags' => 0,
-      ),
-      'empty' => '',
-      'hide_empty' => 0,
-      'empty_zero' => 0,
-      'link_to_node' => 1,
-      'exclude' => 1,
-      'id' => 'title_1',
-      'table' => 'node',
-      'field' => 'title',
-      'override' => array(
-        'button' => 'Override',
-      ),
-      'relationship' => 'feed_nid',
-    ),
-    'nothing' => array(
-      'label' => '',
-      'alter' => array(
-        'text' => 'From [title_1]',
-        'make_link' => 0,
-        'path' => '',
-        'link_class' => '',
-        'alt' => '',
-        'prefix' => '',
-        'suffix' => '',
-        'target' => '',
-        'help' => '',
-        'trim' => 0,
-        'max_length' => '',
-        'word_boundary' => 1,
-        'ellipsis' => 1,
-        'html' => 0,
-        'strip_tags' => 0,
-      ),
-      'empty' => '',
-      'hide_empty' => 0,
-      'empty_zero' => 0,
-      'exclude' => 0,
-      'id' => 'nothing',
-      'table' => 'views',
-      'field' => 'nothing',
-      'override' => array(
-        'button' => 'Override',
-      ),
-      'relationship' => 'none',
-    ),
-    'body' => array(
-      'label' => '',
-      'alter' => array(
-        'alter_text' => 0,
-        'text' => '',
-        'make_link' => 0,
-        'path' => '',
-        'link_class' => '',
-        'alt' => '',
-        'prefix' => '',
-        'suffix' => '',
-        'target' => '',
-        'help' => '',
-        'trim' => 0,
-        'max_length' => '',
-        'word_boundary' => 1,
-        'ellipsis' => 1,
-        'html' => 0,
-        'strip_tags' => 0,
-      ),
-      'empty' => '',
-      'hide_empty' => 0,
-      'empty_zero' => 0,
-      'exclude' => 0,
-      'id' => 'body',
-      'table' => 'node_revisions',
-      'field' => 'body',
-      'override' => array(
-        'button' => 'Override',
-      ),
-      'relationship' => 'none',
-    ),
-    'edit_node' => array(
-      'label' => '',
-      'alter' => array(
-        'alter_text' => 0,
-        'text' => '',
-        'make_link' => 0,
-        'path' => '',
-        'link_class' => '',
-        'alt' => '',
-        'prefix' => '',
-        'suffix' => '',
-        'target' => '',
-        'help' => '',
-        'trim' => 0,
-        'max_length' => '',
-        'word_boundary' => 1,
-        'ellipsis' => 1,
-        'html' => 0,
-        'strip_tags' => 0,
-      ),
-      'empty' => '',
-      'hide_empty' => 0,
-      'empty_zero' => 0,
-      'text' => 'Edit',
-      'exclude' => 0,
-      'id' => 'edit_node',
-      'table' => 'node',
-      'field' => 'edit_node',
-      'override' => array(
-        'button' => 'Override',
-      ),
-      'relationship' => 'none',
-    ),
-  ));
-  $handler->override_option('arguments', array(
-    'nid' => array(
-      'default_action' => 'empty',
-      'style_plugin' => 'default_summary',
-      'style_options' => array(),
-      'wildcard' => 'all',
-      'wildcard_substitution' => 'All',
-      'title' => 'Articles from %1',
-      'breadcrumb' => '',
-      'default_argument_type' => 'fixed',
-      'default_argument' => '',
-      'validate_type' => 'none',
-      'validate_fail' => 'not found',
-      'break_phrase' => 0,
-      'not' => 0,
-      'id' => 'nid',
-      'table' => 'node',
-      'field' => 'nid',
-      'validate_user_argument_type' => 'uid',
-      'validate_user_roles' => array(
-        '2' => 0,
-      ),
-      'relationship' => 'feed_nid',
-      'default_options_div_prefix' => '',
-      'default_argument_fixed' => '',
-      'default_argument_user' => 0,
-      'default_argument_php' => '',
-      'validate_argument_node_type' => array(
-        'feed' => 0,
-        'feed_item' => 0,
-        'article' => 0,
-      ),
-      'validate_argument_node_access' => 0,
-      'validate_argument_nid_type' => 'nid',
-      'validate_argument_vocabulary' => array(),
-      'validate_argument_type' => 'tid',
-      'validate_argument_transform' => 0,
-      'validate_user_restrict_roles' => 0,
-      'validate_argument_php' => '',
-      'override' => array(
-        'button' => 'Override',
-      ),
-    ),
-  ));
-  $handler->override_option('filters', array(
-    'type' => array(
-      'operator' => 'in',
-      'value' => array(
-        'feed_item' => 'feed_item',
-      ),
-      'group' => '0',
-      'exposed' => FALSE,
-      'expose' => array(
-        'operator' => FALSE,
-        'label' => '',
-      ),
-      'id' => 'type',
-      'table' => 'node',
-      'field' => 'type',
-      'override' => array(
-        'button' => 'Override',
-      ),
-      'relationship' => 'none',
-    ),
-  ));
-  $handler->override_option('access', array(
-    'type' => 'perm',
-    'perm' => 'access content',
-  ));
-  $handler->override_option('cache', array(
-    'type' => 'none',
-  ));
-  $handler->override_option('empty', 'There are no items for this feed at the moment.');
-  $handler->override_option('empty_format', '1');
+  $handler->display->display_options['access']['type'] = 'perm';
+  $handler->display->display_options['cache']['type'] = 'none';
+  $handler->display->display_options['query']['type'] = 'views_query';
+  $handler->display->display_options['exposed_form']['type'] = 'basic';
+  $handler->display->display_options['pager']['type'] = 'full';
+  $handler->display->display_options['style_plugin'] = 'default';
+  $handler->display->display_options['row_plugin'] = 'fields';
+  /* Empty text: Global: Text area */
+  $handler->display->display_options['empty']['text']['id'] = 'area';
+  $handler->display->display_options['empty']['text']['table'] = 'views';
+  $handler->display->display_options['empty']['text']['field'] = 'area';
+  $handler->display->display_options['empty']['text']['empty'] = FALSE;
+  $handler->display->display_options['empty']['text']['content'] = 'There are no items for this feed at the moment.';
+  $handler->display->display_options['empty']['text']['format'] = '1';
+  /* Relationship: Feeds Item: Owner feed */
+  $handler->display->display_options['relationships']['feed_nid']['id'] = 'feed_nid';
+  $handler->display->display_options['relationships']['feed_nid']['table'] = 'feeds_node_item';
+  $handler->display->display_options['relationships']['feed_nid']['field'] = 'feed_nid';
+  $handler->display->display_options['relationships']['feed_nid']['required'] = 1;
+  /* Field: Feeds Item: Item URL */
+  $handler->display->display_options['fields']['url']['id'] = 'url';
+  $handler->display->display_options['fields']['url']['table'] = 'feeds_node_item';
+  $handler->display->display_options['fields']['url']['field'] = 'url';
+  $handler->display->display_options['fields']['url']['label'] = '';
+  $handler->display->display_options['fields']['url']['exclude'] = TRUE;
+  $handler->display->display_options['fields']['url']['alter']['alter_text'] = 0;
+  $handler->display->display_options['fields']['url']['alter']['make_link'] = 0;
+  $handler->display->display_options['fields']['url']['alter']['trim'] = 0;
+  $handler->display->display_options['fields']['url']['alter']['word_boundary'] = 1;
+  $handler->display->display_options['fields']['url']['alter']['ellipsis'] = 1;
+  $handler->display->display_options['fields']['url']['alter']['strip_tags'] = 0;
+  $handler->display->display_options['fields']['url']['alter']['html'] = 0;
+  $handler->display->display_options['fields']['url']['hide_empty'] = 0;
+  $handler->display->display_options['fields']['url']['empty_zero'] = 0;
+  $handler->display->display_options['fields']['url']['display_as_link'] = 1;
+  /* Field: Node: Title */
+  $handler->display->display_options['fields']['title']['id'] = 'title';
+  $handler->display->display_options['fields']['title']['table'] = 'node';
+  $handler->display->display_options['fields']['title']['field'] = 'title';
+  $handler->display->display_options['fields']['title']['label'] = '';
+  $handler->display->display_options['fields']['title']['alter']['alter_text'] = 0;
+  $handler->display->display_options['fields']['title']['alter']['make_link'] = 1;
+  $handler->display->display_options['fields']['title']['alter']['path'] = '[url]';
+  $handler->display->display_options['fields']['title']['alter']['prefix'] = '<h2>';
+  $handler->display->display_options['fields']['title']['alter']['suffix'] = '</h2>';
+  $handler->display->display_options['fields']['title']['alter']['trim'] = 0;
+  $handler->display->display_options['fields']['title']['alter']['word_boundary'] = 1;
+  $handler->display->display_options['fields']['title']['alter']['ellipsis'] = 1;
+  $handler->display->display_options['fields']['title']['alter']['strip_tags'] = 0;
+  $handler->display->display_options['fields']['title']['alter']['html'] = 0;
+  $handler->display->display_options['fields']['title']['hide_empty'] = 0;
+  $handler->display->display_options['fields']['title']['empty_zero'] = 0;
+  $handler->display->display_options['fields']['title']['link_to_node'] = 0;
+  /* Field: Node: Title */
+  $handler->display->display_options['fields']['title_1']['id'] = 'title_1';
+  $handler->display->display_options['fields']['title_1']['table'] = 'node';
+  $handler->display->display_options['fields']['title_1']['field'] = 'title';
+  $handler->display->display_options['fields']['title_1']['relationship'] = 'feed_nid';
+  $handler->display->display_options['fields']['title_1']['label'] = '';
+  $handler->display->display_options['fields']['title_1']['exclude'] = TRUE;
+  $handler->display->display_options['fields']['title_1']['alter']['alter_text'] = 0;
+  $handler->display->display_options['fields']['title_1']['alter']['make_link'] = 0;
+  $handler->display->display_options['fields']['title_1']['alter']['trim'] = 0;
+  $handler->display->display_options['fields']['title_1']['alter']['word_boundary'] = 1;
+  $handler->display->display_options['fields']['title_1']['alter']['ellipsis'] = 1;
+  $handler->display->display_options['fields']['title_1']['alter']['strip_tags'] = 0;
+  $handler->display->display_options['fields']['title_1']['alter']['html'] = 0;
+  $handler->display->display_options['fields']['title_1']['hide_empty'] = 0;
+  $handler->display->display_options['fields']['title_1']['empty_zero'] = 0;
+  $handler->display->display_options['fields']['title_1']['link_to_node'] = 1;
+  /* Field: Global: Custom text */
+  $handler->display->display_options['fields']['nothing']['id'] = 'nothing';
+  $handler->display->display_options['fields']['nothing']['table'] = 'views';
+  $handler->display->display_options['fields']['nothing']['field'] = 'nothing';
+  $handler->display->display_options['fields']['nothing']['label'] = '';
+  $handler->display->display_options['fields']['nothing']['alter']['text'] = 'From [title_1]';
+  $handler->display->display_options['fields']['nothing']['alter']['make_link'] = 0;
+  $handler->display->display_options['fields']['nothing']['alter']['trim'] = 0;
+  $handler->display->display_options['fields']['nothing']['alter']['word_boundary'] = 1;
+  $handler->display->display_options['fields']['nothing']['alter']['ellipsis'] = 1;
+  $handler->display->display_options['fields']['nothing']['alter']['strip_tags'] = 0;
+  $handler->display->display_options['fields']['nothing']['alter']['html'] = 0;
+  $handler->display->display_options['fields']['nothing']['hide_empty'] = 0;
+  $handler->display->display_options['fields']['nothing']['empty_zero'] = 0;
+  /* Field: Fields: field_body */
+  $handler->display->display_options['fields']['entity_id']['id'] = 'entity_id';
+  $handler->display->display_options['fields']['entity_id']['table'] = 'field_data_field_body';
+  $handler->display->display_options['fields']['entity_id']['field'] = 'entity_id';
+  $handler->display->display_options['fields']['entity_id']['label'] = '';
+  $handler->display->display_options['fields']['entity_id']['alter']['alter_text'] = 0;
+  $handler->display->display_options['fields']['entity_id']['alter']['make_link'] = 0;
+  $handler->display->display_options['fields']['entity_id']['alter']['absolute'] = 0;
+  $handler->display->display_options['fields']['entity_id']['alter']['trim'] = 0;
+  $handler->display->display_options['fields']['entity_id']['alter']['word_boundary'] = 1;
+  $handler->display->display_options['fields']['entity_id']['alter']['ellipsis'] = 1;
+  $handler->display->display_options['fields']['entity_id']['alter']['strip_tags'] = 0;
+  $handler->display->display_options['fields']['entity_id']['alter']['html'] = 0;
+  $handler->display->display_options['fields']['entity_id']['hide_empty'] = 1;
+  $handler->display->display_options['fields']['entity_id']['empty_zero'] = 0;
+  /* Field: Node: Edit link */
+  $handler->display->display_options['fields']['edit_node']['id'] = 'edit_node';
+  $handler->display->display_options['fields']['edit_node']['table'] = 'node';
+  $handler->display->display_options['fields']['edit_node']['field'] = 'edit_node';
+  $handler->display->display_options['fields']['edit_node']['label'] = '';
+  $handler->display->display_options['fields']['edit_node']['alter']['alter_text'] = 0;
+  $handler->display->display_options['fields']['edit_node']['alter']['make_link'] = 0;
+  $handler->display->display_options['fields']['edit_node']['alter']['trim'] = 0;
+  $handler->display->display_options['fields']['edit_node']['alter']['word_boundary'] = 1;
+  $handler->display->display_options['fields']['edit_node']['alter']['ellipsis'] = 1;
+  $handler->display->display_options['fields']['edit_node']['alter']['strip_tags'] = 0;
+  $handler->display->display_options['fields']['edit_node']['alter']['html'] = 0;
+  $handler->display->display_options['fields']['edit_node']['hide_empty'] = 0;
+  $handler->display->display_options['fields']['edit_node']['empty_zero'] = 0;
+  $handler->display->display_options['fields']['edit_node']['text'] = 'Edit';
+  /* Argument: Node: Nid */
+  $handler->display->display_options['arguments']['nid']['id'] = 'nid';
+  $handler->display->display_options['arguments']['nid']['table'] = 'node';
+  $handler->display->display_options['arguments']['nid']['field'] = 'nid';
+  $handler->display->display_options['arguments']['nid']['relationship'] = 'feed_nid';
+  $handler->display->display_options['arguments']['nid']['default_action'] = 'empty';
+  $handler->display->display_options['arguments']['nid']['style_plugin'] = 'default_summary';
+  $handler->display->display_options['arguments']['nid']['title'] = 'Articles from %1';
+  $handler->display->display_options['arguments']['nid']['default_argument_type'] = 'fixed';
+  $handler->display->display_options['arguments']['nid']['break_phrase'] = 0;
+  $handler->display->display_options['arguments']['nid']['not'] = 0;
+  /* Filter: Node: Type */
+  $handler->display->display_options['filters']['type']['id'] = 'type';
+  $handler->display->display_options['filters']['type']['table'] = 'node';
+  $handler->display->display_options['filters']['type']['field'] = 'type';
+  $handler->display->display_options['filters']['type']['value'] = array(
+    'feed_item' => 'feed_item',
+  );
+  $handler->display->display_options['filters']['type']['expose']['operator'] = FALSE;
+  
+/* Display: Page */
   $handler = $view->new_display('page', 'Page', 'page_1');
-  $handler->override_option('arguments', array(
-    'nid' => array(
-      'default_action' => 'empty',
-      'style_plugin' => 'default_summary',
-      'style_options' => array(),
-      'wildcard' => 'all',
-      'wildcard_substitution' => 'All',
-      'title' => 'All items from %1',
-      'breadcrumb' => '',
-      'default_argument_type' => 'fixed',
-      'default_argument' => '',
-      'validate_type' => 'node',
-      'validate_fail' => 'not found',
-      'break_phrase' => 0,
-      'not' => 0,
-      'id' => 'nid',
-      'table' => 'node',
-      'field' => 'nid',
-      'validate_user_argument_type' => 'uid',
-      'validate_user_roles' => array(
-        '2' => 0,
-      ),
-      'relationship' => 'feed_nid',
-      'default_options_div_prefix' => '',
-      'default_argument_fixed' => '',
-      'default_argument_user' => 0,
-      'default_argument_php' => '',
-      'validate_argument_node_type' => array(
-        'feed' => 'feed',
-        'feed_item' => 0,
-        'article' => 0,
-      ),
-      'validate_argument_node_access' => 0,
-      'validate_argument_nid_type' => 'nid',
-      'validate_argument_vocabulary' => array(),
-      'validate_argument_type' => 'tid',
-      'validate_argument_transform' => 0,
-      'validate_user_restrict_roles' => 0,
-      'validate_argument_php' => '',
-      'override' => array(
-        'button' => 'Use default',
-      ),
-    ),
-  ));
-  $handler->override_option('path', 'node/%/feed-items');
-  $handler->override_option('menu', array(
-    'type' => 'tab',
-    'title' => 'View items',
-    'description' => '',
-    'weight' => '0',
-    'name' => 'navigation',
-  ));
-  $handler->override_option('tab_options', array(
-    'type' => 'none',
-    'title' => '',
-    'description' => '',
-    'weight' => 0,
-    'name' => 'navigation',
-  ));
+  $handler->display->display_options['defaults']['arguments'] = FALSE;
+  /* Argument: Node: Nid */
+  $handler->display->display_options['arguments']['nid']['id'] = 'nid';
+  $handler->display->display_options['arguments']['nid']['table'] = 'node';
+  $handler->display->display_options['arguments']['nid']['field'] = 'nid';
+  $handler->display->display_options['arguments']['nid']['relationship'] = 'feed_nid';
+  $handler->display->display_options['arguments']['nid']['default_action'] = 'empty';
+  $handler->display->display_options['arguments']['nid']['style_plugin'] = 'default_summary';
+  $handler->display->display_options['arguments']['nid']['title'] = 'All items from %1';
+  $handler->display->display_options['arguments']['nid']['default_argument_type'] = 'fixed';
+  $handler->display->display_options['arguments']['nid']['validate_type'] = 'node';
+  $handler->display->display_options['arguments']['nid']['validate_options']['types'] = array(
+    'feed' => 'feed',
+    'feed_item' => 0,
+    'article' => 0,
+  );
+  $handler->display->display_options['arguments']['nid']['break_phrase'] = 0;
+  $handler->display->display_options['arguments']['nid']['not'] = 0;
+  $handler->display->display_options['path'] = 'node/%/feed-items';
+  $handler->display->display_options['menu']['type'] = 'tab';
+  $handler->display->display_options['menu']['title'] = 'View items';
+  $handler->display->display_options['menu']['weight'] = '0';
 
   $views[$view->name] = $view;
 
diff --git a/feeds_ui/feeds_ui.admin.inc b/feeds_ui/feeds_ui.admin.inc
index 86baec9f..08eba6ee 100644
--- a/feeds_ui/feeds_ui.admin.inc
+++ b/feeds_ui/feeds_ui.admin.inc
@@ -273,7 +273,7 @@ function feeds_ui_export_form($form, &$form_state, $importer) {
 function feeds_ui_edit_page($importer, $active = 'help', $plugin_key = '') {
 
   // Get plugins and configuration.
-  $plugins = feeds_get_plugins();
+  $plugins = FeedsPlugin::all();
   $config = $importer->config;
   // Base path for changing the active container.
   $path = 'admin/structure/feeds/edit/'. $importer->id;
@@ -301,9 +301,9 @@ function feeds_ui_edit_page($importer, $active = 'help', $plugin_key = '') {
         $active_container['title'] = t('Basic settings');
         $active_container['body'] = feeds_get_form($importer, 'configForm');
       }
-      // feeds_plugin_instance() returns a correct result because feed has been
+      // feeds_plugin() returns a correct result because feed has been
       // instantiated previously.
-      elseif (in_array($plugin_key, array_keys($plugins)) && $plugin = feeds_plugin_instance($plugin_key, $importer->id)) {
+      elseif (in_array($plugin_key, array_keys($plugins)) && $plugin = feeds_plugin($plugin_key, $importer->id)) {
         $active_container['title'] = t('Settings for !plugin', array('!plugin' => $plugins[$plugin_key]['name']));
         $active_container['body'] = feeds_get_form($plugin, 'configForm');
       }
@@ -414,7 +414,7 @@ function feeds_ui_edit_page($importer, $active = 'help', $plugin_key = '') {
  *   A Form API form definition.
  */
 function feeds_ui_plugin_form($form, &$form_state, $importer, $type) {
-  $plugins = feeds_get_plugins_by_type($type);
+  $plugins = FeedsPlugin::byType($type);
 
   $form = array();
   $form['#importer'] = $importer;
@@ -788,9 +788,10 @@ function theme_feeds_ui_mapping_form($variables) {
     foreach ($form['#mappings'] as $i => $mapping) {
       // Some parsers do not define source options.
       $source = isset($form['source']['#options'][$mapping['source']]) ? $form['source']['#options'][$mapping['source']] : $mapping['source'];
+      $target = isset($form['target']['#options'][$mapping['target']]) ? check_plain($form['target']['#options'][$mapping['target']]) : '<em>' . t('Missing') . '</em>';
       $rows[] = array(
         check_plain($source),
-        check_plain($form['target']['#options'][$mapping['target']]),
+        $target,
         drupal_render($form['unique_flags'][$i]),
         drupal_render($form['remove_flags'][$i]),
       );
diff --git a/includes/FeedsBatch.inc b/includes/FeedsBatch.inc
index 59144c4b..0bd873b3 100644
--- a/includes/FeedsBatch.inc
+++ b/includes/FeedsBatch.inc
@@ -183,6 +183,11 @@ class FeedsImportBatch extends FeedsBatch {
   }
 
   /**
+   * Get a path to a temporary file containing the resource provided by the
+   * fetcher.
+   *
+   * File will be deleted after DRUPAL_MAXIMUM_TEMP_FILE_AGE.
+   *
    * @return
    *   A path to a file containing the raw content as a source.
    *
@@ -191,17 +196,19 @@ class FeedsImportBatch extends FeedsBatch {
    */
   public function getFilePath() {
     if (!isset($this->file_path)) {
-      $dir = 'public://feeds';
-      if (!file_prepare_directory($dir, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
+      $destination = 'public://feeds';
+      if (!file_prepare_directory($destination, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
         throw new Exception(t('Feeds directory either cannot be created or is not writable.'));
       }
-      $dest = file_destination($dir . '/' . get_class($this) .'_'. drupal_get_token($this->url) .'_'. time(), FILE_EXISTS_RENAME);
       $this->file_path = FALSE;
-      $file = file_save_data($this->getRaw(), $dest);
-      if ($file === FALSE) {
-        throw new Exception(t('Cannot write content to %dest', array('%dest' => $dest)));
+      if ($file = file_save_data($this->getRaw(), $destination . '/'. get_class($this) . REQUEST_TIME)) {
+        $file->status = 0;
+        file_save($file);
+        $this->file_path = $file->uri;
+      }
+      else {
+        throw new Exception(t('Cannot write content to %dest', array('%dest' => $destination)));
       }
-      $this->file_path = $file->uri;
     }
     return $this->file_path;
   }
diff --git a/includes/FeedsConfigurable.inc b/includes/FeedsConfigurable.inc
index 6bef67b3..1eb76ed6 100644
--- a/includes/FeedsConfigurable.inc
+++ b/includes/FeedsConfigurable.inc
@@ -47,7 +47,7 @@ abstract class FeedsConfigurable {
   /**
    * Instantiate a FeedsConfigurable object.
    *
-   * Don't use directly, use feeds_importer() or feeds_plugin_instance()
+   * Don't use directly, use feeds_importer() or feeds_plugin()
    * instead.
    */
   public static function instance($class, $id) {
diff --git a/includes/FeedsImporter.inc b/includes/FeedsImporter.inc
index 64b2554f..4ef67544 100644
--- a/includes/FeedsImporter.inc
+++ b/includes/FeedsImporter.inc
@@ -27,7 +27,7 @@ class FeedsImporter extends FeedsConfigurable {
 
   // Every feed has a fetcher, a parser and a processor.
   // These variable names match the possible return values of
-  // feeds_plugin_type().
+  // FeedsPlugin::typeOf().
   protected $fetcher, $parser, $processor;
 
   // This array defines the variable names of the plugins above.
@@ -46,7 +46,7 @@ class FeedsImporter extends FeedsConfigurable {
     // Instantiate fetcher, parser and processor, set their configuration if
     // stored info is available.
     foreach ($this->plugin_types as $type) {
-      $plugin = feeds_plugin_instance($this->config[$type]['plugin_key'], $this->id);
+      $plugin = feeds_plugin($this->config[$type]['plugin_key'], $this->id);
 
       if (isset($this->config[$type]['config'])) {
         $plugin->setConfig($this->config[$type]['config']);
@@ -155,8 +155,8 @@ class FeedsImporter extends FeedsConfigurable {
    */
   public function setPlugin($plugin_key) {
     // $plugin_type can be either 'fetcher', 'parser' or 'processor'
-    if ($plugin_type = feeds_plugin_type($plugin_key)) {
-      if ($plugin = feeds_plugin_instance($plugin_key, $this->id)) {
+    if ($plugin_type = FeedsPlugin::typeOf($plugin_key)) {
+      if ($plugin = feeds_plugin($plugin_key, $this->id)) {
         // Unset existing plugin, switch to new plugin.
         unset($this->$plugin_type);
         $this->$plugin_type = $plugin;
diff --git a/includes/FeedsSource.inc b/includes/FeedsSource.inc
index 377936c8..8c1ad1ae 100644
--- a/includes/FeedsSource.inc
+++ b/includes/FeedsSource.inc
@@ -224,11 +224,12 @@ class FeedsSource extends FeedsConfigurable {
    * Save configuration.
    */
   public function save() {
-    $config = $this->getConfig();
     // Alert implementers of FeedsSourceInterface to the fact that we're saving.
     foreach ($this->importer->plugin_types as $type) {
       $this->importer->$type->sourceSave($this);
     }
+    $config = $this->getConfig();
+
     // Store the source property of the fetcher in a separate column so that we
     // can do fast lookups on it.
     $source = '';
@@ -307,7 +308,7 @@ class FeedsSource extends FeedsConfigurable {
   }
 
   /**
-   * Convenience function. Returns the configuration for a specific class.
+   * Returns the configuration for a specific client class.
    *
    * @param FeedsSourceInterface $client
    *   An object that is an implementer of FeedsSourceInterface.
@@ -319,6 +320,21 @@ class FeedsSource extends FeedsConfigurable {
     return $this->config[get_class($client)];
   }
 
+  /**
+   * Sets the configuration for a specific client class.
+   *
+   * @param FeedsSourceInterface $client
+   *   An object that is an implementer of FeedsSourceInterface.
+   * @param $config
+   *   The configuration for $client.
+   *
+   * @return
+   *   An array stored for $client.
+   */
+  public function setConfigFor(FeedsSourceInterface $client, $config) {
+    $this->config[get_class($client)] = $config;
+  }
+
   /**
    * Return defaults for feed configuration.
    */
diff --git a/libraries/http_request.inc b/libraries/http_request.inc
index a9457247..33d663ad 100644
--- a/libraries/http_request.inc
+++ b/libraries/http_request.inc
@@ -130,7 +130,7 @@ function http_request_get($url, $username = NULL, $password = NULL, $accept_inva
     // https.
     // Validate in PHP, CURLOPT_PROTOCOLS is only supported with cURL 7.19.4
     $uri = parse_url($url);
-    if ($uri['scheme'] != 'http' && $uri['scheme'] != 'https') {
+    if (isset($uri['scheme']) && $uri['scheme'] != 'http' && $uri['scheme'] != 'https') {
       $result->error = 'invalid schema '. $uri['scheme'];
       $result->code = -1003; // This corresponds to drupal_http_request()
     }
diff --git a/mappers/content.inc b/mappers/content.inc
deleted file mode 100644
index 416cfd10..00000000
--- a/mappers/content.inc
+++ /dev/null
@@ -1,59 +0,0 @@
-<?php
-// $Id$
-
-/**
- * @file
- * On behalf implementation of Feeds mapping API for content.module (CCK).
- */
-
-/**
- * Implements hook_feeds_node_processor_targets_alter().
- *
- * @see FeedsNodeProcessor::getMappingTargets().
- */
-function content_feeds_node_processor_targets_alter(&$targets, $content_type) {
-  $info = content_types($content_type);
-  $fields = array();
-  if (isset($info['fields']) && count($info['fields'])) {
-    foreach ($info['fields'] as $field_name => $field) {
-      if (in_array($field['type'], array('text', 'number_integer', 'number_decimal', 'number_float'))) {
-        $fields[$field_name] = isset($field['widget']['label']) ? $field['widget']['label'] : $field_name;
-      }
-    }
-  }
-  foreach ($fields as $k => $name) {
-    $targets[$k] = array(
-      'name' => $name,
-      'callback' => 'content_feeds_set_target',
-      'description' => t('The CCK !name field of the node.', array('!name' => $name)),
-    );
-  }
-}
-
-/**
- * Callback for mapping. Here is where the actual mapping happens.
- *
- * When the callback is invoked, $target contains the name of the field the
- * user has decided to map to and $value contains the value of the feed item
- * element the user has picked as a source.
- */
-function content_feeds_set_target($node, $target, $value) {
-
-  $field = isset($node->$target) ? $node->$target : array();
-
-  // Handle multiple value fields.
-  if (is_array($value)) {
-    $i = 0;
-    foreach ($value as $v) {
-      if (!is_array($v) && !is_object($v)) {
-        $field[$i]['value'] = $v;
-      }
-      $i++;
-    }
-  }
-  else {
-    $field[0]['value'] = $value;
-  }
-
-  $node->$target = $field;
-}
diff --git a/mappers/field.inc b/mappers/field.inc
new file mode 100644
index 00000000..40341646
--- /dev/null
+++ b/mappers/field.inc
@@ -0,0 +1,71 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * On behalf implementation of Feeds mapping API for field.module.
+ *
+ * Does actually not include mappers for field types defined in fields module
+ * (because there aren't any) but mappers for all fields that contain their
+ * value simply in $entity->fieldname['und'][$i]['value'].
+ */
+
+/**
+ * Implements hook_feeds_processor_targets_alter().
+ *
+ * @see FeedsNodeProcessor::getMappingTargets().
+ */
+function field_feeds_processor_targets_alter(&$targets, $entity_type, $content_type) {
+  foreach (field_info_instances($entity_type, $content_type) as $name => $instance) {
+    $info = field_info_field($name);
+    $allowed_types = array(
+      'number_integer',
+      'number_decimal',
+      'number_float',
+      'text',
+      'text_long',
+      'text_with_summary',
+    );
+    if (in_array($info['type'], $allowed_types)) {
+      $targets[$name] = array(
+        'name' => $instance['label'],
+        'callback' => 'field_feeds_set_target',
+        'description' => t('The @label field of the node.', array('@label' => $instance['label'])),
+      );
+    }
+  }
+}
+
+/**
+ * Callback for mapping. Here is where the actual mapping happens.
+ *
+ * When the callback is invoked, $target contains the name of the field the
+ * user has decided to map to and $value contains the value of the feed item
+ * element the user has picked as a source.
+ */
+function field_feeds_set_target($entity, $target, $value) {
+  if (empty($value)) {
+    return;
+  }
+
+  // Handle non-multiple value fields.
+  if (!is_array($value)) {
+    $value = array($value);
+  }
+
+  $info = field_info_field($target);
+
+  // Iterate over all values.
+  $i = 0;
+  $field = isset($entity->$target) ? $entity->$target : array();
+  foreach ($value as $v) {
+    if (!is_array($v) && !is_object($v)) {
+      $field['und'][$i]['value'] = $v;
+    }
+    if ($info['cardinality'] == 1) {
+      break;
+    }
+    $i++;
+  }
+  $entity->{$target} = $field;
+}
diff --git a/mappers/file.inc b/mappers/file.inc
new file mode 100644
index 00000000..bf21e980
--- /dev/null
+++ b/mappers/file.inc
@@ -0,0 +1,86 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * On behalf implementation of Feeds mapping API for file.module and
+ * image.module.
+ *
+ * Does actually not include mappers for field types defined in fields module
+ * (because there aren't any) but mappers for all fields that contain their
+ * value simply in $entity->fieldname['und'][$i]['value'].
+ */
+
+/**
+ * Implements hook_feeds_processor_targets_alter().
+ *
+ * @see FeedsNodeProcessor::getMappingTargets().
+ */
+function file_feeds_processor_targets_alter(&$targets, $entity_type, $content_type = '') {
+  foreach (field_info_instances($entity_type, $content_type) as $name => $instance) {
+    $info = field_info_field($name);
+
+    if (in_array($info['type'], array('file', 'image'))) {
+      $targets[$name] = array(
+        'name' => $instance['label'],
+        'callback' => 'file_feeds_set_target',
+        'description' => t('The @label field of the node.', array('@label' => $instance['label'])),
+      );
+    }
+  }
+}
+
+/**
+ * Callback for mapping. Here is where the actual mapping happens.
+ *
+ * When the callback is invoked, $target contains the name of the field the
+ * user has decided to map to and $value contains the value of the feed item
+ * element the user has picked as a source.
+ */
+function file_feeds_set_target($entity, $target, $value) {
+  if (empty($value)) {
+    return;
+  }
+  module_load_include('inc', 'file');
+
+  // Make sure $value is an array of objects of type FeedsEnclosure.
+  if (!is_array($value)) {
+    $value = array($value);
+  }
+  foreach ($value as $k => $v) {
+    if (!($v instanceof FeedsEnclosure)) {
+      if (is_string($v)) {
+        $value[$k] = new FeedsEnclosure($v, 'application/octet-stream');
+      }
+      else {
+        unset($value[$k]);
+      }
+    }
+  }
+  if (empty($value)) {
+    return;
+  }
+
+  // Determine file destination.
+  // @todo This needs review and debugging.
+  $info = field_info_field($target);
+  $bundle_name = $entity->entity_type == 'node' ? $entity->type : $entity->entity_type;
+  $instance_info = field_info_instance($entity->entity_type, $target, $bundle_name);
+  $account = $entity->uid ? user_load($entity->uid) : NULL;
+  $destination = file_field_widget_uri($info, $instance_info, $account);
+
+  // Populate entity.
+  $i = 0;
+  $field = isset($entity->$target) ? $entity->$target : array();
+  foreach ($value as $v) {
+    if ($file = $v->getFile($destination)) {
+      $field['und'][$i] = (array)$file;
+      $field['und'][$i]['display'] = 1; // @todo: Figure out how to properly populate this field.
+      if ($info['cardinality'] == 1) {
+        break;
+      }
+      $i++;
+    }
+  }
+  $entity->{$target} = $field;
+}
diff --git a/mappers/filefield.inc b/mappers/filefield.inc
deleted file mode 100644
index 82e11bb7..00000000
--- a/mappers/filefield.inc
+++ /dev/null
@@ -1,79 +0,0 @@
-<?php
-// $Id$
-
-/**
- * @file
- * On behalf implementation of Feeds mapping API for filefield.module (CCK).
- *
- * @todo Support <em>list</em> subfields (how to provide default value?)
- * @todo Support <em>data</em> subfields (or its own subfieds?)
- */
-
-/**
- * Implements hook_feeds_node_processor_targets_alter()
- */
-function filefield_feeds_node_processor_targets_alter(&$targets, $content_type) {
-  $info = content_types($content_type);
-  $fields = array();
-  if (isset($info['fields']) && count($info['fields'])) {
-    foreach ($info['fields'] as $field_name => $field) {
-      if ($field['type'] == 'filefield') {
-        $name = isset($field['widget']['label']) ? $field['widget']['label'] : $field_name;
-        $targets[$field_name] = array(
-          'name' => $name,
-          'callback' => 'filefield_feeds_set_target',
-          'description' => t('The URL for the CCK @name field of the node.', array('@name' => $name)),
-        );
-      }
-    }
-  }
-}
-
-/**
- * Implements hook_feeds_set_target().
- *
- * @param $node
- *   The target node.
- * @param $field_name
- *   The name of field on the target node to map to.
- * @param $value
- *   The value to be mapped. Should contain a URL or an array of URLs; a
- *   FeedsEnclosure or an array of FeedsEnclosures.
- *
- * @todo: should we support $object->url again?
- */
-function filefield_feeds_set_target($node, $field_name, $value) {
-  // Normalize $value, create an array of FeedsEnclosures of it.
-  $enclosures = array();
-  if (!is_array($value)) {
-    $value = array($value);
-  }
-  foreach ($value as $k => $v) {
-    if ($v instanceof FeedsEnclosure) {
-      $enclosures[] = $v;
-    }
-    elseif (valid_url($v)) {
-      $enclosures[$k] = new FeedsEnclosure($v, 'application/octet-stream');
-    }
-  }
-
-  // Map enclosures.
-  $items = isset($node->$field_name) ? $node->$field_name : array();
-  foreach ($enclosures as $enclosure) {
-    if ($file = $enclosure->getFile()) {
-      $field = content_fields($field_name, $node->type);
-      $target_dir = filefield_widget_file_path($field, user_load($node->uid));
-      $info = field_file_save_file($enclosure->getFile(), array(), $target_dir);
-      if ($info) {
-        $info['list'] = array();
-        $info['data'] = array('description' => '');
-        if ($field['list_field']) {
-          $info['list'] = $field['list_default'];
-        }
-        $items[] = $info;
-        $error = false;
-      }
-    }
-  }
-  $node->$field_name = $items;
-}
diff --git a/mappers/taxonomy.inc b/mappers/taxonomy.inc
index 92405c51..1b3a115a 100644
--- a/mappers/taxonomy.inc
+++ b/mappers/taxonomy.inc
@@ -12,7 +12,7 @@
 function taxonomy_feeds_parser_sources_alter(&$sources, $content_type) {
   if (!empty($content_type)) {
     foreach (taxonomy_get_vocabularies($content_type) as $vocabulary) {
-      $sources['parent:taxonomy:'. taxonomy_vocabulary_id($vocabulary)] = array(
+      $sources['parent:taxonomy:'. $vocabulary->machine_name] = array(
         'name' => t('Feed node: Taxonomy: @vocabulary', array('@vocabulary' => $vocabulary->name)),
         'description' => t('Taxonomy terms from feed node in given vocabulary.'),
         'callback' => 'taxonomy_feeds_get_source',
@@ -27,7 +27,8 @@ function taxonomy_feeds_parser_sources_alter(&$sources, $content_type) {
 function taxonomy_feeds_get_source(FeedsImportBatch $batch, $key) {
   if ($node = $batch->feedNode()) {
     $terms = taxonomy_node_get_terms($node);
-    $vocabulary = taxonomy_get_vocabulary(str_replace('parent:taxonomy:', '', $key));
+    $vocabularies = taxonomy_vocabulary_load_multiple(array(), array('machine_name' => str_replace('parent:taxonomy:', '', $key)));
+    $vocabulary = array_shift($vocabularies);
     $result = array();
     foreach ($terms as $tid => $term) {
       if ($term->vid == $vocabulary->vid) {
@@ -39,142 +40,91 @@ function taxonomy_feeds_get_source(FeedsImportBatch $batch, $key) {
 }
 
 /**
- * Implements hook_feeds_node_processor_targets_alter().
- *
- * @see FeedsNodeProcessor::getMappingTargets().
+ * Implements hook_feeds_processor_targets_alter().
  */
-function taxonomy_feeds_node_processor_targets_alter(&$targets, $content_type) {
-  foreach (taxonomy_get_vocabularies($content_type) as $vocabulary) {
-    $description = t('The @name vocabulary of the node. If this is a "Tags" vocabulary, any new terms will be created on import. Otherwise only existing terms will be used. If this is not a "Tags" vocabulary and not a "Multiple select" vocabulary, only the first available term will be created. See !settings.', array('@name' => $vocabulary->name, '!settings' => l(t('vocabulary settings'), 'admin/content/taxonomy/edit/vocabulary/'. $vocabulary->vid, array('query' => 'destination='. $_GET['q']))));
-
-    $targets['taxonomy:'. taxonomy_vocabulary_id($vocabulary)] = array(
-      'name' => "Taxonomy: ". $vocabulary->name,
-      'callback' => 'taxonomy_feeds_set_target',
-      'description' => $description,
-      'real_target' => 'taxonomy',
-    );
+function taxonomy_feeds_processor_targets_alter(&$targets, $entity_type, $content_type) {
+  foreach (field_info_instances($entity_type, $content_type) as $name => $instance) {
+    $info = field_info_field($name);
+    if ($info['type'] == 'taxonomy_term_reference') {
+      $targets[$name] = array(
+        'name' => $instance['label'],
+        'callback' => 'taxonomy_feeds_set_target',
+        'description' => t('The @label field of the node.', array('@label' => $instance['label'])),
+      );
+    }
   }
 }
 
 /**
  * Callback for mapping. Here is where the actual mapping happens.
  *
- * @param $node
- *   Reference to the node object we are working on.
- *
- * @param $key
- *   A key as added to the $targets array by
- *   taxonomy_feeds_node_processor_targets_alter().
- *
- * @param $terms
- *   Given terms as array.
- *
- * Add the given terms to the node object so the taxonomy module can add them
- * on node_save().
+ * @todo Do not create new terms for non-autotag fields.
  */
-function taxonomy_feeds_set_target(&$node, $key, $terms) {
-
-  // Return if there are no terms.
+function taxonomy_feeds_set_target($entity, $target, $terms) {
   if (empty($terms)) {
     return;
   }
 
-  // Load target vocabulary to check, if it has the "tags" flag.
-  $vocabulary = taxonomy_get_vocabulary(str_replace('taxonomy:', '', $key));
-
-  // Cast a given single string to an array so we can use it.
+  // Handle non-multiple values.
   if (!is_array($terms)) {
     $terms = array($terms);
   }
 
-  if ($vocabulary->tags) {
-    // Simply add a comma separated list to the node for a "tags" vocabulary.
-    $terms = array_merge($terms, drupal_explode_tags($node->taxonomy['tags'][$vocabulary->vid]));
-    $node->taxonomy['tags'][$vocabulary->vid] = implode(',', $terms);
+  $info = field_info_field($target);
+
+  // See http://drupal.org/node/881530
+  if (isset($info['settings']['allowed_values'][0]['vocabulary'])) {
+    $vocabulary = taxonomy_vocabulary_machine_name_load($info['settings']['allowed_values'][0]['vocabulary']);
   }
   else {
-    foreach ($terms as $term) {
-      if ($term instanceof FeedsTermElement) {
-        $node->taxonomy[$term->tid] = (object)$term;
-      }
-      // Check if a term already exists.
-      elseif ($terms_found = taxonomy_get_term_by_name_vid($term, $vocabulary->vid)) {
-        // If any terms are found add them to the node's taxonomy by found tid.
-        foreach ($terms_found AS $term_found) {
-          $node->taxonomy[$term_found->tid] = $term_found;
-          if (!$vocabulary->multiple) {
-            break;
-          }
-        }
-      }
-      // If the vocab is not for multiple tags break after the first hit.
-      if (!$vocabulary->multiple) {
-        break;
-      }
-    }
+    $vocabulary = taxonomy_vocabulary_load($info['settings']['allowed_values'][0]['vid']);
   }
-}
 
-/**
- * Try to map a string to an existing term by name and vocabulary id.
- *
- * Provides a case-insensitive and trimmed mapping, to maximize the likelihood
- * of a successful match limited by a vocabulary id.
- *
- * @param $name
- *   Name of the term to search for.
- *
- * @param $vid
- *   The vocabulary's ID.
- *
- * @return
- *   An array of matching term objects.
- */
-function taxonomy_get_term_by_name_vid($name, $vid) {
-  $db_result = db_query(db_rewrite_sql("SELECT t.tid, t.name FROM {term_data} t WHERE LOWER(t.name) = LOWER('%s') AND t.vid = %d", 't', 'tid'), trim($name), $vid);
-  $result = array();
-  while ($term = db_fetch_object($db_result)) {
-    $result[] = $term;
-  }
-  return $result;
-}
+  $i = 0;
+  $entity->$target = isset($entity->$target) ? $entity->$target : array();
+  foreach ($terms as $term) {
+    $tid = 0;
+    if ($term instanceof FeedsTermElement) {
+      $tid = $term->tid;
+    }
+    elseif (is_numeric($term)) {
+      $tid = $term;
+    }
+    elseif (is_string($term)) {
+      $tid = taxonomy_term_check_term($term, $vocabulary->vid);
+    }
+    if ($tid) {
+      $entity->{$target}['und'][$i]['tid'] = $tid;
+    }
 
-/**
- * Look up a vocabulary by vid or module name.
- *
- * @param $id
- *   A module name or a numeric vocabulary id.
- *
- * @return
- *   An object of type stdClass that represents a vocabulary.
- */
-function taxonomy_get_vocabulary($id) {
-  static $vocabularies;
-  if (!isset($vocabularies[$id])) {
-    foreach (taxonomy_get_vocabularies() as $vocabulary) {
-      if ($vocabulary->vid == $id) {
-        $vocabularies[$id] = $vocabulary;
-        break;
-      }
-      elseif ($vocabulary->module == $id) {
-        $vocabularies[$id] = $vocabulary;
-        break;
-      }
+    if ($info['cardinality'] == 1) {
+      break;
     }
+    $i++;
   }
-  return $vocabularies[$id];
 }
 
 /**
- * Return the vocabulary identifier, the vocabulary's vid or module.
+ * Checks whether a term identified by name and vocabulary exists. Creates a
+ * new term if it does not exist.
+ *
+ * @param $name
+ *   A term name.
+ * @param $vid
+ *   A vocabulary id.
  *
  * @return
- *   Vocabulary's module name if it is a features vocabulary (= exportable),
- *   vocabulary's vid otherwise.
+ *   A term id.
  */
-function taxonomy_vocabulary_id($vocabulary) {
-  if (strpos($vocabulary->module, 'features_') === 0) {
-    return $vocabulary->module;
+function taxonomy_term_check_term($name, $vid) {
+  $terms = taxonomy_term_load_multiple(array(), array('name' => $name, 'vid' => $vid));
+  if (empty($terms)) {
+    $term = new stdClass();
+    $term->name = $name;
+    $term->vid = $vid;
+    taxonomy_term_save($term);
+    return $term->tid;
   }
-  return $vocabulary->vid;
+  $term = reset($terms);
+  return $term->tid;
 }
diff --git a/plugins/FeedsFileFetcher.inc b/plugins/FeedsFileFetcher.inc
index 05a07355..00bf398e 100644
--- a/plugins/FeedsFileFetcher.inc
+++ b/plugins/FeedsFileFetcher.inc
@@ -54,18 +54,11 @@ class FeedsFileFetcher extends FeedsFetcher {
    * Source form.
    */
   public function sourceForm($source_config) {
-    $form = $info = array();
-    if (!empty($source_config['source']) && file_exists($source_config['source'])) {
-      $wrapper = file_stream_wrapper_get_instance_by_uri($source_config['source']);
-      $path = $wrapper->getExternalUrl();
-      $info = array(
-        'path' => $path,
-        'size' => filesize($source_config['source']),
-      );
-      if (module_exists('mimedetect')) {
-        $info['mime'] = mimedetect_mime($source_config['source']);
-      }
-    }
+    $form = array();
+    $form['fid'] = array(
+      '#type' => 'value',
+      '#value' => empty($source_config['fid']) ? 0 : $source_config['fid'],
+    );
     $form['source'] = array(
       '#type' => empty($this->config['direct']) ? 'value' : 'textfield',
       '#title' => t('File'),
@@ -77,7 +70,7 @@ class FeedsFileFetcher extends FeedsFetcher {
       '#title' => empty($this->config['direct']) ? t('File') : NULL,
       '#description' => empty($source_config['source']) ? t('Select the file to be imported from your local system.') : t('Select a different file to be imported from your local system.'),
       '#theme' => 'feeds_upload',
-      '#file_info' => $info,
+      '#file_info' => empty($source_config['fid']) ? NULL : file_load($source_config['fid']),
       '#size' => 10,
     );
     return $form;
@@ -92,8 +85,10 @@ class FeedsFileFetcher extends FeedsFetcher {
 
     // If there is a file uploaded, save it, otherwise validate input on
     // file.
+    // @todo: Track usage of file, remove file when removing source.
     if ($file = file_save_upload('feeds', array('file_validate_extensions' => array(0 => $this->config['allowed_extensions'])), $feed_dir)) {
       $values['source'] = $file->uri;
+      $values['file'] = $file;
     }
     elseif (empty($values['source'])) {
       form_set_error('feeds][source', t('Upload a file first.'));
@@ -106,6 +101,39 @@ class FeedsFileFetcher extends FeedsFetcher {
     }
   }
 
+  /**
+   * Override parent::sourceSave().
+   */
+  public function sourceSave(FeedsSource $source) {
+    $source_config = $source->getConfigFor($this);
+
+    // If a new file is present, delete the old one and replace it with the new
+    // one.
+    if (isset($source_config['file'])) {
+      $file = $source_config['file'];
+      if (isset($source_config['fid'])) {
+        $this->deleteFile($source_config['fid'], $source->feed_nid);
+      }
+      $file->status = FILE_STATUS_PERMANENT;
+      file_save($file);
+      file_usage_add($file, 'feeds', get_class($this), $source->feed_nid);
+
+      $source_config['fid'] = $file->fid;
+      unset($source_config['file']);
+      $source->setConfigFor($this, $source_config);
+    }
+  }
+
+  /**
+   * Override parent::sourceDelete().
+   */
+  public function sourceDelete(FeedsSource $source) {
+    $source_config = $source->getConfigFor($this);
+    if (isset($source_config['fid'])) {
+      $this->deleteFile($source_config['fid'], $source->feed_nid);
+    }
+  }
+
   /**
    * Override parent::configDefaults().
    */
@@ -135,4 +163,14 @@ class FeedsFileFetcher extends FeedsFetcher {
     );
     return $form;
   }
+
+  /**
+   * Helper. Deletes a file.
+   */
+  protected function deleteFile($fid, $feed_nid) {
+    if ($file = file_load($fid)) {
+      file_usage_delete($file, 'feeds', get_class($this), $feed_nid);
+      file_delete($file);
+    }
+  }
 }
diff --git a/plugins/FeedsNodeProcessor.inc b/plugins/FeedsNodeProcessor.inc
index 8e1dc478..6167d90d 100644
--- a/plugins/FeedsNodeProcessor.inc
+++ b/plugins/FeedsNodeProcessor.inc
@@ -96,7 +96,7 @@ class FeedsNodeProcessor extends FeedsProcessor {
     $nids = array();
     $nodes = db_query_range("SELECT n.nid FROM {node} n JOIN {feeds_node_item} fn ON n.nid = fn.nid WHERE fn.id = :id AND fn.feed_nid = :nid", 0, $count, array(':id' => $source->id, ':nid' => $source->feed_nid));
     foreach ($nodes as $node) {
-      $nids[] = $node->nid;
+      $nids[$node->nid] = $node->nid;
       $batch->deleted++;
     }
     node_delete_multiple($nids);
@@ -295,7 +295,7 @@ class FeedsNodeProcessor extends FeedsProcessor {
 
     // Let other modules expose mapping targets.
     self::loadMappers();
-    drupal_alter('feeds_node_processor_targets', $targets, $this->config['content_type']);
+    feeds_alter('feeds_processor_targets', $targets, 'node', $this->config['content_type']);
 
     return $targets;
   }
@@ -358,11 +358,10 @@ class FeedsNodeProcessor extends FeedsProcessor {
       $node->feeds_node_item->guid = '';
     }
 
-    static $included;
-    if (!$included) {
-      module_load_include('inc', 'node', 'node.pages');
-      $included = TRUE;
-    }
+    // Give mappers a hint at what they're operating on.
+    $node->entity_type = 'node';
+
+    // Let other modules populate default values.
     node_object_prepare($node);
 
     // Populate properties that are set by node_object_prepare().
diff --git a/plugins/FeedsParser.inc b/plugins/FeedsParser.inc
index 254f2897..4319a5fb 100644
--- a/plugins/FeedsParser.inc
+++ b/plugins/FeedsParser.inc
@@ -49,7 +49,7 @@ abstract class FeedsParser extends FeedsPlugin {
   public function getMappingSources() {
     self::loadMappers();
     $sources = array();
-    drupal_alter('feeds_parser_sources', $sources, feeds_importer($this->id)->config['content_type']);
+    feeds_alter('feeds_parser_sources', $sources, feeds_importer($this->id)->config['content_type']);
     if (!feeds_importer($this->id)->config['content_type']) {
       return $sources;
     }
@@ -204,15 +204,6 @@ class FeedsEnclosure extends FeedsElement {
     $this->mime_type = $mime_type;
   }
 
-  /**
-   * Destructor, clean up any temporary files.
-   */
-  public function __destruct() {
-    if (!empty($this->file)) {
-      file_delete($this->file);
-    }
-  }
-
   /**
    * @return
    *   MIME type of return value of getValue().
@@ -235,41 +226,51 @@ class FeedsEnclosure extends FeedsElement {
   }
 
   /**
+   * Get a Drupal file object of the enclosed resource, download if necessary.
+   *
+   * @param $destination
+   *   The path or uri specifying the target directory in which the file is
+   *   expected. If not given, a previously defined directory will be assumed.
+   *   The default directory is tmp.
+   *
    * @return
-   *   A temporary file path to the downloaded resource referenced by the
-   *   enclosure. Downloads resource if not downloaded yet. The file path is
-   *   valid for the time of the page load.
+   *   A file path to the enclosed resource.
    *
-   * @todo Get file extension from mime_type.
-   * @todo This is not concurrency safe.
+   * @todo file_destination(*, FILE_EXISTS_RENAME) is not concurrency safe.
    */
-  public function getFile() {
-    if(empty($this->file) && $this->getValue()) {
-      // Check if this enclosure contains a local file.
-      if (!parse_url($this->getValue(), PHP_URL_SCHEME)) {
-        if (strpos($this->getValue(), 'public://') === 0) {
-          if (file_exists($this->getValue())) {
-            $this->file = $this->getValue();
-            return $this->file;
-          }
-        }
-        throw new Exception(t('Invalid enclosure %enclosure', array('%enclosure' => $this->getValue())));
-      }
-      $filename = basename($this->getValue());
-      if (module_exists('transliteration')) {
-        require_once (drupal_get_path('module', 'transliteration') . '/transliteration.inc');
-        $filename = transliteration_clean_filename($filename);
+  public function getFile($destination) {
+
+    if (empty($this->file) && $this->getValue()) {
+
+      // File is in the right place, nothing to do.
+      if (strpos($this->getValue(), $directory) === 0) {
+        $this->file = $this->getValue();
+        return $this->file;
       }
-      $dest = file_destination(file_directory_temp() .'/'. $filename, FILE_EXISTS_RENAME);
-      if (ini_get('allow_url_fopen')) {
-        $this->file = copy($this->getValue(), $dest) ? $dest : 0;
+
+      // Prepare destination directory.
+      file_prepare_directory($destination, FILE_MODIFY_PERMISSIONS | FILE_CREATE_DIRECTORY);
+
+      // Copy or save file depending its location.
+      if (drupal_realpath($this->getValue())) {
+        $file         = new stdClass();
+        $file->uid    = $user->uid;
+        $file->status = FILE_STATUS_PERMANENT;
+        $file->uri    = $this->getValue();
+        $this->file = file_copy($file, $destination);
       }
       else {
-        $file = file_save_data($this->getContent(), $dest);
-        $this->file = $file ? $file->uri : 0;
+        $filename = basename($this->getValue());
+        if (module_exists('transliteration')) {
+          require_once (drupal_get_path('module', 'transliteration') . '/transliteration.inc');
+          $filename = transliteration_clean_filename($filename);
+        }
+        $this->file = file_save_data($this->getContent(), "$destination/$filename");
       }
-      if ($this->file === 0) {
-        throw new Exception(t('Cannot write content to %dest', array('%dest' => $dest)));
+
+      // We couldn't make sense of this enclosure, throw an exception.
+      if (!$this->file) {
+        throw new Exception(t('Invalid enclosure %enclosure', array('%enclosure' => $this->getValue())));
       }
     }
     return $this->file;
diff --git a/plugins/FeedsPlugin.inc b/plugins/FeedsPlugin.inc
index debc4eec..3e394fd1 100644
--- a/plugins/FeedsPlugin.inc
+++ b/plugins/FeedsPlugin.inc
@@ -83,6 +83,8 @@ abstract class FeedsPlugin extends FeedsConfigurable implements FeedsSourceInter
    *
    * @see FeedsNodeProcessor::map()
    * @see FeedsUserProcessor::map()
+   *
+   * @todo: Use CTools Plugin API.
    */
   protected static function loadMappers() {
     static $loaded = FALSE;
@@ -90,15 +92,107 @@ abstract class FeedsPlugin extends FeedsConfigurable implements FeedsSourceInter
       $path = drupal_get_path('module', 'feeds') .'/mappers';
       $files = drupal_system_listing('/.*\.inc$/', $path, 'name', 0);
       foreach ($files as $file) {
-        if (strstr($file->filename, '/mappers/')) {
-          require_once("./$file->filename");
+        if (strstr($file->uri, '/mappers/')) {
+          require_once("./$file->uri");
         }
       }
-      // Rebuild cache.
-      module_implements('', FALSE, TRUE);
     }
     $loaded = TRUE;
   }
+
+  /**
+   * Get all available plugins.
+   */
+  public static function all() {
+    ctools_include('plugins');
+    $plugins = ctools_get_plugins('feeds', 'plugins');
+
+    $result = array();
+    foreach ($plugins as $key => $info) {
+      if (!empty($info['hidden'])) {
+        continue;
+      }
+      $result[$key] = $info;
+    }
+
+    // Sort plugins by name and return.
+    uasort($result, 'feeds_plugin_compare');
+    return $result;
+  }
+
+  /**
+   * Determines whether given plugin is derived from given base plugin.
+   *
+   * @param $plugin_key
+   *   String that identifies a Feeds plugin key.
+   * @param $parent_plugin
+   *   String that identifies a Feeds plugin key to be tested against.
+   *
+   * @return
+   *   TRUE if $parent_plugin is directly *or indirectly* a parent of $plugin,
+   *   FALSE otherwise.
+   */
+  public static function child($plugin_key, $parent_plugin) {
+    ctools_include('plugins');
+    $plugins = ctools_get_plugins('feeds', 'plugins');
+    $info = $plugins[$plugin_key];
+
+    if (empty($info['handler']['parent'])) {
+      return FALSE;
+    }
+    elseif ($info['handler']['parent'] == $parent_plugin) {
+      return TRUE;
+    }
+    else {
+      return self::child($info['handler']['parent'], $parent_plugin);
+    }
+  }
+
+  /**
+   * Determines the type of a plugin.
+   *
+   * @todo PHP5.3: Implement self::type() and query with $plugin_key::type().
+   *
+   * @param $plugin_key
+   *   String that identifies a Feeds plugin key.
+   *
+   * @return
+   *   One of the following values:
+   *   'fetcher' if the plugin is a fetcher
+   *   'parser' if the plugin is a parser
+   *   'processor' if the plugin is a processor
+   *   FALSE otherwise.
+   */
+  public static function typeOf($plugin_key) {
+    if (self::child($plugin_key, 'FeedsFetcher')) {
+      return 'fetcher';
+    }
+    elseif (self::child($plugin_key, 'FeedsParser')) {
+      return 'parser';
+    }
+    elseif (self::child($plugin_key, 'FeedsProcessor')) {
+      return 'processor';
+    }
+    return FALSE;
+  }
+
+  /**
+   * Gets all available plugins of a particular type.
+   *
+   * @param $type
+   *   'fetcher', 'parser' or 'processor'
+   */
+  public static function byType($type) {
+    $plugins = self::all();
+
+    $result = array();
+    foreach ($plugins as $key => $info) {
+      if ($type == self::typeOf($key)) {
+        $result[$key] = $info;
+      }
+    }
+    return $result;
+  }
 }
 
 /**
@@ -109,3 +203,10 @@ class FeedsMissingPlugin extends FeedsPlugin {
     return array();
   }
 }
+
+/**
+ * Sort callback for FeedsPlugin::all().
+ */
+function feeds_plugin_compare($a, $b) {
+  return strcasecmp($a['name'], $b['name']);
+}
diff --git a/plugins/FeedsProcessor.inc b/plugins/FeedsProcessor.inc
index 4dd3f172..7ee9a482 100644
--- a/plugins/FeedsProcessor.inc
+++ b/plugins/FeedsProcessor.inc
@@ -101,10 +101,6 @@ abstract class FeedsProcessor extends FeedsPlugin {
     // Many mappers add to existing fields rather than replacing them. Hence we
     // need to clear target elements of each item before mapping in case we are
     // mapping on a prepopulated item such as an existing node.
-    if (is_array($target_item)) {
-      $target_item = (object)$target_item;
-      $convert_to_array = TRUE;
-    }
     foreach ($this->config['mappings'] as $mapping) {
       if (isset($targets[$mapping['target']]['real_target'])) {
         unset($target_item->{$targets[$mapping['target']]['real_target']});
@@ -113,9 +109,6 @@ abstract class FeedsProcessor extends FeedsPlugin {
         unset($target_item->{$mapping['target']});
       }
     }
-    if ($convert_to_array) {
-      $target_item = (array)$target_item;
-    }
 
     /*
     This is where the actual mapping happens: For every mapping we envoke
@@ -194,8 +187,8 @@ abstract class FeedsProcessor extends FeedsPlugin {
    *
    * @ingroup mappingapi
    */
-  public function setTargetElement(&$target_item, $target_element, $value) {
-    $target_item[$target_element] = $value;
+  public function setTargetElement($target_item, $target_element, $value) {
+    $target_item->$target_element = $value;
   }
 
   /**
diff --git a/plugins/FeedsSimplePieParser.inc b/plugins/FeedsSimplePieParser.inc
index 18875688..548c46de 100644
--- a/plugins/FeedsSimplePieParser.inc
+++ b/plugins/FeedsSimplePieParser.inc
@@ -211,7 +211,7 @@ class FeedsSimplePieParser extends FeedsParser {
    * Returns cache directory. Creates it if it doesn't exist.
    */
   protected function cacheDirectory() {
-    $directory = file_directory_path() .'/simplepie';
+    $directory = 'public://simplepie';
     file_prepare_directory($dir, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
     return $directory;
   }
diff --git a/plugins/FeedsTermProcessor.inc b/plugins/FeedsTermProcessor.inc
index 8bc9787e..92558a2a 100644
--- a/plugins/FeedsTermProcessor.inc
+++ b/plugins/FeedsTermProcessor.inc
@@ -28,28 +28,29 @@ class FeedsTermProcessor extends FeedsProcessor {
       if (!($tid = $this->existingItemId($batch, $source)) || $this->config['update_existing'] != FEEDS_SKIP_EXISTING) {
 
         // Map item to a term.
-        $term = array();
+        $term = new stdClass();
         if ($tid && $this->config['update_existing'] == FEEDS_UPDATE_EXISTING) {
-          $term = (array) taxonomy_get_term($tid, TRUE);
+          $term = taxonomy_term_load($tid);
           $term = module_invoke_all('feeds_taxonomy_load', $term);
         }
+        $term->entity_type = 'taxonomy_term';
         $term = $this->map($batch, $term, $source->feed_nid);
 
         // Check if term name is set, otherwise continue.
-        if (empty($term['name'])) {
+        if (empty($term->name)) {
           $no_name++;
           continue;
         }
 
         // Add term id if available.
         if (!empty($tid)) {
-          $term['tid'] = $tid;
+          $term->tid = $tid;
         }
 
         // Save the term.
-        $term['feeds_importer_id'] = $this->id;
-        $term['feed_nid'] = $this->feed_nid;
-        taxonomy_save_term($term);
+        $term->feeds_importer_id = $this->id;
+        $term->feed_nid = $source->feed_nid;
+        taxonomy_term_save($term);
         if ($tid) {
           $updated++;
         }
@@ -90,7 +91,7 @@ class FeedsTermProcessor extends FeedsProcessor {
     $deleted = 0;
     $vocabulary = $this->vocabulary();
     $terms = db_query("SELECT td.tid
-                        FROM {term_data} td
+                        FROM {taxonomy_term_data} td
                         JOIN {feeds_term_item} ft ON td.tid = ft.tid
                         WHERE td.vid = :vid
                         AND ft.id = :id
@@ -101,7 +102,7 @@ class FeedsTermProcessor extends FeedsProcessor {
                           ':feed_nid' => $source->feed_nid,
                         ));
     foreach ($terms as $term) {
-      if (taxonomy_del_term($term->tid) == SAVED_DELETED) {
+      if (taxonomy_term_delete($term->tid) == SAVED_DELETED) {
         $deleted++;
       }
     }
@@ -120,17 +121,13 @@ class FeedsTermProcessor extends FeedsProcessor {
   protected function map(FeedsImportBatch $batch, $target_term = NULL) {
     // Prepare term object, have parent class do the iterating.
     if (!$target_term) {
-      $target_term = array();
+      $target_term = new stdClass();
     }
     if (!$vocabulary = $this->vocabulary()) {
       throw new Exception(t('No vocabulary specified for term processor'));
     }
-    $target_term['vid'] = $vocabulary->vid;
+    $target_term->vid = $vocabulary->vid;
     $target_term = parent::map($batch, $target_term);
-    // Taxonomy module expects synonyms to be supplied as a single string.
-    if (isset($target_term['synonyms']) && is_array(isset($target_term['synonyms']))) {
-      $target_term['synonyms'] = implode("\n", $target_term['synonyms']);
-    }
     return $target_term;
   }
 
@@ -150,13 +147,8 @@ class FeedsTermProcessor extends FeedsProcessor {
    */
   public function configForm(&$form_state) {
     $options = array(0 => t('Select a vocabulary'));
-    foreach (taxonomy_get_vocabularies() as $vid => $vocab) {
-      if (strpos($vocab->module, 'features_') === 0) {
-        $options[$vocab->module] = $vocab->name;
-      }
-      else {
-        $options[$vid] = $vocab->name;
-      }
+    foreach (taxonomy_get_vocabularies() as $vocab) {
+      $options[$vocab->machine_name] = check_plain($vocab->name);
     }
     $form = array();
     $form['vocabulary'] = array(
@@ -203,14 +195,12 @@ class FeedsTermProcessor extends FeedsProcessor {
         'name' => t('Term description'),
         'description' => t('Description of the taxonomy term.'),
        ),
-      'synonyms' => array(
-        'name' => t('Term synonyms'),
-        'description' => t('One synonym or an array of synonyms of the taxonomy term.'),
-       ),
     );
     // Let implementers of hook_feeds_term_processor_targets() add their targets.
-    $vocabulary = $this->vocabulary();
-    drupal_alter('feeds_term_processor_targets', $targets, $vocabulary->vid);
+    if ($vocabulary = $this->vocabulary()) {
+      self::loadMappers();
+      feeds_alter('feeds_processor_targets', $targets, 'taxonomy_term', $vocabulary->machine_name);
+    }
     return $targets;
   }
 
@@ -223,7 +213,7 @@ class FeedsTermProcessor extends FeedsProcessor {
     foreach ($this->uniqueTargets($batch) as $target => $value) {
       if ($target == 'name') {
         $vocabulary = $this->vocabulary();
-        if ($tid = db_query("SELECT tid FROM {term_data} WHERE name = :name AND vid = :vid", array(':name' => $value, ':vid' => $vocabulary->vid))->fetchField()) {
+        if ($tid = db_query("SELECT tid FROM {taxonomy_term_data} WHERE name = :name AND vid = :vid", array(':name' => $value, ':vid' => $vocabulary->vid))->fetchField()) {
           return $tid;
         }
       }
@@ -233,20 +223,23 @@ class FeedsTermProcessor extends FeedsProcessor {
 
   /**
    * Return vocabulary to map to.
-   *
-   * Feeds supports looking up vocabularies by their module name as part of an
-   * effort to use the vocabulary.module field as machine name to make
-   * vocabularies exportable.
    */
   public function vocabulary() {
-    $vocabularies = taxonomy_get_vocabularies();
+    // Legacy handling for old feeds importers.
     if (is_numeric($this->config['vocabulary'])) {
-      return $vocabularies[$this->config['vocabulary']];
+      $vocabularies = taxonomy_get_vocabularies();
+      return isset($vocabularies[$this->config['vocabulary']]) ? $vocabularies[$this->config['vocabulary']] : NULL;
     }
     else {
-      foreach ($vocabularies as $vocabulary) {
-        if ($vocabulary->module == $this->config['vocabulary']) {
-          return $vocabulary;
+      if ($vocabulary = taxonomy_vocabulary_machine_name_load($this->config['vocabulary'])) {
+        return $vocabulary;
+      }
+      else {
+        $vocabularies = taxonomy_get_vocabularies();
+        foreach ($vocabularies as $vocabulary) {
+          if ($vocabulary->module == $this->config['vocabulary']) {
+            return $vocabulary;
+          }
         }
       }
     }
diff --git a/plugins/FeedsUserProcessor.inc b/plugins/FeedsUserProcessor.inc
index 1f187b6f..ab9ffe54 100644
--- a/plugins/FeedsUserProcessor.inc
+++ b/plugins/FeedsUserProcessor.inc
@@ -98,6 +98,7 @@ class FeedsUserProcessor extends FeedsProcessor {
     // Prepare term object.
     $target_account = new stdClass();
     $target_account->uid = 0;
+    $target_account->entity_type = 'user';
     $target_account->roles = array_filter($this->config['roles']);
     $target_account->status = $this->config['status'];
 
@@ -189,7 +190,7 @@ class FeedsUserProcessor extends FeedsProcessor {
 
     // Let other modules expose mapping targets.
     self::loadMappers();
-    drupal_alter('feeds_user_processor_targets', $targets);
+    feeds_alter('feeds_processor_targets', $targets, 'user');
 
     return $targets;
   }
diff --git a/tests/feeds.test.inc b/tests/feeds.test.inc
index 9b9a2354..d057befa 100644
--- a/tests/feeds.test.inc
+++ b/tests/feeds.test.inc
@@ -10,14 +10,10 @@
  * Test basic Data API functionality.
  */
 class FeedsWebTestCase extends DrupalWebTestCase {
-
-  public function __construct($test_id = NULL) {
-    parent::__construct($test_id);
-    // Force the feeds test profile. This unfortunately an either-or decision:
-    // either Feeds tests run in feeds_test profile or they run in another
-    // profile. Both is not possible. See http://drupal.org/node/911354
-    $this->profile = 'feeds_test';
-  }
+  // Force the feeds test profile. This unfortunately an either-or decision:
+  // either Feeds tests run in feeds_test profile or they run in another
+  // profile. Both is not possible. See http://drupal.org/node/911354
+  protected $profile = 'feeds_test';
 
   /**
    * Debug utility. Shows current screen.
@@ -118,7 +114,7 @@ class FeedsWebTestCase extends DrupalWebTestCase {
    *   feeds_feeds_plugins()).
    */
   public function setPlugin($id, $plugin_key) {
-    if ($type = feeds_plugin_type($plugin_key)) {
+    if ($type = FeedsPlugin::typeOf($plugin_key)) {
       $edit = array(
         'plugin_key' => $plugin_key,
       );
@@ -161,9 +157,14 @@ class FeedsWebTestCase extends DrupalWebTestCase {
     if (empty($feed_url)) {
       $feed_url = $GLOBALS['base_url'] .'/'. drupal_get_path('module', 'feeds') .'/tests/feeds/developmentseed.rss2';
     }
+
     // If content type not given, retrieve it.
     if (!$content_type) {
-      $config = unserialize(db_query("SELECT config FROM {feeds_importer} WHERE id = :id", array(':id' => $id))->fetchField());
+      $result= db_select('feeds_importer', 'f')
+        ->condition('f.id', $id, '=')
+        ->fields('f', array('config'))
+        ->execute();
+      $config = unserialize($result->fetchField());
       $content_type = $config['content_type'];
       $this->assertFalse(empty($content_type), 'Valid content type found: '. $content_type);
     }
@@ -180,8 +181,18 @@ class FeedsWebTestCase extends DrupalWebTestCase {
     $nid = $this->getNid($this->getUrl());
 
     // Check whether feed got recorded in feeds_source table.
-    $this->assertEqual(1, db_query("SELECT COUNT(*) FROM {feeds_source} WHERE id = :id AND feed_nid = :feed_nid", array(':id'=> $id, ':feed_nid' => $nid))->fetchField());
-    $source = db_query("SELECT * FROM {feeds_source} WHERE id = :id AND feed_nid = :feed_nid", array(':id'=> $id, ':feed_nid' => $nid))->fetch();
+    $query = db_select('feeds_source', 's')
+      ->condition('s.id', $id, '=')
+      ->condition('s.feed_nid', $nid, '=');
+    $query->addExpression("COUNT(*)");
+    $result = $query->execute()->fetchField();
+    $this->assertEqual(1, $result);
+
+    $source = db_select('feeds_source', 's')
+      ->condition('s.id', $id, '=')
+      ->condition('s.feed_nid', $nid, '=')
+      ->fields('s', array('config'))
+      ->execute()->fetch();
     $config = unserialize($source->config);
     $this->assertEqual($config['FeedsHTTPFetcher']['source'], $feed_url, t('URL in DB correct.'));
     return $nid;
@@ -207,7 +218,11 @@ class FeedsWebTestCase extends DrupalWebTestCase {
     $this->assertText('has been updated.');
 
     // Check that the URL was updated in the feeds_source table.
-    $source = db_query("SELECT * FROM {feeds_source} WHERE feed_nid = :nid", array(':nid' => $nid))->fetch();
+    //$source = db_query("SELECT * FROM {feeds_source} WHERE feed_nid = :nid", array(':nid' => $nid))->fetch();
+    $source = db_select('feeds_source', 's')
+      ->condition('s.feed_nid', $nid, '=')
+      ->fields('s', array('config'))
+      ->execute()->fetch();
     $config = unserialize($source->config);
     $this->assertEqual($config['FeedsHTTPFetcher']['source'], $feed_url, t('URL in DB correct.'));
   }
diff --git a/tests/feeds_processor_node.test b/tests/feeds_processor_node.test
index 188a4e8f..823269f3 100644
--- a/tests/feeds_processor_node.test
+++ b/tests/feeds_processor_node.test
@@ -30,25 +30,25 @@ class FeedsRSStoNodesTest extends FeedsWebTestCase {
    */
   public function setUp() {
     parent::setUp('feeds', 'feeds_ui', 'ctools', 'job_scheduler');
-    $this->drupalLogin(
-      $this->drupalCreateUser(
-        array(
-          'administer feeds', 'administer nodes', 'administer content types',
-        )
-      )
-    );
+    $user = $this->drupalCreateUser(array(
+      'administer site configuration',
+      'administer feeds',
+      'administer nodes',
+      'administer content types',
+      'create page content',
+      'edit any page content',
+    ));
+    $this->drupalLogin($user);
   }
 
   /**
    * Test node creation, refreshing/deleting feeds and feed items.
    */
   public function test() {
-    // Set the teaser length to unlimited otherwise tests looking for text on
-    // nodes will fail.
-    $edit = array(
-      'teaser_length' => 0,
-    );
-    $this->drupalPost('admin/content/node-settings', $edit, 'Save configuration');
+    // Set the teaser length display to unlimited otherwise tests looking for
+    // text on nodes will fail.
+    $edit['fields[body][type]'] = 'text_default';
+    $this->drupalPost('admin/structure/types/manage/article/display/teaser', $edit, 'Save');
 
     // Create an importer configuration.
     $this->createImporterConfiguration('Syndication', 'syndication');
@@ -83,6 +83,7 @@ class FeedsRSStoNodesTest extends FeedsWebTestCase {
     );
 
     $nid = $this->createFeedNode();
+
     // Assert 10 items aggregated after creation of the node.
     $this->assertText('Created 10 Article nodes.');
 
@@ -96,49 +97,23 @@ class FeedsRSStoNodesTest extends FeedsWebTestCase {
     $this->drupalGet('node/'. $article_nid);
     $this->assertNoRaw('node/'. $article_nid .'/import');
     $this->assertNoRaw('node/'. $article_nid .'/delete-items');
-    $this->assertEqual("Created/updated by FeedsNodeProcessor", db_query("SELECT nr.log FROM {node} n JOIN {node_revisions} nr ON n.vid = nr.vid WHERE n.nid = :nid", array(':nid' => $article_nid))->fetchField());
+    $this->assertEqual("Created/updated by FeedsNodeProcessor", db_query("SELECT nr.log FROM {node} n JOIN {node_revision} nr ON n.vid = nr.vid WHERE n.nid = :nid", array(':nid' => $article_nid))->fetchField());
 
     // Assert accuracy of aggregated information.
     $this->drupalGet('node');
-    $this->assertPattern('/<span class="submitted">(.*?)Anonymous<\/span>/');
-    $this->assertText('Open Atrium Translation Workflow: Two Way Translation Updates');
-    $this->assertText('Tue, 10/06/2009');
-    $this->assertText('A new translation process for Open Atrium &amp; integration with Localize Drupal');
-    $this->assertText('Week in DC Tech: October 5th Edition');
-    $this->assertText('Mon, 10/05/2009');
-    $this->assertText('There are some great technology events happening this week');
-    $this->assertText('Mapping Innovation at the World Bank with Open Atrium');
-    $this->assertText('Fri, 10/02/2009');
-    $this->assertText('Open Atrium is being used as a base platform for collaboration');
-    $this->assertText('September GeoDC Meetup Tonight');
-    $this->assertText('Wed, 09/30/2009');
-    $this->assertText('Today is the last Wednesday of the month');
-    $this->assertText('Week in DC Tech: September 28th Edition');
-    $this->assertText('Mon, 09/28/2009');
-    $this->assertText('Looking to geek out this week? There are a bunch of');
-    $this->assertText('Open Data for Microfinance: The New MIXMarket.org');
-    $this->assertText('Thu, 09/24/2009');
-    $this->assertText('There are profiles for every country that the MIX Market is hosting.');
-    $this->assertText('Integrating the Siteminder Access System in an Open Atrium-based Intranet');
-    $this->assertText('Tue, 09/22/2009');
-    $this->assertText('In addition to authentication, the Siteminder system');
-    $this->assertText('Week in DC Tech: September 21 Edition');
-    $this->assertText('Mon, 09/21/2009');
-    $this->assertText('an interesting variety of technology events happening in Washington, DC ');
-    $this->assertText('s Software Freedom Day: Impressions &amp; Photos');
-    $this->assertText('Mon, 09/21/2009');
-    $this->assertText('Presenting on Features in Drupal and Open Atrium');
-    $this->assertText('Scaling the Open Atrium UI');
-    $this->assertText('Fri, 09/18/2009');
-    $this->assertText('The first major change is switching');
-
+    $this->assertRaw('<span class="username">Anonymous (not verified)</span>');
+    $this->assertDevseedFeedContent();
+    
     // Assert DB status.
     $count = db_query("SELECT COUNT(*) FROM {node} n INNER JOIN {feeds_node_item} fn ON n.nid = fn.nid")->fetchField();
     $this->assertEqual($count, 10, 'Accurate number of items in database.');
 
     // Assert default input format on first imported feed node.
-    $format = db_query_range("SELECT nr.format FROM {feeds_node_item} fi JOIN {node} n ON fi.nid = n.nid JOIN {node_revisions} nr ON n.vid = nr.vid", 0, 1)->fetchField();
+/*
+// NEEDS update.
+    $format = db_query_range("SELECT nr.format FROM {feeds_node_item} fi JOIN {node} n ON fi.nid = n.nid JOIN {node_revision} nr ON n.vid = nr.vid", 0, 1)->fetchField();
     $this->assertEqual($format, filter_fallback_format(), 'Using default Input format.');
+*/
 
     // Import again.
     $this->drupalPost('node/'. $nid .'/import', array(), 'Import');
@@ -155,7 +130,7 @@ class FeedsRSStoNodesTest extends FeedsWebTestCase {
     $edit = array(
       'node_options[status]' => FALSE,
     );
-    $this->drupalPost('admin/content/node-type/article', $edit, t('Save content type'));
+    $this->drupalPost('admin/structure/types/manage/article', $edit, t('Save content type'));
     $this->drupalPost('node/'. $nid .'/delete-items', array(), 'Delete');
     $this->drupalPost('node/'. $nid .'/import', array(), 'Import');
     $count = db_query("SELECT COUNT(*) FROM {node} n INNER JOIN {feeds_node_item} fn ON n.nid = fn.nid WHERE n.status = 0")->fetchField();
@@ -163,7 +138,7 @@ class FeedsRSStoNodesTest extends FeedsWebTestCase {
     $edit = array(
       'node_options[status]' => TRUE,
     );
-    $this->drupalPost('admin/content/node-type/article', $edit, t('Save content type'));
+    $this->drupalPost('admin/structure/types/manage/article', $edit, t('Save content type'));
     $this->drupalPost('node/'. $nid .'/delete-items', array(), 'Delete');
 
     // Enable replace existing and import updated feed file.
@@ -209,20 +184,23 @@ class FeedsRSStoNodesTest extends FeedsWebTestCase {
 
     // Assert author.
     $this->drupalGet('node');
-    $this->assertPattern('/<span class="submitted">(.*?)'. check_plain($author->name) .'<\/span>/');
+    $this->assertPattern('/<span class="username">' . check_plain($author->name) . '<\/span>/');
     $count = db_query("SELECT COUNT(*) FROM {feeds_node_item} fi JOIN {node} n ON fi.nid = n.nid WHERE n.uid = :uid", array(':uid' => $author->uid))->fetchField();
     $this->assertEqual($count, 10, 'Accurate number of items in database.');
 
     // Assert input format.
-    $format = db_query_range("SELECT nr.format FROM {feeds_node_item} fi JOIN {node} n ON fi.nid = n.nid JOIN {node_revisions} nr ON n.vid = nr.vid", 0, 1)->fetchField();
+/*
+// NEEDS update.
+    $format = db_query_range("SELECT nr.format FROM {feeds_node_item} fi JOIN {node} n ON fi.nid = n.nid JOIN {node_revision} nr ON n.vid = nr.vid", 0, 1)->fetchField();
     $this->assertEqual($format, filter_fallback_format() + 1, 'Set non-default Input format.');
+*/
 
     // Set to update existing, remove authorship of above nodes and import again.
     $this->setSettings('syndication', 'FeedsNodeProcessor', array('update_existing' => 2));
     db_query("UPDATE {node} n JOIN {feeds_node_item} fi ON n.nid = fi.nid SET n.uid = 0, fi.hash=''");
     $this->drupalPost('node/'. $nid .'/import', array(), 'Import');
     $this->drupalGet('node');
-    $this->assertNoPattern('/<span class="submitted">(.*?)'. check_plain($author->name) .'<\/span>/');
+    $this->assertNoPattern('/<span class="username">' . check_plain($author->name) . '<\/span>/');
     $count = db_query("SELECT COUNT(*) FROM {feeds_node_item} fi JOIN {node} n ON fi.nid = n.nid WHERE n.uid = :uid", array(':uid' => $author->uid))->fetchField();
     $this->assertEqual($count, 0, 'Accurate number of items in database.');
 
@@ -238,7 +216,7 @@ class FeedsRSStoNodesTest extends FeedsWebTestCase {
     );
     $this->drupalPost('node/'. $nid .'/import', array(), 'Import');
     $this->drupalGet('node');
-    $this->assertNoPattern('/<span class="submitted">(.*?)'. check_plain($author->name) .'<\/span>/');
+    $this->assertNoPattern('/<span class="username">' . check_plain($author->name) . '<\/span>/');
     $uid = db_query("SELECT uid FROM {node} WHERE nid = :nid", array(':nid' => $nid))->fetchField();
     $count = db_query("SELECT COUNT(*) FROM {node} WHERE uid = :uid", array(':uid' =>$uid))->fetchField();
     $this->assertEqual($count, 11, 'All feed item nodes are assigned to feed node author.');
@@ -304,36 +282,7 @@ class FeedsRSStoNodesTest extends FeedsWebTestCase {
 
     // Assert accuracy of aggregated information.
     $this->drupalGet('node');
-    $this->assertText('Open Atrium Translation Workflow: Two Way Translation Updates');
-    $this->assertText('Tue, 10/06/2009');
-    $this->assertText('A new translation process for Open Atrium &amp; integration with Localize Drupal');
-    $this->assertText('Week in DC Tech: October 5th Edition');
-    $this->assertText('Mon, 10/05/2009');
-    $this->assertText('There are some great technology events happening this week');
-    $this->assertText('Mapping Innovation at the World Bank with Open Atrium');
-    $this->assertText('Fri, 10/02/2009');
-    $this->assertText('Open Atrium is being used as a base platform for collaboration');
-    $this->assertText('September GeoDC Meetup Tonight');
-    $this->assertText('Wed, 09/30/2009');
-    $this->assertText('Today is the last Wednesday of the month');
-    $this->assertText('Week in DC Tech: September 28th Edition');
-    $this->assertText('Mon, 09/28/2009');
-    $this->assertText('Looking to geek out this week? There are a bunch of');
-    $this->assertText('Open Data for Microfinance: The New MIXMarket.org');
-    $this->assertText('Thu, 09/24/2009');
-    $this->assertText('There are profiles for every country that the MIX Market is hosting.');
-    $this->assertText('Integrating the Siteminder Access System in an Open Atrium-based Intranet');
-    $this->assertText('Tue, 09/22/2009');
-    $this->assertText('In addition to authentication, the Siteminder system');
-    $this->assertText('Week in DC Tech: September 21 Edition');
-    $this->assertText('Mon, 09/21/2009');
-    $this->assertText('an interesting variety of technology events happening in Washington, DC ');
-    $this->assertText('s Software Freedom Day: Impressions &amp; Photos');
-    $this->assertText('Mon, 09/21/2009');
-    $this->assertText('Presenting on Features in Drupal and Open Atrium');
-    $this->assertText('Scaling the Open Atrium UI');
-    $this->assertText('Fri, 09/18/2009');
-    $this->assertText('The first major change is switching');
+    $this->assertDevseedFeedContent();
 
     // Assert DB status.
     $count = db_query("SELECT COUNT(*) FROM {feeds_node_item}")->fetchField();
@@ -384,17 +333,19 @@ class FeedsRSStoNodesTest extends FeedsWebTestCase {
     $this->assertEqual($count, 10, 'Accurate number of items in database.');
 
     // Login with new user with only access content permissions.
-    $this->drupalLogin(
-      $this->drupalCreateUser()
-    );
+    $this->drupalLogin($this->drupalCreateUser());
+
     // Navigate to feed import form, access should be denied.
     $this->drupalGet('import/syndication_standalone');
     $this->assertResponse(403);
 
     // Use File Fetcher.
-    $this->drupalLogin(
-      $this->drupalCreateUser(array('administer feeds', 'administer nodes'))
-    );
+    $this->drupalLogin($this->drupalCreateUser(array(
+      'administer feeds',
+      'administer nodes',
+      'create page content',
+    )));
+
     $this->setPlugin('syndication', 'FeedsFileFetcher');
     // Create a feed node.
     $edit = array(
@@ -404,4 +355,40 @@ class FeedsRSStoNodesTest extends FeedsWebTestCase {
     $this->assertText('has been created.');
     $this->assertText('Created 25 Article nodes.');
   }
+
+  /**
+   * Check thet contents of the current page for the DS feed.
+   */
+  function assertDevseedFeedContent() {
+    $this->assertText('Open Atrium Translation Workflow: Two Way Translation Updates');
+    $this->assertText('Tue, 10/06/2009');
+    $this->assertText('A new translation process for Open Atrium &amp; integration with Localize Drupal');
+    $this->assertText('Week in DC Tech: October 5th Edition');
+    $this->assertText('Mon, 10/05/2009');
+    $this->assertText('There are some great technology events happening this week');
+    $this->assertText('Mapping Innovation at the World Bank with Open Atrium');
+    $this->assertText('Fri, 10/02/2009');
+    $this->assertText('is being used as a base platform for collaboration at the World Bank');
+    $this->assertText('September GeoDC Meetup Tonight');
+    $this->assertText('Wed, 09/30/2009');
+    $this->assertText('Today is the last Wednesday of the month');
+    $this->assertText('Week in DC Tech: September 28th Edition');
+    $this->assertText('Mon, 09/28/2009');
+    $this->assertText('Looking to geek out this week? There are a bunch of');
+    $this->assertText('Open Data for Microfinance: The New MIXMarket.org');
+    $this->assertText('Thu, 09/24/2009');
+    $this->assertText('There are profiles for every country that the MIX Market is hosting.');
+    $this->assertText('Integrating the Siteminder Access System in an Open Atrium-based Intranet');
+    $this->assertText('Tue, 09/22/2009');
+    $this->assertText('In addition to authentication, the Siteminder system');
+    $this->assertText('Week in DC Tech: September 21 Edition');
+    $this->assertText('Mon, 09/21/2009');
+    $this->assertText('an interesting variety of technology events happening in Washington, DC ');
+    $this->assertText('s Software Freedom Day: Impressions &amp; Photos');
+    $this->assertText('Mon, 09/21/2009');
+    $this->assertText('Presenting on Features in Drupal and Open Atrium');
+    $this->assertText('Scaling the Open Atrium UI');
+    $this->assertText('Fri, 09/18/2009');
+    $this->assertText('The first major change is switching');
+  }
 }
diff --git a/tests/feeds_processor_term.test b/tests/feeds_processor_term.test
index ef43322c..8efdabea 100644
--- a/tests/feeds_processor_term.test
+++ b/tests/feeds_processor_term.test
@@ -29,7 +29,7 @@ class FeedsCSVtoTermsTest extends FeedsWebTestCase {
    * Set up test.
    */
   public function setUp() {
-    parent::setUp('feeds', 'feeds_ui', 'ctools', 'job_scheduler');
+    parent::setUp('taxonomy', 'ctools', 'feeds', 'feeds_ui', 'job_scheduler');
 
     $this->drupalLogin(
       $this->drupalCreateUser(
@@ -69,18 +69,19 @@ class FeedsCSVtoTermsTest extends FeedsWebTestCase {
 
     $edit = array(
       'name' => 'Addams vocabulary',
+      'machine_name' => 'addams',
     );
-    $this->drupalPost('admin/content/taxonomy/add/vocabulary', $edit, t('Save'));
+    $this->drupalPost('admin/structure/taxonomy/add', $edit, t('Save'));
 
     $edit = array(
-      'vocabulary' => 1,
+      'vocabulary' => 'addams',
     );
     $this->drupalPost('admin/structure/feeds/edit/term_import/settings/FeedsTermProcessor', $edit, t('Save'));
 
     // Import and assert.
     $this->importFile('term_import', $this->absolutePath() .'/tests/feeds/users.csv');
     $this->assertText('Created 5 terms in Addams vocabulary.');
-    $this->drupalGet('admin/content/taxonomy/1');
+    $this->drupalGet('admin/structure/taxonomy/addams');
     $this->assertText('Morticia');
     $this->assertText('Fester');
     $this->assertText('Gomez');
@@ -94,9 +95,9 @@ class FeedsCSVtoTermsTest extends FeedsWebTestCase {
     $edit = array(
       'name' => 'Cousin Itt',
     );
-    $this->drupalPost('admin/content/taxonomy/1/add/term', $edit, t('Save'));
+    $this->drupalPost('admin/structure/taxonomy/addams/add', $edit, t('Save'));
     $this->drupalPost('import/term_import/delete-items', array(), t('Delete'));
-    $this->drupalGet('admin/content/taxonomy/1');
+    $this->drupalGet('admin/structure/taxonomy/addams');
     $this->assertText('Cousin Itt');
     $this->assertNoText('Morticia');
     $this->assertNoText('Fester');
-- 
GitLab