taxonomy_csv.import.line.api.inc 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024
  1. <?php
  2. /**
  3. * @file
  4. * Process import of a csv line, i.e. of a term or a list of terms.
  5. */
  6. /**
  7. * Process the import of items.
  8. *
  9. * @param $line
  10. * Array which contains items of a cleaned and checked csv line.
  11. * @param $options
  12. * An associative array of import options:
  13. * - import_format : format of the csv line (see taxonomy.api.inc)
  14. * - keep_order : boolean. keep order of imported terms or not (default)
  15. * - vocabulary : vocabulary object where to import terms into
  16. * - update_or_ignore: indicates what will become existing terms, if any.
  17. * @param $previous_items
  18. * (Optional) Cleaned and checked previous imported line names and tids array.
  19. * Needed with some contents as one term array structure.
  20. * @param $terms_count
  21. * (Optional integer) Total of imported terms (duplicate included) is needed
  22. * to set weight of terms and to keep order of items, if wished.
  23. *
  24. * @return
  25. * Result array:
  26. * - 'name' => array of imported terms names,
  27. * - 'tid' => array of imported terms tids,
  28. * - 'msg' => array of message codes,
  29. * - 'terms_count' => number of imported terms.
  30. */
  31. function taxonomy_csv_line_import($line, $options, $previous_items = array(), $terms_count = 0) {
  32. // Define default values.
  33. $result = array(
  34. 'name' => array(),
  35. 'tid' => array(),
  36. 'msg' => array(),
  37. 'terms_count' => $terms_count,
  38. );
  39. // Only count check because function variables are already checked.
  40. if (count($line)) {
  41. switch ($options['import_format']) {
  42. case TAXONOMY_CSV_FORMAT_FLAT:
  43. $result = taxonomy_csv_line_import_flat($line, $options, $terms_count);
  44. break;
  45. case TAXONOMY_CSV_FORMAT_STRUCTURE:
  46. case TAXONOMY_CSV_FORMAT_TREE:
  47. // Internally, tree import format use ignore_previous and not update.
  48. if ($options['update_or_ignore'] == TAXONOMY_CSV_EXISTING_UPDATE) {
  49. $options['update_or_ignore'] = TAXONOMY_CSV_EXISTING_IGNORE_PREVIOUS;
  50. }
  51. $result = taxonomy_csv_line_import_structure($line, $options, $previous_items, $terms_count);
  52. break;
  53. case TAXONOMY_CSV_FORMAT_POLYHIERARCHY:
  54. // Internally, polyhierarchy import format doesn't support Ignore.
  55. $options['update_or_ignore'] = TAXONOMY_CSV_EXISTING_UPDATE;
  56. $result = taxonomy_csv_line_import_structure($line, $options, $previous_items, $terms_count);
  57. break;
  58. case TAXONOMY_CSV_FORMAT_FIELDS:
  59. $result = taxonomy_csv_line_import_fields($line, $options, $terms_count);
  60. break;
  61. case TAXONOMY_CSV_FORMAT_TRANSLATE:
  62. if (!module_exists('i18n_taxonomy')) {
  63. $result['msg'][] = 360; // Translation error.
  64. break;
  65. }
  66. switch ($options['vocabulary']->i18n_mode) {
  67. case I18N_MODE_LOCALIZE:
  68. $result = taxonomy_csv_line_import_localize($line, $options, $terms_count);
  69. break;
  70. case I18N_MODE_TRANSLATE:
  71. case I18N_MODE_MULTIPLE:
  72. $result = taxonomy_csv_line_import_translate($line, $options, $terms_count);
  73. break;
  74. default:
  75. $result['msg'][] = 361; // Translation of vocabulary error.
  76. }
  77. break;
  78. default:
  79. $result['msg'][] = 306; // Error unknown import format.
  80. }
  81. }
  82. else {
  83. $result['msg'][] = 685; // No term to process.
  84. }
  85. return $result;
  86. }
  87. /**
  88. * Import a flat line.
  89. *
  90. * @see taxonomy_csv_line_import()
  91. */
  92. function taxonomy_csv_line_import_flat($line, $options, $terms_count = 0) {
  93. // Define default values.
  94. $result = array(
  95. 'name' => array(),
  96. 'tid' => array(),
  97. 'msg' => array(),
  98. 'terms_count' => $terms_count,
  99. );
  100. foreach ($line as $term_name) {
  101. $term = new stdClass;
  102. $term->name = $term_name;
  103. $term->vid = $options['vocabulary']->vid;
  104. $term->vocabulary_machine_name = $options['vocabulary']->machine_name;
  105. $term->format = $options['filter_format'];
  106. $term->language = $options['language'];
  107. // Weight is not set above in order to keep existing one if it exists.
  108. ++$terms_count;
  109. if ($options['keep_order']) {
  110. $term->weight = $terms_count;
  111. }
  112. // Import term then store and check result.
  113. $current_result = taxonomy_csv_term_import($term, $options['update_or_ignore']);
  114. if (_taxonomy_csv_line_result($result, $current_result, $terms_count)) {
  115. return $result;
  116. }
  117. }
  118. return $result;
  119. }
  120. /**
  121. * Import a structure line.
  122. *
  123. * @see taxonomy_csv_line_import()
  124. */
  125. function taxonomy_csv_line_import_structure($line, $options, $previous_items = array(), $terms_count = 0) {
  126. // Define default values.
  127. $result = array(
  128. 'name' => array(),
  129. 'tid' => array(),
  130. 'msg' => array(),
  131. 'terms_count' => $terms_count,
  132. );
  133. // 1. Check if one term or partial line format is used in order to virtually
  134. // complete line with previous line.
  135. // Find first non empty item as line can be full, partial or one term.
  136. for ($first = 0; ($first < count($line)) && empty($line[$first]); $first++) {
  137. }
  138. // Else line is full. As with one term import, imported items on previous line
  139. // can be bypassed if lines are ordered. So, look for first different item.
  140. if (!$first) {
  141. for ($first = 0;
  142. ($first < count($line) - 1)
  143. && ($first < count($previous_items['name']))
  144. && ($line[$first] == $previous_items['name'][$first]);
  145. $first++) {
  146. }
  147. }
  148. // 2. Remove and remember superabondant previous items for next line.
  149. if ($first) {
  150. $result['name'] = $previous_items['name'] = array_slice($previous_items['name'], 0, $first);
  151. $result['tid'] = $previous_items['tid'] = array_slice($previous_items['tid'], 0, $first);
  152. $result['msg'][] = 683; // Previous line term.
  153. // Set root or previous ancestor name and id.
  154. $parent_name = $previous_items['name'][$first - 1];
  155. $parent_tid = $previous_items['tid'][$first - 1];
  156. }
  157. // Item is the first on the line: it's a root term.
  158. else {
  159. $parent_name = '';
  160. $parent_tid = 0;
  161. }
  162. // 3. Import each new term then store and check result.
  163. for ($c = $first; $c < count($line); $c++) {
  164. $term = new stdClass;
  165. $term->name = $line[$c];
  166. $term->vid = $options['vocabulary']->vid;
  167. $term->vocabulary_machine_name = $options['vocabulary']->machine_name;
  168. $term->format = $options['filter_format'];
  169. $term->language = $options['language'];
  170. $term->parent = array($parent_tid => $parent_tid);
  171. // Weight is not set above in order to keep existing one if it exists.
  172. ++$terms_count;
  173. if ($options['keep_order']) {
  174. $term->weight = $terms_count;
  175. }
  176. switch ($options['import_format']) {
  177. case TAXONOMY_CSV_FORMAT_STRUCTURE:
  178. case TAXONOMY_CSV_FORMAT_TREE:
  179. // With TAXONOMY_CSV_EXISTING_IGNORE, parent terms (so all terms but the
  180. // last on this line) are always updated because they are successive
  181. // parents of a child.
  182. $current_result = ($options['update_or_ignore'] == TAXONOMY_CSV_EXISTING_IGNORE && ($c < count($line) - 1)) ?
  183. taxonomy_csv_term_import($term, TAXONOMY_CSV_EXISTING_IGNORE_PREVIOUS, $parent_tid) :
  184. taxonomy_csv_term_import($term, $options['update_or_ignore'], $parent_tid);
  185. break;
  186. case TAXONOMY_CSV_FORMAT_POLYHIERARCHY:
  187. // Check direct duplicates: in Drupal, a term can't be its parent.
  188. $current_result = ($term->name == $parent_name) ?
  189. taxonomy_csv_term_import($term, $options['update_or_ignore'], $parent_tid) :
  190. taxonomy_csv_term_import($term, $options['update_or_ignore'], NULL);
  191. break;
  192. }
  193. $parent_name = $current_result['name'];
  194. $parent_tid = $current_result['tid'];
  195. if (_taxonomy_csv_line_result($result, $current_result, $terms_count)) {
  196. return $result;
  197. }
  198. }
  199. return $result;
  200. }
  201. /**
  202. * Import a fields line.
  203. *
  204. * @see taxonomy_csv_line_import()
  205. */
  206. function taxonomy_csv_line_import_fields($line, $options, $terms_count = 0) {
  207. // Define default values.
  208. $result = array(
  209. 'name' => array(),
  210. 'tid' => array(),
  211. 'msg' => array(),
  212. 'terms_count' => $terms_count,
  213. );
  214. // Define a default term.
  215. $term = new stdClass;
  216. $term->vid = $options['vocabulary']->vid;
  217. $term->vocabulary_machine_name = $options['vocabulary']->machine_name;
  218. $term->format = $options['filter_format'];
  219. $term->language = $options['language'];
  220. // Weight is not set above in order to keep existing one if it exists.
  221. ++$terms_count;
  222. if ($options['keep_order']) {
  223. $term->weight = $terms_count;
  224. }
  225. $parents = array();
  226. // Import normal fields according custom format.
  227. foreach ($options['fields_format'] as $key => $field_name) {
  228. if (!isset($line[$key]) || ($line[$key] === '')) {
  229. continue;
  230. }
  231. if (in_array($field_name, array(
  232. 'tid',
  233. 'name',
  234. 'format',
  235. 'weight',
  236. 'language',
  237. 'i18n_tsid',
  238. 'guid',
  239. ))) {
  240. $term->{$field_name} = $line[$key];
  241. }
  242. elseif ($field_name == 'vid') {
  243. $term->vid = $line[$key];
  244. $term->vocabulary_machine_name = taxonomy_vocabulary_load($line[$key])->machine_name;
  245. }
  246. elseif ($field_name == 'vocabulary_machine_name') {
  247. $term->vocabulary_machine_name = $line[$key];
  248. $term->vid = taxonomy_vocabulary_machine_name_load($line[$key])->vid;
  249. }
  250. elseif ($field_name == 'description') {
  251. $term->description = _taxonomy_csv_set_line_break($line[$key]);
  252. }
  253. elseif ($field_name == 'parent') {
  254. // Just remember value: parents are imported later in the process.
  255. $parents[] = $line[$key];
  256. }
  257. elseif (isset($options['fields'][$field_name])) {
  258. // @todo To be rewritten.
  259. // True fields will be added later.
  260. // Currently, repetitive items are not managed.
  261. $term->fields_to_import[$field_name][] = $line[$key];
  262. $term->fields_to_import_instances = $options['instances'];
  263. $term->fields_to_import_fields = $options['fields'];
  264. }
  265. // Else there is an error.
  266. else {
  267. $current_result['msg'] = 341; // Non existing field.
  268. return $result;
  269. }
  270. }
  271. // Import parents if any.
  272. foreach ($parents as $value) {
  273. // Presume that item is a tid if it's a number.
  274. if (is_numeric($value)) {
  275. if (!$value) {
  276. $current_result['msg'] = 455; // Reference to term 0.
  277. continue;
  278. }
  279. // @todo Check if it isn't the current term id.
  280. // @todo Check if parent is in current vocabulary.
  281. $term->parent[] = $value;
  282. }
  283. // Else find the parent normally.
  284. else {
  285. $parent_term = new stdClass;
  286. $parent_term->name = $value;
  287. $parent_term->vid = $term->vid;
  288. $parent_term->vocabulary_machine_name = $term->vocabulary_machine_name;
  289. $parent_term->language = $term->language;
  290. $current_result = taxonomy_csv_term_import($parent_term, $options['update_or_ignore']);
  291. if (_taxonomy_csv_line_result($result, $current_result, $terms_count)) {
  292. return $result;
  293. }
  294. $term->parent[] = $current_result['tid'];
  295. }
  296. }
  297. // Import main term then store result. No check because only one term.
  298. $current_result = taxonomy_csv_term_import($term, $options['update_or_ignore']);
  299. $error = _taxonomy_csv_line_result($result, $current_result, $terms_count);
  300. return $result;
  301. }
  302. /**
  303. * Import a localization line.
  304. *
  305. * @see taxonomy_csv_line_import()
  306. */
  307. function taxonomy_csv_line_import_localize($line, $options, $terms_count = 0) {
  308. // Define default values.
  309. $result = array(
  310. 'name' => array(),
  311. 'tid' => array(),
  312. 'msg' => array(),
  313. 'terms_count' => $terms_count,
  314. );
  315. // 1. Import main term then store and check result.
  316. $term = new stdClass;
  317. if ($options['translate_by'] == 'name') {
  318. $term->name = $line[0];
  319. }
  320. else {
  321. $term->tid = $line[0];
  322. }
  323. $term->vid = $options['vocabulary']->vid;
  324. $term->vocabulary_machine_name = $options['vocabulary']->machine_name;
  325. // With this i18n mode, language of main term is always undefined, whatever
  326. // the vocabulary language is.
  327. $term->language = 'und';
  328. $first_description = count($options['translate_languages']);
  329. if (isset($line[$first_description]) && $line[$first_description]) {
  330. // Other formats aren't allowed for translation of the description with this
  331. // i18n mode.
  332. $term->format = 'plain_text';
  333. $term->description = _taxonomy_csv_set_line_break($line[$first_description]);
  334. }
  335. // Weight is not set above in order to keep existing one if it exists.
  336. ++$terms_count;
  337. if ($options['keep_order']) {
  338. $term->weight = $terms_count;
  339. }
  340. // Import term then store result.
  341. $current_result = taxonomy_csv_term_import($term, $options['update_or_ignore']);
  342. if (_taxonomy_csv_line_result($result, $current_result, $terms_count)) {
  343. break;
  344. }
  345. // 2. Create localizations.
  346. foreach ($options['translate_languages'] as $key => $language) {
  347. // Don't import main item.
  348. if ($key == 0) {
  349. continue;
  350. }
  351. $result_translation = i18n_string_translation_update(
  352. array('taxonomy', 'term', $current_result['tid'], 'name'),
  353. $line[$key],
  354. $language,
  355. $line[0]
  356. );
  357. if (!$result_translation) {
  358. $result['msg'][] = 360; // Translation error.
  359. return $result;
  360. }
  361. if (isset($line[$first_description + $key])
  362. && $line[$first_description]
  363. && $line[$first_description + $key]
  364. ) {
  365. $result_translation = i18n_string_translation_update(
  366. array('taxonomy', 'term', $current_result['tid'], 'description'),
  367. _taxonomy_csv_set_line_break($line[$first_description + $key]),
  368. $language,
  369. _taxonomy_csv_set_line_break($line[$first_description])
  370. );
  371. if (!$result_translation) {
  372. $result['msg'][] = 360; // Translation error.
  373. return $result;
  374. }
  375. }
  376. }
  377. return $result;
  378. }
  379. /**
  380. * Import a translation line.
  381. *
  382. * @see taxonomy_csv_line_import()
  383. */
  384. function taxonomy_csv_line_import_translate($line, $options, $terms_count = 0) {
  385. // Define default values.
  386. $result = array(
  387. 'name' => array(),
  388. 'tid' => array(),
  389. 'msg' => array(),
  390. 'terms_count' => $terms_count,
  391. );
  392. // 1. Prepare main term.
  393. $term = new stdClass;
  394. if ($options['translate_by'] == 'name') {
  395. $term->name = $line[0];
  396. }
  397. else {
  398. $term->tid = $line[0];
  399. }
  400. $term->vid = $options['vocabulary']->vid;
  401. $term->vocabulary_machine_name = $options['vocabulary']->machine_name;
  402. $term->format = $options['filter_format'];
  403. $term->language = $options['translate_languages'][0];
  404. // Weight is not set above in order to keep existing one if it exists.
  405. ++$terms_count;
  406. if ($options['keep_order']) {
  407. $term->weight = $terms_count;
  408. }
  409. // Import term then store result.
  410. $current_result = taxonomy_csv_term_import($term, $options['update_or_ignore']);
  411. if (_taxonomy_csv_line_result($result, $current_result, $terms_count)) {
  412. return $result;
  413. }
  414. // Need to get term with tid and eventual tsid.
  415. $term = taxonomy_term_load($current_result['tid']);
  416. // 2. Use translation set of the term if it exists, else create it.
  417. $translation_set = ($term->i18n_tsid) ?
  418. i18n_translation_set_load($term->i18n_tsid) :
  419. i18n_translation_set_create('taxonomy_term', $options['vocabulary']->machine_name);
  420. $translation_set->add_item($term, $term->language);
  421. // 3. Check if the term is already translated in order to update or create it.
  422. $existing_terms = $translation_set->get_translations();
  423. foreach ($options['translate_languages'] as $key => $language) {
  424. // Don't import main item.
  425. if ($key == 0) {
  426. continue;
  427. }
  428. // Don't import an empty item.
  429. if (isset($line[$key]) && $line[$key]) {
  430. $translated_term = new stdClass;
  431. if (isset($existing_terms[$language])) {
  432. switch ($options['update_or_ignore']) {
  433. case TAXONOMY_CSV_EXISTING_UPDATE:
  434. $translated_term = $existing_terms[$language];
  435. // As the term is already loaded and we have the tid, we simply save
  436. // it without to find it a new time (avoid search).
  437. $options['update_or_ignore'] = TAXONOMY_CSV_EXISTING_IGNORE;
  438. break;
  439. case TAXONOMY_CSV_EXISTING_IGNORE:
  440. break;
  441. }
  442. }
  443. // 4. Complete or create the translated term, then import it.
  444. $translated_term->name = $line[$key];
  445. $translated_term->vid = $options['vocabulary']->vid;
  446. $translated_term->vocabulary_machine_name = $options['vocabulary']->machine_name;
  447. $translated_term->format = $options['filter_format'];
  448. $translated_term->language = $language;
  449. // Import term then store result.
  450. $current_result = taxonomy_csv_term_import($translated_term, $options['update_or_ignore']);
  451. if (_taxonomy_csv_line_result($result, $current_result, $terms_count)) {
  452. return $result;
  453. }
  454. // Needed to get a term with tid.
  455. $translated_term = taxonomy_term_load($current_result['tid']);
  456. // 5. Update or create the translation set.
  457. // Remove is needed to avoid errors when a translated term is updated.
  458. $translation_set->remove_language($translated_term->language);
  459. $translation_set->add_item($translated_term, $translated_term->language);
  460. }
  461. }
  462. $translation_set->save(TRUE);
  463. return $result;
  464. }
  465. /**
  466. * Helper to merge results with current result.
  467. *
  468. * @return
  469. * TRUE if the result contains an error. Result is passed by reference.
  470. */
  471. function _taxonomy_csv_line_result(&$result, $current_result, $terms_count) {
  472. $result['name'][] = isset($current_result['name']) ? $current_result['name'] : '';
  473. $result['tid'][] = isset($current_result['tid']) ? $current_result['tid'] : 0;
  474. $result['msg'] = isset($result['msg']) ?
  475. array_merge($result['msg'], $current_result['msg']) :
  476. $current_result['msg'];
  477. $result['terms_count'] = $terms_count;
  478. return (_taxonomy_csv_worst_message($current_result['msg']) < TAXONOMY_CSV_PROCESS_NOTICE);
  479. }
  480. /**
  481. * Update/create a term with the given name and parent in the given vocabulary.
  482. *
  483. * @param $term
  484. * A term object to import. Term contains either:
  485. * - 'name' => term name string,
  486. * - 'tid' => term id,
  487. * and eventually, matching options:
  488. * - 'vid' => the vocabulary id where to import,
  489. * - 'description' => description string,
  490. * - 'format' => the filter format of the description,
  491. * - 'language' => the language id of the term,
  492. * - 'weight' => weight integer,
  493. * - 'parent' => array of first level parent tids,
  494. * @param $update_or_ignore
  495. * (Optional) Type of import on existing terms. Default to ignore and create.
  496. * @param $parent_tid
  497. * (Optional) The direct parent term id where to restrict search.
  498. * Used for structure import. Default to NULL (no parent restriction).
  499. *
  500. * @return array
  501. * 'name' => term name,
  502. * 'tid' => term id,
  503. * 'msg' => messages array.
  504. *
  505. * @todo Include true update/replace/ignore for fields.
  506. */
  507. function taxonomy_csv_term_import($term, $update_or_ignore = TAXONOMY_CSV_EXISTING_IGNORE, $parent_tid = NULL) {
  508. $messages = array();
  509. // Basic check to avoid notices when "Check lines" option is disabled.
  510. if ((!isset($term->name) && !isset($term->tid))
  511. || (isset($term->name) && !$term->name)
  512. || (isset($term->tid) && !$term->tid)
  513. ) {
  514. $term->tid = 0;
  515. $messages[] = 432; // Warning line contains an empty term.
  516. return array(
  517. 'name' => '',
  518. 'tid' => 0,
  519. 'msg' => $messages,
  520. );
  521. }
  522. switch ($update_or_ignore) {
  523. case TAXONOMY_CSV_EXISTING_UPDATE:
  524. $existing_term = taxonomy_csv_term_find($term, FALSE, $parent_tid);
  525. if ($existing_term) {
  526. // Update only fields that are set. Other fields are not changed.
  527. foreach ($term as $key => $value) {
  528. // Arrays: merge existing and new items.
  529. // Only used for parent in standard taxonomy of Drupal 7.
  530. if (is_array($value) && isset($existing_term->{$key})) {
  531. $term->{$key} = array_unique(array_merge($existing_term->{$key}, $value));
  532. }
  533. // Else simply use new key: no merge is possible and useful for
  534. // string, numeric or boolean fields.
  535. // Description was an exception in previous version, but this
  536. // exception is removed for simplicity and real use of this module.
  537. // An option may be added if needed.
  538. }
  539. // Existing fields of existing term should be kept even if they have
  540. // not been set in new term.
  541. $term = (object) array_merge((array) $existing_term, (array) $term);
  542. }
  543. break;
  544. case TAXONOMY_CSV_EXISTING_IGNORE_PREVIOUS:
  545. // Doesn't ignore, but use previous parents.
  546. $existing_term = taxonomy_csv_term_find($term, FALSE, $parent_tid);
  547. if ($existing_term) {
  548. // All fields are replaced by new ones. Other existing fields of
  549. // existing term should be kept even if they are not set in new term.
  550. $term = (object) array_merge((array) $existing_term, (array) $term);
  551. }
  552. break;
  553. case TAXONOMY_CSV_EXISTING_IGNORE:
  554. // Nothing to do: keep the term.
  555. break;
  556. }
  557. // Finish to set the term to avoid NULL. Format and language are set before.
  558. if (!isset($term->format)) {
  559. $term->format = 'plain_text';
  560. }
  561. if (!isset($term->description)) {
  562. $term->description = '';
  563. }
  564. // Currently, custom fields are managed by an external function.
  565. if (isset($term->fields_to_import)) {
  566. $messages = _taxonomy_csv_term_field_import($term, $update_or_ignore);
  567. unset($term->fields_to_import);
  568. unset($term->fields_to_import_instances);
  569. unset($term->fields_to_import_fields);
  570. if (_taxonomy_csv_worst_message($messages) < TAXONOMY_CSV_PROCESS_NOTICE) {
  571. return array(
  572. 'name' => '',
  573. 'tid' => 0,
  574. 'msg' => $messages,
  575. );
  576. }
  577. }
  578. // Save regularly formatted term.
  579. // Return either SAVED_NEW, SAVED_UPDATED or FALSE (no change).
  580. $result = taxonomy_term_save($term);
  581. $messages[] = ($result == SAVED_NEW) ? 691 : 692; // Saved or updated.
  582. return array(
  583. 'name' => $term->name,
  584. 'tid' => $term->tid,
  585. 'msg' => $messages,
  586. );
  587. }
  588. /**
  589. * Helper to import attached fields.
  590. *
  591. * @see taxonomy_csv_term_import()
  592. *
  593. * @todo Integrate with taxonomy_csv_line_import and taxonomy_csv_term_import.
  594. * @todo True update/replace/ignore.
  595. */
  596. function _taxonomy_csv_term_field_import($term, $update_or_ignore = TAXONOMY_CSV_EXISTING_IGNORE) {
  597. $messages = array();
  598. // Currently, translatable fields are unmanaged.
  599. $language = 'und'; // Undefined.
  600. foreach ($term->fields_to_import as $field_name => $values) {
  601. $instance = &$term->fields_to_import_instances[$field_name];
  602. $field = &$term->fields_to_import_fields[$field_name];
  603. foreach ($values as $value) {
  604. switch ($field['type']) {
  605. case 'taxonomy_term_reference':
  606. // Get machine name of referenced vocabulary.
  607. $referenced_vocabulary = $field['settings']['allowed_values'][0]['vocabulary'];
  608. $referenced_vocabulary = taxonomy_vocabulary_machine_name_load($referenced_vocabulary);
  609. $referenced_term = new stdClass;
  610. // Presume that item is a tid if it's a number.
  611. if (is_numeric($value)) {
  612. if (!$value) {
  613. $messages['notice'] = t('Unable to make a reference to term "0".');
  614. continue 2;
  615. }
  616. // @todo Check if the term is in allowed vocabularies.
  617. $referenced_term->tid = $value;
  618. }
  619. // Else find the parent normally.
  620. else {
  621. $referenced_term->name = $value;
  622. $referenced_term->vid = $referenced_vocabulary->vid;
  623. $referenced_term->vocabulary_machine_name = $referenced_vocabulary->machine_name;
  624. // @todo Use undefined language or term language?
  625. $referenced_term->language = $term->language;
  626. // @todo Increase term count.
  627. $current_result = taxonomy_csv_term_import($referenced_term, $update_or_ignore);
  628. if (!$current_result['tid']) {
  629. $messages[] = 402; // Unable to import a field term.
  630. return $messages;
  631. }
  632. $referenced_term->tid = $current_result['tid'];
  633. }
  634. $value = array(
  635. 'tid' => $referenced_term->tid,
  636. );
  637. switch ($instance['widget']['type']) {
  638. case 'taxonomy_autocomplete':
  639. // Need tid, vid, name and vocabulary_machine_name (use cache).
  640. $referenced_term = taxonomy_term_load($referenced_term->tid);
  641. $value['name'] = $referenced_term->name;
  642. $value['vid'] = $referenced_term->vid;
  643. $value['vocabulary_machine_name'] = $referenced_term->machine_name;
  644. break;
  645. }
  646. // Complete term if there are already items.
  647. if (is_array($term->{$field_name}) && !empty($term->{$field_name})) {
  648. foreach ($term->{$field_name} as $field_language => &$delta_array) {
  649. $delta_array[] = $value;
  650. }
  651. }
  652. // Else complete term directly.
  653. else {
  654. $term->{$field_name}[$language][0] = $value;
  655. }
  656. break;
  657. case 'file':
  658. $uri_scheme = $field['settings']['uri_scheme'];
  659. $file_directory = $instance['settings']['file_directory'];
  660. $file_extensions = $instance['settings']['file_extensions'];
  661. $source = $value;
  662. $destination = $uri_scheme . '://' . $file_directory;
  663. // Import source if it exists.
  664. if (file_exists($source)) {
  665. $filepath = file_unmanaged_copy($source, $destination, FILE_EXISTS_RENAME);
  666. if ($filepath !== FALSE) {
  667. $file = new stdClass();
  668. $file->uid = 1;
  669. $file->filename = basename($filepath);
  670. $file->uri = $filepath;
  671. $file->filemime = file_get_mimetype($filepath);
  672. $file->filesize = filesize($filepath);
  673. $file->status = 1;
  674. $file->timestamp = time();
  675. $file = file_save($file);
  676. // Complete term if there are already items.
  677. if (is_array($term->{$field_name}) && !empty($term->{$field_name})) {
  678. foreach ($term->{$field_name} as $field_language => &$delta_array) {
  679. $delta_array[] = array(
  680. 'fid' => $file->fid,
  681. 'display' => 1,
  682. 'description' => '',
  683. );
  684. }
  685. }
  686. // Else complete term directly.
  687. else {
  688. $term->{$field_name}[$language][0] = array(
  689. 'fid' => $file->fid,
  690. 'display' => 1,
  691. 'description' => '',
  692. );
  693. }
  694. }
  695. }
  696. elseif ($source != '') {
  697. $messages['notice'] = t('Unable to load attached file.');
  698. }
  699. // Else nothing to import.
  700. break;
  701. case 'list_boolean':
  702. $value = (($value == '0') || (drupal_strtolower($value) == 'false')) ? 0 : 1;
  703. $term->{$field_name}[$language][0] = array('value' => $value);
  704. break;
  705. // Managed by default:
  706. // case 'number_decimal':
  707. // case 'number_integer':
  708. // case 'number_float':
  709. // case 'text':
  710. // case 'text_long':
  711. // case 'text_with_summary': // ?
  712. // Currently doesn't manage default value.
  713. default:
  714. switch ($field['type']) {
  715. case 'number_decimal':
  716. $value = (float) $value;
  717. break;
  718. case 'number_integer':
  719. $value = (integer) $value;
  720. break;
  721. case 'number_float':
  722. $value = (float) $value;
  723. break;
  724. }
  725. // Complete term if there are already items.
  726. if (is_array($term->{$field_name}) && !empty($term->{$field_name})) {
  727. foreach ($term->{$field_name} as $field_language => &$delta_array) {
  728. $delta_array[] = array('value' => $value);
  729. }
  730. }
  731. // Else complete term directly.
  732. else {
  733. $term->{$field_name}[$language][0] = array('value' => $value);
  734. }
  735. break;
  736. }
  737. }
  738. }
  739. return $messages;
  740. }
  741. /**
  742. * Helper to build a standard text field from a simple string or an array.
  743. *
  744. * @todo To be removed (as other helpers) by using of Field api (but slower).
  745. * @todo Fields internationalization. Should be compatible with the i18n mode of
  746. * the vocabulary.
  747. *
  748. * @param $value
  749. * A string or an array to convert.
  750. * @param $language
  751. * (Optional). Language to use. Default is 'und' (undefined).
  752. * @param $format
  753. * (Optional). Format of the field. Default to NULL (fixed plain text).
  754. *
  755. * @return
  756. * Formatted field array.
  757. */
  758. function _taxonomy_csv_field_create_text($value, $language = 'und', $format = NULL) {
  759. // Currently, i18n of term custom fields is not supported.
  760. $language = 'und';
  761. $field = array();
  762. if (!is_array($value) && !empty($value)) {
  763. $value = array($value);
  764. }
  765. if ($format == 'none') {
  766. $format = NULL;
  767. }
  768. foreach ($value as $item) {
  769. if ($item !== '') {
  770. $field[] = array(
  771. 'value' => $item,
  772. 'format' => $format,
  773. 'safe_value' => ($format) ?
  774. check_markup($item, $format, $language) :
  775. check_plain($item),
  776. );
  777. }
  778. }
  779. return ($field) ?
  780. array($language => $field) :
  781. array();
  782. }
  783. /**
  784. * Helper to build a standard term reference field from a string or an array.
  785. *
  786. * @param $value
  787. * A string or an array to convert.
  788. * @param $language
  789. * (Optional). Language to use. Default is 'und' (undefined).
  790. *
  791. * @return
  792. * Formatted field array.
  793. */
  794. function _taxonomy_csv_field_create_taxonomy_term_reference($value, $language = 'und') {
  795. // Currently, i18n of term custom fields is not supported.
  796. $language = 'und';
  797. $field = array();
  798. if (!is_array($value) && !empty($value)) {
  799. $value = array($value);
  800. }
  801. foreach ($value as $item) {
  802. if ($item) {
  803. $field[]['tid'] = $item;
  804. }
  805. }
  806. return ($field) ?
  807. array($language => $field) :
  808. array();
  809. }
  810. /**
  811. * Helper to convert an internal array field to a standard text field.
  812. *
  813. * @param $field_name
  814. * Field to update.
  815. * @param $term
  816. * Term object.
  817. * @param $existing_term
  818. * Previous term object.
  819. *
  820. * @return
  821. * Nothing: $term object is passed by reference.
  822. */
  823. function _taxonomy_csv_field_update_text($field_name, $term, $existing_term) {
  824. // Complete term if there are already items.
  825. if (is_array($existing_term->{$field_name}) && !empty($existing_term->$field_name)) {
  826. $new_field = $term->{$field_name};
  827. $term->{$field_name} = $existing_term->$field_name;
  828. $field = &$term->{$field_name};
  829. foreach ($new_field as $field_language => $array) {
  830. // Don't update if new field is empty.
  831. if (!empty($array)) {
  832. // Update the value for that language.
  833. if (isset($field[$field_language])) {
  834. // Don't use array_merge or array_merge_recursive to avoid duplicates.
  835. $existing = array();
  836. foreach ($field[$field_language] as &$value) {
  837. $existing[] = &$value['value'];
  838. }
  839. foreach ($array as &$new_value) {
  840. // Avoid duplicates and avoid to append an empty item.
  841. if (!in_array($new_value['value'], $existing) && ($new_value['value'] !== '')) {
  842. $field[$field_language][] = $new_value;
  843. }
  844. }
  845. }
  846. // Value for this language is not set, so complete term directly.
  847. else {
  848. foreach ($array as &$new_value) {
  849. // Avoid to append an empty item.
  850. if ($new_value['value'] !== '') {
  851. $field[$field_language][] = $new_value;
  852. }
  853. }
  854. }
  855. }
  856. }
  857. }
  858. // Else nothing to do: term contains already new field.
  859. }
  860. /**
  861. * Helper to convert an internal array field to a standard term reference field.
  862. *
  863. * @param $field_name
  864. * Field to update.
  865. * @param $term
  866. * Term object.
  867. * @param $existing_term
  868. * Previous term object.
  869. *
  870. * @return
  871. * Nothing: $term object is passed by reference.
  872. */
  873. function _taxonomy_csv_field_update_taxonomy_term_reference($field_name, $term, $existing_term) {
  874. // Complete term if there are already items.
  875. if (is_array($existing_term->{$field_name}) && !empty($existing_term->$field_name)) {
  876. $new_field = $term->{$field_name};
  877. $term->{$field_name} = $existing_term->$field_name;
  878. $field = &$term->{$field_name};
  879. foreach ($new_field as $field_language => $array) {
  880. // Don't update if new field is empty.
  881. if (!empty($array)) {
  882. // Update the value for that language.
  883. if (isset($field[$field_language])) {
  884. // Don't use array_merge or array_merge_recursive to avoid duplicates.
  885. $existing = array();
  886. foreach ($field[$field_language] as &$value) {
  887. $existing[] = &$value['tid'];
  888. }
  889. foreach ($array as &$new_value) {
  890. // Avoid duplicates and avoid to append an empty item.
  891. if (!in_array($new_value['tid'], $existing) && ($new_value['tid'] != 0)) {
  892. $field[$field_language][] = $new_value;
  893. }
  894. }
  895. }
  896. // Value for this language is not set, so complete term directly.
  897. else {
  898. foreach ($array as &$new_value) {
  899. // Avoid to append an empty item.
  900. if ($new_value['tid'] != 0) {
  901. $field[$field_language][] = $new_value;
  902. }
  903. }
  904. }
  905. }
  906. }
  907. }
  908. // Else nothing to do: term contains already new field.
  909. }
  910. /**
  911. * Helper to convert an internal repetitive field to a standard text field.
  912. *
  913. * @param $term
  914. * A taxonomy term object with custom fields.
  915. * @param $field_name
  916. * Field to update.
  917. * @param $language
  918. * (Optional) Language code.
  919. *
  920. * @return
  921. * Nothing: $term object is passed by reference.
  922. */
  923. function _taxonomy_csv_line_replace_field_text($term, $field_name, $language = 'und') {
  924. if (isset($term->{$field_name}) && is_array($term->$field_name)) {
  925. $field = array();
  926. foreach ($term->{$field_name} as $value) {
  927. $field[] = array(
  928. 'value' => $value,
  929. 'format' => NULL,
  930. 'safe_value' => check_plain($value),
  931. );
  932. }
  933. $term->{$field_name}[$language] = $field;
  934. }
  935. }