You are here

taxonomy_csv.import.line.api.inc in Taxonomy CSV import/export 7.5

Process import of a csv line, i.e. of a term or a list of terms.

File

import/taxonomy_csv.import.line.api.inc
View source
<?php

/**
 * @file
 * Process import of a csv line, i.e. of a term or a list of terms.
 */

/**
 * Process the import of items.
 *
 * @param $line
 *   Array which contains items of a cleaned and checked csv line.
 * @param $options
 *   An associative array of import options:
 *   - import_format  : format of the csv line (see taxonomy.api.inc)
 *   - keep_order     : boolean. keep order of imported terms or not (default)
 *   - vocabulary     : vocabulary object where to import terms into
 *   - update_or_ignore: indicates what will become existing terms, if any.
 * @param $previous_items
 *   (Optional) Cleaned and checked previous imported line names and tids array.
 *   Needed with some contents as one term array structure.
 * @param $terms_count
 *   (Optional integer) Total of imported terms (duplicate included) is needed
 *   to set weight of terms and to keep order of items, if wished.
 *
 * @return
 *   Result array:
 *   - 'name' => array of imported terms names,
 *   - 'tid'  => array of imported terms tids,
 *   - 'msg'  => array of message codes,
 *   - 'terms_count' => number of imported terms.
 */
function taxonomy_csv_line_import($line, $options, $previous_items = array(), $terms_count = 0) {

  // Define default values.
  $result = array(
    'name' => array(),
    'tid' => array(),
    'msg' => array(),
    'terms_count' => $terms_count,
  );

  // Only count check because function variables are already checked.
  if (count($line)) {
    switch ($options['import_format']) {
      case TAXONOMY_CSV_FORMAT_FLAT:
        $result = taxonomy_csv_line_import_flat($line, $options, $terms_count);
        break;
      case TAXONOMY_CSV_FORMAT_STRUCTURE:
      case TAXONOMY_CSV_FORMAT_TREE:

        // Internally, tree import format use ignore_previous and not update.
        if ($options['update_or_ignore'] == TAXONOMY_CSV_EXISTING_UPDATE) {
          $options['update_or_ignore'] = TAXONOMY_CSV_EXISTING_IGNORE_PREVIOUS;
        }
        $result = taxonomy_csv_line_import_structure($line, $options, $previous_items, $terms_count);
        break;
      case TAXONOMY_CSV_FORMAT_POLYHIERARCHY:

        // Internally, polyhierarchy import format doesn't support Ignore.
        $options['update_or_ignore'] = TAXONOMY_CSV_EXISTING_UPDATE;
        $result = taxonomy_csv_line_import_structure($line, $options, $previous_items, $terms_count);
        break;
      case TAXONOMY_CSV_FORMAT_TID:
      case TAXONOMY_CSV_FORMAT_TID_FLAT:
        $result = taxonomy_csv_line_import_tid($line, $options, $terms_count);
        break;
      case TAXONOMY_CSV_FORMAT_TID_TREE:

        // Internally, tree import format use ignore_previous and not update.
        if ($options['update_or_ignore'] == TAXONOMY_CSV_EXISTING_UPDATE) {
          $options['update_or_ignore'] = TAXONOMY_CSV_EXISTING_IGNORE_PREVIOUS;
        }
        $result = taxonomy_csv_line_import_tid($line, $options, $previous_items, $terms_count);
        break;
      case TAXONOMY_CSV_FORMAT_TID_POLYHIERARCHY:

        // Internally, polyhierarchy import format doesn't support Ignore.
        $options['update_or_ignore'] = TAXONOMY_CSV_EXISTING_UPDATE;
        $result = taxonomy_csv_line_import_tid($line, $options, $previous_items, $terms_count);
        break;
      case TAXONOMY_CSV_FORMAT_FIELDS:
        $result = taxonomy_csv_line_import_fields($line, $options, $previous_items, $terms_count);
        break;
      case TAXONOMY_CSV_FORMAT_TRANSLATE:
        if (!module_exists('i18n_taxonomy')) {
          $result['msg'][] = 360;

          // Translation error.
          break;
        }
        switch ($options['vocabulary']->i18n_mode) {
          case I18N_MODE_LOCALIZE:
            $result = taxonomy_csv_line_import_localize($line, $options, $terms_count);
            break;
          case I18N_MODE_TRANSLATE:
          case I18N_MODE_MULTIPLE:
            $result = taxonomy_csv_line_import_translate($line, $options, $terms_count);
            break;
          default:
            $result['msg'][] = 361;
        }
        break;
      default:
        $result['msg'][] = 306;
    }
  }
  else {
    $result['msg'][] = 685;

    // No term to process.
  }
  return $result;
}

/**
 * Import a flat line.
 *
 * @see taxonomy_csv_line_import()
 */
