Project

General

Profile

Download (15.5 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2

    
3
// at the moment we are using a static meta.xml file
4
define ("METADATA_FILE_NAME", "meta.xml");
5
define ("STATIC_DIR", "/static/");
6
define ("ARCHIVE_ROOT_DIR", "dwca_export/");
7
define ('FILE_MAP', 'dwca_export_archive_descriptor_file_map');
8
define ('FILE_EXTENSION', ".txt");
9

    
10

    
11
/**
12
 * Implements hook_menu().
13
 */
14
function dwca_export_menu() {
15

    
16
	$items = array();
17

    
18
	$items['admin/config/system/dwca_export'] = array(
19
		'title' => 'Darwin Core Archive export',
20
		'description' => t('Create a DarwinCore Archive of this scratchpad.'),
21
		'page callback' => 'drupal_get_form',
22
		'page arguments' => array('dwca_export_config_form'),
23
		'access arguments' => array('access DwC-A export settings'),
24
		'type' => MENU_NORMAL_ITEM,
25
		//'file' => 'dwca_export.admin.inc'
26
	);
27

    
28

    
29
	$items['dwca_export'] = array(
30
		'page callback' => 'dwca_export_deliver_archive',
31
		'access arguments' => array('access content'),
32
		'type' => MENU_CALLBACK
33
	);
34

    
35

    
36
	return $items;
37
}
38

    
39
/**
40
 * Implementation of hook_views_api()
41
 *
42
 * drupal will load dwca_export.views_default.inc when this hook is implemented
43
 */
44
function dwca_export_views_api() {
45
	return array(
46
		'api' => 3.0
47
	);
48
}
49

    
50
/**
51
* Implementation of hook_ctools_plugin_api().
52
*/
53
function dwca_export_ctools_plugin_api(){
54
	list($module, $api) = func_get_args();
55
	if($module == "strongarm" && $api == "strongarm"){
56
		return array(
57
      "version" => 1
58
		);
59
	}
60
}
61

    
62
/**
63
 * Form function, called by drupal_get_form()
64
 * in dwca_export_menu().
65
 */
66
function dwca_export_config_form($form, &$form_state) {
67

    
68
	global $base_url;
69

    
70
	$form['dwca_export_info'] = array(
71
		'#markup' => '<p>For general information on the DarwinCore Archive format please refer to  '
72
		. l('GBIF - Standards and Tools - Darwin Core Archives', 'http://www.gbif.org/informatics/standards-and-tools/publishing-data/data-standards/darwin-core-archives/')
73
		.'</p>'
74
	);
75

    
76
	$form['dwca_export_execute'] = array(
77
		'#markup' => '<p>The DarwinCore Archive export is available at '. l('dwca_export', 'dwca_export').'</p>'
78
		//'#type' => 'button',
79
		//'#value' => t('Export Scratchpad to DarwinCore Archive'), 
80
	  //'#weight' => 19,
81
	);
82
	
83
	$form['dwca_export_view_mapping'] = dwca_export_select_view_form();
84
	
85
	
86

    
87
	$form['#submit'][] = 'dwca_export_config_form_submit';
88
	return system_settings_form($form);
89

    
90
}
91

    
92

    
93
function dwca_export_select_view_form() {
94

    
95
	$views = array(
96
    		'#type' => 'fieldset',
97
    		'#title' => t('View to file mapping'),
98
    		//'#tree' => TRUE,
99
  	);
100

    
101
	foreach(variable_get(FILE_MAP) as $dwca_filename => $view_data){
102
		
103
		if ($dwca_filename == 'description') {
104
			
105
			
106
		$views[$dwca_filename] = array (
107
					'#type' => 'fieldset',
108
  					'#title' => t('DESCRIPTIVE DATA TYPES'),
109
				  	'#weight' => 3,
110
				  	'#collapsible' => TRUE,
111
				  	'#collapsed' => TRUE,
112
				  	'#tree' => FALSE,
113
			
114
  		);
115
			foreach($view_data as $dwca_filename_inner => $view_data_inner){
116
				
117
				
118
				$views[$dwca_filename][$dwca_filename_inner] = array(
119
									  	'#type' => 'textfield',
120
									  	'#title' => t($dwca_filename . '_' . $dwca_filename_inner),
121
									  	'#default_value' => $view_data_inner['view_name'],
122
									  	'#size' => 60,
123
									  	'#description' => t('specify view for ' . $dwca_filename_inner),
124
			
125
				);
126
			}
127
			
128
		} else {
129
			$views[$dwca_filename] = array(
130
					  	'#type' => 'textfield',
131
					  	'#title' => t($dwca_filename),
132
					  	'#default_value' => $view_data['view_name'],
133
					  	'#size' => 60,
134
					  	'#description' => t('specify view for ' . $dwca_filename),
135
			
136
			);
137
		}
138
	}
139

    
140
	return $views;
141
}
142

    
143
function dwca_export_config_form_submit($form, &$form_state) {
144

    
145
	$variables = $form_state['input'];
146
	$save_variables = '';
147
	$dwca_export_archive_descriptor_file_map = variable_get(FILE_MAP);
148

    
149
	foreach ($variables as $key => $value) {
150

    
151
		if (array_key_exists($key, $dwca_export_archive_descriptor_file_map)) {
152

    
153
			$dwca_export_archive_descriptor_file_map[$key]['view_name'] = $value;
154
			//$save_variables .= '<p>' . $key . ' ' . $value . '</p>';
155
		} else {
156

    
157
			$description_key = 'description';
158
			
159
			if (array_key_exists($description_key, $dwca_export_archive_descriptor_file_map)) {
160
				
161
				//get the inner array containing the different description data types
162
				$description_map = $dwca_export_archive_descriptor_file_map[$description_key];
163
				//if (array_key_exists($key, $dwca_export_archive_descriptor_file_map['description'])) {
164
				if (array_key_exists($key, $description_map)) {
165
					//drupal_set_message(t('The ooKEY is... ') . $key . t('The ooVALUE is... ') . $value);
166
					$dwca_export_archive_descriptor_file_map[$description_key][$key]['view_name'] = $value;
167
				}
168
			}
169
		}
170
	}
171

    
172
	variable_del(FILE_MAP);
173
	variable_set(FILE_MAP, $dwca_export_archive_descriptor_file_map);
174

    
175
	//drupal_set_message(t('The classification view is ') . $save_variables);// . '<pre>' . print_r($form_state,true) . '</pre>');//$save_variables);
176
}
177

    
178

    
179

    
180

    
181
// Reports an error if view name entered by the user does not exist in the database.
182
function dwca_export_config_form_validate($form, &$form_state) {
183
	
184
	$variables = $form_state['input'];
185
	$dwca_export_archive_descriptor_file_map = variable_get(FILE_MAP);
186
	$view_names = array();
187
	$missing_view_names = '';
188
	$missing_view = false;
189
	
190
	foreach ($variables as $key => $value) {
191
		
192
		//TODO: Check whether the views for the inner array cotaining all the description views exist
193
		if (array_key_exists($key, $dwca_export_archive_descriptor_file_map)) {
194
					
195
			$view = views_get_view($value);
196
			// check whether there is a view named with this value 
197
			if(!$view) {
198
				$view_names[] = $value;
199
				$missing_view_names .= $value . ', ';
200
				$missing_view = true;
201
			}
202
		}
203
	}
204
	
205
	if ($missing_view) {
206
		form_set_error('', t('VIEW(S) ' . $missing_view_names . ' NOT FOUND. Please input another view name.'));
207
	}
208
	
209
}
210

    
211
/**
212
 * menu callback
213
 */
214
function dwca_export_deliver_archive() {
215

    
216
	$tmp_archive_file_name = dwca_export_create_archive( _dwca_export_archive_descriptor_file_map() );
217

    
218
	if($tmp_archive_file_name && file_valid_uri($tmp_archive_file_name)){
219
		file_transfer($tmp_archive_file_name, array('Content-Type' => 'application/zip'));
220
	} else {
221
		throw new Exception(t('Error creating the archive'));
222
	}
223
}
224

    
225

    
226
/**
227
 * Provides the archive_descriptor_file_map which maps dwca file name to a set of view information.
228
 * The view information contains the fields 'view_name', 'display_id', 'out_file_url'.
229
 * The 'out_file_url' is initailly empty and will be set when this function is called
230
 * with both parameters.
231
 *
232
 * @param String $file_name
233
 * @param String $out_file_url
234
 *
235
 * @return the archive_descriptor_file_map
236
 */
237
function _dwca_export_archive_descriptor_file_map($file_name = NULL, $out_file_url = null){
238
	static $file_map;
239

    
240
	if(!isset($file_map)){
241
		$file_map = variable_get(FILE_MAP);
242
	}
243

    
244
	if($file_name && $out_file_url){
245
		
246
		//TODO: Change this - it's not very generic and will not work if we add new types to FILE_MAP
247
		if (($file_name == 'classification') || ($file_name == 'typesandspecimen') ||
248
				 ($file_name == 'reference') || ($file_name == 'image') || ($file_name == 'distribution')) {
249
			
250
			$file_map[$file_name]['out_file_url'] = $out_file_url;
251
			
252
		} else {
253
		
254
			$file_map['description'][$file_name]['out_file_url'] = $out_file_url;
255
		}
256
	}
257

    
258
	return $file_map;
259
}
260

    
261

    
262
/**
263
 * Walks all view export paths defined in the $views_map.
264
 * Each file is downloaded to the tmp folder and a zip file
265
 * is bundeled of the resulting csv files plus the meta file.
266
 *
267
 * @param $views_map - maps a view paths to dwca filenames
268
 *
269
 * @return the path in the filesystem to the final archive,
270
 * or FALSE in case of an error.
271
 */
272
function dwca_export_create_archive($views_map) {
273
	
274
	// all data is exported to temporary://dwca_export_*
275
	// now we can start bundling the actual archive
276
	$tmp_archive_file_name = drupal_tempnam("temporary://", "dwca_export_archive_");
277
	
278
	// Unfortunately we cannot use drupals ArchiverZip because there ís
279
	// no way to pass in ZipArchive::CREATE to the constructor to create the archive
280
	// TODO test if zip functionality is available (i.e. if(function_exists('zip_open'))
281
	// but I don't know where the proper location for such a check would be
282
	$zip = new ZipArchive();
283
	// it is safe to use drupal_realpath as the tmp file will be certainly local
284
	// and php's ZipArchive does not handle stream URIs
285
	$result = $zip->open(drupal_realpath($tmp_archive_file_name), ZipArchive::CREATE);
286
	
287
	// there might be a better way to get at this information
288
	$module_static_dir_absolute_path = realpath(drupal_get_path('module', 'dwca_export')) . STATIC_DIR;
289
	
290
	if ($result !== TRUE) {
291
		throw new Exception(t('Could not create zip_archive %tmp_archive_file_name', array('%tmp_archive_file_name' => $tmp_archive_file_name)));
292
	}
293
	
294
	
295
	// add metadata
296
	$zip->addFile($module_static_dir_absolute_path.METADATA_FILE_NAME, ARCHIVE_ROOT_DIR.METADATA_FILE_NAME);
297

    
298
	global $base_url;
299

    
300
	// execute all views to export the data into
301
	// temporary csv files (temporary://dwca_export_*). the resulting filenames
302
	// will be stored in _dwca_export_archive_descriptor_file_map()
303
	foreach($views_map as $filename=>$view_data){
304
	
305
		//lorna
306
		if ($filename == 'description') {
307
			
308
			//lorna: this foreach iterates through all the description types in the inner array
309
			foreach($view_data as $filename_inner => $view_data_inner){
310
				$view = views_get_view($view_data_inner['view_name']);
311
					
312
				$options = array (
313
					'output_file' => $filename_inner
314
				);
315
					
316
				//_dwca_export_views_data_export_override_batch($view, $view_data['general']['display_id'], $options);
317
				_dwca_export_views_data_export_override_batch($view, $view_data_inner['display_id'], $options);
318

    
319
				if ($view) {
320
					$view->execute_display($view_data_inner['display_id']);
321
				}else{
322
					throw new Exception(t('A view with the name %view does not exist', array('%view' => $view_data_inner['view_name'])));
323
				}
324
			}
325
			
326
		}
327
		else {
328
			$view = views_get_view($view_data['view_name']);
329
			$options = array (
330
					    'output_file' => $filename
331
			);
332
			_dwca_export_views_data_export_override_batch($view, $view_data['display_id'], $options);
333
				
334
			if ($view) {
335
				$view->execute_display($view_data['display_id']);
336
			} else{
337
				throw new Exception(t('A view with the name %view does not exist', array('%view' => $view_data['view_name'])));
338
			}
339
		}
340
		//end lorna
341

    
342
	}
343

    
344
	// add the csv data files
345
	foreach(_dwca_export_archive_descriptor_file_map() as $dwca_filename=>$view_data){
346

    
347
		if ($dwca_filename == 'description') {
348
			
349
			_dwca_export_concatenate_description_files($view_data, $zip);
350
			
351
		} else {
352
		
353
			$view_temp_file = $view_data['out_file_url'];
354
			_dwca_export_add_files_to_zip($view_temp_file, $dwca_filename, $zip);
355
		}
356
		
357
	}
358

    
359
	$zip->close();
360

    
361
	return $tmp_archive_file_name;
362
}
363

    
364
/**
365
 * 
366
 * Combines multiple Description Views for different description types into a single text file
367
 * @param $view_data The inner array containing the view data for the different description types
368
 * @param $zip The zip file for the files making up the DwC-A.
369
 */
370
function _dwca_export_concatenate_description_files($view_data, $zip){
371
	
372
	$desc_file_name = drupal_tempnam("temporary://", "description.tmp");
373
	$desc_file = fopen(drupal_realpath($desc_file_name), "w");
374

    
375
	//$first_desc_file = true;
376
	//drupal_set_message('CALLED _dwca_export_concatenate_description_files... ');
377
	
378
	
379
	foreach($view_data as $type=>$view_data_inner){
380

    
381
		$view_temp_file = $view_data_inner['out_file_url'];
382

    
383
		$descTypeFile = fopen($view_temp_file, "r");
384

    
385
		while(!feof($descTypeFile)){
386
			$lineOfText = fgets($descTypeFile, 4096);
387

    
388
			$items = explode(',', $lineOfText);
389
			
390
			//don't include empty descriptions or lines at the end of the view which start with the field name id
391
			//some lines just contain a single " if there is a carriage return within the description text so remove these too
392
			if ($items[0]!=="\"id\""  && trim($items[2])!=="\"\"") {
393

    
394
				//Fix - some descriptions are missing the closing " - Fixed in View now
395
				//if (!stripos(strrev($lineOfText), '"') === 0) {
396
						//$lineOfText = $lineOfText . "\"";
397
					//}
398
				
399
				// fix conflict with files that have an EOL that differs from the server EOL
400
				$lineOfText = rtrim($lineOfText, "\r\n") . PHP_EOL;
401
				fwrite($desc_file, $lineOfText);
402
				
403
				
404
				
405
			} else {
406
				fgets($descTypeFile, 4096);
407
			}
408

    
409
		}
410

    
411
		fclose($descTypeFile);
412

    
413
		//currently also adding each individual desc type to archive
414
		//_dwca_export_add_files_to_zip($view_temp_file, $type, $zip);
415
	}
416
	fclose($desc_file);
417

    
418
	_dwca_export_add_files_to_zip($desc_file_name, 'description', $zip);
419
}
420

    
421
function _dwca_export_add_files_to_zip($view_temp_file = NULL, $dwca_filename, $zip){
422
	
423
	if($view_temp_file){
424
		$zip->addFile(drupal_realpath($view_temp_file), ARCHIVE_ROOT_DIR.$dwca_filename . FILE_EXTENSION);
425
	}else{
426
		throw new Exception(t('Cannot create %file', array('%file' => $dwca_filename)));
427
	}
428
}
429

    
430
/**
431
 * Helper function that indicates that we want to
432
 * override the batch that the views_data_export view creates
433
 * on it's initial time through.
434
 *
435
 * Also provides a place to stash options that need to stay around
436
 * until the end of the batch
437
 *
438
 * adapted fom views_data_export.drush.inc
439
 */
440
function _dwca_export_views_data_export_override_batch($view = NULL, $display = NULL, $options = TRUE) {
441
	
442
	static $_views;
443
	if (isset($view)) {
444
		
445
		$_views[$view->name][$display] = $options;
446
	}
447
	return $_views;
448
}
449

    
450

    
451
/**
452
 * Implementation of hook_views_data_export_batch_alter()
453
 *
454
 * adapted fom views_data_export.drush.inc
455
 *
456
 *  @see batch_process() in form.inc
457
 */
458
function dwca_export_views_data_export_batch_alter(&$batch, &$final_destination, &$querystring) {
459

    
460
	$view_name = $batch['view_name'];
461
	$display_id = $batch['display_id'];
462

    
463
	$ok_to_override = _dwca_export_views_data_export_override_batch();
464

    
465
	// Make sure we do nothing if we are called not following the execution of
466
	// our drush command. This could happen if the file with this function in it
467
	// is included during the normal execution of the view
468
	if (!$ok_to_override[$view_name][$display_id]) {
469
		return;
470
	}
471

    
472
	$options = $ok_to_override[$view_name][$display_id];
473

    
474
	// We actually never return from the drupal_alter, but
475
	// use drush's batch system to run the same batch
476

    
477
	// Add a final callback
478
	$batch['operations'][] = array(
479
	    '_dwca_export_views_data_export_batch_finished', array($batch['eid'], $options['output_file']),
480
	);
481

    
482
	$batch['progressive'] = FALSE;
483
}
484

    
485
/**
486
* Implementation of hook_views_data_export_batch_alter()
487
*
488
* @see batch_process() in form.inc
489
*/
490
function dwca_export_batch_alter(&$batch, &$final_destination, &$querystring) {
491
	
492
	if($batch['source_url'] == 'dwca_export'){
493
		$batch['progressive'] = FALSE;
494
	}
495
}
496

    
497

    
498

    
499
/**
500
* Get's called at the end of the drush batch process that generated our export
501
*
502
* adapted fom views_data_export.drush.inc
503
*/
504
function _dwca_export_views_data_export_batch_finished($eid, $output_file, &$context) {
505
	// Fetch export info
506
	$export = views_data_export_get($eid);
507

    
508
	// Perform cleanup
509
	$view = views_data_export_view_retrieve($eid);
510
	$view->set_display($export->view_display_id);
511
	$view->display_handler->batched_execution_state = $export;
512
	$view->display_handler->remove_index();
513

    
514
	// Get path to temp file
515
	$temp_file = $view->display_handler->outputfile_path();
516

    
517
	_dwca_export_archive_descriptor_file_map($output_file, $temp_file);
518

    
519
}
(4-4/6)