Index: feeds/plugins/FeedsFeedNodeProcessor.inc =================================================================== RCS file: /cvs/drupal/contributions/modules/feeds/plugins/FeedsFeedNodeProcessor.inc,v retrieving revision 1.13 diff -u -r1.13 FeedsFeedNodeProcessor.inc --- feeds/plugins/FeedsFeedNodeProcessor.inc 13 Jul 2010 19:32:06 -0000 1.13 +++ feeds/plugins/FeedsFeedNodeProcessor.inc 22 Jul 2010 11:02:18 -0000 @@ -107,6 +107,7 @@ 'content_type' => '', 'update_existing' => 0, 'mappings' => array(), + 'mapping_on_import' => 0, ); } Index: feeds/plugins/FeedsUserProcessor.inc =================================================================== RCS file: /cvs/drupal/contributions/modules/feeds/plugins/FeedsUserProcessor.inc,v retrieving revision 1.17 diff -u -r1.17 FeedsUserProcessor.inc --- feeds/plugins/FeedsUserProcessor.inc 18 Jul 2010 19:47:37 -0000 1.17 +++ feeds/plugins/FeedsUserProcessor.inc 22 Jul 2010 11:02:18 -0000 @@ -117,6 +117,7 @@ 'update_existing' => FALSE, 'status' => 1, 'mappings' => array(), + 'mapping_on_import' => 0, ); } Index: feeds/plugins/FeedsNodeProcessor.inc =================================================================== RCS file: /cvs/drupal/contributions/modules/feeds/plugins/FeedsNodeProcessor.inc,v retrieving revision 1.45 diff -u -r1.45 FeedsNodeProcessor.inc --- feeds/plugins/FeedsNodeProcessor.inc 18 Jul 2010 18:33:43 -0000 1.45 +++ feeds/plugins/FeedsNodeProcessor.inc 22 Jul 2010 11:02:18 -0000 @@ -153,6 +153,7 @@ 'expire' => FEEDS_EXPIRE_NEVER, 'mappings' => array(), 'author' => 0, + 'mapping_on_import' => 0, ); } @@ -390,7 +391,6 @@ * Used for batch deletion. */ function _feeds_node_delete($nid) { - $node = node_load($nid); db_query("DELETE FROM {node} WHERE nid = %d", $node->nid); Index: feeds/plugins/FeedsDataProcessor.inc =================================================================== RCS file: /cvs/drupal/contributions/modules/feeds/plugins/FeedsDataProcessor.inc,v retrieving revision 1.15 diff -u -r1.15 FeedsDataProcessor.inc --- feeds/plugins/FeedsDataProcessor.inc 13 Jul 2010 19:32:06 -0000 1.15 +++ feeds/plugins/FeedsDataProcessor.inc 22 Jul 2010 11:02:18 -0000 @@ -136,9 +136,6 @@ 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); } /** @@ -257,6 +254,7 @@ 'update_existing' => FEEDS_SKIP_EXISTING, 'expire' => FEEDS_EXPIRE_NEVER, // Don't expire items by default. 'mappings' => array(), + 'mapping_on_import' => 0, ); } Index: feeds/plugins/FeedsProcessor.inc =================================================================== RCS file: /cvs/drupal/contributions/modules/feeds/plugins/FeedsProcessor.inc,v retrieving revision 1.14 diff -u -r1.14 FeedsProcessor.inc --- feeds/plugins/FeedsProcessor.inc 18 Jul 2010 18:33:43 -0000 1.14 +++ feeds/plugins/FeedsProcessor.inc 22 Jul 2010 11:02:18 -0000 @@ -108,7 +108,10 @@ $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']}); } @@ -155,62 +158,27 @@ * 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']); + 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; } /** @@ -264,7 +232,7 @@ protected function uniqueTargets($source_item) { $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. @@ -298,4 +266,263 @@ } $loaded = TRUE; } + + /** + * 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' => '
' . + 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 Index: feeds/plugins/FeedsTermProcessor.inc =================================================================== RCS file: /cvs/drupal/contributions/modules/feeds/plugins/FeedsTermProcessor.inc,v retrieving revision 1.10 diff -u -r1.10 FeedsTermProcessor.inc --- feeds/plugins/FeedsTermProcessor.inc 13 Jul 2010 19:32:06 -0000 1.10 +++ feeds/plugins/FeedsTermProcessor.inc 22 Jul 2010 11:02:18 -0000 @@ -126,6 +126,7 @@ 'vocabulary' => 0, 'update_existing' => 0, 'mappings' => array(), + 'mapping_on_import' => 0, ); } Index: feeds/plugins/FeedsCSVParser.inc =================================================================== RCS file: /cvs/drupal/contributions/modules/feeds/plugins/FeedsCSVParser.inc,v retrieving revision 1.12 diff -u -r1.12 FeedsCSVParser.inc --- feeds/plugins/FeedsCSVParser.inc 11 Jul 2010 01:36:39 -0000 1.12 +++ feeds/plugins/FeedsCSVParser.inc 22 Jul 2010 11:02:18 -0000 @@ -63,7 +63,7 @@ $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']; Index: feeds/includes/FeedsBatch.inc =================================================================== RCS file: /cvs/drupal/contributions/modules/feeds/includes/FeedsBatch.inc,v retrieving revision 1.12 diff -u -r1.12 FeedsBatch.inc --- feeds/includes/FeedsBatch.inc 20 Jun 2010 01:10:29 -0000 1.12 +++ feeds/includes/FeedsBatch.inc 22 Jul 2010 11:02:17 -0000 @@ -98,7 +98,7 @@ if (!file_check_directory($dir, TRUE)) { 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); + $dest = file_destination($dir . '/' . get_class($this) .'_'. drupal_get_token($this->url) .'_'. time(), FILE_EXISTS_RENAME); $this->file_path = file_save_data($this->getRaw(), $dest); if($this->file_path === 0) { throw new Exception(t('Cannot write content to %dest', array('%dest' => $dest))); @@ -180,4 +180,20 @@ $this->items[] = $item; $this->total = 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; + } } Index: feeds/includes/FeedsConfigurable.inc =================================================================== RCS file: /cvs/drupal/contributions/modules/feeds/includes/FeedsConfigurable.inc,v retrieving revision 1.12 diff -u -r1.12 FeedsConfigurable.inc --- feeds/includes/FeedsConfigurable.inc 28 Feb 2010 17:16:25 -0000 1.12 +++ feeds/includes/FeedsConfigurable.inc 22 Jul 2010 11:02:18 -0000 @@ -178,31 +178,43 @@ */ public function configFormValidate(&$values) { } + + /** + * Submission handler for configForm(). + * + * @param $values + */ + public function configFormSubmit(&$values) { + $this->addConfig($values); + $this->save(); + drupal_set_message(t('Your changes have been saved.')); + feeds_cache_clear(FALSE); + } } /** * Config form wrapper. Use to render the configuration form of * a FeedsConfigurable object. * - * @param + * @param $configurable * FeedsConfigurable object. - * @param - * The parent object to perform the save on. + * @param $form_method + * The form method that should be rendered. * * @return * Rendered config form, if available. Empty string otherwise. */ -function feeds_get_config_form($configurable) { +function feeds_get_form($configurable, $form_method) { $form_state = array(); - if ($configurable->configForm($form_state)) { - return drupal_get_form(get_class($configurable) .'_feeds_config_form', $configurable); + if (method_exists($configurable, $form_method)) { + return drupal_get_form(get_class($configurable) .'_feeds_form', $configurable, $form_method); } return ''; } /** * Config form callback. Don't call directly, but use - * feeds_get_config_form($configurable) instead. + * feeds_get_form($configurable, 'method') instead. * * @param * FormAPI $form_state. @@ -210,12 +222,15 @@ * FeedsConfigurable object. * @param * The object to perform the save() operation on. + * @param $form_method + * The $form_method that should be rendered. */ -function feeds_config_form(&$form_state, $configurable) { - $form = $configurable->configForm($form_state); +function feeds_form(&$form_state, $configurable, $form_method) { + $form = $configurable->$form_method($form_state); $form['#configurable'] = $configurable; - $form['#validate'] = array('feeds_config_form_validate'); - $form['#submit'] = array('feeds_config_form_submit'); + $form['#feeds_form_method'] = $form_method; + $form['#validate'] = array('feeds_form_validate'); + $form['#submit'] = array('feeds_form_submit'); $form['submit'] = array( '#type' => 'submit', '#value' => t('Save'), @@ -225,18 +240,21 @@ } /** - * Validation handler for feeds_config_form(). + * Validation handler for feeds_form(). */ -function feeds_config_form_validate($form, &$form_state) { - $form['#configurable']->configFormValidate($form_state['values']); +function feeds_form_validate($form, &$form_state) { + $validate_method = $form['#feeds_form_method'] .'Validate'; + if (method_exists($form['#configurable'], $validate_method)) { + $form['#configurable']->$validate_method($form_state['values']); + } } /** - * Submit handler for feeds_config_form(). + * Submit handler for feeds_form(). */ -function feeds_config_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); -} +function feeds_form_submit($form, &$form_state) { + $submit_method = $form['#feeds_form_method'] .'Submit'; + if (method_exists($form['#configurable'], $submit_method)) { + $form['#configurable']->$submit_method($form_state['values']); + } +} \ No newline at end of file Index: feeds/feeds_ui/feeds_ui.admin.inc =================================================================== RCS file: /cvs/drupal/contributions/modules/feeds/feeds_ui/feeds_ui.admin.inc,v retrieving revision 1.34 diff -u -r1.34 feeds_ui.admin.inc --- feeds/feeds_ui/feeds_ui.admin.inc 10 Jul 2010 23:30:19 -0000 1.34 +++ feeds/feeds_ui/feeds_ui.admin.inc 22 Jul 2010 11:02:17 -0000 @@ -36,17 +36,6 @@ } /** - * 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) { @@ -308,18 +297,22 @@ ctools_include('dependent'); if (empty($plugin_key)) { $active_container['title'] = t('Basic settings'); - $active_container['body'] = feeds_get_config_form($importer); + $active_container['body'] = feeds_get_form($importer, 'configForm'); } // feeds_plugin_instance() 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)) { $active_container['title'] = t('Settings for !plugin', array('!plugin' => $plugins[$plugin_key]['name'])); - $active_container['body'] = feeds_get_config_form($plugin); + $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; } @@ -354,7 +347,7 @@ // Fetcher. $fetcher = $plugins[$config['fetcher']['plugin_key']]; $actions = array(); - if (feeds_get_config_form($importer->fetcher)) { + if (feeds_get_form($importer->fetcher, 'configForm')) { $actions = array(l(t('Settings'), $path .'/settings/'. $config['fetcher']['plugin_key'])); } $info['title'] = t('Fetcher'); @@ -371,7 +364,7 @@ // Parser. $parser = $plugins[$config['parser']['plugin_key']]; $actions = array(); - if (feeds_get_config_form($importer->parser)) { + if (feeds_get_form($importer->parser, 'configForm')) { $actions = array(l(t('Settings'), $path .'/settings/'. $config['parser']['plugin_key'])); } $info['title'] = t('Parser'); @@ -388,10 +381,10 @@ // Processor. $processor = $plugins[$config['processor']['plugin_key']]; $actions = array(); - if (feeds_get_config_form($importer->processor)) { + 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,153 +474,6 @@ } /** - * 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)) { - 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' => '