function taxonomy_csv_line_import_flat($line, $options, $terms_count = 0) {

  // Define default values.
  $result = array(
    'name' => array(),
    'tid' => array(),
    'msg' => array(),
    'terms_count' => $terms_count,
  );
  foreach ($line as $term_name) {
    $term = new stdClass();
    $term->name = $term_name;
    $term->vid = $options['vocabulary']->vid;
    $term->vocabulary_machine_name = $options['vocabulary']->machine_name;
    $term->format = $options['filter_format'];
    $term->language = $options['language'];

    // Weight is not set above in order to keep existing one if it exists.
    ++$terms_count;
    if ($options['keep_order']) {
      $term->weight = $terms_count;
    }

    // Import term then store and check result.
    $current_result = taxonomy_csv_term_import($term, $options['update_or_ignore']);
    if (_taxonomy_csv_line_result($result, $current_result, $terms_count)) {
      return $result;
    }
  }
  return $result;
}

/**
 * Import a structure line.
 *
 * @see taxonomy_csv_line_import()
 */
function taxonomy_csv_line_import_structure($line, $options, $previous_items = array(), $terms_count = 0) {

  // Define default values.
  $result = array(
    'name' => array(),
    'tid' => array(),
    'msg' => array(),
    'terms_count' => $terms_count,
  );

  // 1. Check if one term or partial line format is used in order to virtually
  // complete line with previous line.
  // Find first non empty item as line can be full, partial or one term.
  for ($first = 0; $first < count($line) && empty($line[$first]); $first++) {
  }

  // Else line is full. As with one term import, imported items on previous line
  // can be bypassed if lines are ordered. So, look for first different item.
  if (!$first) {
    for ($first = 0; $first < count($line) - 1 && $first < count($previous_items['name']) && $line[$first] == $previous_items['name'][$first]; $first++) {
    }
  }

  // 2. Remove and remember superabondant previous items for next line.
  if ($first) {
    $result['name'] = $previous_items['name'] = array_slice($previous_items['name'], 0, $first);
    $result['tid'] = $previous_items['tid'] = array_slice($previous_items['tid'], 0, $first);
    $result['msg'][] = 683;

    // Previous line term.
    // Set root or previous ancestor name and id.
    $parent_name = $previous_items['name'][$first - 1];
    $parent_tid = $previous_items['tid'][$first - 1];
  }
  else {
    $parent_name = '';
    $parent_tid = 0;
  }

  // 3. Import each new term then store and check result.
  for ($c = $first; $c < count($line); $c++) {

    // Don't import empty terms. Needed with import of 'Structure and Fields',
    // if user chooses to keep fields on the same columns.
    if ($line[$c] === '') {
      continue;
    }
    $term = new stdClass();
    $term->name = $line[$c];
    $term->vid = $options['vocabulary']->vid;
    $term->vocabulary_machine_name = $options['vocabulary']->machine_name;
    $term->format = $options['filter_format'];
    $term->language = $options['language'];
    $term->parent = array(
      $parent_tid => $parent_tid,
    );

    // Weight is not set above in order to keep existing one if it exists.
    ++$terms_count;
    if ($options['keep_order']) {
      $term->weight = $terms_count;
    }
    switch ($options['import_format']) {
      case TAXONOMY_CSV_FORMAT_STRUCTURE:
      case TAXONOMY_CSV_FORMAT_TREE:

        // With TAXONOMY_CSV_EXISTING_IGNORE, parent terms (so all terms but the
        // last on this line) are always updated because they are successive
        // parents of a child.
        $current_result = $options['update_or_ignore'] == TAXONOMY_CSV_EXISTING_IGNORE && $c < count($line) - 1 ? taxonomy_csv_term_import($term, TAXONOMY_CSV_EXISTING_IGNORE_PREVIOUS, $parent_tid) : taxonomy_csv_term_import($term, $options['update_or_ignore'], $parent_tid);
        break;
      case TAXONOMY_CSV_FORMAT_POLYHIERARCHY:

        // Check direct duplicates: in Drupal, a term can't be its parent.
        $current_result = $term->name == $parent_name ? taxonomy_csv_term_import($term, $options['update_or_ignore'], $parent_tid) : taxonomy_csv_term_import($term, $options['update_or_ignore'], NULL);
        break;
    }
    $parent_name = $current_result['name'];
    $parent_tid = $current_result['tid'];
    if (_taxonomy_csv_line_result($result, $current_result, $terms_count)) {
      return $result;
    }
  }
  return $result;
}

/**
 * Import a flat or a structure line with tids and names.
 *
 * @see taxonomy_csv_line_import()
 */
