diff --git feeds.module feeds.module index 4ddac1c..377ee81 100644 --- feeds.module +++ feeds.module @@ -103,6 +103,15 @@ function feeds_menu() { 'file' => 'feeds.pages.inc', 'type' => MENU_LOCAL_TASK, ); + $items['import/'. $importer->id . '/mapping'] = array( + 'title' => t('Mapping'), + 'page callback' => 'drupal_get_form', + 'page arguments' => array('feeds_import_mapping_form', 1), + 'access callback' => 'feeds_access', + 'access arguments' => array('import', $importer->id), + 'file' => 'feeds.pages.inc', + 'type' => MENU_LOCAL_TASK, + ); } else { $items['node/%node/import'] = array( @@ -125,6 +134,16 @@ function feeds_menu() { 'type' => MENU_LOCAL_TASK, 'weight' => 11, ); + $items['node/%node/mapping'] = array( + 'title' => t('Mapping'), + 'page callback' => 'drupal_get_form', + 'page arguments' => array('feeds_import_mapping_form', NULL, 1), + 'access callback' => 'feeds_access', + 'access arguments' => array('import', 1), + 'file' => 'feeds.pages.inc', + 'type' => MENU_LOCAL_TASK, + 'weight' => 12, + ); } $items += $importer->fetcher->menuItem(); } @@ -154,6 +173,9 @@ function feeds_theme() { 'feeds_upload' => array( 'file' => 'feeds.pages.inc', ), + 'feeds_mapping_form' => array( + 'file' => 'feeds.pages.inc' + ), ); } @@ -298,10 +320,21 @@ function feeds_nodeapi(&$node, $op, $form) { $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($node_feeds['suppress_import'])) { - feeds_batch_set(t('Importing'), 'import', $importer_id, $node->nid); + if ($source->importer->processor->config['mapping_on_import']) { + $_REQUEST['destination'] = 'node/' . $node->nid . '/mapping/' . $source->importer->id; + } + else { + // 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($node_feeds['suppress_import'])) { + feeds_batch_set(t('Importing'), 'import', $importer_id, $node->nid); + } + // Add import to scheduler. + feeds_scheduler()->add($importer_id, 'import', $node->nid); + // Add expiry to schedule, in case this is the first feed of this + // configuration. + feeds_scheduler()->add($importer_id, 'expire'); + $node_feeds = NULL; } // Add import to scheduler. feeds_scheduler()->add($importer_id, 'import', $node->nid); diff --git feeds.pages.inc feeds.pages.inc index ae54403..a531479 100644 --- feeds.pages.inc +++ feeds.pages.inc @@ -59,6 +59,12 @@ function feeds_import_form(&$form_state, $importer_id) { '#type' => 'submit', '#value' => t('Import'), ); + + // If mapping on import is selected, redirect to feeds_import_mapping_form + if ($source->importer->processor->config['mapping_on_import']) { + $form['#redirect'] = 'import/' . $importer_id . '/mapping'; + } + return $form; } @@ -74,20 +80,25 @@ function feeds_import_form_validate($form, &$form_state) { * Submit handler for feeds_import_form(). */ function feeds_import_form_submit($form, &$form_state) { - // Save source and import. $source = feeds_source($form['#importer_id']); $source->addConfig($form_state['values']['feeds']); $source->save(); - // Refresh feed if import on create is selected. - if ($source->importer->config['import_on_create']) { - feeds_batch_set(t('Importing'), 'import', $form['#importer_id']); + if ($source->importer->processor->config['mapping_on_import']) { + // Handle the redirection to mapping form + $form_state['redirect'] = $form['#redirect']; } + else { + // Refresh feed if import on create is selected. + if ($source->importer->config['import_on_create']) { + feeds_batch_set(t('Importing'), 'import', $form['#importer_id']); + } - // Add importer to schedule. - feeds_scheduler()->add($form['#importer_id'], 'import'); - feeds_scheduler()->add($form['#importer_id'], 'expire'); + // Add importer to schedule. + feeds_scheduler()->add($form['#importer_id'], 'import'); + feeds_scheduler()->add($form['#importer_id'], 'expire'); + } } /** @@ -181,3 +192,147 @@ function theme_feeds_upload($element) { $output .= ''; return theme('form_element', $element, $output); } + +/** + * Declare Feeds mapping on import form + */ +function feeds_import_mapping_form(&$form_state, $importer_id, $node = NULL) { + if (empty($node)) { + $feed_nid = 0; + } + else { + $importer_id = feeds_get_importer_id($node->type); + $feed_nid = empty($node) ? 0 : $node->nid; + } + + $source = feeds_source($importer_id, $feed_nid); + + // Fetch and parse the source to enable batch to provide mapping sources + $batch = $source->importer->fetcher->fetch($source); + $source->importer->parser->parse($batch, $source); + + $form = $source->importer->processor->mappingForm($form_state, 'import', $batch); + + // Declare the appropriate configurable and forms method for appropriate submit handler + $form['#feeds_form_method'] = 'mappingForm'; + $form['#configurable'] = $source->importer->processor; + + $form['#importer_id'] = $importer_id; + $form['#feed_nid'] = $feed_nid; + + $form['import'] = array( + '#type' => 'submit', + '#value' => t('Import'), + '#submit' => array('feeds_import_mapping_form_import_submit'), + ); + return $form; +} + +/** + * Default submit handler for mapping on import form + */ +function feeds_import_mapping_form_submit($form, &$form_state) { + $form['#configurable']->addConfig($form_state['values']); + $form['#configurable']->save(); + drupal_set_message(t('Your changes have been saved.')); + feeds_cache_clear(FALSE); +} + +/** + * Import submit handler for mapping on import form + */ +function feeds_import_mapping_form_import_submit($form, &$form_state) { + // Handle the redirections + if (!$form['#feed_nid']) { + $form_state['redirect'] = 'import/' . $form['#importer_id']; + } + else { + $form_state['redirect'] = 'node/' . $form['#feed_nid']; + } + + $source = feeds_source($form['#importer_id'], $form['#feed_nid']); + + // Refresh feed if import on create is selected. + if ($source->importer->config['import_on_create']) { + feeds_batch_set(t('Importing'), 'import', $form['#importer_id'], $form['#feed_nid']); + } + + // Add importer to schedule. + feeds_scheduler()->add($form['#importer_id'], 'import', $form['#feed_nid']); + feeds_scheduler()->add($form['#importer_id'], 'expire', $form['#feed_nid']); +} + +/** + * Theme function for feeds_mapping_form(). + */ +function theme_feeds_mapping_form($form) { + // Build the actual mapping table. + $header = array( + t('Source'), + t('Target'), + t('Unique target'), + ' ', + ); + $rows = array(); + if (is_array($form['#mappings'])) { + foreach ($form['#mappings'] as $i => $mapping) { + $rows[] = array( + $mapping['source'], + $mapping['target'], + drupal_render($form['unique_flags'][$i]), + drupal_render($form['remove_flags'][$i]), + ); + } + } + if (!count($rows)) { + $rows[] = array( + array( + 'colspan' => 4, + 'data' => t('No mappings defined.'), + ), + ); + } + $rows[] = array( + drupal_render($form['source']), + drupal_render($form['target']), + '', + drupal_render($form['add']), + ); + $output = '
'; + $output .= theme('table', $header, $rows); + + // Build the help table that explains available sources. + $legend = ''; + $rows = array(); + foreach (element_children($form['legendset']['legend']['sources']) as $k) { + $rows[] = array( + drupal_render($form['legendset']['legend']['sources'][$k]['name']), + drupal_render($form['legendset']['legend']['sources'][$k]['description']), + ); + } + if (count($rows)) { + $legend .= '

'. t('Sources') .'

'; + $legend .= theme('table', array(t('Name'), t('Description')), $rows); + } + + // Build the help table that explains available targets. + $rows = array(); + foreach (element_children($form['legendset']['legend']['targets']) as $k) { + $rows[] = array( + drupal_render($form['legendset']['legend']['targets'][$k]['name']), + drupal_render($form['legendset']['legend']['targets'][$k]['description']), + ); + } + $legend .= '

'. t('Targets') .'

'; + $legend .= theme('table', array(t('Name'), t('Description')), $rows); + + // Stick tables into collapsible fieldset. + $form['legendset']['legend'] = array( + '#value' => '
'. $legend .'
', + ); + + $output .= drupal_render($form['legendset']); + + $output .= drupal_render($form); + return $output; +} \ No newline at end of file diff --git feeds_ui/feeds_ui.admin.inc feeds_ui/feeds_ui.admin.inc index 15bfd08..6ed4acc 100644 --- feeds_ui/feeds_ui.admin.inc +++ feeds_ui/feeds_ui.admin.inc @@ -36,17 +36,6 @@ function feeds_ui_edit_help() { } /** - * Help text for mapping. - */ -function feeds_ui_mapping_help() { - return t(' -

- Define which elements of a single item of a feed (= Sources) map to which content pieces in Drupal (= Targets). Make sure that at least one definition has a Unique target. A unique target means that a value for a target can only occur once. E. g. only one item with the URL http://example.com/story/1 can exist. -

- '); -} - -/** * Build overview of available configurations. */ function feeds_ui_overview_form(&$form_status) { @@ -280,7 +269,6 @@ function feeds_ui_export_form(&$form_state, $importer) { * Edit feed configuration. */ function feeds_ui_edit_page($importer, $active = 'help', $plugin_key = '') { - // Get plugins and configuration. $plugins = feeds_get_plugins(); $config = $importer->config; @@ -314,12 +302,14 @@ function feeds_ui_edit_page($importer, $active = 'help', $plugin_key = '') { // instantiated previously. elseif (in_array($plugin_key, array_keys($plugins)) && $plugin = feeds_plugin_instance($plugin_key, $importer->id)) { $active_container['title'] = t('Settings for !plugin', array('!plugin' => $plugins[$plugin_key]['name'])); - $active_container['body'] = feeds_get_form($plugin, 'mappingForm'); + $active_container['body'] = feeds_get_form($plugin, 'configForm'); } break; case 'mapping': - $active_container['title'] = t('Mapping for !processor', array('!processor' => $plugins[$config['processor']['plugin_key']]['name'])); - $active_container['body'] = drupal_get_form('feeds_ui_mapping_form', $importer); + if (in_array($plugin_key, array_keys($plugins)) && $plugin = feeds_plugin_instance($plugin_key, $importer->id)) { + $active_container['title'] = t('Mapping for !processor', array('!processor' => $plugins[$config['processor']['plugin_key']]['name'])); + $active_container['body'] = feeds_get_form($plugin, 'mappingForm'); + } break; } @@ -390,8 +380,8 @@ function feeds_ui_edit_page($importer, $active = 'help', $plugin_key = '') { $actions = array(); if (feeds_get_form($importer->processor, 'configForm')) { $actions[] = l(t('Settings'), $path .'/settings/'. $config['processor']['plugin_key']); + $actions[] = l(t('Mapping'), $path .'/mapping/'. $config['processor']['plugin_key']); } - $actions[] = l(t('Mapping'), $path .'/mapping'); $info['title'] = t('Processor'); $info['body'] = array( array( @@ -481,182 +471,6 @@ function theme_feeds_ui_plugin_form($form) { } /** - * Edit mapping. - * - * @todo Completely merge this into config form handling. This is just a - * shared form of configuration, most of the common functionality can live in - * FeedsProcessor, a flag can tell whether mapping is supported or not. - */ -function feeds_ui_mapping_form(&$form_state, $importer) { - drupal_add_js(drupal_get_path('module', 'feeds_ui') .'/feeds_ui.js'); - - $form = array(); - $form['#importer'] = $importer; - $form['help']['#value'] = feeds_ui_mapping_help(); - - // Get mapping sources from parsers and targets from processor, format them - // for output. - // Some parsers do not define mapping sources but let them define on the fly. - if ($sources = $importer->parser->getMappingSources()) { - $source_options = _feeds_ui_format_options($sources); - foreach ($sources as $k => $source) { - $legend['sources'][$k]['name']['#value'] = empty($source['name']) ? $k : $source['name']; - $legend['sources'][$k]['description']['#value'] = empty($source['description']) ? '' : $source['description']; - } - } - else { - $legend['sources']['#value'] = t('This parser supports free source definitions. Enter the name of the source field in lower case into the Source text field above.'); - } - $targets = $importer->processor->getMappingTargets(); - $target_options = _feeds_ui_format_options($targets); - foreach ($targets as $k => $target) { - $legend['targets'][$k]['name']['#value'] = empty($target['name']) ? $k : $target['name']; - $legend['targets'][$k]['description']['#value'] = empty($target['description']) ? '' : $target['description']; - } - - $form['legendset'] = array( - '#type' => 'fieldset', - '#title' => t('Legend'), - '#collapsible' => TRUE, - '#collapsed' => TRUE, - '#tree' => TRUE, - ); - $form['legendset']['legend'] = $legend; - - // Add unique and remove forms to mappings. - $mappings = $importer->processor->getMappings(); - $form['unique_flags'] = $form['remove_flags'] = array( - '#tree' => TRUE, - ); - - if (is_array($mappings)) { - $form['mappings']['#tree'] = TRUE; - foreach ($mappings as $i => $mapping) { - - $mappings[$i]['source'] = isset($source_options) ? $source_options[$mappings[$i]['source']] : $mappings[$i]['source']; - $mappings[$i]['target'] = isset($target_options[$mappings[$i]['target']]) ? $target_options[$mappings[$i]['target']] : $mappings[$i]['target']; - $param = array( - 'processor' => $importer->processor, - 'mapping' => $mapping, - ); - - if (isset($targets[$mapping['target']]['optional_unique']) && $targets[$mapping['target']]['optional_unique'] === TRUE) { - - $form['unique_flags'][$i] = array( - '#type' => 'checkbox', - '#default_value' => !empty($mapping['unique']), - '#attributes' => array('class' => 'feeds-ui-trigger-submit'), - ); - } - $form['remove_flags'][$i] = array( - '#type' => 'checkbox', - '#title' => t('Remove'), - '#prefix' => '', - ); - - // Declare the mappings data in the hidden field, so later - // the full mapping data will be submitted in $form_state['values']['mappings']. - foreach($mapping as $k => $v) { - $form['mappings'][$i][$k] = array ( - '#type' => 'hidden', - '#value' => $v, - ); - } - } - } - - $form['#mappings'] = $mappings; - $form['#targets'] = $targets; - if ($sources) { - $form['source'] = array( - '#type' => 'select', - '#options' => array('' => t('Select a source')) + $source_options, - ); - } - else { - $form['source'] = array( - '#type' => 'textfield', - '#size' => 20, - '#default_value' => t('Name of source field'), - '#attributes' => array('class' => 'hide-text-on-focus'), - ); - } - $form['target'] = array( - '#type' => 'select', - '#options' => array('' => t('Select a target')) + _feeds_ui_format_options($targets), - ); - $form['add'] = array( - '#type' => 'submit', - '#value' => t('Add'), - '#submit' => array('feeds_ui_mapping_form_add_submit'), - ); - $form['save'] = array( - '#type' => 'submit', - '#value' => t('Save'), - '#attributes' => array('class' => 'feeds-ui-hidden-submit'), - ); - return $form; -} - -/** - * Submit handler for add button on feeds_ui_mapping_form(). - */ -function feeds_ui_mapping_form_add_submit($form, &$form_state) { - $importer = $form['#importer']; - try { - // addMapping method is now used for custom FeedsProcessor - // to declare what they want to do when mapping is added - // e.g. FeedsDataProcessor - if (method_exists($importer->processor, 'addMapping')) { - $importer->processor->addMapping($form_state['values']['source'], $form_state['values']['target']); - } - - $form_state['values']['mappings'][] = array( - 'source' => $form_state['values']['source'], - 'target' => $form_state['values']['target'], - 'unique' => FALSE, - ); - $importer->processor->addConfig($form_state['values']); - $importer->processor->save(); - drupal_set_message(t('Mapping has been added.')); - } - catch (Exception $e) { - drupal_set_message($e->getMessage(), 'error'); - } -} - -/** - * Submit handler for save button on feeds_ui_mapping_form(). - */ -function feeds_ui_mapping_form_submit($form, &$form_state) { - $processor = $form['#importer']->processor; - $mappings = $processor->config['mappings']; - // We may set some unique flags to mappings that we remove in the subsequent - // step, that's fine. - if (isset($form_state['values']['unique_flags'])) { - foreach ($form_state['values']['unique_flags'] as $k => $v) { - $form_state['values']['mappings'][$k]['unique'] = $v; - } - } - - foreach ($form_state['values']['remove_flags'] as $k => $v) { - if ($v) { - unset($form_state['values']['mappings'][$k]); - // Keep or keys clean. - $form_state['values']['mappings'] = array_values($form_state['values']['mappings']); - } - } - drupal_set_message(t('Your changes have been saved.')); - - // Because now full mapping is submitted, so no further validation for - // out of sync mappings is needed (i.e. the mapping is modified by other people while - // we are at it). Simply replace it. - $processor->addConfig($form_state['values']); - $processor->save(); -} - -/** * Walk the result of FeedsParser::getMappingSources() or * FeedsProcessor::getMappingTargets() and format them into * a Form API options array. @@ -789,80 +603,4 @@ function theme_feeds_ui_container($container) { $output .= '
'; return $output; -} - -/** - * Theme function for feeds_ui_mapping_form(). - */ -function theme_feeds_ui_mapping_form($form) { - - // Build the actual mapping table. - $header = array( - t('Source'), - t('Target'), - t('Unique target'), - ' ', - ); - $rows = array(); - if (is_array($form['#mappings'])) { - foreach ($form['#mappings'] as $i => $mapping) { - $rows[] = array( - $mapping['source'], - $mapping['target'], - drupal_render($form['unique_flags'][$i]), - drupal_render($form['remove_flags'][$i]), - ); - } - } - if (!count($rows)) { - $rows[] = array( - array( - 'colspan' => 4, - 'data' => t('No mappings defined.'), - ), - ); - } - $rows[] = array( - drupal_render($form['source']), - drupal_render($form['target']), - '', - drupal_render($form['add']), - ); - $output = '
'; - $output .= theme('table', $header, $rows); - - // Build the help table that explains available sources. - $legend = ''; - $rows = array(); - foreach (element_children($form['legendset']['legend']['sources']) as $k) { - $rows[] = array( - drupal_render($form['legendset']['legend']['sources'][$k]['name']), - drupal_render($form['legendset']['legend']['sources'][$k]['description']), - ); - } - if (count($rows)) { - $legend .= '

'. t('Sources') .'

'; - $legend .= theme('table', array(t('Name'), t('Description')), $rows); - } - - // Build the help table that explains available targets. - $rows = array(); - foreach (element_children($form['legendset']['legend']['targets']) as $k) { - $rows[] = array( - drupal_render($form['legendset']['legend']['targets'][$k]['name']), - drupal_render($form['legendset']['legend']['targets'][$k]['description']), - ); - } - $legend .= '

'. t('Targets') .'

'; - $legend .= theme('table', array(t('Name'), t('Description')), $rows); - - // Stick tables into collapsible fieldset. - $form['legendset']['legend'] = array( - '#value' => '
'. $legend .'
', - ); - - $output .= drupal_render($form['legendset']); - - $output .= drupal_render($form); - return $output; -} +} \ No newline at end of file diff --git feeds_ui/feeds_ui.module feeds_ui/feeds_ui.module index 6df8623..9a1f90d 100644 --- feeds_ui/feeds_ui.module +++ feeds_ui/feeds_ui.module @@ -90,9 +90,6 @@ function feeds_ui_theme() { 'feeds_ui_overview_form' => array( 'file' => 'feeds_ui.admin.inc', ), - 'feeds_ui_mapping_form' => array( - 'file' => 'feeds_ui.admin.inc', - ), 'feeds_ui_edit_page' => array( 'file' => 'feeds_ui.admin.inc', ), diff --git includes/FeedsBatch.inc includes/FeedsBatch.inc index a7f9aec..4c872b8 100644 --- includes/FeedsBatch.inc +++ includes/FeedsBatch.inc @@ -302,6 +302,22 @@ class FeedsImportBatch extends FeedsBatch { public function getItemCount() { return count($this->items); } + + /** + * Get mapping sources after the batch items has been populated + */ + public function getMappingSources() { + $sources = array(); + if ($this->total > 0) { + foreach ($this->items[0] as $k => $v) { + $sources[$k] = array( + 'title' => $k, + 'description' => '', + ); + } + } + return $sources; + } } /** diff --git plugins/FeedsCSVParser.inc plugins/FeedsCSVParser.inc index fe82032..fe3aba8 100644 --- plugins/FeedsCSVParser.inc +++ plugins/FeedsCSVParser.inc @@ -72,7 +72,7 @@ class FeedsCSVParser extends FeedsParser { $form = array(); $form['#weight'] = -10; - $mappings = feeds_importer($this->id)->processor->config['mappings']; + $mappings = feeds_importer($this->id)->processor->getMappings(); $sources = $uniques = array(); foreach ($mappings as $mapping) { $sources[] = $mapping['source']; diff --git plugins/FeedsDataProcessor.inc plugins/FeedsDataProcessor.inc index cbdb6af..baedc44 100644 --- plugins/FeedsDataProcessor.inc +++ plugins/FeedsDataProcessor.inc @@ -230,6 +230,7 @@ class FeedsDataProcessor extends FeedsProcessor { 'update_existing' => FEEDS_SKIP_EXISTING, 'expire' => FEEDS_EXPIRE_NEVER, // Don't expire items by default. 'mappings' => array(), + 'mapping_on_import' => 0, ); } diff --git plugins/FeedsDataProcessor.inc.orig plugins/FeedsDataProcessor.inc.orig deleted file mode 100644 index 88cd283..0000000 --- plugins/FeedsDataProcessor.inc.orig +++ /dev/null @@ -1,365 +0,0 @@ -expiryTime(); - - while ($item = $batch->shiftItem()) { - $id = $this->existingItemId($batch, $source); - - if ($id === FALSE || $this->config['update_existing']) { - // Map item to a data record, feed_nid and timestamp are mandatory. - $data = array(); - $data['feed_nid'] = $source->feed_nid; - $data = $this->map($batch, $data); - if (!isset($data['timestamp'])) { - $data['timestamp'] = FEEDS_REQUEST_TIME; - } - - // Only save if this item is not expired. - if ($expiry_time != FEEDS_EXPIRE_NEVER && $data['timestamp'] < (FEEDS_REQUEST_TIME - $expiry_time)) { - continue; - } - - // Save data. - if ($id !== FALSE) { - $data['id'] = $id; - $this->handler()->update($data, 'id'); - $updated++; - } - else { - $this->handler()->insert($data); - $inserted++; - } - } - } - - // Set messages. - if ($inserted) { - drupal_set_message(format_plural($inserted, 'Created @number item.', 'Created @number items.', array('@number' => $inserted))); - } - elseif ($updated) { - drupal_set_message(format_plural($updated, 'Updated @number item.', 'Updated @number items.', array('@number' => $updated))); - } - else { - drupal_set_message(t('There are no new items.')); - } - } - - /** - * Implementation of FeedsProcessor::clear(). - * - * Delete all data records for feed_nid in this table. - */ - public function clear(FeedsBatch $batch, FeedsSource $source) { - $clause = array( - 'feed_nid' => $source->feed_nid, - ); - $num = $this->handler()->delete($clause); - drupal_set_message('All items of this feed have been deleted.'); - } - - /** - * Implement expire(). - */ - public function expire($time = NULL) { - if ($time === NULL) { - $time = $this->expiryTime(); - } - if ($time == FEEDS_EXPIRE_NEVER) { - return FEEDS_BATCH_COMPLETE; - } - $clause = array( - 'timestamp' => array( - '<', - FEEDS_REQUEST_TIME - $time, - ), - ); - $num = $this->handler()->delete($clause); - drupal_set_message(format_plural($num, 'Expired @number record from @table.', 'Expired @number records from @table.', array('@number' => $num, '@table' => $this->tableName()))); - return FEEDS_BATCH_COMPLETE; - } - - /** - * Return expiry time. - */ - public function expiryTime() { - return $this->config['expire']; - } - - /** - * Override parent::addMapping() and create a new field for new mapping - * targets. - * - * @see getMappingTargets(). - */ - public function addMapping($source, $target, $unique = FALSE) { - if (empty($source) || empty($target)) { - return; - } - - // Create a new field with targets that start with "new:" - @list($new, $type) = explode(':', $target); - - if ($new == 'new') { - // Build a field name from the source key. - $field_name = data_safe_name($source); - // Get the full schema spec from data. - $type = data_get_field_definition($type); - // Add the field to the table. - $schema = $this->table()->get('table_schema'); - if (!isset($schema['fields'][$field_name])) { - $target = $this->table()->addField($field_name, $type); - // Let the user know. - drupal_set_message(t('Created new field "!name".', array('!name' => $field_name))); - } - else { - throw new Exception(t('Field !field_name already exists as a mapping target. Remove it from mapping if you would like to map another source to it. Remove it from !data_table table if you would like to change its definition.', array('!field_name' => $field_name, '!data_table' => l($this->table()->get('name'), 'admin/content/data')))); - } - } - - // Let parent populate the mapping configuration. - parent::addMapping($source, $target, $unique); - } - - /** - * Return available mapping targets. - */ - public function getMappingTargets() { - $schema = $this->table()->get('table_schema'); - $meta = $this->table()->get('meta'); - - // Collect all existing fields except id and field_nid and offer them as - // mapping targets. - $existing_fields = $new_fields = array(); - if (isset($schema['fields'])) { - foreach ($schema['fields'] as $field_name => $field) { - if (!in_array($field_name, array('id', 'feed_nid'))) { - // Any existing field can be optionally unique. - // @todo Push this reverse mapping of spec to short name into data - // module. - $type = $field['type']; - if ($type == 'int' && $field['unsigned']) { - $type = 'unsigned int'; - } - $existing_fields[$field_name] = array( - 'name' => empty($meta['fields'][$field_name]['label']) ? $field_name : $meta['fields'][$field_name]['label'], - 'description' => t('Field of type !type.', array('!type' => $type)), - 'optional_unique' => TRUE, - ); - } - } - } - - // Do the same for every joined table. - foreach ($this->handler()->joined_tables as $table) { - $schema = data_get_table($table)->get('table_schema'); - if (isset($schema['fields'])) { - foreach ($schema['fields'] as $field_name => $field) { - if (!in_array($field_name, array('id', 'feed_nid'))) { - // Fields in joined tables can't be unique. - $type = $field['type']; - if ($type == 'int' && $field['unsigned']) { - $type = 'unsigned int'; - } - $existing_fields["$table.$field_name"] = array( - 'name' => $table .'.'. (empty($meta['fields'][$field_name]['label']) ? $field_name : $meta['fields'][$field_name]['label']), - 'description' => t('Joined field of type !type.', array('!type' => $type)), - 'optional_unique' => FALSE, - ); - } - } - } - } - - // Now add data field types as mapping targets. - $field_types = drupal_map_assoc(array_keys(data_get_field_definitions())); - foreach ($field_types as $k => $v) { - $new_fields['new:'. $k] = array( - 'name' => t('[new] !type', array('!type' => $v)), - 'description' => t('Creates a new column of type !type.', array('!type' => $v)), - ); - } - $fields = $new_fields + $existing_fields; - drupal_alter('feeds_data_processor_targets', $fields, $this->table()->get('name')); - return $fields; - } - - /** - * Set target element, bring element in a FeedsDataHandler format. - */ - public function setTargetElement(&$target_item, $target_element, $value) { - if (empty($value)) { - return; - } - if (strpos($target_element, '.')) { - /** - Add field in FeedsDataHandler format. - - This is the tricky part, FeedsDataHandler expects an *array* of records - at #[joined_table_name]. We need to iterate over the $value that has - been mapped to this element and create a record array from each of - them. - */ - list($table, $field) = explode('.', $target_element); - - $values = array(); - $value = is_array($value) ? $value : array($value); - foreach ($value as $v) { - // Create a record array. - $values[] = array( - $field => $v, - ); - } - if (is_array($target_item["#$table"])) { - $target_item["#$table"] = array_merge($target_item["#$table"], $values); - } - else { - $target_item["#$table"] = $values; - } - } - else { - if (is_array($target_item[$target_element]) && is_array($value)) { - $target_item[$target_element] = array_merge($target_item[$target_element], $value); - } - else { - $target_item[$target_element] = $value; - } - } - } - - /** - * Iterate through unique targets and try to load existing records. - * Return id for the first match. - */ - protected function existingItemId(FeedsImportBatch $batch, FeedsSource $source) { - foreach ($this->uniqueTargets($batch) as $target => $value) { - if ($records = $this->handler()->load(array('feed_nid' => $source->feed_nid, $target => $value))) { - return $records[0]['id']; - } - } - return FALSE; - } - - /** - * Override parent::configDefaults(). - */ - public function configDefaults() { - return array( - 'update_existing' => FEEDS_SKIP_EXISTING, - 'expire' => FEEDS_EXPIRE_NEVER, // Don't expire items by default. - 'mappings' => array(), - ); - } - - /** - * Override parent::configForm(). - */ - public function configForm(&$form_state) { - $period = drupal_map_assoc(array(FEEDS_EXPIRE_NEVER, 3600, 10800, 21600, 43200, 86400, 259200, 604800, 604800 * 4, 604800 * 12, 604800 * 24, 31536000), 'feeds_format_expire'); - $form['expire'] = array( - '#type' => 'select', - '#title' => t('Expire items'), - '#options' => $period, - '#description' => t('Select after how much time data records should be deleted. The timestamp target value will be used for determining the item\'s age, see Mapping settings.'), - '#default_value' => $this->config['expire'], - ); - $form['update_existing'] = array( - '#type' => 'checkbox', - '#title' => t('Replace existing records'), - '#description' => t('If an existing record is found for an imported record, replace it. Existing records will be determined using mappings that are a "unique target".'), - '#default_value' => $this->config['update_existing'], - ); - return $form; - } - - /** - * Return the data table name for this feed. - */ - protected function tableName() { - return variable_get('feeds_data_'. $this->id, 'feeds_data_'. $this->id); - } - - /** - * Return the data table for this feed. - * - * @throws Exception $e - * Throws this exception if a table cannot be found and cannot be created. - * - * @todo Make *Data module* throw exception when table can't be found or - * can't be created. - */ - protected function table() { - if ($table = data_get_table($this->tableName())) { - return $table; - } - else { - if ($table = data_create_table($this->tableName(), $this->baseSchema(), feeds_importer($this->id)->config['name'])) { - return $table; - } - } - throw new Exception(t('Could not create data table.')); - } - - /** - * Return a data handler for this table. - * - * Avoids a call to table() to not unnecessarily instantiate DataTable. - */ - protected function handler() { - data_include('DataHandler'); - feeds_include('FeedsDataHandler'); - return FeedsDataHandler::instance($this->tableName(), 'id'); - } - - /** - * Every Feeds data table must have these elements. - */ - protected function baseSchema() { - return array( - 'fields' => array( - 'feed_nid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'id' => array( - 'type' => 'serial', - 'size' => 'normal', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'timestamp' => array( - 'description' => 'The Unix timestamp for the data.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => FALSE, - ), - ), - 'indexes' => array( - 'feed_nid' => array('feed_nid'), - 'id' => array('id'), - 'timestamp' => array('timestamp'), - ), - 'primary key' => array( - '0' => 'id', - ), - ); - } -} diff --git plugins/FeedsFeedNodeProcessor.inc plugins/FeedsFeedNodeProcessor.inc index 06430dd..1152b46 100644 --- plugins/FeedsFeedNodeProcessor.inc +++ plugins/FeedsFeedNodeProcessor.inc @@ -105,6 +105,7 @@ class FeedsFeedNodeProcessor extends FeedsProcessor { 'content_type' => '', 'update_existing' => 0, 'mappings' => array(), + 'mapping_on_import' => 0, ); } diff --git plugins/FeedsNodeProcessor.inc plugins/FeedsNodeProcessor.inc index 5b4e13f..881e784 100644 --- plugins/FeedsNodeProcessor.inc +++ plugins/FeedsNodeProcessor.inc @@ -150,6 +150,7 @@ class FeedsNodeProcessor extends FeedsProcessor { 'expire' => FEEDS_EXPIRE_NEVER, 'mappings' => array(), 'author' => 0, + 'mapping_on_import' => 0, ); } @@ -403,7 +404,6 @@ class FeedsNodeProcessor extends FeedsProcessor { * Used for batch deletion. */ function _feeds_node_delete($nid) { - $node = node_load($nid); db_query("DELETE FROM {node} WHERE nid = %d", $node->nid); diff --git plugins/FeedsProcessor.inc plugins/FeedsProcessor.inc index 26d17da..24b29fd 100644 --- plugins/FeedsProcessor.inc +++ plugins/FeedsProcessor.inc @@ -105,7 +105,10 @@ abstract class FeedsProcessor extends FeedsPlugin { $target_item = (object)$target_item; $convert_to_array = TRUE; } - foreach ($this->config['mappings'] as $mapping) { + + $mappings = $this->getMappings(); + + foreach ($mappings as $mapping) { if (isset($targets[$mapping['target']]['real_target'])) { unset($target_item->{$targets[$mapping['target']]['real_target']}); } @@ -165,14 +168,27 @@ abstract class FeedsProcessor extends FeedsPlugin { * Declare default configuration. */ public function configDefaults() { - return array('mappings' => array()); + return array('mappings' => array(), + 'mapping_on_import' => 0, + ); } /** * Get mappings. + * + * Mappings must not be got directly by using $this->config['mappings'] but instead via + * this function. */ public function getMappings() { - return isset($this->config['mappings']) ? $this->config['mappings'] : array(); + // If the source is not empty, use the source instead. + if (isset($this->source)) { + $config = $this->source->getConfigFor($this); + $mappings = isset($config['mappings']) ? $config['mappings'] : $this->config['mappings']; + } + else { + $mappings = $this->config['mappings']; + } + return $mappings; } /** @@ -226,7 +242,7 @@ abstract class FeedsProcessor extends FeedsPlugin { protected function uniqueTargets(FeedsImportBatch $batch) { $parser = feeds_importer($this->id)->parser; $targets = array(); - foreach ($this->config['mappings'] as $mapping) { + foreach ($this->getMappings() as $mapping) { if ($mapping['unique']) { // Invoke the parser's getSourceElement to retrieve the value for this // mapping's source. @@ -235,4 +251,262 @@ abstract class FeedsProcessor extends FeedsPlugin { } return $targets; } + + /** + * configDefaults for FeedsSource + */ + function sourceDefaults() { + return array ('mappings' => $this->config['mappings']); + } + + /** + * FeedsProcessor has source config + * default: mappings + */ + function hasSourceConfig() { + return TRUE; + } + + /** + * Render the mapping form. + * This function should NOT be overwritten. + * @param + * The location from where this function is called. For now, it will be passed either + * 'import' or 'config' + * @param + * The batch object that contains populated items that has been fetched and parsed. + * This will only be handled if the first parameter is NOT 'config' + */ + public function mappingForm(&$form_state, $loc_id = 'config', $batch = NULL) { + drupal_add_js(drupal_get_path('module', 'feeds_ui') .'/feeds_ui.js'); + + // Get the FeedsImporter object + $importer = feeds_importer($this->id); + + $form = array(); + if ($loc_id == 'config') { + // Mapping on import checkbox + $form['mapping_on_import'] = array( + '#type' => 'checkbox', + '#title' => t('Define mapping on import'), + '#description' => t('If this is checked, the mapping form here will only act as a default mapping. User can further specify the preferred mapping in the import page.'), + '#weight' => -100, + '#default_value' => $this->config['mapping_on_import'], + ); + } + + $form['#theme'] = 'feeds_mapping_form'; + $form['help']['#value'] = feeds_mapping_form_help(); + + // Get mapping sources from parsers or batch and targets from processor, format them + // for output. + // Some parsers do not define mapping sources but let them define on the fly. + if ($loc_id == 'config' || empty($batch)) { + $sources = $importer->parser->getMappingSources(); + } + else { + $sources = $batch->getMappingSources(); + } + + if ($sources) { + $source_options = _feeds_mapping_form_format_options($sources); + foreach ($sources as $k => $source) { + $legend['sources'][$k]['name']['#value'] = empty($source['name']) ? $k : $source['name']; + $legend['sources'][$k]['description']['#value'] = empty($source['description']) ? '' : $source['description']; + } + } + else { + $legend['sources']['#value'] = t('This parser supports free source definitions. Enter the name of the source field in lower case into the Source text field above.'); + } + $targets = $importer->processor->getMappingTargets(); + $target_options = _feeds_mapping_form_format_options($targets); + foreach ($targets as $k => $target) { + $legend['targets'][$k]['name']['#value'] = empty($target['name']) ? $k : $target['name']; + $legend['targets'][$k]['description']['#value'] = empty($target['description']) ? '' : $target['description']; + } + + $form['legendset'] = array( + '#type' => 'fieldset', + '#title' => t('Legend'), + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#tree' => TRUE, + ); + $form['legendset']['legend'] = $legend; + + $mappings = $this->getMappings(); + + // Add unique and remove forms to mappings. + $form['unique_flags'] = $form['remove_flags'] = array( + '#tree' => TRUE, + ); + + if (is_array($mappings)) { + $form['mappings']['#tree'] = TRUE; + + foreach ($mappings as $i => $mapping) { + $mappings[$i]['source'] = isset($source_options) ? $source_options[$mappings[$i]['source']] : $mappings[$i]['source']; + $mappings[$i]['target'] = $target_options[$mappings[$i]['target']]; + $param = array( + 'processor' => $importer->processor, + 'mapping' => $mapping, + ); + + if (isset($targets[$mapping['target']]['optional_unique']) && $targets[$mapping['target']]['optional_unique'] === TRUE) { + + $form['unique_flags'][$i] = array( + '#type' => 'checkbox', + '#default_value' => !empty($mapping['unique']), + '#attributes' => array('class' => 'feeds-ui-trigger-submit'), + ); + } + $form['remove_flags'][$i] = array( + '#type' => 'checkbox', + '#title' => t('Remove'), + '#prefix' => '', + ); + + // Declare the mappings data in the hidden field, so later + // the full mapping data will be submitted. + foreach($mapping as $k => $v) { + $form['mappings'][$i][$k] = array ( + '#type' => 'hidden', + '#value' => $v, + ); + } + } + + $form['#mappings'] = $mappings; + $form['#targets'] = $targets; + if ($sources) { + $form['source'] = array( + '#type' => 'select', + '#options' => array('' => t('Select a source')) + $source_options, + ); + } + else { + $form['source'] = array( + '#type' => 'textfield', + '#size' => 20, + '#default_value' => t('Name of source field'), + '#attributes' => array('class' => 'hide-text-on-focus'), + ); + } + $form['target'] = array( + '#type' => 'select', + '#options' => array('' => t('Select a target')) + _feeds_mapping_form_format_options($targets), + ); + $form['add'] = array( + '#type' => 'submit', + '#value' => t('Add'), + '#submit' => array('feeds_form_submit'), + ); + } + + $form['save'] = array( + '#type' => 'submit', + '#value' => t('Save'), + '#attributes' => array('class' => 'feeds-ui-hidden-submit'), + '#submit' => array('feeds_form_submit'), + ); + return $form; + } + + /** + * Submit handler for FeedsProcessor::mappingForm() + */ + function mappingFormSubmit(&$values) { + // If add new mappings submit is invoked. + if ($values['op'] == 'Add') { + if (!empty($values['source']) && !empty($values['target'])) { + if (method_exists($this, 'addMapping')) { + $this->addMapping($values['source'], $values['target']); + } + $values['mappings'][] = array( + 'source' => $values['source'], + 'target' => $values['target'], + 'unique' => FALSE, + ); + } + } + // If other submit is invoked. + else { + // We may set some unique flags to mappings that we remove in the subsequent + // step, that's fine. + if (isset($values['unique_flags'])) { + foreach ($values['unique_flags'] as $k => $v) { + $values['mappings'][$k]['unique'] = $v; + } + } + + if (isset($values['remove_flags'])) { + foreach ($values['remove_flags'] as $k => $v) { + if ($v) { + unset($values['mappings'][$k]); + // Keep or keys clean. + $values['mappings'] = array_values($values['mappings']); + } + } + } + } + + if ($this->config['mapping_on_import'] && !empty($this->source)) { + $source_values[get_class($this)] = $values; + $this->source->addConfig($source_values); + $this->source->save(); + } + else { + $this->addConfig($values); + $this->save(); + } + + drupal_set_message(t('Your changes have been saved.')); + feeds_cache_clear(FALSE); + } + + /** + * Validation handler for mappingForm() + */ + function mappingFormValidate(&$values) { + } +} + +/** + * Walk the result of FeedsParser::getMappingSources() or + * FeedsProcessor::getMappingTargets() and format them into + * a Form API options array. + */ +function _feeds_mapping_form_format_options($options) { + $result = array(); + foreach ($options as $k => $v) { + if (is_array($v) && !empty($v['name'])) { + $result[$k] = $v['name']; + } + elseif (is_array($v)) { + $result[$k] = $k; + } + else { + $result[$k] = $v; + } + } + return $result; } + +/** + * Help text for mapping. + */ +function feeds_mapping_form_help() { + return + '

' . + t('Define which elements of a single item of a feed') . + '(= ' . t('Sources') . ') ' . + t('map to which content pieces in Drupal') . + ' (= ' . t('Targets') . '). ' . + t('Make sure that at least one definition has a') . + ' ' . t('Unique target') . '. ' . + t('A unique target means that a value for a target can only occur once. E. g. only one item with the URL ') . + 'http://example.com/story/1 ' . + t('can exist.') . + '

'; +} \ No newline at end of file diff --git plugins/FeedsProcessor.inc.orig plugins/FeedsProcessor.inc.orig deleted file mode 100644 index 77072ef..0000000 --- plugins/FeedsProcessor.inc.orig +++ /dev/null @@ -1,286 +0,0 @@ -feed_nid. It is the - * processor's responsibility to store the feed_nid of an imported item in - * the processing stage. - */ - public abstract function clear(FeedsBatch $batch, FeedsSource $source); - - /** - * Delete feed items younger than now - $time. Do not invoke expire on a - * processor directly, but use FeedsImporter::expire() instead. - * - * @see FeedsImporter::expire(). - * @see FeedsDataProcessor::expire(). - * - * @param $time - * If implemented, all items produced by this configuration that are older - * than FEEDS_REQUEST_TIME - $time should be deleted. - * If $time === NULL processor should use internal configuration. - * - * @return - * FEEDS_BATCH_COMPLETE if all items have been processed, a float between 0 - * and 0.99* indicating progress otherwise. - */ - public function expire($time = NULL) { - return FEEDS_BATCH_COMPLETE; - } - - /** - * Execute mapping on an item. - * - * This method encapsulates the central mapping functionality. When an item is - * processed, it is passed through map() where the properties of $source_item - * are mapped onto $target_item following the processor's mapping - * configuration. - * - * For each mapping FeedsParser::getSourceElement() is executed to retrieve - * the source element, then FeedsProcessor::setTargetElement() is invoked - * to populate the target item properly. Alternatively a - * hook_x_targets_alter() may have specified a callback for a mapping target - * in which case the callback is asked to populate the target item instead of - * FeedsProcessor::setTargetElement(). - * - * @ingroup mappingapi - * - * @see hook_feeds_parser_sources_alter() - * @see hook_feeds_data_processor_targets_alter() - * @see hook_feeds_node_processor_targets_alter() - * @see hook_feeds_term_processor_targets_alter() - * @see hook_feeds_user_processor_targets_alter() - */ - protected function map(FeedsImportBatch $batch, $target_item = NULL) { - - // Static cache $targets as getMappingTargets() may be an expensive method. - static $sources; - if (!isset($sources[$this->id])) { - $sources[$this->id] = feeds_importer($this->id)->parser->getMappingSources(); - } - static $targets; - if (!isset($targets[$this->id])) { - $targets[$this->id] = $this->getMappingTargets(); - } - $parser = feeds_importer($this->id)->parser; - if (empty($target_item)) { - $target_item = array(); - } - - // 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']}); - } - elseif (isset($target_item->{$mapping['target']})) { - 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 - the parser's getSourceElement() method to retrieve the value of the source - element and pass it to the processor's setTargetElement() to stick it - on the right place of the target item. - - If the mapping specifies a callback method, use the callback instead of - setTargetElement(). - */ - self::loadMappers(); - foreach ($this->config['mappings'] as $mapping) { - // Retrieve source element's value from parser. - if (is_array($sources[$this->id][$mapping['source']]) && - isset($sources[$this->id][$mapping['source']]['callback']) && - function_exists($sources[$this->id][$mapping['source']]['callback'])) { - $callback = $sources[$this->id][$mapping['source']]['callback']; - $value = $callback($batch, $mapping['source']); - } - else { - $value = $parser->getSourceElement($batch, $mapping['source']); - } - - // Map the source element's value to the target. - if (is_array($targets[$this->id][$mapping['target']]) && - isset($targets[$this->id][$mapping['target']]['callback']) && - function_exists($targets[$this->id][$mapping['target']]['callback'])) { - $callback = $targets[$this->id][$mapping['target']]['callback']; - $callback($target_item, $mapping['target'], $value); - } - else { - $this->setTargetElement($target_item, $mapping['target'], $value); - } - } - return $target_item; - } - - /** - * Per default, don't support expiry. If processor supports expiry of imported - * items, return the time after which items should be removed. - */ - public function expiryTime() { - return FEEDS_EXPIRE_NEVER; - } - - /** - * Declare default configuration. - */ - public function configDefaults() { - return array('mappings' => array()); - } - - /** - * Add a mapping to existing mappings. - * - * @param $source - * A string that identifies a source element. - * @param $target - * A string that identifies a target element. - * @param $unique - * A boolean that defines whether the target value should be unique. If - * TRUE only one item with a given target value can exist on the local - * system. Compare with existingItemId() and uniqueTargets(). - */ - public function addMapping($source, $target, $unique = FALSE) { - if (!empty($source) && !empty($target)) { - $this->config['mappings'][] = array( - 'source' => $source, - 'target' => $target, - 'unique' => $unique, - ); - } - } - - /** - * Set unique state of a mapping target. - */ - public function setUnique($source, $target, $unique) { - if (!empty($source) && !empty($target)) { - foreach ($this->config['mappings'] as $k => $mapping) { - if ($mapping['source'] == $source && $mapping['target'] == $target) { - $this->config['mappings'][$k]['unique'] = $unique; - } - } - } - } - - /** - * Remove a mapping. - */ - public function removeMapping($source, $target) { - foreach ($this->config['mappings'] as $k => $mapping) { - if ($mapping['source'] == $source && $mapping['target'] == $target) { - unset($this->config['mappings'][$k]); - } - } - // Keep or keys clean. - $this->config['mappings'] = array_values($this->config['mappings']); - } - - /** - * Get mappings. - */ - public function getMappings() { - return isset($this->config['mappings']) ? $this->config['mappings'] : array(); - } - - /** - * Declare possible mapping targets that this processor exposes. - * - * @ingroup mappingapi - * - * @return - * An array of mapping targets. Keys are paths to targets - * separated by ->, values are TRUE if target can be unique, - * FALSE otherwise. - */ - public function getMappingTargets() { - return array(); - } - - /** - * Set a concrete target element. Invoked from FeedsProcessor::map(). - * - * @ingroup mappingapi - */ - public function setTargetElement(&$target_item, $target_element, $value) { - $target_item[$target_element] = $value; - } - - /** - * Retrieve the target item's existing id if available. Otherwise return 0. - * - * @ingroup mappingapi - * - * @param $batch - * A FeedsImportBatch object. - * @param FeedsSource $source - * The source information about this import. - */ - protected function existingItemId(FeedsImportBatch $batch, FeedsSource $source) { - return 0; - } - - /** - * Utility function that iterates over a target array and retrieves all - * sources that are unique. - * - * @param $batch - * A FeedsImportBatch. - * - * @return - * An array where the keys are target field names and the values are the - * elements from the source item mapped to these targets. - */ - protected function uniqueTargets(FeedsImportBatch $batch) { - $parser = feeds_importer($this->id)->parser; - $targets = array(); - foreach ($this->config['mappings'] as $mapping) { - if ($mapping['unique']) { - // Invoke the parser's getSourceElement to retrieve the value for this - // mapping's source. - $targets[$mapping['target']] = $parser->getSourceElement($batch, $mapping['source']); - } - } - return $targets; - } -} diff --git plugins/FeedsTermProcessor.inc plugins/FeedsTermProcessor.inc index af50d6f..c5ab5ff 100644 --- plugins/FeedsTermProcessor.inc +++ plugins/FeedsTermProcessor.inc @@ -131,6 +131,7 @@ class FeedsTermProcessor extends FeedsProcessor { 'vocabulary' => 0, 'update_existing' => FEEDS_SKIP_EXISTING, 'mappings' => array(), + 'mapping_on_import' => 0, ); } diff --git plugins/FeedsUserProcessor.inc plugins/FeedsUserProcessor.inc index b320916..99ff850 100644 --- plugins/FeedsUserProcessor.inc +++ plugins/FeedsUserProcessor.inc @@ -114,6 +114,7 @@ class FeedsUserProcessor extends FeedsProcessor { 'update_existing' => FALSE, 'status' => 1, 'mappings' => array(), + 'mapping_on_import' => 0, ); } diff --git tests/feeds.test.inc tests/feeds.test.inc index 96c470c..552e219 100644 --- tests/feeds.test.inc +++ tests/feeds.test.inc @@ -57,7 +57,7 @@ class FeedsWebTestCase extends DrupalWebTestCase { - + @@ -299,7 +299,6 @@ class FeedsWebTestCase extends DrupalWebTestCase { $unique = !empty($mapping['unique']); unset($mapping['unique']); $this->drupalPost($path, $mapping, t('Add')); - // If unique was set, set the last mapping's unique flag. if ($unique) { $edit = array(