diff --git a/plugins/csv_export/csv_export.js b/plugins/csv_export/csv_export.js --- a/plugins/csv_export/csv_export.js +++ b/plugins/csv_export/csv_export.js @@ -25,10 +25,24 @@ * for the JavaScript code in this file. */ -if (window.rcmail) { - rcmail.addEventListener('beforeexport', function(e) { return csv_export_dialog(e, 'export'); }) - .addEventListener('beforeexport-selected', function(e) { return csv_export_dialog(e, 'export-selected'); }); -} +window.rcmail && rcmail.addEventListener('init', function() { + if (rcmail.task == 'addressbook') { + rcmail.addEventListener('beforeexport', function(e) { return csv_export_dialog(e, 'export'); }) + .addEventListener('beforeexport-selected', function(e) { return csv_export_dialog(e, 'export-selected'); }); + } + // for tasks export we already have dialog, add format selector there + else if (rcmail.task == 'tasks') { + var options = [ + $('').attr({value: 'itip', selected: true}).text('iCal'), + $('').attr({value: 'csv'}).text('csv') + ], + entry = $('').attr('class', 'form-section') + .append($('').attr('for', 'tasks-export-form-format').text(rcmail.get_label('csv_export.format'))) + .append($('').attr({name: '_format', id: 'tasks-export-form-format'}).append(options)); + + $('#tasks-export-form').append(entry); + } +}); // Display dialog with format selection function csv_export_dialog(event, action) @@ -90,7 +104,12 @@ return; } - var params = {_source: rcmail.env.source, _gid: rcmail.env.group, _format: 'csv'}; + var params = { + _source: rcmail.env.source, + _gid: rcmail.env.group, + _format: 'csv', + _token: rcmail.env.request_token + }; if (action == 'export') { params._search = rcmail.env.search_request; diff --git a/plugins/csv_export/csv_export.php b/plugins/csv_export/csv_export.php --- a/plugins/csv_export/csv_export.php +++ b/plugins/csv_export/csv_export.php @@ -24,7 +24,7 @@ class csv_export extends rcube_plugin { - public $task = 'addressbook'; + public $task = 'addressbook|tasks'; /** @@ -36,9 +36,10 @@ // register hooks $this->add_hook('addressbook_export', array($this, 'addressbook_export')); + $this->add_hook('tasks_export', array($this, 'tasks_export')); // Add localization and js script - if ($this->api->output->type == 'html' && !$this->rcmail->action) { + if ($this->api->output->type == 'html' && !$rcmail->action) { $this->add_texts('localization', true); $this->api->output->add_label('export', 'cancel'); $this->include_script('csv_export.js'); @@ -62,7 +63,10 @@ require_once(__DIR__ . '/vcard2csv.php'); - $csv = new vcard2csv; + $csv = new vcard2csv; + $rcmail = rcube::get_instance(); + + $rcmail->output->nocacheing_headers(); // send downlaod headers $csv->headers(); @@ -83,4 +87,40 @@ exit; } + + /** + * Handler for the tasks_export hook. + * + * @param array $p Hash array with hook parameters + * + * @return array Hash array with modified hook parameters + */ + public function tasks_export($p) + { + if ($_GET['_format'] != 'csv' && $_POST['_format'] != 'csv') { + return $p; + } + + require_once(__DIR__ . '/event2csv.php'); + + $csv = new event2csv; + $rcmail = rcube::get_instance(); + + $rcmail->output->nocacheing_headers(); + + // don't kill the connection if download takes more than 30 sec. + @set_time_limit(300); + + // send downlaod headers + $csv->headers(preg_replace('/\.ics$/', '.csv', $p['filename'])); + + // sent format line + echo $csv->head(); + + foreach ((array) $p['result'] as $record) { + echo $csv->record($record); + } + + exit; + } } diff --git a/plugins/csv_export/event2csv.php b/plugins/csv_export/event2csv.php new file mode 100644 --- /dev/null +++ b/plugins/csv_export/event2csv.php @@ -0,0 +1,148 @@ + + * + * Copyright (C) 2011-2016, Kolab Systems AG + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +class event2csv +{ + /** + * CSV label to text mapping for English + * + * @var array + */ + protected $fields = array( + 'uid' => "UID", + 'title' => "Title", + 'description' => "Description", + 'start' => "Start", + 'due' => "Due", + 'organizer_name' => "Organizer Name", + 'organizer_email' => "Organizer Email", + ); + + + /** + * Convert task/event to CSV record + * + * @param string $event Event data + * + * @return string CSV record + */ + public function record($event) + { + $csv = array(); + + foreach (array_keys($this->fields) as $key) { + $value = $event[$key]; + + switch ($key) { + case 'start': + case 'due': + if ($value) { + $value = $value->format($value->_dateonly ? 'Y-m-d' : 'Y-m-d H:i:s'); + } + break; + + case 'organizer_email': + $value = $event['organizer']['email']; + break; + + case 'organizer_name': + $value = $event['organizer']['name']; + break; + } + + $csv[] = $value; + } + + return $this->csv($csv); + } + + /** + * Build csv data header (list of field names) + * + * @return string CSV file header + */ + public function head() + { + return $this->csv($this->fields); + } + + /** + * Send headers of file download + */ + public static function headers($filename = 'tasks.csv') + { + // send downlaod headers + header('Content-Type: text/csv; charset=' . RCUBE_CHARSET); + header('Content-Disposition: attachment; filename="' . $filename . '"'); + } + + /** + * Creates CSV record + */ + protected function csv($fields = array(), $delimiter = ',', $enclosure = '"') + { + $str = ''; + $escape_char = "\\"; + + foreach ($fields as $value) { + if (strpos($value, $delimiter) !== false + || strpos($value, $enclosure) !== false + || strpos($value, ' ') !== false + || strpos($value, "\n") !== false + || strpos($value, "\r") !== false + || strpos($value, "\t") !== false + ) { + $str2 = $enclosure; + $escaped = 0; + $len = strlen($value); + + for ($i=0; $i<$len; $i++) { + if ($value[$i] == $escape_char) { + $escaped = 1; + } + else if (!$escaped && $value[$i] == $enclosure) { + $str2 .= $enclosure; + } + else { + $escaped = 0; + } + + $str2 .= $value[$i]; + } + + $str2 .= $enclosure; + $str .= $str2 . $delimiter; + } + else { + $str .= $value . $delimiter; + } + } + + if (!empty($fields)) { + $str[strlen($str)-1] = "\n"; + } + + return $str; + } +} diff --git a/plugins/csv_export/localization/en_US.inc b/plugins/csv_export/localization/en_US.inc --- a/plugins/csv_export/localization/en_US.inc +++ b/plugins/csv_export/localization/en_US.inc @@ -13,3 +13,4 @@ $labels['text'] = 'Please, choose the export file format:'; $labels['csv'] = 'CSV (comma-separated)'; $labels['vcf'] = 'vCard'; +$labels['format'] = 'Format'; diff --git a/plugins/csv_export/vcard2csv.php b/plugins/csv_export/vcard2csv.php --- a/plugins/csv_export/vcard2csv.php +++ b/plugins/csv_export/vcard2csv.php @@ -158,11 +158,11 @@ /** * Send headers of file download */ - public static function headers() + public static function headers($filename = 'contacts.csv') { // send downlaod headers header('Content-Type: text/csv; charset=' . RCUBE_CHARSET); - header('Content-Disposition: attachment; filename="contacts.csv"'); + header('Content-Disposition: attachment; filename="' . $filename . '"'); } protected function csv($fields = array(), $delimiter = ',', $enclosure = '"')