function taxonomy_csv_line_import_tid($line, $options, $previous_items = array(), $terms_count = 0) {

  // Define default values.
  $result = array(
    'name' => array(),
    'tid' => array(),
    'msg' => array(),
    'terms_count' => $terms_count,
  );
  foreach ($line as $key => $term_tid) {
    if ($key % 2 === 1) {
      continue;
    }
    $term = new stdClass();
    if (is_numeric($term_tid) && $term_tid != 0) {
      $term->tid = $term_tid;
    }
    $term->name = $line[$key + 1];
    $term->vid = $options['vocabulary']->vid;
    $term->vocabulary_machine_name = $options['vocabulary']->machine_name;
    $term->format = $options['filter_format'];
    $term->language = $options['language'];
    if ($options['import_format'] !== TAXONOMY_CSV_FORMAT_TID_FLAT && isset($parent_tid)) {
      $term->parent = array(
        $parent_tid => $parent_tid,
      );
    }

    // Weight is not set above in order to keep existing one if it exists.
    ++$terms_count;
    if ($options['keep_order']) {
      $term->weight = $terms_count;
    }

    // Import term then store and check result.
    $current_result = taxonomy_csv_term_import($term, $options['update_or_ignore']);

    // Parent tid is not used with flat import, but to to a test is slower.
    $parent_tid = $current_result['tid'];
    if (_taxonomy_csv_line_result($result, $current_result, $terms_count)) {
      return $result;
    }
  }
  return $result;
}

/**
 * Import a fields line.
 *
 * @see taxonomy_csv_line_import()
 */
function taxonomy_csv_line_import_fields($line, $options, $previous_items = array(), $terms_count = 0) {

  // Define default values.
  $result = array(
    'name' => array(),
    'tid' => array(),
    'msg' => array(),
    'terms_count' => $terms_count,
  );

  // Define a default term.
  $term = new stdClass();
  $term->vid = $options['vocabulary']->vid;
  $term->vocabulary_machine_name = $options['vocabulary']->machine_name;
  $term->format = $options['filter_format'];
  $term->language = $options['language'];

  // Weight is not set above in order to keep existing one if it exists.
  ++$terms_count;
  if ($options['keep_order']) {
    $term->weight = $terms_count;
  }
  $parents = array();

  // Check if a structure is imported.
  if ($options['fields_format'][0] == 'tree' || $options['fields_format'][0] == 'poly') {
    $line_structure = array_slice($line, 0, count($line) - count($options['fields_format']) + 1);
    $options_structure = $options;
    $options_structure['import_format'] = $options['fields_format'][0] == 'tree' ? TAXONOMY_CSV_FORMAT_TREE : TAXONOMY_CSV_FORMAT_POLYHIERARCHY;
    $result = taxonomy_csv_line_import($line_structure, $options_structure, $previous_items, $term_count);
    if (_taxonomy_csv_worst_message($result['msg']) < TAXONOMY_CSV_PROCESS_NOTICE) {
      return $result;
    }

    // Fields will be attached to the last defined term on the line.
    $term->name = $result['name'][count($result['name']) - 1];
    $term->tid = $result['tid'][count($result['tid']) - 1];
    $line = array_slice($line, count($line) - count($options['fields_format']) + 1);
    array_shift($options['fields_format']);
    $terms_count--;
  }

  // Import normal fields according custom format.
  foreach ($options['fields_format'] as $key => $field_name) {
    if (!isset($line[$key]) || $line[$key] === '') {
      continue;
    }
    if (in_array($field_name, array(
      'tid',
      'name',
      'format',
      'weight',
      'language',
      'i18n_tsid',
      'guid',
    ))) {
      $term->{$field_name} = $line[$key];
    }
    elseif ($field_name == 'vid') {
      $term->vid = $line[$key];
      $term->vocabulary_machine_name = taxonomy_vocabulary_load($line[$key])->machine_name;
    }
    elseif ($field_name == 'vocabulary_machine_name') {
      $term->vocabulary_machine_name = $line[$key];
      $term->vid = taxonomy_vocabulary_machine_name_load($line[$key])->vid;
    }
    elseif ($field_name == 'description') {
      $term->description = _taxonomy_csv_set_line_break($line[$key]);
    }
    elseif ($field_name == 'parent') {

      // Just remember value: parents are imported later in the process.
      $parents[] = $line[$key];
    }
    elseif (isset($options['fields'][$field_name])) {

      // @todo To be rewritten.
      // True fields will be added later.
      // Currently, repetitive items are not managed.
      $term->fields_to_import[$field_name][] = $line[$key];
      $term->fields_to_import_instances = $options['instances'];
      $term->fields_to_import_fields = $options['fields'];
    }
    else {
      $current_result['msg'] = 341;

      // Non existing field.
      return $result;
    }
  }

  // Import parents if any.
  foreach ($parents as $value) {

    // Presume that item is a tid if it's a number.
    if (is_numeric($value)) {
      if (!$value) {
        $current_result['msg'] = 455;

        // Reference to term 0.
        continue;
      }

      // @todo Check if it isn't the current term id.
      // @todo Check if parent is in current vocabulary.
      $term->parent[] = $value;
    }
    else {
      $parent_term = new stdClass();
      $parent_term->name = $value;
      $parent_term->vid = $term->vid;
      $parent_term->vocabulary_machine_name = $term->vocabulary_machine_name;
      $parent_term->language = $term->language;
      $current_result = taxonomy_csv_term_import($parent_term, $options['update_or_ignore']);
      if (_taxonomy_csv_line_result($result, $current_result, $terms_count)) {
        return $result;
      }
      $term->parent[] = $current_result['tid'];
    }
  }

  // Import main term then store result. No check because only one term.
  $current_result = taxonomy_csv_term_import($term, $options['update_or_ignore']);
  $error = _taxonomy_csv_line_result($result, $current_result, $terms_count);
  return $result;
}

