Merge branch 'master' of ssh://git.kolabsys.com/git/roundcube
This commit is contained in:
commit
1f7d51be85
12 changed files with 886 additions and 67 deletions
|
@ -5,7 +5,7 @@
|
|||
+ Edit: 3.16: Reminder set
|
||||
+ Edit: 3.17: Priority: High/Low
|
||||
- Edit: 3.18: Recurrence (in line with Kontact)
|
||||
- Edit: 3.19: Attachment Upload
|
||||
+ Edit: 3.19: Attachment Upload
|
||||
- Edit: 3.20: Print
|
||||
- Add/Manage Attendees
|
||||
- Edit: 3.21: Required / Optional / Resource specification
|
||||
|
|
|
@ -106,6 +106,8 @@ class calendar extends rcube_plugin
|
|||
$this->register_action('load_events', array($this, 'load_events'));
|
||||
$this->register_action('search_events', array($this, 'search_events'));
|
||||
$this->register_action('export_events', array($this, 'export_events'));
|
||||
$this->register_action('upload', array($this, 'attachment_upload'));
|
||||
$this->register_action('get-attachment', array($this, 'attachment_get'));
|
||||
$this->register_action('randomdata', array($this, 'generate_randomdata'));
|
||||
}
|
||||
else if ($this->rc->task == 'settings') {
|
||||
|
@ -156,7 +158,7 @@ class calendar extends rcube_plugin
|
|||
|
||||
// Add JS files to the page header
|
||||
$this->ui->addJS();
|
||||
|
||||
|
||||
$this->register_handler('plugin.calendar_css', array($this->ui, 'calendar_css'));
|
||||
$this->register_handler('plugin.calendar_list', array($this->ui, 'calendar_list'));
|
||||
$this->register_handler('plugin.calendar_select', array($this->ui, 'calendar_select'));
|
||||
|
@ -167,14 +169,16 @@ class calendar extends rcube_plugin
|
|||
$this->register_handler('plugin.alarm_select', array($this->ui, 'alarm_select'));
|
||||
$this->register_handler('plugin.snooze_select', array($this->ui, 'snooze_select'));
|
||||
$this->register_handler('plugin.recurrence_form', array($this->ui, 'recurrence_form'));
|
||||
$this->register_handler('plugin.attachments_form', array($this->ui, 'attachments_form'));
|
||||
$this->register_handler('plugin.attachments_list', array($this->ui, 'attachments_list'));
|
||||
$this->register_handler('plugin.edit_recurring_warning', array($this->ui, 'recurring_event_warning'));
|
||||
$this->register_handler('plugin.searchform', array($this->rc->output, 'search_form')); // use generic method from rcube_template
|
||||
|
||||
$this->rc->output->add_label('low','normal','high');
|
||||
|
||||
$this->rc->output->add_label('low','normal','high','delete','cancel','uploading');
|
||||
|
||||
$this->rc->output->send("calendar.calendar");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handler for preferences_sections_list hook.
|
||||
* Adds Calendar settings sections into preferences sections list.
|
||||
|
@ -426,16 +430,20 @@ class calendar extends rcube_plugin
|
|||
$action = get_input_value('action', RCUBE_INPUT_POST);
|
||||
$event = get_input_value('e', RCUBE_INPUT_POST);
|
||||
$success = $reload = false;
|
||||
|
||||
|
||||
switch ($action) {
|
||||
case "new":
|
||||
// create UID for new event
|
||||
$event['uid'] = $this->generate_uid();
|
||||
$success = $this->driver->new_event($event);
|
||||
$this->prepare_event($event);
|
||||
if ($success = $this->driver->new_event($event))
|
||||
$this->cleanup_event($event);
|
||||
$reload = true;
|
||||
break;
|
||||
case "edit":
|
||||
$success = $this->driver->edit_event($event);
|
||||
$this->prepare_event($event);
|
||||
if ($success = $this->driver->edit_event($event))
|
||||
$this->cleanup_event($event);
|
||||
$reload = true;
|
||||
break;
|
||||
case "resize":
|
||||
|
@ -616,7 +624,7 @@ class calendar extends rcube_plugin
|
|||
$event['alarms_text'] = $this->_alarms_text($event['alarms']);
|
||||
if ($event['recurrence'])
|
||||
$event['recurrence_text'] = $this->_recurrence_text($event['recurrence']);
|
||||
|
||||
|
||||
$json[] = array(
|
||||
'start' => gmdate('c', $this->fromGMT($event['start'])), // client treats date strings as they were in users's timezone
|
||||
'end' => gmdate('c', $this->fromGMT($event['end'])), // so shift timestamps to users's timezone and render a date string
|
||||
|
@ -842,4 +850,250 @@ class calendar extends rcube_plugin
|
|||
$this->rc->output->redirect('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for attachments upload
|
||||
*/
|
||||
public function attachment_upload()
|
||||
{
|
||||
$event = get_input_value('_id', RCUBE_INPUT_GPC);
|
||||
$calendar = get_input_value('calendar', RCUBE_INPUT_GPC);
|
||||
$uploadid = get_input_value('_uploadid', RCUBE_INPUT_GPC);
|
||||
|
||||
$eventid = $calendar.':'.$event;
|
||||
|
||||
if (!is_array($_SESSION['event_session']) || $_SESSION['event_session']['id'] != $eventid) {
|
||||
$_SESSION['event_session'] = array();
|
||||
$_SESSION['event_session']['id'] = $eventid;
|
||||
$_SESSION['event_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['event_session']['attachments'][$id] = $attachment;
|
||||
|
||||
if (($icon = $_SESSION['calendar_deleteicon']) && is_file($icon)) {
|
||||
$button = html::img(array(
|
||||
'src' => $icon,
|
||||
'alt' => rcube_label('delete')
|
||||
));
|
||||
}
|
||||
else {
|
||||
$button = Q(rcube_label('delete'));
|
||||
}
|
||||
|
||||
$content = html::a(array(
|
||||
'href' => "#delete",
|
||||
'onclick' => sprintf("return %s.remove_from_attachment_list('rcmfile%s')", JS_OBJECT_NAME, $id),
|
||||
'title' => rcube_label('delete'),
|
||||
), $button);
|
||||
|
||||
$content .= Q($attachment['name']);
|
||||
|
||||
$this->rc->output->command('add2attachment_list', "rcmfile$id", array(
|
||||
'html' => $content,
|
||||
'name' => $attachment['name'],
|
||||
'mimetype' => $attachment['mimetype'],
|
||||
'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()
|
||||
{
|
||||
$event = get_input_value('_event', RCUBE_INPUT_GPC);
|
||||
$calendar = get_input_value('_cal', RCUBE_INPUT_GPC);
|
||||
$id = get_input_value('_id', RCUBE_INPUT_GPC);
|
||||
|
||||
$event = array('id' => $event, 'calendar' => $calendar);
|
||||
|
||||
// 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();
|
||||
send_nocacheing_headers();
|
||||
|
||||
if (isset($_SESSION['calendar_attachment']))
|
||||
$attachment = $_SESSION['calendar_attachment'];
|
||||
else
|
||||
$attachment = $_SESSION['calendar_attachment'] = $this->driver->get_attachment($id, $event);
|
||||
|
||||
// show part page
|
||||
if (!empty($_GET['_frame'])) {
|
||||
$this->rc->output->add_handlers(array('attachmentframe' => array($this, 'attachment_frame')));
|
||||
$this->rc->output->send('calendar.attachment');
|
||||
exit;
|
||||
}
|
||||
|
||||
unset($_SESSION['calendar_attachment']);
|
||||
|
||||
if ($attachment) {
|
||||
$mimetype = strtolower($attachment['mimetype']);
|
||||
list($ctype_primary, $ctype_secondary) = explode('/', $mimetype);
|
||||
|
||||
$browser = $this->rc->output->browser;
|
||||
|
||||
// send download headers
|
||||
if ($_GET['_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 {
|
||||
// $mimetype = rcmail_fix_mimetype($mimetype);
|
||||
header("Content-Type: $mimetype");
|
||||
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();
|
||||
// @TODO: use washtml on $body
|
||||
$OUTPUT->write($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\"");
|
||||
}
|
||||
|
||||
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 = $_SESSION['calendar_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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares new/edited event properties before save
|
||||
*/
|
||||
private function prepare_event(&$event)
|
||||
{
|
||||
$eventid = $event['calendar'].':'.$event['id'];
|
||||
|
||||
$attachments = array();
|
||||
if (is_array($_SESSION['event_session']) && $_SESSION['event_session']['id'] == $eventid) {
|
||||
if (!empty($_SESSION['event_session']['attachments'])) {
|
||||
foreach ($_SESSION['event_session']['attachments'] as $id => $attachment) {
|
||||
if (is_array($event['attachments']) && in_array($id, $event['attachments'])) {
|
||||
$attachments[$id] = $attachment;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$event['attachments'] = $attachments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases some resources after successful event save
|
||||
*/
|
||||
private function cleanup_event(&$event)
|
||||
{
|
||||
// remove temp. attachment files
|
||||
if (!empty($_SESSION['event_session']) && ($eventid = $_SESSION['event_session']['id'])) {
|
||||
$this->rc->plugins->exec_hook('attachments_cleanup', array('group' => $eventid));
|
||||
unset($_SESSION['event_session']);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -114,6 +114,78 @@ function rcube_calendar_ui(settings)
|
|||
return fromto;
|
||||
};
|
||||
|
||||
var load_attachment = function(event, att)
|
||||
{
|
||||
var qstring = '_id='+urlencode(att.id)+'&_event='+urlencode(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) {
|
||||
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);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
rcmail.goto_url('get-attachment', qstring+'&_download=1', false);
|
||||
};
|
||||
|
||||
// build event attachments list
|
||||
var event_show_attachments = function(list, container, event, edit)
|
||||
{
|
||||
var i, id, len, img, content, li, elem,
|
||||
ul = document.createElement('UL');
|
||||
|
||||
for (i=0, len=list.length; i<len; i++) {
|
||||
li = document.createElement('LI');
|
||||
elem = list[i];
|
||||
|
||||
if (edit) {
|
||||
rcmail.env.attachments[elem.id] = elem;
|
||||
// delete icon
|
||||
content = document.createElement('A');
|
||||
content.href = '#delete';
|
||||
content.title = rcmail.gettext('delete');
|
||||
$(content).click({id: elem.id}, function(e) { remove_attachment(this, e.data.id); return false; });
|
||||
|
||||
if (!rcmail.env.deleteicon)
|
||||
content.innerHTML = rcmail.gettext('delete');
|
||||
else {
|
||||
img = document.createElement('IMG');
|
||||
img.src = rcmail.env.deleteicon;
|
||||
img.alt = rcmail.gettext('delete');
|
||||
content.appendChild(img);
|
||||
}
|
||||
|
||||
li.appendChild(content);
|
||||
}
|
||||
|
||||
// name/link
|
||||
content = document.createElement('A');
|
||||
content.innerHTML = list[i].name;
|
||||
content.href = '#load';
|
||||
$(content).click({event: event, att: elem}, function(e) {
|
||||
load_attachment(e.data.event, e.data.att); return false; });
|
||||
li.appendChild(content);
|
||||
|
||||
ul.appendChild(li);
|
||||
}
|
||||
|
||||
if (edit && rcmail.gui_objects.attachmentlist) {
|
||||
ul.id = rcmail.gui_objects.attachmentlist.id;
|
||||
rcmail.gui_objects.attachmentlist = ul;
|
||||
}
|
||||
|
||||
container.empty().append(ul);
|
||||
};
|
||||
|
||||
var remove_attachment = function(elem, id)
|
||||
{
|
||||
$(elem.parentNode).hide();
|
||||
rcmail.env.deleted_attachments.push(id);
|
||||
delete rcmail.env.attachments[id];
|
||||
};
|
||||
|
||||
// event details dialog (show only)
|
||||
var event_show_dialog = function(event)
|
||||
{
|
||||
|
@ -151,7 +223,18 @@ function rcube_calendar_ui(settings)
|
|||
var sensitivitylabels = { 0:rcmail.gettext('public'), 1:rcmail.gettext('private'), 2:rcmail.gettext('confidential') };
|
||||
$('#event-sensitivity').show().children('.event-text').html(Q(sensitivitylabels[event.sensitivity]));
|
||||
}
|
||||
|
||||
|
||||
// create attachments list
|
||||
if ($.isArray(event.attachments)) {
|
||||
event_show_attachments(event.attachments, $('#event-attachments').children('.event-text'), event);
|
||||
if (event.attachments.length > 0) {
|
||||
$('#event-attachments').show();
|
||||
}
|
||||
}
|
||||
else if (calendar.attachments) {
|
||||
// fetch attachments, some drivers doesn't set 'attachments' popr of the event
|
||||
}
|
||||
|
||||
var buttons = {};
|
||||
if (calendar.editable && event.editable !== false) {
|
||||
buttons[rcmail.gettext('edit', 'calendar')] = function() {
|
||||
|
@ -306,7 +389,24 @@ function rcube_calendar_ui(settings)
|
|||
}
|
||||
else
|
||||
$('#edit-recurring-warning').hide();
|
||||
|
||||
|
||||
// attachments
|
||||
if (calendar.attachments) {
|
||||
rcmail.enable_command('remove-attachment', !calendar.readonly);
|
||||
rcmail.env.deleted_attachments = [];
|
||||
// we're sharing some code for uploads handling with app.js
|
||||
rcmail.env.attachments = [];
|
||||
rcmail.env.compose_id = event.id; // for rcmail.async_upload_form()
|
||||
|
||||
if ($.isArray(event.attachments)) {
|
||||
event_show_attachments(event.attachments, $('#edit-attachments'), event, true);
|
||||
}
|
||||
else {
|
||||
$('#edit-attachments > ul').empty();
|
||||
// fetch attachments, some drivers doesn't set 'attachments' array for event
|
||||
}
|
||||
}
|
||||
|
||||
// buttons
|
||||
var buttons = {};
|
||||
|
||||
|
@ -334,9 +434,10 @@ function rcube_calendar_ui(settings)
|
|||
priority: priority.val(),
|
||||
sensitivity: sensitivity.val(),
|
||||
recurrence: '',
|
||||
alarms: ''
|
||||
alarms: '',
|
||||
deleted_attachments: rcmail.env.deleted_attachments
|
||||
};
|
||||
|
||||
|
||||
// serialize alarm settings
|
||||
// TODO: support multiple alarm entries
|
||||
var alarm = $('select.edit-alarm-type').val();
|
||||
|
@ -347,7 +448,14 @@ function rcube_calendar_ui(settings)
|
|||
else if ((val = parseInt($('input.edit-alarm-value').val())) && !isNaN(val) && val >= 0)
|
||||
data.alarms = offset[0] + val + offset[1] + ':' + alarm;
|
||||
}
|
||||
|
||||
|
||||
// uploaded attachments list
|
||||
var attachments = [];
|
||||
for (var i in rcmail.env.attachments)
|
||||
if (i.match(/^rcmfile([0-9a-z]+)/))
|
||||
attachments.push(RegExp.$1);
|
||||
data.attachments = attachments;
|
||||
|
||||
// gather recurrence settings
|
||||
var freq;
|
||||
if ((freq = recurrence.val()) != '') {
|
||||
|
@ -1065,7 +1173,7 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
|||
|
||||
// configure toobar buttons
|
||||
rcmail.register_command('addevent', function(){ cal.add_event(); }, true);
|
||||
|
||||
|
||||
// configure list operations
|
||||
rcmail.register_command('calendar-create', function(){ cal.calendar_edit_dialog(null); }, true);
|
||||
rcmail.register_command('calendar-edit', function(){ cal.calendar_edit_dialog(cal.calendars[cal.selected_calendar]); }, false);
|
||||
|
|
|
@ -51,6 +51,13 @@
|
|||
* 'sensitivity' => 0|1|2, // Event sensitivity (0=public, 1=private, 2=confidential)
|
||||
* 'alarms' => '-15M:DISPLAY', // Reminder settings inspired by valarm definition (e.g. display alert 15 minutes before event)
|
||||
* 'savemode' => 'all|future|current|new', // How changes on recurring event should be handled
|
||||
* 'attachments' => array( // List of attachments
|
||||
* 'name' => 'File name',
|
||||
* 'mimetype' => 'Content type',
|
||||
* 'size' => 1..n, // in bytes
|
||||
* 'id' => 'Attachment identifier'
|
||||
* ),
|
||||
* 'deleted_attachments' => array(), // array of attachment identifiers to delete when event is updated
|
||||
* );
|
||||
*/
|
||||
|
||||
|
@ -196,14 +203,49 @@ abstract class calendar_driver
|
|||
abstract function dismiss_alarm($event_id, $snooze = 0);
|
||||
|
||||
/**
|
||||
* Save an attachment related to the given event
|
||||
* Get list of event's attachments.
|
||||
* Drivers can return list of attachments as event property.
|
||||
* If they will do not do this list_attachments() method will be used.
|
||||
*
|
||||
* @param array $event Hash array with event properties:
|
||||
* id: Event identifier
|
||||
* calendar: Calendar identifier
|
||||
*
|
||||
* @return array List of attachments, each as hash array:
|
||||
* id: Attachment identifier
|
||||
* name: Attachment name
|
||||
* mimetype: MIME content type of the attachment
|
||||
* size: Attachment size
|
||||
*/
|
||||
public function add_attachment($attachment, $event_id) { }
|
||||
public function list_attachments($event) { }
|
||||
|
||||
/**
|
||||
* Remove a specific attachment from the given event
|
||||
* Get attachment properties
|
||||
*
|
||||
* @param string $id Attachment identifier
|
||||
* @param array $event Hash array with event properties:
|
||||
* id: Event identifier
|
||||
* calendar: Calendar 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 remove_attachment($attachment, $event_id) { }
|
||||
public function get_attachment($id, $event) { }
|
||||
|
||||
/**
|
||||
* Get attachment body
|
||||
*
|
||||
* @param string $id Attachment identifier
|
||||
* @param array $event Hash array with event properties:
|
||||
* id: Event identifier
|
||||
* calendar: Calendar identifier
|
||||
*
|
||||
* @return string Attachment body
|
||||
*/
|
||||
public function get_attachment_body($id, $event) { }
|
||||
|
||||
/**
|
||||
* List availabale categories
|
||||
|
|
|
@ -156,22 +156,15 @@ class database_driver extends calendar_driver
|
|||
{
|
||||
if (!$this->calendars[$prop['id']])
|
||||
return false;
|
||||
|
||||
// delete all events of this calendar
|
||||
$query = $this->rc->db->query(
|
||||
"DELETE FROM " . $this->db_events . "
|
||||
WHERE calendar_id=?",
|
||||
$prop['id']
|
||||
);
|
||||
|
||||
// TODO: also delete linked attachments
|
||||
|
||||
|
||||
// events and attachments will be deleted by foreign key cascade
|
||||
|
||||
$query = $this->rc->db->query(
|
||||
"DELETE FROM " . $this->db_calendars . "
|
||||
WHERE calendar_id=?",
|
||||
$prop['id']
|
||||
);
|
||||
|
||||
|
||||
return $this->rc->db->affected_rows($query);
|
||||
}
|
||||
|
||||
|
@ -213,11 +206,28 @@ class database_driver extends calendar_driver
|
|||
$event['alarms'],
|
||||
$event['notifyat']
|
||||
);
|
||||
|
||||
if ($success = $this->rc->db->insert_id($this->sequence_events))
|
||||
|
||||
$event_id = $this->rc->db->insert_id($this->sequence_events);
|
||||
|
||||
if ($event_id) {
|
||||
// add attachments
|
||||
if (!empty($event['attachments'])) {
|
||||
foreach ($event['attachments'] as $attachment) {
|
||||
$attachment = $this->rc->plugins->exec_hook('attachment_get', $attachment);
|
||||
|
||||
if (!$attachment['data']) {
|
||||
$attachments['data'] = file_get_contents($attachment['path']);
|
||||
}
|
||||
|
||||
$this->add_attachment($attachment, $event_id);
|
||||
unset($attachment);
|
||||
}
|
||||
}
|
||||
|
||||
$this->_update_recurring($event);
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
return $event_id;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -403,11 +413,33 @@ class database_driver extends calendar_driver
|
|||
),
|
||||
$event['id']
|
||||
);
|
||||
|
||||
|
||||
$success = $this->rc->db->affected_rows($query);
|
||||
|
||||
// add attachments
|
||||
if ($success && !empty($event['attachments'])) {
|
||||
foreach ($event['attachments'] as $attachment) {
|
||||
$attachment = $this->rc->plugins->exec_hook('attachment_get', $attachment);
|
||||
|
||||
if (!$attachment['data']) {
|
||||
$attachments['data'] = file_get_contents($attachment['path']);
|
||||
}
|
||||
|
||||
$this->add_attachment($attachment, $event['id']);
|
||||
unset($attachment);
|
||||
}
|
||||
}
|
||||
|
||||
// remove attachments
|
||||
if ($success && !empty($event['deleted_attachments'])) {
|
||||
foreach ($event['deleted_attachments'] as $attachment) {
|
||||
$this->remove_attachment($attachment, $event['id']);
|
||||
}
|
||||
}
|
||||
|
||||
if ($success && $update_recurring)
|
||||
$this->_update_recurring($event);
|
||||
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
|
@ -736,19 +768,110 @@ class database_driver extends calendar_driver
|
|||
/**
|
||||
* Save an attachment related to the given event
|
||||
*/
|
||||
public function add_attachment($attachment, $event_id)
|
||||
private function add_attachment($attachment, $event_id)
|
||||
{
|
||||
// TBD.
|
||||
return false;
|
||||
$query = $this->rc->db->query(sprintf(
|
||||
"INSERT INTO " . $this->db_attachments .
|
||||
" (event_id, filename, mimetype, size, data)" .
|
||||
" VALUES (?, ?, ?, ?, ?)",
|
||||
$event_id,
|
||||
$attachment['name'],
|
||||
$attachment['mimetype'],
|
||||
strlen($attachment['data']),
|
||||
base64_encode($attachment['data']),
|
||||
);
|
||||
|
||||
return $this->rc->db->affected_rows($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a specific attachment from the given event
|
||||
*/
|
||||
public function remove_attachment($attachment, $event_id)
|
||||
private function remove_attachment($attachment_id, $event_id)
|
||||
{
|
||||
// TBD.
|
||||
return false;
|
||||
$query = $this->rc->db->query(
|
||||
"DELETE FROM " . $this->db_attachments .
|
||||
" WHERE attachment_id = ?" .
|
||||
" AND event_id IN (SELECT event_id FROM " . $this->db_events .
|
||||
" WHERE event_id = ?" .
|
||||
" AND calendar_id IN (" . $this->calendar_ids . "))",
|
||||
$attachment_id,
|
||||
$event_id
|
||||
);
|
||||
|
||||
return $this->rc->db->affected_rows($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* List attachments of specified event
|
||||
*/
|
||||
public function list_attachments($event)
|
||||
{
|
||||
$attachments = array();
|
||||
|
||||
if (!empty($this->rc->user->ID)) {
|
||||
$result = $this->rc->db->query(
|
||||
"SELECT attachment_id AS id, filename AS name, mimetype, size " .
|
||||
" FROM " . $this->db_attachments .
|
||||
" WHERE user_id=?".
|
||||
" AND event_id=?".
|
||||
"ORDER BY filename",
|
||||
$this->rc->user->ID,
|
||||
$event['id']
|
||||
);
|
||||
|
||||
while ($result && ($arr = $this->rc->db->fetch_assoc($result))) {
|
||||
$attachments[] = $arr;
|
||||
}
|
||||
}
|
||||
|
||||
return $attachments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attachment properties
|
||||
*/
|
||||
public function get_attachment($id, $event)
|
||||
{
|
||||
if (!empty($this->rc->user->ID)) {
|
||||
$result = $this->rc->db->query(
|
||||
"SELECT attachment_id AS id, filename AS name, mimetype, size " .
|
||||
" FROM " . $this->db_attachments .
|
||||
" WHERE attachment_id=?".
|
||||
" AND event_id=?".
|
||||
$id,
|
||||
$event['id']
|
||||
);
|
||||
|
||||
if ($result && ($arr = $this->rc->db->fetch_assoc($result))) {
|
||||
return $arr;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attachment body
|
||||
*/
|
||||
public function get_attachment_body($id, $event)
|
||||
{
|
||||
if (!empty($this->rc->user->ID)) {
|
||||
$result = $this->rc->db->query(
|
||||
"SELECT data " .
|
||||
" FROM " . $this->db_attachments .
|
||||
" WHERE attachment_id=?".
|
||||
" AND event_id=?".
|
||||
$id,
|
||||
$event['id']
|
||||
);
|
||||
|
||||
if ($result && ($arr = $this->rc->db->fetch_assoc($result))) {
|
||||
return base64_decode($arr['data']);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -23,6 +23,7 @@ class kolab_calendar
|
|||
public $id;
|
||||
public $ready = false;
|
||||
public $readonly = true;
|
||||
public $attachments = true;
|
||||
|
||||
private $cal;
|
||||
private $storage;
|
||||
|
@ -145,6 +146,15 @@ class kolab_calendar
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Getter for the attachment body
|
||||
*/
|
||||
public function get_attachment_body($id)
|
||||
{
|
||||
return $this->storage->getAttachment($id);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Getter for a single event object
|
||||
*/
|
||||
|
@ -206,7 +216,7 @@ class kolab_calendar
|
|||
$events = array_merge($events, $this->_get_recurring_events($event, $start, $end));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $events;
|
||||
}
|
||||
|
||||
|
@ -222,7 +232,7 @@ class kolab_calendar
|
|||
{
|
||||
if (!is_array($event))
|
||||
return false;
|
||||
|
||||
|
||||
//generate new event from RC input
|
||||
$object = $this->_from_rcube_event($event);
|
||||
$saved = $this->storage->save($object);
|
||||
|
@ -415,10 +425,24 @@ class kolab_calendar
|
|||
$rrule['EXDATE'][] = strtotime($excl . date(' H:i:s', $rec['start-date'])); // use time of event start
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$sensitivity_map = array_flip($this->sensitivity_map);
|
||||
$priority_map = array_flip($this->priority_map);
|
||||
|
||||
|
||||
// @TODO: Horde code assumes that there will be no more than
|
||||
// one file with the same name, while this is not required by MIME format
|
||||
// and not forced by the Calendar UI
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'id' => $rec['uid'],
|
||||
'uid' => $rec['uid'],
|
||||
|
@ -431,6 +455,7 @@ class kolab_calendar
|
|||
'recurrence' => $rrule,
|
||||
'alarms' => $alarm_value . $alarm_unit,
|
||||
'categories' => $rec['categories'],
|
||||
'attachments' => $attachments,
|
||||
'free_busy' => $rec['show-time-as'],
|
||||
'priority' => isset($priority_map[$rec['priority']]) ? $priority_map[$rec['priority']] : 1,
|
||||
'sensitivity' => $sensitivity_map[$rec['sensitivity']],
|
||||
|
@ -555,7 +580,18 @@ class kolab_calendar
|
|||
$object['start-date'] += $tz_offset - date('Z'); // because Horde_Kolab_Format_Date::encodeDate() uses strftime()
|
||||
$object['_is_all_day'] = 1;
|
||||
}
|
||||
|
||||
|
||||
// in Horde attachments are indexed by name
|
||||
$object['_attachments'] = array();
|
||||
if (!empty($event['attachments'])) {
|
||||
foreach ($event['attachments'] as $idx => $attachment) {
|
||||
// Roundcube ID has nothing to Horde ID, remove it
|
||||
unset($attachment['id']);
|
||||
$object['_attachments'][$attachment['name']] = $attachment;
|
||||
unset($event['attachments'][$idx]);
|
||||
}
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ class kolab_driver extends calendar_driver
|
|||
// features this backend supports
|
||||
public $alarms = true;
|
||||
public $attendees = false;
|
||||
public $attachments = false;
|
||||
public $attachments = true;
|
||||
public $categoriesimmutable = true;
|
||||
|
||||
private $rc;
|
||||
|
@ -229,8 +229,20 @@ class kolab_driver extends calendar_driver
|
|||
public function new_event($event)
|
||||
{
|
||||
$cid = $event['calendar'] ? $event['calendar'] : reset(array_keys($this->calendars));
|
||||
if ($storage = $this->calendars[$cid])
|
||||
if ($storage = $this->calendars[$cid]) {
|
||||
// handle attachments to add
|
||||
if (!empty($event['attachments'])) {
|
||||
foreach ($event['attachments'] as $idx => $attachment) {
|
||||
// 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
|
||||
$attachment = $this->cal->rc->plugins->exec_hook('attachment_get', $attachment);
|
||||
|
||||
$event['attachments'][$idx]['content'] = $attachment['data'] ? $attachment['data'] : file_get_contents($attachment['path']);
|
||||
}
|
||||
}
|
||||
|
||||
return $storage->insert_event($event);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -327,21 +339,52 @@ class kolab_driver extends calendar_driver
|
|||
{
|
||||
if (!($storage = $this->calendars[$event['calendar']]))
|
||||
return false;
|
||||
|
||||
|
||||
$success = false;
|
||||
$savemode = 'all';
|
||||
$attachments = array();
|
||||
$old = $master = $storage->get_event($event['id']);
|
||||
|
||||
|
||||
// delete existing attachment(s)
|
||||
if (!empty($event['deleted_attachments'])) {
|
||||
foreach ($event['deleted_attachments'] as $attachment) {
|
||||
if (!empty($old['attachments'])) {
|
||||
foreach ($old['attachments'] as $idx => $att) {
|
||||
if ($att['id'] == $attachment) {
|
||||
unset($old['attachments'][$idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handle attachments to add
|
||||
if (!empty($event['attachments'])) {
|
||||
foreach ($event['attachments'] as $attachment) {
|
||||
// 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
|
||||
$attachment = $this->cal->rc->plugins->exec_hook('attachment_get', $attachment);
|
||||
|
||||
$attachments[] = array(
|
||||
'name' => $attachment['name'],
|
||||
'type' => $attachment['mimetype'],
|
||||
'content' => $attachment['data'] ? $attachment['data'] : file_get_contents($attachment['path']),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$event['attachments'] = array_merge($old['attachments'], $attachments);
|
||||
|
||||
// modify a recurring event, check submitted savemode to do the right things
|
||||
if ($old['recurrence'] || $old['recurrence_id']) {
|
||||
$master = $old['recurrence_id'] ? $storage->get_event($old['recurrence_id']) : $old;
|
||||
$savemode = $event['savemode'];
|
||||
}
|
||||
|
||||
|
||||
// keep saved exceptions (not submitted by the client)
|
||||
if ($old['recurrence']['EXDATE'])
|
||||
$event['recurrence']['EXDATE'] = $old['recurrence']['EXDATE'];
|
||||
|
||||
|
||||
switch ($savemode) {
|
||||
case 'new':
|
||||
// save submitted data as new (non-recurring) event
|
||||
|
@ -525,23 +568,51 @@ class kolab_driver extends calendar_driver
|
|||
|
||||
return $this->rc->db->affected_rows($query);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Save an attachment related to the given event
|
||||
* List attachments from the given event
|
||||
*/
|
||||
public function add_attachment($attachment, $event_id)
|
||||
public function list_attachments($event)
|
||||
{
|
||||
|
||||
if (!($storage = $this->calendars[$event['calendar']]))
|
||||
return false;
|
||||
|
||||
$event = $storage->get_event($event['id']);
|
||||
|
||||
return $event['attachments'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a specific attachment from the given event
|
||||
* Get attachment properties
|
||||
*/
|
||||
public function remove_attachment($attachment, $event_id)
|
||||
public function get_attachment($id, $event)
|
||||
{
|
||||
|
||||
if (!($storage = $this->calendars[$event['calendar']]))
|
||||
return false;
|
||||
|
||||
$event = $storage->get_event($event['id']);
|
||||
|
||||
if ($event && !empty($event['attachments'])) {
|
||||
foreach ($event['attachments'] as $att) {
|
||||
if ($att['id'] == $id) {
|
||||
return $att;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attachment body
|
||||
*/
|
||||
public function get_attachment_body($id, $event)
|
||||
{
|
||||
if (!($storage = $this->calendars[$event['calendar']]))
|
||||
return false;
|
||||
|
||||
return $storage->get_attachment_body($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* List availabale categories
|
||||
|
|
|
@ -461,4 +461,60 @@ class calendar_ui
|
|||
|
||||
return $select_prefix->show() . ' ' . $select_wday->show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the form for event attachments upload
|
||||
*/
|
||||
function attachments_form($attrib = array())
|
||||
{
|
||||
// add ID if not given
|
||||
if (!$attrib['id'])
|
||||
$attrib['id'] = 'rcmUploadForm';
|
||||
|
||||
// find max filesize value
|
||||
$max_filesize = parse_bytes(ini_get('upload_max_filesize'));
|
||||
$max_postsize = parse_bytes(ini_get('post_max_size'));
|
||||
if ($max_postsize && $max_postsize < $max_filesize)
|
||||
$max_filesize = $max_postsize;
|
||||
|
||||
$this->rc->output->set_env('max_filesize', $max_filesize);
|
||||
|
||||
$max_filesize = show_bytes($max_filesize);
|
||||
|
||||
$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('buttons', $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))))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate HTML element for attachments list
|
||||
*/
|
||||
function attachments_list($attrib = array())
|
||||
{
|
||||
if (!$attrib['id'])
|
||||
$attrib['id'] = 'rcmAttachmentList';
|
||||
|
||||
$skin_path = $this->rc->config->get('skin_path');
|
||||
if ($attrib['deleteicon']) {
|
||||
$_SESSION['calendar_deleteicon'] = $skin_path . $attrib['deleteicon'];
|
||||
$this->rc->output->set_env('deleteicon', $skin_path . $attrib['deleteicon']);
|
||||
}
|
||||
if ($attrib['cancelicon'])
|
||||
$this->rc->output->set_env('cancelicon', $skin_path . $attrib['cancelicon']);
|
||||
if ($attrib['loadingicon'])
|
||||
$this->rc->output->set_env('loadingicon', $skin_path . $attrib['loadingicon']);
|
||||
|
||||
$this->rc->output->add_gui_object('attachmentlist', $attrib['id']);
|
||||
|
||||
return html::tag('ul', $attrib, '', html::$common_attrib);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -247,6 +247,71 @@ a.miniColors-trigger {
|
|||
margin-top: -3px;
|
||||
}
|
||||
|
||||
#attachmentcontainer
|
||||
{
|
||||
position: absolute;
|
||||
top: 80px;
|
||||
left: 20px;
|
||||
right: 20px;
|
||||
bottom: 20px;
|
||||
}
|
||||
|
||||
#attachmentframe
|
||||
{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 1px solid #999999;
|
||||
background-color: #F9F9F9;
|
||||
}
|
||||
|
||||
.attachments-list ul
|
||||
{
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
list-style-image: none;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.attachments-list ul li
|
||||
{
|
||||
height: 18px;
|
||||
font-size: 12px;
|
||||
padding-left: 2px;
|
||||
padding-top: 2px;
|
||||
padding-right: 4px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
-o-text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.attachments-list ul li img
|
||||
{
|
||||
padding-right: 2px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.attachments-list ul li a
|
||||
{
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.attachments-list ul li a:hover
|
||||
{
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#eventshow .attachments-list ul
|
||||
{
|
||||
display: block;
|
||||
}
|
||||
|
||||
#eventshow .attachments-list ul li
|
||||
{
|
||||
float: left;
|
||||
}
|
||||
|
||||
|
||||
/* jQuery UI overrides */
|
||||
|
||||
#eventshow h1 {
|
||||
|
|
52
plugins/calendar/skins/default/templates/attachment.html
Normal file
52
plugins/calendar/skins/default/templates/attachment.html
Normal file
|
@ -0,0 +1,52 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title><roundcube:object name="pagetitle" /></title>
|
||||
<roundcube:include file="/includes/links.html" />
|
||||
</head>
|
||||
<body class="extwin">
|
||||
|
||||
<roundcube:include file="/includes/header.html" />
|
||||
|
||||
<div id="partheader">
|
||||
<!--
|
||||
<roundcube:object name="attachmentControls" cellpadding="2" cellspacing="0" />
|
||||
-->
|
||||
<div style="position:absolute; top:2px; right:0; width:12em; text-align:right">
|
||||
[<a href="#close" class="closelink" onclick="self.close()"><roundcube:label name="close" /></a>]
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="attachmentcontainer">
|
||||
<roundcube:object name="attachmentFrame" id="attachmentframe" width="100%" height="100%" />
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title><roundcube:object name="pagetitle" /></title>
|
||||
<roundcube:include file="/includes/links.html" />
|
||||
</head>
|
||||
<body class="extwin">
|
||||
|
||||
<roundcube:include file="/includes/header.html" />
|
||||
|
||||
<div id="partheader">
|
||||
<!--
|
||||
<roundcube:object name="attachmentControls" cellpadding="2" cellspacing="0" />
|
||||
-->
|
||||
<div style="position:absolute; top:2px; right:0; width:12em; text-align:right">
|
||||
[<a href="#close" class="closelink" onclick="self.close()"><roundcube:label name="close" /></a>]
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="attachmentcontainer">
|
||||
<roundcube:object name="attachmentFrame" id="attachmentframe" width="100%" height="100%" />
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -7,6 +7,7 @@
|
|||
<script type="text/javascript" src="/functions.js"></script>
|
||||
</head>
|
||||
<body class="calendarmain">
|
||||
|
||||
<roundcube:include file="/includes/taskbar.html" />
|
||||
<roundcube:include file="/includes/header.html" />
|
||||
|
||||
|
@ -75,10 +76,14 @@
|
|||
<label><roundcube:label name="calendar.sensitivity" /></label>
|
||||
<span class="event-text"></span>
|
||||
</div>
|
||||
<div class="event-section" id="event-attachments">
|
||||
<label><roundcube:label name="attachments" /></label>
|
||||
<span class="event-text attachments-list"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="eventedit">
|
||||
<form id="eventtabs" action="#">
|
||||
<form id="eventtabs" action="#" method="post">
|
||||
<ul>
|
||||
<li><a href="#event-tab-1"><roundcube:label name="calendar.tabsummary" /></a></li>
|
||||
<li id="edit-tab-recurrence"><a href="#event-tab-2"><roundcube:label name="calendar.tabrecurrence" /></a></li>
|
||||
|
@ -161,14 +166,19 @@
|
|||
</div>
|
||||
<!-- attendees list -->
|
||||
<div id="event-tab-3">
|
||||
|
||||
|
||||
</div>
|
||||
<!-- attachments list -->
|
||||
<!-- attachments list (with upload form) -->
|
||||
<div id="event-tab-4">
|
||||
|
||||
<div class="border-after" id="edit-attachments-form">
|
||||
<roundcube:object name="plugin.attachments_form" id="calendar-attachment-form" attachmentFieldSize="30" />
|
||||
</div>
|
||||
<span id="edit-attachments" class="attachments-list">
|
||||
<roundcube:object name="plugin.attachments_list" id="attachmentlist" deleteIcon="/images/icons/delete.png" cancelIcon="/images/icons/delete.png" loadingIcon="/images/display/loading_blue.gif" />
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
<roundcube:object name="plugin.edit_recurring_warning" class="edit-recurring-warning" style="display:none" />
|
||||
</div>
|
||||
|
||||
|
@ -235,4 +245,4 @@ $(document).ready(function(e){
|
|||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
|
||||
class kolab_folders extends rcube_plugin
|
||||
{
|
||||
public $task = '?(?!login).*';
|
||||
|
||||
public $types = array('mail', 'event', 'journal', 'task', 'note', 'contact');
|
||||
public $mail_types = array('inbox', 'drafts', 'sentitems', 'outbox', 'wastebasket', 'junkemail');
|
||||
private $rc;
|
||||
|
|
Loading…
Add table
Reference in a new issue