/[drupal]/contributions/modules/taxonomy_xml/taxonomy_xml.module
ViewVC logotype

Contents of /contributions/modules/taxonomy_xml/taxonomy_xml.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.12 - (show annotations) (download) (as text)
Wed Oct 8 21:47:09 2008 UTC (13 months, 2 weeks ago) by karpuz
Branch: MAIN
CVS Tags: HEAD
Changes since 1.11: +4 -4 lines
File MIME type: text/x-php
SA-2008-063 http://drupal.org/node/318739
1 <?php
2 /* $Id: taxonomy_xml.module,v 1.11 2008/06/03 09:30:30 nicolash Exp $ */
3
4 /**
5 Copyright (c) 2007 Nicolas Haase <nicolas.haase@team.ourbrisbane.com>
6 Copyright (c) 2006 Sami Khan <sami@etopian.com>
7 Copyright (c) 2005 Sheldon Rampton <sheldon@prwatch.org>
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License.
12 */
13
14 /**
15 * @file taxonomy_xml.module
16 * This module makes it possible to import and export taxonomies
17 * as XML documents.
18 */
19
20
21 /**
22 * Implementation of hook_help().
23 */
24 function taxonomy_xml_help($path, $arg) {
25 switch ($path) {
26 case 'admin/modules#description':
27 return t('Makes it possible to import and export taxonomy terms via XML.');
28 case 'admin/content/taxonomy/import':
29 return t("You can upload a vocabulary and/or taxonomy terms from a properly-formatted XML document. If you want to add the terms to an existing vocabulary, ".
30 "use the \"Target vocabulary\" selector below. If you select \"determined by source file,\" the add to vocabulary will be specified by the XML document itself. ".
31 "(To avoid duplications, already-existing terms will not be added.)");
32 case 'admin/content/taxonomy/export':
33 return t("You can export XML documents for each vocabulary and its terms in this website's !taxonomies. Choose the vocabulary from the list below.",
34 array('!taxonomies' => l(t("taxonomies"), "admin/help/taxonomy")));
35 case 'admin/help#taxonomy_xml':
36 return t("This module makes it possible to import and export vocabularies and taxonomy terms via XML (requires taxonomy.module). ".
37 "Once installed and enabled, it module provides a list of downloadable XML documents for each vocabulary at !downloads. To import a vocabulary, use !upload.",
38 array('!downloads' => l(t("taxonomy XML"), "admin/taxonomy/export"), '!upload' => l("administer &raquo; categories &raquo; import", "admin/taxonomy/import")));
39 }
40 }
41
42 /**
43 * Implementation of hook_menu: Define menu links.
44 *
45 * @note See hook_menu for a description of return values.
46 */
47 function taxonomy_xml_menu() {
48 if (!module_exists('taxonomy')) {
49 return;
50 }
51
52 $items = array();
53
54 $items['admin/content/taxonomy/export'] = array(
55 'title' => t('Export'),
56 'access arguments' => array('administer taxonomy'),
57 'page callback' => 'taxonomy_xml_export',
58 'type' => MENU_LOCAL_TASK);
59
60 $items['admin/content/taxonomy/import'] = array(
61 'title' => t('Import'),
62 'access arguments' => array('administer taxonomy'),
63 'page callback' => 'taxonomy_xml_import',
64 'type' => MENU_LOCAL_TASK);
65
66 $items['taxonomy_xml'] = array(
67 'title' => t('Taxonomy XML'),
68 'access arguments' => array('access content'),
69 'page callback' => 'taxonomy_xml_file',
70 'type' => MENU_CALLBACK);
71
72 return $items;
73 }
74
75 /**
76 * taxonomy_xml_export
77 *
78 * Outputs an unordered list of all available vocabularies for export
79 *
80 * @return An unordered HTML list
81 */
82 function taxonomy_xml_export() {
83 // return the list of vocabularies
84 $output = '';
85 $vocabularies = module_invoke('taxonomy', 'get_vocabularies');
86
87 if (empty($vocabularies)) {
88 $output .= t('There are no vocabularies present');
89 }
90 else {
91 $output .= "\n<ul>";
92 foreach ($vocabularies as $vocabulary) {
93 $output .= "\n\t<li>". l($vocabulary->name, "taxonomy_xml/$vocabulary->vid") .'</li>';
94 }
95 $output .= "\n</ul>";
96 }
97 return $output;
98 }
99
100 /**
101 * taxonomy_xml_file
102 */
103 function taxonomy_xml_file() {
104 $vid = arg(1);
105
106 //retriving Vocabulary name
107 $vocabulary = (array) (module_invoke('taxonomy', 'vocabulary_load', $vid));
108 //print_r($vocabulary);
109 $vname = strtolower(str_replace(' ', '_', trim($vocabulary['name'])));
110 unset($vocabulary);
111
112 $file = taxonomy_xml_create($vid);
113
114 if (!empty($_SERVER['HTTP_USER_AGENT']) && (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 5.5') || strpos($_SERVER['HTTP_USER_AGENT'], 'Opera'))) {
115 header('Content-Type: application/dummy');
116 }
117 else {
118 header('Content-Type: application/octet-stream');
119 }
120 if (headers_sent()) {
121 echo 'Some data has already been output to browser, can\'t send file';
122 }
123 header('Content-Length: '. strlen($file));
124 header("Content-Disposition: attachment; filename=taxonomy_$vname.xml");
125 echo $file;
126 }
127
128 /**
129 * Menu callback for the import page.
130 */
131 function taxonomy_xml_import() {
132 return drupal_get_form('taxonomy_xml_import_form');
133 }
134
135 /**
136 * Imports the actual XML.
137 */
138 function taxonomy_xml_import_form_validate($form, &$form_state) {
139 $validators = array(
140 'file_validate_extensions' => array('xml'),
141 );
142
143 if ($file = file_save_upload('xml', $validators)) {
144 $fd = fopen($file->filepath, "rb");
145 if (!$fd) {
146 form_set_error('xml', t('Vocabulary import failed: file %filename cannot be read.', array('%filename' => $file->filename)));
147 }
148 else {
149 $info = fstat($fd);
150 $len = $info["size"];
151 $text = fread($fd, $len);
152
153 fclose($fd);
154 drupal_set_message(t('Loaded file %filename.', array('%filename' => $file->filename)));
155 taxonomy_xml_parse($text, $form_state['values']['vid'], $form_state['values']['term'], $form_state['values']['duplicate']);
156 }
157 }
158 else {
159 form_set_error('xml', t('Vocabulary import failed: file was not uploaded.'));
160 }
161 }
162
163 /**
164 * Builds the import form.
165 */
166 function taxonomy_xml_import_form() {
167 $vocs[0] = t('[Determined by source file]');
168 foreach (module_invoke('taxonomy', 'get_vocabularies') as $vid => $voc) {
169 $vocs[$vid] = $voc->name;
170 }
171 $form['vid'] = array(
172 '#type' => 'select',
173 '#title' => t('Target vocabulary'),
174 '#default_value' => 0,
175 '#options' => $vocs,
176 '#description' => t('The vocabulary into which terms should be loaded.'),
177 );
178 /*
179 $form['term'] = array(
180 '#type' => 'checkbox',
181 '#title' => ('Select a term from the vocabulary.'),
182 '#description' => t("Allows a selection of a term in the vocabulary to be the parent of the imported taxonomy."),
183 );*/
184 $form['xml'] = array(
185 '#type' => 'file',
186 '#title' => t('File to import'),
187 '#description' => t('Click "Browse..." to select an XML document to upload.'),
188 );
189 $form['duplicate'] = array(
190 '#type' => 'checkbox',
191 '#title' => t('Allow duplicate terms'),
192 '#description' => t('If you want to keep the same term in different positions in the vocabulary hierarchy, check this'),
193 '#default_value' => 0,
194 );
195 $form['submit'] = array(
196 '#type' => 'submit',
197 '#value' => t('Import'),
198 );
199 $form['#attributes'] = array('enctype' => 'multipart/form-data');
200
201 return $form;
202 }
203
204 /**
205 * taxonomy_xml_create
206 * @param $vid
207 * Which vocabulary to generate the tree for.
208 *
209 * @param $parent
210 * The term ID under which to generate the tree. If 0, generate the tree
211 * for the entire vocabulary.
212 *
213 * @param $depth
214 * Internal use only.
215 *
216 * @param $max_depth
217 * The number of levels of the tree to return. Leave NULL to return all levels.
218 *
219 * @return
220 * The text of an XML document.
221 */
222 function taxonomy_xml_create($vid, $parent = 0, $depth = -1, $max_depth = NULL) {
223 $output = '<?xml version="1.0" standalone="no"?>'."\n";
224 //$output .= '<!DOCTYPE taxonomy SYSTEM "taxonomy.dtd">'."\n";
225 $tree = module_invoke('taxonomy', 'get_tree', $vid, $parent, $depth, $max_depth);
226 if ($tree) {
227 $vocabulary = module_invoke('taxonomy', 'vocabulary_load', $vid);
228 $output .= "<vocabulary>\n";
229 foreach ($vocabulary as $key => $value) {
230 if (is_array($value)) {
231 $output .= "<$key>". check_plain(implode(',', $value)) ."</$key>";
232 }
233 else {
234 $output .= "<$key>". check_plain($value) ."</$key>";
235 }
236 }
237 foreach ($tree as $term) {
238
239 $synonyms = taxonomy_get_synonyms($term->tid);
240 $output .= "<term>";
241 foreach ($term as $key => $value) {
242 if ($key == 'parents') {
243 foreach ($value as $parent) {
244 $output .= "<parent>". check_plain($parent) ."</parent>";
245 }
246 }
247 else {
248 $output .= "<$key>". check_plain($value) ."</$key>";
249 }
250 }
251 if ($synonyms) {
252 $output .= "<synonyms>";
253 $output .= implode("\n", $synonyms);
254 $output .= "</synonyms>";
255 }
256 $output .= "</term>";
257 }
258 $output .= "</vocabulary>\n";
259 }
260 return $output;
261 }
262
263 /**
264 * Call-back function used by the XML parser.
265 */
266 function taxonomy_xml_element_start($parser, $name, $attributes) {
267 global $_tx_term, $_tx_element, $_tx_tag;
268
269 switch ($name) {
270 case 'vocabulary':
271 $_tx_element = $name;
272 break;
273 case 'term':
274 $_tx_element = $name;
275 $_tx_term += 1;
276 }
277 $_tx_tag = $name;
278 }
279
280 /**
281 * Call-back function used by the XML parser.
282 */
283 function taxonomy_xml_element_end($parser, $name) {
284 global $_tx_element;
285
286 switch ($name) {
287 case 'vocabulary':
288 case 'term':
289 $_tx_element = '';
290 }
291 }
292
293 /**
294 * Call-back function used by the XML parser.
295 */
296 function taxonomy_xml_element_data($parser, $data) {
297 global $_tx_vocabulary, $_tx_element, $_tx_terms, $_tx_term, $_tx_tag;
298
299 switch ($_tx_element) {
300 case 'term':
301 if ($_tx_tag == 'parent') {
302 if (trim($data)) {
303 $_tx_terms[$_tx_term][$_tx_tag][] = $data;
304 }
305 }
306 else {
307 $_tx_terms[$_tx_term][$_tx_tag] .= trim($data);
308 }
309 break;
310 default:
311 $_tx_vocabulary[$_tx_tag] .= trim($data);
312 }
313 }
314
315 /**
316 * Initiate the parser on the custom XML schema.
317 *
318 * This uses the XML callback parser with tag callbacks.
319 *
320 * @see taxonomy_xml_element_start
321 * @see taxonomy_xml_element_end
322 * @see taxonomy_xml_element_data
323 *
324 */
325 function taxonomy_xml_parse(&$data, $vid = 0, $parent_tid = NULL, $duplicate = 0) {
326 global $_tx_terms, $_tx_vocabulary;
327
328 // Unset the global variables before we use them:
329 unset($GLOBALS['element'], $GLOBALS['term'], $GLOBALS['tag']);
330 $_tx_terms = array();
331 $_tx_vocabulary = array();
332 $new_terms = array();
333
334 // parse the data:
335 $xml_parser = drupal_xml_parser_create($data);
336 xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, false); //
337 xml_set_element_handler($xml_parser, 'taxonomy_xml_element_start', 'taxonomy_xml_element_end');
338 xml_set_character_data_handler($xml_parser, 'taxonomy_xml_element_data');
339
340 if (!xml_parse($xml_parser, $data, 1)) {
341 watchdog('error', 'Taxonomy_xml: failed to parse file: %error at line %line.', array('%error' => xml_error_string(xml_get_error_code($xml_parser)), '%line' => xml_get_current_line_number($xml_parser)));
342 drupal_set_message(t('Failed to parse file: %error at line %line.', array('%error' => xml_error_string(xml_get_error_code($xml_parser)), '%line' => xml_get_current_line_number($xml_parser))), 'error');
343 }
344 xml_parser_free($xml_parser);
345
346
347 // If an existing vocabulary has been chosen or has the same name as the vocabulary being added,
348 // terms should be added to the existing vocabulary. Otherwise a new vocabulary should be created.
349
350 if ($vid == 0) {
351 $name = $_tx_vocabulary['name'];
352 $vid = db_result(db_query("SELECT vid FROM {vocabulary} WHERE LOWER('%s') LIKE LOWER(name)", trim($name)));
353 if ($vid) {
354 $_tx_vocabulary = (array) (module_invoke('taxonomy', 'vocabulary_load', $vid));
355 }
356 else {
357 unset($_tx_vocabulary['vid']);
358
359 // The node type array for a vocab has the type name as the key and the value set to 1
360 // so we have to slightly modify the array
361 $node_types = explode(',', $_tx_vocabulary['nodes']);
362 $_tx_vocabulary['nodes'] = array();
363 foreach ($node_types as $type) {
364 $_tx_vocabulary['nodes'][$type] = 1;
365 }
366 taxonomy_save_vocabulary($_tx_vocabulary);
367 }
368 }
369 else {
370 $_tx_vocabulary = (array) (module_invoke('taxonomy', 'vocabulary_load', $vid));
371 }
372
373 // Get the maximum depth of terms
374 foreach ($_tx_terms as $term) {
375 $term_depth[] = $term['depth'];
376 }
377 // Import terms in order of depth
378 $new_tid = array();
379 for ($i=0;$i<=max($term_depth);$i++) {
380 foreach ($_tx_terms as $term) {
381 if ($term['depth'] != $i) {
382 continue;
383 }
384 $term['vid'] = $_tx_vocabulary['vid'];
385 $term['old_tid'] = $term['tid'];
386 unset($term['tid']);
387 if (is_array($term['parent'])) {
388 foreach ($term['parent'] as $key => $value) {
389 if ($value) {
390 $term['parent'][$key] = $new_tid[$value];
391 }
392 }
393 }
394 $term_exists = false;
395 if ($duplicate == 0) {
396 $existing_terms = module_invoke('taxonomy', 'get_term_by_name', $term['name']);
397 if (count($existing_terms > 0)) {
398 foreach ($existing_terms as $existing_term) {
399 if ($existing_term->vid == $term['vid']) {
400 $term_exists = true;
401 // Map the term tid from the imported XML file to the tid in term_data database table
402 $new_tid[$term['old_tid']] = $existing_term->tid;
403 $skipped_terms[$existing_term->name] = 1;
404 }
405 }
406 }
407 }
408 // If the term doesn't already exist in this vocabulary, add it.
409 if (!$term_exists) {
410 taxonomy_save_term($term);
411 // Map the term tid from the imported XML file to the tid in term_data database table
412 $new_tid[$term['old_tid']] = $term['tid'];
413 $new_terms[] = $term['name'];
414 }
415 }
416
417 $output .= t('Vocabulary %name: ', array('%name' => $_tx_vocabulary['name']));
418 if ($new_terms) {
419 $output .= t('Added term(s) %terms', array('%terms' => implode(', ', $new_terms)));
420 }
421 else {
422 $output .= t('No terms added.');
423 }
424 if ($skipped_terms) {
425 $output .= t('Ignored duplicate term(s) %terms', array('%terms' => implode(', ', array_keys($skipped_terms))));
426 }
427 drupal_set_message($output);
428 }
429 }

  ViewVC Help
Powered by ViewVC 1.1.2