/**
 * Import a localization line.
 *
 * @see taxonomy_csv_line_import()
 */
function taxonomy_csv_line_import_localize($line, $options, $terms_count = 0) {

  // Define default values.
  $result = array(
    'name' => array(),
    'tid' => array(),
    'msg' => array(),
    'terms_count' => $terms_count,
  );

  // 1. Import main term then store and check result.
  $term = new stdClass();
  if ($options['translate_by'] == 'name') {
    $term->name = $line[0];
  }
  else {
    $term->tid = $line[0];
  }
  $term->vid = $options['vocabulary']->vid;
  $term->vocabulary_machine_name = $options['vocabulary']->machine_name;

  // With this i18n mode, language of main term is always undefined, whatever
  // the vocabulary language is.
  $term->language = 'und';
  $first_description = count($options['translate_languages']);
  if (isset($line[$first_description]) && $line[$first_description]) {

    // Other formats aren't allowed for translation of the description with this
    // i18n mode.
    $term->format = 'plain_text';
    $term->description = _taxonomy_csv_set_line_break($line[$first_description]);
  }

  // Weight is not set above in order to keep existing one if it exists.
  ++$terms_count;
  if ($options['keep_order']) {
    $term->weight = $terms_count;
  }

  // Import term then store result.
  $current_result = taxonomy_csv_term_import($term, $options['update_or_ignore']);
  if (_taxonomy_csv_line_result($result, $current_result, $terms_count)) {
    return $result;
  }

  // 2. Create localizations.
  foreach ($options['translate_languages'] as $key => $language) {

    // Don't import main item.
    if ($key == 0) {
      continue;
    }
    $result_translation = i18n_string_translation_update(array(
      'taxonomy',
      'term',
      $current_result['tid'],
      'name',
    ), $line[$key], $language, $line[0]);
    if (!$result_translation) {
      $result['msg'][] = 360;

      // Translation error.
      return $result;
    }
    if (isset($line[$first_description + $key]) && $line[$first_description] && $line[$first_description + $key]) {
      $result_translation = i18n_string_translation_update(array(
        'taxonomy',
        'term',
        $current_result['tid'],
        'description',
      ), _taxonomy_csv_set_line_break($line[$first_description + $key]), $language, _taxonomy_csv_set_line_break($line[$first_description]));
      if (!$result_translation) {
        $result['msg'][] = 360;

        // Translation error.
        return $result;
      }
    }
  }
  return $result;
}

/**
 * Import a translation line.
 *
 * @see taxonomy_csv_line_import()
 */
