diff --git a/feeds_ui/feeds_ui.admin.inc b/feeds_ui/feeds_ui.admin.inc index e5d0f96edd634fdb8292e7e900e86af1e9280dd5..1799fe5f1f2e8eb05bc362512bae105e9784aa3e 100644 --- a/feeds_ui/feeds_ui.admin.inc +++ b/feeds_ui/feeds_ui.admin.inc @@ -232,7 +232,7 @@ function feeds_ui_create_form_submit($form, &$form_state) { * Delete configuration form. */ function feeds_ui_delete_form($form, &$form_state, $importer) { - $form['#importer'] = $importer; + $form['#importer'] = $importer->id; if ($importer->export_type & EXPORT_IN_CODE) { $title = t('Would you really like to revert the importer @importer?', array('@importer' => $importer->config['name'])); $button_label = t('Revert'); @@ -257,7 +257,7 @@ function feeds_ui_delete_form_submit($form, &$form_state) { $form_state['redirect'] = 'admin/structure/feeds'; // Remove importer. - $form['#importer']->delete(); + feeds_importer($form['#importer'])->delete(); // Clear cache, deleting a configuration may have an affect on menu tree. feeds_cache_clear(); @@ -428,7 +428,7 @@ function feeds_ui_plugin_form($form, &$form_state, $importer, $type) { $plugins = FeedsPlugin::byType($type); $form = array(); - $form['#importer'] = $importer; + $form['#importer'] = $importer->id; $form['#plugin_type'] = $type; foreach ($plugins as $key => $plugin) { @@ -454,8 +454,9 @@ function feeds_ui_plugin_form($form, &$form_state, $importer, $type) { */ function feeds_ui_plugin_form_submit($form, &$form_state) { // Set the plugin and save feed. - $form['#importer']->setPlugin($form_state['values']['plugin_key']); - $form['#importer']->save(); + $importer = feeds_importer($form['#importer']); + $importer->setPlugin($form_state['values']['plugin_key']); + $importer->save(); drupal_set_message(t('Changed @type plugin.', array('@type' => $form['#plugin_type']))); } @@ -497,7 +498,7 @@ function feeds_ui_mapping_form($form, &$form_state, $importer) { drupal_add_js(drupal_get_path('module', 'feeds_ui') . '/feeds_ui.js'); $form = array(); - $form['#importer'] = $importer; + $form['#importer'] = $importer->id; $form['#mappings'] = $mappings = $importer->processor->getMappings(); $form['help']['#markup'] = feeds_ui_mapping_help(); $form['#prefix'] = '<div id="feeds-ui-mapping-form-wrapper">'; @@ -534,7 +535,7 @@ function feeds_ui_mapping_form($form, &$form_state, $importer) { $form['legendset']['legend'] = $legend; // Add config forms and remove flags to mappings. - $form['config'] = $form['remove_flags'] = array( + $form['config'] = $form['remove_flags'] = $form['mapping_weight'] = array( '#tree' => TRUE, ); if (is_array($mappings)) { @@ -549,6 +550,18 @@ function feeds_ui_mapping_form($form, &$form_state, $importer) { '#prefix' => '<div class="feeds-ui-checkbox-link">', '#suffix' => '</div>', ); + + $form['mapping_weight'][$i] = array( + '#type' => 'weight', + '#title' => '', + '#default_value' => $i, + '#delta' => 10, + '#attributes' => array( + 'class' => array( + 'feeds-ui-mapping-weight' + ), + ), + ); } } @@ -721,7 +734,7 @@ function feeds_ui_mapping_form_add_validate($form, &$form_state) { * Submit handler for add button on feeds_ui_mapping_form(). */ function feeds_ui_mapping_form_add_submit($form, &$form_state) { - $importer = $form['#importer']; + $importer = feeds_importer($form['#importer']); try { $mappings = $form['#mappings']; $mappings[] = array( @@ -742,7 +755,7 @@ function feeds_ui_mapping_form_add_submit($form, &$form_state) { * Submit handler for save button on feeds_ui_mapping_form(). */ function feeds_ui_mapping_form_submit($form, &$form_state) { - $importer = feeds_importer($form['#importer']->id); + $importer = feeds_importer($form['#importer']); $processor = $importer->processor; $form_state += array( @@ -756,8 +769,8 @@ function feeds_ui_mapping_form_submit($form, &$form_state) { $form_state['mapping_settings'][$form_state['mapping_settings_edit']] = $values; } - // We may set some settings to mappings that we remove in the subsequent - // step, that's fine. + // We may set some settings to mappings that we remove in the subsequent step, + // that's fine. $mappings = $form['#mappings']; foreach ($form_state['mapping_settings'] as $k => $v) { $mappings[$k] = array( @@ -767,14 +780,21 @@ function feeds_ui_mapping_form_submit($form, &$form_state) { } if (!empty($form_state['values']['remove_flags'])) { - foreach ($form_state['values']['remove_flags'] as $k => $v) { - if ($v) { - unset($mappings[$k]); - } + $remove_flags = array_keys(array_filter($form_state['values']['remove_flags'])); + + foreach ($remove_flags as $k) { + unset($mappings[$k]); + unset($form_state['values']['mapping_weight'][$k]); } - // Keep our keys clean. - $mappings = array_values($mappings); } + + // Keep our keys clean. + $mappings = array_values($mappings); + + if (!empty($mappings)) { + array_multisort($form_state['values']['mapping_weight'], $mappings); + } + $processor->addConfig(array('mappings' => $mappings)); $importer->save(); drupal_set_message(t('Your changes have been saved.')); @@ -978,6 +998,7 @@ function theme_feeds_ui_mapping_form($variables) { t('Target'), t('Target configuration'), ' ', + t('Weight'), ); $rows = array(); if (is_array($form['#mappings'])) { @@ -986,17 +1007,21 @@ function theme_feeds_ui_mapping_form($variables) { $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), - $target, - drupal_render($form['config'][$i]), - drupal_render($form['remove_flags'][$i]), + 'data' => array( + check_plain($source), + $target, + drupal_render($form['config'][$i]), + drupal_render($form['remove_flags'][$i]), + drupal_render($form['mapping_weight'][$i]), + ), + 'class' => array('draggable', 'tabledrag-leaf'), ); } } if (!count($rows)) { $rows[] = array( array( - 'colspan' => 4, + 'colspan' => 5, 'data' => t('No mappings defined.'), ), ); @@ -1006,9 +1031,10 @@ function theme_feeds_ui_mapping_form($variables) { drupal_render($form['target']), '', drupal_render($form['add']), + '', ); $output = '<div class="help feeds-admin-ui">' . drupal_render($form['help']) . '</div>'; - $output .= theme('table', array('header' => $header, 'rows' => $rows)); + $output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'feeds-ui-mapping-overview'))); // Build the help table that explains available sources. $legend = ''; @@ -1041,7 +1067,8 @@ function theme_feeds_ui_mapping_form($variables) { ); $output .= drupal_render($form['legendset']); - $output .= drupal_render_children($form); + + drupal_add_tabledrag('feeds-ui-mapping-overview', 'order', 'sibling', 'feeds-ui-mapping-weight'); return $output; } diff --git a/includes/FeedsConfigurable.inc b/includes/FeedsConfigurable.inc index 2c170c75e339ec1088e60f52807010792d1bc8a2..0f3b013c65a3874ee98482a8e803ed0493c690a1 100644 --- a/includes/FeedsConfigurable.inc +++ b/includes/FeedsConfigurable.inc @@ -253,18 +253,39 @@ function feeds_form($form, &$form_state, $configurable, $form_method) { * Validation handler for feeds_form(). */ 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']); - } + _feeds_form_helper($form, $form_state, 'Validate'); } /** - * Submit handler for feeds_config_form(). + * Submit handler for feeds_form(). */ 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']); + _feeds_form_helper($form, $form_state, 'Submit'); +} + +/** + * Helper for Feeds validate and submit callbacks. + */ +function _feeds_form_helper($form, &$form_state, $action) { + $method = $form['#feeds_form_method'] . $action; + $class = get_class($form['#configurable']); + $id = $form['#configurable']->id; + + // Re-initialize the configurable object. Using feeds_importer() and + // feeds_plugin() will ensure that we're using the same instance. We can't + // reuse the previous form instance because feeds_importer() is used to save. + // This will re-initialize all of the plugins anyway, causing some tricky + // saving issues in certain cases. + // See http://drupal.org/node/1672880. + + if ($class == variable_get('feeds_importer_class', 'FeedsImporter')) { + $form['#configurable'] = feeds_importer($id); + } + else { + $form['#configurable'] = feeds_plugin($class, $id); + } + + if (method_exists($form['#configurable'], $method)) { + $form['#configurable']->$method($form_state['values']); } } diff --git a/mappers/path.inc b/mappers/path.inc index d9319d959e415452c6bb62c7652c76c90b544a8d..7d5b99fda96c77ca5f150c05ec5eeee4cc8b6e62 100644 --- a/mappers/path.inc +++ b/mappers/path.inc @@ -11,18 +11,18 @@ * @see FeedsNodeProcessor::getMappingTargets(). */ function path_feeds_processor_targets_alter(&$targets, $entity_type, $bundle_name) { - if (module_exists('path')) { - - switch ($entity_type) { - case 'node': - case 'taxonomy_term': - $targets['path_alias'] = array( - 'name' => t('Path alias'), - 'description' => t('URL path alias of the node.'), - 'callback' => 'path_feeds_set_target', - ); - break; - } + switch ($entity_type) { + case 'node': + case 'taxonomy_term': + case 'user': + $targets['path_alias'] = array( + 'name' => t('Path alias'), + 'description' => t('URL path alias of the node.'), + 'callback' => 'path_feeds_set_target', + 'summary_callback' => 'path_feeds_summary_callback', + 'form_callback' => 'path_feeds_form_callback', + ); + break; } } @@ -33,10 +33,13 @@ function path_feeds_processor_targets_alter(&$targets, $entity_type, $bundle_nam * user has decided to map to and $value contains the value of the feed item * element the user has picked as a source. */ -function path_feeds_set_target($source, $entity, $target, $value) { +function path_feeds_set_target($source, $entity, $target, $value, $mapping) { + if (empty($value)) { + $value = ''; + } // Path alias cannot be multi-valued, so use the first value. - if (is_array($value) && $value) { + if (is_array($value)) { $value = $value[0]; } @@ -55,9 +58,64 @@ function path_feeds_set_target($source, $entity, $target, $value) { } } - // Prevent Pathauto (http://drupal.org/project/pathauto) from overwriting the - // alias. $entity->path['pathauto'] = FALSE; + // Allow pathauto to set the path alias if the option is set, and this value + // is empty. + if (!empty($mapping['pathauto_override']) && !$value) { + $entity->path['pathauto'] = TRUE; + } + else { + $entity->path['alias'] = ltrim($value, '/'); + } + debug($entity); +} - $entity->path['alias'] = ltrim($value, '/'); +/** + * Mapping configuration summary for path.module. + * + * @param $mapping + * Associative array of the mapping settings. + * @param $target + * Array of target settings, as defined by the processor or + * hook_feeds_processor_targets_alter(). + * @param $form + * The whole mapping form. + * @param $form_state + * The form state of the mapping form. + * + * @return + * Returns, as a string that may contain HTML, the summary to display while + * the full form isn't visible. + * If the return value is empty, no summary and no option to view the form + * will be displayed. + */ +function path_feeds_summary_callback($mapping, $target, $form, $form_state) { + if (!module_exists('pathauto')) { + return; + } + + if (empty($mapping['pathauto_override'])) { + return t('Do not allow Pathauto if empty.'); + } + + else { + return t('Allow Pathauto if empty.'); + } +} + +/** + * Settings form callback. + * + * @return + * The per mapping configuration form. Once the form is saved, $mapping will + * be populated with the form values. + */ +function path_feeds_form_callback($mapping, $target, $form, $form_state) { + return array( + 'pathauto_override' => array( + '#type' => 'checkbox', + '#title' => t('Allow Pathauto to set the alias if the value is empty.'), + '#default_value' => !empty($mapping['pathauto_override']), + ), + ); } diff --git a/tests/feeds/path_alias.csv b/tests/feeds/path_alias.csv index af4ebb95d03d2fe3b297d1b6af1e0520c3abed56..a3b8ac2bab4f51e26aedadd00a50e43794a5603b 100644 --- a/tests/feeds/path_alias.csv +++ b/tests/feeds/path_alias.csv @@ -1,10 +1,10 @@ Title,GUID,path -"Ut wisi enim ad minim veniam",1,path1 -"Duis autem vel eum iriure dolor",2,path2 -"Nam liber tempor",3,path3 -Typi non habent"",4,path4 -"Lorem ipsum",5,path5 -"Investigationes demonstraverunt",6,path6 -"Claritas est etiam",7,path7 -"Mirum est notare",8,path8 -"Eodem modo typi",9,path9 +pathauto1,1,path1 +pathauto2,2,path2 +pathauto3,3,path3 +pathauto4,4,path4 +pathauto5,5,path5 +pathauto6,6,path6 +pathauto7,7,path7 +pathauto8,8,path8 +pathauto9,9,path9 diff --git a/tests/feeds_mapper_path.test b/tests/feeds_mapper_path.test index 86509f10ec061ca2a28e5c1212e12135ec3aace0..d3bc4b7cd1e1468833d2f5113defa1ad42254b5e 100644 --- a/tests/feeds_mapper_path.test +++ b/tests/feeds_mapper_path.test @@ -153,3 +153,91 @@ class FeedsMapperPathTestCase extends FeedsMapperTestCase { $this->assertEqual(count($in_db), count($aliases), 'Correct number of aliases in db.'); } } + +/** + * Class for testing Feeds <em>path</em> mapper with pathauto.module. + */ +class FeedsMapperPathPathautoTestCase extends FeedsMapperTestCase { + public static function getInfo() { + return array( + 'name' => 'Mapper: Path with pathauto', + 'description' => 'Test Feeds Mapper support for path aliases and pathauto.', + 'group' => 'Feeds', + 'dependencies' => array('pathauto'), + ); + } + + public function setUp() { + parent::setUp(array('pathauto')); + } + + /** + * Basic for allowing pathauto to override the alias. + */ + public function test() { + + // Create importer configuration. + $this->createImporterConfiguration($this->randomName(), 'path_test'); + $this->setPlugin('path_test', 'FeedsFileFetcher'); + $this->setPlugin('path_test', 'FeedsCSVParser'); + $this->addMappings('path_test', array( + 0 => array( + 'source' => 'Title', + 'target' => 'title', + 'unique' => FALSE, + ), + 1 => array( + 'source' => 'does_not_exist', + 'target' => 'path_alias', + 'pathauto_override' => TRUE, + ), + 2 => array( + 'source' => 'GUID', + 'target' => 'guid', + 'unique' => TRUE, + ), + )); + + // Turn on update existing. + $this->setSettings('path_test', 'FeedsNodeProcessor', array('update_existing' => 2)); + + // Import RSS file. + $this->importFile('path_test', $this->absolutePath() . '/tests/feeds/path_alias.csv'); + $this->assertText('Created 9 nodes'); + + $aliases = array(); + + for ($i = 1; $i <= 9; $i++) { + $aliases[] = "path$i"; + } + + $this->assertAliasCount($aliases); + + // Adding a mapping will force update. + $this->addMappings('path_test', array( + 3 => array( + 'source' => 'fake', + 'target' => 'body', + ), + )); + // Import RSS file. + $this->importFile('path_test', $this->absolutePath() . '/tests/feeds/path_alias.csv'); + $this->assertText('Updated 9 nodes'); + + // Check that duplicate aliases are not created. + $this->assertAliasCount($aliases); + } + + public function assertAliasCount($aliases) { + debug($aliases); + $in_db = db_select('url_alias', 'a') + ->fields('a') + ->condition('a.alias', $aliases) + ->execute() + ->fetchAll(); + + debug($in_db); + + $this->assertEqual(count($in_db), count($aliases), 'Correct number of aliases in db.'); + } +}