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
|
//$view_all_names_except_description = array('classification', 'specimen', 'reference', 'image', 'distribution');
|
11
|
|
12
|
|
13
|
/**
|
14
|
* Implements hook_menu().
|
15
|
*/
|
16
|
function dwca_export_menu() {
|
17
|
|
18
|
$items = array();
|
19
|
|
20
|
$items['admin/config/system/dwca_export'] = array(
|
21
|
'title' => 'Darwin Core Archive export',
|
22
|
'description' => t('Create a DarwinCore Archive of this scratchpad.'),
|
23
|
'page callback' => 'drupal_get_form',
|
24
|
'page arguments' => array('dwca_export_config_form'),
|
25
|
'access arguments' => array('access DwC-A export settings'),
|
26
|
'type' => MENU_NORMAL_ITEM,
|
27
|
//'file' => 'dwca_export.admin.inc'
|
28
|
);
|
29
|
|
30
|
|
31
|
$items['dwca_export'] = array(
|
32
|
'page callback' => 'dwca_export_deliver_archive',
|
33
|
'access arguments' => array('access content'),
|
34
|
'type' => MENU_CALLBACK
|
35
|
);
|
36
|
|
37
|
|
38
|
return $items;
|
39
|
}
|
40
|
|
41
|
function dwca_export_view_names_array(){
|
42
|
|
43
|
$view_names = array('classification', 'specimen', 'reference', 'image', 'distribution');
|
44
|
|
45
|
return $view_names;
|
46
|
}
|
47
|
|
48
|
/**
|
49
|
* Implementation of hook_views_api()
|
50
|
*
|
51
|
* drupal will load dwca_export.views_default.inc when this hook is implemented
|
52
|
*/
|
53
|
function dwca_export_views_api() {
|
54
|
return array(
|
55
|
'api' => 3.0
|
56
|
);
|
57
|
}
|
58
|
|
59
|
/**
|
60
|
* Implementation of hook_ctools_plugin_api().
|
61
|
*/
|
62
|
function dwca_export_ctools_plugin_api(){
|
63
|
list($module, $api) = func_get_args();
|
64
|
if($module == "strongarm" && $api == "strongarm"){
|
65
|
return array(
|
66
|
"version" => 1
|
67
|
);
|
68
|
}
|
69
|
}
|
70
|
|
71
|
/**
|
72
|
* Form function, called by drupal_get_form()
|
73
|
* in dwca_export_menu().
|
74
|
*/
|
75
|
function dwca_export_config_form($form, &$form_state) {
|
76
|
|
77
|
global $base_url;
|
78
|
|
79
|
$form['dwca_export_info'] = array(
|
80
|
'#markup' => '<p>For general information on the DarwinCore Archive format please refer to '
|
81
|
. l('GBIF - Standards and Tools - Darwin Core Archives', 'http://www.gbif.org/informatics/standards-and-tools/publishing-data/data-standards/darwin-core-archives/')
|
82
|
.'</p>'
|
83
|
);
|
84
|
|
85
|
$form['dwca_export_execute'] = array(
|
86
|
'#markup' => '<p>The DarwinCore Archive export is available at '. l('dwca_export', 'dwca_export').'</p>'
|
87
|
//'#type' => 'button',
|
88
|
//'#value' => t('Export Scratchpad to DarwinCore Archive'),
|
89
|
//'#weight' => 19,
|
90
|
);
|
91
|
|
92
|
$form['dwca_export_view_mapping'] = dwca_export_select_view_form();
|
93
|
|
94
|
|
95
|
|
96
|
$form['#submit'][] = 'dwca_export_config_form_submit';
|
97
|
return system_settings_form($form);
|
98
|
|
99
|
}
|
100
|
|
101
|
|
102
|
function dwca_export_select_view_form() {
|
103
|
|
104
|
$views = array(
|
105
|
'#type' => 'fieldset',
|
106
|
'#title' => t('View to file mapping'),
|
107
|
//'#tree' => TRUE,
|
108
|
);
|
109
|
|
110
|
foreach(variable_get(FILE_MAP) as $dwca_filename => $view_data){
|
111
|
|
112
|
if ($dwca_filename == 'description') {
|
113
|
|
114
|
|
115
|
$views[$dwca_filename] = array (
|
116
|
'#type' => 'fieldset',
|
117
|
'#title' => t('DESCRIPTIVE DATA TYPES'),
|
118
|
'#weight' => 3,
|
119
|
'#collapsible' => TRUE,
|
120
|
'#collapsed' => TRUE,
|
121
|
//'#prefix' => '<table>',
|
122
|
//'#suffix' => '</table>',
|
123
|
'#tree' => TRUE,
|
124
|
|
125
|
);
|
126
|
foreach($view_data as $dwca_filename_inner => $view_data_inner){
|
127
|
|
128
|
|
129
|
$views[$dwca_filename][$dwca_filename_inner] = array(
|
130
|
'#type' => 'textfield',
|
131
|
'#title' => t($dwca_filename . '_' . $dwca_filename_inner),
|
132
|
'#default_value' => $view_data_inner['view_name'],
|
133
|
'#size' => 60,
|
134
|
'#description' => t('specify view for ' . $dwca_filename_inner),
|
135
|
//'#prefix' => '<tr>',
|
136
|
//'#suffix' => '</tr>',
|
137
|
|
138
|
);
|
139
|
|
140
|
//TODO: Change collaped discription form to table, layout using <td> and <tr> e.g. try 3 description types per row
|
141
|
|
142
|
}
|
143
|
|
144
|
} else {
|
145
|
$views[$dwca_filename] = array(
|
146
|
'#type' => 'textfield',
|
147
|
'#title' => t($dwca_filename),
|
148
|
'#default_value' => $view_data['view_name'],
|
149
|
'#size' => 60,
|
150
|
'#description' => t('specify view for ' . $dwca_filename),
|
151
|
|
152
|
);
|
153
|
}
|
154
|
}
|
155
|
|
156
|
return $views;
|
157
|
}
|
158
|
|
159
|
function dwca_export_config_form_submit($form, &$form_state) {
|
160
|
|
161
|
$variables = $form_state['input'];
|
162
|
$save_variables = '';
|
163
|
$dwca_export_archive_descriptor_file_map = variable_get(FILE_MAP);
|
164
|
|
165
|
foreach ($variables as $key => $value) {
|
166
|
|
167
|
if (array_key_exists($key, $dwca_export_archive_descriptor_file_map)) {
|
168
|
|
169
|
$dwca_export_archive_descriptor_file_map[$key]['view_name'] = $value;
|
170
|
//$save_variables .= '<p>' . $key . ' ' . $value . '</p>';
|
171
|
} else {
|
172
|
|
173
|
$description_key = 'description';
|
174
|
|
175
|
if (array_key_exists($description_key, $dwca_export_archive_descriptor_file_map)) {
|
176
|
|
177
|
//get the inner array containing the different description data types
|
178
|
$description_map = $dwca_export_archive_descriptor_file_map[$description_key];
|
179
|
|
180
|
if (array_key_exists($key, $description_map)) {
|
181
|
//drupal_set_message(t('The ooKEY is... ') . $key . t('The ooVALUE is... ') . $value);
|
182
|
$dwca_export_archive_descriptor_file_map[$description_key][$key]['view_name'] = $value;
|
183
|
}
|
184
|
}
|
185
|
}
|
186
|
}
|
187
|
|
188
|
variable_del(FILE_MAP);
|
189
|
variable_set(FILE_MAP, $dwca_export_archive_descriptor_file_map);
|
190
|
|
191
|
//drupal_set_message(t('The classification view is ') . $save_variables);// . '<pre>' . print_r($form_state,true) . '</pre>');//$save_variables);
|
192
|
}
|
193
|
|
194
|
|
195
|
|
196
|
|
197
|
// Reports an error if view name entered by the user does not exist in the database.
|
198
|
function dwca_export_config_form_validate($form, &$form_state) {
|
199
|
|
200
|
$variables = $form_state['input'];
|
201
|
$dwca_export_archive_descriptor_file_map = variable_get(FILE_MAP);
|
202
|
$view_names = array();
|
203
|
$missing_view_names = '';
|
204
|
$missing_view = false;
|
205
|
|
206
|
foreach ($variables as $key => $value) {
|
207
|
|
208
|
//TODO: Check whether the views for the inner array cotaining all the description views exist
|
209
|
if (array_key_exists($key, $dwca_export_archive_descriptor_file_map)) {
|
210
|
|
211
|
$view = views_get_view($value);
|
212
|
// check whether there is a view named with this value
|
213
|
if(!$view) {
|
214
|
$view_names[] = $value;
|
215
|
$missing_view_names .= $value . ', ';
|
216
|
$missing_view = true;
|
217
|
}
|
218
|
}
|
219
|
}
|
220
|
|
221
|
if ($missing_view) {
|
222
|
form_set_error('', t('VIEW(S) ' . $missing_view_names . ' NOT FOUND. Please input another view name.'));
|
223
|
}
|
224
|
|
225
|
}
|
226
|
|
227
|
/**
|
228
|
* menu callback
|
229
|
*/
|
230
|
function dwca_export_deliver_archive() {
|
231
|
|
232
|
$tmp_archive_file_name = dwca_export_create_archive( _dwca_export_archive_descriptor_file_map() );
|
233
|
|
234
|
if($tmp_archive_file_name && file_valid_uri($tmp_archive_file_name)){
|
235
|
file_transfer($tmp_archive_file_name, array('Content-Type' => 'application/zip'));
|
236
|
} else {
|
237
|
throw new Exception(t('Error creating the archive'));
|
238
|
}
|
239
|
}
|
240
|
|
241
|
|
242
|
/**
|
243
|
* Provides the archive_descriptor_file_map which maps dwca file name to a set of view information.
|
244
|
* The view information contains the fields 'view_name', 'display_id', 'out_file_url'.
|
245
|
* The 'out_file_url' is initailly empty and will be set when this function is called
|
246
|
* with both parameters.
|
247
|
*
|
248
|
* @param String $file_name
|
249
|
* @param String $out_file_url
|
250
|
*
|
251
|
* @return the archive_descriptor_file_map
|
252
|
*/
|
253
|
function _dwca_export_archive_descriptor_file_map($file_name = NULL, $out_file_url = null){
|
254
|
static $file_map;
|
255
|
|
256
|
if(!isset($file_map)){
|
257
|
$file_map = variable_get(FILE_MAP);
|
258
|
}
|
259
|
|
260
|
if($file_name && $out_file_url){
|
261
|
|
262
|
if (in_array($file_name, dwca_export_view_names_array())) {
|
263
|
|
264
|
$file_map[$file_name]['out_file_url'] = $out_file_url;
|
265
|
|
266
|
} else {
|
267
|
|
268
|
$file_map['description'][$file_name]['out_file_url'] = $out_file_url;
|
269
|
}
|
270
|
}
|
271
|
|
272
|
return $file_map;
|
273
|
}
|
274
|
|
275
|
|
276
|
/**
|
277
|
* Walks all view export paths defined in the $views_map.
|
278
|
* Each file is downloaded to the tmp folder and a zip file
|
279
|
* is bundeled of the resulting csv files plus the meta file.
|
280
|
*
|
281
|
* @param $views_map - maps a view paths to dwca filenames
|
282
|
*
|
283
|
* @return the path in the filesystem to the final archive,
|
284
|
* or FALSE in case of an error.
|
285
|
*/
|
286
|
function dwca_export_create_archive($views_map) {
|
287
|
|
288
|
// all data is exported to temporary://dwca_export_*
|
289
|
// now we can start bundling the actual archive
|
290
|
$tmp_archive_file_name = drupal_tempnam("temporary://", "dwca_export_archive_");
|
291
|
|
292
|
// Unfortunately we cannot use drupals ArchiverZip because there ís
|
293
|
// no way to pass in ZipArchive::CREATE to the constructor to create the archive
|
294
|
// TODO test if zip functionality is available (i.e. if(function_exists('zip_open'))
|
295
|
// but I don't know where the proper location for such a check would be
|
296
|
$zip = new ZipArchive();
|
297
|
// it is safe to use drupal_realpath as the tmp file will be certainly local
|
298
|
// and php's ZipArchive does not handle stream URIs
|
299
|
$result = $zip->open(drupal_realpath($tmp_archive_file_name), ZipArchive::CREATE);
|
300
|
|
301
|
// there might be a better way to get at this information
|
302
|
$module_static_dir_absolute_path = realpath(drupal_get_path('module', 'dwca_export')) . STATIC_DIR;
|
303
|
|
304
|
if ($result !== TRUE) {
|
305
|
throw new Exception(t('Could not create zip_archive %tmp_archive_file_name', array('%tmp_archive_file_name' => $tmp_archive_file_name)));
|
306
|
}
|
307
|
|
308
|
|
309
|
// add metadata
|
310
|
$zip->addFile($module_static_dir_absolute_path.METADATA_FILE_NAME, ARCHIVE_ROOT_DIR.METADATA_FILE_NAME);
|
311
|
|
312
|
global $base_url;
|
313
|
|
314
|
// execute all views to export the data into
|
315
|
// temporary csv files (temporary://dwca_export_*). the resulting filenames
|
316
|
// will be stored in _dwca_export_archive_descriptor_file_map()
|
317
|
foreach($views_map as $filename=>$view_data){
|
318
|
|
319
|
//lorna
|
320
|
if ($filename == 'description') {
|
321
|
|
322
|
//lorna: this foreach iterates through all the description types in the inner array
|
323
|
foreach($view_data as $filename_inner => $view_data_inner){
|
324
|
$view = views_get_view($view_data_inner['view_name']);
|
325
|
|
326
|
$options = array (
|
327
|
'output_file' => $filename_inner
|
328
|
);
|
329
|
|
330
|
_dwca_export_views_data_export_override_batch($view, $view_data_inner['display_id'], $options);
|
331
|
|
332
|
if ($view) {
|
333
|
$view->execute_display($view_data_inner['display_id']);
|
334
|
}else{
|
335
|
throw new Exception(t('A view with the name %view does not exist', array('%view' => $view_data_inner['view_name'])));
|
336
|
}
|
337
|
}
|
338
|
|
339
|
}
|
340
|
else {
|
341
|
$view = views_get_view($view_data['view_name']);
|
342
|
$options = array (
|
343
|
'output_file' => $filename
|
344
|
);
|
345
|
_dwca_export_views_data_export_override_batch($view, $view_data['display_id'], $options);
|
346
|
|
347
|
if ($view) {
|
348
|
$view->execute_display($view_data['display_id']);
|
349
|
} else{
|
350
|
throw new Exception(t('A view with the name %view does not exist', array('%view' => $view_data['view_name'])));
|
351
|
}
|
352
|
}
|
353
|
//end lorna
|
354
|
|
355
|
}
|
356
|
|
357
|
// add the csv data files
|
358
|
foreach(_dwca_export_archive_descriptor_file_map() as $dwca_filename=>$view_data){
|
359
|
|
360
|
if ($dwca_filename == 'description') {
|
361
|
|
362
|
_dwca_export_concatenate_description_files($view_data, $zip);
|
363
|
|
364
|
} else {
|
365
|
|
366
|
$view_temp_file = $view_data['out_file_url'];
|
367
|
_dwca_export_add_files_to_zip($view_temp_file, $dwca_filename, $zip);
|
368
|
}
|
369
|
|
370
|
}
|
371
|
|
372
|
$zip->close();
|
373
|
|
374
|
return $tmp_archive_file_name;
|
375
|
}
|
376
|
|
377
|
/**
|
378
|
*
|
379
|
* Combines multiple Description Views for different description types into a single text file
|
380
|
* @param $view_data The inner array containing the view data for the different description types
|
381
|
* @param $zip The zip file for the files making up the DwC-A.
|
382
|
*/
|
383
|
function _dwca_export_concatenate_description_files($view_data, $zip){
|
384
|
|
385
|
$desc_file_name = drupal_tempnam("temporary://", "description.tmp");
|
386
|
$desc_file = fopen(drupal_realpath($desc_file_name), "w");
|
387
|
|
388
|
foreach($view_data as $type=>$view_data_inner){
|
389
|
|
390
|
$view_temp_file = $view_data_inner['out_file_url'];
|
391
|
|
392
|
$descTypeFile = fopen($view_temp_file, "r");
|
393
|
|
394
|
while(!feof($descTypeFile)){
|
395
|
$lineOfText = fgets($descTypeFile, 4096);
|
396
|
|
397
|
$items = explode(',', $lineOfText);
|
398
|
|
399
|
//don't include empty descriptions or lines at the end of the view which start with the field name id
|
400
|
//some lines just contain a single " if there is a carriage return within the description text so remove these too
|
401
|
|
402
|
if (isset($items[0]) && isset($items[2])) {
|
403
|
|
404
|
if ($items[0]!=="\"id\"" && trim($items[2])!=="\"\"") {
|
405
|
|
406
|
// fix conflict with files that have an EOL that differs from the server EOL
|
407
|
$lineOfText = rtrim($lineOfText, "\r\n") . PHP_EOL;
|
408
|
fwrite($desc_file, $lineOfText);
|
409
|
|
410
|
}
|
411
|
|
412
|
} else {
|
413
|
fgets($descTypeFile, 4096);
|
414
|
}
|
415
|
|
416
|
}
|
417
|
|
418
|
fclose($descTypeFile);
|
419
|
}
|
420
|
fclose($desc_file);
|
421
|
|
422
|
_dwca_export_add_files_to_zip($desc_file_name, 'description', $zip);
|
423
|
}
|
424
|
|
425
|
function _dwca_export_add_files_to_zip($view_temp_file = NULL, $dwca_filename, $zip){
|
426
|
|
427
|
if($view_temp_file){
|
428
|
$zip->addFile(drupal_realpath($view_temp_file), ARCHIVE_ROOT_DIR.$dwca_filename . FILE_EXTENSION);
|
429
|
}else{
|
430
|
throw new Exception(t('Cannot create %file', array('%file' => $dwca_filename)));
|
431
|
}
|
432
|
}
|
433
|
|
434
|
/**
|
435
|
* Helper function that indicates that we want to
|
436
|
* override the batch that the views_data_export view creates
|
437
|
* on it's initial time through.
|
438
|
*
|
439
|
* Also provides a place to stash options that need to stay around
|
440
|
* until the end of the batch
|
441
|
*
|
442
|
* adapted fom views_data_export.drush.inc
|
443
|
*/
|
444
|
function _dwca_export_views_data_export_override_batch($view = NULL, $display = NULL, $options = TRUE) {
|
445
|
|
446
|
static $_views;
|
447
|
if (isset($view)) {
|
448
|
|
449
|
$_views[$view->name][$display] = $options;
|
450
|
}
|
451
|
return $_views;
|
452
|
}
|
453
|
|
454
|
|
455
|
/**
|
456
|
* Implementation of hook_views_data_export_batch_alter()
|
457
|
*
|
458
|
* adapted fom views_data_export.drush.inc
|
459
|
*
|
460
|
* @see batch_process() in form.inc
|
461
|
*/
|
462
|
function dwca_export_views_data_export_batch_alter(&$batch, &$final_destination, &$querystring) {
|
463
|
|
464
|
$view_name = $batch['view_name'];
|
465
|
$display_id = $batch['display_id'];
|
466
|
|
467
|
$ok_to_override = _dwca_export_views_data_export_override_batch();
|
468
|
|
469
|
// Make sure we do nothing if we are called not following the execution of
|
470
|
// our drush command. This could happen if the file with this function in it
|
471
|
// is included during the normal execution of the view
|
472
|
if (!$ok_to_override[$view_name][$display_id]) {
|
473
|
return;
|
474
|
}
|
475
|
|
476
|
$options = $ok_to_override[$view_name][$display_id];
|
477
|
|
478
|
// We actually never return from the drupal_alter, but
|
479
|
// use drush's batch system to run the same batch
|
480
|
|
481
|
// Add a final callback
|
482
|
$batch['operations'][] = array(
|
483
|
'_dwca_export_views_data_export_batch_finished', array($batch['eid'], $options['output_file']),
|
484
|
);
|
485
|
|
486
|
$batch['progressive'] = FALSE;
|
487
|
}
|
488
|
|
489
|
/**
|
490
|
* Implementation of hook_views_data_export_batch_alter()
|
491
|
*
|
492
|
* @see batch_process() in form.inc
|
493
|
*/
|
494
|
function dwca_export_batch_alter(&$batch, &$final_destination, &$querystring) {
|
495
|
|
496
|
if($batch['source_url'] == 'dwca_export'){
|
497
|
$batch['progressive'] = FALSE;
|
498
|
}
|
499
|
}
|
500
|
|
501
|
|
502
|
|
503
|
/**
|
504
|
* Get's called at the end of the drush batch process that generated our export
|
505
|
*
|
506
|
* adapted fom views_data_export.drush.inc
|
507
|
*/
|
508
|
function _dwca_export_views_data_export_batch_finished($eid, $output_file, &$context) {
|
509
|
// Fetch export info
|
510
|
$export = views_data_export_get($eid);
|
511
|
|
512
|
// Perform cleanup
|
513
|
$view = views_data_export_view_retrieve($eid);
|
514
|
$view->set_display($export->view_display_id);
|
515
|
$view->display_handler->batched_execution_state = $export;
|
516
|
$view->display_handler->remove_index();
|
517
|
|
518
|
// Get path to temp file
|
519
|
$temp_file = $view->display_handler->outputfile_path();
|
520
|
|
521
|
_dwca_export_archive_descriptor_file_map($output_file, $temp_file);
|
522
|
|
523
|
}
|
524
|
|
525
|
function dwca_export_permission() {
|
526
|
return array(
|
527
|
'administer dwca-export' => array(
|
528
|
'title' => t('Access dwca-export'),
|
529
|
'description' => t('Allows user to access DwC-A export configuration page and execute export of the DwC-A data.'),
|
530
|
));
|
531
|
}
|