function taxonomy_csv_line_import_translate($line, $options, $terms_count = 0) {

  // Define default values.
  $result = array(
    'name' => array(),
    'tid' => array(),
    'msg' => array(),
    'terms_count' => $terms_count,
  );

  // 1. Prepare main term.
  $term = new stdClass();
  if ($options['translate_by'] == 'name') {
    $term->name = $line[0];
  }
  else {
    $term->tid = $line[0];
  }
  $term->vid = $options['vocabulary']->vid;
  $term->vocabulary_machine_name = $options['vocabulary']->machine_name;
  $term->format = $options['filter_format'];
  $term->language = $options['translate_languages'][0];

  // Weight is not set above in order to keep existing one if it exists.
  ++$terms_count;
  if ($options['keep_order']) {
    $term->weight = $terms_count;
  }

  // Import term then store result.
  $current_result = taxonomy_csv_term_import($term, $options['update_or_ignore']);
  if (_taxonomy_csv_line_result($result, $current_result, $terms_count)) {
    return $result;
  }

  // Need to get term with tid and eventual tsid.
  $term = taxonomy_term_load($current_result['tid']);

  // 2. Use translation set of the term if it exists, else create it.
  $translation_set = $term->i18n_tsid ? i18n_translation_set_load($term->i18n_tsid) : i18n_translation_set_create('taxonomy_term', $options['vocabulary']->machine_name);
  $translation_set
    ->add_item($term, $term->language);

  // 3. Check if the term is already translated in order to update or create it.
  $existing_terms = $translation_set
    ->get_translations();
  foreach ($options['translate_languages'] as $key => $language) {

    // Don't import main item.
    if ($key == 0) {
      continue;
    }

    // Don't import an empty item.
    if (isset($line[$key]) && $line[$key]) {
      $translated_term = new stdClass();
      if (isset($existing_terms[$language])) {
        switch ($options['update_or_ignore']) {
          case TAXONOMY_CSV_EXISTING_UPDATE:
            $translated_term = $existing_terms[$language];

            // As the term is already loaded and we have the tid, we simply save
            // it without to find it a new time (avoid search).
            $options['update_or_ignore'] = TAXONOMY_CSV_EXISTING_IGNORE;
            break;
          case TAXONOMY_CSV_EXISTING_IGNORE:
            break;
        }
      }

      // 4. Complete or create the translated term, then import it.
      $translated_term->name = $line[$key];
      $translated_term->vid = $options['vocabulary']->vid;
      $translated_term->vocabulary_machine_name = $options['vocabulary']->machine_name;
      $translated_term->format = $options['filter_format'];
      $translated_term->language = $language;

      // Import term then store result.
      $current_result = taxonomy_csv_term_import($translated_term, $options['update_or_ignore']);
      if (_taxonomy_csv_line_result($result, $current_result, $terms_count)) {
        return $result;
      }

      // Needed to get a term with tid.
      $translated_term = taxonomy_term_load($current_result['tid']);

      // 5. Update or create the translation set.
      // Remove is needed to avoid errors when a translated term is updated.
      $translation_set
        ->remove_language($translated_term->language);
      $translation_set
        ->add_item($translated_term, $translated_term->language);
    }
  }
  $translation_set
    ->save(TRUE);
  return $result;
}

/**
 * Helper to merge results with current result.
 *
 * @return
 *   TRUE if the result contains an error. Result is passed by reference.
 */
function _taxonomy_csv_line_result(&$result, $current_result, $terms_count) {
  $result['name'][] = isset($current_result['name']) ? $current_result['name'] : '';
  $result['tid'][] = isset($current_result['tid']) ? $current_result['tid'] : 0;
  $result['msg'] = isset($result['msg']) ? array_merge($result['msg'], $current_result['msg']) : $current_result['msg'];
  $result['terms_count'] = $terms_count;
  return _taxonomy_csv_worst_message($current_result['msg']) < TAXONOMY_CSV_PROCESS_NOTICE;
}

/**
 * Update/create a term with the given name and parent in the given vocabulary.
 *
 * @param $term
 *   A term object to import. Term contains either:
 *   - 'name'        => term name string,
 *   - 'tid'         => term id,
 *   and eventually, matching options:
 *   - 'vid'         => the vocabulary id where to import,
 *   - 'description' => description string,
 *   - 'format'      => the filter format of the description,
 *   - 'language'    => the language id of the term,
 *   - 'weight'      => weight integer,
 *   - 'parent'      => array of first level parent tids,
 * @param $update_or_ignore
 *   (Optional) Type of import on existing terms. Default to ignore and create.
 * @param $parent_tid
 *   (Optional) The direct parent term id where to restrict search.
 *   Used for structure import. Default to NULL (no parent restriction).
 *
 * @return array
 *    'name' => term name,
 *    'tid'  => term id,
 *    'msg'  => messages array.
 *
 * @todo Include true update/replace/ignore for fields.
 */
