From 1b09ae2801177dd4b732b4334712d28c6ac060ac Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Wed, 25 Apr 2012 19:26:40 +0200 Subject: [PATCH] Finish attachment handling and display for events --- plugins/calendar/calendar.php | 23 ++++---- plugins/calendar/calendar_ui.js | 4 +- .../calendar/drivers/kolab/kolab_calendar.php | 53 +++++++++---------- .../calendar/drivers/kolab/kolab_driver.php | 17 +++--- plugins/calendar/lib/calendar_ui.php | 6 +-- plugins/calendar/skins/larry/calendar.css | 40 ++++++++------ .../skins/larry/templates/attachment.html | 2 +- .../lib/rcube_kolab_contacts.php | 4 +- plugins/libkolab/lib/kolab_format_event.php | 29 +++++++++- plugins/libkolab/lib/kolab_storage_folder.php | 24 ++++----- 10 files changed, 117 insertions(+), 85 deletions(-) diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php index 0e61615f..05cee6c8 100644 --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -1069,6 +1069,11 @@ class calendar extends rcube_plugin $settings['identity'] = array('name' => $identity['name'], 'email' => $identity['email'], 'emails' => ';' . join(';', $identity['emails'])); } + // define list of file types which can be displayed inline + // same as in program/steps/mail/show.inc + $mimetypes = $this->rc->config->get('client_mimetypes', 'text/plain,text/html,text/xml,image/jpeg,image/gif,image/png,application/x-javascript,application/pdf,application/x-shockwave-flash'); + $settings['mimetypes'] = is_string($mimetypes) ? explode(',', $mimetypes) : (array)$mimetypes; + return $settings; } @@ -1534,12 +1539,8 @@ class calendar extends rcube_plugin } ob_end_clean(); - send_nocacheing_headers(); - if (isset($_SESSION['calendar_attachment'])) - $attachment = $_SESSION['calendar_attachment']; - else - $attachment = $_SESSION['calendar_attachment'] = $this->driver->get_attachment($id, $event); + $attachment = $GLOBALS['calendar_attachment'] = $this->driver->get_attachment($id, $event); // show part page if (!empty($_GET['_frame'])) { @@ -1550,12 +1551,16 @@ class calendar extends rcube_plugin exit; } - $this->rc->session->remove('calendar_attachment'); - if ($attachment) { $mimetype = strtolower($attachment['mimetype']); list($ctype_primary, $ctype_secondary) = explode('/', $mimetype); + $body = $this->driver->get_attachment_body($id, $event); + + // TODO: allow post-processing of the attachment body + //$plugin = $RCMAIL->plugins->exec_hook('message_part_get', + // array('uid' => $MESSAGE->uid, 'id' => $part->mime_id, 'mimetype' => $mimetype, 'part' => $part, 'download' => !empty($_GET['_download']))); + $browser = $this->rc->output->browser; // send download headers @@ -1573,8 +1578,6 @@ class calendar extends rcube_plugin header("Content-Transfer-Encoding: binary"); } - $body = $this->driver->get_attachment_body($id, $event); - // display page, @TODO: support text/plain (and maybe some other text formats) if ($mimetype == 'text/html' && empty($_GET['_download'])) { $OUTPUT = new rcube_html_page(); @@ -1614,7 +1617,7 @@ class calendar extends rcube_plugin */ public function attachment_frame($attrib) { - $attachment = $_SESSION['calendar_attachment']; + $attachment = $GLOBALS['calendar_attachment']; $mimetype = strtolower($attachment['mimetype']); list($ctype_primary, $ctype_secondary) = explode('/', $mimetype); diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js index f7683cff..063cbf5a 100644 --- a/plugins/calendar/calendar_ui.js +++ b/plugins/calendar/calendar_ui.js @@ -260,7 +260,7 @@ function rcube_calendar_ui(settings) var qstring = '_id='+urlencode(att.id)+'&_event='+urlencode(event.recurrence_id||event.id)+'&_cal='+urlencode(event.calendar); // open attachment in frame if it's of a supported mimetype - if (id && att.mimetype && $.inArray(att.mimetype, rcmail.mimetypes)>=0) { + if (id && att.mimetype && $.inArray(att.mimetype, settings.mimetypes)>=0) { rcmail.attachment_win = window.open(rcmail.env.comm_path+'&_action=get-attachment&'+qstring+'&_frame=1', 'rcubeeventattachment'); if (rcmail.attachment_win) { window.setTimeout(function() { rcmail.attachment_win.focus(); }, 10); @@ -268,6 +268,8 @@ function rcube_calendar_ui(settings) } } + return; + rcmail.goto_url('get-attachment', qstring+'&_download=1', false); }; diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php index 3a2a02af..77631591 100644 --- a/plugins/calendar/drivers/kolab/kolab_calendar.php +++ b/plugins/calendar/drivers/kolab/kolab_calendar.php @@ -169,14 +169,6 @@ class kolab_calendar return $this->storage; } - /** - * Getter for the attachment body - */ - public function get_attachment_body($id) - { - return $this->storage->getAttachment($id); - } - /** * Getter for a single event object @@ -427,15 +419,15 @@ class kolab_calendar if ($record['end'] <= $record['start'] && $record['allday']) $record['end'] = $record['start'] + 3600; - if (!empty($rec['_attachments'])) { - foreach ($rec['_attachments'] as $name => $attachment) { - // @TODO: 'type' and 'key' are the only supported (no 'size') - $attachments[] = array( - 'id' => $attachment['key'], - 'mimetype' => $attachment['type'], - 'name' => $name, - ); + if (!empty($record['_attachments'])) { + foreach ($record['_attachments'] as $name => $attachment) { + if ($attachment !== false) { + $attachment['name'] = $name; + $attachments[] = $attachment; + } } + + $record['attachments'] = $attachments; } $sensitivity_map = array_flip($this->sensitivity_map); @@ -461,28 +453,31 @@ class kolab_calendar // in Horde attachments are indexed by name $object['_attachments'] = array(); - if (!empty($event['attachments'])) { + if (is_array($event['attachments'])) { $collisions = array(); foreach ($event['attachments'] as $idx => $attachment) { // Roundcube ID has nothing to do with Horde ID, remove it if ($attachment['content']) unset($attachment['id']); - // Horde code assumes that there will be no more than - // one file with the same name: make filenames unique - $filename = $attachment['name']; - if ($collisions[$filename]++) { - $ext = preg_match('/(\.[a-z0-9]{1,6})$/i', $filename, $m) ? $m[1] : null; - $attachment['name'] = basename($filename, $ext) . '-' . $collisions[$filename] . $ext; + // flagged for deletion => set to false + if ($attachment['_deleted']) { + $object['_attachments'][$attachment['name']] = false; } + else { + // Horde code assumes that there will be no more than + // one file with the same name: make filenames unique + $filename = $attachment['name']; + if ($collisions[$filename]++) { + $ext = preg_match('/(\.[a-z0-9]{1,6})$/i', $filename, $m) ? $m[1] : null; + $attachment['name'] = basename($filename, $ext) . '-' . $collisions[$filename] . $ext; + } - // set type parameter - if ($attachment['mimetype']) - $attachment['type'] = $attachment['mimetype']; - - $object['_attachments'][$attachment['name']] = $attachment; - unset($event['attachments'][$idx]); + $object['_attachments'][$attachment['name']] = $attachment; + } } + + unset($event['attachments']); } // translate sensitivity property diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php index 7744dc03..aea6783c 100644 --- a/plugins/calendar/drivers/kolab/kolab_driver.php +++ b/plugins/calendar/drivers/kolab/kolab_driver.php @@ -579,7 +579,7 @@ class kolab_driver extends calendar_driver if (!empty($old['attachments'])) { foreach ($old['attachments'] as $idx => $att) { if ($att['id'] == $attachment) { - unset($old['attachments'][$idx]); + $old['attachments'][$idx]['_deleted'] = true; } } } @@ -592,12 +592,12 @@ class kolab_driver extends calendar_driver // skip entries without content (could be existing ones) if (!$attachment['data'] && !$attachment['path']) continue; - // we'll read file contacts into memory, Horde/Kolab classes does the same - // So we cannot save memory, rcube_imap class can do this better + $attachments[] = array( 'name' => $attachment['name'], - 'type' => $attachment['mimetype'], - 'content' => $attachment['data'] ? $attachment['data'] : file_get_contents($attachment['path']), + 'mimetype' => $attachment['mimetype'], + 'content' => $attachment['data'], + 'path' => $attachment['path'], ); } } @@ -625,7 +625,7 @@ class kolab_driver extends calendar_driver // copy attachment data to new event foreach ((array)$event['attachments'] as $idx => $attachment) { if (!$attachment['data']) - $attachment['data'] = $fromcalendar->get_attachment_body($attachment['id']); + $attachment['data'] = $fromcalendar->get_attachment_body($attachment['id'], $event); } $success = $storage->insert_event($event); @@ -875,13 +875,14 @@ class kolab_driver extends calendar_driver /** * Get attachment body + * @see calendar_driver::get_attachment_body() */ public function get_attachment_body($id, $event) { - if (!($storage = $this->calendars[$event['calendar']])) + if (!($cal = $this->calendars[$event['calendar']])) return false; - return $storage->get_attachment_body($id); + return $cal->storage->get_attachment($event['id'], $id); } /** diff --git a/plugins/calendar/lib/calendar_ui.php b/plugins/calendar/lib/calendar_ui.php index c740e4aa..ba8cc83d 100644 --- a/plugins/calendar/lib/calendar_ui.php +++ b/plugins/calendar/lib/calendar_ui.php @@ -657,13 +657,13 @@ class calendar_ui if (!empty($this->cal->attachment['name'])) { $table->add('title', Q(rcube_label('filename'))); - $table->add(null, Q($this->cal->attachment['name'])); - $table->add(null, '[' . html::a('?'.str_replace('_frame=', '_download=', $_SERVER['QUERY_STRING']), Q(rcube_label('download'))) . ']'); + $table->add('header', Q($this->cal->attachment['name'])); + $table->add('download-link', html::a('?'.str_replace('_frame=', '_download=', $_SERVER['QUERY_STRING']), Q(rcube_label('download')))); } if (!empty($this->cal->attachment['size'])) { $table->add('title', Q(rcube_label('filesize'))); - $table->add(null, Q(show_bytes($this->cal->attachment['size']))); + $table->add('header', Q(show_bytes($this->cal->attachment['size']))); } return $table->show($attrib); diff --git a/plugins/calendar/skins/larry/calendar.css b/plugins/calendar/skins/larry/calendar.css index a95d0fcb..cba1149c 100644 --- a/plugins/calendar/skins/larry/calendar.css +++ b/plugins/calendar/skins/larry/calendar.css @@ -286,39 +286,45 @@ a.miniColors-trigger { #attachmentcontainer { position: absolute; - top: 80px; - left: 20px; - right: 20px; - bottom: 20px; + top: 60px; + left: 0px; + right: 0px; + bottom: 0px; } #attachmentframe { width: 100%; height: 100%; - border: 1px solid #999999; - background-color: #F9F9F9; + border: 0; + background-color: #fff; + border-radius: 4px; } #partheader { - position: absolute; - top: 20px; - left: 220px; - right: 20px; - height: 40px; + position: relative; + padding: 3px 0; + background: #f9f9f9; + background: -moz-linear-gradient(top, #fff 0%, #e9e9e9 100%); + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#fff), color-stop(100%,#e9e9e9)); + background: -o-linear-gradient(top, #fff 0%, #e9e9e9 100%); + background: -ms-linear-gradient(top, #fff 0%, #e9e9e9 100%); + background: linear-gradient(top, #fff 0%, #e9e9e9 100%); } #partheader table td { - padding-left: 2px; - padding-right: 4px; - vertical-align: middle; - font-size: 11px; + color: #666; + padding: 2px 8px; } -#partheader table td.title { - color: #666; +#partheader table td.header { font-weight: bold; } +#partheader table td.title a { + color: #666; + text-decoration: none; +} + #edit-attachments { margin-top: 0.6em; } diff --git a/plugins/calendar/skins/larry/templates/attachment.html b/plugins/calendar/skins/larry/templates/attachment.html index 439afd40..4d4789da 100644 --- a/plugins/calendar/skins/larry/templates/attachment.html +++ b/plugins/calendar/skins/larry/templates/attachment.html @@ -26,7 +26,7 @@
- +
diff --git a/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php b/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php index ebfa29a7..7695402a 100644 --- a/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php +++ b/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php @@ -1057,7 +1057,7 @@ class rcube_kolab_contacts extends rcube_addressbook if ($record['photo'] && strlen($record['photo']) < 255 && ($att = $record['_attachments'][$record['photo']])) { // only fetch photo content if requested if ($this->action == 'photo') - $record['photo'] = $att['content'] ? $att['content'] : $this->storagefolder->get_attachment($record['uid'], $att['key']); + $record['photo'] = $att['content'] ? $att['content'] : $this->storagefolder->get_attachment($record['uid'], $att['id']); } // truncate publickey value for display @@ -1123,7 +1123,7 @@ class rcube_kolab_contacts extends rcube_addressbook if ($contact['photo']) { $attkey = 'photo.attachment'; $contact['_attachments'][$attkey] = array( - 'type' => rc_image_content_type($contact['photo']), + 'mimetype' => rc_image_content_type($contact['photo']), 'content' => preg_match('![^a-z0-9/=+-]!i', $contact['photo']) ? $contact['photo'] : base64_decode($contact['photo']), ); $contact['photo'] = $attkey; diff --git a/plugins/libkolab/lib/kolab_format_event.php b/plugins/libkolab/lib/kolab_format_event.php index 97718ef4..78e51ca4 100644 --- a/plugins/libkolab/lib/kolab_format_event.php +++ b/plugins/libkolab/lib/kolab_format_event.php @@ -280,7 +280,17 @@ class kolab_format_event extends kolab_format } $this->obj->setAlarms($valarms); - // TODO: save attachments + // save attachments + $vattach = new vectorattachment; + foreach ((array)$object['_attachments'] as $name => $attr) { + if (empty($attr)) + continue; + $attach = new Attachment; + $attach->setLabel($name); + $attach->setUri('cid:' . $name, $attr['mimetype']); + $vattach->push($attach); + } + $this->obj->setAttachments($vattach); // cache this data unset($object['_formatobj']); @@ -425,7 +435,22 @@ class kolab_format_event extends kolab_format } } - // TODO: handle attachments + // handle attachments + $vattach = $this->obj->attachments(); + for ($i=0; $i < $vattach->size(); $i++) { + $attach = $vattach->get($i); + + // skip cid: attachments which are mime message parts handled by kolab_storage_folder + if (substr($attach->uri(), 0, 4) != 'cid') { + $name = $attach->label(); + $data = $attach->data(); + $object['_attachments'][$name] = array( + 'mimetype' => $attach->mimetype(), + 'size' => strlen($data), + 'content' => $data, + ); + } + } $this->data = $object; return $this->data; diff --git a/plugins/libkolab/lib/kolab_storage_folder.php b/plugins/libkolab/lib/kolab_storage_folder.php index 6a010e55..73b6dbba 100644 --- a/plugins/libkolab/lib/kolab_storage_folder.php +++ b/plugins/libkolab/lib/kolab_storage_folder.php @@ -350,8 +350,8 @@ class kolab_storage_folder } else if ($part->filename) { $attachments[$part->filename] = array( - 'key' => $part->mime_id, - 'type' => $part->mimetype, + 'id' => $part->mime_id, + 'mimetype' => $part->mimetype, 'size' => $part->size, ); } @@ -388,13 +388,10 @@ class kolab_storage_folder } if ($format->is_valid()) { - if ($formatobj) - return $format; - $object = $format->to_array(); $object['_msguid'] = $msguid; $object['_mailbox'] = $this->name; - $object['_attachments'] = $attachments; + $object['_attachments'] = array_merge((array)$object['_attachments'], $attachments); $object['_formatobj'] = $format; $this->objcache[$msguid] = $object; @@ -425,8 +422,8 @@ class kolab_storage_folder $object['_attachments'][$name] = $old['_attachments'][$name]; } // load photo.attachment from old Kolab2 format to be directly embedded in xcard block - if ($name == 'photo.attachment' && !isset($object['photo']) && !$object['_attachments'][$name]['content'] && $att['key']) { - $object['photo'] = $this->get_attachment($object['_msguid'], $att['key'], $object['_mailbox']); + if ($name == 'photo.attachment' && !isset($object['photo']) && !$object['_attachments'][$name]['content'] && $att['id']) { + $object['photo'] = $this->get_attachment($object['_msguid'], $att['id'], $object['_mailbox']); unset($object['_attachments'][$name]); } } @@ -601,12 +598,15 @@ class kolab_storage_folder // save object attachments as separate parts // TODO: optimize memory consumption by using tempfiles for transfer foreach ((array)$object['_attachments'] as $name => $att) { - if (empty($att['content']) && !empty($att['key'])) { + if (empty($att['content']) && !empty($att['id'])) { $msguid = !empty($object['_msguid']) ? $object['_msguid'] : $object['uid']; - $att['content'] = $this->get_attachment($msguid, $att['key'], $object['_mailbox']); + $att['content'] = $this->get_attachment($msguid, $att['id'], $object['_mailbox']); } if (!empty($att['content'])) { - $mime->addAttachment($att['content'], $att['type'], $name, false); + $mime->addAttachment($att['content'], $att['mimetype'], $name, false); + } + else if (!empty($att['path'])) { + $mime->addAttachment($att['path'], $att['mimetype'], $name, true); } } @@ -681,7 +681,7 @@ class kolab_storage_folder */ public function getOwner() { - console("Call to deprecated method kolab_storage_folder::getOwner()"); + PEAR::raiseError("Call to deprecated method kolab_storage_folder::getOwner()"); return $this->get_owner(); }