Index: feedapi_mapper.admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/feedapi_mapper/Attic/feedapi_mapper.admin.inc,v
retrieving revision 1.1.2.2
diff -u -p -r1.1.2.2 feedapi_mapper.admin.inc
--- feedapi_mapper.admin.inc	7 Aug 2009 14:07:44 -0000	1.1.2.2
+++ feedapi_mapper.admin.inc	10 Aug 2009 20:26:51 -0000
@@ -27,19 +27,38 @@ function theme_feedapi_mapper_descriptio
  */
 function theme_feedapi_mapper_form($form) {
   $output = '';
-
+  _feedapi_mapper_load_mappers();
   $type_url_str = str_replace('_', '-', $form['#node']->type);
-  $url = isset($form['#node']->nid) ?
+  $url_delete = isset($form['#node']->nid) ?
     'node/' . $form['#node']->nid .'/map/delete/' :
     'admin/content/node-type/'. $type_url_str .'/map/delete/';
+  $url_toggle_unique = isset($form['#node']->nid) ?
+    'node/' . $form['#node']->nid .'/map/unique/' :
+    'admin/content/node-type/'. $type_url_str .'/map/unique/';
   if (isset($form['#mapping']['mapping'])) {
+    $feed_settings = feedapi_get_settings($form['#node']->type, $form['#node']->vid);
+    $active_processors = array_keys($feed_settings['processors']);
     foreach ($form['#mapping']['mapping'] as $feed_path => $node_path) {
+      $target = unserialize($node_path);
+      if (function_exists($target[0] .'_feedapi_mapper')) {
+	$unique_supported = FALSE;
+	foreach ($active_processors as $processor) {
+          $unique_supported = ($unique_supported | call_user_func($target[0] .'_feedapi_mapper', 'unique supported', $form['#node'], $processor, NULL, $target[1]));
+	}
+        if ($unique_supported == TRUE) {
+          $unique = $form['#mapping']['unique'][$feed_path] ? t('Yes') : t('No');
+	  $unique = l($unique, $url_toggle_unique . base64_encode($feed_path) .'/'. drupal_get_token($feed_path));
+        }
+        else {
+          $unique = 'N/A';
+        }
+      }
       $rows[] = array(
         // @todo: Set proper messages.
         isset($form['#feed_map'][$feed_path]) ? $form['#feed_map'][$feed_path] : '',
         isset($form['#field_map'][$node_path]) ? $form['#field_map'][$node_path] : t('<em>Error: missing target.</em>'),
-        $form['#mapping']['unique'][$feed_path] ? t('Yes') : t('No'),
-        l(t('Delete'), $url . base64_encode($feed_path)),
+	$unique,
+        l(t('Delete'), $url_delete . base64_encode($feed_path)),
       );
     }
   }
@@ -123,6 +142,32 @@ function feedapi_mapper_revert_submit($f
 }
 
 /**
+ * Toggles the value of the unique-ness of the given key
+ * @see theme_feedapi_mapper_form()
+ */
+function feedapi_mapper_unique_toggle($param, $encoded_key, $token) {
+  $key = base64_decode($encoded_key);
+  if (!drupal_valid_token($token, $key)) {
+    drupal_set_message(t('Invalid request'), 'error');
+    drupal_goto('');
+  }
+  if (is_string($param)) {
+    $node = new stdClass();
+    $node->type = str_replace('-', '_', $param);
+    $path = 'admin/content/node-type/'. $param .'/map';
+  }
+  else {
+    $node = $param;
+    $path = "node/{$node->nid}/map";
+    $param = $node->nid;
+  }
+  $mapping = feedapi_mapper_load_mapping($node);
+  feedapi_mapper_delete_mapping($param, $key);
+  feedapi_mapper_add_mapping($param, $key, $mapping['mapping'][$key], !$mapping['unique'][$key]);
+  drupal_goto($path);
+}
+
+/**
  * Form callback confirmation form for fall back to default.
  */
 function feedapi_mapper_default_form($form_state, $feed_node) {
@@ -276,9 +321,6 @@ function feedapi_mapper_form($form_state
     '#type' => 'select',
     '#options' => $field_map_options,
   );
-  $form['unique'] = array(
-    '#type' => 'checkbox',
-  );
   $form['add'] = array(
     '#type' => 'submit',
     '#value' => t('Add'),
@@ -348,7 +390,7 @@ function feedapi_mapper_form_validate($f
  */
 function feedapi_mapper_form_submit($form, &$form_state) {
   $param = ($form['#node']->nid && $form['#override']) ? $form['#node']->nid : $form['#node']->type;
-  feedapi_mapper_add_mapping($param, $form_state['values']['source'], $form_state['values']['target'], $form_state['values']['unique']);
+  feedapi_mapper_add_mapping($param, $form_state['values']['source'], $form_state['values']['target'], FALSE);
 }
 
 /**
Index: feedapi_mapper.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/feedapi_mapper/feedapi_mapper.module,v
retrieving revision 1.2.2.17
diff -u -p -r1.2.2.17 feedapi_mapper.module
--- feedapi_mapper.module	7 Aug 2009 15:43:43 -0000	1.2.2.17
+++ feedapi_mapper.module	10 Aug 2009 20:26:51 -0000
@@ -55,6 +55,13 @@ function feedapi_mapper_menu() {
     'access arguments' => array(1),
     'file' => 'feedapi_mapper.admin.inc',
   );
+  $items['node/%node/map/unique/%/%'] = array(
+    'page callback' => 'feedapi_mapper_unique_toggle',
+    'page arguments' => array(1, 4, 5),
+    'access callback' => 'feedapi_mapper_access_mapper',
+    'access arguments' => array(1),
+    'file' => 'feedapi_mapper.admin.inc',
+  );
   $items['node/%node/map/default'] = array(
     'title' => 'Default',
     'page callback' => 'drupal_get_form',
@@ -123,6 +130,14 @@ function feedapi_mapper_menu() {
       'access arguments' => array(3),
       'file' => 'feedapi_mapper.admin.inc',
     );
+    $items['admin/content/node-type/'. $type_url_str .'/map/unique/%/%'] = array(
+      'page callback' => 'feedapi_mapper_unique_toggle',
+      'page arguments' => array(3, 6, 7),
+      'load arguments' => array(3),
+      'access callback' => 'feedapi_mapper_access_mapper',
+      'access arguments' => array(3),
+      'file' => 'feedapi_mapper.admin.inc',
+    );
   }
   return $items;
 }
@@ -226,6 +241,45 @@ function feedapi_mapper_map($feed_node, 
 }
 
 /**
+ * Collects the ids of the duplicated items of $item.
+ * Utilized from FeedAPI processors.
+ * @see _feedapi_node_unique() for example.
+ *
+ * @param $feed_node
+ *  Feed node object of this feed item.
+ * @param $item
+ *   Feed item to decide about.
+ *
+ * @return
+ *   In the top level, serial indexed array represents the different fields.
+ *   Returns FALSE when there are no defined unique fields at all.
+ *
+ *   Array structure in the case of node processor:
+ *   array(
+ *    [0] => array(itemid1 => array(feedid1, feedid2))
+ *    [1] => array(itemid2 => array(feedid1, feedid2))
+ *
+ *  Otherwise the content is up to the processor.
+ */
+function feedapi_mapper_unique($feed_node, $processor, $item) {
+  _feedapi_mapper_load_mappers();
+  $uniques = feedapi_mapper_get_uniques($feed_node);
+  if (empty($uniques)) {
+    return FALSE;
+  }
+  $fields_ids = array();
+  foreach ($uniques as $unique) {
+    $feed_item_element = feedapi_mapper_get_element($unique['source'], _feedapi_mapper_obj2array($item));
+    $dup_ids = call_user_func($unique['target'][0] .'_feedapi_mapper', 'unique', $feed_node, $processor, NULL, $feed_item_element, $unique['target'][1]);
+    // If they're not compatible with the given processor the result should be empty.
+    if (!empty($dup_ids)) {
+      $fields_ids[] = $dup_ids;
+    }
+  }
+  return $fields_ids;
+}
+
+/**
  * Returns paths to unique feed elements.
  * 
  * @param $feed_node
@@ -371,6 +425,7 @@ function _feedapi_mapper_load_mapping($p
   if ($mapping = ctools_export_load_object('feedapi_mapper', 'conditions', array('param' => $param))) {
     $mapping = (array) array_pop($mapping);
     if (!empty($mapping['mapping'])) {
+      $mapping['unique'] = $mapping['unique_elements'];
       return $mapping;
     }
   }
Index: tests/feedapi_mapper.test
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/feedapi_mapper/tests/feedapi_mapper.test,v
retrieving revision 1.2.2.1
diff -u -p -r1.2.2.1 feedapi_mapper.test
--- tests/feedapi_mapper.test	7 Aug 2009 15:29:01 -0000	1.2.2.1
+++ tests/feedapi_mapper.test	10 Aug 2009 20:26:51 -0000
@@ -66,6 +66,8 @@ class FeedApiMapperBasicTestCase extends
       'filename' => 'drupal.xml',
       'mappers' => array('node'),
       'mapping' => array(
+        serialize(array('title')) => serialize(array('node', 'title')),
+	serialize(array('description')) => serialize(array('node', 'body')),
       ),
     );
 
Index: tests/feedapi_mapper_content.test
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/feedapi_mapper/tests/feedapi_mapper_content.test,v
retrieving revision 1.2.2.1
diff -u -p -r1.2.2.1 feedapi_mapper_content.test
--- tests/feedapi_mapper_content.test	7 Aug 2009 15:29:01 -0000	1.2.2.1
+++ tests/feedapi_mapper_content.test	10 Aug 2009 20:26:51 -0000
@@ -57,6 +57,7 @@ class FeedApiMapperContentTestCase exten
       'mappers' => array('node', 'content'),
       'mapping' => array(
         serialize(array('title')) => serialize(array('node', 'title')),
+	serialize(array('description')) => serialize(array('node', 'body')),
         serialize(array('options', 'raw' ,'comments')) => serialize(array('content', 'field_alpha')),
         serialize(array('options', 'timestamp')) => serialize(array('content', 'field_beta')),
         // @todo: use a field that contains a decimal
