taxonomy_csv.import.admin.inc 57 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340
  1. <?php
  2. /**
  3. * @file
  4. * Create taxonomy csv import form and validate user input.
  5. */
  6. /**
  7. * Invoke associated files.
  8. */
  9. $module_dir = drupal_get_path('module', 'taxonomy_csv');
  10. require_once($module_dir . '/taxonomy_csv.api.inc');
  11. /**
  12. * Generates the taxonomy CSV import form.
  13. *
  14. * Form contain six fieldsets:
  15. * - 1. What to import ?
  16. * - 1. Content of the source
  17. * - 2. Specific options of structure/fields/translation
  18. * - 3. Keep order of terms by adding a weight
  19. * - 2. Where are items to import ?
  20. * - 1. Source select
  21. * - 2. Source file or text area
  22. * - 3. How is your source formatted ?
  23. * - 1. Source delimiter
  24. * - 2. Source enclosure
  25. * - 3. Description filter format
  26. * - 4. Custom fields filter format
  27. * - 5. Language of imported terms
  28. * - 6. Line and utf8 checks
  29. * - 4. Which vocabulary to import into (destination) ?
  30. * - 1. Destination type
  31. * - 2. Vocabulary choice
  32. * - 3. Vocabulary internationalization (mode and language)
  33. * - 4. Autocreate custom fields
  34. * - 5. Deletion of terms
  35. * - 6. Vocabulary hierarchy set or check
  36. * - 5. When a term exist, what to do with it ?
  37. * - 1. Previous or existing terms
  38. * - 2. Specific import options depending on source content
  39. * - 6. Advanced options
  40. * - 1. Basic stats
  41. * - 2. List of terms
  42. * - 3. How to be notified
  43. *
  44. * @ingroup forms
  45. * @see taxonomy_csv_import_form_validate()
  46. * @see taxonomy_csv_import_form_submit()
  47. * @see taxonomy_csv_import_form_default_values_validate()
  48. * @see taxonomy_csv_import_form_default_values_submit()
  49. */
  50. function taxonomy_csv_import_form($form, &$form_state) {
  51. // User is used to get filter formats the current user has access to.
  52. global $user;
  53. $list_recommended_values = _taxonomy_csv_values('import_default_ui');
  54. $list_previous_values = array();
  55. foreach ($list_recommended_values as $key => $value) {
  56. $list_previous_values[$key] = isset($form_state['values'][$key]) ?
  57. $form_state['values'][$key] :
  58. variable_get('taxonomy_csv_' . $key, $value);
  59. }
  60. $list_import_format = _taxonomy_csv_values('import_format');
  61. $list_import_delimiter = array(
  62. 'comma' => t('« , » (Comma)'),
  63. 'semicolon' => t('« ; » (Semicolon)'),
  64. 'tabulation' => t('« » (Tabulation)'),
  65. 'pipe' => t('« | » (Pipe)'),
  66. 'space' => t('« » (Space)'),
  67. 'currency_sign' => t('« ¤ » (Currency sign)'),
  68. 'soft_tab' => t('« » (Soft tab: two spaces or more)'),
  69. 'custom_delimiter' => t('Custom delimiter'),
  70. );
  71. $list_import_enclosure = array(
  72. 'none' => t('None'),
  73. 'quotation' => t('« " » (Quotation mark)'),
  74. 'quote' => t("« ' » (Quote)"),
  75. 'custom_enclosure' => t('Custom enclosure'),
  76. );
  77. $list_filter_format_custom = array(
  78. 'none' => t("None (Fixed plain text: user can't choose)"),
  79. );
  80. foreach (filter_formats($user) as $format) {
  81. $list_filter_format_custom[$format->format] = $format->name;
  82. }
  83. $list_filter_format = $list_filter_format_custom;
  84. unset($list_filter_format['none']);
  85. $list_import_language = array(
  86. 'und' => t('Language neutral'),
  87. );
  88. if (module_exists('locale')) {
  89. $list_import_language += locale_language_list('name');
  90. foreach (locale_language_list('native') as $key => $value) {
  91. $list_import_language[$key] .= ' [' . $value . ']';
  92. }
  93. }
  94. $list_vocabularies = taxonomy_vocabulary_get_names();
  95. $list_vocabulary_target = array(
  96. 'autocreate' => t('Autocreate a new vocabulary'),
  97. 'existing' => t('Import in an existing vocabulary'),
  98. );
  99. // Build form.
  100. $form['tab'] = array(
  101. '#type' => 'vertical_tabs',
  102. '#default_tab' => 'format',
  103. );
  104. $form['tab']['format'] = array(
  105. '#type' => 'fieldset',
  106. '#title' => t('1. What do you want to import?'),
  107. '#collapsible' => TRUE,
  108. '#collapsed' => FALSE,
  109. '#description' => t('Choose the type of import. Help for each type is displayed below when format is selected.') . '<br />'
  110. . '<strong>' . t('Important') . '</strong>: ' . t('If you have a tree structure, you should import it before fields or translations, and for each translation if you use a full Translate mode.'),
  111. );
  112. $form['tab']['format']['import_format'] = array(
  113. '#type' => 'radios',
  114. '#title' => '',
  115. '#options' => $list_import_format,
  116. '#default_value' => $list_previous_values['import_format'],
  117. );
  118. // Generic form for import format help.
  119. foreach ($list_import_format as $key => $value) {
  120. $form['tab']['format'][$key] = array(
  121. '#type' => 'item',
  122. '#states' => array(
  123. 'visible' => array(
  124. ':input[name=import_format]' => array('value' => $key),
  125. ),
  126. ),
  127. );
  128. }
  129. // Prepare descriptions.
  130. $line_format = '<br /><br />'
  131. . '<strong><em>' . t('Line format') . '</em></strong><br />'
  132. . '<code>';
  133. $example = '</code><br /><br />'
  134. . '<strong><em>' . t('Examples') . '</em></strong>'
  135. . '<ul><li><code>';
  136. $li = '</code></li><li><code>';
  137. $description_long = '</code></li></ul>';
  138. // Complete generic form with specific help for each import format.
  139. $form['tab']['format'][TAXONOMY_CSV_FORMAT_STRUCTURE]['#description'] = t('Allow to create a flat vocabulary, a tree structure (geography, classification...) or a polyhierarchy (genealogy, complex nomenclatures...).')
  140. . $line_format
  141. . t('term name, child term name, sub-child term name...')
  142. // Example Flat.
  143. . '</code><br /><br />'
  144. . '<strong><em>' . t('Examples (flat)') . '</em></strong>'
  145. . '<ul><li><code>'
  146. . t('foo') . $li
  147. . t('bar, small bar')
  148. . '</code></li></ul>'
  149. // Example Tree.
  150. . '</code><br />'
  151. . '<strong><em>' . t('Examples (simple tree)') . '</em></strong>'
  152. . '<ul><li><code>'
  153. . t('foo, bar, small bar') . $li
  154. . t('Europe, France, Paris') . $li
  155. . t('Europe, France, Lyon') . $li
  156. . t('Europe, UK, London') . $li
  157. . t('America, USA, Washington') . $li
  158. . t(', , Hollywood')
  159. . '</code></li></ul>'
  160. // Example Polyhierarchy.
  161. . '</code><br />'
  162. . '<strong><em>' . t('Examples (Polyhierarchy)') . '</em></strong>'
  163. . '<ul><li><code>'
  164. . t('foo, bar, small bar') . $li
  165. . t('Grand-Mother, Mother, Daughter') . $li
  166. . t('Grand-Father, Mother, Son') . $li
  167. . t('Grand-Mother 2, Father, Daughter') . $li
  168. . t('Grand-Father 2, Father, Son') . $li
  169. . t(', , Son 2') . $li
  170. . t(', Uncle') . $li
  171. . t('Grand-Mother 2, Uncle') . $li
  172. . t('Father, Son 3')
  173. . $description_long
  174. . '<br />'
  175. . t('The first term is imported as a root level parent, the second as child of first term, the third as child of second term and so on. The lower child is the last term of a line. Others are hierarchical parents.') . '<br />'
  176. . t('To repeat previously imported items on each line is not needed: import process can manage one term by line.') . ' ' . t('If terms are repeated each line and lines are ordered, result and speed are same as with one term by line format.');
  177. $form['tab']['format'][TAXONOMY_CSV_FORMAT_FIELDS]['#description'] = t('Allow to choose a custom order of fields on a csv line.')
  178. . $line_format
  179. . t('term name/id, field_1, field_2...')
  180. . $example
  181. . t('foo, foo field 1, foo field 2') . $li
  182. . t('bar, bar field 1, bar field 2')
  183. . $description_long;
  184. // See below.
  185. $form['tab']['format'][TAXONOMY_CSV_FORMAT_TRANSLATE]['#description'] = t('Allow to import name and descriptions and their translation.')
  186. . $line_format
  187. . '<em>' . t('Vocabulary with Translate mode:') . '</em> ' . t('term name/id, first translated term name, second translated term name...') . '<br />'
  188. . '<em>' . t('Vocabulary with Localize mode:') . '</em> ' . t('term name/id, first translation of term name... , description, first translation of description...')
  189. . $example
  190. . t('foo, bar') . $li
  191. . t('"United Kingdom", "Royaume-Uni", "Vereinigte Königreich"') . $li
  192. . t('"Germany", "Allemagne", "A European country", "Un pays européen"') . '<em> ' . t('[vocabulary with Localize mode only]') . '</em> '
  193. . $description_long
  194. . t('The term is in the first column followed by its translations. If the i18n mode is Localize, then description and translated descriptions can be added.') . '<br />'
  195. . '<strong>' . t('Note') . '</strong>:' . '<br />'
  196. . '<ul>'
  197. . '<li>' . t('With a vocabulary in Translate mode, a term with an undefined language cannot be translated, so do not forget to choose a language when you import original terms.') . '</li>'
  198. . '<li>' . t('With a vocabulary in Localize mode, only terms with a undefined language can be translated, so do not set a language when you import original terms.') . '</li>'
  199. . '</ul>';
  200. if (!module_exists('i18n_taxonomy')) {
  201. $form['tab']['format']['import_format']['#options'][TAXONOMY_CSV_FORMAT_TRANSLATE] .= ' [' . t('Unavailable') . ']';
  202. $form['tab']['format'][TAXONOMY_CSV_FORMAT_TRANSLATE]['#description'] .= '<br />'
  203. . t('This format is available only if the submodule <a href="!link">Taxonomy internationalization</a> is enabled.', array(
  204. '!link' => url('http://drupal.org/project/i18n'),
  205. ));
  206. }
  207. // Specific options.
  208. // Specific options of Structure.
  209. $form['tab']['format']['specific'][TAXONOMY_CSV_FORMAT_STRUCTURE]['structure_type'] = array(
  210. '#type' => 'radios',
  211. '#title' => t('Type of structure'),
  212. '#options' => array(
  213. TAXONOMY_CSV_FORMAT_FLAT => t('Flat vocabulary'),
  214. TAXONOMY_CSV_FORMAT_TREE => t('Simple tree'),
  215. TAXONOMY_CSV_FORMAT_POLYHIERARCHY => t('Polyhierarchy'),
  216. ),
  217. '#default_value' => $list_previous_values['structure_type'],
  218. '#description' => t('Choose if terms have no parent, one parent only, or multiple parents.'),
  219. '#states' => array(
  220. 'visible' => array(
  221. ':input[name=import_format]' => array('value' => TAXONOMY_CSV_FORMAT_STRUCTURE),
  222. ),
  223. ),
  224. );
  225. // Specific options of Fields.
  226. $form['tab']['format']['specific'][TAXONOMY_CSV_FORMAT_FIELDS]['import_fields_format'] = array(
  227. '#type' => 'textfield',
  228. '#title' => t('Set order of items on a csv line'),
  229. '#default_value' => $list_previous_values['import_fields_format'],
  230. '#size' => 60,
  231. '#maxlength' => 1024,
  232. '#description' => t('Set custom order of csv input separated by comma.') . '<br />'
  233. . '<strong>' . t('Notes:') . '</strong>'
  234. . '<ul>'
  235. . '<li>' . t("First field need to be 'name' or 'tid'.") . '</li>'
  236. . '<li>' . t('Items can be repeated for multivalued fields.') . '</li>'
  237. . '<li>' . t("If you choose to set 'vid' or 'vocabulary_machine_name', the vocabulary should exist before import.") . '</li>'
  238. . '<li>' . t("It's not recommended to import translations at the same time than parents and term references, unless all of them are in the same vocabulary and have the same language.") . '</li>'
  239. . '<li>' . t('Duplicate terms are not fully managed for parents (it is recommended to use "Structure" import) and term references.') . '</li>'
  240. . '</ul>'
  241. . '<strong>' . t('Examples:') . '</strong>'
  242. . '<ul>'
  243. . '<li><code>' . t('name, field_mycustomfield_1_machinename, field_mycustomfield_2_machinename') . '</code></li>'
  244. . '<li><code>' . t('name, description, field_country, field_year_started') . '</code></li>'
  245. . '<li><code>' . t('name, description, weight, parent, synonym, synonym, synonym, related_term, related_term, related_term, related_term') . '</code></li>'
  246. . '</ul>',
  247. '#states' => array(
  248. 'visible' => array(
  249. ':input[name=import_format]' => array('value' => TAXONOMY_CSV_FORMAT_FIELDS),
  250. ),
  251. ),
  252. );
  253. // Specific options of Translate.
  254. $form['tab']['format']['specific'][TAXONOMY_CSV_FORMAT_TRANSLATE]['translate_by'] = array(
  255. '#type' => 'radios',
  256. '#title' => t('Type of source'),
  257. '#options' => array(
  258. 'name' => t('First item is the source term name'),
  259. 'tid' => t('First item is the source term id'),
  260. ),
  261. '#default_value' => $list_previous_values['translate_by'],
  262. '#description' => t('Choose how to identify the source term.') . ' ' . t("If the source term doesn't exist, it will be created.") . '<ul>'
  263. . '<li>' . t('To use name is simpler, but cannot be used if vocabulary has duplicate names.') . '</li>'
  264. . '<li>' . t('To use term id is quicker, but you need to export your vocabulary to get tids.') . '</li>'
  265. . '</ul>',
  266. '#states' => array(
  267. 'visible' => array(
  268. ':input[name=import_format]' => array('value' => TAXONOMY_CSV_FORMAT_TRANSLATE),
  269. ),
  270. ),
  271. );
  272. $form['tab']['format']['specific'][TAXONOMY_CSV_FORMAT_TRANSLATE]['translate_languages'] = array(
  273. '#type' => 'textfield',
  274. '#title' => t('List of languages'),
  275. '#default_value' => $list_previous_values['translate_languages'],
  276. '#size' => 60,
  277. '#maxlength' => 1024,
  278. '#description' => t('Set the list of languages of terms, for example "<code>en, fr, de</code>".') . '<br />'
  279. . '<strong>' . ('Notes:') . '</strong>'
  280. . '<ul>'
  281. . '<li>' . t('All languages should have been enabled in <a href="!link">Regional and language settings</a> before import.', array('!link' => url('admin/config/regional/language/overview'))) . '</li>'
  282. . '<li>' . '<strong>' . t('Important') . '</strong>: ' . t('If your taxonomy is structured, you should import the structure <em>before</em>. Furthermore, if the i18n mode is Translate, you should import the structure for each translation before. The translate process only creates translation sets for terms, but does not recreate any structure.') . '</li>'
  283. . '<li>' . t('With Translate mode, terms with an undefined language (<code>"und"</code>) cannot be translated.') . '</li>'
  284. . '<li>' . t('With Localize mode, only terms with an undefined language can be translated, so the first language should be "und" and the default language of the site cannot be used.') . '</li>'
  285. . '</ul>',
  286. '#states' => array(
  287. 'visible' => array(
  288. ':input[name=import_format]' => array('value' => TAXONOMY_CSV_FORMAT_TRANSLATE),
  289. ),
  290. ),
  291. );
  292. $form['tab']['format']['keep_order'] = array(
  293. '#type' => 'checkbox',
  294. '#title' => t('Keep order of terms'),
  295. '#default_value' => $list_previous_values['keep_order'],
  296. '#description' => t('If checked, a weight will be added to each term, so order of lines will be preserved. If unchecked, order will be alphabetic.') . '<br />' .
  297. t('Note: With polyhierarchical vocabulary (multiple parents for a term) and duplicate terms, unpredictable results may occur, because weight of a term is incremented each time it appears and a term can only have one weight, without context.') . '<br />' .
  298. t("You shouldn't use this option if your custom format has a 'weight' field."),
  299. );
  300. $form['tab']['import'] = array(
  301. '#type' => 'fieldset',
  302. '#title' => t('2. Where are items to import?'),
  303. '#collapsible' => TRUE,
  304. '#collapsed' => FALSE,
  305. );
  306. $form['tab']['import']['source_choice'] = array(
  307. '#type' => 'radios',
  308. '#title' => '',
  309. '#options' => array(
  310. 'text' => t('In the below text area'),
  311. 'file' => t('In a local file'),
  312. 'url' => t('In a distant file'),
  313. ),
  314. '#default_value' => $list_previous_values['source_choice'],
  315. );
  316. $form['tab']['import']['text'] = array(
  317. '#type' => 'textarea',
  318. '#title' => t('Terms to import'),
  319. '#rows' => 5,
  320. '#cols' => 80,
  321. '#default_value' => isset($form_state['values']['text']) ?
  322. $form_state['values']['text'] :
  323. '',
  324. '#description' => t('Write your csv formatted terms directly in this text area.'),
  325. '#states' => array(
  326. 'visible' => array(
  327. ':input[name=source_choice]' => array('value' => 'text'),
  328. ),
  329. ),
  330. );
  331. $form['tab']['import']['file'] = array(
  332. '#type' => 'file',
  333. '#title' => t('CSV file'),
  334. '#description' => t('Browse to the file') . '<br >'
  335. . (($max_size = parse_size(ini_get('upload_max_filesize'))) ?
  336. t('Due to server restrictions, the <strong>maximum upload file size is !max_size</strong>. Files that exceed this size will be disregarded.', array('!max_size' => format_size($max_size))) . '<br >'
  337. . t('Use "distant file" import to go beyond, even with local file.') :
  338. ''),
  339. '#states' => array(
  340. 'visible' => array(
  341. ':input[name=source_choice]' => array('value' => 'file'),
  342. ),
  343. ),
  344. );
  345. $form['tab']['import']['url'] = array(
  346. '#type' => 'textfield',
  347. '#title' => t('CSV file'),
  348. '#description' => t('Enter the url (http, ftp, file, path...)'),
  349. '#states' => array(
  350. 'visible' => array(
  351. ':input[name=source_choice]' => array('value' => 'url'),
  352. ),
  353. ),
  354. );
  355. $form['tab']['csv_format'] = array(
  356. '#type' => 'fieldset',
  357. '#title' => t('3. How is your source formatted?'),
  358. '#description' => t('Default delimiter is a comma ("<strong><code>,</code></strong>"). Default enclosure is none, but quotation mark ("<strong><code>"</code></strong>") is automatically managed.') . '<br />'
  359. . t('Notice') . ': ' . t('Either you import terms by a file or by a text area, the csv format is the same.') . '<br />'
  360. . '<strong>' . t('Warning') . '</strong>: ' . t('You should enclose any item beginning with a non-ascii letter, such as "É", "ç", "œ" or any non-latin letter.'),
  361. '#collapsible' => TRUE,
  362. '#collapsed' => FALSE,
  363. );
  364. $form['tab']['csv_format']['import_delimiter'] = array(
  365. '#type' => 'select',
  366. '#title' => t('CSV value delimiter'),
  367. '#options' => $list_import_delimiter,
  368. '#default_value' => $list_previous_values['import_delimiter'],
  369. '#description' => t("Choose the delimiter used in the CSV file you want to import. Tabulation can't be used with text area import."),
  370. );
  371. $form['tab']['csv_format']['import_delimiter_soft_tab_width'] = array(
  372. '#type' => 'select',
  373. '#title' => t('Soft tab width'),
  374. '#options' => array(
  375. 2 => 2,
  376. 3 => 3,
  377. 4 => 4,
  378. 5 => 5,
  379. 6 => 6,
  380. 7 => 7,
  381. 8 => 8,
  382. 9 => 9,
  383. ),
  384. '#default_value' => $list_previous_values['import_delimiter_soft_tab_width'],
  385. '#description' => t("Specify number of spaces of the soft tab delimiter. These spaces will be replaced with a true tab."),
  386. '#states' => array(
  387. 'visible' => array(
  388. ':input[name=import_delimiter]' => array('value' => 'soft_tab'),
  389. ),
  390. ),
  391. );
  392. $form['tab']['csv_format']['import_delimiter_custom'] = array(
  393. '#type' => 'textfield',
  394. '#title' => 'Custom delimiter',
  395. '#default_value' => $list_previous_values['import_delimiter_custom'],
  396. '#size' => 2,
  397. '#maxlength' => 1,
  398. '#description' => t('Specify your custom delimiter.'),
  399. '#states' => array(
  400. 'visible' => array(
  401. ':input[name=import_delimiter]' => array('value' => 'custom_delimiter'),
  402. ),
  403. ),
  404. );
  405. $form['tab']['csv_format']['import_enclosure'] = array(
  406. '#type' => 'select',
  407. '#title' => t('CSV value enclosure'),
  408. '#options' => $list_import_enclosure,
  409. '#default_value' => $list_previous_values['import_enclosure'],
  410. '#description' => t('Choose the enclosure used in the CSV file you want to import.'),
  411. );
  412. $form['tab']['csv_format']['import_enclosure_custom'] = array(
  413. '#type' => 'textfield',
  414. '#title' => 'Custom enclosure',
  415. '#default_value' => $list_previous_values['import_enclosure_custom'],
  416. '#size' => 2,
  417. '#maxlength' => 1,
  418. '#description' => t('Specify your custom enclosure.'),
  419. '#states' => array(
  420. 'visible' => array(
  421. ':input[name=import_enclosure]' => array('value' => 'custom_enclosure'),
  422. ),
  423. ),
  424. );
  425. $form['tab']['csv_format']['filter_format'] = array(
  426. '#type' => 'select',
  427. '#title' => t('Format of the description'),
  428. '#options' => $list_filter_format,
  429. '#default_value' => $list_previous_values['filter_format'],
  430. '#description' => t('Choose the default format used for the description. This parameter is set in your imported term even if there is no description.') . '<br />'
  431. . '<strong>' . t('Notes') . '</strong>:'
  432. . '<ul>'
  433. . '<li>' . t('You can select only formats you can use.') . '</li>'
  434. . '<li>' . t('Check is made by Drupal only when description is displayed.') . '</li>'
  435. . '<li>' . t('If your vocabulary is localizable and you want to translate description, you should use "plain text" format or change the translation mode of the vocabulary.') . '</li>'
  436. . '</ul>',
  437. );
  438. $form['tab']['csv_format']['filter_format_custom'] = array(
  439. '#type' => 'select',
  440. '#title' => t('Default format of custom fields'),
  441. '#options' => $list_filter_format_custom,
  442. '#default_value' => $list_previous_values['filter_format_custom'],
  443. '#description' => t('Choose the default format used for custom fields.') . '<br />'
  444. . '<strong>' . t('Notes') . '</strong>:'
  445. . '<ul>'
  446. . '<li>' . t('You can select only formats you can use. It can be changed later.') . '</li>'
  447. . '<li>' . t('Sanitization is made during import.') . '</li>'
  448. . '</ul>',
  449. );
  450. $form['tab']['csv_format']['import_language'] = array(
  451. '#type' => 'select',
  452. '#title' => t('Default language of terms'),
  453. '#options' => $list_import_language,
  454. '#default_value' => $list_previous_values['import_language'],
  455. '#description' => t('Choose the default language of terms.') . '<br />'
  456. . t('Note') . ': ' . t('For the format "Name and description translation", this option will be used for the translated item.') . '<br />'
  457. . '<strong>' . t('Warning') . '</strong>: ' . t('You need to check your regional and multilingual settings if you want to see terms in another language than the default one of the vocabulary.'),
  458. '#states' => array(
  459. 'invisible' => array(
  460. ':input[name=import_format]' => array('value' => TAXONOMY_CSV_FORMAT_TRANSLATE),
  461. ),
  462. ),
  463. );
  464. if (!module_exists('i18n_taxonomy')) {
  465. $form['tab']['csv_format']['import_language']['#default_value'] = $list_recommended_values['import_language'];
  466. $form['tab']['csv_format']['import_language']['#disabled'] = TRUE;
  467. $form['tab']['csv_format']['import_language']['#description'] = t('This option is available only if the submodule <a href="!link">Taxonomy internationalization</a> is enabled.', array(
  468. '!link' => url('http://drupal.org/project/i18n'),
  469. ));
  470. }
  471. $form['tab']['csv_format']['check_line'] = array(
  472. '#type' => 'checkbox',
  473. '#title' => t('Check lines'),
  474. '#default_value' => $list_previous_values['check_line'],
  475. '#description' => t('Format of each line of your input (order of items, format of names, duplicate terms...) can be checked if you are not sure of your vocabulary.'),
  476. );
  477. $form['tab']['csv_format']['check_utf8'] = array(
  478. '#type' => 'checkbox',
  479. '#title' => t('Check UTF-8 format'),
  480. '#description' => t('File needs to be UTF-8 formatted. You can disable this check if you are sure that your file is encoded correctly. Desactivation is needed with some server configurations too. This option is not used with a textarea import.') . ' '
  481. . t('ISO-8859-1 and ISO-8859-15 files are automatically converted.'),
  482. '#states' => array(
  483. 'invisible' => array(
  484. ':input[name=source_choice]' => array('value' => 'text'),
  485. ),
  486. ),
  487. );
  488. if (function_exists('mb_detect_encoding')) {
  489. $form['tab']['csv_format']['check_utf8']['#default_value'] = $list_previous_values['check_utf8'];
  490. }
  491. else {
  492. $form['tab']['csv_format']['check_utf8']['#default_value'] = FALSE;
  493. $form['tab']['csv_format']['check_utf8']['#disabled'] = TRUE;
  494. $form['tab']['csv_format']['check_utf8']['#description'] .= t('This checkbox is currently disabled, because iconv, GNU recode or mbstring for PHP are not installed on your server.');
  495. }
  496. $form['tab']['csv_format']['locale_custom'] = array(
  497. '#type' => 'textfield',
  498. '#title' => t('Manually set locale of the file'),
  499. '#default_value' => $list_previous_values['locale_custom'],
  500. '#size' => 60,
  501. '#maxlength' => 255,
  502. '#description' => t('To set locale can resolve some import issues related to file or database. Choose the locale you use, for example "<code>en_DK.utf8</code>", and it will be automatically set before import and reset after. Let empty when no problem occurs: import will use your default locale.'),
  503. );
  504. $form['tab']['destination'] = array(
  505. '#type' => 'fieldset',
  506. '#title' => t('4. Which vocabulary do you want to import into?'),
  507. '#collapsible' => TRUE,
  508. '#collapsed' => FALSE,
  509. '#description' => t('Terms can be imported into a new vocabulary or in an existing one. You can choose to duplicate an existing vocabulary too in order to check import. You might want to !add-new-vocab.', array(
  510. '!add-new-vocab' => l(t('add a new vocabulary'), 'admin/structure/taxonomy/add', array('query' => drupal_get_destination())),
  511. )) . '<br />'
  512. . t('If you import Fields and one of them is "vid" or "vocabulary_machine_name", this vocabulary need to exist before import, else error can occur.'),
  513. );
  514. if (count($list_vocabularies) == 0) {
  515. $form['tab']['destination']['#description'] .= '<br /><br />'
  516. . t("As there isn't any vocabulary, terms will be imported in a new automatically created vocabulary.");
  517. $form['tab']['destination']['vocabulary_target'] = array(
  518. '#type' => 'value',
  519. '#value' => 'autocreate',
  520. );
  521. $form['tab']['destination']['vocabulary_id'] = array(
  522. '#type' => 'value',
  523. '#value' => 0,
  524. );
  525. }
  526. else {
  527. $form['tab']['destination']['vocabulary_target'] = array(
  528. '#type' => 'radios',
  529. '#options' => $list_vocabulary_target,
  530. '#default_value' => $list_previous_values['vocabulary_target'],
  531. '#description' => t('Choose the vocabulary where you want to import your items.'),
  532. );
  533. $form['tab']['destination']['vocabulary_id'] = array(
  534. '#type' => 'select',
  535. '#title' => t('Vocabulary choice'),
  536. '#options' => array(
  537. 0 => t('[Choose an existing vocabulary]'),
  538. ),
  539. '#default_value' => $list_previous_values['vocabulary_id'],
  540. '#description' => t('The vocabulary you want to import the file into.'),
  541. '#states' => array(
  542. 'invisible' => array(
  543. ':input[name=vocabulary_target]' => array('value' => 'autocreate'),
  544. ),
  545. ),
  546. );
  547. foreach ($list_vocabularies as $vocabulary) {
  548. $form['tab']['destination']['vocabulary_id']['#options'][$vocabulary->vid] = $vocabulary->name;
  549. }
  550. }
  551. // Options language and i18n_mode.
  552. // Module i18n_taxonomy is enabled.
  553. if (module_exists('i18n_taxonomy')) {
  554. $form['tab']['destination'] += i18n_translation_mode_element('taxonomy_vocabulary');
  555. $form['tab']['destination']['i18n_translation']['#states'] = array(
  556. 'visible' => array(
  557. ':input[name=vocabulary_target]' => array('value' => 'autocreate'),
  558. ),
  559. );
  560. $form['tab']['destination']['i18n_translation']['i18n_mode']['#description'] .= '<br />'
  561. . t('Note') . ': ' . t('This option can be changed later.');
  562. // Use previous options.
  563. $form['tab']['destination']['i18n_translation']['i18n_mode']['#default_value'] = $list_previous_values['i18n_mode'];
  564. $form['tab']['destination']['i18n_translation']['language']['#default_value'] = ($list_previous_values['i18n_mode'] == 2) ? // I18N_MODE_LANGUAGE.
  565. $list_previous_values['language'] :
  566. $list_recommended_values['language'];
  567. }
  568. // No internationalization feature.
  569. else {
  570. $form['tab']['destination']['i18n_translation'] = array(
  571. '#type' => 'item',
  572. '#title' => t('Internationalization of vocabulary'),
  573. '#description' => t('Internationalization options are available only if the submodule <a href="!link">Taxonomy i18n</a> is enabled.',
  574. array('!link' => url('http://drupal.org/project/i18n'))),
  575. '#states' => array(
  576. 'visible' => array(
  577. ':input[name=vocabulary_target]' => array('value' => 'autocreate'),
  578. ),
  579. ),
  580. );
  581. $form['tab']['destination']['i18n_mode'] = array(
  582. '#type' => 'value',
  583. '#value' => $list_recommended_values['i18n_mode'],
  584. );
  585. $form['tab']['destination']['language'] = array(
  586. '#type' => 'value',
  587. '#value' => $list_recommended_values['language'],
  588. );
  589. }
  590. $form['tab']['destination']['import_fields_custom'] = array(
  591. '#type' => 'textfield',
  592. '#title' => t('Set custom fields of the vocabulary'),
  593. '#default_value' => $list_previous_values['import_fields_custom'],
  594. '#size' => 60,
  595. '#maxlength' => 1024,
  596. '#description' => t('Fields of custom format are automatically attached if they exist, else they are created before.') . ' ' . t('When a field is created, options are set to Drupal defaults for a text field with a unlimited number of values.') . ' ' . t("If you want another type, you have to create it before or set it here with the '|' symbol.") . '<br />'
  597. . t('Currently only these field types are managed by default:')
  598. . '<ul>'
  599. . '<li>' . 'text' . '</li>'
  600. . '<li>' . 'text_long' . '</li>'
  601. . '<li>' . 'text_with_summary' . '</li>'
  602. . '<li>' . 'number_decimal' . '</li>'
  603. . '<li>' . 'number_integer' . '</li>'
  604. . '<li>' . 'number_float' . '</li>'
  605. . '<li>' . 'list_boolean' . '</li>'
  606. . '<li>' . 'taxonomy_term_reference' . ' <em>' . t('[Note: if value is a number, the field will be linked to the term with that tid; if value is a string, the field will be linked to an existing or a new term with that name.]') . '</em>' . '</li>'
  607. . '<li>' . 'file' . '</li>'
  608. . '</ul>'
  609. . t('Other types are well imported too if their content is a "value".') . '<br />'
  610. . t('Be careful with "taxonomy_term_reference", because this field allows references to one vocabulary only, so referenced vocabulary is set to current one, which can be an issue if the same field is used somewhere else.') . '<br />'
  611. . t('Examples:') . '<ul>'
  612. . '<li><code>' . t('field_mycustomfield_1_machinename, field_mycustomfield_2_machinename|file') . '</code></li>'
  613. . '<li><code>' . t('field_content, field_reference|taxonomy_term_reference') . '</code></li>'
  614. . '<li><code>' . t('field_country|text, field_year_started|text') . '</code></li></ul>',
  615. '#states' => array(
  616. 'visible' => array(
  617. ':input[name=import_format]' => array('value' => TAXONOMY_CSV_FORMAT_FIELDS),
  618. ),
  619. ),
  620. );
  621. $form['tab']['destination']['delete_terms'] = array(
  622. '#type' => 'checkbox',
  623. '#title' => t('Automatically delete all terms of the selected vocabulary before import'),
  624. '#default_value' => $list_previous_values['delete_terms'],
  625. '#description' => t("Warning: You won't be warned before deletion."),
  626. '#states' => array(
  627. 'visible' => array(
  628. ':input[name=vocabulary_target]' => array('value' => 'existing'),
  629. ),
  630. ),
  631. );
  632. $form['tab']['destination']['info_hierarchy'] = array(
  633. '#type' => 'item',
  634. '#title' => t('What is the type of vocabulary?'),
  635. );
  636. $form['tab']['destination']['check_hierarchy'] = array(
  637. '#type' => 'checkbox',
  638. '#title' => t('Automatically check vocabulary hierarchy'),
  639. '#default_value' => $list_previous_values['check_hierarchy'],
  640. '#description' => t('Warning: to calculate true hierarchy of vocabulary is memory intensive. Choose to check automatically only if your vocabulary is little.'),
  641. );
  642. $form['tab']['destination']['set_hierarchy'] = array(
  643. '#type' => 'radios',
  644. // '#title is needed to avoid a #states bug.
  645. '#title' => '',
  646. '#options' => array(
  647. 0 => t('no parent (flat)'),
  648. 1 => t('single parent (tree)'),
  649. 2 => t('multiple parents (polyhierarchy)'),
  650. ),
  651. '#default_value' => $list_previous_values['set_hierarchy'],
  652. '#states' => array(
  653. 'invisible' => array(
  654. ':input[name=check_hierarchy]' => array('checked' => TRUE),
  655. ),
  656. ),
  657. );
  658. $form['tab']['import_options'] = array(
  659. '#type' => 'fieldset',
  660. '#title' => t('5. When a term exists, what to do with it?'),
  661. '#collapsible' => TRUE,
  662. '#collapsed' => FALSE,
  663. '#description' => t('What will existing term become when a term with same name and same language will be imported?'),
  664. );
  665. $form['tab']['import_options']['update_or_ignore'] = array(
  666. '#type' => 'radios',
  667. '#options' => array(
  668. TAXONOMY_CSV_EXISTING_UPDATE => t('Update it and merge fields (avoid duplicate terms, recommended)'),
  669. TAXONOMY_CSV_EXISTING_IGNORE => t('Ignore it and create a new term (duplicate terms may be created)'),
  670. ),
  671. '#default_value' => $list_previous_values['update_or_ignore'],
  672. '#description' => t('This option allows to set what previous imported terms will become if a new line contains the same terms.') . ' ' . t('Usually, it indicates an error or a unoptimized source, unless you allow duplicates.'),
  673. );
  674. $form['tab']['import_options']['update_or_ignore_info_polyhierarchy'] = array(
  675. '#type' => 'item',
  676. '#description' => '<strong>' . t('Note') . '</strong>: ' . t('You cannot choose to ignore existing items when you import a polyhierarchical structure.'),
  677. '#states' => array(
  678. 'visible' => array(
  679. ':input[name=import_format]' => array('value' => TAXONOMY_CSV_FORMAT_STRUCTURE),
  680. ':input[name=structure_type]' => array('value' => TAXONOMY_CSV_FORMAT_POLYHIERARCHY),
  681. ),
  682. ),
  683. );
  684. $form['tab']['advanced_options'] = array(
  685. '#type' => 'fieldset',
  686. '#title' => t('6. Informations on process and advanced options'),
  687. '#collapsible' => TRUE,
  688. '#collapsed' => FALSE,
  689. '#description' => t('All these options influe on memory and time process. The more information you want, the more power you need.') . ' '
  690. . t("It's recommended to reduce displayed infos when imported vocabulary is big (from 1000 or 10000 lines depending on the server)."),
  691. );
  692. $form['tab']['advanced_options']['result_choices'] = array(
  693. '#type' => 'checkboxes',
  694. '#options' => array(
  695. 'result_stats' => t('Basic stats on imported terms'),
  696. 'result_terms' => t('List of imported terms'),
  697. ),
  698. '#default_value' => array(
  699. $list_previous_values['result_stats'],
  700. $list_previous_values['result_terms'],
  701. ),
  702. );
  703. $form['tab']['advanced_options']['result_level'] = array(
  704. '#type' => 'select',
  705. '#title' => t('Log level'),
  706. '#options' => array(
  707. 'first' => t('Only first warning or notice'),
  708. 'warnings' => t('Warnings only'),
  709. 'notices' => t('Warnings and notices'),
  710. 'infos' => t('Warnings, notices and informations'),
  711. ),
  712. '#default_value' => $list_previous_values['result_level'],
  713. );
  714. $form['tab']['advanced_options']['result_type'] = array(
  715. '#type' => 'radios',
  716. '#title' => t('Group informations'),
  717. '#options' => array(
  718. 'by_message' => t('By message (compact view)'),
  719. 'by_line' => t('By line (list view)'),
  720. // 'by_collapse' => t('By line (collapsible view)'),
  721. ),
  722. '#default_value' => $list_previous_values['result_type'],
  723. '#states' => array(
  724. 'invisible' => array(
  725. ':input[name=result_level]' => array('value' => 'first'),
  726. ),
  727. ),
  728. );
  729. $form['tab']['advanced_options']['info'] = array(
  730. '#type' => 'item',
  731. '#description' => t('Warning: to display warnings, notices and informations, especially by line, can help you to detect issues when submitted list of terms is not clean and when you choose to check lines, but it is memory intensive.'),
  732. );
  733. $form['actions'] = array('#type' => 'actions');
  734. $form['actions']['submit'] = array(
  735. '#type' => 'submit',
  736. '#value' => t('Import'),
  737. );
  738. $form['actions']['import_default_values'] = array(
  739. '#type' => 'submit',
  740. '#value' => t('Reset to defaults'),
  741. '#validate' => array('taxonomy_csv_import_form_default_values_validate'),
  742. '#submit' => array('taxonomy_csv_import_form_default_values_submit'),
  743. );
  744. return $form;
  745. }
  746. /**
  747. * Handles CSV import form validation.
  748. *
  749. * @see taxonomy_csv_import_form()
  750. */
  751. function taxonomy_csv_import_form_validate($form, &$form_state) {
  752. // Invoke taxonomy_csv.import.api.
  753. $module_dir = drupal_get_path('module', 'taxonomy_csv');
  754. require_once($module_dir . '/import/taxonomy_csv.import.api.inc');
  755. $options = &$form_state['values'];
  756. // 1. Preload text or file in order to check it.
  757. switch ($options['source_choice']) {
  758. case 'text':
  759. $options['url'] = '';
  760. $options['file'] = '';
  761. break;
  762. case 'url':
  763. $options['text'] = '';
  764. $options['file'] = '';
  765. break;
  766. case 'file':
  767. $options['text'] = '';
  768. $options['url'] = '';
  769. // When source is path, need to preload here.
  770. $file = file_save_upload('file', array('file_validate_extensions' => array('csv txt')));
  771. // fopen and fseek need a real path.
  772. if (!empty($file)) {
  773. $file->filepath = drupal_realpath($file->uri);
  774. if (!empty($file->filepath)) {
  775. $options['file'] = $file;
  776. }
  777. }
  778. break;
  779. }
  780. $messages = _taxonomy_csv_import_input_preload($options);
  781. // 2, Simplify values to be compatible with api checks.
  782. // Define true delimiter.
  783. $delimiter = array(
  784. 'comma' => ',',
  785. 'semicolon' => ';',
  786. 'tabulation' => "\t",
  787. 'pipe' => '|',
  788. 'space' => ' ',
  789. 'currency_sign' => '¤',
  790. 'soft_tab' => str_repeat(' ', $options['import_delimiter_soft_tab_width']),
  791. 'custom_delimiter' => $options['import_delimiter_custom'],
  792. );
  793. $options['delimiter'] = $delimiter[$options['import_delimiter']];
  794. // Define true enclosure.
  795. $enclosure = array(
  796. 'none' => '',
  797. 'quotation' => '"',
  798. 'quote' => "'",
  799. 'custom_enclosure' => $options['import_enclosure_custom'],
  800. );
  801. $options['enclosure'] = $enclosure[$options['import_enclosure']];
  802. // Warning: in API, 'language' is option for terms.
  803. // This issue is related to subform 'i18n_translation', provided by 'i18n'.
  804. $options['vocabulary_language'] = $options['language'];
  805. $options['language'] = $options['import_language'];
  806. // Translate languages is trimmed array of translate_languages.
  807. $options['translate_languages'] = (trim($options['translate_languages'] == '')) ?
  808. array() :
  809. array_values(array_map('trim', explode(',', drupal_strtolower($options['translate_languages']))));
  810. // Custom format is trimmed array of import_fields_format.
  811. $options['fields_format'] = (trim($options['import_fields_format'] == '')) ?
  812. array() :
  813. array_values(array_map('trim', explode(',', $options['import_fields_format'])));
  814. // Custom fields is trimmed array of import_fields_custom.
  815. $options['fields_custom'] = (trim($options['import_fields_custom'] == '')) ?
  816. array() :
  817. array_values(array_map('trim', explode(',', $options['import_fields_custom'])));
  818. // Define result preferences.
  819. foreach ($options['result_choices'] as $key => $value) {
  820. $options[$key] = $value;
  821. }
  822. // 3. Make api checks and eventually update options by reference.
  823. $messages = array_merge($messages, _taxonomy_csv_import_check_options($options));
  824. // Return i18n options for UI.
  825. $options['import_language'] = $options['language'];
  826. $options['language'] = $options['vocabulary_language'];
  827. unset($options['vocabulary_language']);
  828. // In UI, translate_languages is a string and not an array.
  829. $options['translate_languages'] = implode(', ', $options['translate_languages']);
  830. // Return form set errors for api errors.
  831. foreach (array(
  832. 'fields_format' => 'import_fields_format',
  833. 'delimiter' => 'import_delimiter',
  834. 'enclosure' => 'import_enclosure',
  835. 'fields_custom' => 'import_fields_custom',
  836. 'language' => 'import_language',
  837. 'vocabulary_language' => 'language',
  838. ) as $key => $value) {
  839. if (isset($messages[$key])) {
  840. $messages[$value] = $messages[$key];
  841. unset($messages[$key]);
  842. }
  843. }
  844. // 4. Make non api checks.
  845. if (($options['import_delimiter'] == 'custom_delimiter')
  846. && (empty($options['import_delimiter_custom']))) {
  847. $messages['import_delimiter_custom'] = t('You choose to use a custom delimiter, but your delimiter is empty.');
  848. }
  849. if (($options['import_enclosure'] == 'custom_enclosure')
  850. && (empty($options['import_enclosure_custom']))) {
  851. $messages['import_enclosure_custom'] = t('You choose to use a custom enclosure, but your enclosure is empty.');
  852. }
  853. if (($options['import_delimiter'] == 'custom_delimiter')
  854. && (drupal_strlen($options['import_delimiter_custom']) > 1)) {
  855. $messages['import_delimiter_custom'] = t('Delimiter should have only one character.');
  856. }
  857. if (($options['import_enclosure'] == 'custom_enclosure')
  858. && (drupal_strlen($options['import_enclosure_custom']) > 1)) {
  859. $messages['import_enclosure_custom'] = t('Enclosure should have only zero or one character.');
  860. }
  861. // 5. Finish validatation of form.
  862. foreach ($messages as $item => $message) {
  863. form_set_error(check_plain($item), filter_xss($message));
  864. }
  865. }
  866. /**
  867. * Validate options of imported vocabulary or line.
  868. *
  869. * @param $options
  870. * Array of options.
  871. *
  872. * @return
  873. * Array of messages errors if any.
  874. * By reference options are cleaned and completed.
  875. */
  876. function _taxonomy_csv_import_check_options(&$options) {
  877. global $user;
  878. $messages = array();
  879. // Custom order check.
  880. // @todo Use a regexp.
  881. if ($options['import_format'] == TAXONOMY_CSV_FORMAT_FIELDS) {
  882. if (!is_array($options['fields_format'])) {
  883. $messages['fields_format'] = t('Field format is an array of machine name fields.');
  884. }
  885. elseif ($options['fields_format'] === array()) {
  886. $messages['fields_format'] = t("Field format can't be empty.");
  887. }
  888. elseif (in_array('', $options['fields_format'])) {
  889. $messages['fields_format'] = t('One of the fields is empty.');
  890. }
  891. // Check if name is the first item.
  892. elseif ($options['fields_format'][0] != 'name' && $options['fields_format'][0] != 'tid') {
  893. $messages['fields_format'] = t("First field has to be 'name' or 'tid'.");
  894. }
  895. elseif (array_map('strtolower', $options['fields_format']) != $options['fields_format']) {
  896. $messages['fields_format'] = t("Fields have to be in lower case.");
  897. }
  898. elseif (str_replace(' ', '', implode(',', $options['fields_format'])) != implode(',', $options['fields_format'])) {
  899. $messages['fields_format'] = t("Fields can't have spaces.");
  900. }
  901. elseif (in_array('weight', $options['fields_format']) && $options['keep_order']) {
  902. $messages['keep_order'] = t("You can't keep order of terms when you import weights of terms.");
  903. }
  904. // Check items that must be unique.
  905. else {
  906. foreach (array(
  907. 'tid',
  908. 'vid',
  909. 'name',
  910. 'description',
  911. 'format',
  912. 'weight',
  913. 'language',
  914. 'i18n_tsid',
  915. 'guid',
  916. 'vocabulary_machine_name',
  917. ) as $item) {
  918. if (count(array_keys($options['fields_format'], $item)) > 1) {
  919. $messages['fields_format'] = t("Fields can have only one '!item'.", array(
  920. '!item' => $item,
  921. ));
  922. }
  923. }
  924. }
  925. }
  926. // Delimiter or enclosure greater than one character are useless with fgetcsv,
  927. // except with soft tab.
  928. if ($options['delimiter'] == ''
  929. || ((drupal_strlen($options['delimiter']) > 1) && (trim($options['delimiter'], ' ')) != '')
  930. ) {
  931. $messages['delimiter'] = t('Delimiter should be a one character string or a soft tab (two spaces or more).');
  932. }
  933. if (drupal_strlen($options['enclosure']) == 0) {
  934. // With fgetcsv, empty enclosure bugs, so use default quote enclosure.
  935. $options['enclosure'] = '"';
  936. }
  937. elseif (drupal_strlen($options['enclosure']) > 1) {
  938. $messages['enclosure'] = t('Enclosure lenght cannot be greater than one character.');
  939. }
  940. if ($options['delimiter'] == $options['enclosure']) {
  941. $messages['delimiter'] = t('Delimiter and enclosure cannot be same character.');
  942. }
  943. // Check filter format.
  944. $list_filter_format_custom = array(
  945. 'none' => t("None (Fixed plain text: user can't choose)"),
  946. );
  947. foreach (filter_formats($user) as $format) {
  948. $list_filter_format_custom[$format->format] = $format->name;
  949. }
  950. if (!isset($list_filter_format_custom[$options['filter_format_custom']])) {
  951. $messages['filter_format_custom'] = t('Filter format for custom fields is not authorized.');
  952. }
  953. $list_filter_format = $list_filter_format_custom;
  954. unset($list_filter_format['none']);
  955. if (!isset($list_filter_format[$options['filter_format']])) {
  956. $messages['filter_format'] = t('Filter format for description is not authorized.');
  957. }
  958. // Clean locale if any.
  959. $options['locale_custom'] = trim($options['locale_custom']);
  960. // Replace machine name with vocabulary id.
  961. if (!is_numeric($options['vocabulary_id'])) {
  962. $vocabulary = taxonomy_vocabulary_machine_name_load($options['vocabulary_id']);
  963. $options['vocabulary_id'] = $vocabulary->vid;
  964. }
  965. // Calculates number of lines to be imported.
  966. if (!isset($options['file'])
  967. || !is_object($options['file'])
  968. || count(file($options['file']->filepath)) == 0) {
  969. $messages['total_lines'] = t('No term to import.');
  970. }
  971. if (!in_array($options['vocabulary_target'], array(
  972. 'autocreate',
  973. 'existing',
  974. ))) {
  975. $messages['vocabulary_target'] = t('Destination target should be "autocreate" or "existing".');
  976. }
  977. if ($options['vocabulary_target'] == 'existing') {
  978. $list_vocabularies = taxonomy_get_vocabularies();
  979. if (!isset($list_vocabularies[$options['vocabulary_id']])) {
  980. $messages['vocabulary_id'] = t("You choose to use an existing vocabulary, but you haven't choose it.");
  981. }
  982. }
  983. // Check default language.
  984. $list_language = array(
  985. 'und' => t('Language neutral'),
  986. );
  987. if (module_exists('locale')) {
  988. $list_language += locale_language_list('name');
  989. foreach (locale_language_list('native') as $key => $value) {
  990. $list_language[$key] .= ' [' . $value . ']';
  991. }
  992. }
  993. if (!isset($list_language[$options['language']])) {
  994. $messages['language'] = t('The chosen language for terms is not enabled.') . ' ' . t('Add it before import in <a href="!link">locale settings</a>.', array(
  995. '!link' => url('admin/config/regional/language/overview'),
  996. ));
  997. }
  998. if (!isset($list_language[$options['vocabulary_language']])) {
  999. $messages['vocabulary_language'] = t('The chosen language for vocabulary is not enabled.') . ' ' . t('Add it before import in <a href="!link">locale settings</a>.', array(
  1000. '!link' => url('admin/config/regional/language/overview'),
  1001. ));
  1002. }
  1003. // Check i18n mode.
  1004. if (module_exists('i18n_taxonomy')) {
  1005. if ($options['vocabulary_target'] != 'autocreate') {
  1006. $vocabulary = taxonomy_vocabulary_load($options['vocabulary_id']);
  1007. }
  1008. // Create a pseudo vocabulary to simplify the check of options.
  1009. else {
  1010. $vocabulary = (object) array(
  1011. 'i18n_mode' => $options['i18n_mode'],
  1012. 'language' => $options['vocabulary_language'],
  1013. );
  1014. }
  1015. if (($vocabulary->i18n_mode == I18N_MODE_NONE) && ($options['language'] != 'und')) {
  1016. $messages['language'] = t('With a vocabulary without language, terms should have a undefined language.');
  1017. }
  1018. if (($vocabulary->i18n_mode == I18N_MODE_LANGUAGE) && ($options['language'] != $vocabulary->language)) {
  1019. $messages['language'] = t('With a fixed language vocabulary, terms should have the same language than the vocabulary.');
  1020. }
  1021. if (($vocabulary->i18n_mode == I18N_MODE_LOCALIZE) && ($options['language'] != 'und')) {
  1022. $messages['language'] = t('With a localizable vocabulary, terms should have a undefined language.');
  1023. }
  1024. // Check accordance of options.
  1025. if ($options['import_format'] == TAXONOMY_CSV_FORMAT_TRANSLATE) {
  1026. // Vocabulary should be localizable or translatable.
  1027. if (($vocabulary->i18n_mode == I18N_MODE_NONE)
  1028. || ($vocabulary->i18n_mode == I18N_MODE_LANGUAGE)
  1029. ) {
  1030. $messages['i18n_mode'] = t('You need to create or to select a <em>localizable</em> or <em>translatable</em> vocabulary if you want to import translations of names and descriptions.');
  1031. }
  1032. $languages_unset = array_diff($options['translate_languages'], array_flip($list_language));
  1033. if (($vocabulary->i18n_mode == I18N_MODE_LOCALIZE || $vocabulary->i18n_mode == I18N_MODE_TRANSLATE)
  1034. && $languages_unset
  1035. ) {
  1036. $messages['translate_languages'] = t('You can use only existing and enabled languages and not "!languages".', array(
  1037. '!languages' => implode('", "', $languages_unset),
  1038. ));
  1039. }
  1040. if ($vocabulary->i18n_mode == I18N_MODE_TRANSLATE && in_array('und', $options['translate_languages'])) {
  1041. $messages['translate_languages'] = t('You cannot use undefined language ("und") with "Translate" i18n mode.');
  1042. }
  1043. if ($vocabulary->i18n_mode == I18N_MODE_LOCALIZE && $options['translate_languages'][0] != 'und') {
  1044. $messages['translate_languages'] = t('First language should be undefined ("und") with "Localize" i18n mode.');
  1045. }
  1046. if ($vocabulary->i18n_mode == I18N_MODE_LOCALIZE && in_array(language_default('language'), $options['translate_languages'])) {
  1047. $messages['translate_languages'] = t('With "Localize" mode, you cannot translate a term into the default language ("!language"). Term in that language will be the first one on each csv line.', array(
  1048. '!language' => language_default('language') . ' = ' . language_default('name'),
  1049. ));
  1050. }
  1051. if (($vocabulary->i18n_mode == I18N_MODE_LOCALIZE) && ($options['filter_format'] != 'plain_text')) {
  1052. // i18n module requires that the description be plain text to translate it.
  1053. $messages['filter_format'] = t('With a localized vocabulary, you need to choose a plain text description. Choose or create a translatable vocabulary to use html descriptions.');
  1054. }
  1055. }
  1056. }
  1057. elseif ($options['i18n_mode'] != 0) {
  1058. $messages['i18n_mode'] = t('"Taxonomy i18n" module need to be enabled to set an internationalization option.');
  1059. }
  1060. // Custom fields check.
  1061. // @todo Use a regexp.
  1062. if ($options['import_format'] == TAXONOMY_CSV_FORMAT_FIELDS) {
  1063. if (!is_array($options['fields_custom'])) {
  1064. $messages['fields_custom'] = t('Custom fields are an array of machine name fields.');
  1065. }
  1066. elseif ($options['fields_custom'] == array()) {
  1067. // Good
  1068. }
  1069. elseif (in_array('', $options['fields_custom'])) {
  1070. $messages['fields_custom'] = t('One of the custom fields is empty.');
  1071. }
  1072. // Name need to be unique..
  1073. elseif (count(array_unique($options['fields_custom'])) != count($options['fields_custom'])) {
  1074. $messages['fields_custom'] = t("Each custom fields sould have a unique machine name.");
  1075. }
  1076. elseif (array_map('strtolower', $options['fields_custom']) != $options['fields_custom']) {
  1077. $messages['fields_custom'] = t("Custom fields have to be in lower case.");
  1078. }
  1079. elseif (str_replace(' ', '', implode(',', $options['fields_custom'])) != implode(',', $options['fields_custom'])) {
  1080. $messages['fields_custom'] = t("Custom fields can't have spaces.");
  1081. }
  1082. else {
  1083. foreach ($options['fields_custom'] as $key => $value) {
  1084. $field_name = (strpos($value, '|') === FALSE) ?
  1085. $value :
  1086. drupal_substr($value, 0, strpos($value, '|'));
  1087. $field_type = (strpos($value, '|') === FALSE) ?
  1088. 'text' :
  1089. drupal_substr($value, strpos($value, '|') + 1);
  1090. if (in_array($field_name, array(
  1091. 'tid',
  1092. 'vid',
  1093. 'name',
  1094. 'description',
  1095. 'format',
  1096. 'weight',
  1097. 'language',
  1098. 'i18n_tsid',
  1099. 'vocabulary_machine_name',
  1100. 'guid',
  1101. 'parent',
  1102. ))) {
  1103. $messages['fields_custom'] = t("Default term fields don't need to be defined.");
  1104. }
  1105. }
  1106. }
  1107. }
  1108. if (!$options['update_or_ignore']) {
  1109. $messages['update_or_ignore'] = t('Please set what will become existing terms.');
  1110. }
  1111. elseif ($options['update_or_ignore'] == TAXONOMY_CSV_EXISTING_IGNORE
  1112. && ($options['import_format'] == TAXONOMY_CSV_FORMAT_POLYHIERARCHY
  1113. || ($options['import_format'] == TAXONOMY_CSV_FORMAT_STRUCTURE && $options['structure_type'] == TAXONOMY_CSV_FORMAT_POLYHIERARCHY)
  1114. )
  1115. ) {
  1116. $messages['update_or_ignore'] = t('You cannot choose to ignore existing items when you import a polyhierarchical structure.');
  1117. }
  1118. if ($options['check_hierarchy']
  1119. && ($options['set_hierarchy'] < 0 || $options['set_hierarchy'] > 2)) {
  1120. $messages['set_hierarchy'] = t('You need to set hierarchy level if hierarchy check of vocabulary is disabled.');
  1121. }
  1122. return $messages;
  1123. }
  1124. /**
  1125. * Handles CSV import form submission and launch batch set.
  1126. *
  1127. * @see taxonomy_csv_import_form()
  1128. */
  1129. function taxonomy_csv_import_form_submit($form, &$form_state) {
  1130. // Remember last preferences and prepare only options to be sent to Api.
  1131. foreach (array(
  1132. 'import_format',
  1133. 'structure_type',
  1134. 'import_fields_format',
  1135. 'translate_by',
  1136. 'translate_languages',
  1137. 'keep_order',
  1138. 'source_choice',
  1139. 'import_delimiter',
  1140. 'import_delimiter_soft_tab_width',
  1141. 'import_delimiter_custom',
  1142. 'import_enclosure',
  1143. 'import_enclosure_custom',
  1144. 'filter_format',
  1145. 'filter_format_custom',
  1146. 'import_language',
  1147. 'check_line',
  1148. 'check_utf8',
  1149. 'locale_custom',
  1150. 'vocabulary_target',
  1151. 'vocabulary_id',
  1152. 'i18n_mode',
  1153. 'language',
  1154. 'import_fields_custom',
  1155. 'delete_terms',
  1156. 'check_hierarchy',
  1157. 'set_hierarchy',
  1158. 'update_or_ignore',
  1159. // General options.
  1160. 'result_stats',
  1161. 'result_terms',
  1162. 'result_level',
  1163. 'result_type',
  1164. ) as $option) {
  1165. variable_set('taxonomy_csv_' . $option, $form_state['values'][$option]);
  1166. $options[$option] = $form_state['values'][$option];
  1167. }
  1168. // Finish to prepare $options. Unset useless options for api.
  1169. if ($options['source_choice'] == 'text') {
  1170. $options['text'] = &$form_state['values']['text'];
  1171. unset($form_state['values']['text']);
  1172. }
  1173. // Translate languages is trimmed array of translate_languages.
  1174. $options['translate_languages'] = (trim($options['translate_languages'] == '')) ?
  1175. array() :
  1176. array_values(array_map('trim', explode(',', drupal_strtolower($options['translate_languages']))));
  1177. unset($options['import_delimiter']);
  1178. unset($options['import_delimiter_soft_tab_width']);
  1179. unset($options['import_delimiter_custom']);
  1180. unset($options['import_enclosure']);
  1181. unset($options['import_enclosure_custom']);
  1182. $options['delimiter'] = $form_state['values']['delimiter'];
  1183. $options['enclosure'] = $form_state['values']['enclosure'];
  1184. unset($options['import_fields_format']);
  1185. unset($options['import_fields_custom']);
  1186. if ($options['import_format'] == TAXONOMY_CSV_FORMAT_FIELDS) {
  1187. $options['fields_format'] = $form_state['values']['fields_format'];
  1188. $options['fields_custom'] = $form_state['values']['fields_custom'];
  1189. }
  1190. else {
  1191. unset($options['fields_format']);
  1192. unset($options['fields_custom']);
  1193. }
  1194. if ($options['import_format'] == TAXONOMY_CSV_FORMAT_STRUCTURE) {
  1195. $options['import_format'] = $options['structure_type'];
  1196. }
  1197. unset($options['structure_type']);
  1198. // Warning: in API, 'language' is option for terms.
  1199. $options['vocabulary_language'] = $form_state['values']['language'];
  1200. $options['language'] = $options['import_language'];
  1201. unset($options['import_language']);
  1202. $options['file'] = $form_state['values']['file'];
  1203. $options['url'] = $form_state['values']['url'];
  1204. // Internal options.
  1205. $options['check_options'] = FALSE; // Already done.
  1206. $options['result_display'] = TRUE;
  1207. // Prepares process batch (will be automatically processed when returns).
  1208. taxonomy_csv_vocabulary_import($options);
  1209. }
  1210. /**
  1211. * Restore recommended default values in the import form. Empty validate hook.
  1212. *
  1213. * @see taxonomy_csv_import_form()
  1214. */
  1215. function taxonomy_csv_import_form_default_values_validate($form, &$form_state) {
  1216. }
  1217. /**
  1218. * Restore recommended default values in the import form.
  1219. *
  1220. * @see taxonomy_csv_import_form()
  1221. */
  1222. function taxonomy_csv_import_form_default_values_submit($form, &$form_state) {
  1223. foreach (_taxonomy_csv_values('import_default_ui') as $option => $value) {
  1224. variable_set("taxonomy_csv_$option", $value);
  1225. }
  1226. unset($form_state['values']);
  1227. unset($form_state['storage']);
  1228. drupal_set_message(t('Import options have been reset to default.'));
  1229. }