diff --git a/feeds.info b/feeds.info index 6e32601..eadb544 100644 --- a/feeds.info +++ b/feeds.info @@ -36,6 +36,7 @@ files[] = tests/feeds_mapper_date.test files[] = tests/feeds_mapper_date_multiple.test files[] = tests/feeds_mapper_field.test files[] = tests/feeds_mapper_file.test +files[] = tests/feeds_mapper_multilingual_fields.test files[] = tests/feeds_mapper_path.test files[] = tests/feeds_mapper_profile.test files[] = tests/feeds_mapper_unique.test diff --git a/feeds.module b/feeds.module index 4418e9c..8becf38 100644 --- a/feeds.module +++ b/feeds.module @@ -905,6 +905,83 @@ function feeds_item_info_save($entity, $entity_id) { } /** + * Implements hook_feeds_processor_targets_alter(). + */ +function locale_feeds_processor_targets_alter(array &$targets, $entity_type, $bundle_name) { + $language_options = feeds_mapper_options_language(); + + foreach ($targets as $target => $target_config) { + list($field_name) = explode(':', $target); + if (!$info = field_info_field($field_name)) { + continue; + } + if (!$info['translatable']) { + continue; + } + + $targets[$target]['summary_callback'][] = 'feeds_mapper_locale_summary'; + $targets[$target]['form_callback'][] = 'feeds_mapper_locale_form'; + $targets[$target]['preprocess_callback'][] = 'feeds_mapper_locale_preprocess'; + } +} + +/** + * Summary callback for language configuration. + */ +function feeds_mapper_locale_summary(array $mapping, array $target, array $form, array &$form_state) { + list($field_name) = explode(':', $mapping['target']); + $info = field_info_field($field_name); + + if ($info['translatable']) { + $language_options = feeds_mapper_options_language(); + if (empty($mapping['language'])) { + return t('Language: @search', array('@search' => $language_options[LANGUAGE_NONE])); + } + return t('Language: @search', array('@search' => $language_options[$mapping['language']])); + } +} + +/** + * Callback for language mapper configuration form. + */ +function feeds_mapper_locale_form(array $mapping, array $target, array $form, array &$form_state) { + list($field_name) = explode(':', $mapping['target']); + $info = field_info_field($field_name); + if ($info['translatable']) { + $form['language'] = array( + '#type' => 'select', + '#title' => t('Language'), + '#options' => feeds_mapper_options_language(), + '#default_value' => !empty($mapping['language']) ? $mapping['language'] : LANGUAGE_NONE, + '#description' => t('You can select a particular field language (or language nautral) or use language code saved in entity\'s language property (e.g. $node->language) you set by some mapper above.'), + ); + } + return $form; +} + +/** + * Callback for processing the mapping configuration. + */ +function feeds_mapper_locale_preprocess(FeedsSource $source, $entity, $target, array $terms, array &$mapping) { + // Set the language of the field depending on the mapping configuration. + $mapping['language'] = !empty($mapping['language']) ? $mapping['language'] : LANGUAGE_NONE; + $mapping['language'] = $mapping['language'] === 'from_property' ? $entity->language : $mapping['language']; +} + +/** + * Return the options list of language options for target configuration. + * + * @return array + * A list of available language options. + */ +function feeds_mapper_options_language() { + return array( + 'from_property' => t('Inherit language'), + LANGUAGE_NONE => t('Language neutral'), + ) + locale_language_list('name'); +} + +/** * @} */ diff --git a/feeds_ui/feeds_ui.admin.inc b/feeds_ui/feeds_ui.admin.inc index 1ed2de3..01a7493 100644 --- a/feeds_ui/feeds_ui.admin.inc +++ b/feeds_ui/feeds_ui.admin.inc @@ -635,10 +635,8 @@ function feeds_ui_mapping_settings_form($form, $form_state, $i, $mapping, $targe if ($form_state['mapping_settings_edit'] === $i) { $settings_form = array(); // Build the form. - if (!empty($target['form_callback'])) { - foreach ((array) $target['form_callback'] as $callback) { - $settings_form += call_user_func($callback, $mapping, $target, $form, $form_state); - } + foreach ((array) $target['form_callback'] as $callback) { + $settings_form += $callback($mapping, $target, array(), $form_state); } // Merge in the optional unique form. @@ -662,14 +660,12 @@ function feeds_ui_mapping_settings_form($form, $form_state, $i, $mapping, $targe ); } else { - $summary = ''; // Build the summary. - if (!empty($target['summary_callback'])) { - foreach ((array) $target['summary_callback'] as $callback) { - $summary[] = call_user_func($callback, $mapping, $target, $form, $form_state); - } - $summary = implode('
', $summary); + $summary = array(); + foreach ((array) $target['summary_callback'] as $callback) { + $summary[] = $callback($mapping, $target, $form, $form_state); } + $summary = implode('
', $summary); // Append the optional unique summary. if ($optional_unique_summary = feeds_ui_mapping_settings_optional_unique_summary($mapping, $target, $form, $form_state)) { diff --git a/mappers/date.inc b/mappers/date.inc index abb56d3..165db5a 100644 --- a/mappers/date.inc +++ b/mappers/date.inc @@ -35,7 +35,7 @@ function date_feeds_processor_targets($entity_type, $bundle_name) { /** * Callback for setting target values. */ -function date_feeds_set_target($source, $entity, $target, array $values) { +function date_feeds_set_target(FeedsSource $source, $entity, $target, array $values, array $mapping) { list($field_name, $sub_field) = explode(':', $target, 2); $delta = 0; diff --git a/mappers/file.inc b/mappers/file.inc index b21a0f7..3c73d38 100644 --- a/mappers/file.inc +++ b/mappers/file.inc @@ -42,13 +42,9 @@ function file_feeds_processor_targets($entity_type, $bundle_name) { } /** - * Callback for mapping. Here is where the actual mapping happens. - * - * When the callback is invoked, $target contains the name of the field the - * user has decided to map to and $value contains the value of the feed item - * element the user has picked as a source. + * Callback for mapping files. */ -function file_feeds_set_target($source, $entity, $target, array $values) { +function file_feeds_set_target(FeedsSource $source, $entity, $target, array $values, $mapping = array()) { // Add default of uri for backwards compatibility. list($field_name, $sub_field) = explode(':', $target . ':uri'); $info = field_info_field($field_name); @@ -84,30 +80,30 @@ function file_feeds_set_target($source, $entity, $target, array $values) { } // Populate entity. - $field = isset($entity->$field_name) ? $entity->$field_name : array(LANGUAGE_NONE => array()); + $field = isset($entity->$field_name) ? $entity->$field_name : array($mapping['language'] => array()); $delta = 0; foreach ($values as $v) { if ($info['cardinality'] == $delta) { break; } - if (!isset($field[LANGUAGE_NONE][$delta])) { - $field[LANGUAGE_NONE][$delta] = array(); + if (!isset($field[$mapping['language']][$delta])) { + $field[$mapping['language']][$delta] = array(); } switch ($sub_field) { case 'alt': case 'title': - $field[LANGUAGE_NONE][$delta][$sub_field] = $v; + $field[$mapping['language']][$delta][$sub_field] = $v; break; case 'uri': if ($v) { try { $file = $v->getFile($destination); - $field[LANGUAGE_NONE][$delta] += (array) $file; + $field[$mapping['language']][$delta] += (array) $file; // @todo: Figure out how to properly populate this field. - $field[LANGUAGE_NONE][$delta]['display'] = 1; + $field[$mapping['language']][$delta]['display'] = 1; } catch (Exception $e) { watchdog_exception('Feeds', $e, nl2br(check_plain($e))); diff --git a/mappers/link.inc b/mappers/link.inc index 9b4c011..7a64e10 100644 --- a/mappers/link.inc +++ b/mappers/link.inc @@ -41,10 +41,10 @@ function link_feeds_processor_targets($entity_type, $bundle_name) { * user has decided to map to and $value contains the value of the feed item * element the user has picked as a source. */ -function link_feeds_set_target($source, $entity, $target, array $values) { +function link_feeds_set_target(FeedsSource $source, $entity, $target, array $values, array $mapping) { list($field_name, $column) = explode(':', $target); - $field = isset($entity->$field_name) ? $entity->$field_name : array('und' => array()); + $field = isset($entity->$field_name) ? $entity->$field_name : array($mapping['language'] => array()); $delta = 0; foreach ($values as $value) { @@ -53,7 +53,7 @@ function link_feeds_set_target($source, $entity, $target, array $values) { } if (is_scalar($value)) { - $field['und'][$delta][$column] = (string) $value; + $field[$mapping['language']][$delta][$column] = (string) $value; } $delta++; } diff --git a/mappers/number.inc b/mappers/number.inc index 056a285..6541aa1 100644 --- a/mappers/number.inc +++ b/mappers/number.inc @@ -37,9 +37,9 @@ function number_feeds_processor_targets($entity_type, $bundle_name) { * * Ensure that $value is a numeric to avoid database errors. */ -function number_feeds_set_target($source, $entity, $target, array $values) { +function number_feeds_set_target(FeedsSource $source, $entity, $target, array $values, array $mapping) { // Iterate over all values. - $field = isset($entity->$target) ? $entity->$target : array('und' => array()); + $field = isset($entity->$target) ? $entity->$target : array($mapping['language'] => array()); foreach ($values as $value) { @@ -48,7 +48,7 @@ function number_feeds_set_target($source, $entity, $target, array $values) { } if (is_numeric($value)) { - $field['und'][] = array('value' => $value); + $field[$mapping['language']][] = array('value' => $value); } } diff --git a/mappers/taxonomy.inc b/mappers/taxonomy.inc index a1d5fe6..5b6e5ef 100644 --- a/mappers/taxonomy.inc +++ b/mappers/taxonomy.inc @@ -96,7 +96,7 @@ function taxonomy_feeds_preprocess_callback(FeedsSource $source, $entity, $targe * * @todo Do not create new terms for non-autotag fields. */ -function taxonomy_feeds_set_target($source, $entity, $target, array $terms, $mapping = array()) { +function taxonomy_feeds_set_target(FeedsSource $source, $entity, $target, array $terms, array $mapping) { $info = field_info_field($target); $cache = &drupal_static(__FUNCTION__); @@ -118,10 +118,10 @@ function taxonomy_feeds_set_target($source, $entity, $target, array $terms, $map ->range(0, 1); - $field = isset($entity->$target) ? $entity->$target : array('und' => array()); + $field = isset($entity->$target) ? $entity->$target : array($mapping['language'] => array()); // Allow for multiple mappings to the same target. - $delta = count($field['und']); + $delta = count($field[$mapping['language']]); // Iterate over all values. foreach ($terms as $term) { @@ -174,7 +174,7 @@ function taxonomy_feeds_set_target($source, $entity, $target, array $terms, $map } if ($tid && isset($cache['allowed_values'][$target][$tid])) { - $field['und'][] = array('tid' => $tid); + $field[$mapping['language']][] = array('tid' => $tid); $delta++; } } diff --git a/mappers/text.inc b/mappers/text.inc index 5696f4d..7d4c60d 100644 --- a/mappers/text.inc +++ b/mappers/text.inc @@ -47,7 +47,7 @@ function text_feeds_processor_targets($entity_type, $bundle_name) { /** * Callback for mapping text fields. */ -function text_feeds_set_target(FeedsSource $source, $entity, $target, array $values, array $mapping = array()) { +function text_feeds_set_target(FeedsSource $source, $entity, $target, array $values, array $mapping) { list($field_name, $column) = explode(':', $target . ':value'); if ($column === 'value' && isset($source->importer->processor->config['input_format'])) { @@ -58,7 +58,7 @@ function text_feeds_set_target(FeedsSource $source, $entity, $target, array $val ); } - $field = isset($entity->$field_name) ? $entity->$field_name : array('und' => array()); + $field = isset($entity->$field_name) ? $entity->$field_name : array($mapping['language'] => array()); // Iterate over all values. $delta = 0; @@ -70,10 +70,10 @@ function text_feeds_set_target(FeedsSource $source, $entity, $target, array $val if (is_scalar($value) && strlen($value)) { - $field['und'][$delta][$column] = (string) $value; + $field[$mapping['language']][$delta][$column] = (string) $value; if (isset($mapping['format'])) { - $field['und'][$delta]['format'] = $mapping['format']; + $field[$mapping['language']][$delta]['format'] = $mapping['format']; } } diff --git a/plugins/FeedsProcessor.inc b/plugins/FeedsProcessor.inc index 5dc3f36..baa59fd 100755 --- a/plugins/FeedsProcessor.inc +++ b/plugins/FeedsProcessor.inc @@ -80,6 +80,21 @@ abstract class FeedsProcessor extends FeedsPlugin { } /** + * Provides a list of language options for use in select lists. + * + * @return array + * A keyed array of langcode => language. + */ + public function languageOptions() { + if (!module_exists('locale')) { + return array(); + } + return array( + LANGUAGE_NONE => t('Language neutral'), + ) + locale_language_list('name') ; + } + + /** * Create a new entity. * * @param $source @@ -669,11 +684,10 @@ abstract class FeedsProcessor extends FeedsPlugin { $value = array($value); } - if (!empty($mapping['preprocess_callback'])) { - foreach ($mapping['preprocess_callback'] as $preprocess) { - $preprocess($source, $target_item, $mapping['target'], $value, $mapping); - } + foreach ($mapping['preprocess_callback'] as $preprocess) { + $preprocess($source, $target_item, $mapping['target'], $value, $mapping); } + $mapping += array('language' => $this->config['language']); $callback($source, $target_item, $mapping['target'], $value, $mapping); } @@ -709,6 +723,7 @@ abstract class FeedsProcessor extends FeedsPlugin { 'input_format' => NULL, 'skip_hash_check' => FALSE, 'bundle' => $bundle, + 'language' => LANGUAGE_NONE, ); } @@ -735,6 +750,16 @@ abstract class FeedsProcessor extends FeedsPlugin { ); } + if (module_exists('locale')) { + $form['language'] = array( + '#type' => 'select', + '#options' => $this->languageOptions(), + '#title' => t('Language'), + '#required' => TRUE, + '#default_value' => $this->config['language'], + ); + } + $tokens = array('@entities' => strtolower($info['label plural'])); $form['update_existing'] = array( @@ -836,6 +861,13 @@ abstract class FeedsProcessor extends FeedsPlugin { $type = $this->entityType(); $bundle = $this->bundle(); $targets += module_invoke_all('feeds_processor_targets', $type, $bundle); + foreach ($targets as $target => $info) { + $targets[$target] += array( + 'summary_callback' => array(), + 'form_callback' => array(), + 'preprocess_callback' => array(), + ); + } drupal_alter('feeds_processor_targets', $targets, $type, $bundle); } diff --git a/tests/feeds_tests.module b/tests/feeds_tests.module index 5ce8961..1b22539 100644 --- a/tests/feeds_tests.module +++ b/tests/feeds_tests.module @@ -128,8 +128,8 @@ function feeds_tests_feeds_processor_targets($entity_type, $bundle) { * * @see my_module_set_target() */ -function feeds_tests_mapper_set_target($source, $entity, $target, $value, $mapping) { - $entity->body['und'][0]['value'] = serialize($mapping); +function feeds_tests_mapper_set_target(FeedsSource $source, $entity, $target, array $values, array $mapping) { + $entity->body[$mapping['language']][0]['value'] = serialize($mapping); } /**