taxonomy_csv.export.api.inc 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705
  1. <?php
  2. /**
  3. * @file
  4. * Validate export options and manage export process.
  5. */
  6. /**
  7. * Invoke associated include file.
  8. */
  9. $module_dir = drupal_get_path('module', 'taxonomy_csv');
  10. require_once($module_dir . '/taxonomy_csv.api.inc');
  11. require_once($module_dir . '/taxonomy_csv.term.api.inc');
  12. require_once($module_dir . '/taxonomy_csv.vocabulary.api.inc');
  13. /**
  14. * Process the export of a vocabulary.
  15. *
  16. * If not used in a form, don't forget to use batch_process().
  17. *
  18. * @param $options
  19. * An associative array of options:
  20. * - export_format : see format of the csv line (see taxonomy.api.inc)
  21. * - vocabulary_id : vid or machine_name (or array of them) of the vocabulary
  22. * to export (default: 0, which means all)
  23. * - delimiter : one character csv delimiter (default: ',')
  24. * - enclosure : zero or one character csv enclosure (default: '"')
  25. * - line_ending : 'Unix' (default), 'Mac' or 'MS-DOS'
  26. * - order : order of terms: 'name' (default), 'tid' or 'weight'
  27. * // Level of result process infos:
  28. * - check_options : boolean. check or not (default) this array of options
  29. * - result_display: boolean. display or not (default). Only used with UI
  30. * - result_duplicates: boolean. display or not (default) duplicate terms
  31. * All options have default values.
  32. * Warning: default values are different with UI.
  33. *
  34. * @return
  35. * Array of errors or file object to download (need to execute batch process;
  36. * result is logged in watchdog).
  37. */
  38. function taxonomy_csv_export($options) {
  39. // Complete $options with default values if needed.
  40. // Default api and UI options are different.
  41. $options += _taxonomy_csv_values('export_default_api');
  42. // Presave a file in order to check access to temporary folder.
  43. $messages = _taxonomy_csv_export_output_presave($options);
  44. if (count($messages)) {
  45. return $messages;
  46. }
  47. // Process export.
  48. $messages = taxonomy_csv_vocabulary_export($options);
  49. // Return errors if any.
  50. if (count($messages)) {
  51. return $messages;
  52. }
  53. // Else return file infos.
  54. else {
  55. return $options['file'];
  56. }
  57. }
  58. /**
  59. * Presave output.
  60. *
  61. * Check if there is write access and prepare file.
  62. *
  63. * @param $options
  64. * Array. Same as taxonomy_csv_export.
  65. *
  66. * @return
  67. * Array of messages errors if any.
  68. * By reference options are cleaned and completed.
  69. */
  70. function _taxonomy_csv_export_output_presave(&$options) {
  71. $messages = array();
  72. // Check if there is write access and prepare file.
  73. $filename = file_unmanaged_save_data(
  74. '',
  75. 'public://' . 'taxocsv.csv',
  76. 'FILE_EXISTS_RENAME');
  77. if (!$filename) {
  78. $messages['file'] = t('Check access rights to temp directory: export needs permission to write and to read in it. Export failed.');
  79. }
  80. else {
  81. $options['file'] = (object) array(
  82. 'filename' => basename($filename),
  83. 'filepath' => drupal_realpath($filename),
  84. 'filesize' => filesize($filename),
  85. );
  86. }
  87. return $messages;
  88. }
  89. /**
  90. * Prepare the export of a vocabulary.
  91. *
  92. * @note
  93. * If not used in a form, don't forget to use batch_process().
  94. *
  95. * @param $options
  96. * Array. Same as taxonomy_csv_export.
  97. *
  98. * @return
  99. * Array of errors or nothing (batch process to execute).
  100. */
  101. function taxonomy_csv_vocabulary_export($options) {
  102. // Check options and return array of messages in case of errors.
  103. if ($options['check_options']) {
  104. // Invoke export admin file.
  105. $module_dir = drupal_get_path('module', 'taxonomy_csv');
  106. require_once("$module_dir/export/taxonomy_csv.export.admin.inc");
  107. $result = _taxonomy_csv_export_check_options($options);
  108. if (count($result)) {
  109. return $result;
  110. }
  111. }
  112. // Complete $options with some csv variables.
  113. $options['separator'] = $options['enclosure'] . $options['delimiter'] . $options['enclosure'];
  114. $line_ending = array(
  115. 'Unix' => "\n",
  116. 'Mac' => "\r",
  117. 'MS-DOS' => "\r\n",
  118. );
  119. $options['end_of_line'] = $line_ending[$options['line_ending']];
  120. // Calculates number of terms to be exported.
  121. $options['total_terms'] = taxonomy_csv_vocabulary_count_terms($options['vocabulary_id']);
  122. // Get infos about fields of vocabulary.
  123. $options['vocabulary'] = array();
  124. foreach ($options['vocabulary_id'] as $vocabulary_id) {
  125. $options['vocabulary'][$vocabulary_id] = taxonomy_vocabulary_load($vocabulary_id);
  126. $vocabulary = &$options['vocabulary'][$vocabulary_id];
  127. $vocabulary->instances = field_info_instances('taxonomy_term', $vocabulary->machine_name);
  128. // Prepare list of fields to be exported.
  129. $vocabulary->fields = array();
  130. // Not included, because referenced terms are exported by name.
  131. // $vocabulary->fields['tid'] = array('cardinality' => 1);
  132. $vocabulary->fields['name'] = array('cardinality' => 1);
  133. // Not included, because there is already 'vocabulary_machine_name'.
  134. // $vocabulary->fields['vid'] = array('cardinality' => 1);
  135. $vocabulary->fields['vocabulary_machine_name'] = array('cardinality' => 1);
  136. $vocabulary->fields['description'] = array('cardinality' => 1);
  137. $vocabulary->fields['format'] = array('cardinality' => 1);
  138. $vocabulary->fields['weight'] = array('cardinality' => 1);
  139. $vocabulary->fields['parent'] = array('cardinality' => -1);
  140. if (module_exists('i18n_taxonomy')) {
  141. switch ($vocabulary->i18n_mode) {
  142. case I18N_MODE_LANGUAGE:
  143. case I18N_MODE_LOCALIZE:
  144. $vocabulary->fields['language'] = array('cardinality' => 1);
  145. break;
  146. case I18N_MODE_TRANSLATE:
  147. case I18N_MODE_MULTIPLE:
  148. $vocabulary->fields['language'] = array('cardinality' => 1);
  149. $vocabulary->fields['i18n_tsid'] = array('cardinality' => 1);
  150. break;
  151. }
  152. }
  153. // @todo
  154. // $vocabulary->fields['guid'] = array('cardinality' => 1);
  155. // Prepare list of unlimited fields to be exported.
  156. $vocabulary->fields_unlimited = array();
  157. $vocabulary->fields_unlimited['parent'] = 1;
  158. if (is_array($vocabulary->instances)) {
  159. foreach ($vocabulary->instances as $field_name => $value) {
  160. $vocabulary->fields[$field_name] = field_info_field($field_name);
  161. // Get the list of fields with an unlimited number of values to avoid
  162. // the loop of check (used with custom fields export).
  163. // The minimum is set to one to avoid zero value.
  164. if ($vocabulary->fields[$field_name]['cardinality'] == -1) {
  165. $vocabulary->fields_unlimited[$field_name] = 1;
  166. }
  167. }
  168. }
  169. }
  170. // Prepare export batch.
  171. $batch = array(
  172. 'title' => t('Exporting !total_terms terms to CSV file...', array('!total_terms' => $options['total_terms'])),
  173. 'init_message' => t('Starting downloading of datas...') . '<br />'
  174. . t('Wait some seconds for pre-processing...'),
  175. 'progress_message' => '',
  176. 'error_message' => t('An error occurred during the export.'),
  177. 'finished' => '_taxonomy_csv_vocabulary_export_finished',
  178. 'file' => drupal_get_path('module', 'taxonomy_csv') . '/export/taxonomy_csv.export.api.inc',
  179. 'progressive' => TRUE,
  180. 'operations' => array(
  181. 0 => array('_taxonomy_csv_vocabulary_export_process', array($options)),
  182. ),
  183. );
  184. batch_set($batch);
  185. }
  186. /**
  187. * Batch process of vocabulary export.
  188. *
  189. * @todo Check if direct query and only tid save is really less memory
  190. * intensive (with core taxonomy cache or not).
  191. * @todo Don't remember terms, but load them one by one in order to decrease
  192. * memory usage.
  193. *
  194. * @param $options
  195. * Array of batch options.
  196. * @param &$context
  197. * Batch context to keep results and messages.
  198. *
  199. * @return
  200. * NULL because use of &$context.
  201. */
  202. function _taxonomy_csv_vocabulary_export_process($options, &$context) {
  203. // First callback.
  204. if (empty($context['sandbox'])) {
  205. // Remember options as batch_set can't use form_storage.
  206. // It allows too that first line in result is numbered 1 and not 0.
  207. $context['results'][0] = $options;
  208. // Initialize some variables.
  209. $context['results'][0]['current_term'] = 0;
  210. $context['results'][0]['current_name'] = '';
  211. $context['results'][0]['worst_tid'] = 0;
  212. $context['results'][0]['worst_term'] = 0;
  213. $context['results'][0]['worst_name'] = '';
  214. $context['results'][0]['worst_message'] = 799;
  215. // No pointer because new line is appended to file.
  216. $context['results'][0]['handle'] = fopen($options['file']->filepath, 'a+');
  217. // Fetch terms to be exported and order them (by tree or by specific order).
  218. // Prepare a hierarchical tree of terms.
  219. if (in_array($options['export_format'], array(
  220. TAXONOMY_CSV_FORMAT_STRUCTURE,
  221. TAXONOMY_CSV_FORMAT_TREE,
  222. TAXONOMY_CSV_FORMAT_POLYHIERARCHY,
  223. ))) {
  224. $context['sandbox']['terms'] = array();
  225. // Export only selected vocabularies.
  226. if ($options['vocabulary_id'] && ($options['vocabulary_id'] != array(0))) {
  227. $vocabularies = array();
  228. foreach ($options['vocabulary_id'] as $vocabulary) {
  229. $vocabularies[] = taxonomy_vocabulary_load($vocabulary);
  230. }
  231. }
  232. // Export all vocabularies.
  233. else {
  234. $vocabularies = taxonomy_vocabulary_get_names();
  235. }
  236. foreach ($vocabularies as $vocabulary) {
  237. $context['sandbox']['terms'] = array_merge($context['sandbox']['terms'], taxonomy_get_tree($vocabulary->vid));
  238. }
  239. }
  240. // Prepare a list of normal terms.
  241. // @todo Prepare a simple list of term names or definitions without links in
  242. // order to reduce memory usage for some formats.
  243. // @todo Use a one term approach to reduce memory usage (anyway, all terms
  244. // will be load)?
  245. else {
  246. $query = new EntityFieldQuery();
  247. $query->entityCondition('entity_type', 'taxonomy_term');
  248. if ($options['vocabulary_id'] && ($options['vocabulary_id'] != array(0))) {
  249. $query->propertyCondition('vid', $options['vocabulary_id']);
  250. }
  251. $query->propertyOrderBy($options['order'], 'ASC');
  252. $terms = $query->execute();
  253. if ($terms['taxonomy_term'] === NULL) {
  254. $terms['taxonomy_term'] = array();
  255. }
  256. $context['sandbox']['terms'] = taxonomy_term_load_multiple(array_keys($terms['taxonomy_term']));
  257. }
  258. // Get max number of values for fields with a undefined number of values.
  259. if ($options['export_format'] == TAXONOMY_CSV_FORMAT_FIELDS) {
  260. // Currently, manage only undefined language.
  261. $language = 'und';
  262. foreach ($context['sandbox']['terms'] as $term) {
  263. foreach (array_keys($options['vocabulary'][$term->vid]->fields_unlimited) as $field_name) {
  264. if (isset($term->{$field_name})) {
  265. // Remember the number of items if this term field has got more
  266. // items than previous one.
  267. if ($field_name == 'parent'
  268. && (count($term->parent) > $options['vocabulary'][$term->vid]->fields_unlimited['parent'])
  269. ) {
  270. $options['vocabulary'][$term->vid]->fields_unlimited['parent'] = count($term->parent);
  271. }
  272. elseif (count($term->{$field_name}[$language]) > $options['vocabulary'][$term->vid]->fields_unlimited[$field_name]) {
  273. $options['vocabulary'][$term->vid]->fields_unlimited[$field_name] = count($term->{$field_name}[$language]);
  274. }
  275. }
  276. }
  277. }
  278. // Keep updated the list of vocabulary with undefined fields.
  279. $context['results'][0]['vocabulary'] = $options['vocabulary'];
  280. $context['sandbox']['vocabulary'] = $options['vocabulary'];
  281. }
  282. // Display a warning if terms count in taxonomy_term_data table is different
  283. // of terms counted with Drupal taxonomy_term_load_multiple() function.
  284. // See http://drupal.org/node/1359260.
  285. if ($options['total_terms'] != count($context['sandbox']['terms'])) {
  286. // Memorize the first wrong term.
  287. $first_wrong_term = array_shift(array_diff_key($terms['taxonomy_term'], taxonomy_term_load_multiple(array_keys($terms['taxonomy_term']))));
  288. $context['results'][0]['worst_tid'] = $first_wrong_term->tid;
  289. $context['results'][0]['worst_term'] = '';
  290. $context['results'][0]['worst_name'] = 'tid:' . ' ' . $first_wrong_term->tid;
  291. $context['results'][0]['worst_message'] = 408; // Warning not a good term.
  292. }
  293. // Clean memory.
  294. if (isset($terms)) {
  295. unset($terms);
  296. }
  297. // Duplicate names will be searched after export process.
  298. $context['results'][0]['duplicate_terms'] = array();
  299. }
  300. elseif (!is_resource($context['results'][0]['handle'])) {
  301. // Reopen file in case of memory out.
  302. $context['results'][0]['handle'] = fopen($options['file']->filepath, 'a+');
  303. }
  304. // Get list of unlimited multivalued fields.
  305. // @todo Currently, these fields are not saved in batch.
  306. if ($options['export_format'] == TAXONOMY_CSV_FORMAT_FIELDS) {
  307. $options['vocabulary'] = $context['sandbox']['vocabulary'];
  308. }
  309. // To simplify use of variables.
  310. $worst_tid = &$context['results'][0]['worst_tid'];
  311. $worst_term = &$context['results'][0]['worst_term'];
  312. $worst_name = &$context['results'][0]['worst_name'];
  313. $worst_message = &$context['results'][0]['worst_message'];
  314. $handle = &$context['results'][0]['handle'];
  315. $duplicate_terms = &$context['results'][0]['duplicate_terms'];
  316. $term_number = &$context['results'][0]['current_term'];
  317. $current_name = &$context['results'][0]['current_name'];
  318. $terms_list = &$context['sandbox']['terms'];
  319. $terms_list_ids = array_keys($terms_list);
  320. // Process one term until end of vocabulary.
  321. if ($term_number < count($context['sandbox']['terms'])) {
  322. $term = $terms_list[$terms_list_ids[$term_number]];
  323. ++$term_number;
  324. // Remember current name/tid in case of error.
  325. $current_name = $term->name;
  326. $current_tid = $term->tid;
  327. // Process export of current term.
  328. $result = taxonomy_csv_term_export($term, $options, $terms_list, $duplicate_terms);
  329. // If result is empty, there is nothing to export (with Translation export,
  330. // terms can be skipped and exported with another term).
  331. $result['msg'] = taxonomy_csv_line_export($result['line'], $options, $handle, $result['msg']);
  332. // Remember worst message of exported terms.
  333. $worst_message_new = _taxonomy_csv_worst_message($result['msg']);
  334. if ($worst_message > $worst_message_new) {
  335. $worst_tid = $current_tid;
  336. $worst_term = $term_number;
  337. $worst_name = $current_name;
  338. $worst_message = $worst_message_new;
  339. };
  340. // Remember messages. Currently useless because there isn't any warning or
  341. // notice message (only error). A result level can be added here if needed.
  342. if (count($result['msg'])) {
  343. $context['results'][$term_number] = $result['msg'];
  344. }
  345. // Inform about progress.
  346. $context['message'] = t('Term !term_number of !total_terms processed: %term', array(
  347. '!term_number' => $term_number,
  348. '!total_terms' => $options['total_terms'],
  349. '%term' => $current_name,
  350. ));
  351. // Check worst message of exported terms and update progress.
  352. if ($worst_message >= TAXONOMY_CSV_PROCESS_WARNING) {
  353. // Count should be <= 0.99 to avoid batch stop before end (Drupal 7 bug).
  354. $context['finished'] = floor($term_number / count($context['sandbox']['terms']) * 100) / 100;
  355. }
  356. else {
  357. $context['finished'] = 1;
  358. }
  359. }
  360. }
  361. /**
  362. * Callback for finished batch export and display result informations.
  363. */
  364. function _taxonomy_csv_vocabulary_export_finished($success, $results, $operations) {
  365. $options = &$results[0];
  366. unset($results[0]);
  367. // Close exported file.
  368. if ($options['handle']) {
  369. fclose($options['handle']);
  370. }
  371. // Invoke export stats file if user wants to display results.
  372. if ($options['result_display']) {
  373. $module_dir = drupal_get_path('module', 'taxonomy_csv');
  374. require_once("$module_dir/export/taxonomy_csv.export.result.inc");
  375. }
  376. // Short summary information is different if batch succeeded or not.
  377. if ($success) {
  378. if (!empty($options['worst_tid'])) {
  379. $worst_path = drupal_get_path_alias('taxonomy/term/' . $options['worst_tid']);
  380. $options['worst_name'] = l($options['worst_name'], $worst_path);
  381. }
  382. $variables = array(
  383. '!total_terms' => $options['total_terms'],
  384. '!worst_count' => $options['worst_term'],
  385. '!worst_name' => $options['worst_name'],
  386. '!worst_msg' => $options['result_display'] ?
  387. _taxonomy_csv_message_text($options['worst_message']) :
  388. t('Message code') . ' = ' . $options['worst_message'],
  389. );
  390. $messages = array(
  391. WATCHDOG_DEBUG => t('No error, warnings or notices have been reported during export process of !total_terms terms.', $variables),
  392. WATCHDOG_INFO => t('No error, warnings or notices have been reported during export process of !total_terms terms.', $variables),
  393. 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),
  394. 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),
  395. 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),
  396. );
  397. $worst_level = intval($options['worst_message'] / 100);
  398. $message = $messages[$worst_level];
  399. }
  400. else {
  401. $message = t('Exportation failed. Export process was successful until the term !term_count (!term_name) of a total of !total_terms.', array(
  402. '!term_count' => $options['current_term'],
  403. '!term_name' => $options['current_name'],
  404. '!total_terms' => $options['total_terms'],
  405. )) . '<br />'
  406. . 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(
  407. '!link' => url('http://drupal.org/project/issues/taxonomy_csv/'),
  408. ));
  409. $worst_level = WATCHDOG_ERROR;
  410. }
  411. // Set result message in watchdog and eventually in user interface.
  412. // Use of a $message variable is unrecommended, but simpler and working.
  413. // See http://drupal.org/node/323101
  414. watchdog('taxonomy_csv', $message, NULL, $worst_level);
  415. if ($options['result_display']) {
  416. _taxonomy_csv_export_result($options, $worst_level, $message, $results);
  417. }
  418. }
  419. /**
  420. * Export a line.
  421. *
  422. * @param $line
  423. * Array to be exported to a line.
  424. * @param $options
  425. * An associative array of export options:
  426. * - 'separator' : string separator (formatted delimiter and enclosure).
  427. * - 'enclosure' : item enclosure.
  428. * - 'end_of_line': end of line string.
  429. * @param $handle
  430. * Handle of the open file where to save line.
  431. * @param $result_message
  432. * (Optional) Array of messages.
  433. *
  434. * @return
  435. * Result array of messages.
  436. */
  437. function taxonomy_csv_line_export($line, $options, &$handle, $result = array()) {
  438. if (!$line) {
  439. return $result;
  440. }
  441. // Check if separator, enclosure or line ending exist in line.
  442. $check_line = implode('', $line);
  443. if ((strpos($check_line, $options['separator']) !== FALSE)
  444. || (($options['enclosure'] != '')
  445. && (strpos($check_line, $options['enclosure']) !== FALSE))
  446. || (($options['enclosure'] == '')
  447. && (strpos($check_line, $options['end_of_line']) !== FALSE))) {
  448. $result[] = 313; // Error delimiter or enclosure.
  449. }
  450. // Skip a term with warning.
  451. elseif (_taxonomy_csv_worst_message($result) < TAXONOMY_CSV_PROCESS_NOTICE) {
  452. }
  453. else {
  454. // Save line to file.
  455. $line = $options['enclosure'] . implode($options['separator'], $line) . $options['enclosure'] . $options['end_of_line'];
  456. if (fwrite($handle, $line) === FALSE) {
  457. $result[] = 312; // Unable to write to file.
  458. }
  459. }
  460. return $result;
  461. }
  462. /**
  463. * Export a term to a line matching the options.
  464. *
  465. * @param $term
  466. * Full term object to export.
  467. * @param $options
  468. * An associative array of export options:
  469. * - export_format : format of the csv line (see taxonomy.api.inc)
  470. * @param $terms_list
  471. * (Optional) Array of all term objects to export, used to avoid to repeat
  472. * fetch of terms.
  473. * @param $duplicate_terms
  474. * (Optional) Array of duplicate terms names indexed by tid.
  475. *
  476. * @return
  477. * Result array with:
  478. * - 'line' => array of exported items,
  479. * - 'msg' => array of messages arrays.
  480. */
  481. function taxonomy_csv_term_export($term, $options, &$terms_list = array(), $duplicate_terms = array()) {
  482. // Define default values.
  483. $result = array(
  484. 'line' => array(),
  485. 'msg' => array(),
  486. );
  487. // Only count check because term and options are already checked.
  488. if (count($term)) {
  489. switch ($options['export_format']) {
  490. case TAXONOMY_CSV_FORMAT_FLAT:
  491. $result['line'][] = $term->name;
  492. break;
  493. case TAXONOMY_CSV_FORMAT_STRUCTURE:
  494. case TAXONOMY_CSV_FORMAT_TREE:
  495. $terms = taxonomy_csv_term_get_first_path($term, $terms_list);
  496. foreach ($terms as $parent) {
  497. $result['line'][] = $parent->name;
  498. }
  499. $result['line'][] = $term->name;
  500. break;
  501. case TAXONOMY_CSV_FORMAT_POLYHIERARCHY:
  502. // @todo
  503. // Warning : taxonomy_csv_term_get_first_path() returns only first path.
  504. break;
  505. case TAXONOMY_CSV_FORMAT_FIELDS:
  506. // Currently, manage only undefined language.
  507. $language = 'und';
  508. // Use of field_get_items is slower.
  509. foreach ($options['vocabulary'][$term->vid]->fields as $field_name => $field) {
  510. $count = 0;
  511. // Item is a Field.
  512. if (isset($field['type'])) {
  513. if (isset($term->{$field_name}[$language])) {
  514. // For taxonomy term reference, use name instead of value.
  515. if ($field['type'] == 'taxonomy_term_reference') {
  516. foreach ($term->{$field_name}[$language] as &$item) {
  517. $result['line'][] = isset($terms_list[$item['tid']]) ?
  518. $terms_list[$item['tid']]->name :
  519. taxonomy_term_load($item['tid'])->name;
  520. }
  521. }
  522. // For long text, need to escape the value.
  523. elseif ($field['type'] == 'text_long'
  524. || $field['type'] == 'text_with_summary'
  525. ) {
  526. foreach ($term->{$field_name}[$language] as &$item) {
  527. $result['line'][] = _taxonomy_csv_escape_line_break($item['value']);
  528. }
  529. }
  530. else {
  531. // Key is generally 'value' but it can be something else.
  532. reset($field['columns']);
  533. foreach ($term->{$field_name}[$language] as &$item) {
  534. $result['line'][] = $item[key($field['columns'])];
  535. }
  536. }
  537. $count = count($term->{$field_name}[$language]);
  538. }
  539. }
  540. // Item is a database term field (name, tid, language...).
  541. else {
  542. // For taxonomy term parent, use name instead of value.
  543. if ($field_name == 'parent') {
  544. if (isset($term->parent)) {
  545. foreach ($term->parent as $tid) {
  546. $result['line'][] = isset($terms_list[$tid]) ?
  547. $terms_list[$tid]->name :
  548. taxonomy_term_load($tid)->name;
  549. }
  550. $count = count($term->parent);
  551. }
  552. }
  553. elseif ($field_name == 'description') {
  554. $result['line'][] = _taxonomy_csv_escape_line_break($term->description);
  555. $count = 1;
  556. }
  557. else {
  558. $result['line'][] = $term->{$field_name};
  559. $count = 1;
  560. }
  561. }
  562. // Add empty value the max number of values in the vocabulary times.
  563. if (isset($options['vocabulary'][$term->vid]->fields_unlimited[$field_name])) {
  564. if ($count < $options['vocabulary'][$term->vid]->fields_unlimited[$field_name]) {
  565. $result['line'] = array_merge($result['line'], array_fill(0, $options['vocabulary'][$term->vid]->fields_unlimited[$field_name] - $count, ''));
  566. }
  567. }
  568. elseif ($count < $field['cardinality']) {
  569. $result['line'] = array_merge($result['line'], array_fill(0, $field['cardinality'] - $count, ''));
  570. }
  571. }
  572. break;
  573. case TAXONOMY_CSV_FORMAT_TRANSLATE:
  574. if (!module_exists('i18n_taxonomy')) {
  575. $result['msg'][] = 360; // Translation error.
  576. break;
  577. }
  578. switch ($options['vocabulary'][$term->vid]->i18n_mode) {
  579. case I18N_MODE_NONE:
  580. case I18N_MODE_LANGUAGE:
  581. $result['line'][] = $term->name;
  582. break;
  583. case I18N_MODE_LOCALIZE:
  584. $result['line'][] = $term->name;
  585. $languages = locale_language_list('name');
  586. unset($languages[language_default('language')]);
  587. foreach ($languages as $language => $value) {
  588. $translation = i18n_string_translate(
  589. array('taxonomy', 'term', $term->tid, 'name'),
  590. $term->name,
  591. array('langcode' => $language)
  592. );
  593. $result['line'][] = $translation ?
  594. $translation :
  595. '';
  596. }
  597. break;
  598. case I18N_MODE_TRANSLATE:
  599. case I18N_MODE_MULTIPLE:
  600. $languages = array(
  601. 'und' => t('Language neutral'),
  602. );
  603. $languages += locale_language_list('name');
  604. $languages = array_flip(array_keys($languages));
  605. $result['line'] = array_fill(0, count($languages), '');
  606. if ($term->i18n_tsid == 0) {
  607. $result['line'][$languages[$term->language]] = $term->name;
  608. }
  609. else {
  610. $translation_set = i18n_translation_set_load($term->i18n_tsid);
  611. $translations = $translation_set->get_translations();
  612. $language_min = $languages[$term->language];
  613. foreach ($translations as $language => $translated_term) {
  614. $result['line'][$languages[$language]] = $translated_term->name;
  615. // Check if this term is already exported (when there is a term
  616. // with a language before the current one).
  617. if ($languages[$language] < $language_min) {
  618. $language_min = $languages[$language];
  619. }
  620. }
  621. // This term is already exported or will be exported with another
  622. // term.
  623. if ($language_min < $languages[$term->language]) {
  624. $result['line'] = array();
  625. }
  626. }
  627. break;
  628. }
  629. break;
  630. default:
  631. $result['msg'][] = 307; // Error unknown export format.
  632. }
  633. }
  634. else {
  635. $result['msg'][] = 385; // Error no term to process.
  636. }
  637. // Clean result.
  638. $result['msg'] = array_unique($result['msg']);
  639. sort($result['msg']);
  640. return $result;
  641. }