function taxonomy_csv_term_import($term, $update_or_ignore = TAXONOMY_CSV_EXISTING_IGNORE, $parent_tid = NULL) {
  $messages = array();

  // Basic check to avoid notices when "Check lines" option is disabled.
  if (!isset($term->name) && !isset($term->tid) || isset($term->name) && !$term->name || isset($term->tid) && !$term->tid) {
    $term->tid = 0;
    $messages[] = 432;

    // Warning line contains an empty term.
    return array(
      'name' => '',
      'tid' => 0,
      'msg' => $messages,
    );
  }
  switch ($update_or_ignore) {
    case TAXONOMY_CSV_EXISTING_UPDATE:
      $existing_term = taxonomy_csv_term_find($term, FALSE, $parent_tid);
      if ($existing_term) {

        // Update only fields that are set. Other fields are not changed.
        foreach ($term as $key => $value) {

          // Arrays: merge existing and new items.
          // Only used for parent in standard taxonomy of Drupal 7.
          if (is_array($value) && isset($existing_term->{$key})) {
            $term->{$key} = array_unique(array_merge($existing_term->{$key}, $value));
          }

          // Else simply use new key: no merge is possible and useful for
          // string, numeric or boolean fields.
          // Description was an exception in previous version, but this
          // exception is removed for simplicity and real use of this module.
          // An option may be added if needed.
        }

        // Existing fields of existing term should be kept even if they have
        // not been set in new term.
        $term = (object) array_merge((array) $existing_term, (array) $term);
      }
      else {
        unset($term->tid);
      }
      break;
    case TAXONOMY_CSV_EXISTING_IGNORE_PREVIOUS:

      // Doesn't ignore, but use previous parents.
      $existing_term = taxonomy_csv_term_find($term, FALSE, $parent_tid);
      if ($existing_term) {

        // All fields are replaced by new ones. Other existing fields of
        // existing term should be kept even if they are not set in new term.
        $term = (object) array_merge((array) $existing_term, (array) $term);
      }
      else {
        unset($term->tid);
      }
      break;
    case TAXONOMY_CSV_EXISTING_IGNORE:

      // Nothing to do: keep the term.
      break;
  }

  // Finish to set the term to avoid NULL. Format and language are set before.
  if (!isset($term->format)) {
    $term->format = 'plain_text';
  }
  if (!isset($term->description)) {
    $term->description = '';
  }

  // Currently, custom fields are managed by an external function.
  if (isset($term->fields_to_import)) {
    $messages = _taxonomy_csv_term_field_import($term, $update_or_ignore);
    unset($term->fields_to_import);
    unset($term->fields_to_import_instances);
    unset($term->fields_to_import_fields);
    if (_taxonomy_csv_worst_message($messages) < TAXONOMY_CSV_PROCESS_NOTICE) {
      return array(
        'name' => '',
        'tid' => 0,
        'msg' => $messages,
      );
    }
  }

  // Save regularly formatted term.
  // Return either SAVED_NEW, SAVED_UPDATED or FALSE (no change).
  $result = taxonomy_term_save($term);
  $messages[] = $result == SAVED_NEW ? 691 : 692;

  // Saved or updated.
  return array(
    'name' => $term->name,
    'tid' => $term->tid,
    'msg' => $messages,
  );
}

/**
 * Helper to import attached fields.
 *
 * @see taxonomy_csv_term_import()
 *
 * @todo Integrate with taxonomy_csv_line_import and taxonomy_csv_term_import.
 * @todo True update/replace/ignore.
 */
