Add support for task attachments (#895)

This commit is contained in:
Thomas Bruederli 2012-08-01 15:52:28 +02:00
parent a16a9c5810
commit 05a92b0b92
11 changed files with 775 additions and 44 deletions

View file

@ -26,7 +26,7 @@ class tasklist_kolab_driver extends tasklist_driver
{
// features supported by the backend
public $alarms = false;
public $attachments = false;
public $attachments = true;
public $undelete = false; // task undelete action
private $rc;
@ -346,6 +346,18 @@ class tasklist_kolab_driver extends tasklist_driver
$task['changed'] = $record['dtstamp'];
}
if (!empty($record['_attachments'])) {
foreach ($record['_attachments'] as $key => $attachment) {
if ($attachment !== false) {
if (!$attachment['name'])
$attachment['name'] = $key;
$attachments[] = $attachment;
}
}
$task['attachments'] = $attachments;
}
return $task;
}
@ -387,6 +399,47 @@ class tasklist_kolab_driver extends tasklist_driver
$object[$key] = $val;
}
// delete existing attachment(s)
if (!empty($task['deleted_attachments'])) {
foreach ($task['deleted_attachments'] as $attachment) {
if (is_array($object['_attachments'])) {
foreach ($object['_attachments'] as $idx => $att) {
if ($att['id'] == $attachment)
$object['_attachments'][$idx] = false;
}
}
}
unset($task['deleted_attachments']);
}
// in kolab_storage attachments are indexed by content-id
if (is_array($task['attachments'])) {
foreach ($task['attachments'] as $idx => $attachment) {
$key = null;
// Roundcube ID has nothing to do with the storage ID, remove it
if ($attachment['content']) {
unset($attachment['id']);
}
else {
foreach ((array)$old['_attachments'] as $cid => $oldatt) {
if ($oldatt && $attachment['id'] == $oldatt['id'])
$key = $cid;
}
}
// replace existing entry
if ($key) {
$object['_attachments'][$key] = $attachment;
}
// append as new attachment
else {
$object['_attachments'][] = $attachment;
}
}
unset($task['attachments']);
}
unset($object['tempid'], $object['raw']);
return $object;
}
@ -442,7 +495,8 @@ class tasklist_kolab_driver extends tasklist_driver
$saved = false;
}
else {
$task['id'] = $task['uid'];
$task = $this->_to_rcube_task($object);
$task['list'] = $list_id;
$this->tasks[$task['uid']] = $task;
}
@ -480,6 +534,54 @@ class tasklist_kolab_driver extends tasklist_driver
}
/**
* Get attachment properties
*
* @param string $id Attachment identifier
* @param array $task Hash array with event properties:
* id: Task identifier
* list: List identifier
*
* @return array Hash array with attachment properties:
* id: Attachment identifier
* name: Attachment name
* mimetype: MIME content type of the attachment
* size: Attachment size
*/
public function get_attachment($id, $task)
{
$task['uid'] = $task['id'];
$task = $this->get_task($task);
if ($task && !empty($task['attachments'])) {
foreach ($task['attachments'] as $att) {
if ($att['id'] == $id)
return $att;
}
}
return null;
}
/**
* Get attachment body
*
* @param string $id Attachment identifier
* @param array $task Hash array with event properties:
* id: Task identifier
* list: List identifier
*
* @return string Attachment body
*/
public function get_attachment_body($id, $task)
{
if ($storage = $this->folders[$task['list']]) {
return $storage->get_attachment($task['id'], $id);
}
return false;
}
/**
*
*/

View file

