summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksander Machniak <machniak@kolabsys.com>2012-04-18 17:51:51 (GMT)
committerAleksander Machniak <machniak@kolabsys.com>2012-04-18 17:51:51 (GMT)
commitd5c5194dc5112cfd72b40a31209722a756590e99 (patch)
tree6c212564d0e09a83a18d0fe322f224494f6f6d88
parent3aab9325f173964e376016ec20459a177d77ff7a (diff)
parente935cdbf788bb941525dad4a28003a2c8c1fe458 (diff)
downloadroundcubemail-plugins-kolab-d5c5194dc5112cfd72b40a31209722a756590e99.tar.gz
Merge branch 'master' of ssh://git.kolabsys.com/git/roundcube
-rw-r--r--plugins/calendar/calendar.php140
-rw-r--r--plugins/calendar/calendar_ui.js46
-rw-r--r--plugins/calendar/drivers/database/database_driver.php2
-rw-r--r--plugins/calendar/drivers/kolab/kolab_calendar.php2
-rw-r--r--plugins/calendar/drivers/kolab/kolab_driver.php45
-rw-r--r--plugins/calendar/lib/calendar_ical.php16
-rw-r--r--plugins/calendar/lib/calendar_itip.php4
-rw-r--r--plugins/calendar/lib/calendar_ui.php12
-rw-r--r--plugins/calendar/lib/js/fullcalendar.js50
-rw-r--r--plugins/calendar/localization/de_CH.inc11
-rw-r--r--plugins/calendar/localization/de_DE.inc11
-rw-r--r--plugins/calendar/localization/en_US.inc12
-rw-r--r--plugins/calendar/package.xml2
-rw-r--r--plugins/calendar/skins/default/calendar.css33
-rw-r--r--plugins/calendar/skins/default/fullcalendar.css10
-rw-r--r--plugins/calendar/skins/default/print.css4
-rw-r--r--plugins/calendar/skins/default/templates/calendar.html24
-rw-r--r--plugins/calendar/skins/larry/.htaccess5
-rw-r--r--plugins/calendar/skins/larry/calendar.css1332
-rw-r--r--plugins/calendar/skins/larry/templates/attachment.html36
-rw-r--r--plugins/calendar/skins/larry/templates/calendar.html202
-rw-r--r--plugins/calendar/skins/larry/templates/eventedit.html100
-rw-r--r--plugins/calendar/skins/larry/templates/freebusylegend.html7
-rw-r--r--plugins/calendar/skins/larry/templates/itipattend.html36
-rw-r--r--plugins/calendar/skins/larry/templates/kolabacl.html24
-rw-r--r--plugins/calendar/skins/larry/templates/kolabform.html9
-rw-r--r--plugins/calendar/skins/larry/templates/print.html28
-rw-r--r--plugins/kolab_addressbook/kolab_addressbook.php18
-rw-r--r--plugins/kolab_addressbook/lib/rcube_kolab_contacts.php53
-rw-r--r--plugins/kolab_addressbook/localization/de_CH.inc46
-rw-r--r--plugins/kolab_addressbook/localization/de_DE.inc46
-rw-r--r--plugins/kolab_addressbook/package.xml2
-rw-r--r--plugins/kolab_auth/kolab_auth.php22
-rw-r--r--plugins/kolab_auth/localization/de_CH.inc5
-rw-r--r--plugins/kolab_auth/localization/de_DE.inc5
-rw-r--r--plugins/kolab_auth/package.xml8
-rw-r--r--plugins/kolab_config/package.xml2
-rw-r--r--plugins/kolab_core/package.xml2
-rw-r--r--plugins/kolab_core/rcube_kolab.php39
-rw-r--r--plugins/kolab_folders/config.inc.php.dist4
-rw-r--r--plugins/kolab_folders/kolab_folders.php93
-rw-r--r--plugins/kolab_folders/localization/de_CH.inc24
-rw-r--r--plugins/kolab_folders/localization/de_DE.inc24
-rw-r--r--plugins/kolab_folders/package.xml2
-rw-r--r--plugins/kolab_zpush/kolab_zpush.php42
-rw-r--r--plugins/kolab_zpush/localization/de_CH.inc12
-rw-r--r--plugins/kolab_zpush/localization/de_DE.inc32
-rw-r--r--plugins/kolab_zpush/localization/en_US.inc1
-rw-r--r--plugins/kolab_zpush/package.xml11
-rwxr-xr-xplugins/kolab_zpush/skins/larry/alarm-clock.pngbin0 -> 841 bytes
-rw-r--r--plugins/kolab_zpush/skins/larry/config.css135
-rw-r--r--plugins/kolab_zpush/skins/larry/foldertypes.pngbin0 -> 2291 bytes
-rw-r--r--plugins/kolab_zpush/skins/larry/pointer-left.pngbin0 -> 1283 bytes
-rwxr-xr-xplugins/kolab_zpush/skins/larry/synchronize.pngbin0 -> 836 bytes
-rw-r--r--plugins/kolab_zpush/skins/larry/templates/config.html71
-rw-r--r--plugins/owncloud/LICENSE661
-rw-r--r--plugins/owncloud/config.inc.php.dist4
-rw-r--r--plugins/owncloud/localization/en_US.inc6
-rw-r--r--plugins/owncloud/localization/pl_PL.inc6
-rw-r--r--plugins/owncloud/owncloud.php84
-rw-r--r--plugins/owncloud/skins/default/cloud.pngbin0 -> 1068 bytes
-rw-r--r--plugins/owncloud/skins/default/owncloud.css12
-rw-r--r--plugins/owncloud/skins/default/templates/owncloud.html19
-rw-r--r--plugins/owncloud/skins/larry/cloud.pngbin0 -> 1844 bytes
-rw-r--r--plugins/owncloud/skins/larry/owncloud.css14
-rw-r--r--plugins/owncloud/skins/larry/templates/owncloud.html20
66 files changed, 3481 insertions, 247 deletions
diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php
index 39496ca..312a51e 100644
--- a/plugins/calendar/calendar.php
+++ b/plugins/calendar/calendar.php
@@ -38,6 +38,7 @@ class calendar extends rcube_plugin
public $home; // declare public to be used in other classes
public $urlbase;
public $timezone;
+ public $timezone_offset;
public $gmt_offset;
public $ical;
@@ -96,10 +97,11 @@ class calendar extends rcube_plugin
$this->add_texts('localization/', $this->rc->task == 'calendar' && (!$this->rc->action || $this->rc->action == 'print'));
// set user's timezone
- $this->timezone = $this->rc->config->get('timezone');
- $this->dst_active = $this->rc->config->get('dst_active');
- $this->gmt_offset = ($this->timezone + $this->dst_active) * 3600;
- $this->user_timezone = new DateTimeZone($this->timezone ? timezone_name_from_abbr("", $this->gmt_offset, $this->dst_active) : 'GMT');
+ $this->timezone = new DateTimeZone($this->rc->config->get('timezone', 'GMT'));
+ $now = new DateTime('now', $this->timezone);
+ $this->timezone_offset = $now->format('Z') / 3600;
+ $this->dst_active = $now->format('I');
+ $this->gmt_offset = $now->getOffset();
require($this->home . '/lib/calendar_ui.php');
$this->ui = new calendar_ui($this);
@@ -119,6 +121,9 @@ class calendar extends rcube_plugin
if ($this->rc->action == 'attend' && !empty($_REQUEST['_t'])) {
$this->add_hook('startup', array($this, 'itip_attend_response'));
}
+ else if ($this->rc->action == 'feed' && !empty($_REQUEST['_cal'])) {
+ $this->add_hook('startup', array($this, 'ical_feed_export'));
+ }
else if ($this->rc->task == 'calendar' && $this->rc->action != 'save-pref') {
if ($this->rc->action != 'upload') {
$this->load_driver();
@@ -172,8 +177,9 @@ class calendar extends rcube_plugin
'command' => 'calendar-create-from-mail',
'label' => 'calendar.createfrommail',
'type' => 'link',
- 'classact' => 'calendarlink active',
- 'class' => 'calendarlink',
+ 'classact' => 'icon calendarlink active',
+ 'class' => 'icon calendarlink',
+ 'innerclass' => 'icon calendar',
))),
'messagemenu');
}
@@ -232,6 +238,26 @@ class calendar extends rcube_plugin
return $this->ical;
}
+ /**
+ *
+ */
+ public function get_default_calendar($writeable = false)
+ {
+ $cal_id = $this->rc->config->get('calendar_default_calendar');
+ $calendars = $this->driver->list_calendars();
+ $calendar = $calendars[$cal_id] ? $calendars[$cal_id] : null;
+ if (!$calendar || ($writeable && $calendar['readonly'])) {
+ foreach ($calendars as $cal) {
+ if (!$writeable || !$cal['readonly']) {
+ $calendar = $cal;
+ break;
+ }
+ }
+ }
+
+ return $calendar;
+ }
+
/**
* Render the main calendar view from skin template
@@ -890,7 +916,7 @@ class calendar extends rcube_plugin
/**
* Construct the ics file for exporting events to iCalendar format;
*/
- function export_events()
+ function export_events($terminate = true)
{
$start = get_input_value('start', RCUBE_INPUT_GET);
$end = get_input_value('end', RCUBE_INPUT_GET);
@@ -903,14 +929,73 @@ class calendar extends rcube_plugin
$calname = $calendars[$calid]['name'] ? $calendars[$calid]['name'] : $calid;
$events = $this->driver->load_events($start, $end, null, $calid, 0);
}
+ else
+ $events = array();
header("Content-Type: text/calendar");
header("Content-Disposition: inline; filename=".$calname.'.ics');
-
+
$this->get_ical()->export($events, '', true);
+
+ if ($terminate)
+ exit;
+ }
+
+
+ /**
+ * Handler for iCal feed requests
+ */
+ function ical_feed_export()
+ {
+ // process HTTP auth info
+ if (!empty($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) {
+ $_POST['_user'] = $_SERVER['PHP_AUTH_USER']; // used for rcmail::autoselect_host()
+ $auth = $this->rc->plugins->exec_hook('authenticate', array(
+ 'host' => $this->rc->autoselect_host(),
+ 'user' => trim($_SERVER['PHP_AUTH_USER']),
+ 'pass' => $_SERVER['PHP_AUTH_PW'],
+ 'cookiecheck' => true,
+ 'valid' => true,
+ ));
+ if ($auth['valid'] && !$auth['abort'])
+ $this->rc->login($auth['user'], $auth['pass'], $auth['host']);
+ }
+
+ // require HTTP auth
+ if (empty($_SESSION['user_id'])) {
+ header('WWW-Authenticate: Basic realm="Roundcube Calendar"');
+ header('HTTP/1.0 401 Unauthorized');
+ exit;
+ }
+
+ // decode calendar feed hash
+ $format = 'ics';
+ $calhash = get_input_value('_cal', RCUBE_INPUT_GET);
+ if (preg_match(($suff_regex = '/\.([a-z0-9]{3,5})$/i'), $calhash, $m)) {
+ $format = strtolower($m[1]);
+ $calhash = preg_replace($suff_regex, '', $calhash);
+ }
+
+ if (!strpos($calhash, ':'))
+ $calhash = base64_decode($calhash);
+
+ list($user, $_GET['source']) = explode(':', $calhash, 2);
+
+ // sanity check user
+ if ($this->rc->user->get_username() == $user) {
+ $this->load_driver();
+ $this->export_events(false);
+ }
+ else {
+ header('HTTP/1.0 404 Not Found');
+ }
+
+ // don't save session data
+ session_destroy();
exit;
}
+
/**
*
*/
@@ -939,7 +1024,7 @@ class calendar extends rcube_plugin
$settings['agenda_sections'] = $this->rc->config->get('calendar_agenda_sections', $this->defaults['calendar_agenda_sections']);
$settings['event_coloring'] = (int)$this->rc->config->get('calendar_event_coloring', $this->defaults['calendar_event_coloring']);
$settings['time_indicator'] = (int)$this->rc->config->get('calendar_time_indicator', $this->defaults['calendar_time_indicator']);
- $settings['timezone'] = $this->timezone;
+ $settings['timezone'] = $this->timezone_offset;
$settings['dst'] = $this->dst_active;
// localization
@@ -1052,6 +1137,10 @@ class calendar extends rcube_plugin
if ($event['recurrence'])
$event['recurrence_text'] = $this->_recurrence_text($event['recurrence']);
+ foreach ((array)$event['attachments'] as $k => $attachment) {
+ $event['attachments'][$k]['classname'] = rcmail_filetype2classname($attachment['mimetype'], $attachment['name']);
+ }
+
return 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
@@ -1373,6 +1462,7 @@ class calendar extends rcube_plugin
$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'),
), $button);
@@ -1383,6 +1473,7 @@ class calendar extends rcube_plugin
'html' => $content,
'name' => $attachment['name'],
'mimetype' => $attachment['mimetype'],
+ 'classname' => rcmail_filetype2classname($attachment['mimetype'], $attachment['name']),
'complete' => true), $uploadid);
}
else { // upload failed
@@ -1661,8 +1752,8 @@ class calendar extends rcube_plugin
}
// add timezone information
- if ($tzinfo && ($tzname = $this->user_timezone->getName())) {
- $fromto .= ' (' . $tzname . ')';
+ if ($tzinfo && ($tzname = $this->timezone->getName())) {
+ $fromto .= ' (' . strtr($tzname, '_', ' ') . ')';
}
return $fromto;
@@ -1873,13 +1964,26 @@ class calendar extends rcube_plugin
break;
}
}
-
+
// send itip reply to organizer
if ($status && $itip->update_invitation($invitation, $invitation['attendee'], strtoupper($status))) {
$this->invitestatus = html::div('rsvp-status ' . strtolower($status), $this->gettext('youhave'.strtolower($status)));
}
else
$this->rc->output->command('display_message', $this->gettext('errorsaving'), 'error', -1);
+
+ // if user is logged in...
+ if ($this->rc->user->ID) {
+ $this->load_driver();
+ $invitation = $itip->get_invitation($token);
+
+ // save the event to his/her default calendar if not yet present
+ if (!$this->driver->get_event($this->event) && ($calendar = $this->get_default_calendar(true))) {
+ $invitation['event']['calendar'] = $calendar['id'];
+ if ($this->driver->new_event($invitation['event']))
+ $this->rc->output->command('display_message', $this->gettext(array('name' => 'importedsuccessfully', 'vars' => array('calendar' => $calendar['name']))), 'confirmation');
+ }
+ }
}
$this->register_handler('plugin.event_inviteform', array($this, 'itip_event_inviteform'));
@@ -1900,10 +2004,10 @@ class calendar extends rcube_plugin
/**
*
*/
- public function itip_event_inviteform($p)
+ public function itip_event_inviteform($attrib)
{
$hidden = new html_hiddenfield(array('name' => "_t", 'value' => $this->token));
- return html::tag('form', array('action' => $this->rc->url(array('task' => 'calendar', 'action' => 'attend')), 'method' => 'post', 'noclose' => true)) . $hidden->show();
+ return html::tag('form', array('action' => $this->rc->url(array('task' => 'calendar', 'action' => 'attend')), 'method' => 'post', 'noclose' => true) + $attrib) . $hidden->show();
}
/**
@@ -2299,7 +2403,7 @@ class calendar extends rcube_plugin
$default_port = 80;
if (rcube_https_check()) {
$schema = 'https';
- $default_port = 143;
+ $default_port = 443;
}
$url = $schema . '://' . $_SERVER['HTTP_HOST'];
if ($_SERVER['SERVER_PORT'] != $default_port)
@@ -2311,4 +2415,10 @@ class calendar extends rcube_plugin
return $url;
}
+
+ public function ical_feed_hash($source)
+ {
+ return base64_encode($this->rc->user->get_username() . ':' . $source);
+ }
+
}
diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js
index 31481d3..a2aac7c 100644
--- a/plugins/calendar/calendar_ui.js
+++ b/plugins/calendar/calendar_ui.js
@@ -6,7 +6,7 @@
* @author Thomas Bruederli <bruederli@kolabsys.com>
*
* Copyright (C) 2010, Lazlo Westerhof <hello@lazlo.me>
- * Copyright (C) 2011, Kolab Systems AG <contact@kolabsys.com>
+ * Copyright (C) 2012, Kolab Systems AG <contact@kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
@@ -276,10 +276,12 @@ function rcube_calendar_ui(settings)
{
var i, id, len, img, content, li, elem,
ul = document.createElement('UL');
+ ul.className = 'attachmentslist';
for (i=0, len=list.length; i<len; i++) {
- li = document.createElement('LI');
elem = list[i];
+ li = document.createElement('LI');
+ li.className = elem.classname;
if (edit) {
rcmail.env.attachments[elem.id] = elem;
@@ -287,6 +289,7 @@ function rcube_calendar_ui(settings)
content = document.createElement('A');
content.href = '#delete';
content.title = rcmail.gettext('delete');
+ content.className = 'delete';
$(content).click({id: elem.id}, function(e) { remove_attachment(this, e.data.id); return false; });
if (!rcmail.env.deleteicon)
@@ -303,7 +306,8 @@ function rcube_calendar_ui(settings)
// name/link
content = document.createElement('A');
- content.innerHTML = list[i].name;
+ content.innerHTML = elem.name;
+ content.className = 'file';
content.href = '#load';
$(content).click({event: event, att: elem}, function(e) {
load_attachment(e.data.event, e.data.att); return false; });
@@ -343,7 +347,8 @@ function rcube_calendar_ui(settings)
$('#event-description').show().children('.event-text').html(text2html(event.description, 300, 6));
// render from-to in a nice human-readable way
- $('#event-date').html(Q(me.event_date_text(event))).show();
+ // -> now shown in dialog title
+ // $('#event-date').html(Q(me.event_date_text(event))).show();
if (event.recurrence && event.recurrence_text)
$('#event-repeat').show().children('.event-text').html(Q(event.recurrence_text));
@@ -432,7 +437,7 @@ function rcube_calendar_ui(settings)
modal: false,
resizable: !bw.ie6,
closeOnEscape: (!bw.ie6 && !bw.ie7), // disable for performance reasons
- title: null,
+ title: Q(me.event_date_text(event)),
close: function() {
$dialog.dialog('destroy').hide();
},
@@ -1953,6 +1958,26 @@ function rcube_calendar_ui(settings)
this.refresh(p);
};
+ // show URL of the given calendar in a dialog box
+ this.showurl = function(calendar)
+ {
+ var $dialog = $('#calendarurlbox').dialog('close');
+
+ if (calendar.feedurl) {
+ $dialog.dialog({
+ resizable: true,
+ closeOnEscape: true,
+ title: rcmail.gettext('showurl', 'calendar'),
+ close: function() {
+ $dialog.dialog("destroy").hide();
+ },
+ width: 520
+ }).show();
+
+ $('#calfeedurl').val(calendar.feedurl).select();
+ }
+ };
+
// refresh the calendar view after saving event data
this.refresh = function(p)
{
@@ -2126,15 +2151,15 @@ function rcube_calendar_ui(settings)
this.dialog_resize = function(id, height, width)
{
var win = $(window), w = win.width(), h = win.height();
- $(id).dialog('option', { height: Math.min(h-20, height+110), width: Math.min(w-20, width+50) })
+ $(id).dialog('option', { height: Math.min(h-20, height+130), width: Math.min(w-20, width+50) })
.dialog('option', 'position', ['center', 'center']); // only works in a separate call (!?)
};
// adjust calendar view size
this.view_resize = function()
{
- var footer = fc.fullCalendar('getView').name == 'table' ? $('#agendaoptions').height() + 2 : 0;
- fc.fullCalendar('option', 'height', $('#main').height() - footer);
+ var footer = fc.fullCalendar('getView').name == 'table' ? $('#agendaoptions').outerHeight() : 0;
+ fc.fullCalendar('option', 'height', $('#calendar').height() - footer);
};
@@ -2183,7 +2208,7 @@ function rcube_calendar_ui(settings)
var id = $(this).data('id');
rcmail.select_folder(id, 'rcmlical');
rcmail.enable_command('calendar-edit', true);
- rcmail.enable_command('calendar-remove', 'events-import', !me.calendars[id].readonly);
+ rcmail.enable_command('calendar-remove', 'events-import', 'calendar-showurl', true);
me.selected_calendar = id;
})
.dblclick(function(){ me.calendar_edit_dialog(me.calendars[me.selected_calendar]); })
@@ -2216,7 +2241,7 @@ function rcube_calendar_ui(settings)
month: viewdate.getMonth(),
year: viewdate.getFullYear(),
ignoreTimezone: true, // will treat the given date strings as in local (browser's) timezone
- height: $('#main').height(),
+ height: $('#calendar').height(),
eventSources: event_sources,
monthNames : settings['months'],
monthNamesShort : settings['months_short'],
@@ -2673,6 +2698,7 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
rcmail.register_command('calendar-edit', function(){ cal.calendar_edit_dialog(cal.calendars[cal.selected_calendar]); }, false);
rcmail.register_command('calendar-remove', function(){ cal.calendar_remove(cal.calendars[cal.selected_calendar]); }, false);
rcmail.register_command('events-import', function(){ cal.import_events(cal.calendars[cal.selected_calendar]); }, false);
+ rcmail.register_command('calendar-showurl', function(){ cal.showurl(cal.calendars[cal.selected_calendar]); }, false);
// search and export events
rcmail.register_command('export', function(){ rcmail.goto_url('export_events', { source:cal.selected_calendar }); }, true);
diff --git a/plugins/calendar/drivers/database/database_driver.php b/plugins/calendar/drivers/database/database_driver.php
index 83843d7..ba7f1e7 100644
--- a/plugins/calendar/drivers/database/database_driver.php
+++ b/plugins/calendar/drivers/database/database_driver.php
@@ -578,7 +578,7 @@ class database_driver extends calendar_driver
// read master if deleting a recurring event
if ($event['recurrence'] || $event['recurrence_id']) {
- $master = $event['recurrence_id'] ? $this->get_event(array('id' => $old['recurrence_id'])) : $event;
+ $master = $event['recurrence_id'] ? $this->get_event(array('id' => $event['recurrence_id'])) : $event;
$savemode = $event['_savemode'];
}
diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php
index a346037..ccd542a 100644
--- a/plugins/calendar/drivers/kolab/kolab_calendar.php
+++ b/plugins/calendar/drivers/kolab/kolab_calendar.php
@@ -145,7 +145,7 @@ class kolab_calendar
public function get_foldername()
{
$parts = explode('/', $this->imap_folder);
- return rcube_charset_convert(end($parts), 'UTF7-IMAP');
+ return rcube_charset::convert(end($parts), 'UTF7-IMAP');
}
/**
diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php
index 763dbba..0617800 100644
--- a/plugins/calendar/drivers/kolab/kolab_driver.php
+++ b/plugins/calendar/drivers/kolab/kolab_driver.php
@@ -78,7 +78,7 @@ class kolab_driver extends calendar_driver
// convert to UTF8 and sort
$names = array();
foreach ($folders as $folder)
- $names[$folder->name] = rcube_charset_convert($folder->name, 'UTF7-IMAP');
+ $names[$folder->name] = rcube_charset::convert($folder->name, 'UTF7-IMAP');
asort($names, SORT_LOCALE_STRING);
@@ -160,8 +160,8 @@ class kolab_driver extends calendar_driver
}
// subscribe to new calendar by default
- $this->rc->imap_connect();
- $this->rc->imap->subscribe($folder);
+ $storage = $this->rc->get_storage();
+ $storage->subscribe($folder);
// create ID
$id = rcube_kolab::folder_id($folder);
@@ -226,11 +226,11 @@ class kolab_driver extends calendar_driver
public function subscribe_calendar($prop)
{
if ($prop['id'] && ($cal = $this->calendars[$prop['id']])) {
- $this->rc->imap_connect();
+ $storage = $this->rc->get_storage();
if ($prop['active'])
- return $this->rc->imap->subscribe($cal->get_realname());
+ return $storage->subscribe($cal->get_realname());
else
- return $this->rc->imap->unsubscribe($cal->get_realname());
+ return $storage->unsubscribe($cal->get_realname());
}
return false;
@@ -246,14 +246,14 @@ class kolab_driver extends calendar_driver
*/
private function folder_update(&$prop)
{
- $folder = rcube_charset_convert($prop['name'], RCMAIL_CHARSET, 'UTF7-IMAP');
+ $folder = rcube_charset::convert($prop['name'], RCMAIL_CHARSET, 'UTF7-IMAP');
$oldfolder = $prop['oldname']; // UTF7
$parent = $prop['parent']; // UTF7
- $delimiter = $_SESSION['imap_delimiter'];
+ $storage = $this->rc->get_storage();
+ $delimiter = $storage->get_hierarchy_delimiter();
if (strlen($oldfolder)) {
- $this->rc->imap_connect();
- $options = $this->rc->imap->mailbox_info($oldfolder);
+ $options = $storage->folder_info($oldfolder);
}
if (!empty($options) && ($options['norename'] || $options['protected'])) {
@@ -285,14 +285,12 @@ class kolab_driver extends calendar_driver
}
else {
// add namespace prefix (when needed)
- $this->rc->imap_init();
- $folder = $this->rc->imap->mod_mailbox($folder, 'in');
+ $folder = $storage->mod_folder($folder, 'in');
}
// Check access rights to the parent folder
if (strlen($parent) && (!strlen($oldfolder) || $oldfolder != $folder)) {
- $this->rc->imap_connect();
- $parent_opts = $this->rc->imap->mailbox_info($parent);
+ $parent_opts = $storage->folder_info($parent);
if ($parent_opts['namespace'] != 'personal'
&& (empty($parent_opts['rights']) || !preg_match('/[ck]/', implode($parent_opts['rights'])))
) {
@@ -320,8 +318,8 @@ class kolab_driver extends calendar_driver
// TODO: also save 'showalarams' and other properties here
if ($result && $prop['color']) {
- if (!($meta_saved = $this->rc->imap->set_metadata($folder, array('/shared/vendor/kolab/color' => $prop['color'])))) // try in shared namespace
- $meta_saved = $this->rc->imap->set_metadata($folder, array('/private/vendor/kolab/color' => $prop['color'])); // try in private namespace
+ if (!($meta_saved = $storage->set_metadata($folder, array('/shared/vendor/kolab/color' => $prop['color'])))) // try in shared namespace
+ $meta_saved = $storage->set_metadata($folder, array('/private/vendor/kolab/color' => $prop['color'])); // try in private namespace
if ($meta_saved)
unset($prop['color']); // unsetting will prevent fallback to local user prefs
}
@@ -1016,7 +1014,8 @@ class kolab_driver extends calendar_driver
$hidden_fields[] = array('name' => 'oldname', 'value' => $folder);
- $delim = $_SESSION['imap_delimiter'];
+ $storage = $this->rc->get_storage();
+ $delim = $storage->get_hierarchy_delimiter();
$form = array();
if (strlen($folder)) {
@@ -1024,8 +1023,7 @@ class kolab_driver extends calendar_driver
array_pop($path_imap); // pop off name part
$path_imap = implode($path_imap, $delim);
- $this->rc->imap_connect();
- $options = $this->rc->imap->mailbox_info($folder);
+ $options = $storage->folder_info($folder);
}
else {
$path_imap = '';
@@ -1179,7 +1177,8 @@ class kolab_driver extends calendar_driver
$hidden_fields[] = array('name' => 'oldname', 'value' => $folder);
- $delim = $_SESSION['imap_delimiter'];
+ $storage = $this->rc->get_storage();
+ $delim = $storage->get_hierarchy_delimiter();
$form = array();
if (strlen($folder)) {
@@ -1187,14 +1186,16 @@ class kolab_driver extends calendar_driver
array_pop($path_imap); // pop off name part
$path_imap = implode($path_imap, $delim);
- $this->rc->imap_connect();
- $options = $this->rc->imap->mailbox_info($folder);
+ $options = $storage->folder_info($folder);
// Allow plugins to modify the form content (e.g. with ACL form)
$plugin = $this->rc->plugins->exec_hook('calendar_form_kolab',
array('form' => $form, 'options' => $options, 'name' => $folder));
}
+ if (!$plugin['form']['sharing']['content'])
+ $plugin['form']['sharing']['content'] = html::div('hint', $this->cal->gettext('aclnorights'));
+
return $plugin['form']['sharing']['content'];
}
diff --git a/plugins/calendar/lib/calendar_ical.php b/plugins/calendar/lib/calendar_ical.php
index 9a53672..c99fab3 100644
--- a/plugins/calendar/lib/calendar_ical.php
+++ b/plugins/calendar/lib/calendar_ical.php
@@ -40,8 +40,7 @@ class calendar_ical
private $rc;
private $cal;
- private $timezone = 'Z';
-
+
public $method;
public $events = array();
@@ -49,12 +48,6 @@ class calendar_ical
{
$this->cal = $cal;
$this->rc = $cal->rc;
-
- // compose timezone string
- if ($cal->timezone) {
- $hours = floor($cal->timezone + $cal->dst_active);
- $this->timezone = sprintf('%s%02d:%02d', ($hours >= 0 ? '+' : ''), $hours, ($cal->timezone - $hours) * 60);
- }
}
/**
@@ -313,7 +306,12 @@ class calendar_ical
private function _date2time($prop)
{
// create timestamp at 12:00 in user's timezone
- return is_array($prop) ? strtotime(sprintf('%04d%02d%02dT120000%s', $prop['year'], $prop['month'], $prop['mday'], $this->timezone)) : $prop;
+ if (is_array($prop)) {
+ $date = new DateTime(sprintf('%04d%02d%02dT120000', $prop['year'], $prop['month'], $prop['mday']), $this->cal->timezone);
+ console($prop, $date->format('r'));
+ return $date->getTimestamp();
+ }
+ return $prop;
}
diff --git a/plugins/calendar/lib/calendar_itip.php b/plugins/calendar/lib/calendar_itip.php
index 8b1a74b..6507b51 100644
--- a/plugins/calendar/lib/calendar_itip.php
+++ b/plugins/calendar/lib/calendar_itip.php
@@ -96,8 +96,8 @@ class calendar_itip
}
$message->headers($headers, true);
- $message->setTXTBody(rcube_message::format_flowed($mailbody, 79));
-
+ $message->setTXTBody(rcube_mime::format_flowed($mailbody, 79));
+
// finally send the message
return rcmail_deliver_message($message, $headers['X-Sender'], $mailto, $smtp_error);
}
diff --git a/plugins/calendar/lib/calendar_ui.php b/plugins/calendar/lib/calendar_ui.php
index a8c8980..35a5c9c 100644
--- a/plugins/calendar/lib/calendar_ui.php
+++ b/plugins/calendar/lib/calendar_ui.php
@@ -48,11 +48,11 @@ class calendar_ui
// add taskbar button
$this->cal->add_button(array(
- 'name' => 'calendar',
+ 'command' => 'calendar',
'class' => 'button-calendar',
+ 'classsel' => 'button-calendar button-selected',
+ 'innerclass' => 'button-inner',
'label' => 'calendar.calendar',
- 'href' => './?_task=calendar',
- 'onclick' => sprintf("%s.command('plugin.calendar');return false", JS_OBJECT_NAME),
), 'taskbar');
// load basic client script (which - unfortunately - requires fullcalendar)
@@ -168,6 +168,7 @@ class calendar_ui
$css .= " border-color: #$color;";
$css .= "}\n";
}
+ $css .= ".$class .handle { background-color: #$color; }";
}
return html::tag('style', array('type' => 'text/css'), $css);
@@ -191,6 +192,7 @@ class calendar_ui
$prop['freebusy'] = $this->cal->driver->freebusy;
$prop['attachments'] = $this->cal->driver->attachments;
$prop['undelete'] = $this->cal->driver->undelete;
+ $prop['feedurl'] = $this->cal->get_url(array('_cal' => $this->cal->ical_feed_hash($id) . '.ics', 'action' => 'feed'));
$jsenv[$id] = $prop;
$html_id = html_identifier($id);
@@ -202,7 +204,9 @@ class calendar_ui
$class .= ' '.$prop['class_name'];
$li .= html::tag('li', array('id' => 'rcmlical' . $html_id, 'class' => $class),
- html::tag('input', array('type' => 'checkbox', 'name' => '_cal[]', 'value' => $id, 'checked' => $prop['active']), '') . html::span(null, Q($prop['name'])));
+ html::tag('input', array('type' => 'checkbox', 'name' => '_cal[]', 'value' => $id, 'checked' => $prop['active']), '') .
+ html::span('handle', '&nbsp;') .
+ html::span('calname', Q($prop['name'])));
}
$this->rc->output->set_env('calendars', $jsenv);
diff --git a/plugins/calendar/lib/js/fullcalendar.js b/plugins/calendar/lib/js/fullcalendar.js
index b04c86e..bffc0e7 100644
--- a/plugins/calendar/lib/js/fullcalendar.js
+++ b/plugins/calendar/lib/js/fullcalendar.js
@@ -1,17 +1,18 @@
/**
* @preserve
- * FullCalendar v1.5.2
- * http://arshaw.com/fullcalendar/
+ * FullCalendar v1.5.3-rcube-0.7.1
+ * https://github.com/roundcube/fullcalendar
*
* Use fullcalendar.css for basic styling.
* For event drag & drop, requires jQuery UI draggable.
* For event resizing, requires jQuery UI resizable.
*
* Copyright (c) 2011 Adam Shaw
+ * Copyright (c) 2011, Kolab Systems AG
* Dual licensed under the MIT and GPL licenses, located in
* MIT-LICENSE.txt and GPL-LICENSE.txt respectively.
*
- * Date: Sun Aug 21 22:06:09 2011 -0700
+ * Date: Mon Feb 13 23:00:46 2012 +0100
*
*/
@@ -57,7 +58,7 @@ var defaults = {
week: 'ddd M/d',
day: 'dddd M/d',
list: 'dddd, MMM d, yyyy',
- table: 'dddd, MMM d, yyyy'
+ table: 'MMM d, yyyy'
},
timeFormat: { // for event elements
'': 'h(:mm)t' // default
@@ -136,7 +137,7 @@ var rtlDefaults = {
-var fc = $.fullCalendar = { version: "1.5.2" };
+var fc = $.fullCalendar = { version: "1.5.3-rcube-0.7.1" };
var fcViews = fc.views = {};
@@ -662,8 +663,7 @@ function Calendar(element, options, eventSources) {
} else if (name.indexOf('list') == 0 || name == 'tableCols') {
options[name] = value;
currentView.start = null; // force re-render
- }
- else if (name == 'maxHeight') {
+ } else if (name == 'maxHeight') {
options[name] = value;
}
}
@@ -933,6 +933,7 @@ function EventManager(options, _sources) {
function fetchEvents(start, end, src) {
rangeStart = start;
rangeEnd = end;
+ // partially clear cache if refreshing one source only (issue #1061)
cache = typeof src != 'undefined' ? $.grep(cache, function(e) { return !isSourcesEqual(e.source, src); }) : [];
var fetchID = ++currentFetchID;
var len = sources.length;
@@ -942,6 +943,7 @@ function EventManager(options, _sources) {
fetchEventSource(sources[i], fetchID);
}
}
+
function fetchEventSource(source, fetchID) {
@@ -1127,7 +1129,7 @@ function EventManager(options, _sources) {
event.source = stickySource;
}
}
-
+ // always push event to cache (issue #1112:)
cache.push(event);
reportEvents(cache);
}
@@ -1616,7 +1618,7 @@ var dateFormatters = {
return 'th';
}
return ['st', 'nd', 'rd'][date%10-1] || 'th';
- },
+ } ,
W : function(d) { return iso8601Week(d); }
};
@@ -3702,6 +3704,7 @@ function AgendaEventRenderer() {
var timeLineInterval;
+
/* Rendering
----------------------------------------------------------------------------*/
@@ -4011,7 +4014,7 @@ function AgendaEventRenderer() {
}
- // draw a horizontal line across the agenda view indicating the current time (#143)
+ // draw a horizontal line indicating the current time (#143)
function setTimeIndicator()
{
var container = getBodyContent();
@@ -4941,7 +4944,7 @@ function DayEventRenderer() {
}
seg.outerHeight = element[0].offsetHeight + val;
}
- else
+ else // always set a value (issue #1108 )
seg.outerHeight = 0;
}
}
@@ -5296,6 +5299,7 @@ function HoverListener(coordinateGrid) {
function mouse(ev) {
+ _fixUIEvent(ev); // see below
var newCell = coordinateGrid.cell(ev.pageX, ev.pageY);
if (!newCell != !cell || newCell && (newCell.row != cell.row || newCell.col != cell.col)) {
if (newCell) {
@@ -5319,6 +5323,19 @@ function HoverListener(coordinateGrid) {
}
+
+
+// this fix was only necessary for jQuery UI 1.8.16 (and jQuery 1.7 or 1.7.1)
+// upgrading to jQuery UI 1.8.17 (and using either jQuery 1.7 or 1.7.1) fixed the problem
+// but keep this in here for 1.8.16 users
+// and maybe remove it down the line
+
+function _fixUIEvent(event) { // for issue 1168
+ if (event.pageX === undefined) {
+ event.pageX = event.originalEvent.pageX;
+ event.pageY = event.originalEvent.pageY;
+ }
+}
function HorizontalPositionCache(getElement) {
var t = this,
@@ -5346,7 +5363,6 @@ function HorizontalPositionCache(getElement) {
}
-
/* Additional view: list (by bruederli@kolabsys.com)
---------------------------------------------------------------------------------*/
@@ -5464,7 +5480,7 @@ function ListEventRenderer() {
var tm = opt('theme') ? 'ui' : 'fc';
var headerClass = tm + "-widget-header";
var contentClass = tm + "-widget-content";
- var i, j, seg, event, times, s, skinCss, skinCssAttr, classes, segContainer, eventElements;
+ var i, j, seg, event, times, s, skinCss, skinCssAttr, classes, segContainer, eventElement, eventElements, triggerRes;
for (j=0; j < segs.length; j++) {
seg = segs[j];
@@ -5669,7 +5685,6 @@ function ListView(element, calendar) {
}
-
/* Additional view: table (by bruederli@kolabsys.com)
---------------------------------------------------------------------------------*/
@@ -5722,7 +5737,7 @@ function TableEventRenderer() {
var contentClass = tm + "-widget-content";
var tableCols = opt('tableCols');
var timecol = $.inArray('time', tableCols) >= 0;
- var i, j, seg, event, times, s, skinCss, skinCssAttr, skinClasses, rowClasses, segContainer, eventElements;
+ var i, j, seg, event, times, s, skinCss, skinCssAttr, skinClasses, rowClasses, segContainer, eventElements, eventElement, triggerRes;
for (j=0; j < segs.length; j++) {
seg = segs[j];
@@ -5900,6 +5915,5 @@ function TableView(element, calendar) {
}
}
-
-
-})(jQuery); \ No newline at end of file
+
+})(jQuery);
diff --git a/plugins/calendar/localization/de_CH.inc b/plugins/calendar/localization/de_CH.inc
index 34f8909..ee8383d 100644
--- a/plugins/calendar/localization/de_CH.inc
+++ b/plugins/calendar/localization/de_CH.inc
@@ -31,6 +31,7 @@ $labels['day'] = 'Tag';
$labels['week'] = 'Woche';
$labels['month'] = 'Monat';
$labels['agenda'] = 'Agenda';
+$labels['new'] = 'Neu';
$labels['new_event'] = 'Neuer Termin';
$labels['edit_event'] = 'Termin bearbeiten';
$labels['edit'] = 'Bearbeiten';
@@ -38,11 +39,13 @@ $labels['save'] = 'Speichern';
$labels['remove'] = 'Entfernen';
$labels['cancel'] = 'Abbrechen';
$labels['select'] = 'Auswählen';
-$labels['print'] = 'Kalender drucken';
+$labels['print'] = 'Drucken';
+$labels['printtitle'] = 'Kalender drucken';
$labels['title'] = 'Titel';
$labels['description'] = 'Beschrieb';
$labels['all-day'] = 'ganztägig';
-$labels['export'] = 'Exportieren...';
+$labels['export'] = 'Exportieren';
+$labels['exporttitle'] = 'Kalender als iCalendar exportieren';
$labels['location'] = 'Ort';
$labels['date'] = 'Datum';
$labels['start'] = 'Beginn';
@@ -61,7 +64,7 @@ $labels['confidential'] = 'vertraulich';
$labels['alarms'] = 'Erinnerung';
$labels['generated'] = 'erstellt am';
$labels['printdescriptions'] = 'Beschrieb drucken';
-$labels['parentcalendar'] = 'Übergeordneter Kalender';
+$labels['parentcalendar'] = 'Erstellen in';
$labels['searchearlierdates'] = '« Frühere Termine suchen';
$labels['searchlaterdates'] = 'Spätere Termine suchen »';
$labels['andnmore'] = '$nr weitere...';
@@ -72,6 +75,8 @@ $labels['importevents'] = 'Termine importieren';
$labels['importrange'] = 'Termine ab';
$labels['onemonthback'] = '1 Monat zurück';
$labels['nmonthsback'] = '$nr Monate zurück';
+$labels['showurl'] = 'URL anzeigen';
+$labels['showurldescription'] = 'Über die folgende Adresse können Sie mit einem beliebigen Kalenderprogramm auf Ihren Kalender zugreifen, sofern dieses das iCal-Format unterstützt.';
// agenda view
$labels['listrange'] = 'Angezeigter Bereich:';
diff --git a/plugins/calendar/localization/de_DE.inc b/plugins/calendar/localization/de_DE.inc
index 9a16e87..3c3a4bd 100644
--- a/plugins/calendar/localization/de_DE.inc
+++ b/plugins/calendar/localization/de_DE.inc
@@ -31,6 +31,7 @@ $labels['day'] = 'Tag';
$labels['week'] = 'Woche';
$labels['month'] = 'Monat';
$labels['agenda'] = 'Agenda';
+$labels['new'] = 'Neu';
$labels['new_event'] = 'Neuer Termin';
$labels['edit_event'] = 'Termin bearbeiten';
$labels['edit'] = 'Bearbeiten';
@@ -38,11 +39,13 @@ $labels['save'] = 'Speichern';
$labels['remove'] = 'Entfernen';
$labels['cancel'] = 'Abbrechen';
$labels['select'] = 'Auswählen';
-$labels['print'] = 'Kalender drucken';
+$labels['print'] = 'Drucken';
+$labels['printtitle'] = 'Kalender drucken';
$labels['title'] = 'Titel';
$labels['description'] = 'Beschrieb';
$labels['all-day'] = 'ganztägig';
-$labels['export'] = 'Exportieren...';
+$labels['export'] = 'Exportieren';
+$labels['exporttitle'] = 'Kalender als iCalendar exportieren';
$labels['location'] = 'Ort';
$labels['date'] = 'Datum';
$labels['start'] = 'Beginn';
@@ -61,7 +64,7 @@ $labels['confidential'] = 'vertraulich';
$labels['alarms'] = 'Erinnerung';
$labels['generated'] = 'erstellt am';
$labels['printdescriptions'] = 'Beschrieb drucken';
-$labels['parentcalendar'] = 'Übergeordneter Kalender';
+$labels['parentcalendar'] = 'Erstellen in';
$labels['searchearlierdates'] = '« Frühere Termine suchen';
$labels['searchlaterdates'] = 'Spätere Termine suchen »';
$labels['andnmore'] = '$nr weitere...';
@@ -72,6 +75,8 @@ $labels['importevents'] = 'Termine importieren';
$labels['importrange'] = 'Termine ab';
$labels['onemonthback'] = '1 Monat zurück';
$labels['nmonthsback'] = '$nr Monate zurück';
+$labels['showurl'] = 'URL anzeigen';
+$labels['showurldescription'] = 'Über die folgende Adresse können Sie mit einem beliebigen Kalenderprogramm auf Ihren Kalender zugreifen, sofern dieses das iCal-Format unterstützt.';
// agenda view
$labels['listrange'] = 'Angezeigter Bereich:';
diff --git a/plugins/calendar/localization/en_US.inc b/plugins/calendar/localization/en_US.inc
index 8050bb8..e13cd88 100644
--- a/plugins/calendar/localization/en_US.inc
+++ b/plugins/calendar/localization/en_US.inc
@@ -31,6 +31,7 @@ $labels['day'] = 'Day';
$labels['week'] = 'Week';
$labels['month'] = 'Month';
$labels['agenda'] = 'Agenda';
+$labels['new'] = 'New';
$labels['new_event'] = 'New event';
$labels['edit_event'] = 'Edit event';
$labels['edit'] = 'Edit';
@@ -38,11 +39,13 @@ $labels['save'] = 'Save';
$labels['remove'] = 'Remove';
$labels['cancel'] = 'Cancel';
$labels['select'] = 'Select';
-$labels['print'] = 'Print calendars';
+$labels['print'] = 'Print';
+$labels['printtitle'] = 'Print calendars';
$labels['title'] = 'Summary';
$labels['description'] = 'Description';
$labels['all-day'] = 'all-day';
-$labels['export'] = 'Export to iCalendar';
+$labels['export'] = 'Export';
+$labels['exporttitle'] = 'Export to iCalendar';
$labels['location'] = 'Location';
$labels['date'] = 'Date';
$labels['start'] = 'Start';
@@ -61,7 +64,7 @@ $labels['confidential'] = 'confidential';
$labels['alarms'] = 'Reminder';
$labels['generated'] = 'generated at';
$labels['printdescriptions'] = 'Print descriptions';
-$labels['parentcalendar'] = 'Superior calendar';
+$labels['parentcalendar'] = 'Insert inside';
$labels['searchearlierdates'] = '« Search for earlier events';
$labels['searchlaterdates'] = 'Search for later events »';
$labels['andnmore'] = '$nr more...';
@@ -72,6 +75,8 @@ $labels['importevents'] = 'Import events';
$labels['importrange'] = 'Events from';
$labels['onemonthback'] = '1 month back';
$labels['nmonthsback'] = '$nr months back';
+$labels['showurl'] = 'Show calendar URL';
+$labels['showurldescription'] = 'Use the following address to access your calendar from other applications. You can copy and paste this into any calendar software that supports the iCal format.';
// agenda view
$labels['listrange'] = 'Range to display:';
@@ -205,6 +210,7 @@ $labels['localchangeswarning'] = 'You are about to make changes that will only b
$labels['importsuccess'] = 'Successfully imported $nr events';
$labels['importnone'] = 'No events found to be imported';
$labels['importerror'] = 'An error occured while importing';
+$labels['aclnorights'] = 'You do not have administrator rights on this calendar.';
// recurrence form
$labels['repeat'] = 'Repeat';
diff --git a/plugins/calendar/package.xml b/plugins/calendar/package.xml
index 3fc38c2..ed42622 100644
--- a/plugins/calendar/package.xml
+++ b/plugins/calendar/package.xml
@@ -4,7 +4,7 @@
http://pear.php.net/dtd/package-2.0
http://pear.php.net/dtd/package-2.0.xsd">
<name>calendar</name>
- <uri>http://kolabsys.com</uri>
+ <uri>http://git.kolab.org/roundcube-plugins-kolab/</uri>
<summary>Calendar plugin</summary>
<description>-</description>
<lead>
diff --git a/plugins/calendar/skins/default/calendar.css b/plugins/calendar/skins/default/calendar.css
index aa1d550..21dcb1b 100644
--- a/plugins/calendar/skins/default/calendar.css
+++ b/plugins/calendar/skins/default/calendar.css
@@ -22,7 +22,7 @@ body.calendarmain {
bottom: 10px;
}
-#sidebar {
+#calendarsidebar {
position: absolute;
top: 37px;
left: 10px;
@@ -58,7 +58,7 @@ body.calendarmain {
cursor: pointer;
}
-#sidebartoggle {
+#calendarsidebartoggle {
position: absolute;
left: 244px;
width: 8px;
@@ -72,7 +72,7 @@ div.sidebarclosed {
background-position: -8px 48% !important;
}
-#sidebartoggle:hover {
+#calendarsidebartoggle:hover {
background-color: #ddd;
}
@@ -123,10 +123,11 @@ pre {
display: block;
}
-#calendarslist li span {
+#calendarslist li span.handle {
cursor: default;
background: url(images/calendars.png) 0 -2px no-repeat;
- padding-left: 18px;
+ display: inline-block;
+ width: 20px;
}
#calendarslist li input {
@@ -142,26 +143,34 @@ pre {
font-weight: bold;
}
-#calendarslist li.readonly span {
+#calendarslist li.readonly span.handle {
background-position: 0 -20px;
}
-#calendarslist li.other span {
+#calendarslist li.other span.handle {
background-position: 0 -38px;
}
-#calendarslist li.other.readonly span {
+#calendarslist li.other.readonly span.handle {
background-position: 0 -56px;
}
-#calendarslist li.shared span {
+#calendarslist li.shared span.handle {
background-position: 0 -74px;
}
-#calendarslist li.shared.readonly span {
+#calendarslist li.shared.readonly span.handle {
background-position: 0 -92px;
}
+#calfeedurl {
+ width: 98%;
+ background: #fbfbfb;
+ padding: 4px;
+ margin-bottom: 1em;
+ resize: none;
+}
+
#agendalist {
width: 100%;
margin: 0 auto;
@@ -1164,7 +1173,9 @@ fieldset #calendarcategories div {
/* Invitation UI in mail */
#messagemenu li a.calendarlink {
- background: url(images/calendars.png) 7px -109px no-repeat;
+ background-image: url(images/calendars.png);
+ background-position: 7px -109px;
+ background-repeat: no-repeat;
}
div.calendar-invitebox {
diff --git a/plugins/calendar/skins/default/fullcalendar.css b/plugins/calendar/skins/default/fullcalendar.css
index 404e282..17f1b37 100644
--- a/plugins/calendar/skins/default/fullcalendar.css
+++ b/plugins/calendar/skins/default/fullcalendar.css
@@ -1,11 +1,12 @@
/*
- * FullCalendar v1.5.2 Stylesheet
+ * FullCalendar v1.5.3-rcube-0.7.1 Stylesheet
*
* Copyright (c) 2011 Adam Shaw
+ * Copyright (c) 2011, Kolab Systems AG
* Dual licensed under the MIT and GPL licenses, located in
* MIT-LICENSE.txt and GPL-LICENSE.txt respectively.
*
- * Date: Sun Aug 21 22:06:09 2011 -0700
+ * Date: Mon Feb 13 23:00:46 2012 +0100
*
*/
@@ -477,7 +478,7 @@ table.fc-border-separate {
text-decoration: none;
cursor: pointer;
padding: 1px;
-}
+}
/* Agenda Week View, Agenda Day View
------------------------------------------------------------------------*/
@@ -620,8 +621,7 @@ table.fc-border-separate {
.fc-agenda .ui-resizable-resizing { /* TODO: better selector */
_overflow: hidden;
}
-
-
+
.fc-timeline {
position: absolute;
width: 100%;
diff --git a/plugins/calendar/skins/default/print.css b/plugins/calendar/skins/default/print.css
index eeb4fad..3c9bd71 100644
--- a/plugins/calendar/skins/default/print.css
+++ b/plugins/calendar/skins/default/print.css
@@ -20,6 +20,10 @@ body, td, th, div, p, h3, select, input, textarea {
overflow: visible;
}
+#calendar .fc-header-right {
+ padding-right: 0;
+}
+
#printconfig {
position: fixed;
top: 0;
diff --git a/plugins/calendar/skins/default/templates/calendar.html b/plugins/calendar/skins/default/templates/calendar.html
index 6253ab7..a22056d 100644
--- a/plugins/calendar/skins/default/templates/calendar.html
+++ b/plugins/calendar/skins/default/templates/calendar.html
@@ -12,7 +12,7 @@
<roundcube:include file="/includes/header.html" />
<div id="main">
- <div id="sidebar">
+ <div id="calendarsidebar">
<div id="datepicker"></div>
<div id="calendars" style="visibility:hidden">
<div class="boxtitle"><roundcube:label name="calendar.calendars" /></div>
@@ -25,7 +25,7 @@
</div>
</div>
</div>
- <div id="sidebartoggle"></div>
+ <div id="calendarsidebartoggle"></div>
<div id="calendar">
<roundcube:object name="plugin.angenda_options" class="boxfooter" id="agendaoptions" />
</div>
@@ -36,6 +36,7 @@
<li><roundcube:button command="calendar-edit" label="calendar.edit" classAct="active" /></li>
<li><roundcube:button command="calendar-remove" label="calendar.remove" classAct="active" /></li>
<li><roundcube:button command="events-import" label="calendar.importevents" classAct="active" /></li>
+ <li><roundcube:button command="calendar-showurl" label="calendar.showurl" classAct="active" /></li>
<roundcube:if condition="env:calendar_driver == 'kolab'" />
<li class="separator_above"><roundcube:button command="folders" task="settings" type="link" label="managefolders" classAct="active" /></li>
<roundcube:endif />
@@ -142,6 +143,11 @@
<roundcube:object name="plugin.events_import_form" id="events-import-form" uploadFieldSize="30" />
</div>
+<div id="calendarurlbox" class="uidialog">
+ <p><roundcube:label name="calendar.showurldescription" /></p>
+ <textarea id="calfeedurl" rows="2" readonly="readonly"></textarea>
+</div>
+
<div id="alarm-snooze-dropdown" class="popupmenu">
<roundcube:object name="plugin.snooze_select" type="ul" />
</div>
@@ -169,24 +175,24 @@ rcmail_ui.popups.calendaroptions = { id:'calendaroptionsmenu', above:1, obj:$('#
$(document).ready(function(e){
// initialize sidebar toggle
- $('#sidebartoggle').click(function() {
+ $('#calendarsidebartoggle').click(function() {
var width = $(this).data('sidebarwidth');
var offset = $(this).data('offset');
- var $sidebar = $('#sidebar'), time = 250;
+ var $sidebar = $('#calendarsidebar'), time = 250;
if ($sidebar.is(':visible')) {
- $sidebar.animate({ left:'-'+(width+10)+'px' }, time, function(){ $('#sidebar').hide(); });
- $(this).animate({ left:'8px'}, time, function(){ $('#sidebartoggle').addClass('sidebarclosed') });
+ $sidebar.animate({ left:'-'+(width+10)+'px' }, time, function(){ $('#calendarsidebar').hide(); });
+ $(this).animate({ left:'8px'}, time, function(){ $('#calendarsidebartoggle').addClass('sidebarclosed') });
$('#calendar').animate({ left:'20px'}, time, function(){ $(this).fullCalendar('render'); });
}
else {
$sidebar.show().animate({ left:'10px' }, time);
- $(this).animate({ left:offset+'px'}, time, function(){ $('#sidebartoggle').removeClass('sidebarclosed'); });
+ $(this).animate({ left:offset+'px'}, time, function(){ $('#calendarsidebartoggle').removeClass('sidebarclosed'); });
$('#calendar').animate({ left:(width+16)+'px'}, time, function(){ $(this).fullCalendar('render'); });
}
})
- .data('offset', $('#sidebartoggle').position().left)
- .data('sidebarwidth', $('#sidebar').width() + $('#sidebar').position().left);
+ .data('offset', $('#calendarsidebartoggle').position().left)
+ .data('sidebarwidth', $('#calendarsidebar').width() + $('#calendarsidebar').position().left);
});
</script>
diff --git a/plugins/calendar/skins/larry/.htaccess b/plugins/calendar/skins/larry/.htaccess
new file mode 100644
index 0000000..d973105
--- /dev/null
+++ b/plugins/calendar/skins/larry/.htaccess
@@ -0,0 +1,5 @@
+# get all unknown files from default skin folder
+RewriteEngine On
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteCond %{REQUEST_FILENAME} !-d
+RewriteRule (.*) ../default/$1 [qsappend,last]
diff --git a/plugins/calendar/skins/larry/calendar.css b/plugins/calendar/skins/larry/calendar.css
new file mode 100644
index 0000000..488f37c
--- /dev/null
+++ b/plugins/calendar/skins/larry/calendar.css
@@ -0,0 +1,1332 @@
+/**
+ * Roundcube Calendar plugin styles for skin "Larry"
+ *
+ * Copyright (c) 2012, The Roundcube Dev Team
+ * Screendesign by FLINT / Büro für Gestaltung, bueroflint.com
+ *
+ * The contents are subject to the Creative Commons Attribution-ShareAlike
+ * License. It is allowed to copy, distribute, transmit and to adapt the work
+ * by keeping credits to the original autors in the README file.
+ * See http://creativecommons.org/licenses/by-sa/3.0/ for details.
+ *
+ * $Id$
+ */
+
+body.calendarmain {
+ overflow: hidden;
+}
+
+body.calendarmain #mainscreen {
+ left: 0;
+}
+
+#calendarsidebar {
+ position: absolute;
+ top: 0;
+ left: 10px;
+ bottom: 0;
+ width: 240px;
+}
+
+#datepicker {
+ margin-top: 12px;
+ width: 100%;
+ min-height: 190px;
+}
+
+#datepicker .ui-datepicker {
+ width: 100% !important;
+ box-shadow: none;
+ -moz-box-shadow: none;
+ -webkit-box-shadow: none;
+}
+
+#datepicker .ui-datepicker td a {
+ padding: 5px 4px;
+ font-size: 12px;
+}
+
+#datepicker td.ui-datepicker-activerange {
+ border-color: #69a2b6;
+}
+
+#datepicker .ui-datepicker-activerange a {
+ color: #185d7a;
+ background: #d9f1fb;
+ background: -moz-linear-gradient(top, #d9f1fb 0%, #c5e3ee 100%);
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#d9f1fb), color-stop(100%,#c5e3ee));
+ background: -o-linear-gradient(top, #d9f1fb 0%, #c5e3ee 100%);
+ background: -ms-linear-gradient(top, #d9f1fb 0%, #c5e3ee 100%);
+ background: linear-gradient(top, #d9f1fb 0%, #c5e3ee 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#d9f1fb', endColorstr='#c5e3ee', GradientType=0);
+}
+
+#datepicker .ui-datepicker-activerange a.ui-state-active {
+ color: #fff;
+ background: #00acd4;
+ background: -moz-linear-gradient(top, #00acd4 0%, #008fc7 100%);
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#00acd4), color-stop(100%,#008fc7));
+ background: -o-linear-gradient(top, #00acd4 0%, #008fc7 100%);
+ background: -ms-linear-gradient(top, #00acd4 0%, #008fc7 100%);
+ background: linear-gradient(top, #00acd4 0%, #008fc7 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00acd4', endColorstr='#008fc7', GradientType=0);
+}
+
+#datepicker td.ui-datepicker-week-col {
+ cursor: pointer;
+}
+
+#datepicker .ui-datepicker-title {
+ margin: 2px 2.3em 3px 2.3em;
+}
+
+#datepicker .ui-datepicker .ui-datepicker-prev,
+#datepicker .ui-datepicker .ui-datepicker-next {
+ top: 4px;
+}
+
+#calendarsidebartoggle {
+ position: absolute;
+ left: 254px;
+ width: 8px;
+ top: 37px;
+ bottom: 0;
+ background: url(images/toggle.gif) 0 48% no-repeat transparent;
+ cursor: pointer;
+}
+
+div.sidebarclosed {
+ background-position: -8px 48% !important;
+}
+
+#calendarsidebartoggle:hover {
+ background-color: #ddd;
+}
+
+#calendar {
+ position: absolute;
+ top: 0;
+ left: 266px;
+ right: 0;
+ bottom: 0;
+ padding-bottom: 28px;
+}
+
+.calendarmain #message.statusbar {
+ border: 1px solid #c3c3c3;
+ border-bottom-color: #ababab;
+}
+
+#print {
+ width: 680px;
+}
+
+pre {
+ font-family: "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;
+}
+
+#calendars {
+ position: absolute;
+ top: 276px;
+ left: 0;
+ bottom: 0;
+ right: 0;
+}
+
+#calendarslist li {
+ margin: 0;
+ height: 20px;
+ padding: 6px 8px 2px;
+ display: block;
+ position: relative;
+ white-space: nowrap;
+}
+
+#calendarslist li label {
+ display: block;
+}
+
+#calendarslist li span.calname {
+ cursor: default;
+ background: url(images/calendars.png) 0 -2px no-repeat;
+ padding-left: 22px;
+ padding-bottom: 2px;
+ color: #004458;
+}
+
+#calendarslist li span.handle {
+ display: inline-block;
+ padding: 0;
+ border-radius: 7px;
+ margin-right: 6px;
+ width: 10px;
+ height: 10px;
+ font-size: 0.8em;
+ border: 1px solid rgba(0, 0, 0, 0.5);
+ -webkit-box-shadow: inset 0px 0 1px 1px rgba(0, 0, 0, 0.3);
+ -moz-box-shadow: inset 0px 0 1px 1px rgba(0, 0, 0, 0.3);
+ box-shadow: inset 0px 0 1px 1px rgba(0, 0, 0, 0.3);
+}
+
+#calendarslist li input {
+ position: absolute;
+ top: 3px;
+ right: 5px;
+}
+
+#calendarslist li.selected {
+ background-color: #c7e3ef;
+}
+
+#calendarslist li.selected span.calname {
+ font-weight: bold;
+}
+
+#calendarslist li.readonly span.calname {
+ background-position: 0 -20px;
+}
+
+#calendarslist li.other span.calname {
+ background-position: 0 -38px;
+}
+
+#calendarslist li.other.readonly span.calname {
+ background-position: 0 -56px;
+}
+
+#calendarslist li.shared span.calname {
+ background-position: 0 -74px;
+}
+
+#calendarslist li.shared.readonly span.calname {
+ background-position: 0 -92px;
+}
+
+#calfeedurl {
+ width: 98%;
+ background: #fbfbfb;
+ padding: 4px;
+ margin-bottom: 1em;
+ resize: none;
+}
+
+#agendalist {
+ width: 100%;
+ margin: 0 auto;
+ margin-top: 60px;
+ border: 1px solid #C1DAD7;
+ display: none;
+}
+
+#agendalist table {
+ width: 100%;
+}
+
+#agendalist td,
+#agendalist th {
+ border-right: 1px solid #C1DAD7;
+ border-bottom: 1px solid #C1DAD7;
+ background: #fff;
+ padding: 6px 6px 6px 12px;
+}
+
+#agendalist tr {
+ vertical-align: top;
+}
+
+#agendalist th {
+ font-weight: bold;
+}
+
+#calendartoolbar {
+ position: absolute;
+ top: -6px;
+ right: 0;
+ height: 40px;
+ z-index: 200;
+}
+
+#calendartoolbar a {
+ padding-right: 10px;
+}
+
+#quicksearchbar {
+ right: 4px;
+}
+
+body.calendarmain #searchmenulink {
+ width: 15px;
+}
+
+div.uidialog {
+ display: none;
+}
+
+#user {
+ position: absolute;
+ top: 10px;
+ right: 100px;
+ left: 100px;
+ text-align: center;
+}
+
+a.morelink {
+ font-size: 90%;
+ color: #0069a6;
+ text-decoration: none;
+}
+
+a.morelink:hover {
+ text-decoration: underline;
+}
+
+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;
+}
+
+#partheader {
+ position: absolute;
+ top: 20px;
+ left: 220px;
+ right: 20px;
+ height: 40px;
+}
+
+#partheader table td {
+ padding-left: 2px;
+ padding-right: 4px;
+ vertical-align: middle;
+ font-size: 11px;
+}
+
+#partheader table td.title {
+ color: #666;
+ font-weight: bold;
+}
+
+#edit-attachments {
+ margin-top: 0.6em;
+}
+
+#edit-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;
+}
+
+#edit-attachments ul li a.file {
+ padding: 0;
+}
+
+#edit-attachments-form {
+ padding-top: 1.2em;
+}
+
+#edit-attachments-form .buttons {
+ margin: 0.5em 0;
+}
+
+#event-attachments .attachmentslist li {
+ float: left;
+ margin-right: 1em;
+}
+
+#event-attachments .attachmentslist li a {
+ outline: none;
+}
+
+#event-attendees span.attendee {
+ padding-right: 18px;
+ margin-right: 0.5em;
+ background: url(images/attendee-status.gif) right 0 no-repeat;
+}
+
+#event-attendees span.attendee a.mailtolink {
+ text-decoration: none;
+ white-space: nowrap;
+}
+
+#event-attendees span.attendee a.mailtolink:hover {
+ text-decoration: underline;
+}
+
+#event-attendees span.accepted {
+ background-position: right -20px;
+}
+
+#event-attendees span.declined {
+ background-position: right -40px;
+}
+
+#event-attendees span.tentative {
+ background-position: right -60px;
+}
+
+#event-attendees span.organizer {
+ background-position: right -80px;
+}
+
+/* jQuery UI overrides */
+
+#eventshow h1 {
+ font-size: 18px;
+ margin: -0.3em 0 0.4em 0;
+}
+
+#eventshow label,
+#eventshow h5.label {
+ font-weight: normal;
+ font-size: 1em;
+ color: #999;
+ margin: 0 0 0.2em 0;
+}
+
+#eventshow {
+ margin: 0 -0.2em;
+}
+
+#eventshow.sensitivity-private {
+ background: url(images/badge_private.png) top right no-repeat;
+}
+
+#eventshow.sensitivity-confidential {
+ background: url(images/badge_confidential.png) top right no-repeat;
+}
+
+.sensitivity-private #event-title {
+ margin-right: 50px;
+}
+
+.sensitivity-confidential #event-title {
+ margin-right: 60px;
+}
+
+#eventshow div.event-line {
+ margin-top: 0.1em;
+ margin-bottom: 0.3em;
+}
+
+#eventedit {
+ position: relative;
+ top: -1.5em;
+ padding: 0.5em 0.1em;
+ margin: 0 -0.2em;
+}
+
+#eventedit input.text,
+#eventedit textarea {
+ width: 97%;
+}
+
+#eventtabs {
+ position: relative;
+ padding: 0;
+ border: 0;
+ border-radius: 0;
+}
+
+div.form-section,
+#eventshow div.event-section,
+#eventtabs div.event-section {
+ margin-top: 0.2em;
+ margin-bottom: 0.8em;
+}
+
+#eventtabs .border-after {
+ padding-bottom: 0.8em;
+ margin-bottom: 0.8em;
+ border-bottom: 2px solid #fafafa;
+}
+
+#eventshow label,
+#eventedit label,
+.form-section label {
+ display: inline-block;
+ min-width: 7em;
+ padding-right: 0.5em;
+}
+
+#eventedit .formtable td.label {
+ min-width: 6em;
+}
+
+td.topalign {
+ vertical-align: top;
+}
+
+#eventedit label.weekday,
+#eventedit label.monthday {
+ min-width: 3em;
+}
+
+#eventedit label.month {
+ min-width: 5em;
+}
+
+#edit-recurrence-yearly-bymonthblock {
+ margin-left: 7.5em;
+}
+
+#eventedit .recurrence-form {
+ display: none;
+}
+
+#eventedit .formtable td {
+ padding: 0.2em 0;
+}
+
+.ui-dialog .event-update-confirm {
+ padding: 0 0.5em 0.5em 0.5em;
+}
+
+.event-dialog-message,
+.event-update-confirm .message {
+ margin-top: 0.5em;
+ padding: 0.8em;
+ background-color: #F7FDCB;
+ border: 1px solid #C2D071;
+}
+
+.event-dialog-message .message,
+.event-update-confirm .message {
+ margin-bottom: 0.5em;
+}
+
+.edit-recurring-warning .savemode {
+ padding-left: 20px;
+}
+
+.event-update-confirm .savemode {
+ padding-left: 30px;
+}
+
+.event-dialog-message span.ui-icon,
+.event-update-confirm span.ui-icon {
+ float: left;
+ margin: 0 7px 20px 0;
+}
+
+.event-dialog-message label,
+.event-update-confirm label {
+ min-width: 3em;
+ padding-right: 1em;
+}
+
+.event-update-confirm a.button {
+ margin: 0 0.5em 0 0.2em;
+ min-width: 5em;
+}
+
+#event-rsvp,
+#edit-attendees-notify {
+ margin: 0.3em 0;
+ padding: 0.5em;
+ border: 1px solid #ffdf0e;
+ background-color: #fef893;
+}
+
+#edit-attendees-table {
+ width: 100%;
+ margin-top: 0.5em;
+}
+
+#edit-attendees-table td.role {
+ width: 9em;
+}
+
+#edit-attendees-table td.availability,
+#edit-attendees-table td.confirmstate {
+ width: 4em;
+}
+
+#edit-attendees-table td.options {
+ width: 3em;
+ text-align: right;
+ padding-right: 4px;
+}
+
+#edit-attendees-table td.name {
+ width: auto;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+#edit-attendees-form {
+ position: relative;
+ margin-top: 1em;
+}
+
+#edit-attendees-form #edit-attendee-schedule {
+ position: absolute;
+ top: 0;
+ right: 0;
+}
+
+#edit-attendees-table select.edit-attendee-role {
+ border: 0;
+ padding: 2px;
+ background: white;
+}
+
+.availability img.availabilityicon {
+ margin: 1px;
+ width: 14px;
+ height: 14px;
+ border-radius: 4px;
+ -moz-border-radius: 4px;
+}
+
+.availability img.availabilityicon.loading {
+ background: url(images/loading_blue.gif) center no-repeat;
+}
+
+#schedule-freebusy-times td.unknown,
+.availability img.availabilityicon.unknown {
+ background: #ddd;
+}
+
+#schedule-freebusy-times td.free,
+.availability img.availabilityicon.free {
+ background: #0c0;
+}
+
+#schedule-freebusy-times td.busy,
+.availability img.availabilityicon.busy {
+ background: #c00;
+}
+
+#schedule-freebusy-times td.tentative,
+.availability img.availabilityicon.tentative {
+ background: #66d;
+}
+
+#schedule-freebusy-times td.out-of-office,
+.availability img.availabilityicon.out-of-office {
+ background: #f0b400;
+}
+
+#schedule-freebusy-times td.all-busy,
+#schedule-freebusy-times td.all-tentative,
+#schedule-freebusy-times td.all-out-of-office {
+ background-image: url(images/freebusy-colors.png);
+ background-position: top right;
+ background-repeat: no-repeat;
+}
+
+#schedule-freebusy-times td.all-tentative {
+ background-position: right -40px;
+}
+
+#schedule-freebusy-times td.all-out-of-office {
+ background-position: right -80px;
+}
+
+#edit-attendees-legend {
+ margin-top: 3em;
+ margin-bottom: 0.5em;
+}
+
+#edit-attendees-legend .legend {
+ margin-right: 2em;
+ white-space: nowrap;
+}
+
+#edit-attendees-legend img.availabilityicon {
+ vertical-align: middle;
+}
+
+#edit-attendees-table tbody td.confirmstate {
+ overflow: hidden;
+ white-space: nowrap;
+ text-indent: -2000%;
+}
+
+#edit-attendees-table td.confirmstate span {
+ display: block;
+ width: 20px;
+ background: url(images/attendee-status.gif) 5px 0 no-repeat;
+}
+
+#edit-attendees-table td.confirmstate span.needs-action {
+}
+
+#edit-attendees-table td.confirmstate span.accepted {
+ background-position: 5px -20px;
+}
+
+#edit-attendees-table td.confirmstate span.declined {
+ background-position: 5px -40px;
+}
+
+#edit-attendees-table td.confirmstate span.tentative {
+ background-position: 5px -60px;
+}
+
+#attendees-freebusy-table {
+ width: 100%;
+ table-layout: fixed;
+ border-collapse: collapse;
+ margin: 0.5em 0;
+}
+
+#attendees-freebusy-table td.attendees {
+ width: 18em;
+ border: 1px solid #ccc;
+ vertical-align: top;
+ overflow: hidden;
+}
+
+#attendees-freebusy-table td.times {
+ width: auto;
+ vertical-align: top;
+ border: 1px solid #ccc;
+}
+
+#attendees-freebusy-table div.scroll {
+ position: relative;
+ overflow: auto;
+}
+
+#attendees-freebusy-table h3.boxtitle {
+ margin: 0;
+ height: auto !important;
+ border-color: #ccc;
+}
+
+.attendees-list .attendee {
+ padding: 3px 4px 3px 1px;
+ background: url(images/attendee-status.gif) 2px -97px no-repeat;
+ white-space: nowrap;
+}
+
+.attendees-list a.attendee-role-toggle {
+ display: inline-block;
+ width: 16px;
+ margin-right: 3px;
+ cursor: pointer;
+}
+
+.attendees-list div.attendee {
+ border-top: 1px solid #ccc;
+}
+
+.attendees-list span.attendee {
+ padding-left: 20px;
+ margin-right: 2em;
+}
+
+.attendees-list .organizer {
+ background-position: 3px -77px;
+}
+
+.attendees-list .opt-participant {
+ background-position: 2px -117px;
+}
+
+.attendees-list .chair {
+ background-position: 2px -137px;
+}
+
+.attendees-list .loading {
+ background: url(images/loading_blue.gif) 1px 50% no-repeat;
+}
+
+.attendees-list .total {
+ background: none;
+ padding-left: 4px;
+ font-weight: bold;
+}
+
+.attendees-list .spacer,
+#schedule-freebusy-times tr.spacer td {
+ background: 0;
+ font-size: 50%;
+}
+
+#schedule-freebusy-times {
+ border-collapse: collapse;
+ width: 100%;
+}
+
+#schedule-freebusy-times td {
+ padding: 3px;
+ border: 1px solid #ccc;
+}
+
+#schedule-freebusy-times tr.dates th {
+ border-color: #aaa;
+ border-style: solid;
+ border-width: 0 1px 0 1px;
+ white-space: nowrap;
+}
+
+#attendees-freebusy-table div.timesheader,
+#schedule-freebusy-times tr.times td {
+ min-width: 30px;
+ font-size: 9px;
+ padding: 5px 2px 6px 2px;
+ text-align: center;
+}
+
+#schedule-freebusy-times tr.times td.allday {
+ min-width: 60px;
+}
+
+#schedule-freebusy-times tr.times td {
+ cursor: pointer;
+}
+
+#schedule-event-time {
+ position: absolute;
+ border: 2px solid #333;
+ background: #777;
+ background: rgba(60, 60, 60, 0.6);
+ opacity: 0.5;
+ border-radius: 4px;
+ cursor: move;
+ filter: alpha(opacity=40); /* IE8 */
+}
+
+#eventfreebusy .schedule-options {
+ position: relative;
+ margin-bottom: 1.5em;
+}
+
+#eventfreebusy .schedule-buttons {
+ position: absolute;
+ top: 0;
+ right: 0;
+}
+
+#eventfreebusy .schedule-find-buttons {
+ padding-bottom:0.5em;
+}
+
+#eventfreebusy .schedule-find-buttons button {
+ min-width: 9em;
+ text-align: center;
+}
+
+span.edit-alarm-set {
+ white-space: nowrap;
+}
+
+a.dropdown-link {
+ color: #CC0000;
+ font-size: 12px;
+ text-decoration: none;
+}
+
+a.dropdown-link:after {
+ content: ' ▼';
+ font-size: 11px;
+ color: #666;
+}
+
+#eventedit .ui-tabs-panel {
+ min-height: 20em;
+}
+
+.alarm-item {
+ margin: 0.4em 0 1em 0;
+}
+
+.alarm-item .event-title {
+ font-size: 14px;
+ margin: 0.1em 0 0.3em 0;
+}
+
+.alarm-item div.event-section {
+ margin-top: 0.1em;
+ margin-bottom: 0.3em;
+}
+
+.alarm-item .alarm-actions {
+ margin-top: 0.4em;
+}
+
+.alarm-item div.alarm-actions a {
+ color: #CC0000;
+ margin-right: 0.8em;
+ text-decoration: none;
+}
+
+a.alarm-action-snooze:after {
+ content: ' ▼';
+ font-size: 10px;
+ color: #666;
+}
+
+#alarm-snooze-dropdown {
+ z-index: 5000;
+}
+
+.ui-dialog-buttonset a.dropdown-link {
+ margin-right: 1em;
+}
+/*
+.ui-datepicker-calendar .ui-datepicker-today .ui-state-default {
+ border-color: #cccccc;
+ background: #ffffcc;
+ color: #000;
+}
+*/
+.ui-datepicker-calendar .ui-datepicker-week-col {
+ border: 0;
+ color: #999;
+ font-size: 90%;
+ text-align: right;
+ padding-right: 6px;
+}
+/*
+.ui-datepicker th {
+ padding: 0.3em 0;
+ font-size: 10px;
+}
+
+.ui-datepicker td span,
+.ui-datepicker td a {
+ padding-left: 0.1em;
+}
+*/
+.ui-autocomplete {
+ max-height: 160px;
+ overflow-y: auto;
+ overflow-x: hidden;
+}
+
+.ui-autocomplete .ui-menu-item {
+ white-space: nowrap;
+}
+
+* html .ui-autocomplete {
+ height: 160px;
+}
+
+span.spacer {
+ padding-left: 3em;
+}
+
+#agendaoptions {
+ position: absolute;
+ bottom: 28px;
+ left: 0;
+ right: 0;
+ height: auto;
+ z-index: 200;
+ background: #d6eaf3;
+ border: 1px solid #c3c3c3;
+ border-top-color: #ddd;
+ padding: 4px 5px;
+}
+
+#agendaoptions label {
+ color: #69939e;
+ text-shadow: 1px 1px #f2f2f2;
+ padding-right: 0.5em;
+}
+
+#calendar-kolabform {
+ position: relative;
+ margin: 0 -8px;
+ min-width: 660px;
+ min-height: 400px;
+}
+
+#calendar-kolabform table td.title {
+ font-weight: bold;
+ white-space: nowrap;
+ color: #666;
+ padding-right: 10px;
+}
+
+.propform fieldset.tab {
+ background: #efefef;
+ display: block;
+ margin-top: 0.5em;
+ padding: 0.5em 1em;
+}
+
+/* fullcalendar style overrides */
+
+.rcube-fc-content {
+ overflow: hidden;
+ border: 0;
+ border-radius: 4px 4px 0 0;
+ box-shadow: 0 0 2px #999;
+ -o-box-shadow: 0 0 2px #999;
+ -webkit-box-shadow: 0 0 2px #999;
+ -moz-box-shadow: 0 0 2px #999;
+}
+
+.fc-content {
+ position: absolute !important;
+ top: 38px;
+ left: 0;
+ right: 0;
+ bottom: 28px;
+ background: #fff;
+}
+
+#fish-eye-view .fc-content {
+ top: 2px;
+ bottom: 2px;
+}
+
+.calendarmain .fc-button,
+.calendarmain .fc-button.fc-state-hover,
+.calendarmain .fc-button.fc-state-down {
+ border: 0;
+ background: none;
+}
+
+.calendarmain .fc-state-default .fc-button-inner,
+.calendarmain .fc-state-hover .fc-button-inner {
+ margin: 0 0 0 0;
+ color: #555;
+ text-shadow: 0px 1px 1px #fff;
+ border: 1px solid #a2a2a2;
+ background: #f7f7f7;
+ background: -moz-linear-gradient(top, #f9f9f9 0%, #e6e6e6 100%);
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f9f9f9), color-stop(100%,#e6e6e6));
+ background: -o-linear-gradient(top, #f9f9f9 0%, #e6e6e6 100%);
+ background: -ms-linear-gradient(top, #f9f9f9 0%, #e6e6e6 100%);
+ background: linear-gradient(top, #f9f9f9 0%, #e6e6e6 100%);
+ box-shadow: 0 1px 1px 0 rgba(140, 140, 140, 0.3);
+ -o-box-shadow: 0 1px 1px 0 rgba(140, 140, 140, 0.3);
+ -webkit-box-shadow: 0 1px 1px 0 rgba(140, 140, 140, 0.3);
+ -moz-box-shadow: 0 1px 1px 0 rgba(140, 140, 140, 0.3);
+ text-decoration: none;
+}
+
+.calendarmain .fc-state-disabled .fc-button-inner {
+ color: #bbb;
+}
+
+.calendarmain .fc-header .fc-button {
+ margin-left: -1px;
+ margin-right: 0;
+}
+
+.calendarmain .fc-state-down .fc-button-inner {
+ margin: 0;
+ border: 1px solid #a2a2a2;
+ background: #e6e6e6;
+ background: -moz-linear-gradient(top, #e6e6e6 0%, #f9f9f9 100%);
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#e6e6e6), color-stop(100%,#f9f9f9));
+ background: -o-linear-gradient(top, #e6e6e6 0%, #f9f9f9 100%);
+ background: -ms-linear-gradient(top, #e6e6e6 0%, #f9f9f9 100%);
+ background: linear-gradient(top, #e6e6e6 0%, #f9f9f9 100%);
+}
+
+.calendarmain .fc-state-active .fc-button-inner {
+ color: #333;
+ background: #bababa;
+ background: -moz-linear-gradient(top, #bababa 0%, #d8d8d8 100%);
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#bababa), color-stop(100%,#d8d8d8));
+ background: -o-linear-gradient(top, #bababa 0%, #d8d8d8 100%);
+ background: -ms-linear-gradient(top, #bababa 0%, #d8d8d8 100%);
+ background: linear-gradient(top, #bababa 0%, #d8d8d8 100%);
+}
+
+.calendarmain .fc-corner-left .fc-button-inner,
+.calendarmain .fc-corner-left .fc-button-content {
+ border-top-left-radius: 4px;
+ border-bottom-left-radius: 4px;
+}
+
+.calendarmain .fc-corner-right .fc-button-inner,
+.calendarmain .fc-corner-right .fc-button-content {
+ border-top-right-radius: 4px;
+ border-bottom-right-radius: 4px;
+}
+
+.calendarmain .fc-state-default .fc-button-effect {
+ display: none;
+}
+
+.calendarmain .fc-button-content {
+ height: 2.2em;
+ line-height: 2.2em;
+}
+
+#calendar .fc-header-right {
+ padding-right: 250px;
+}
+
+.fc-event {
+ font-size: 1em !important;
+}
+
+.fc-event-title {
+ font-weight: bold;
+}
+
+.fc-event-hori .fc-event-title {
+ font-weight: normal;
+ white-space: nowrap;
+}
+
+.fc-event-hori .fc-event-time {
+ white-space: nowrap;
+ font-weight: normal !important;
+ font-size: 10px;
+ padding-right: 0.6em;
+}
+
+.fc-grid .fc-event-time {
+ font-weight: normal !important;
+ padding-right: 0.3em;
+}
+
+.fc-event-cateories {
+ font-style:italic;
+}
+
+div.fc-event-location {
+ font-size: 90%;
+}
+
+.fc-more-link {
+ color: #999;
+ padding-top: 1px;
+ cursor: pointer;
+}
+
+.fc-agenda-slots td div {
+ height: 22px;
+}
+
+.fc-sat, .fc-sun {
+ background-color: #fdfdfd;
+}
+
+.fc-widget-header {
+ background-color: #d6eaf3;
+ color: #004458;
+ text-shadow: 0px 1px 1px #fff;
+}
+
+.fc-view thead th.fc-widget-header {
+ padding: 8px 0;
+ color: #69939e;
+}
+
+.fc-day-number {
+ color: #578da5;
+}
+
+.fc-icon-alarms,
+.fc-icon-sensitive,
+.fc-icon-recurring {
+ display: inline-block;
+ width: 11px;
+ height: 11px;
+ background: url(images/eventicons.gif) 0 0 no-repeat;
+ margin-left: 3px;
+ line-height: 10px;
+}
+
+.fc-icon-alarms {
+ background-position: 0 -13px;
+}
+
+.fc-icon-sensitive {
+ background-position: 0 -25px;
+}
+
+.fc-list-section .fc-event {
+ cursor: pointer;
+}
+
+/*.calendarmain .fc-view-list div.fc-list-header,*/
+.calendarmain .fc-view-table td.fc-list-header,
+#edit-attendees-table thead td {
+ color: #69939e;
+ font-size: 11px;
+ font-weight: bold;
+ background: #d6eaf3;
+ background: -moz-linear-gradient(left, #e3f2f6 0, #d6eaf3 14px, #d6eaf3 100%);
+ background: -webkit-gradient(linear, left top, right top, color-stop(0,#e3f2f6), color-stop(8%,#d6eaf3), color-stop(100%,#d6eaf3));
+ background: -o-linear-gradient(left, #e3f2f6 0, #d6eaf3 14px, #d6eaf3 100%);
+ background: -ms-linear-gradient(left, #e3f2f6 0, #d6eaf3 14px ,#d6eaf3 100%);
+ background: linear-gradient(left, #e3f2f6 0, #d6eaf3 14px, #d6eaf3 100%);
+ border: 0;
+ padding: 7px;
+}
+
+.calendarmain .fc-view-table tr.fc-event td {
+ border-color: #ddd;
+ padding: 4px 7px;
+}
+
+.calendarmain .fc-view-table col.fc-event-location {
+ width: 20%;
+}
+
+.calendarmain .fc-view-table tr.fc-event td.fc-event-handle {
+ padding: 5px 10px 2px 7px;
+ width: 12px;
+}
+
+.calendarmain .fc-view-table .fc-event-handle .fc-event-skin {
+ margin: 0;
+ padding: 0;
+ display: inline-block;
+ width: 10px;
+ height: 10px;
+ font-size: 6px;
+ border-radius: 8px;
+}
+
+.calendarmain .fc-view-table .fc-event-handle .fc-event-inner {
+ display: inline-block;
+ width: 10px;
+ height: 10px;
+ padding: 0;
+ font-size: 10px;
+ border-radius: 8px;
+ border: 1px solid rgba(0, 0, 0, 0.4);
+ -webkit-box-shadow: inset 0px 0 1px 1px rgba(0, 0, 0, 0.3);
+ -moz-box-shadow: inset 0px 0 1px 1px rgba(0, 0, 0, 0.3);
+ box-shadow: inset 0px 0 1px 1px rgba(0, 0, 0, 0.3);
+}
+
+
+.fc-listappend {
+ text-align: center;
+ margin: 1em 0;
+}
+
+.fc-listappend .message {
+ padding: 0.5em;
+ margin-bottom: 0.5em;
+ font-size: 150%;
+ color: #999;
+}
+
+.fc-listappend .formlinks a {
+ font-size: 12px;
+ padding: 0 0.3em;
+}
+
+.fc-event-temp {
+ opacity: 0.4;
+ filter: alpha(opacity=40); /* IE8 */
+}
+
+/* Settings section */
+
+fieldset #calendarcategories div {
+ margin-bottom: 0.3em;
+}
+
+/* Invitation UI in mail */
+
+#messagemenu li a.calendarlink span.calendar {
+ background-position: 0px -1948px;
+}
+
+div.calendar-invitebox {
+ min-height: 20px;
+ margin: 5px 8px;
+ padding: 3px 6px 6px 34px;
+ border: 1px solid #ffdf0e;
+ background: url(images/calendar.png) 6px 5px no-repeat #fef893;
+}
+
+div.calendar-invitebox td.ititle {
+ font-weight: bold;
+ padding-right: 0.5em;
+}
+
+div.calendar-invitebox td.label {
+ color: #666;
+ padding-right: 1em;
+}
+
+#event-rsvp .rsvp-buttons,
+div.calendar-invitebox .rsvp-status,
+div.calendar-invitebox .rsvp-buttons {
+ margin-top: 0.5em;
+}
+
+#event-rsvp input.button,
+div.calendar-invitebox input.button {
+ font-weight: bold;
+ margin-right: 0.5em;
+}
+
+div.calendar-invitebox .calendar-select {
+ font-weight: 10px;
+ margin-left: 1em;
+}
+
+div.calendar-invitebox .rsvp-status.loading {
+ color: #666;
+ padding: 1px 0 2px 24px;
+ background: url(images/loading_blue.gif) top left no-repeat;
+}
+
+div.calendar-invitebox .rsvp-status.declined,
+div.calendar-invitebox .rsvp-status.tentative,
+div.calendar-invitebox .rsvp-status.accepted {
+ padding: 0 0 1px 22px;
+ background: url(images/attendee-status.gif) 2px -20px no-repeat;
+}
+
+div.calendar-invitebox .rsvp-status.declined {
+ background-position: 2px -40px;
+}
+
+div.calendar-invitebox .rsvp-status.tentative {
+ background-position: 2px -60px;
+}
+
+/* iTIP attend reply page */
+
+.calendaritipattend .centerbox {
+ width: 40em;
+ margin: 80px auto;
+ padding: 10px 10px 10px 90px;
+ background: url(images/invitation.png) 10px 10px no-repeat #fff;
+}
+
+.calendaritipattend .calendar-invitebox {
+ background: none;
+ padding-left: 0;
+ border: 0;
+ margin: 0 0 2em 0;
+}
+
+.calendaritipattend .calendar-invitebox .rsvp-status {
+ margin-top: 2.5em;
+ font-size: 110%;
+ font-weight: bold;
+}
+
+.calendaritipattend .calendar-invitebox td.title,
+.calendaritipattend .calendar-invitebox td.ititle {
+ font-size: 120%;
+}
+
diff --git a/plugins/calendar/skins/larry/templates/attachment.html b/plugins/calendar/skins/larry/templates/attachment.html
new file mode 100644
index 0000000..439afd4
--- /dev/null
+++ b/plugins/calendar/skins/larry/templates/attachment.html
@@ -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" style="width:100%; height:100%" />
+ </div>
+
+</div>
+
+</body>
+</html>
+
diff --git a/plugins/calendar/skins/larry/templates/calendar.html b/plugins/calendar/skins/larry/templates/calendar.html
new file mode 100644
index 0000000..c072e35
--- /dev/null
+++ b/plugins/calendar/skins/larry/templates/calendar.html
@@ -0,0 +1,202 @@
+<roundcube:object name="doctype" value="html5" />
+<html>
+<head>
+<title><roundcube:object name="pagetitle" /></title>
+<roundcube:include file="/includes/links.html" />
+<!--[if lte IE 7]><link rel="stylesheet" type="text/css" href="./plugins/calendar/$__skin_path/iehacks.css" /><![endif]-->
+</head>
+<body class="calendarmain noscroll">
+
+<roundcube:include file="/includes/header.html" />
+
+<div id="mainscreen">
+ <div id="calendarsidebar">
+ <div id="quicksearchbar">
+ <roundcube:object name="plugin.searchform" id="quicksearchbox" />
+ <a id="searchmenulink" class="iconbutton searchoptions" > </a>
+ <roundcube:button command="reset-search" id="searchreset" class="iconbutton reset" title="resetsearch" content=" " />
+ </div>
+
+ <div id="datepicker" class="uibox"></div>
+
+ <div id="calendars" class="uibox listbox" style="visibility:hidden">
+ <h2 class="boxtitle"><roundcube:label name="calendar.calendars" /></h2>
+ <div class="scroller withfooter">
+ <roundcube:object name="plugin.calendar_list" id="calendarslist" class="listing" />
+ </div>
+ <div class="boxfooter">
+ <roundcube:button command="calendar-create" type="link" title="calendar.createcalendar" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" content="+" /><roundcube:button name="calendaroptionslink" id="calendaroptionsmenulink" type="link" title="calendar.calendaractions" class="listbutton groupactions" onclick="UI.show_popup('calendaroptionsmenu', undefined, { above:true });return false" innerClass="inner" content="&#9881;" />
+ </div>
+ </div>
+ </div>
+ <div id="calendarsidebartoggle"></div>
+
+ <div id="calendartoolbar" class="toolbar">
+ <roundcube:button command="addevent" type="link" class="button addevent disabled" classAct="button addevent" classSel="button addevent pressed" label="calendar.new_event" title="calendar.new_event" />
+ <roundcube:button command="print" type="link" class="button print disabled" classAct="button print" classSel="button print pressed" label="calendar.print" title="calendar.printtitle" />
+ <roundcube:button command="export" type="link" class="button export disabled" classAct="button export" classSel="button export pressed" label="calendar.export" title="calendar.exporttitle" />
+ <roundcube:container name="toolbar" id="calendartoolbar" />
+ </div>
+
+ <div id="calendar">
+ <roundcube:object name="plugin.angenda_options" class="boxfooter" id="agendaoptions" />
+ <roundcube:object name="message" id="message" class="statusbar" />
+ </div>
+</div>
+
+<div id="calendaroptionsmenu" class="popupmenu">
+ <ul class="toolbarmenu">
+ <li><roundcube:button command="calendar-edit" label="calendar.edit" classAct="active" /></li>
+ <li><roundcube:button command="calendar-remove" label="calendar.remove" classAct="active" /></li>
+ <li><roundcube:button command="events-import" label="calendar.importevents" classAct="active" /></li>
+ <li><roundcube:button command="calendar-showurl" label="calendar.showurl" classAct="active" /></li>
+ <roundcube:if condition="env:calendar_driver == 'kolab'" />
+ <li class="separator_above"><roundcube:button command="folders" task="settings" type="link" label="managefolders" classAct="active" /></li>
+ <roundcube:endif />
+ </ul>
+</div>
+
+<div id="eventshow" class="uidialog">
+ <h1 id="event-title">Event Title</h1>
+ <div class="event-section" id="event-location">Location</div>
+ <div class="event-section" id="event-date">From-To</div>
+ <div class="event-section" id="event-description">
+ <h5 class="label"><roundcube:label name="calendar.description" /></h5>
+ <div class="event-text"></div>
+ </div>
+ <div class="event-section" id="event-repeat">
+ <h5 class="label"><roundcube:label name="calendar.repeat" /></h5>
+ <div class="event-text"></div>
+ </div>
+ <div class="event-section" id="event-alarm">
+ <h5 class="label"><roundcube:label name="calendar.alarms" /></h5>
+ <div class="event-text"></div>
+ </div>
+ <div class="event-section" id="event-attendees">
+ <h5 class="label"><roundcube:label name="calendar.tabattendees" /></h5>
+ <div class="event-text"></div>
+ </div>
+ <div class="event-line" id="event-calendar">
+ <label><roundcube:label name="calendar.calendar" /></label>
+ <span class="event-text">Default</span>
+ </div>
+ <div class="event-line" id="event-category">
+ <label><roundcube:label name="calendar.category" /></label>
+ <span class="event-text"></span>
+ </div>
+ <div class="event-line" id="event-free-busy">
+ <label><roundcube:label name="calendar.freebusy" /></label>
+ <span class="event-text"></span>
+ </div>
+ <div class="event-line" id="event-priority">
+ <label><roundcube:label name="calendar.priority" /></label>
+ <span class="event-text"></span>
+ </div>
+ <div class="event-line" id="event-sensitivity">
+ <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>
+ <div class="event-text"></div>
+ </div>
+
+ <roundcube:object name="plugin.event_rsvp_buttons" id="event-rsvp" style="display:none" />
+</div>
+
+<roundcube:include file="/templates/eventedit.html" />
+
+<div id="eventfreebusy" class="uidialog">
+ <roundcube:object name="plugin.attendees_freebusy_table" id="attendees-freebusy-table" cellspacing="0" cellpadding="0" border="0" />
+
+ <div class="schedule-options">
+ &nbsp;
+ <div class="schedule-buttons">
+ <button id="shedule-freebusy-prev" title="<roundcube:label name='previouspage' />">&#9668;</button><button id="shedule-freebusy-next" title="<roundcube:label name='nextpage' />">&#9658;</button>
+ </div>
+ </div>
+
+ <div style="float:left; width:28em">
+ <div class="form-section">
+ <label for="schedule-startdate"><roundcube:label name="calendar.start" /></label>
+ <input type="text" name="startdate" size="10" id="schedule-startdate" disabled="true" /> &nbsp;
+ <input type="text" name="starttime" size="6" id="schedule-starttime" disabled="true" />
+ </div>
+ <div class="form-section">
+ <label for="schedule-enddate"><roundcube:label name="calendar.end" /></label>
+ <input type="text" name="enddate" size="10" id="schedule-enddate" disabled="true" /> &nbsp;
+ <input type="text" name="endtime" size="6" id="schedule-endtime" disabled="true" />
+ </div>
+ </div>
+ <div style="float:left">
+ <div class="schedule-find-buttons">
+ <button id="shedule-find-prev">&#9668; <roundcube:label name="calendar.prevslot" /></button>
+ <button id="shedule-find-next"><roundcube:label name="calendar.nextslot" /> &#9658;</button>
+ </div>
+ <div class="schedule-options">
+ <label><input type="checkbox" id="schedule-freebusy-workinghours" value="1" /><roundcube:label name="calendar.onlyworkinghours" /></label>
+ </div>
+ </div>
+ <br style="clear:both;" />
+
+ <roundcube:include file="/templates/freebusylegend.html" />
+ <div class="attendees-list">
+ <span class="attendee organizer"><roundcube:label name="calendar.roleorganizer" /></span>
+ <span class="attendee req-participant"><roundcube:label name="calendar.rolerequired" /></span>
+ <span class="attendee opt-participant"><roundcube:label name="calendar.roleoptional" /></span>
+ <span class="attendee chair"><roundcube:label name="calendar.roleresource" /></span>
+ </div>
+</div>
+
+<div id="calendarform" class="uidialog">
+ <roundcube:label name="loading" />
+</div>
+
+<div id="eventsimport" class="uidialog">
+ <roundcube:object name="plugin.events_import_form" id="events-import-form" uploadFieldSize="30" />
+</div>
+
+<div id="calendarurlbox" class="uidialog">
+ <p><roundcube:label name="calendar.showurldescription" /></p>
+ <textarea id="calfeedurl" rows="2" readonly="readonly"></textarea>
+</div>
+
+<div id="alarm-snooze-dropdown" class="popupmenu">
+ <roundcube:object name="plugin.snooze_select" type="ul" />
+</div>
+
+<roundcube:object name="plugin.calendar_css" />
+
+<script type="text/javascript">
+
+// UI startup
+var UI = new rcube_mail_ui();
+
+$(document).ready(function(e){
+ UI.init();
+
+ // initialize sidebar toggle
+ $('#calendarsidebartoggle').click(function() {
+ var width = $(this).data('sidebarwidth');
+ var offset = $(this).data('offset');
+ var $sidebar = $('#calendarsidebar'), time = 250;
+
+ if ($sidebar.is(':visible')) {
+ $sidebar.animate({ left:'-'+(width+10)+'px' }, time, function(){ $('#calendarsidebar').hide(); });
+ $(this).animate({ left:'8px'}, time, function(){ $('#calendarsidebartoggle').addClass('sidebarclosed') });
+ $('#calendar').animate({ left:'20px'}, time, function(){ $(this).fullCalendar('render'); });
+ }
+ else {
+ $sidebar.show().animate({ left:'10px' }, time);
+ $(this).animate({ left:offset+'px'}, time, function(){ $('#calendarsidebartoggle').removeClass('sidebarclosed'); });
+ $('#calendar').animate({ left:(width+16)+'px'}, time, function(){ $(this).fullCalendar('render'); });
+ }
+ })
+ .data('offset', $('#calendarsidebartoggle').position().left)
+ .data('sidebarwidth', $('#calendarsidebar').width() + $('#calendarsidebar').position().left);
+});
+
+</script>
+
+</body>
+</html>
diff --git a/plugins/calendar/skins/larry/templates/eventedit.html b/plugins/calendar/skins/larry/templates/eventedit.html
new file mode 100644
index 0000000..a3f58e8
--- /dev/null
+++ b/plugins/calendar/skins/larry/templates/eventedit.html
@@ -0,0 +1,100 @@
+<div id="eventedit" class="uidialog uidialog-tabbed">
+ <form id="eventtabs" action="#" method="post" enctype="multipart/form-data">
+ <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><li id="edit-tab-attendees"><a href="#event-tab-3"><roundcube:label name="calendar.tabattendees" /></a></li><li id="edit-tab-attachments"><a href="#event-tab-4"><roundcube:label name="calendar.tabattachments" /></a></li>
+ </ul>
+ <!-- basic info -->
+ <div id="event-tab-1">
+ <div class="event-section">
+ <label for="edit-title"><roundcube:label name="calendar.title" /></label>
+ <br />
+ <input type="text" class="text" name="title" id="edit-title" size="40" />
+ </div>
+ <div class="event-section">
+ <label for="edit-location"><roundcube:label name="calendar.location" /></label>
+ <br />
+ <input type="text" class="text" name="location" id="edit-location" size="40" />
+ </div>
+ <div class="event-section">
+ <label for="edit-description"><roundcube:label name="calendar.description" /></label>
+ <br />
+ <textarea name="description" id="edit-description" class="text" rows="5" cols="40"></textarea>
+ </div>
+ <div class="event-section">
+ <label style="float:right;padding-right:0.5em"><input type="checkbox" name="allday" id="edit-allday" value="1" /><roundcube:label name="calendar.all-day" /></label>
+ <label for="edit-startdate"><roundcube:label name="calendar.start" /></label>
+ <input type="text" name="startdate" size="10" id="edit-startdate" /> &nbsp;
+ <input type="text" name="starttime" size="6" id="edit-starttime" />
+ </div>
+ <div class="event-section">
+ <label for="edit-enddate"><roundcube:label name="calendar.end" /></label>
+ <input type="text" name="enddate" size="10" id="edit-enddate" /> &nbsp;
+ <input type="text" name="endtime" size="6" id="edit-endtime" />
+ </div>
+ <div class="event-section" id="edit-alarms">
+ <label for="edit-alarm"><roundcube:label name="calendar.alarms" /></label>
+ <roundcube:object name="plugin.alarm_select" />
+ </div>
+ <div class="event-section" id="calendar-select">
+ <label for="edit-calendar"><roundcube:label name="calendar.calendar" /></label>
+ <roundcube:object name="plugin.calendar_select" id="edit-calendar" />
+ </div>
+ <div class="event-section">
+ <label for="edit-categories"><roundcube:label name="calendar.category" /></label>
+ <roundcube:object name="plugin.category_select" id="edit-categories" />
+ </div>
+ <div class="event-section">
+ <label for="edit-free-busy"><roundcube:label name="calendar.freebusy" /></label>
+ <roundcube:object name="plugin.freebusy_select" id="edit-free-busy" />
+ </div>
+ <div class="event-section">
+ <label for="edit-priority"><roundcube:label name="calendar.priority" /></label>
+ <roundcube:object name="plugin.priority_select" id="edit-priority" />
+ </div>
+ <div class="event-section">
+ <label for="edit-sensitivity"><roundcube:label name="calendar.sensitivity" /></label>
+ <roundcube:object name="plugin.sensitivity_select" id="edit-sensitivity" />
+ </div>
+ </div>
+ <!-- recurrence settings -->
+ <div id="event-tab-2">
+ <div class="event-section border-after">
+ <roundcube:object name="plugin.recurrence_form" part="frequency" />
+ </div>
+ <div class="recurrence-form border-after" id="recurrence-form-daily">
+ <roundcube:object name="plugin.recurrence_form" part="daily" class="event-section" />
+ </div>
+ <div class="recurrence-form border-after" id="recurrence-form-weekly">
+ <roundcube:object name="plugin.recurrence_form" part="weekly" class="event-section" />
+ </div>
+ <div class="recurrence-form border-after" id="recurrence-form-monthly">
+ <roundcube:object name="plugin.recurrence_form" part="monthly" class="event-section" />
+ </div>
+ <div class="recurrence-form border-after" id="recurrence-form-yearly">
+ <roundcube:object name="plugin.recurrence_form" part="yearly" class="event-section" />
+ </div>
+ <div class="recurrence-form" id="recurrence-form-until">
+ <roundcube:object name="plugin.recurrence_form" part="until" class="event-section" />
+ </div>
+ </div>
+ <!-- attendees list -->
+ <div id="event-tab-3">
+ <roundcube:object name="plugin.attendees_list" id="edit-attendees-table" class="records-table" />
+ <roundcube:object name="plugin.attendees_form" id="edit-attendees-form" />
+ <roundcube:include file="/templates/freebusylegend.html" />
+ </div>
+ <!-- attachments list (with upload form) -->
+ <div id="event-tab-4">
+ <div id="edit-attachments">
+ <roundcube:object name="plugin.attachments_list" id="attachment-list" class="attachmentslist" cancelIcon="/images/0.gif" />
+ </div>
+ <div id="edit-attachments-form">
+ <roundcube:object name="plugin.attachments_form" id="calendar-attachment-form" attachmentFieldSize="30" />
+ </div>
+ </div>
+ </form>
+
+ <roundcube:object name="plugin.edit_attendees_notify" id="edit-attendees-notify" class="event-dialog-message" style="display:none" />
+ <roundcube:object name="plugin.edit_recurring_warning" class="event-dialog-message edit-recurring-warning" style="display:none" />
+ <div id="edit-localchanges-warning" class="event-dialog-message" style="display:none"><roundcube:label name="calendar.localchangeswarning" /></div>
+</div> \ No newline at end of file
diff --git a/plugins/calendar/skins/larry/templates/freebusylegend.html b/plugins/calendar/skins/larry/templates/freebusylegend.html
new file mode 100644
index 0000000..51e5ddc
--- /dev/null
+++ b/plugins/calendar/skins/larry/templates/freebusylegend.html
@@ -0,0 +1,7 @@
+ <div id="edit-attendees-legend" class="availability">
+ <span class="legend"><img class="availabilityicon free" src="./program/blank.gif" /> <roundcube:label name="calendar.availfree" /></span>
+ <span class="legend"><img class="availabilityicon busy" src="./program/blank.gif" /> <roundcube:label name="calendar.availbusy" /></span>
+ <span class="legend"><img class="availabilityicon tentative" src="./program/blank.gif" /> <roundcube:label name="calendar.availtentative" /></span>
+ <span class="legend"><img class="availabilityicon out-of-office" src="./program/blank.gif" /> <roundcube:label name="calendar.availoutofoffice" /></span>
+ <span class="legend"><img class="availabilityicon unknown" src="./program/blank.gif" /> <roundcube:label name="calendar.availunknown" /></span>
+ </div>
diff --git a/plugins/calendar/skins/larry/templates/itipattend.html b/plugins/calendar/skins/larry/templates/itipattend.html
new file mode 100644
index 0000000..bfbb08f
--- /dev/null
+++ b/plugins/calendar/skins/larry/templates/itipattend.html
@@ -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 calendaritipattend">
+
+<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 class="centerbox uibox">
+ <roundcube:object name="plugin.event_inviteform" />
+ <roundcube:object name="plugin.event_invitebox" class="calendar-invitebox" />
+ <roundcube:object name="plugin.event_rsvp_buttons" type="submit" iname="rsvp" id="event-rsvp" />
+ <roundcube:object name="message" id="message" />
+ </form>
+</div>
+
+</div>
+
+</body>
+</html> \ No newline at end of file
diff --git a/plugins/calendar/skins/larry/templates/kolabacl.html b/plugins/calendar/skins/larry/templates/kolabacl.html
new file mode 100644
index 0000000..05a279a
--- /dev/null
+++ b/plugins/calendar/skins/larry/templates/kolabacl.html
@@ -0,0 +1,24 @@
+<roundcube:object name="doctype" value="html5" />
+<html>
+<head>
+<title><roundcube:object name="pagetitle" /></title>
+<roundcube:include file="/includes/links.html" />
+<style type="text/css" media="screen">
+
+body.aclform {
+ background: #efefef;
+ margin: 0;
+}
+
+body.aclform .hint {
+ margin: 1em;
+}
+
+</style>
+</head>
+<body class="iframe aclform">
+
+<roundcube:object name="folderacl" />
+
+</body>
+</html>
diff --git a/plugins/calendar/skins/larry/templates/kolabform.html b/plugins/calendar/skins/larry/templates/kolabform.html
new file mode 100644
index 0000000..77a1c30
--- /dev/null
+++ b/plugins/calendar/skins/larry/templates/kolabform.html
@@ -0,0 +1,9 @@
+<div id="calendar-kolabform" class="propform tabbed">
+ <roundcube:object name="calendarform" />
+</div>
+<style type="text/css">
+#calendarpropform { min-width:680px; margin-top:-12px; }
+</style>
+<script type="text/javascript">
+UI.init_tabs('#calendar-kolabform');
+</script> \ No newline at end of file
diff --git a/plugins/calendar/skins/larry/templates/print.html b/plugins/calendar/skins/larry/templates/print.html
new file mode 100644
index 0000000..1c31ca2
--- /dev/null
+++ b/plugins/calendar/skins/larry/templates/print.html
@@ -0,0 +1,28 @@
+<roundcube:object name="doctype" value="html5" />
+<html>
+<head>
+<title><roundcube:object name="pagetitle" /></title>
+</head>
+<body class="calendarprint">
+
+<div id="printconfig" class="noprint">
+ <div class="calwidth">
+ <a href="#close" onclick="window.close()" class="rightalign"><roundcube:label name="close" /></a>
+ <span class="prop"><input type="button" id="printme" value="<roundcube:label name='print' />" onclick="window.print()"></span>
+ <span class="prop"><label><input type="checkbox" id="propdescription" checked="checked" /> <roundcube:label name="calendar.printdescriptions" /></label></span>
+ </div>
+</div>
+
+<roundcube:object name="message" id="message" class="noprint" />
+
+<div id="calendar" class="calwidth"></div>
+
+<div class="calwidth">
+ <roundcube:object name="plugin.calendar_list" activeonly="true" id="calendarlist" />
+</div>
+
+<roundcube:object name="plugin.calendar_css" printmode="true" />
+
+<!--[if lte IE 7]><link rel="stylesheet" type="text/css" href="./plugins/calendar/skins/default/print.iehacks.css" /><![endif]-->
+</body>
+</html> \ No newline at end of file
diff --git a/plugins/kolab_addressbook/kolab_addressbook.php b/plugins/kolab_addressbook/kolab_addressbook.php
index f33a037..c03c426 100644
--- a/plugins/kolab_addressbook/kolab_addressbook.php
+++ b/plugins/kolab_addressbook/kolab_addressbook.php
@@ -131,7 +131,7 @@ class kolab_addressbook extends rcube_plugin
'editable' => $abook->editable,
'groups' => $abook->groups,
'undelete' => $abook->undelete && $undelete,
- 'realname' => rcube_charset_convert($abook->get_realname(), 'UTF7-IMAP'), // IMAP folder name
+ 'realname' => rcube_charset::convert($abook->get_realname(), 'UTF7-IMAP'), // IMAP folder name
'class_name' => $abook->get_namespace(),
'kolab' => true,
);
@@ -258,7 +258,7 @@ class kolab_addressbook extends rcube_plugin
// convert to UTF8 and sort
$names = array();
foreach ($this->folders as $c_folder)
- $names[$c_folder->name] = rcube_charset_convert($c_folder->name, 'UTF7-IMAP');
+ $names[$c_folder->name] = rcube_charset::convert($c_folder->name, 'UTF7-IMAP');
asort($names, SORT_LOCALE_STRING);
@@ -420,14 +420,14 @@ class kolab_addressbook extends rcube_plugin
*/
public function book_save()
{
+ $storage = $this->rc->get_storage();
$folder = trim(get_input_value('_name', RCUBE_INPUT_POST, true, 'UTF7-IMAP'));
$oldfolder = trim(get_input_value('_oldname', RCUBE_INPUT_POST, true)); // UTF7-IMAP
$path = trim(get_input_value('_parent', RCUBE_INPUT_POST, true)); // UTF7-IMAP
- $delimiter = $_SESSION['imap_delimiter'];
+ $delimiter = $storage->get_hierarchy_delimiter();
if (strlen($oldfolder)) {
- $this->rc->imap_connect();
- $options = $this->rc->imap->mailbox_info($oldfolder);
+ $options = $storage->folder_info($oldfolder);
}
if (!empty($options) && ($options['norename'] || $options['protected'])) {
@@ -458,14 +458,12 @@ class kolab_addressbook extends rcube_plugin
}
else {
// add namespace prefix (when needed)
- $this->rc->imap_init();
- $folder = $this->rc->imap->mod_mailbox($folder, 'in');
+ $folder = $storage->mod_folder($folder, 'in');
}
// Check access rights to the parent folder
if (strlen($path) && (!strlen($oldfolder) || $oldfolder != $folder)) {
- $this->rc->imap_connect();
- $parent_opts = $this->rc->imap->mailbox_info($path);
+ $parent_opts = $storage->folder_info($path);
if ($parent_opts['namespace'] != 'personal'
&& (empty($parent_opts['rights']) || !preg_match('/[ck]/', implode($parent_opts['rights'])))
) {
@@ -552,7 +550,7 @@ class kolab_addressbook extends rcube_plugin
'readonly' => false,
'editable' => true,
'groups' => true,
- 'realname' => rcube_charset_convert($folder, 'UTF7-IMAP'), // IMAP folder name
+ 'realname' => rcube_charset::convert($folder, 'UTF7-IMAP'), // IMAP folder name
'class_name' => $kolab_folder->get_namespace(),
'kolab' => true,
), rcube_kolab::folder_id($oldfolder));
diff --git a/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php b/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php
index 9c40afb..f028f52 100644
--- a/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php
+++ b/plugins/kolab_addressbook/lib/rcube_kolab_contacts.php
@@ -131,8 +131,9 @@ class rcube_kolab_contacts extends rcube_addressbook
public function __construct($imap_folder = null)
{
- if ($imap_folder)
+ if ($imap_folder) {
$this->imap_folder = $imap_folder;
+ }
// extend coltypes configuration
$format = rcube_kolab::get_format('contact');
@@ -653,8 +654,9 @@ class rcube_kolab_contacts extends rcube_addressbook
}
// store IMAP uids for undelete()
- if (!$force)
+ if (!$force) {
$_SESSION['kolab_delete_uids'] = $imap_uids;
+ }
return $count;
}
@@ -1015,32 +1017,39 @@ class rcube_kolab_contacts extends rcube_addressbook
*/
private function _sort_contacts_comp($a, $b)
{
- $a_name = $a['name'];
- $b_name = $b['name'];
-
- if (!$a_name) {
- $a_name = join(' ', array_filter(array($a['prefix'], $a['firstname'],
- $a['middlename'], $a['surname'], $a['suffix'])));
- if (!$a_name) {
- $a_name = is_array($a['email']) ? $a['email'][0] : $a['email'];
- }
- }
- if (!$b_name) {
- $b_name = join(' ', array_filter(array($b['prefix'], $b['firstname'],
- $b['middlename'], $b['surname'], $b['suffix'])));
- if (!$b_name) {
- $b_name = is_array($b['email']) ? $b['email'][0] : $b['email'];
- }
+ $a_value = $b_value = '';
+
+ switch ($this->sort_col) {
+ case 'name':
+ $a_value = $a['name'] . $a['prefix'];
+ $b_value = $b['name'] . $b['prefix'];
+ case 'firstname':
+ $a_value .= $a['firstname'] . $a['middlename'] . $a['surname'];
+ $b_value .= $b['firstname'] . $b['middlename'] . $b['surname'];
+ break;
+
+ case 'surname':
+ $a_value = $a['surname'] . $a['firstname'] . $a['middlename'];
+ $b_value = $b['surname'] . $b['firstname'] . $b['middlename'];
+ break;
+
+ default:
+ $a_value = $a[$this->sort_col];
+ $b_value = $b[$this->sort_col];
+ break;
}
- // return strcasecmp($a_name, $b_name);
+ $a_value .= is_array($a['email']) ? $a['email'][0] : $a['email'];
+ $b_value .= is_array($b['email']) ? $b['email'][0] : $b['email'];
+
+ // return strcasecmp($a_value, $b_value);
// make sorting unicode-safe and locale-dependent
- if ($a_name == $b_name)
+ if ($a_value == $b_value)
return 0;
- $arr = array($a_name, $b_name);
+ $arr = array($a_value, $b_value);
sort($arr, SORT_LOCALE_STRING);
- return $a_name == $arr[0] ? -1 : 1;
+ return $a_value == $arr[0] ? -1 : 1;
}
/**
diff --git a/plugins/kolab_addressbook/localization/de_CH.inc b/plugins/kolab_addressbook/localization/de_CH.inc
new file mode 100644
index 0000000..f91a24b
--- /dev/null
+++ b/plugins/kolab_addressbook/localization/de_CH.inc
@@ -0,0 +1,46 @@
+<?php
+
+$labels = array();
+$labels['initials'] = 'Initialen';
+$labels['profession'] = 'Berufsbezeichnung';
+$labels['officelocation'] = 'Büro Adresse';
+$labels['children'] = 'Kinder';
+$labels['pgppublickey'] = 'Öffentlicher PGP-Schlüssel';
+$labels['freebusyurl'] = 'Frei/Belegt URL';
+$labels['typebusiness'] = 'Dienstlich';
+$labels['typebusinessfax'] = 'Dienst';
+$labels['typecompany'] = 'Firma';
+$labels['typeprimary'] = 'Primär';
+$labels['typetelex'] = 'Telex';
+$labels['typeradio'] = 'Funk';
+$labels['typeisdn'] = 'ISDN';
+$labels['typettytdd'] = 'Telescrit';
+$labels['typecallback'] = 'Rückruf';
+$labels['settings'] = 'Einstellungen';
+
+$labels['bookcreate'] = 'Adressbuch anlegen';
+$labels['bookedit'] = 'Adressbuch bearbeiten';
+$labels['bookdelete'] = 'Adressbuch löschen';
+$labels['bookproperties'] = 'Eigenschaften des Adressbuchs';
+$labels['bookname'] = 'Name des Buches';
+$labels['parentbook'] = 'Übergeordnetes Buch';
+
+$labels['addressbookprio'] = 'Reihenfolge der Adressbücher';
+$labels['personalfirst'] = 'Private(s) Adressbuch/Adressbücher zuerst';
+$labels['globalfirst'] = 'Globale(s) Adressbuch/Adressbücher zuerst';
+$labels['personalonly'] = 'Nur persönliche(s) Adressbuch/Adressbücher';
+$labels['globalonly'] = 'Nur globale(s) Adressbuch/Adressbücher';
+
+$messages['bookdeleteconfirm'] = 'Soll das gewählte Adressbuch und alle Kontakte darin wirklich gelöscht werden?';
+$messages['bookdeleting'] = 'Adressbuch wird gelöscht...';
+$messages['booksaving'] = 'Adressbuch wird gespeichert...';
+$messages['bookdeleted'] = 'Adressbuch erfolgreich gelöscht.';
+$messages['bookupdated'] = 'Adressbuch erfolgreich aktualisiert.';
+$messages['bookcreated'] = 'Adressbuch erfolgreich angelegt.';
+$messages['bookdeleteerror'] = 'Fehler beim Löschen des Adressbuchs.';
+$messages['bookupdateerror'] = 'Fehler beim Aktualisieren des Adressbuchs.';
+$messages['bookcreateerror'] = 'Fehler beim Anlegen des Adressbuchs.';
+$messages['nobooknamewarning'] = 'Bitte den Namen des Adressbuchs angeben.';
+$messages['noemailnamewarning'] = 'Bitte E-Mail-Adresse oder Namen des Kontakts angeben.';
+
+?>
diff --git a/plugins/kolab_addressbook/localization/de_DE.inc b/plugins/kolab_addressbook/localization/de_DE.inc
new file mode 100644
index 0000000..5fd86b7
--- /dev/null
+++ b/plugins/kolab_addressbook/localization/de_DE.inc
@@ -0,0 +1,46 @@
+<?php
+
+$labels = array();
+$labels['initials'] = 'Initialen';
+$labels['profession'] = 'Berufsbezeichnung';
+$labels['officelocation'] = 'Büro Adresse';
+$labels['children'] = 'Kinder';
+$labels['pgppublickey'] = 'Öffentlicher PGP-Schlüssel';
+$labels['freebusyurl'] = 'Frei/Belegt URL';
+$labels['typebusiness'] = 'Dienstlich';
+$labels['typebusinessfax'] = 'Dienst';
+$labels['typecompany'] = 'Firma';
+$labels['typeprimary'] = 'Primär';
+$labels['typetelex'] = 'Fernschreiber';
+$labels['typeradio'] = 'Funktelefon';
+$labels['typeisdn'] = 'ISDN';
+$labels['typettytdd'] = 'Schreibtelefon';
+$labels['typecallback'] = 'Rückruf';
+$labels['settings'] = 'Einstellungen';
+
+$labels['bookcreate'] = 'Adressbuch anlegen';
+$labels['bookedit'] = 'Adressbuch bearbeiten';
+$labels['bookdelete'] = 'Adressbuch löschen';
+$labels['bookproperties'] = 'Eigenschaften des Adressbuchs';
+$labels['bookname'] = 'Name des Buches';
+$labels['parentbook'] = 'Übergeordnetes Buch';
+
+$labels['addressbookprio'] = 'Reihenfolge der Adressbücher';
+$labels['personalfirst'] = 'Private(s) Adressbuch/Adressbücher zuerst';
+$labels['globalfirst'] = 'Globale(s) Adressbuch/Adressbücher zuerst';
+$labels['personalonly'] = 'Nur persönliche(s) Adressbuch/Adressbücher';
+$labels['globalonly'] = 'Nur globale(s) Adressbuch/Adressbücher';
+
+$messages['bookdeleteconfirm'] = 'Soll das gewählte Adressbuch und alle Kontakte darin wirklich gelöscht werden?';
+$messages['bookdeleting'] = 'Adressbuch wird gelöscht...';
+$messages['booksaving'] = 'Adressbuch wird gespeichert...';
+$messages['bookdeleted'] = 'Adressbuch erfolgreich gelöscht.';
+$messages['bookupdated'] = 'Adressbuch erfolgreich aktualisiert.';
+$messages['bookcreated'] = 'Adressbuch erfolgreich angelegt.';
+$messages['bookdeleteerror'] = 'Fehler beim Löschen des Adressbuchs.';
+$messages['bookupdateerror'] = 'Fehler beim Aktualisieren des Adressbuchs.';
+$messages['bookcreateerror'] = 'Fehler beim Anlegen des Adressbuchs.';
+$messages['nobooknamewarning'] = 'Bitte den Namen des Adressbuchs angeben.';
+$messages['noemailnamewarning'] = 'Bitte E-Mail-Adresse oder Namen des Kontakts angeben.';
+
+?>
diff --git a/plugins/kolab_addressbook/package.xml b/plugins/kolab_addressbook/package.xml
index 7e7b84c..e077dcb 100644
--- a/plugins/kolab_addressbook/package.xml
+++ b/plugins/kolab_addressbook/package.xml
@@ -4,7 +4,7 @@
http://pear.php.net/dtd/package-2.0
http://pear.php.net/dtd/package-2.0.xsd">
<name>kolab_addressbook</name>
- <uri>http://kolabsys.com</uri>
+ <uri>http://git.kolab.org/roundcube-plugins-kolab/</uri>
<summary>Kolab addressbook</summary>
<description>
Sample plugin to add a new address book source with data from Kolab storage.
diff --git a/plugins/kolab_auth/kolab_auth.php b/plugins/kolab_auth/kolab_auth.php
index 3fa8524..691f542 100644
--- a/plugins/kolab_auth/kolab_auth.php
+++ b/plugins/kolab_auth/kolab_auth.php
@@ -43,7 +43,7 @@ class kolab_auth extends rcube_plugin
// Hooks related to "Login As" feature
$this->add_hook('template_object_loginform', array($this, 'login_form'));
- $this->add_hook('imap_connect', array($this, 'imap_connect'));
+ $this->add_hook('storage_connect', array($this, 'imap_connect'));
$this->add_hook('managesieve_connect', array($this, 'imap_connect'));
$this->add_hook('smtp_connect', array($this, 'smtp_connect'));
@@ -135,7 +135,7 @@ class kolab_auth extends rcube_plugin
} else {
$dont_override = $rcmail->config->get('dont_override');
if (in_array($setting_name, $dont_override)) {
- $_dont_override = Array();
+ $_dont_override = array();
foreach ($dont_override as $_setting) {
if ($_setting != $setting_name) {
$_dont_override[] = $_setting;
@@ -346,6 +346,9 @@ class kolab_auth extends rcube_plugin
// Set credentials
if ($record) {
+ // Store UID in session for use by other plugins
+ $_SESSION['kolab_uid'] = is_array($record['uid']) ? $record['uid'][0] : $record['uid'];
+
if ($login_attr)
$this->data['user_login'] = is_array($record[$login_attr]) ? $record[$login_attr][0] : $record[$login_attr];
if ($alias_attr)
@@ -405,8 +408,9 @@ class kolab_auth extends rcube_plugin
*/
private function init_ldap()
{
- if ($this->ldap)
+ if ($this->ldap) {
return $this->ldap->ready;
+ }
$rcmail = rcmail::get_instance();
@@ -447,8 +451,9 @@ class kolab_auth extends rcube_plugin
$this->ldap->set_filter($filter);
$results = $this->ldap->list_records();
- if (count($results->records) == 1)
+ if (count($results->records) == 1) {
return $results->records[0];
+ }
}
/**
@@ -480,9 +485,16 @@ class kolab_auth extends rcube_plugin
*/
class kolab_auth_ldap_backend extends rcube_ldap
{
+ function __construct($p, $debug=false, $mail_domain=null)
+ {
+ parent::__construct($p, $debug, $mail_domain);
+ $this->fieldmap['uid'] = 'uid';
+ }
+
function set_filter($filter)
{
- if ($filter)
+ if ($filter) {
$this->prop['filter'] = $filter;
+ }
}
}
diff --git a/plugins/kolab_auth/localization/de_CH.inc b/plugins/kolab_auth/localization/de_CH.inc
new file mode 100644
index 0000000..9cdad33
--- /dev/null
+++ b/plugins/kolab_auth/localization/de_CH.inc
@@ -0,0 +1,5 @@
+<?php
+
+$labels['loginas'] = 'Anmelden als';
+
+?>
diff --git a/plugins/kolab_auth/localization/de_DE.inc b/plugins/kolab_auth/localization/de_DE.inc
new file mode 100644
index 0000000..9cdad33
--- /dev/null
+++ b/plugins/kolab_auth/localization/de_DE.inc
@@ -0,0 +1,5 @@
+<?php
+
+$labels['loginas'] = 'Anmelden als';
+
+?>
diff --git a/plugins/kolab_auth/package.xml b/plugins/kolab_auth/package.xml
index f333dd4..937798d 100644
--- a/plugins/kolab_auth/package.xml
+++ b/plugins/kolab_auth/package.xml
@@ -4,7 +4,7 @@
http://pear.php.net/dtd/package-2.0
http://pear.php.net/dtd/package-2.0.xsd">
<name>kolab_auth</name>
- <uri>http://kolabsys.com</uri>
+ <uri>http://git.kolab.org/roundcube-plugins-kolab/</uri>
<summary>Kolab Authentication</summary>
<description>
Authenticates on LDAP server, finds canonized authentication ID for IMAP
@@ -18,9 +18,9 @@
<email>machniak@kolabsys.com</email>
<active>yes</active>
</lead>
- <date>2011-11-01</date>
+ <date>2012-02-29</date>
<version>
- <release>0.1</release>
+ <release>0.2</release>
<api>0.1</api>
</version>
<stability>
@@ -38,6 +38,8 @@
<file name="config.inc.php.dist" role="data"></file>
<file name="LICENSE" role="data"></file>
+ <file name="localization/de_CH.inc" role="data"></file>
+ <file name="localization/de_DE.inc" role="data"></file>
<file name="localization/en_US.inc" role="data"></file>
<file name="localization/pl_PL.inc" role="data"></file>
</dir>
diff --git a/plugins/kolab_config/package.xml b/plugins/kolab_config/package.xml
index 73291c8..85c7faa 100644
--- a/plugins/kolab_config/package.xml
+++ b/plugins/kolab_config/package.xml
@@ -4,7 +4,7 @@
http://pear.php.net/dtd/package-2.0
http://pear.php.net/dtd/package-2.0.xsd">
<name>kolab_config</name>
- <uri>http://kolabsys.com</uri>
+ <uri>http://git.kolab.org/roundcube-plugins-kolab/</uri>
<summary>Kolab configuration storage</summary>
<description>
Plugin to use Kolab server as a configuration storage. Provides an API to handle
diff --git a/plugins/kolab_core/package.xml b/plugins/kolab_core/package.xml
index 46aa29f..fa40754 100644
--- a/plugins/kolab_core/package.xml
+++ b/plugins/kolab_core/package.xml
@@ -4,7 +4,7 @@
http://pear.php.net/dtd/package-2.0
http://pear.php.net/dtd/package-2.0.xsd">
<name>kolab_core</name>
- <uri>http://kolabsys.com</uri>
+ <uri>http://git.kolab.org/roundcube-plugins-kolab/</uri>
<summary>Kolab API</summary>
<description>
Plugin to setup a basic environment for interaction with a Kolab server.
diff --git a/plugins/kolab_core/rcube_kolab.php b/plugins/kolab_core/rcube_kolab.php
index 7f2702f..6bd5f60 100644
--- a/plugins/kolab_core/rcube_kolab.php
+++ b/plugins/kolab_core/rcube_kolab.php
@@ -222,15 +222,9 @@ class rcube_kolab
static $subscribed; // local cache
if (!$subscribed) {
- $rcmail = rcmail::get_instance();
- // try without connection first (list could be served from cache)
- $subscribed = $rcmail->imap ? $rcmail->imap->list_mailboxes() : array();
-
- // now really get the list from the IMAP server
- if (empty($subscribed) || $subscribed == array('INBOX')) {
- $rcmail->imap_connect();
- $subscribed = $rcmail->imap->list_mailboxes();
- }
+ $rcmail = rcmail::get_instance();
+ $storage = $rcmail->get_storage();
+ $subscribed = $storage->list_folders();
}
return in_array($folder, $subscribed);
@@ -390,10 +384,9 @@ class rcube_kolab
*/
public static function object_name($folder, &$folder_ns=null)
{
- $rcmail = rcmail::get_instance();
- $rcmail->imap_init();
-
- $namespace = $rcmail->imap->get_namespace();
+ $rcmail = rcmail::get_instance();
+ $storage = $rcmail->get_storage();
+ $namespace = $storage->get_namespace();
$found = false;
if (!empty($namespace['shared'])) {
@@ -444,9 +437,9 @@ class rcube_kolab
}
if (empty($delim))
- $delim = $rcmail->imap->get_hierarchy_delimiter();
+ $delim = $storage->get_hierarchy_delimiter();
- $folder = rcube_charset_convert($folder, 'UTF7-IMAP');
+ $folder = rcube_charset::convert($folder, 'UTF7-IMAP');
$folder = str_replace($delim, ' &raquo; ', $folder);
if ($prefix)
@@ -467,10 +460,9 @@ class rcube_kolab
*/
public static function folder_namespace($folder)
{
- $rcmail = rcmail::get_instance();
- $rcmail->imap_init();
-
- $namespace = $rcmail->imap->get_namespace();
+ $rcmail = rcmail::get_instance();
+ $storage = $rcmail->get_storage();
+ $namespace = $storage->get_namespace();
if (!empty($namespace)) {
foreach ($namespace as $nsname => $nsvalue) {
@@ -498,10 +490,13 @@ class rcube_kolab
*/
public static function folder_selector($type, $attrs, $current = '')
{
+ $rcmail = rcmail::get_instance();
+ $storage = $rcmail->get_storage();
+
// get all folders of specified type
$folders = self::get_folders($type);
- $delim = $_SESSION['imap_delimiter'];
+ $delim = $storage->get_hierarchy_delimiter();
$names = array();
$len = strlen($current);
@@ -528,12 +523,12 @@ class rcube_kolab
}
}
- $names[$name] = rcube_charset_convert($name, 'UTF7-IMAP');
+ $names[$name] = rcube_charset::convert($name, 'UTF7-IMAP');
}
// Make sure parent folder is listed (might be skipped e.g. if it's namespace root)
if ($p_len && !isset($names[$parent])) {
- $names[$parent] = rcube_charset_convert($parent, 'UTF7-IMAP');
+ $names[$parent] = rcube_charset::convert($parent, 'UTF7-IMAP');
}
// Sort folders list
diff --git a/plugins/kolab_folders/config.inc.php.dist b/plugins/kolab_folders/config.inc.php.dist
index d907a98..e393684 100644
--- a/plugins/kolab_folders/config.inc.php.dist
+++ b/plugins/kolab_folders/config.inc.php.dist
@@ -26,9 +26,9 @@ $rcmail_config['kolab_folders_mail_drafts'] = '';
// Sent folder
$rcmail_config['kolab_folders_mail_sentitems'] = '';
// Trash folder
-$rcmail_config['kolab_folders_mail_junkemail'] = '';
+$rcmail_config['kolab_folders_mail_wastebasket'] = '';
// Others folders
$rcmail_config['kolab_folders_mail_outbox'] = '';
-$rcmail_config['kolab_folders_mail_wastebasket'] = '';
+$rcmail_config['kolab_folders_mail_junkemail'] = '';
?>
diff --git a/plugins/kolab_folders/kolab_folders.php b/plugins/kolab_folders/kolab_folders.php
index 446a091..e1b2e63 100644
--- a/plugins/kolab_folders/kolab_folders.php
+++ b/plugins/kolab_folders/kolab_folders.php
@@ -41,7 +41,7 @@ class kolab_folders extends rcube_plugin
$this->rc = rcmail::get_instance();
// Folder listing hooks
- $this->add_hook('mailboxes_list', array($this, 'mailboxes_list'));
+ $this->add_hook('storage_folders', array($this, 'mailboxes_list'));
// Folder manager hooks
$this->add_hook('folder_form', array($this, 'folder_form'));
@@ -72,7 +72,6 @@ class kolab_folders extends rcube_plugin
$folderdata = $this->get_folder_type_list($args['root'].$args['name'], true);
if (!is_array($folderdata)) {
- $args['folders'] = false;
return $args;
}
@@ -91,9 +90,14 @@ class kolab_folders extends rcube_plugin
return $args;
}
+ $storage = $this->rc->get_storage();
+
// Get folders list
if ($args['mode'] == 'LIST') {
- $args['folders'] = $this->rc->imap->conn->listMailboxes($args['root'], $args['name']);
+ if (!$storage->check_connection()) {
+ return $args;
+ }
+ $args['folders'] = $storage->conn->listMailboxes($args['root'], $args['name']);
}
else {
$args['folders'] = $this->list_subscribed($args['root'], $args['name']);
@@ -200,16 +204,21 @@ class kolab_folders extends rcube_plugin
$ctype = 'mail';
}
+ $storage = $this->rc->get_storage();
+
// Don't allow changing type of shared folder, according to ACL
if (strlen($mbox)) {
- $options = $this->rc->imap->mailbox_info($mbox);
+ $options = $storage->folder_info($mbox);
if ($options['namespace'] != 'personal' && !in_array('a', $options['rights'])) {
- if (in_array($ctype, $this->types))
+ if (in_array($ctype, $this->types)) {
$value = $this->gettext('foldertype'.$ctype);
- else
+ }
+ else {
$value = $ctype;
- if ($subtype)
+ }
+ if ($subtype) {
$value .= ' ('. ($subtype == 'default' ? $this->gettext('default') : $subtype) .')';
+ }
$args['form']['props']['fieldsets']['settings']['content']['foldertype'] = array(
'label' => $this->gettext('folderctype'),
@@ -298,13 +307,15 @@ class kolab_folders extends rcube_plugin
$ctype .= $subtype ? '.'.$subtype : '';
+ $storage = $this->rc->get_storage();
+
// Create folder
if (!strlen($old_mbox)) {
// By default don't subscribe to non-mail folders
if ($subscribe)
$subscribe = (bool) preg_match('/^mail/', $ctype);
- $result = $this->rc->imap->create_mailbox($mbox, $subscribe);
+ $result = $storage->create_folder($mbox, $subscribe);
// Set folder type
if ($result) {
$this->set_folder_type($mbox, $ctype);
@@ -313,7 +324,7 @@ class kolab_folders extends rcube_plugin
// Rename folder
else {
if ($old_mbox != $mbox) {
- $result = $this->rc->imap->rename_mailbox($old_mbox, $mbox);
+ $result = $storage->rename_folder($old_mbox, $mbox);
}
else {
$result = true;
@@ -348,9 +359,11 @@ class kolab_folders extends rcube_plugin
*/
function metadata_support()
{
- return $this->rc->imap->get_capability('METADATA') ||
- $this->rc->imap->get_capability('ANNOTATEMORE') ||
- $this->rc->imap->get_capability('ANNOTATEMORE2');
+ $storage = $this->rc->get_storage();
+
+ return $storage->get_capability('METADATA') ||
+ $storage->get_capability('ANNOTATEMORE') ||
+ $storage->get_capability('ANNOTATEMORE2');
}
/**
@@ -362,7 +375,8 @@ class kolab_folders extends rcube_plugin
*/
function get_folder_type($folder)
{
- $folderdata = $this->rc->imap->get_metadata($folder, array(kolab_folders::CTYPE_KEY));
+ $storage = $this->rc->get_storage();
+ $folderdata = $storage->get_metadata($folder, array(kolab_folders::CTYPE_KEY));
return explode('.', $folderdata[$folder][kolab_folders::CTYPE_KEY]);
}
@@ -377,7 +391,9 @@ class kolab_folders extends rcube_plugin
*/
function set_folder_type($folder, $type='mail')
{
- return $this->rc->imap->set_metadata($folder, array(kolab_folders::CTYPE_KEY => $type));
+ $storage = $this->rc->get_storage();
+
+ return $storage->set_metadata($folder, array(kolab_folders::CTYPE_KEY => $type));
}
/**
@@ -390,23 +406,27 @@ class kolab_folders extends rcube_plugin
*/
private function list_subscribed($root='', $name='*')
{
- $imap = $this->rc->imap;
+ $storage = $this->rc->get_storage();
+
+ if (!$storage->check_connection()) {
+ return null;
+ }
// Code copied from rcube_imap::_list_mailboxes()
// Server supports LIST-EXTENDED, we can use selection options
// #1486225: Some dovecot versions returns wrong result using LIST-EXTENDED
if (!$this->rc->config->get('imap_force_lsub') && $imap->get_capability('LIST-EXTENDED')) {
// This will also set mailbox options, LSUB doesn't do that
- $a_folders = $imap->conn->listMailboxes($root, $name,
+ $a_folders = $storage->conn->listMailboxes($root, $name,
NULL, array('SUBSCRIBED'));
// remove non-existent folders
- if (is_array($a_folders) && $name = '*') {
+ if (is_array($a_folders) && $name = '*' && !empty($storage->conn->data['LIST'])) {
foreach ($a_folders as $idx => $folder) {
- if ($imap->conn->data['LIST'] && ($opts = $imap->conn->data['LIST'][$folder])
+ if (($opts = $storage->conn->data['LIST'][$folder])
&& in_array('\\NonExistent', $opts)
) {
- $imap->conn->unsubscribe($folder);
+ $storage->conn->unsubscribe($folder);
unset($a_folders[$idx]);
}
}
@@ -414,17 +434,17 @@ class kolab_folders extends rcube_plugin
}
// retrieve list of folders from IMAP server using LSUB
else {
- $a_folders = $imap->conn->listSubscribed($root, $name);
+ $a_folders = $storage->conn->listSubscribed($root, $name);
// unsubscribe non-existent folders, remove from the list
- if (is_array($a_folders) && $name == '*') {
+ if (is_array($a_folders) && $name == '*' && !empty($storage->conn->data['LIST'])) {
foreach ($a_folders as $idx => $folder) {
- if ($imap->conn->data['LIST'] && ($opts = $imap->conn->data['LIST'][$folder])
- && in_array('\\Noselect', $opts)
+ if (!isset($storage->conn->data['LIST'][$folder])
+ || in_array('\\Noselect', $storage->conn->data['LIST'][$folder])
) {
// Some servers returns \Noselect for existing folders
- if (!$imap->mailbox_exists($folder)) {
- $imap->conn->unsubscribe($folder);
+ if (!$storage->folder_exists($folder)) {
+ $storage->conn->unsubscribe($folder);
unset($a_folders[$idx]);
}
}
@@ -445,15 +465,17 @@ class kolab_folders extends rcube_plugin
*/
function get_folder_type_list($mbox, $create_defaults = false)
{
+ $storage = $this->rc->get_storage();
+
// Use mailboxes. prefix so the cache will be cleared by core
// together with other mailboxes-related cache data
$cache_key = 'mailboxes.folder-type.'.$mbox;
// get cached metadata
- $metadata = $this->rc->imap->get_cache($cache_key);
+ $metadata = $storage->get_cache($cache_key);
if (!is_array($metadata)) {
- $metadata = $this->rc->imap->get_metadata($mbox, kolab_folders::CTYPE_KEY);
+ $metadata = $storage->get_metadata($mbox, kolab_folders::CTYPE_KEY);
$need_update = true;
}
@@ -473,7 +495,7 @@ class kolab_folders extends rcube_plugin
// write mailboxlist to cache
if ($need_update) {
- $this->rc->imap->update_cache($cache_key, $metadata);
+ $storage->update_cache($cache_key, $metadata);
}
return $metadata;
@@ -488,6 +510,7 @@ class kolab_folders extends rcube_plugin
*/
function get_default_folder($type)
{
+ $storage = $this->rc->get_storage();
$folderdata = $this->get_folder_type_list('*');
if (!is_array($folderdata)) {
@@ -495,7 +518,7 @@ class kolab_folders extends rcube_plugin
}
$type .= '.default';
- $namespace = $this->rc->imap->get_namespace();
+ $namespace = $storage->get_namespace();
// get all folders of specified type
$folderdata = array_intersect($folderdata, array($type));
@@ -552,7 +575,8 @@ class kolab_folders extends rcube_plugin
*/
private function create_default_folders(&$folderdata, $cache_key = null)
{
- $namespace = $this->rc->imap->get_namespace();
+ $storage = $this->rc->get_storage();
+ $namespace = $storage->get_namespace();
$defaults = array();
$need_update = false;
@@ -573,7 +597,7 @@ class kolab_folders extends rcube_plugin
$opt_name = 'kolab_folders_' . $type . '_' . $subtype;
if ($folder = $this->rc->config->get($opt_name)) {
// convert configuration value to UTF7-IMAP charset
- $folder = rcube_charset_convert($folder, RCMAIL_CHARSET, 'UTF7-IMAP');
+ $folder = rcube_charset::convert($folder, RCMAIL_CHARSET, 'UTF7-IMAP');
// and namespace prefix if needed
if ($prefix && strpos($folder, $prefix) === false && $folder != 'INBOX') {
$folder = $prefix . $folder;
@@ -621,8 +645,8 @@ class kolab_folders extends rcube_plugin
list($type1, $type2) = explode('.', $type);
// create folder
- if ($type1 != 'mail' || !$this->rc->imap->mailbox_exists($foldername)) {
- $this->rc->imap->create_mailbox($foldername, $type1 == 'mail');
+ if ($type1 != 'mail' || !$storage->folder_exists($foldername)) {
+ $storage->create_folder($foldername, $type1 == 'mail');
}
// set type
@@ -637,7 +661,8 @@ class kolab_folders extends rcube_plugin
// update cache
if ($need_update && $cache_key) {
- $this->rc->imap->update_cache($cache_key, $folderdata);
+ $storage->update_cache($cache_key, $folderdata);
}
}
+
}
diff --git a/plugins/kolab_folders/localization/de_CH.inc b/plugins/kolab_folders/localization/de_CH.inc
new file mode 100644
index 0000000..f9f6e16
--- /dev/null
+++ b/plugins/kolab_folders/localization/de_CH.inc
@@ -0,0 +1,24 @@
+<?php
+
+$labels = array();
+
+$labels['folderctype'] = 'Ordnerinhalt';
+$labels['foldertypemail'] = 'E-Mail';
+$labels['foldertypeevent'] = 'Kalender'; // Termine?
+$labels['foldertypejournal'] = 'Journal';
+$labels['foldertypetask'] = 'Aufgaben';
+$labels['foldertypenote'] = 'Notizen';
+$labels['foldertypecontact'] = 'Kontakte';
+$labels['foldertypeconfiguration'] = 'Konfiguration';
+
+$labels['default'] = 'Standard';
+$labels['inbox'] = 'Posteingang';
+$labels['drafts'] = 'Entwürfe';
+$labels['sentitems'] = 'Gesendet';
+$labels['outbox'] = 'Postausgang';
+$labels['wastebasket'] = 'Gelöscht';
+$labels['junkemail'] = 'Spam';
+
+$messages['defaultfolderexists'] = 'Es existiert bereits ein Standardordner für den angegebenen Typ';
+
+?>
diff --git a/plugins/kolab_folders/localization/de_DE.inc b/plugins/kolab_folders/localization/de_DE.inc
new file mode 100644
index 0000000..f9f6e16
--- /dev/null
+++ b/plugins/kolab_folders/localization/de_DE.inc
@@ -0,0 +1,24 @@
+<?php
+
+$labels = array();
+
+$labels['folderctype'] = 'Ordnerinhalt';
+$labels['foldertypemail'] = 'E-Mail';
+$labels['foldertypeevent'] = 'Kalender'; // Termine?
+$labels['foldertypejournal'] = 'Journal';
+$labels['foldertypetask'] = 'Aufgaben';
+$labels['foldertypenote'] = 'Notizen';
+$labels['foldertypecontact'] = 'Kontakte';
+$labels['foldertypeconfiguration'] = 'Konfiguration';
+
+$labels['default'] = 'Standard';
+$labels['inbox'] = 'Posteingang';
+$labels['drafts'] = 'Entwürfe';
+$labels['sentitems'] = 'Gesendet';
+$labels['outbox'] = 'Postausgang';
+$labels['wastebasket'] = 'Gelöscht';
+$labels['junkemail'] = 'Spam';
+
+$messages['defaultfolderexists'] = 'Es existiert bereits ein Standardordner für den angegebenen Typ';
+
+?>
diff --git a/plugins/kolab_folders/package.xml b/plugins/kolab_folders/package.xml
index 54ff20b..b1c364a 100644
--- a/plugins/kolab_folders/package.xml
+++ b/plugins/kolab_folders/package.xml
@@ -4,7 +4,7 @@
http://pear.php.net/dtd/package-2.0
http://pear.php.net/dtd/package-2.0.xsd">
<name>kolab_folders</name>
- <uri>http://kolabsys.com</uri>
+ <uri>http://git.kolab.org/roundcube-plugins-kolab/</uri>
<summary>Type-aware folder management/listing for Kolab</summary>
<description>
The plugin extends folders handling with features of the Kolab Suite
diff --git a/plugins/kolab_zpush/kolab_zpush.php b/plugins/kolab_zpush/kolab_zpush.php
index c7469f1..f19fe3a 100644
--- a/plugins/kolab_zpush/kolab_zpush.php
+++ b/plugins/kolab_zpush/kolab_zpush.php
@@ -6,7 +6,7 @@
* @version 0.2
* @author Thomas Bruederli <bruederli@kolabsys.com>
*
- * Copyright (C) 2011, Kolab Systems AG <contact@kolabsys.com>
+ * Copyright (C) 2012, Kolab Systems AG <contact@kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
@@ -64,11 +64,12 @@ class kolab_zpush extends rcube_plugin
*/
public function init_imap()
{
- $this->rc->imap_connect();
+ $storage = $this->rc->get_storage();
+
$this->cache = $this->rc->get_cache('zpush', 'db', 900);
$this->cache->expunge();
- if ($meta = $this->rc->imap->get_metadata(self::ROOT_MAILBOX, self::ACTIVESYNC_KEY)) {
+ if ($meta = $storage->get_metadata(self::ROOT_MAILBOX, self::ACTIVESYNC_KEY)) {
// clear cache if device config changed
if (($oldmeta = $this->cache->read('devicemeta')) && $oldmeta != $meta)
$this->cache->remove();
@@ -85,13 +86,13 @@ class kolab_zpush extends rcube_plugin
*/
public function json_command()
{
- $cmd = get_input_value('cmd', RCUBE_INPUT_GPC);
- $imei = get_input_value('id', RCUBE_INPUT_GPC);
+ $storage = $this->rc->get_storage();
+ $cmd = get_input_value('cmd', RCUBE_INPUT_GPC);
+ $imei = get_input_value('id', RCUBE_INPUT_GPC);
switch ($cmd) {
case 'load':
$result = array();
- $this->init_imap();
$devices = $this->list_devices();
if ($device = $devices[$imei]) {
$result['id'] = $imei;
@@ -113,7 +114,6 @@ class kolab_zpush extends rcube_plugin
break;
case 'save':
- $this->init_imap();
$devices = $this->list_devices();
$syncmode = intval(get_input_value('syncmode', RCUBE_INPUT_POST));
$devicealias = get_input_value('devicealias', RCUBE_INPUT_POST, true);
@@ -132,13 +132,13 @@ class kolab_zpush extends rcube_plugin
$this->root_meta['DEVICE'][$imei]['LAXPIC'] = $laxpic;
$this->root_meta['FOLDER'][$imei]['S'] = intval($subsciptions[self::ROOT_MAILBOX]);
- $err = !$this->rc->imap->set_metadata(self::ROOT_MAILBOX,
+ $err = !$storage->set_metadata(self::ROOT_MAILBOX,
array(self::ACTIVESYNC_KEY => $this->serialize_metadata($this->root_meta)));
// update cached meta data
if (!$err) {
$this->cache->remove('devicemeta');
- $this->cache->write('devicemeta', $this->rc->imap->get_metadata(self::ROOT_MAILBOX, self::ACTIVESYNC_KEY));
+ $this->cache->write('devicemeta', $storage->get_metadata(self::ROOT_MAILBOX, self::ACTIVESYNC_KEY));
}
}
// iterate over folders list and update metadata if necessary
@@ -153,12 +153,12 @@ class kolab_zpush extends rcube_plugin
unset($meta['TYPE']);
// read metadata first
- $folderdata = $this->rc->imap->get_metadata($folder, array(self::ACTIVESYNC_KEY));
+ $folderdata = $storage->get_metadata($folder, array(self::ACTIVESYNC_KEY));
if ($asyncdata = $folderdata[$folder][self::ACTIVESYNC_KEY])
$metadata = $this->unserialize_metadata($asyncdata);
$metadata['FOLDER'] = $meta;
- $err |= !$this->rc->imap->set_metadata($folder, array(self::ACTIVESYNC_KEY => $this->serialize_metadata($metadata)));
+ $err |= !$storage->set_metadata($folder, array(self::ACTIVESYNC_KEY => $this->serialize_metadata($metadata)));
}
}
@@ -184,9 +184,9 @@ class kolab_zpush extends rcube_plugin
unset($this->root_meta['DEVICE'][$imei], $this->root_meta['FOLDER'][$imei]);
// update annotation and cached meta data
- if ($success = $this->rc->imap->set_metadata(self::ROOT_MAILBOX, array(self::ACTIVESYNC_KEY => $this->serialize_metadata($this->root_meta)))) {
+ if ($success = $storage->set_metadata(self::ROOT_MAILBOX, array(self::ACTIVESYNC_KEY => $this->serialize_metadata($this->root_meta)))) {
$this->cache->remove('devicemeta');
- $this->cache->write('devicemeta', $this->rc->imap->get_metadata(self::ROOT_MAILBOX, self::ACTIVESYNC_KEY));
+ $this->cache->write('devicemeta', $storage->get_metadata(self::ROOT_MAILBOX, self::ACTIVESYNC_KEY));
// remove device annotation in every folder
foreach ($this->folders_meta() as $folder => $meta) {
@@ -199,12 +199,12 @@ class kolab_zpush extends rcube_plugin
unset($meta[$imei], $meta['TYPE']);
// read metadata first and update FOLDER property
- $folderdata = $this->rc->imap->get_metadata($folder, array(self::ACTIVESYNC_KEY));
+ $folderdata = $storage->get_metadata($folder, array(self::ACTIVESYNC_KEY));
if ($asyncdata = $folderdata[$folder][self::ACTIVESYNC_KEY])
$metadata = $this->unserialize_metadata($asyncdata);
$metadata['FOLDER'] = $meta;
- if ($this->rc->imap->set_metadata($folder, array(self::ACTIVESYNC_KEY => $this->serialize_metadata($metadata)))) {
+ if ($storage->set_metadata($folder, array(self::ACTIVESYNC_KEY => $this->serialize_metadata($metadata)))) {
$this->folders_meta[$folder] = $metadata;
$this->folders_meta[$folder]['TYPE'] = $type;
}
@@ -236,12 +236,12 @@ class kolab_zpush extends rcube_plugin
*/
public function config_view()
{
- require_once($this->home . '/kolab_zpush_ui.php');
+ require_once $this->home . '/kolab_zpush_ui.php';
- $this->init_imap();
+ $storage = $this->rc->get_storage();
// checks if IMAP server supports any of METADATA, ANNOTATEMORE, ANNOTATEMORE2
- if (!($this->rc->imap->get_capability('METADATA') || $this->rc->imap->get_capability('ANNOTATEMORE') || $this->rc->imap->get_capability('ANNOTATEMORE2'))) {
+ if (!($storage->get_capability('METADATA') || $storage->get_capability('ANNOTATEMORE') || $storage->get_capability('ANNOTATEMORE2'))) {
$this->rc->output->show_message($this->gettext('notsupported'), 'error');
}
@@ -286,9 +286,11 @@ class kolab_zpush extends rcube_plugin
}
// fetch folder data from server
else {
- $this->folders = $this->rc->imap->list_unsubscribed();
+ $storage = $this->rc->get_storage();
+ $this->folders = $storage->list_folders();
+
foreach ($this->folders as $folder) {
- $folderdata = $this->rc->imap->get_metadata($folder, array(self::ACTIVESYNC_KEY, self::CTYPE_KEY));
+ $folderdata = $storage->get_metadata($folder, array(self::ACTIVESYNC_KEY, self::CTYPE_KEY));
$foldertype = explode('.', $folderdata[$folder][self::CTYPE_KEY]);
if ($asyncdata = $folderdata[$folder][self::ACTIVESYNC_KEY]) {
diff --git a/plugins/kolab_zpush/localization/de_CH.inc b/plugins/kolab_zpush/localization/de_CH.inc
index 2f92044..f0abdbf 100644
--- a/plugins/kolab_zpush/localization/de_CH.inc
+++ b/plugins/kolab_zpush/localization/de_CH.inc
@@ -6,21 +6,21 @@ $labels['devices'] = 'Geräte';
$labels['devicealias'] = 'Gerätename';
$labels['syncmode'] = 'Modus';
$labels['modeauto'] = 'Automatisch';
-$labels['modeflat'] = 'Forciere flache Struktur';
-$labels['modefolder'] = 'Forciere Ordnerstruktur';
+$labels['modeflat'] = 'Flache Struktur erzwingen';
+$labels['modefolder'] = 'Ordnerstruktur erzwingen';
$labels['synchronize'] = 'Synchronisieren';
$labels['withalarms'] = 'Mit Erinnerungen';
$labels['syncsettings'] = 'Synchronisationseinstellungen';
$labels['deviceconfig'] = 'Gerätekonfiguration';
$labels['folderstosync'] = 'Order zum Synchronisieren';
$labels['mail'] = 'E-Mail';
-$labels['contact'] = 'Adressbücher';
-$labels['event'] = 'Kalendar';
+$labels['contact'] = 'Kontakte';
+$labels['event'] = 'Kalender';
$labels['task'] = 'Aufgaben';
$labels['note'] = 'Notizen';
$labels['deletedevice'] = 'Gerät löschen';
$labels['imageformat'] = 'Bildformat';
-$labels['laxpiclabel'] = 'Erlaube PNG- und GIF-Bilder';
+$labels['laxpiclabel'] = 'PNG- und GIF-Bilder erlauben';
$labels['introtext'] = 'Wählen Sie das zu konfigurierende Gerät aus';
$labels['nodevices'] = 'Es sind noch keine Geräte registriert.<br/><br/>Um ein neues Gerät anzumelden, verbinden Sie dieses zuerst mit dem Server. Eine Anleitung dazu finden Sie im <a href="http://wiki.kolab.org/Z_push#Clients">Wiki</a>. Anschliessend laden Sie diese Seite neu und das Gerät wird hier aufgelistet.';
$labels['savingdata'] = 'Daten werden gespeichert...';
@@ -29,4 +29,4 @@ $labels['notsupported'] = 'Ihr Server unterstützt keine Activesync-Konfiguratio
$labels['devicedeleteconfirm'] = 'Wollen Sie wirklich alle Einstellungen für dieses Gerät löschen?';
$labels['successfullydeleted'] = 'Die Geräteinstellungen wurden erfolgreich gelöscht';
-?> \ No newline at end of file
+?>
diff --git a/plugins/kolab_zpush/localization/de_DE.inc b/plugins/kolab_zpush/localization/de_DE.inc
new file mode 100644
index 0000000..f0abdbf
--- /dev/null
+++ b/plugins/kolab_zpush/localization/de_DE.inc
@@ -0,0 +1,32 @@
+<?php
+
+$labels = array();
+$labels['tabtitle'] = 'Activesync';
+$labels['devices'] = 'Geräte';
+$labels['devicealias'] = 'Gerätename';
+$labels['syncmode'] = 'Modus';
+$labels['modeauto'] = 'Automatisch';
+$labels['modeflat'] = 'Flache Struktur erzwingen';
+$labels['modefolder'] = 'Ordnerstruktur erzwingen';
+$labels['synchronize'] = 'Synchronisieren';
+$labels['withalarms'] = 'Mit Erinnerungen';
+$labels['syncsettings'] = 'Synchronisationseinstellungen';
+$labels['deviceconfig'] = 'Gerätekonfiguration';
+$labels['folderstosync'] = 'Order zum Synchronisieren';
+$labels['mail'] = 'E-Mail';
+$labels['contact'] = 'Kontakte';
+$labels['event'] = 'Kalender';
+$labels['task'] = 'Aufgaben';
+$labels['note'] = 'Notizen';
+$labels['deletedevice'] = 'Gerät löschen';
+$labels['imageformat'] = 'Bildformat';
+$labels['laxpiclabel'] = 'PNG- und GIF-Bilder erlauben';
+$labels['introtext'] = 'Wählen Sie das zu konfigurierende Gerät aus';
+$labels['nodevices'] = 'Es sind noch keine Geräte registriert.<br/><br/>Um ein neues Gerät anzumelden, verbinden Sie dieses zuerst mit dem Server. Eine Anleitung dazu finden Sie im <a href="http://wiki.kolab.org/Z_push#Clients">Wiki</a>. Anschliessend laden Sie diese Seite neu und das Gerät wird hier aufgelistet.';
+$labels['savingdata'] = 'Daten werden gespeichert...';
+$labels['savingerror'] = 'Fehler beim Speichern';
+$labels['notsupported'] = 'Ihr Server unterstützt keine Activesync-Konfiguration';
+$labels['devicedeleteconfirm'] = 'Wollen Sie wirklich alle Einstellungen für dieses Gerät löschen?';
+$labels['successfullydeleted'] = 'Die Geräteinstellungen wurden erfolgreich gelöscht';
+
+?>
diff --git a/plugins/kolab_zpush/localization/en_US.inc b/plugins/kolab_zpush/localization/en_US.inc
index 5160d91..2537d4d 100644
--- a/plugins/kolab_zpush/localization/en_US.inc
+++ b/plugins/kolab_zpush/localization/en_US.inc
@@ -18,6 +18,7 @@ $labels['contact'] = 'Address Books';
$labels['event'] = 'Calendars';
$labels['task'] = 'Tasks';
$labels['note'] = 'Notes';
+$labels['configuration'] = 'Configuration';
$labels['deletedevice'] = 'Delete device';
$labels['imageformat'] = 'Image format';
$labels['laxpiclabel'] = 'Allow PNG and GIF images';
diff --git a/plugins/kolab_zpush/package.xml b/plugins/kolab_zpush/package.xml
index 6313d1e..250d1a6 100644
--- a/plugins/kolab_zpush/package.xml
+++ b/plugins/kolab_zpush/package.xml
@@ -4,7 +4,7 @@
http://pear.php.net/dtd/package-2.0
http://pear.php.net/dtd/package-2.0.xsd">
<name>kolab_zpush</name>
- <uri>http://kolabsys.com</uri>
+ <uri>http://git.kolab.org/roundcube-plugins-kolab/</uri>
<summary>Z-Push configuration utility for Kolab accounts</summary>
<description></description>
<lead>
@@ -16,7 +16,7 @@
<date>2011-11-14</date>
<time>12:12:00</time>
<version>
- <release>0.2</release>
+ <release>0.3</release>
</version>
<stability>
<release>stable</release>
@@ -38,6 +38,13 @@
<file name="skins/default/foldertypes.png" role="data"></file>
<file name="skins/default/pointer-left.gif" role="data"></file>
<file name="skins/default/synchronize.png" role="data"></file>
+ <file name="skins/larry/templates/config.html" role="data"></file>
+ <file name="skins/larry/config.css" role="data"></file>
+ <file name="skins/larry/alarm-clock.png" role="data"></file>
+ <file name="skins/larry/deviceactions.png" role="data"></file>
+ <file name="skins/larry/foldertypes.png" role="data"></file>
+ <file name="skins/larry/pointer-left.png" role="data"></file>
+ <file name="skins/larry/synchronize.png" role="data"></file>
<file name="README" role="data"></file>
<file name="LICENSE" role="data"></file>
</dir>
diff --git a/plugins/kolab_zpush/skins/larry/alarm-clock.png b/plugins/kolab_zpush/skins/larry/alarm-clock.png
new file mode 100755
index 0000000..518bdc1
--- /dev/null
+++ b/plugins/kolab_zpush/skins/larry/alarm-clock.png
Binary files differ
diff --git a/plugins/kolab_zpush/skins/larry/config.css b/plugins/kolab_zpush/skins/larry/config.css
new file mode 100644
index 0000000..40d5b87
--- /dev/null
+++ b/plugins/kolab_zpush/skins/larry/config.css
@@ -0,0 +1,135 @@
+/* Stylesheets for the Kolab Z-Push configuration UI */
+
+#configform {
+ padding-top: 15px;
+}
+
+#sectionslist {
+ width: 220px;
+}
+
+#prefs-box {
+ position: absolute;
+ top: 0;
+ left: 232px;
+ right: 0;
+ bottom: 0;
+}
+
+#devices-table {
+ width: 100%;
+ table-layout: fixed;
+}
+
+#devices-table td {
+ cursor: pointer;
+}
+
+#devices-table td span.devicetype {
+ padding-left: 1em;
+ font-style: italic;
+ color: #69939e;
+}
+
+div.subscriptionblock {
+ float: left;
+ margin: 0 3em 2em 0;
+ padding: 0;
+}
+
+div.subscriptionblock h3 {
+ font-size: 14px;
+ color: #333;
+ margin: 0 0 0.6em 0;
+ padding: 2px 4px 2px 30px;
+ background: url(foldertypes.png) 4px 2px no-repeat;
+}
+
+div.subscriptionblock h3.contact {
+ background-position: 4px -18px;
+}
+
+div.subscriptionblock h3.event {
+ background-position: 4px -38px;
+}
+
+div.subscriptionblock h3.task {
+ background-position: 4px -58x;
+}
+
+div.subscriptionblock h3.note {
+ background-position: 4px -78px;
+}
+
+#foldersubscriptions thead td {
+ color: #69939e;
+ font-weight: bold;
+ padding: 3px 5px;
+ min-width: 2em;
+ background: #d6eaf3;
+ border-bottom: 2px solid #fff;
+}
+
+#foldersubscriptions tbody td {
+ background: #eee;
+ padding: 2px 5px;
+ border-bottom: 2px solid #fff;
+}
+
+#foldersubscriptions td label {
+ display: block;
+}
+
+#foldersubscriptions td.mailbox {
+ padding-right: 3em;
+ padding-left: 2px;
+ min-width: 12em;
+}
+
+#foldersubscriptions td.virtual {
+ color: #999;
+}
+
+#foldersubscriptions {
+ overflow: auto;
+ max-height: 400px;
+ margin-top: 0.5em;
+}
+
+#introtext {
+ position: absolute;
+ top: 16px;
+ left: 5px;
+ padding-left: 10px;
+ max-width: 40em;
+}
+
+#introtext .pointer-left {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 12px;
+ height: 80%;
+ background: url(pointer-left.png) right center no-repeat;
+}
+
+#introtext .inner {
+ color: #eee;
+ padding: 12px;
+ border: 1px solid #333;
+ border-radius: 4px;
+ text-shadow: 0px 1px 1px #333;
+ background: #5b5b5b;
+ background: -moz-linear-gradient(top, #5b5b5b 0%, #3a3a3a 100%);
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#5b5b5b), color-stop(100%,#3a3a3a));
+ background: -o-linear-gradient(top, #5b5b5b 0%, #3a3a3a 100%);
+ background: -ms-linear-gradient(top, #5b5b5b 0%, #3a3a3a 100%);
+ background: linear-gradient(top, #5b5b5b 0%, #3a3a3a 100%);
+ -webkit-box-shadow: inset 0px 0px 0px 1px #7e7e7e, 0 2px 6px 0 #333;
+ -moz-box-shadow: inset 0px 0px 0px 1px #7e7e7e, 0 2px 6px 0 #333;
+ box-shadow: inset 0px 0px 0px 1px #7e7e7e, 0 2px 6px 0 #333;
+}
+
+#introtext a {
+ color: #b0ccd7;
+}
diff --git a/plugins/kolab_zpush/skins/larry/foldertypes.png b/plugins/kolab_zpush/skins/larry/foldertypes.png
new file mode 100644
index 0000000..4950296
--- /dev/null
+++ b/plugins/kolab_zpush/skins/larry/foldertypes.png
Binary files differ
diff --git a/plugins/kolab_zpush/skins/larry/pointer-left.png b/plugins/kolab_zpush/skins/larry/pointer-left.png
new file mode 100644
index 0000000..bfa7e86
--- /dev/null
+++ b/plugins/kolab_zpush/skins/larry/pointer-left.png
Binary files differ
diff --git a/plugins/kolab_zpush/skins/larry/synchronize.png b/plugins/kolab_zpush/skins/larry/synchronize.png
new file mode 100755
index 0000000..ba5ebd1
--- /dev/null
+++ b/plugins/kolab_zpush/skins/larry/synchronize.png
Binary files differ
diff --git a/plugins/kolab_zpush/skins/larry/templates/config.html b/plugins/kolab_zpush/skins/larry/templates/config.html
new file mode 100644
index 0000000..9671bc6
--- /dev/null
+++ b/plugins/kolab_zpush/skins/larry/templates/config.html
@@ -0,0 +1,71 @@
+<roundcube:object name="doctype" value="html5" />
+<html>
+<head>
+<title><roundcube:object name="pagetitle" /></title>
+<roundcube:include file="/includes/links.html" />
+</head>
+<body>
+
+<roundcube:include file="/includes/header.html" />
+
+<div id="mainscreen" class="offset">
+
+<roundcube:include file="/includes/settingstabs.html" />
+
+<div id="settings-right">
+
+<div id="sectionslist" class="uibox listbox">
+<h2 id="directorylist-title" class="boxtitle"><roundcube:label name="kolab_zpush.devices" /></h2>
+<div class="boxlistcontent">
+ <roundcube:object name="plugin.devicelist" id="devices-table" class="listing" cellspacing="0" />
+</div>
+<div class="boxfooter">
+ <roundcube:button type="link" command="plugin.delete-device" title="kolab_zpush.deletedevice" class="listbutton delete disabled" classAct="listbutton delete" innerClass="inner" content="x" />
+</div>
+</div>
+
+<div id="prefs-box" class="uibox contentbox">
+ <h1 class="boxtitle" style="display:none"><roundcube:label name="kolab_zpush.syncsettings"></h1>
+ <form action="#" method="post" id="configform" class="boxcontent tabbed" style="display:none">
+ <fieldset>
+ <legend><roundcube:label name="kolab_zpush.folderstosync" /></legend>
+ <roundcube:object name="plugin.foldersubscriptions" form="configform" id="foldersubscriptions" syncicon="synchronize.png" alarmicon="alarm-clock.png" />
+ </fieldset>
+ <fieldset>
+ <legend><roundcube:label name="kolab_zpush.deviceconfig" /></legend>
+ <roundcube:object name="plugin.deviceconfigform" form="configform" class="propform" />
+ </fieldset>
+
+ <p class="formbuttons">
+ <roundcube:button type="input" class="button mainaction" command="plugin.save-config" label="save" />
+ <roundcube:button type="input" class="button" command="plugin.delete-device" label="kolab_zpush.deletedevice" />
+ </p>
+ </form>
+
+ <div id="introtext">
+ <div class="inner">
+ <roundcube:if condition="env:devicecount" />
+ <roundcube:label name="kolab_zpush.introtext" />
+ <roundcube:else />
+ <roundcube:label name="kolab_zpush.nodevices" html="true" />
+ <roundcube:endif />
+ </div>
+ <div class="pointer-left"></div>
+ </div>
+
+ <roundcube:object name="message" id="message" class="statusbar" />
+</div>
+
+</div>
+
+<roundcube:include file="/includes/footer.html" />
+
+<script type="text/javascript">
+
+ var viewsplit = new rcube_splitter({ id:'devicelistsplitter', p1:'#sectionslist', p2:'#prefs-box', orientation:'v', relative:true, start:226, min:150, size:12 });
+ rcmail.add_onload('viewsplit.init()');
+
+</script>
+
+</body>
+</html>
diff --git a/plugins/owncloud/LICENSE b/plugins/owncloud/LICENSE
new file mode 100644
index 0000000..dba13ed
--- /dev/null
+++ b/plugins/owncloud/LICENSE
@@ -0,0 +1,661 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<http://www.gnu.org/licenses/>.
diff --git a/plugins/owncloud/config.inc.php.dist b/plugins/owncloud/config.inc.php.dist
new file mode 100644
index 0000000..c9e9d23
--- /dev/null
+++ b/plugins/owncloud/config.inc.php.dist
@@ -0,0 +1,4 @@
+<?php
+
+// ownCloud URL
+$rcmail_config['owncloud_url'] = 'https://owncloud.webmail.tld';
diff --git a/plugins/owncloud/localization/en_US.inc b/plugins/owncloud/localization/en_US.inc
new file mode 100644
index 0000000..f5cb8fb
--- /dev/null
+++ b/plugins/owncloud/localization/en_US.inc
@@ -0,0 +1,6 @@
+<?php
+
+$labels = array();
+$labels['owncloud'] = 'Files';
+
+?>
diff --git a/plugins/owncloud/localization/pl_PL.inc b/plugins/owncloud/localization/pl_PL.inc
new file mode 100644
index 0000000..4b84bd4
--- /dev/null
+++ b/plugins/owncloud/localization/pl_PL.inc
@@ -0,0 +1,6 @@
+<?php
+
+$labels = array();
+$labels['owncloud'] = 'Pliki';
+
+?>
diff --git a/plugins/owncloud/owncloud.php b/plugins/owncloud/owncloud.php
new file mode 100644
index 0000000..d847cc3
--- /dev/null
+++ b/plugins/owncloud/owncloud.php
@@ -0,0 +1,84 @@
+<?php
+
+/**
+ * OwnCloud Plugin
+ *
+ * @author Aleksander 'A.L.E.C' Machniak <machniak@kolabsys.com>
+ * @licence GNU AGPL
+ *
+ * Configuration (see config.inc.php.dist)
+ *
+ */
+
+class owncloud extends rcube_plugin
+{
+ // all task excluding 'login' and 'logout'
+ public $task = '?(?!login|logout).*';
+ // we've got no ajax handlers
+ public $noajax = true;
+ // skip frames
+ public $noframe = true;
+
+ function init()
+ {
+ $rcmail = rcmail::get_instance();
+
+ // requires kolab_auth plugin
+ if (empty($_SESSION['kolab_uid'])) {
+ return;
+ }
+
+ $this->add_texts('localization/', false);
+
+ // register task
+ $this->register_task('owncloud');
+
+ // register actions
+ $this->register_action('index', array($this, 'action'));
+ $this->register_action('redirect', array($this, 'redirect'));
+
+ // add taskbar button
+ $this->add_button(array(
+ 'command' => 'owncloud',
+ 'class' => 'button-owncloud',
+ 'classsel' => 'button-owncloud button-selected',
+ 'innerclass' => 'button-inner',
+ 'label' => 'owncloud.owncloud',
+ ), 'taskbar');
+
+ $skin = $rcmail->config->get('skin');
+ if (!file_exists($this->home."/skins/$skin/owncloud.css")) {
+ $skin = 'default';
+ }
+
+ // add style for taskbar button (must be here) and Help UI
+ $this->include_stylesheet("skins/$skin/owncloud.css");
+ }
+
+ function action()
+ {
+ $rcmail = rcmail::get_instance();
+
+ $rcmail->output->add_handlers(array('owncloudframe' => array($this, 'frame')));
+ $rcmail->output->set_pagetitle($this->gettext('owncloud'));
+ $rcmail->output->send('owncloud.owncloud');
+ }
+
+ function frame()
+ {
+ $rcmail = rcmail::get_instance();
+
+ $this->load_config();
+
+ $src = $rcmail->config->get('owncloud_url');
+ $user = $_SESSION['kolab_uid']; // requires kolab_auth plugin
+ $pass = $rcmail->decrypt($_SESSION['password']);
+
+ $src = preg_replace('/^(https?:\/\/)/',
+ '\\1' . urlencode($user) . ':' . urlencode($pass) . '@', $src);
+
+ return '<iframe id="owncloudframe" width="100%" height="100%" frameborder="0"'
+ .' src="' . $src. '"></iframe>';
+ }
+
+}
diff --git a/plugins/owncloud/skins/default/cloud.png b/plugins/owncloud/skins/default/cloud.png
new file mode 100644
index 0000000..15a288f
--- /dev/null
+++ b/plugins/owncloud/skins/default/cloud.png
Binary files differ
diff --git a/plugins/owncloud/skins/default/owncloud.css b/plugins/owncloud/skins/default/owncloud.css
new file mode 100644
index 0000000..920b730
--- /dev/null
+++ b/plugins/owncloud/skins/default/owncloud.css
@@ -0,0 +1,12 @@
+/***** ownCloud plugin styles *****/
+
+#taskbar a.button-owncloud
+{
+ background-image: url(cloud.png);
+}
+
+.owncloud-box
+{
+ overflow: auto;
+ background-color: #F2F2F2;
+}
diff --git a/plugins/owncloud/skins/default/templates/owncloud.html b/plugins/owncloud/skins/default/templates/owncloud.html
new file mode 100644
index 0000000..efbd362
--- /dev/null
+++ b/plugins/owncloud/skins/default/templates/owncloud.html
@@ -0,0 +1,19 @@
+<!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" />
+<link rel="stylesheet" type="text/css" href="/this/owncloud.css" />
+<link rel="stylesheet" type="text/css" href="/settings.css" />
+</head>
+<body>
+
+<roundcube:include file="/includes/taskbar.html" />
+<roundcube:include file="/includes/header.html" />
+
+<div id="mainscreen" class="box owncloud-box">
+ <roundcube:object name="owncloudframe" />
+</div>
+
+</body>
+</html>
diff --git a/plugins/owncloud/skins/larry/cloud.png b/plugins/owncloud/skins/larry/cloud.png
new file mode 100644
index 0000000..7ad3cd9
--- /dev/null
+++ b/plugins/owncloud/skins/larry/cloud.png
Binary files differ
diff --git a/plugins/owncloud/skins/larry/owncloud.css b/plugins/owncloud/skins/larry/owncloud.css
new file mode 100644
index 0000000..1e9aa89
--- /dev/null
+++ b/plugins/owncloud/skins/larry/owncloud.css
@@ -0,0 +1,14 @@
+/***** ownCloud plugin styles *****/
+
+#taskbar a.button-owncloud span.button-inner
+{
+ background: url(cloud.png) 5px 5px no-repeat;
+ height: 14px;
+}
+
+#taskbar a.button-owncloud:hover span.button-inner,
+#taskbar a.button-owncloud.button-selected span.button-inner
+{
+ background: url(cloud.png) 5px -16px no-repeat;
+ height: 14px;
+}
diff --git a/plugins/owncloud/skins/larry/templates/owncloud.html b/plugins/owncloud/skins/larry/templates/owncloud.html
new file mode 100644
index 0000000..8b3f470
--- /dev/null
+++ b/plugins/owncloud/skins/larry/templates/owncloud.html
@@ -0,0 +1,20 @@
+<roundcube:object name="doctype" value="html5" />
+<html>
+<head>
+<title><roundcube:object name="pagetitle" /></title>
+<roundcube:include file="/includes/links.html" />
+<link rel="stylesheet" type="text/css" href="/this/owncloud.css" />
+<link rel="stylesheet" type="text/css" href="/settings.css" />
+</head>
+<body class="noscroll">
+
+<roundcube:include file="/includes/header.html" />
+
+<div id="mainscreen" class="offset uibox" style="overflow: hidden">
+ <roundcube:object name="owncloudframe" />
+</div>
+
+<roundcube:include file="/includes/footer.html" />
+
+</body>
+</html>