function _taxonomy_csv_term_field_import($term, $update_or_ignore = TAXONOMY_CSV_EXISTING_IGNORE) {
  $messages = array();

  // Currently, translatable fields are unmanaged.
  $language = 'und';

  // Undefined.
  foreach ($term->fields_to_import as $field_name => $values) {
    $instance =& $term->fields_to_import_instances[$field_name];
    $field =& $term->fields_to_import_fields[$field_name];
    foreach ($values as $value) {
      switch ($field['type']) {
        case 'taxonomy_term_reference':

          // Get machine name of referenced vocabulary.
          $referenced_vocabulary = $field['settings']['allowed_values'][0]['vocabulary'];
          $referenced_vocabulary = taxonomy_vocabulary_machine_name_load($referenced_vocabulary);
          $referenced_term = new stdClass();

          // Presume that item is a tid if it's a number.
          if (is_numeric($value)) {
            if (!$value) {
              $messages['notice'] = t('Unable to make a reference to term "0".');
              continue 2;
            }

            // @todo Check if the term is in allowed vocabularies.
            $referenced_term->tid = $value;
          }
          else {
            $referenced_term->name = $value;
            $referenced_term->vid = $referenced_vocabulary->vid;
            $referenced_term->vocabulary_machine_name = $referenced_vocabulary->machine_name;

            // @todo Use undefined language or term language?
            $referenced_term->language = $term->language;

            // @todo Increase term count.
            $current_result = taxonomy_csv_term_import($referenced_term, $update_or_ignore);
            if (!$current_result['tid']) {
              $messages[] = 402;

              // Unable to import a field term.
              return $messages;
            }
            $referenced_term->tid = $current_result['tid'];
          }
          $value = array(
            'tid' => $referenced_term->tid,
          );
          switch ($instance['widget']['type']) {
            case 'taxonomy_autocomplete':

              // Need tid, vid, name and vocabulary_machine_name (use cache).
              $referenced_term = taxonomy_term_load($referenced_term->tid);
              $value['name'] = $referenced_term->name;
              $value['vid'] = $referenced_term->vid;
              $value['vocabulary_machine_name'] = $referenced_term->machine_name;
              break;
          }

          // Complete term if there are already items.
          if (is_array($term->{$field_name}) && !empty($term->{$field_name})) {
            foreach ($term->{$field_name} as $field_language => &$delta_array) {
              $delta_array[] = $value;
            }
          }
          else {
            $term->{$field_name}[$language][0] = $value;
          }
          break;
        case 'file':
        case 'image':
          $uri_scheme = $field['settings']['uri_scheme'];
          $file_directory = $instance['settings']['file_directory'];
          $file_extensions = $instance['settings']['file_extensions'];
          $source = $value;
          $destination = $uri_scheme . '://' . $file_directory;

          // Try to create directory if it does not exist.
          file_prepare_directory($destination, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);

          // Import source if it exists.
          if (file_exists($source)) {
            $filepath = file_unmanaged_copy($source, $destination, FILE_EXISTS_RENAME);
            if ($filepath !== FALSE) {
              $file = new stdClass();
              $file->uid = 1;
              $file->filename = basename($filepath);
              $file->uri = $filepath;
              $file->filemime = file_get_mimetype($filepath);
              $file->filesize = filesize($filepath);
              $file->status = 1;
              $file->timestamp = time();
              $file = file_save($file);

              // Complete term if there are already items.
              if (is_array($term->{$field_name}) && !empty($term->{$field_name})) {
                foreach ($term->{$field_name} as $field_language => &$delta_array) {
                  $delta_array[] = array(
                    'fid' => $file->fid,
                    'display' => 1,
                    'description' => '',
                  );
                }
              }
              else {
                $term->{$field_name}[$language][0] = array(
                  'fid' => $file->fid,
                  'display' => 1,
                  'description' => '',
                );
              }
            }
          }
          elseif ($source != '') {
            $messages['notice'] = t('Unable to load attached file.');
          }

          // Else nothing to import.
          break;
        case 'list_boolean':
          $value = $value == '0' || drupal_strtolower($value) == 'false' ? 0 : 1;
          $term->{$field_name}[$language][0] = array(
            'value' => $value,
          );
          break;

        // Managed by default:
        // case 'number_decimal':
        // case 'number_integer':
        // case 'number_float':
        // case 'text':
        // case 'text_long':
        // case 'text_with_summary': // ?
        // Currently doesn't manage default value.
        default:
          switch ($field['type']) {
            case 'number_decimal':
              $value = (double) $value;
              break;
            case 'number_integer':
              $value = (int) $value;
              break;
            case 'number_float':
              $value = (double) $value;
              break;
          }

          // Complete term if there are already items.
          if (is_array($term->{$field_name}) && !empty($term->{$field_name})) {
            foreach ($term->{$field_name} as $field_language => &$delta_array) {
              $delta_array[] = array(
                'value' => $value,
              );
            }
          }
          else {
            $term->{$field_name}[$language][0] = array(
              'value' => $value,
            );
          }
          break;
      }
    }
  }
  return $messages;
}

/**
 * Helper to build a standard text field from a simple string or an array.
 *
 * @todo To be removed (as other helpers) by using of Field api (but slower).
 * @todo Fields internationalization. Should be compatible with the i18n mode of
 *   the vocabulary.
 *
 * @param $value
 *   A string or an array to convert.
 * @param $language
 *   (Optional). Language to use. Default is 'und' (undefined).
 * @param $format
 *   (Optional). Format of the field. Default to NULL (fixed plain text).
 *
 * @return
 *   Formatted field array.
 */
function _taxonomy_csv_field_create_text($value, $language = 'und', $format = NULL) {

  // Currently, i18n of term custom fields is not supported.
  $language = 'und';
  $field = array();
  if (!is_array($value) && !empty($value)) {
    $value = array(
      $value,
    );
  }
  if ($format == 'none') {
    $format = NULL;
  }
  foreach ($value as $item) {
    if ($item !== '') {
      $field[] = array(
        'value' => $item,
        'format' => $format,
        'safe_value' => $format ? check_markup($item, $format, $language) : check_plain($item),
      );
    }
  }
  return $field ? array(
    $language => $field,
  ) : array();
}

/**
 * Helper to build a standard term reference field from a string or an array.
 *
 * @param $value
 *   A string or an array to convert.
 * @param $language
 *   (Optional). Language to use. Default is 'und' (undefined).
 *
 * @return
 *   Formatted field array.
 */
function _taxonomy_csv_field_create_taxonomy_term_reference($value, $language = 'und') {

  // Currently, i18n of term custom fields is not supported.
  $language = 'und';
  $field = array();
  if (!is_array($value) && !empty($value)) {
    $value = array(
      $value,
    );
  }
  foreach ($value as $item) {
    if ($item) {
      $field[]['tid'] = $item;
    }
  }
  return $field ? array(
    $language => $field,
  ) : array();
}

/**
 * Helper to convert an internal array field to a standard text field.
 *
 * @param $field_name
 *   Field to update.
 * @param $term
 *   Term object.
 * @param $existing_term
 *   Previous term object.
 *
 * @return
 *   Nothing: $term object is passed by reference.
 */