@ -174,6 +174,34 @@ abstract class tasklist_driver
return false;
}
/**
* Get attachment properties
*
* @param string $id Attachment identifier
* @param array $task Hash array with event properties:
* id: Task identifier
* list: List identifier
*
* @return array Hash array with attachment properties:
* id: Attachment identifier
* name: Attachment name
* mimetype: MIME content type of the attachment
* size: Attachment size
*/
public function get_attachment($id, $task) { }
/**
* Get attachment body
*
* @param string $id Attachment identifier
* @param array $task Hash array with event properties:
* id: Task identifier
* list: List identifier
*
* @return string Attachment body
*/
public function get_attachment_body($id, $task) { }
/**
* List availabale categories
* The default implementation reads them from config/user prefs

View file

@ -36,6 +36,11 @@ $labels['save'] = 'Speichern';
$labels['cancel'] = 'Abbrechen';
$labels['addsubtask'] = 'Neue Teilaufgabe';
$labels['tabsummary'] = 'Übersicht';
$labels['tabrecurrence'] = 'Wiederholung';
$labels['tabattachments'] = 'Anhänge';
$labels['tabsharing'] = 'Freigabe';
$labels['editlist'] = 'Ressource bearbeiten';
$labels['createlist'] = 'Neue Ressource';
$labels['listactions'] = 'Ressourcenoptionen...';

View file

@ -36,6 +36,11 @@ $labels['save'] = 'Save';
$labels['cancel'] = 'Cancel';
$labels['addsubtask'] = 'Add subtask';
$labels['tabsummary'] = 'Summary';
$labels['tabrecurrence'] = 'Recurrence';
$labels['tabattachments'] = 'Attachments';
$labels['tabsharing'] = 'Sharing';
$labels['editlist'] = 'Edit resource';
$labels['createlist'] = 'Add resource';
$labels['listactions'] = 'Resource options...';

View file

@ -373,13 +373,14 @@ ul.toolbarmenu li span.icon.taskadd {
}
.taskhead .tags {
display: block;
position: absolute;
top: 4px;
top: 3px;
right: 110px;
max-width: 14em;
height: 16px;
overflow: hidden;
padding-top: 1px;
padding-bottom: 4px;
text-align: right;
}
@ -388,7 +389,7 @@ ul.toolbarmenu li span.icon.taskadd {
background: #d9ecf4;
border: 1px solid #c2dae5;
border-radius: 4px;
padding: 2px 8px;
padding: 1px 7px;
margin-right: 3px;
}
@ -529,6 +530,13 @@ ul.toolbarmenu li span.delete {
display:none;
}
#taskedit {
position: relative;
top: -1.5em;
padding: 0.5em 0.1em;
margin: 0 -0.2em;
}
#taskshow h2 {
margin-top: -0.5em;
}
@ -553,11 +561,43 @@ a.morelink:hover {
text-decoration: underline;
}
#taskedit .ui-tabs-panel {
min-height: 24em;
}
#taskeditform input.text,
#taskeditform textarea {
width: 97%;
}
#taskeditform .formbuttons {
margin: 0.5em 0;
}
#taskedit-attachments {
margin: 0.6em 0;
}
#taskedit-attachments ul li {
display: block;
color: #333;
font-weight: bold;
padding: 8px 4px 3px 30px;
text-shadow: 0px 1px 1px #fff;
text-decoration: none;
white-space: nowrap;
}
#taskedit-attachments ul li a.file {
padding: 0;
}
#taskedit-attachments-form {
margin-top: 1em;
padding-top: 0.8em;
border-top: 2px solid #fafafa;
}
div.form-section {
position: relative;
margin-top: 0.2em;
@ -568,6 +608,7 @@ div.form-section {
display: inline-block;
min-width: 7em;
padding-right: 0.5em;
margin-bottom: 0.3em;
}
label.block {
@ -587,6 +628,38 @@ label.block {
width: 97%;
}
#taskedit .droptarget {
background-image: url(../../../../skins/larry/images/filedrop.png) !important;
background-position: center bottom !important;
background-repeat: no-repeat !important;
}
#taskedit .droptarget.hover,
#taskedit .droptarget.active {
border-color: #019bc6;
box-shadow: 0 0 3px 2px rgba(71,135,177, 0.5);
-moz-box-shadow: 0 0 3px 2px rgba(71,135,177, 0.5);
-webkit-box-shadow: 0 0 3px 2px rgba(71,135,177, 0.5);
-o-box-shadow: 0 0 3px 2px rgba(71,135,177, 0.5);
}
#taskedit .droptarget.hover {
background-color: #d9ecf4;
box-shadow: 0 0 5px 2px rgba(71,135,177, 0.9);
-moz-box-shadow: 0 0 5px 2px rgba(71,135,177, 0.9);
-webkit-box-shadow: 0 0 5px 2px rgba(71,135,177, 0.9);
-o-box-shadow: 0 0 5px 2px rgba(71,135,177, 0.9);
}
#task-attachments .attachmentslist li {
float: left;
margin-right: 1em;
}
#task-attachments .attachmentslist li a {
outline: none;
}
/**
* Styles of the tagedit inputsforms

View file

@ -0,0 +1,36 @@
<roundcube:object name="doctype" value="html5" />
<html>
<head>
<title><roundcube:object name="pagetitle" /></title>
<roundcube:include file="/includes/links.html" />
</head>
<body class="extwin">
<div id="header">
<div id="topline">
<div class="topright">
<a href="#close" class="closelink" onclick="self.close()"><roundcube:label name="close" /></a>
</div>
</div>
<div id="topnav">
<roundcube:object name="logo" src="/images/roundcube_logo.png" id="toplogo" border="0" alt="Logo" />
</div>
<br style="clear:both" />
</div>
<div id="mainscreen">
<div id="partheader" class="uibox">
<roundcube:object name="plugin.attachmentcontrols" class="headers-table" />
</div>
<div id="attachmentcontainer" class="uibox">
<roundcube:object name="plugin.attachmentframe" id="attachmentframe" class="header-table" style="width:100%" />
</div>
</div>
</body>
</html>

View file

@ -120,6 +120,10 @@
<label><roundcube:label name="tasklist.complete" /></label>
<span class="task-text"></span>
</div>
<div id="task-attachments" class="form-section">
<label><roundcube:label name="attachments" /></label>
<div class="task-text"></div>
</div>
</div>
<roundcube:include file="/templates/taskedit.html" />

View file

@ -1,5 +1,10 @@
<div id="taskedit">
<div id="taskedit" class="uidialog uidialog-tabbed">
<form id="taskeditform" action="#" method="post" enctype="multipart/form-data">
<ul>
<li><a href="#taskedit-tab-1"><roundcube:label name="tasklist.tabsummary" /></a></li><li id="taskedit-tab-attachments"><a href="#taskedit-tab-2"><roundcube:label name="tasklist.tabattachments" /></a></li>
</ul>
<!-- basic info -->
<div id="taskedit-tab-1">
<div class="form-section">
<label for="edit-title"><roundcube:label name="tasklist.title" /></label>
<br />
@ -35,5 +40,16 @@
<label for="edit-tasklist"><roundcube:label name="tasklist.list" /></label>
<roundcube:object name="plugin.tasklist_select" id="edit-tasklist" tabindex="26" />
</div>
</div>
<!-- attachments list (with upload form) -->
<div id="taskedit-tab-2">
<div id="taskedit-attachments">
<roundcube:object name="plugin.attachments_list" id="attachment-list" class="attachmentslist" />
</div>
<div id="taskedit-attachments-form">
<roundcube:object name="plugin.attachments_form" id="tasklist-attachment-form" attachmentFieldSize="30" />
</div>
<roundcube:object name="plugin.filedroparea" id="taskedit-tab-2" />
</div>
</form>
</div>

View file

@ -95,6 +95,8 @@ function rcube_tasklist_ui(settings)
/* basic initializations */
$('#taskedit').tabs();
var completeness_slider = $('#edit-completeness-slider').slider({
range: 'min',
slide: function(e, ui){
@ -568,6 +570,7 @@ function rcube_tasklist_ui(settings)
}
// remove from list index
var oldlist = listindex.join('%%%');
var oldindex = listindex.indexOf(rec.id);
if (oldindex >= 0) {
slice = listindex.slice(0,oldindex);
@ -610,6 +613,9 @@ function rcube_tasklist_ui(settings)
slice.push(rec.id);
listindex = slice.concat(listindex.slice(index));
}
else { // restore old list index
listindex = oldlist.split('%%%');
}
}
/**
@ -729,6 +735,15 @@ function rcube_tasklist_ui(settings)
});
}
// build attachments list
$('#task-attachments').hide();
if ($.isArray(rec.attachments)) {
task_show_attachments(rec.attachments || [], $('#task-attachments').children('.task-text'), rec);
if (rec.attachments.length > 0) {
$('#task-attachments').show();
}
}
// define dialog buttons
var buttons = {};
buttons[rcmail.gettext('edit','tasklist')] = function() {
@ -769,11 +784,15 @@ function rcube_tasklist_ui(settings)
list = rec.list && me.tasklists[rec.list] ? me.tasklists[rec.list] :
(me.selected_list ? me.tasklists[me.selected_list] : { editable: action=='new' });
if (list.readonly || (action == 'edit' && (!rec || rec.readonly || rec.temp)))
if (list.readonly || (action == 'edit' && (!rec || rec.readonly)))
return false;
me.selected_task = $.extend({}, rec); // clone task object
// assign temporary id
if (!me.selected_task.id)
me.selected_task.id = -(++idcount);
// fill form data
var title = $('#edit-title').val(rec.title || '');
var description = $('#edit-description').val(rec.description || '');
@ -810,6 +829,26 @@ function rcube_tasklist_ui(settings)
return false;
})
// attachments
rcmail.enable_command('remove-attachment', !list.readonly);
me.selected_task.deleted_attachments = [];
// we're sharing some code for uploads handling with app.js
rcmail.env.attachments = [];
rcmail.env.compose_id = me.selected_task.id; // for rcmail.async_upload_form()
if ($.isArray(rec.attachments)) {
task_show_attachments(rec.attachments, $('#taskedit-attachments'), rec, true);
}
else {
$('#taskedit-attachments > ul').empty();
}
// show/hide tabs according to calendar's feature support
$('#taskedit-tab-attachments')[(list.attachments?'show':'hide')]();
// activate the first tab
$('#eventtabs').tabs('select', 0);
// define dialog buttons
var buttons = {};
buttons[rcmail.gettext('save', 'tasklist')] = function() {
@ -818,6 +857,7 @@ function rcube_tasklist_ui(settings)
me.selected_task[key] = input.val();
});
me.selected_task.tags = [];
me.selected_task.attachments = [];
// do some basic input validation
if (me.selected_task.startdate && me.selected_task.date) {
@ -834,6 +874,12 @@ function rcube_tasklist_ui(settings)
me.selected_task.tags.push(elem.value);
});
// uploaded attachments list
for (var i in rcmail.env.attachments) {
if (i.match(/^rcmfile(.+)/))
me.selected_task.attachments.push(RegExp.$1);
}
if (me.selected_task.list && me.selected_task.list != rec.list)
me.selected_task._fromlist = rec.list;
@ -878,6 +924,91 @@ function rcube_tasklist_ui(settings)
title.select();
}
/**
* Open a task attachment either in a browser window for inline view or download it
*/
function load_attachment(rec, att)
{
var qstring = '_id='+urlencode(att.id)+'&_t='+urlencode(rec.recurrence_id||rec.id)+'&_list='+urlencode(rec.list);
// open attachment in frame if it's of a supported mimetype
// similar as in app.js and calendar_ui.js
if (att.id && att.mimetype && $.inArray(att.mimetype, settings.mimetypes)>=0) {
rcmail.attachment_win = window.open(rcmail.env.comm_path+'&_action=get-attachment&'+qstring+'&_frame=1', 'rcubetaskattachment');
if (rcmail.attachment_win) {
window.setTimeout(function() { rcmail.attachment_win.focus(); }, 10);
return;
}
}
rcmail.goto_url('get-attachment', qstring+'&_download=1', false);
};
/**
* Build task attachments list
*/
function task_show_attachments(list, container, rec, edit)
{
var i, id, len, content, li, elem,
ul = $('<ul>').addClass('attachmentslist');
for (i=0, len=list.length; i<len; i++) {
elem = list[i];
li = $('<li>').addClass(elem.classname);
if (edit) {
rcmail.env.attachments[elem.id] = elem;
// delete icon
content = $('<a>')
.attr('href', '#delete')
.attr('title', rcmail.gettext('delete'))
.addClass('delete')
.click({ id:elem.id }, function(e) {
remove_attachment(this, e.data.id);
return false;
});
if (!rcmail.env.deleteicon) {
content.html(rcmail.gettext('delete'));
}
else {
$('<img>').attr('src', rcmail.env.deleteicon).attr('alt', rcmail.gettext('delete')).appendTo(content);
}
li.append(content);
}
// name/link
$('<a>')
.attr('href', '#load')
.addClass('file')
.html(elem.name).click({ task:rec, att:elem }, function(e) {
load_attachment(e.data.task, e.data.att);
return false;
}).appendTo(li);
ul.append(li);
}
if (edit && rcmail.gui_objects.attachmentlist) {
ul.id = rcmail.gui_objects.attachmentlist.id;
rcmail.gui_objects.attachmentlist = ul.get(0);
}
container.empty().append(ul);
};
/**
*
*/
var remove_attachment = function(elem, id)
{
$(elem.parentNode).hide();
me.selected_task.deleted_attachments.push(id);
delete rcmail.env.attachments[id];
};
/**
*
*/

View file

@ -83,6 +83,8 @@ class tasklist extends rcube_plugin
$this->register_action('fetch', array($this, 'fetch_tasks'));
$this->register_action('inlineui', array($this, 'get_inline_ui'));
$this->register_action('mail2task', array($this, 'mail_message2task'));
$this->register_action('get-attachment', array($this, 'attachment_get'));
$this->register_action('upload', array($this, 'attachment_upload'));
}
else if ($this->rc->task == 'mail') {
// TODO: register hooks to catch ical/vtodo email attachments
@ -296,6 +298,24 @@ class tasklist extends rcube_plugin
}
}
$attachments = array();
$taskid = $rec['id'];
if (is_array($_SESSION['tasklist_session']) && $_SESSION['tasklist_session']['id'] == $taskid) {
if (!empty($_SESSION['tasklist_session']['attachments'])) {
foreach ($_SESSION['tasklist_session']['attachments'] as $id => $attachment) {
if (is_array($rec['attachments']) && in_array($id, $rec['attachments'])) {
$attachments[$id] = $this->rc->plugins->exec_hook('attachment_get', $attachment);
unset($attachments[$id]['abort'], $attachments[$id]['group']);
}
}
}
}
$rec['attachments'] = $attachments;
if (is_numeric($rec['id']) && $rec['id'] < 0)
unset($rec['id']);
return $rec;
}
@ -464,6 +484,10 @@ class tasklist extends rcube_plugin
}
}
foreach ((array)$rec['attachments'] as $k => $attachment) {
$rec['attachments'][$k]['classname'] = rcmail_filetype2classname($attachment['mimetype'], $attachment['name']);
}
if (!isset($rec['_depth'])) {
$rec['_depth'] = 0;
$parent_id = $this->task_tree[$rec['id']];
@ -571,6 +595,229 @@ class tasklist extends rcube_plugin
}
/******* Attachment handling *******/
/*** pretty much the same as in plugins/calendar/calendar.php ***/
/**
* Handler for attachments upload
*/
public function attachment_upload()
{
// Upload progress update
if (!empty($_GET['_progress'])) {
rcube_upload_progress();
}
$taskid = get_input_value('_id', RCUBE_INPUT_GPC);
$uploadid = get_input_value('_uploadid', RCUBE_INPUT_GPC);
// prepare session storage
if (!is_array($_SESSION['tasklist_session']) || $_SESSION['tasklist_session']['id'] != $taskid) {
$_SESSION['tasklist_session'] = array();
$_SESSION['tasklist_session']['id'] = $taskid;
$_SESSION['tasklist_session']['attachments'] = array();
}
// clear all stored output properties (like scripts and env vars)
$this->rc->output->reset();
if (is_array($_FILES['_attachments']['tmp_name'])) {
foreach ($_FILES['_attachments']['tmp_name'] as $i => $filepath) {
// Process uploaded attachment if there is no error
$err = $_FILES['_attachments']['error'][$i];
if (!$err) {
$attachment = array(
'path' => $filepath,
'size' => $_FILES['_attachments']['size'][$i],
'name' => $_FILES['_attachments']['name'][$i],
'mimetype' => rc_mime_content_type($filepath, $_FILES['_attachments']['name'][$i], $_FILES['_attachments']['type'][$i]),
'group' => $eventid,
);
$attachment = $this->rc->plugins->exec_hook('attachment_upload', $attachment);
}
if (!$err && $attachment['status'] && !$attachment['abort']) {
$id = $attachment['id'];
// store new attachment in session
unset($attachment['status'], $attachment['abort']);
$_SESSION['tasklist_session']['attachments'][$id] = $attachment;
$content = html::a(array(
'href' => "#delete",
'class' => 'delete',
'onclick' => sprintf("return %s.remove_from_attachment_list('rcmfile%s')", JS_OBJECT_NAME, $id),
'title' => rcube_label('delete'),
), Q(rcube_label('delete')));
$content .= Q($attachment['name']);
$this->rc->output->command('add2attachment_list', "rcmfile$id", array(
'html' => $content,
'name' => $attachment['name'],
'mimetype' => $attachment['mimetype'],
'classname' => rcmail_filetype2classname($attachment['mimetype'], $attachment['name']),
'complete' => true), $uploadid);
}
else { // upload failed
if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) {
$msg = rcube_label(array('name' => 'filesizeerror', 'vars' => array(
'size' => show_bytes(parse_bytes(ini_get('upload_max_filesize'))))));
}
else if ($attachment['error']) {
$msg = $attachment['error'];
}
else {
$msg = rcube_label('fileuploaderror');
}
$this->rc->output->command('display_message', $msg, 'error');
$this->rc->output->command('remove_from_attachment_list', $uploadid);
}
}
}
else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
// if filesize exceeds post_max_size then $_FILES array is empty,
// show filesizeerror instead of fileuploaderror
if ($maxsize = ini_get('post_max_size')) {
$msg = rcube_label(array('name' => 'filesizeerror', 'vars' => array(
'size' => show_bytes(parse_bytes($maxsize)))));
}
else {
$msg = rcube_label('fileuploaderror');
}
$this->rc->output->command('display_message', $msg, 'error');
$this->rc->output->command('remove_from_attachment_list', $uploadid);
}
$this->rc->output->send('iframe');
}
/**
* Handler for attachments download/displaying
*/
public function attachment_get()
{
$task = get_input_value('_t', RCUBE_INPUT_GPC);
$list = get_input_value('_list', RCUBE_INPUT_GPC);
$id = get_input_value('_id', RCUBE_INPUT_GPC);
$task = array('id' => $task, 'list' => $list);
// show loading page
if (!empty($_GET['_preload'])) {
$url = str_replace('&_preload=1', '', $_SERVER['REQUEST_URI']);
$message = rcube_label('loadingdata');
header('Content-Type: text/html; charset=' . RCMAIL_CHARSET);
print "<html>\n<head>\n"
. '<meta http-equiv="refresh" content="0; url='.Q($url).'">' . "\n"
. '<meta http-equiv="content-type" content="text/html; charset='.RCMAIL_CHARSET.'">' . "\n"
. "</head>\n<body>\n$message\n</body>\n</html>";
exit;
}
ob_end_clean();
$attachment = $this->attachment = $this->driver->get_attachment($id, $task);
// show part page
if (!empty($_GET['_frame'])) {
$this->register_handler('plugin.attachmentframe', array($this, 'attachment_frame'));
$this->register_handler('plugin.attachmentcontrols', array($this->ui, 'attachment_controls'));
$this->rc->output->send('tasklist.attachment');
exit;
}
if ($attachment) {
// allow post-processing of the attachment body
$part = new rcube_message_part;
$part->filename = $attachment['name'];
$part->size = $attachment['size'];
$part->mimetype = $attachment['mimetype'];
$plugin = $this->rc->plugins->exec_hook('message_part_get', array(
'body' => $this->driver->get_attachment_body($id, $task),
'mimetype' => strtolower($attachment['mimetype']),
'download' => !empty($_GET['_download']),
'part' => $part,
));
if ($plugin['abort'])
exit;
$mimetype = $plugin['mimetype'];
list($ctype_primary, $ctype_secondary) = explode('/', $mimetype);
$browser = $this->rc->output->browser;
// send download headers
if ($plugin['download']) {
header("Content-Type: application/octet-stream");
if ($browser->ie)
header("Content-Type: application/force-download");
}
else if ($ctype_primary == 'text') {
header("Content-Type: text/$ctype_secondary");
}
else {
header("Content-Type: $mimetype");
header("Content-Transfer-Encoding: binary");
}
// display page, @TODO: support text/plain (and maybe some other text formats)
if ($mimetype == 'text/html' && empty($_GET['_download'])) {
$OUTPUT = new rcube_html_page();
// @TODO: use washtml on $body
$OUTPUT->write($plugin['body']);
}
else {
// don't kill the connection if download takes more than 30 sec.
@set_time_limit(0);
$filename = $attachment['name'];
$filename = preg_replace('[\r\n]', '', $filename);
if ($browser->ie && $browser->ver < 7)
$filename = rawurlencode(abbreviate_string($filename, 55));
else if ($browser->ie)
$filename = rawurlencode($filename);
else
$filename = addcslashes($filename, '"');
$disposition = !empty($_GET['_download']) ? 'attachment' : 'inline';
header("Content-Disposition: $disposition; filename=\"$filename\"");
echo $plugin['body'];
}
exit;
}
// if we arrive here, the requested part was not found
header('HTTP/1.1 404 Not Found');
exit;
}
/**
* Template object for attachment display frame
*/
public function attachment_frame($attrib)
{
$attachment = $this->attachment;
$mimetype = strtolower($attachment['mimetype']);
list($ctype_primary, $ctype_secondary) = explode('/', $mimetype);
$attrib['src'] = './?' . str_replace('_frame=', ($ctype_primary == 'text' ? '_show=' : '_preload='), $_SERVER['QUERY_STRING']);
return html::iframe($attrib);
}
/******* Email related function *******/
public function mail_message2task()

