diff --git a/field_collection.module" b/field_collection.module --- a/field_collection.module +++ b/field_collection.module @@ -1983,3 +1983,231 @@ 'revision_id' => $field_collection->revision_id, ); } + +/** + * Implements hook_feeds_processor_targets_alter() + */ +function field_collection_feeds_processor_targets_alter(&$targets, $entity_type, $bundle_name) { + // Thou shalt not attempt Sorcery! + if ($entity_type == 'field_collection_item') { + return; + } + foreach (field_info_instances($entity_type, $bundle_name) as $name => $instance) { + $info = field_info_field($name); + if ($info['type'] == 'field_collection') { + $new_targets = array(); + feeds_alter('feeds_processor_targets', $new_targets, 'field_collection_item', $info['field_name']); + foreach ($new_targets as $sub_name => $target) { + $new_name = $info['field_name'] . ':' . $sub_name; + $targets[$new_name] = $target; + if (isset($target['name'])) { + $targets[$new_name]['name'] = $instance['label'] . ': ' . $target['name']; + } + // We override callback for now and retrieve original later. + $targets[$new_name]['callback'] = 'field_collection_feeds_set_target'; + $targets[$new_name]['form_callback'] = 'field_collection_feeds_mapper_form_language'; + $targets[$new_name]['summary_callback'] = 'field_collection_feeds_mapper_summary_language'; + } + } + } +} + +/** + * Process Field Collection items + */ +function field_collection_feeds_set_target($source, $entity, $target, $value, $main_mapping) { + static $sub_targets = array(); + $args = explode(':', $target); + $target = array_shift($args); + $sub_target = implode(':', $args); + // Retrieve the submapping so we can pass it to the callback + $sub_mapping = array(); + $config = $source->importer()->getConfig(); + if (!empty($config['processor']['config']['mappings'])) { + foreach ( $config['processor']['config']['mappings'] as $mapping ) { + if ( $mapping['target'] == $target . ':' . $sub_target && $main_mapping['language'] == $mapping['language']) { + $sub_mapping = $mapping; + $sub_mapping['target'] = $sub_target; + break; + } + } + } + // Now we retrieve old callbacks and keep then on a static cache + if (!isset($sub_targets[$target])) { + feeds_alter('feeds_processor_targets', $sub_targets[$target], 'field_collection_item', $target, $sub_mapping); + } + $_sub_targets = $sub_targets[$target]; + $value = is_array($value) ? $value : array($value); + $info = field_info_field($target); + $field = isset($entity->$target) ? $entity->$target : array(); + $language = ($info['translatable'] && isset($entity->language)) ? $entity->language : LANGUAGE_NONE; + if (isset($_sub_targets[$sub_target]['callback']) && function_exists($_sub_targets[$sub_target]['callback'])) { + $callback = $_sub_targets[$sub_target]['callback']; + } + // Iterate over all values. + $delta = 0; + foreach ($value as $v) { + if (isset($field[$language][$delta]['entity'])) { + $field_collection_item = $field[$language][$delta]['entity']; + } + elseif (isset($field[$language][$delta]['value'])) { + $field_collection_item = field_collection_item_load($field[$language][$delta]['value']); + } + if (empty($field_collection_item)) { + $field_collection_item = entity_create('field_collection_item', array('field_name' => $target)); + } + // Hack: feeds mapping callbacks (at least text_feeds_set_target), seem to + // want an empty array for a Field Collection item target, to be sure the + // item is iterated over at least once. + //unset($field_collection_item->$sub_target);// Commented out for language handling. Might cause problems? + if (isset($callback)) { + // 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. + // @see FeedsProcessor->map() + $field_collection_item->$sub_target = NULL; + $callback($source, $field_collection_item, $sub_target, $v, $sub_mapping); + } + $field[$language][$delta]['entity'] = $field_collection_item; + $field[$language][$delta]['value'] = $field_collection_item->item_id; + unset($field_collection_item); + if ($info['cardinality'] == 1) { + break; + } + $delta++; + } + // In case of an update, there can be less values (less field collections) in + // the source than in the former entity. We empty the subtarget value. If all + // the subtargets are empty, the whole field collection will be removed in + // hook_feeds_presave() and then the field collection entity deleted by + // field_collection_field_update(). + while (isset($field[$language][$delta])) { + if (isset($field[$language][$delta]['entity'])) { + $field_collection_item = $field[$language][$delta]['entity']; + } + elseif (isset($field[$language][$delta]['value'])) { + $field_collection_item = field_collection_item_load($field[$language][$delta]['value']); + } + + if (isset($field_collection_item)) { + if (isset($callback)) { + $field_collection_item->$sub_target = NULL; + } + + $field[$language][$delta]['entity'] = $field_collection_item; + } + $delta++; + } + $entity->{$target} = $field; +} + +/** + * Implements hook_feeds_presave(). + */ +function field_collection_feeds_presave($source, $entity) { + // Do not save any empty field collection items that may have been created + // during the mapping process. Since the mapping is done field by field in + // field_collection_feeds_set_target(), we have to wait until this hook (when + // the field collection item is fully built up) before we can check if it's + // empty. + $config = $source->importer()->getConfig(); + if (!empty($config['processor']['config']['mappings'])) { + // Find all field collection mappings. + foreach ($config['processor']['config']['mappings'] as $mapping) { + if (isset($mapping['target'])) { + $args = explode(':', $mapping['target']); + $field_name = array_shift($args); + if (($field = field_info_field($field_name)) && $field['type'] == 'field_collection') { + // If the field collection item is empty, do not save it. + if (!empty($entity->{$field_name})) { + foreach ($entity->{$field_name} as $langcode => &$items) { + foreach ($items as $delta => $item) { + if (isset($item['entity']) && field_collection_item_is_empty($item['entity'])) { + unset($items[$delta]); + } + } + // Clean up the final array. + if (empty($items)) { + unset($entity->{$field_name}[$langcode]); + } + else { + $items = array_values($items); + } + } + } + } + } + } + } +} + +/** + * Returns the language summary text for use in a mapper target summary_callback + * + * @param array $mapping + * Associative array of the mapping settings. + * @param array $target + * Array of target settings, as defined by the processor or + * hook_feeds_processor_targets_alter(). + * @param array $form + * The whole mapping form. + * @param array $form_state + * The form state of the mapping form. + * + * @return string + * 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 field_collection_feeds_mapper_summary_language($mapping, $target, $form, $form_state) { + if (module_exists('locale')) { + list($field_name, $subfield_name) = explode(':', $mapping['target']); + $info = field_info_field($subfield_name); + if ($info['translatable']) { + $language_options = array(LANGUAGE_NONE => t('All languages')) + locale_language_list('name'); + if (empty($mapping['language'])) { + return t('Language: @search', array('@search' => $language_options[LANGUAGE_NONE])); + } + return t('Language: @search', array('@search' => $language_options[$mapping['language']])); + } + } + return FALSE; +} + +/** + * Returns the language form for use in a mapper target form_callback + * + * @param array $mapping + * Associative array of the mapping settings. + * @param array $target + * Array of target settings, as defined by the processor or + * hook_feeds_processor_targets_alter(). + * @param array $form + * The whole mapping form. + * @param array $form_state + * The form state of the mapping form. + * + * @return string + * 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 field_collection_feeds_mapper_form_language($mapping, $target, $form, $form_state) { + $form = array(); + if (module_exists('locale')) { + list($field_name, $subfield_name) = explode(':', $mapping['target']); + $info = field_info_field($subfield_name); + if ($info['translatable']) { + $language_options = array(LANGUAGE_NONE => t('All languages')) + locale_language_list('name'); + $form['language'] = array( + '#type' => 'select', + '#title' => t('Language'), + '#options' => $language_options, + '#default_value' => !empty($mapping['language']) ? $mapping['language'] : LANGUAGE_NONE, + ); + } + } + return $form; +}