function _taxonomy_csv_field_update_text($field_name, $term, $existing_term) {

  // Complete term if there are already items.
  if (is_array($existing_term->{$field_name}) && !empty($existing_term->{$field_name})) {
    $new_field = $term->{$field_name};
    $term->{$field_name} = $existing_term->{$field_name};
    $field =& $term->{$field_name};
    foreach ($new_field as $field_language => $array) {

      // Don't update if new field is empty.
      if (!empty($array)) {

        // Update the value for that language.
        if (isset($field[$field_language])) {

          // Don't use array_merge or array_merge_recursive to avoid duplicates.
          $existing = array();
          foreach ($field[$field_language] as &$value) {
            $existing[] =& $value['value'];
          }
          foreach ($array as &$new_value) {

            // Avoid duplicates and avoid to append an empty item.
            if (!in_array($new_value['value'], $existing) && $new_value['value'] !== '') {
              $field[$field_language][] = $new_value;
            }
          }
        }
        else {
          foreach ($array as &$new_value) {

            // Avoid to append an empty item.
            if ($new_value['value'] !== '') {
              $field[$field_language][] = $new_value;
            }
          }
        }
      }
    }
  }

  // Else nothing to do: term contains already new field.
}

/**
 * Helper to convert an internal array field to a standard term reference field.
 *
 * @param $field_name
 *   Field to update.
 * @param $term
 *   Term object.
 * @param $existing_term
 *   Previous term object.
 *
 * @return
 *   Nothing: $term object is passed by reference.
 */
function _taxonomy_csv_field_update_taxonomy_term_reference($field_name, $term, $existing_term) {

  // Complete term if there are already items.
  if (is_array($existing_term->{$field_name}) && !empty($existing_term->{$field_name})) {
    $new_field = $term->{$field_name};
    $term->{$field_name} = $existing_term->{$field_name};
    $field =& $term->{$field_name};
    foreach ($new_field as $field_language => $array) {

      // Don't update if new field is empty.
      if (!empty($array)) {

        // Update the value for that language.
        if (isset($field[$field_language])) {

          // Don't use array_merge or array_merge_recursive to avoid duplicates.
          $existing = array();
          foreach ($field[$field_language] as &$value) {
            $existing[] =& $value['tid'];
          }
          foreach ($array as &$new_value) {

            // Avoid duplicates and avoid to append an empty item.
            if (!in_array($new_value['tid'], $existing) && $new_value['tid'] != 0) {
              $field[$field_language][] = $new_value;
            }
          }
        }
        else {
          foreach ($array as &$new_value) {

            // Avoid to append an empty item.
            if ($new_value['tid'] != 0) {
              $field[$field_language][] = $new_value;
            }
          }
        }
      }
    }
  }

  // Else nothing to do: term contains already new field.
}

/**
 * Helper to convert an internal repetitive field to a standard text field.
 *
 * @param $term
 *   A taxonomy term object with custom fields.
 * @param $field_name
 *   Field to update.
 * @param $language
 *   (Optional) Language code.
 *
 * @return
 *   Nothing: $term object is passed by reference.
 */
function _taxonomy_csv_line_replace_field_text($term, $field_name, $language = 'und') {
  if (isset($term->{$field_name}) && is_array($term->{$field_name})) {
    $field = array();
    foreach ($term->{$field_name} as $value) {
      $field[] = array(
        'value' => $value,
        'format' => NULL,
        'safe_value' => check_plain($value),
      );
    }
    $term->{$field_name}[$language] = $field;
  }
}

Functions

Namesort descending Description
taxonomy_csv_line_import Process the import of items.
taxonomy_csv_line_import_fields Import a fields line.
taxonomy_csv_line_import_flat Import a flat line.
taxonomy_csv_line_import_localize Import a localization line.
taxonomy_csv_line_import_structure Import a structure line.
taxonomy_csv_line_import_tid Import a flat or a structure line with tids and names.
taxonomy_csv_line_import_translate Import a translation line.
taxonomy_csv_term_import Update/create a term with the given name and parent in the given vocabulary.
_taxonomy_csv_field_create_taxonomy_term_reference Helper to build a standard term reference field from a string or an array.
_taxonomy_csv_field_create_text Helper to build a standard text field from a simple string or an array.
_taxonomy_csv_field_update_taxonomy_term_reference Helper to convert an internal array field to a standard term reference field.
_taxonomy_csv_field_update_text Helper to convert an internal array field to a standard text field.
_taxonomy_csv_line_replace_field_text Helper to convert an internal repetitive field to a standard text field.
_taxonomy_csv_line_result Helper to merge results with current result.
_taxonomy_csv_term_field_import Helper to import attached fields.