taxonomy_csv.export.api.inc in Taxonomy CSV import/export 6.5
Validate export options and manage export process.
File
export/taxonomy_csv.export.api.incView source
<?php
/**
* @file
* Validate export options and manage export process.
*/
/**
* Invoke associated include file.
* taxonomy_csv.export.admin.inc is invoked only if user wants to check input
* options.
* taxonomy_csv.export.result.inc is invoked only if user wants result messages.
*/
$module_dir = drupal_get_path('module', 'taxonomy_csv');
require_once "{$module_dir}/taxonomy_csv.api.inc";
require_once "{$module_dir}/taxonomy_csv.term.api.inc";
require_once "{$module_dir}/taxonomy_csv.vocabulary.api.inc";
/**
* Process the export of a vocabulary.
*
* If not used in a form, don't forget to use batch_process().
*
* @param $options
* An associative array of options:
* - export_format : see _taxonomy_csv_values('export_format')
* - vocabulary_id : vocabulary id to export (default: 0, which means all)
* - delimiter : one character csv delimiter (default: ',')
* - enclosure : zero or one character csv enclosure (default: '"')
* - line_ending : 'Unix' (default), 'Mac' or 'MS-DOS'
* - order : order of terms: 'name' (default), 'tid' or 'weight'
* // Specific to def_links:
* - def_links_terms_ids : 'name_if_needed' (default), 'name' or 'tid'
* - def_links_vocabularies_ids : 'none' (default), 'name' or 'vid'
* // Level of result process infos:
* - check_options : boolean. check or not (default) this array of options
* - result_display: boolean. display or not (default). Only used with UI
* - result_duplicates: boolean. display or not (default) duplicate terms
* All options have default values.
* Warning: default values are different with UI.
*
* @return
* Array of errors or file object to download (need to execute batch process;
* result is logged in watchdog).
*/
function taxonomy_csv_export($options) {
// Complete $options with default values if needed.
// Default api and UI options are different.
$options += _taxonomy_csv_values('export_default_api');
// Presave a file in order to check access to temporary folder.
$messages = _taxonomy_csv_export_output_presave($options);
if (count($messages)) {
return $messages;
}
// Process export.
$messages = taxonomy_csv_vocabulary_export($options);
// Return errors if any.
if (count($messages)) {
return $messages;
}
else {
return $options['file'];
}
}
/**
* Presave output.
*
* Check if there is write access and prepare file.
*
* @param $options
* Array. Same as taxonomy_csv_export.
*
* @return
* Array of messages errors if any.
* By reference options are cleaned and completed.
*/
function _taxonomy_csv_export_output_presave(&$options) {
$messages = array();
// Check if there is write access and prepare file.
$filename = file_save_data('', 'taxocsv.csv', 'FILE_EXISTS_RENAME');
if (!$filename) {
$messages['file'] = t('Check access rights to temp directory: export needs permission to write and to read in it. Export failed.');
}
else {
$options['file'] = (object) array(
'filename' => basename($filename),
'filepath' => realpath($filename),
'filesize' => filesize($filename),
);
}
return $messages;
}
/**
* Prepare the export of a vocabulary.
* If not used in a form, don't forget to use batch_process().
*
* @param $options
* Array. Same as taxonomy_csv_export.
*
* @return
* Array of errors or nothing (batch process to execute).
*/
function taxonomy_csv_vocabulary_export($options) {
// Check options and return array of messages in case of errors.
if ($options['check_options']) {
// Invoke export admin file.
$module_dir = drupal_get_path('module', 'taxonomy_csv');
require_once "{$module_dir}/export/taxonomy_csv.export.admin.inc";
$result = _taxonomy_csv_export_check_options($options);
if (count($result)) {
return $result;
}
}
// Complete $options with some csv variables.
$options['separator'] = $options['enclosure'] . $options['delimiter'] . $options['enclosure'];
$line_ending = array(
'Unix' => "\n",
'Mac' => "\r",
'MS-DOS' => "\r\n",
);
$options['end_of_line'] = $line_ending[$options['line_ending']];
// Calculates number of terms to be exported.
$options['total_terms'] = taxonomy_csv_vocabulary_count_terms($options['vocabulary_id']);
// Prepare export batch.
$batch = array(
'title' => t('Exporting !total_terms terms to CSV file...', array(
'!total_terms' => $options['total_terms'],
)),
'init_message' => t('Starting downloading of datas...') . '<br />' . t('Wait some seconds for pre-processing...'),
'progress_message' => '',
'error_message' => t('An error occurred during the export.'),
'finished' => '_taxonomy_csv_vocabulary_export_finished',
'file' => drupal_get_path('module', 'taxonomy_csv') . '/export/taxonomy_csv.export.api.inc',
'progressive' => TRUE,
'operations' => array(
0 => array(
'_taxonomy_csv_vocabulary_export_process',
array(
$options,
),
),
),
);
batch_set($batch);
}
/**
* Batch process of vocabulary export.
*
* @todo Check if direct query and only tid save is really less memory
* intensive (with core taxonomy cache or not).
* @todo Don't remember terms, but load them one by one in order to decrease
* memory usage.
*
* @param $options
* Array of batch options.
* @param &$context
* Batch context to keep results and messages.
*
* @return
* NULL because use of &$context.
*/
function _taxonomy_csv_vocabulary_export_process($options, &$context) {
// First callback.
if (empty($context['sandbox'])) {
// Remember options as batch_set can't use form_storage.
// It allows too that first line in result is numbered 1 and not 0.
$context['results'][0] = $options;
// Initialize some variables.
$context['results'][0]['current_term'] = 0;
$context['results'][0]['current_name'] = '';
$context['results'][0]['worst_tid'] = 0;
$context['results'][0]['worst_term'] = 0;
$context['results'][0]['worst_name'] = '';
$context['results'][0]['worst_message'] = 799;
// No pointer because new line is appended to file.
$context['results'][0]['handle'] = fopen($options['file']->filepath, 'a+');
// Prepare list of vocabularies to be exported.
if ($options['vocabulary_id'] && $options['vocabulary_id'] != array(
0,
)) {
$vocabularies = array();
foreach ($options['vocabulary_id'] as $vocabulary) {
$vocabularies[] = taxonomy_vocabulary_load($vocabulary);
}
}
else {
$vocabularies = taxonomy_get_vocabularies();
}
// Fetch terms to be exported and order them (by tree or by specific order).
// Prepare a hierarchical tree of terms.
if (in_array($options['export_format'], array(
TAXONOMY_CSV_FORMAT_TREE_STRUCTURE,
TAXONOMY_CSV_FORMAT_POLYHIERARCHY,
))) {
$context['sandbox']['terms'] = array();
foreach ($vocabularies as $vocabulary) {
$context['sandbox']['terms'] = array_merge($context['sandbox']['terms'], taxonomy_get_tree($vocabulary->vid));
}
}
else {
$context['sandbox']['terms'] = taxonomy_csv_term_load_multiple(array(), array(
// Automatically manages one, some or all vocabularies.
'vid' => $options['vocabulary_id'],
'order' => $options['order'],
));
}
// Get max number of values for fields with a undefined number of values.
if ($options['export_format'] == TAXONOMY_CSV_FORMAT_FIELDS) {
// Currently, manage only undefined language.
$language = 'und';
$options['vocabulary'] = array();
foreach ($vocabularies as $vocabulary) {
$options['vocabulary'][$vocabulary->vid] = $vocabulary;
$options['vocabulary'][$vocabulary->vid]->fields_unlimited = array(
'parents' => 0,
'synonyms' => 0,
'relations' => 0,
);
}
foreach ($context['sandbox']['terms'] as $term) {
foreach (array(
'parents',
'synonyms',
'relations',
) as $field_name) {
// Remember the number of items if this term field has got more items
// than previous one.
if (count($term->{$field_name}) > $options['vocabulary'][$term->vid]->fields_unlimited[$field_name]) {
$options['vocabulary'][$term->vid]->fields_unlimited[$field_name] = count($term->{$field_name});
}
}
}
// Keep the list of vocabulary with undefined fields.
$context['results'][0]['vocabulary'] = $options['vocabulary'];
$context['sandbox']['vocabulary'] = $options['vocabulary'];
}
// Prepare list of duplicate terms if needed.
$context['results'][0]['duplicate_terms'] = array();
if (in_array($options['export_format'], array(
TAXONOMY_CSV_FORMAT_DEFINITION_LINKS,
))) {
$context['results'][0]['duplicate_terms'] = array();
foreach ($options['vocabulary_id'] as $item) {
$context['results'][0]['duplicate_terms'] += taxonomy_csv_term_find_duplicate($item);
}
}
}
elseif (!is_resource($context['results'][0]['handle'])) {
// Reopen file in case of memory out.
$context['results'][0]['handle'] = fopen($options['file']->filepath, 'a+');
}
// Get list of unlimited multivalued fields.
// @todo Currently, these fields are not saved in batch.
if ($options['export_format'] == TAXONOMY_CSV_FORMAT_FIELDS) {
$options['vocabulary'] = $context['sandbox']['vocabulary'];
}
// To simplify use of variables.
$worst_tid =& $context['results'][0]['worst_tid'];
$worst_term =& $context['results'][0]['worst_term'];
$worst_name =& $context['results'][0]['worst_name'];
$worst_message =& $context['results'][0]['worst_message'];
$handle =& $context['results'][0]['handle'];
$duplicate_terms =& $context['results'][0]['duplicate_terms'];
$term_number =& $context['results'][0]['current_term'];
$current_name =& $context['results'][0]['current_name'];
$terms_list =& $context['sandbox']['terms'];
$terms_list_ids = array_keys($terms_list);
// Process one term until end of vocabulary.
if ($term_number < count($context['sandbox']['terms'])) {
$term = $terms_list[$terms_list_ids[$term_number]];
++$term_number;
// Remember current name/tid in case of error.
$current_name = $term->name;
$current_tid = $term->tid;
// Process export of current term.
$result = taxonomy_csv_term_export($term, $options, $terms_list, $duplicate_terms);
$result['msg'] = taxonomy_csv_line_export($result['line'], $options, $handle, $result['msg']);
// Remember worst message of exported terms.
$worst_message_new = _taxonomy_csv_worst_message($result['msg']);
if ($worst_message > $worst_message_new) {
$worst_tid = $current_tid;
$worst_term = $term_number;
$worst_name = $current_name;
$worst_message = $worst_message_new;
}
// Remember messages. Currently useless because there isn't any warning or
// notice message (only error). A result level can be added here if needed.
if (count($result['msg'])) {
$context['results'][$term_number] = $result['msg'];
}
// Inform about progress.
$context['message'] = t('Term !term_number of !total_terms processed: %term', array(
'!term_number' => $term_number,
'!total_terms' => $options['total_terms'],
'%term' => $current_name,
));
// Check worst message of exported terms and update progress.
if ($worst_message >= TAXONOMY_CSV_PROCESS_WARNING) {
// Count should be <= 0.99 to avoid to display "100%" before end.
$context['finished'] = floor($term_number / count($context['sandbox']['terms']) * 100) / 100;
}
else {
$context['finished'] = 1;
}
}
}
/**
* Callback for finished batch export and display result informations.
*/
function _taxonomy_csv_vocabulary_export_finished($success, $results, $operations) {
$options =& $results[0];
unset($results[0]);
// Close exported file.
if ($options['handle']) {
fclose($options['handle']);
}
// Invoke export stats file if user wants to display results.
if ($options['result_display']) {
$module_dir = drupal_get_path('module', 'taxonomy_csv');
require_once "{$module_dir}/export/taxonomy_csv.export.result.inc";
}
// Short summary information is different if batch succeeded or not.
if ($success) {
if (!empty($options['worst_tid'])) {
$worst_path = drupal_get_path_alias('taxonomy/term/' . $options['worst_tid']);
$options['worst_name'] = l($options['worst_name'], $worst_path);
}
$variables = array(
'!total_terms' => $options['total_terms'],
'!worst_count' => $options['worst_term'],
'!worst_name' => $options['worst_name'],
'!worst_msg' => $options['result_display'] ? _taxonomy_csv_message_text($options['worst_message']) : t('Message code') . ' = ' . $options['worst_message'],
);
$messages = array(
WATCHDOG_DEBUG => t('No error, warnings or notices have been reported during export process of !total_terms terms.', $variables),
WATCHDOG_INFO => t('No error, warnings or notices have been reported during export process of !total_terms terms.', $variables),
WATCHDOG_NOTICE => t('Notices have been reported during export process (bad formatted or empty terms). !total_terms terms processed. First notice occurred on term !worst_count (!worst_name) [!worst_msg].', $variables),
WATCHDOG_WARNING => t('Warnings have been reported during export process (bad formatted terms). !total_terms terms processed. First term skipped is term !worst_count (!worst_name) [!worst_msg].', $variables),
WATCHDOG_ERROR => t('Errors have been reported during export process. Process failed at term !worst_count (!worst_name) of a total of !total_terms [!worst_msg].', $variables),
);
$worst_level = intval($options['worst_message'] / 100);
$message = isset($messages[$worst_level]) ? $messages[$worst_level] : $messages[WATCHDOG_DEBUG];
}
else {
$message = t('Exportation failed. Export process was successful until the term !term_count (!term_name) of a total of !total_terms.', array(
'!term_count' => $options['current_term'],
'!term_name' => $options['current_name'],
'!total_terms' => $options['total_terms'],
)) . '<br />' . t('This issue is related to export process and may be caused by a memory overrun of the database. If not, you can reinstall module from a fresh release or submit an issue on <a href="!link">Taxonomy CSV import/export module</a>.', array(
'!link' => url('http://drupal.org/project/issues/taxonomy_csv/'),
));
$worst_level = WATCHDOG_ERROR;
}
// Set result message in watchdog and eventually in user interface.
// Use of a $message variable is unrecommended, but simpler and working.
// See http://drupal.org/node/323101
watchdog('taxonomy_csv', $message, NULL, $worst_level);
if ($options['result_display']) {
_taxonomy_csv_export_result($options, $worst_level, $message, $results);
}
}
/**
* Export a line.
*
* @param $line
* Array to be exported to a line.
* @param $options
* An associative array of export options:
* - 'separator' : string separator (formatted delimiter and enclosure).
* - 'enclosure' : item enclosure.
* - 'end_of_line': end of line string.
* @param $handle
* Handle of the open file where to save line.
* @param $result_message
* (Optional) Array of messages.
*
* @return Result array:
* Array of messages.
*/
function taxonomy_csv_line_export($line, $options, &$handle, $result = array()) {
// Check if separator, enclosure or line ending exist in line.
$check_line = implode('', $line);
if (strpos($check_line, $options['separator']) !== FALSE || $options['enclosure'] != '' && strpos($check_line, $options['enclosure']) !== FALSE || $options['enclosure'] == '' && strpos($check_line, $options['end_of_line']) !== FALSE) {
$result[] = 313;
// Error delimiter or enclosure.
}
elseif (_taxonomy_csv_worst_message($result) < TAXONOMY_CSV_PROCESS_NOTICE) {
}
else {
// Save line to file.
$line = $options['enclosure'] . implode($options['separator'], $line) . $options['enclosure'] . $options['end_of_line'];
if (fwrite($handle, $line) === FALSE) {
$result[] = 312;
// Unable to write to file.
}
}
return $result;
}
/**
* Export a term to a line matching the options.
*
* @param $term
* Full term object to export.
* @param $options
* An associative array of export options:
* - export_format : see _taxonomy_csv_values('export_format')
* // Specific to def_links:
* - def_links_terms_ids : 'name_if_needed' (default), 'name' or 'tid'
* - def_links_vocabularies_ids : 'none' (default), 'name' or 'vid'
* @param $terms_list
* (Optional) Array of all term objects to export, used to avoid to repeat
* fetch of terms. Currently needed only with tree_structure export.
* @param $duplicate_terms
* (Optional) Array of duplicate terms names indexed by tid.
* Duplicate terms are managed only with def_links.
*
* @return Result array:
* 'line' => array of exported items,
* 'msg' => array of messages arrays.
*/
function taxonomy_csv_term_export($term, $options, &$terms_list = array(), $duplicate_terms = array()) {
// Define default values.
$result = array(
'line' => array(),
'msg' => array(),
);
// Only count check because term and options are already checked.
if (count($term)) {
switch ($options['export_format']) {
case TAXONOMY_CSV_FORMAT_ALONE_TERMS:
$result['line'] = array(
$term->name,
);
break;
case TAXONOMY_CSV_FORMAT_TID_NAME:
$result['line'] = array(
$term->tid,
$term->name,
);
break;
case TAXONOMY_CSV_FORMAT_TREE_STRUCTURE:
$terms = taxonomy_csv_term_get_first_path($term, $terms_list);
foreach ($terms as $parent) {
$result['line'][] = $parent->name;
}
$result['line'][] = $term->name;
break;
case TAXONOMY_CSV_FORMAT_POLYHIERARCHY:
// Warning : taxonomy_csv_term_get_first_path() returns only first path.
break;
case TAXONOMY_CSV_FORMAT_DEFINITION_LINKS:
// Prepare identifiants of main term and links.
// Check if each term is a duplicate, because identifiants are names,
// except for duplicate terms. For them, tid is appended to name.
$terms = array(
'main' => array(
$term,
),
// Synonyms are included in term object in Drupal 6.
'parents' => taxonomy_get_parents($term->tid),
'children' => taxonomy_get_children($term->tid),
'relations' => taxonomy_get_related($term->tid),
);
foreach (array(
'main',
'parents',
'children',
'relations',
) as $link) {
$ids[$link] = array();
foreach ($terms[$link] as $item) {
// Option is to use term id.
if ($options['def_links_terms_ids'] == 'tid') {
$ids[$link][] = $item->tid;
}
elseif (isset($duplicate_terms[$item->tid])) {
$ids[$link][] = $item->name . ' ' . $item->tid;
}
else {
$ids[$link][] = $item->name;
}
}
}
// Prepare main term vocabulary identifiant.
// Vocabulary identifiant depends on def_links_vocabularies_ids.
$vocabulary = taxonomy_vocabulary_load($term->vid);
// Display a warning if a term has no vocabulary.
// See http://drupal.org/node/1359260.
if (!$vocabulary) {
$result['msg'][] = 409;
// Warning not a good term.
break;
}
$vid = $options['def_links_vocabularies_ids'] == 'none' ? '' : $vocabulary->{$options['def_links_vocabularies_ids']};
// Prepare vocabularies identifiants of related terms.
$relations_vocs = array();
// Option 'none' is impossible with multiple vocabularies, so use name.
$vocabulary_option = $options['def_links_vocabularies_ids'] == 'none' ? 'name' : $options['def_links_vocabularies_ids'];
foreach ($terms['relations'] as $item) {
// Check if related term vocabulary is main term vocabulary.
$vocabulary = taxonomy_vocabulary_load($item->vid);
$relations_vocs[] = $item->vid == $term->vid ? $vid : $vocabulary->{$vocabulary_option};
}
$result['line'] = array_merge(array(
$term->name,
// Main term identifiant: use tid, name or nothing.
$options['def_links_terms_ids'] == 'name_if_needed' && $term->name == $ids['main'][0] ? '' : $ids['main'][0],
$vid,
_taxonomy_csv_escape_line_break($term->description),
$term->weight,
count($term->synonyms),
count($ids['parents']),
count($ids['children']),
count($ids['relations']),
), $term->synonyms, $ids['parents'], $ids['children'], $ids['relations'], $relations_vocs);
break;
case TAXONOMY_CSV_FORMAT_FIELDS:
$result['line'] = array(
$term->name,
$term->vid,
$term->description,
'plain_text',
$term->weight,
);
foreach ($term->parents as $tid) {
$result['line'][] = isset($terms_list[$tid]) ? $terms_list[$tid]->name : taxonomy_csv_term_load($tid)->name;
}
// Add empty value the max number of values in the vocabulary times.
if (count($term->parents) < $options['vocabulary'][$term->vid]->fields_unlimited['parents']) {
$result['line'] = array_merge($result['line'], array_fill(0, $options['vocabulary'][$term->vid]->fields_unlimited['parents'] - count($term->parents), ''));
}
if (module_exists('i18n_taxonomy')) {
switch ($option['vocabulary'][$term->vid]->i18n_mode) {
case I18N_MODE_LANGUAGE:
case I18N_MODE_LOCALIZE:
$result['line'][] = $term->language;
break;
case I18N_MODE_TRANSLATE:
case I18N_MODE_MULTIPLE:
$result['line'][] = $term->language;
// $result['line'][] = $term->i18n_tsid;
break;
}
}
foreach ($term->synonyms as $synonym) {
$result['line'][] = $synonym;
}
if (count($term->synonyms) < $options['vocabulary'][$term->vid]->fields_unlimited['synonyms']) {
$result['line'] = array_merge($result['line'], array_fill(0, $options['vocabulary'][$term->vid]->fields_unlimited['synonyms'] - count($term->synonyms), ''));
}
foreach ($term->relations as $tid) {
$result['line'][] = isset($terms_list[$tid]) ? $terms_list[$tid]->name : taxonomy_csv_term_load($tid)->name;
}
if (count($term->relations) < $options['vocabulary'][$term->vid]->fields_unlimited['relations']) {
$result['line'] = array_merge($result['line'], array_fill(0, $options['vocabulary'][$term->vid]->fields_unlimited['relations'] - count($term->relations), ''));
}
break;
case TAXONOMY_CSV_FORMAT_PARENTS:
$result['line'] = array_merge(array(
$term->name,
), taxonomy_csv_term_get_parents_names($term->tid));
break;
case TAXONOMY_CSV_FORMAT_CHILDREN:
$result['line'] = array_merge(array(
$term->name,
), taxonomy_csv_term_get_children_names($term->tid));
break;
case TAXONOMY_CSV_FORMAT_RELATIONS:
$result['line'] = array_merge(array(
$term->name,
), taxonomy_csv_term_get_relations_names($term->tid));
break;
case TAXONOMY_CSV_FORMAT_SYNONYMS:
$result['line'] = array_merge(array(
$term->name,
), $term->synonyms);
break;
case TAXONOMY_CSV_FORMAT_DEFINITIONS:
$result['line'] = array_merge(array(
$term->name,
$term->weight,
_taxonomy_csv_escape_line_break($term->description),
), $term->synonyms);
break;
case TAXONOMY_CSV_FORMAT_DESCRIPTIONS:
$result['line'] = array(
$term->name,
_taxonomy_csv_escape_line_break($term->description),
);
break;
case TAXONOMY_CSV_FORMAT_WEIGHTS:
$result['line'] = array(
$term->name,
$term->weight,
);
break;
case TAXONOMY_CSV_FORMAT_FIELDS:
// @todo Extra fields.
break;
default:
// Check external formats. Use it only if it works.
$funcname = "taxonomy_csv_term_export_{$options['export_format']}";
if (taxonomy_csv_format_check($options['export_format'], $funcname)) {
$result = $funcname($term, $options, $duplicate_terms);
}
else {
$result['msg'][] = 307;
// Error unknown export format.
}
}
}
else {
$result['msg'][] = 385;
// Error no term to process.
}
// Clean result.
$result['msg'] = array_unique($result['msg']);
sort($result['msg']);
return $result;
}
Functions
Name![]() |
Description |
---|---|
taxonomy_csv_export | Process the export of a vocabulary. |
taxonomy_csv_line_export | Export a line. |
taxonomy_csv_term_export | Export a term to a line matching the options. |
taxonomy_csv_vocabulary_export | Prepare the export of a vocabulary. If not used in a form, don't forget to use batch_process(). |
_taxonomy_csv_export_output_presave | Presave output. |
_taxonomy_csv_vocabulary_export_finished | Callback for finished batch export and display result informations. |
_taxonomy_csv_vocabulary_export_process | Batch process of vocabulary export. |