Index: feeds.api.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/feeds/feeds.api.php,v
retrieving revision 1.10
diff -u -p -r1.10 feeds.api.php
--- feeds.api.php	24 Feb 2010 01:19:33 -0000	1.10
+++ feeds.api.php	26 May 2010 16:39:03 -0000
@@ -108,6 +108,19 @@ function hook_feeds_after_import(FeedsIm
  */
 
 /**
+ * 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) {
+}
+
+/**
  * Alter mapping targets for nodes. Use this hook to add additional target
  * options to the mapping form of Node processors.
  *
Index: mappers/profile.inc
===================================================================
RCS file: mappers/profile.inc
diff -N mappers/profile.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ mappers/profile.inc	26 May 2010 16:39:04 -0000
@@ -0,0 +1,38 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * On behalf implementation of Feeds mapping API for user profiles.
+ */
+
+/**
+ * Implementation of feeds_user_processor_target_alter
+ *
+ */
+function profile_feeds_user_processor_targets_alter(&$targets) {
+  if (module_exists('profile')) {
+    $categories = profile_categories();
+
+    foreach ($categories as $category) {
+      $result = _profile_get_fields($category['name']);
+      while ($record = db_fetch_object($result)) {
+        $targets[$record->name] = array(
+          'name' => t('Profile:'.$record->title),
+          'description' => t('Profile:'.$record->title),
+          'callback' => 'profile_feeds_set_target',
+        );
+      }
+    }
+  }
+}
+
+/**
+ * Set the user profile target after import. 
+ */
+function profile_feeds_set_target($account, $target, $value) {
+  $account->{$target} = $value;
+
+  return $account;
+}
+
Index: plugins/FeedsUserProcessor.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/feeds/plugins/FeedsUserProcessor.inc,v
retrieving revision 1.10
diff -u -p -r1.10 FeedsUserProcessor.inc
--- plugins/FeedsUserProcessor.inc	28 Apr 2010 22:18:30 -0000	1.10
+++ plugins/FeedsUserProcessor.inc	26 May 2010 16:39:05 -0000
@@ -94,6 +94,26 @@ class FeedsUserProcessor extends FeedsPr
   }
 
   /**
+   * Loads on-behalf implementations from mappers/
+   */
+  protected static function loadMappers() {
+    static $loaded = FALSE;
+
+    if (!$loaded) {
+      $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");
+        }
+      }
+      // Rebuild cache.
+      module_implements('', FALSE, TRUE);
+    }
+    $loaded = TRUE;
+  }
+
+  /**
    * Execute mapping on an item.
    */
   protected function map($source_item) {
@@ -188,6 +208,11 @@ class FeedsUserProcessor extends FeedsPr
         'optional_unique' => TRUE,
        );
     }
+
+    // Let other modules expose mapping targets.
+    self::loadMappers();
+    drupal_alter('feeds_user_processor_targets', $targets);
+
     return $targets;
   }
 
Index: tests/feeds_mapper_profile.test
===================================================================
RCS file: tests/feeds_mapper_profile.test
diff -N tests/feeds_mapper_profile.test
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/feeds_mapper_profile.test	26 May 2010 16:39:05 -0000
@@ -0,0 +1,119 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Test suite for the feeds profile mapper.
+ */
+
+require_once(drupal_get_path('module', 'feeds') . '/tests/feeds_mapper_test.inc');
+
+/**
+ * Class for testing Feeds <em>content</em> mapper.
+ */
+class FeedsMapperProfileTestCase extends FeedsMapperTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => t('Mapper: Profile'),
+      'description' => t('Test Feeds Mapper support for profile fields. <strong>Requires profile module</strong>.'),
+      'group' => t('Feeds'),
+    );
+  }
+
+  /**
+   * Set up the test.
+   */
+  function setUp() {
+    // Call parent setup with required modules.
+    parent::setUp('feeds', 'feeds_ui', 'ctools', 'profile');
+
+    // Create user and login.
+    $this->drupalLogin($this->drupalCreateUser(
+        array(
+          'administer users',
+          'administer feeds',
+          'administer site configuration'
+        )
+    ));
+  }
+
+  /**
+   * Basic test loading a doulbe entry CSV file.
+   */
+  function test() {
+
+    // Create profile fields.
+    $edit = array(
+      'category' => 'test',
+      'title' => 'color',
+      'name' => 'profile_textfield_test',
+    );
+    $name = $this->drupalPost('admin/user/profile/add/textfield', $edit, t('Save field'));
+    $edit = array(
+      'category' => 'test',
+      'title' => 'letter',
+      'name' => 'profile_select_test',
+      'options' => 'alpha' . "\n" . 'beta' . "\n" . 'gamma',
+    );
+    $name = $this->drupalPost('admin/user/profile/add/selection', $edit, t('Save field'));
+
+    // Create a feed.
+    $this->createFeedConfiguration('Profile import', 'profile_import');
+
+    // Set and configure plugins.
+    $this->setPlugin('profile_import', 'FeedsFileFetcher');
+    $this->setPlugin('profile_import', 'FeedsCSVParser');
+    $this->setPlugin('profile_import', 'FeedsUserProcessor');
+
+    // Go to mapping page and create a couple of mappings.
+    $mappings = array(
+      '0' => array(
+        'source' => 'name',
+        'target' => 'name',
+        'unique' => 0,
+      ),
+      '1' => array(
+        'source' => 'mail',
+        'target' => 'mail',
+        'unique' => 1,
+      ),
+      '2' => array(
+        'source' => 'color',
+        'target' => 'profile_textfield_test',
+        'unique' => FALSE,
+      ),
+      '3' => array(
+        'source' => 'letter',
+        'target' => 'profile_select_test',
+        'unique' => FALSE,
+      ),
+    );
+    $this->addMappings('profile_import', $mappings);
+
+    // Change some of the basic configuration.
+    $edit = array(
+      'content_type' => '',
+      'import_period' => FEEDS_SCHEDULE_NEVER,
+    );
+    $this->drupalPost('admin/build/feeds/edit/profile_import/settings', $edit, 'Save');
+
+    // Import CSV file.
+    $this->importFile('profile_import', $this->absolutePath() .'/tests/feeds/profile.csv');
+    $this->assertText('Created 2 users.');
+
+    // Check the two imported users.
+    $this->drupalGet('admin/user/user');
+    $this->assertText('magna');
+    $this->assertText('rhoncus');
+
+    $account = user_load(array('name' => 'magna'));    
+    $this->assertEqual($account->profile_textfield_test, 'red', 'User profile_textfield_test is correct'); 
+    $this->assertEqual($account->profile_select_test, 'alpha', 'User profile_select_test is correct'); 
+
+    $account = user_load(array('name' => 'rhoncus'));    
+    $this->assertEqual($account->profile_textfield_test, 'blue', 'User profile_textfield_test is correct'); 
+    $this->assertEqual($account->profile_select_test, 'beta', 'User profile_select_test is correct'); 
+  }
+
+}
Index: tests/feeds/profile.csv
===================================================================
RCS file: tests/feeds/profile.csv
diff -N tests/feeds/profile.csv
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/feeds/profile.csv	26 May 2010 16:39:07 -0000
@@ -0,0 +1,3 @@
+name,mail,color,letter
+magna,auctor@tortor.com,red,alpha
+rhoncus,rhoncus@habitasse.org,blue,beta