View file

@ -81,6 +81,16 @@ class tasklist_ui
$this->plugin->register_handler('plugin.tasks', array($this, 'tasks_resultview'));
$this->plugin->register_handler('plugin.tagslist', array($this, 'tagslist'));
$this->plugin->register_handler('plugin.tags_editline', array($this, 'tags_editline'));
$this->plugin->register_handler('plugin.attachments_form', array($this, 'attachments_form'));
$this->plugin->register_handler('plugin.attachments_list', array($this, 'attachments_list'));
$this->plugin->register_handler('plugin.filedroparea', array($this, 'file_drop_area'));
// 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 = $this->rc->output->get_env('tasklist_settings');
$settings['mimetypes'] = is_string($mimetypes) ? explode(',', $mimetypes) : (array)$mimetypes;
$this->rc->output->set_env('tasklist_settings', $settings);
$this->plugin->include_script('jquery.tagedit.js');
$this->plugin->include_script('tasklist.js');
@ -102,6 +112,7 @@ class tasklist_ui
$prop['alarms'] = $this->plugin->driver->alarms;
$prop['undelete'] = $this->plugin->driver->undelete;
$prop['sortable'] = $this->plugin->driver->sortable;
$prop['attachments'] = $this->plugin->driver->attachments;
$jsenv[$id] = $prop;
$html_id = html_identifier($id);
@ -234,4 +245,77 @@ class tasklist_ui
return html::div($attrib, $input->show(''));
}
/**
* Generate HTML element for attachments list
*/
function attachments_list($attrib = array())
{
if (!$attrib['id'])
$attrib['id'] = 'rcmAttachmentList';
$this->rc->output->add_gui_object('attachmentlist', $attrib['id']);
return html::tag('ul', $attrib, '', html::$common_attrib);
}
/**
* Generate the form for event attachments upload
*/
function attachments_form($attrib = array())
{
// add ID if not given
if (!$attrib['id'])
$attrib['id'] = 'rcmUploadForm';
// Get max filesize, enable upload progress bar
$max_filesize = rcube_upload_init();
$button = new html_inputfield(array('type' => 'button'));
$input = new html_inputfield(array(
'type' => 'file',
'name' => '_attachments[]',
'multiple' => 'multiple',
'size' => $attrib['attachmentfieldsize'],
));
return html::div($attrib,
html::div(null, $input->show()) .
html::div('formbuttons', $button->show(rcube_label('upload'), array('class' => 'button mainaction',
'onclick' => JS_OBJECT_NAME . ".upload_file(this.form)"))) .
html::div('hint', rcube_label(array('name' => 'maxuploadsize', 'vars' => array('size' => $max_filesize))))
);
}
/**
* Register UI object for HTML5 drag & drop file upload
*/
function file_drop_area($attrib = array())
{
if ($attrib['id']) {
$this->rc->output->add_gui_object('filedrop', $attrib['id']);
$this->rc->output->set_env('filedrop', array('action' => 'upload', 'fieldname' => '_attachments'));
}
}
/**
*
*/
function attachment_controls($attrib = array())
{
$table = new html_table(array('cols' => 3));
if (!empty($this->plugin->attachment['name'])) {
$table->add('title', Q(rcube_label('filename')));
$table->add('header', Q($this->plugin->attachment['name']));
$table->add('download-link', html::a('?'.str_replace('_frame=', '_download=', $_SERVER['QUERY_STRING']), Q(rcube_label('download'))));
}
if (!empty($this->plugin->attachment['size'])) {
$table->add('title', Q(rcube_label('filesize')));
$table->add('header', Q(show_bytes($this->plugin->attachment['size'])));
}
return $table->show($attrib);
}
}