Commit e03fea06 authored by Daniel Morlock's avatar Daniel Morlock
Browse files

Merge tag 'roundcubemail-plugins-kolab-3.1.15' into feature_caldav

Release 3.1.15

Conflicts:
	plugins/calendar/calendar.php
	plugins/calendar/skins/larry/templates/calendar.html
parents b1b033d8 d0f57081
This diff is collapsed.
......@@ -1630,6 +1630,17 @@ function rcube_calendar_ui(settings)
event.temp = true;
event.className = 'fc-event-cal-'+data.calendar+' fc-event-temp';
fc.fullCalendar(data.id ? 'updateEvent' : 'renderEvent', event);
// mark all recurring instances as temp
if (event.recurrence || event.recurrence_id) {
var base_id = event.recurrence_id ? event.recurrence_id.replace(/-\d+$/, '') : event.id;
$.each(fc.fullCalendar('clientEvents', function(e){ return e.id == base_id || e.recurrence_id == base_id; }), function(i,ev) {
ev.temp = true;
ev.editable = false;
event.className += ' fc-event-temp';
fc.fullCalendar('updateEvent', ev);
});
}
}
};
......@@ -2714,8 +2725,8 @@ function rcube_calendar_ui(settings)
minical = $('#datepicker').datepicker($.extend(datepicker_settings, {
inline: true,
showWeek: true,
changeMonth: false, // maybe enable?
changeYear: false, // maybe enable?
changeMonth: true,
changeYear: true,
onSelect: function(dateText, inst) {
ignore_click = true;
var d = minical.datepicker('getDate'); //parse_datetime('0:0', dateText);
......@@ -2740,12 +2751,16 @@ function rcube_calendar_ui(settings)
base_date.setYear(minical.data('year'));
base_date.setHours(12);
base_date.setDate(base_date.getDate() - ((base_date.getDay() + 6) % 7) + datepicker_settings.firstDay);
var day_off = base_date.getDay() - datepicker_settings.firstDay;
var base_kw = iso8601Week(base_date);
var target_kw = parseInt(cell.html());
var diff = (target_kw - base_kw) * 7 * DAY_MS;
var base_kw = iso8601Week(base_date),
target_kw = parseInt(cell.html()),
wdiff = target_kw - base_kw;
if (wdiff > 10) // year jump
base_date.setYear(base_date.getFullYear() - 1);
else if (wdiff < -10)
base_date.setYear(base_date.getFullYear() + 1);
// select monday of the chosen calendar week
var date = new Date(base_date.getTime() - day_off * DAY_MS + diff);
var day_off = base_date.getDay() - datepicker_settings.firstDay,
date = new Date(base_date.getTime() - day_off * DAY_MS + wdiff * 7 * DAY_MS);
fc.fullCalendar('gotoDate', date).fullCalendar('setDate', date).fullCalendar('changeView', 'agendaWeek');
minical.datepicker('setDate', date);
}
......@@ -2765,6 +2780,8 @@ function rcube_calendar_ui(settings)
$('#edit-attendees-form .attendees-invitebox').show();
}
}
// reset autocompletion on tab change (#3389)
rcmail.ksearch_blur();
}
});
$('#edit-enddate').datepicker(datepicker_settings);
......
......@@ -54,7 +54,18 @@
* 'free_busy' => 'free|busy|outofoffice|tentative', // Show time as
* 'priority' => 0-9, // Event priority (0=undefined, 1=highest, 9=lowest)
* 'sensitivity' => 'public|private|confidential', // Event sensitivity
* 'alarms' => '-15M:DISPLAY', // Reminder settings inspired by valarm definition (e.g. display alert 15 minutes before event)
* 'alarms' => '-15M:DISPLAY', // DEPRECATED Reminder settings inspired by valarm definition (e.g. display alert 15 minutes before event)
* 'valarms' => array( // List of reminders (new format), each represented as a hash array:
* array(
* 'trigger' => '-PT90M', // ISO 8601 period string prefixed with '+' or '-', or DateTime object
* 'action' => 'DISPLAY|EMAIL|AUDIO',
* 'duration' => 'PT15M', // ISO 8601 period string
* 'repeat' => 0, // number of repetitions
* 'description' => '', // text to display for DISPLAY actions
* 'summary' => '', // message text for EMAIL actions
* 'attendees' => array(), // list of email addresses to receive alarm messages
* ),
* ),
* 'attachments' => array( // List of attachments
* 'name' => 'File name',
* 'mimetype' => 'Content type',
......
......@@ -61,7 +61,7 @@ CREATE TABLE IF NOT EXISTS `attachments` (
`filename` varchar(255) NOT NULL DEFAULT '',
`mimetype` varchar(255) NOT NULL DEFAULT '',
`size` int(11) NOT NULL DEFAULT '0',
`data` longtext NOT NULL DEFAULT '',
`data` longtext NOT NULL,
PRIMARY KEY(`attachment_id`),
CONSTRAINT `fk_attachments_event_id` FOREIGN KEY (`event_id`)
REFERENCES `events`(`event_id`) ON DELETE CASCADE ON UPDATE CASCADE
......
......@@ -11,7 +11,7 @@ CREATE TABLE IF NOT EXISTS `kolab_alarms` (
`user_id` int(10) UNSIGNED NOT NULL,
`notifyat` DATETIME DEFAULT NULL,
`dismissed` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0',
PRIMARY KEY(`event_id`),
PRIMARY KEY(`event_id`,`user_id`),
CONSTRAINT `fk_kolab_alarms_user_id` FOREIGN KEY (`user_id`)
REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE
) /*!40000 ENGINE=INNODB */;
......
ALTER TABLE `kolab_alarms` DROP PRIMARY KEY;
ALTER TABLE `kolab_alarms` ADD PRIMARY KEY (`alarm_id`, `user_id`);
......@@ -33,7 +33,7 @@ class kolab_driver extends calendar_driver
public $freebusy = true;
public $attachments = true;
public $undelete = true;
public $alarm_types = array('DISPLAY');
public $alarm_types = array('DISPLAY','AUDIO');
public $categoriesimmutable = true;
private $rc;
......@@ -558,7 +558,7 @@ class kolab_driver extends calendar_driver
return false;
if ($event['_savemode'] != 'new') {
if (!$fromcalendar->storage->move($event['id'], $storage->get_realname()))
if (!$fromcalendar->storage->move($event['id'], $storage->storage))
return false;
$fromcalendar = $storage;
......
......@@ -1431,9 +1431,9 @@ function parseISO8601(s, ignoreTimezone) { // ignoreTimezone defaults to false
if (!m) {
return null;
}
var date = new Date(m[1], 0, 1);
var date = new Date(m[1], 0, 2);
if (ignoreTimezone || !m[13]) {
var check = new Date(m[1], 0, 1, 9, 0);
var check = new Date(m[1], 0, 2, 9, 0);
if (m[3]) {
date.setMonth(m[3] - 1);
check.setMonth(m[3] - 1);
......
......@@ -460,6 +460,12 @@ a.miniColors-trigger {
margin-bottom: 0.3em;
}
#eventshow #event-url .event-text {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
#eventedit {
position: relative;
padding: 0.5em 0.1em;
......
......@@ -18,6 +18,22 @@ body.calendarmain #mainscreen {
left: 0;
}
/* overrides for tablets and mobile phones */
@media screen and (max-device-width: 1024px){
body.calendarmain {
overflow: visible;
}
body.calendarmain #mainscreen {
min-width: 1000px !important;
min-height: 520px !important;
}
body.calendarmain #header {
min-width: 1020px !important;
}
}
body.attachmentwin #mainscreen {
top: 60px;
}
......@@ -117,7 +133,6 @@ div.sidebarclosed {
left: 266px;
right: 0;
bottom: 0;
padding-bottom: 28px;
}
.calendarmain #message.statusbar {
......@@ -125,10 +140,10 @@ div.sidebarclosed {
border-bottom-color: #ababab;
}
#calendar .timezonedisplay {
#timezonedisplay {
position: absolute;
bottom: 9px;
right: 8px;
bottom: 5px;
right: 12px;
font-size: 0.85em;
color: #666;
}
......@@ -577,6 +592,12 @@ div.form-section,
padding-right: 0.5em;
}
.calendarmain .eventdialog #event-url .event-text {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
#eventedit .formtable td.label {
min-width: 6em;
}
......@@ -1015,7 +1036,7 @@ a.dropdown-link:after {
#agendaoptions {
position: absolute;
bottom: 28px;
bottom: 0;
left: 0;
right: 0;
height: auto;
......@@ -1024,6 +1045,7 @@ a.dropdown-link:after {
border: 1px solid #c3c3c3;
border-top-color: #ddd;
border-bottom-color: #bbb;
border-radius: 0 0 4px 4px;
background: #ebebeb;
background: -moz-linear-gradient(top, #ebebeb 0%, #c6c6c6 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ebebeb), color-stop(100%,#c6c6c6));
......@@ -1063,7 +1085,7 @@ a.dropdown-link:after {
.rcube-fc-content {
overflow: hidden;
border: 0;
border-radius: 4px 4px 0 0;
border-radius: 4px;
box-shadow: 0 0 2px #999;
-o-box-shadow: 0 0 2px #999;
-webkit-box-shadow: 0 0 2px #999;
......@@ -1075,7 +1097,7 @@ a.dropdown-link:after {
top: 40px;
left: 0;
right: 0;
bottom: 28px;
bottom: 0;
background: #fff;
}
......@@ -1333,6 +1355,9 @@ div.fc-event-location {
.calendarmain .fc-view-table tr.fc-event td {
border-color: #ddd;
padding: 4px 7px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.calendarmain .fc-view-table tr.fc-event td.fc-event-handle {
......@@ -1363,8 +1388,12 @@ div.fc-event-location {
box-shadow: inset 0px 0 1px 1px rgba(0, 0, 0, 0.3);
}
.calendarmain .fc-view-table col.fc-event-location {
width: 25%;
}
.fc-view-table table.fc-list-smart {
table-layout: auto;
/* table-layout: auto; */
}
.fc-listappend {
......@@ -1398,7 +1427,7 @@ fieldset #calendarcategories div {
/* Invitation UI in mail */
#messagemenu li a.calendarlink span.calendar {
background-position: 0px -1948px;
background-position: 0px -2197px;
}
div.calendar-invitebox {
......
......@@ -5,7 +5,7 @@
<roundcube:include file="/includes/links.html" />
<!--[if lte IE 7]><link rel="stylesheet" type="text/css" href="/this/iehacks.css" /><![endif]-->
</head>
<body class="calendarmain noscroll">
<body class="calendarmain">
<roundcube:include file="/includes/header.html" />
......@@ -41,8 +41,6 @@
<div id="calendar">
<roundcube:object name="plugin.angenda_options" class="boxfooter" id="agendaoptions" />
<roundcube:object name="message" id="message" class="statusbar" />
<div class="timezonedisplay"><roundcube:var name="env:timezone" /></div>
</div>
</div>
......@@ -52,6 +50,10 @@
</ul>
</div>
<div id="timezonedisplay"><roundcube:var name="env:timezone" /></div>
<roundcube:object name="message" id="messagestack" />
<div id="calendaroptionsmenu" class="popupmenu">
<ul class="toolbarmenu">
<li><roundcube:button command="calendar-edit" label="calendar.edit" classAct="active" /></li>
......
......@@ -11,7 +11,7 @@
*/
$rcmail_config['kolab_addressbook_prio'] = 0;
// Base URL to build fully qualified URIs to access calendars via CALDAV
// Base URL to build fully qualified URIs to access address books via CardDAV
// The following replacement variables are supported:
// %h - Current HTTP host
// %u - Current webmail user name
......
......@@ -111,7 +111,7 @@ class kolab_addressbook extends rcube_plugin
// register this address source
$sources[$abook_id] = array(
'id' => $abook_id,
'name' => $name,
'name' => html_entity_decode($name, ENT_NOQUOTES, RCMAIL_CHARSET),
'readonly' => $abook->readonly,
'editable' => $abook->editable,
'groups' => $abook->groups,
......
......@@ -337,10 +337,14 @@ class rcube_kolab_contacts extends rcube_addressbook
}
}
}
else {
$this->result->count = $this->storagefolder->count($query);
foreach ($this->dataset as $idx => $record) {
$this->result->add($this->_to_rcube_contact($record));
else if (!empty($this->dataset)) {
$this->result->count = isset($query) ? $this->storagefolder->count($query) : count($this->dataset);
$start_row = $subset < 0 ? $this->page_size + $subset : 0;
$last_row = min($subset != 0 ? $start_row + abs($subset) : $this->page_size, $this->result->count);
for ($i = $start_row; $i < $last_row; $i++) {
$this->result->add($this->_to_rcube_contact($this->dataset[$i]));
}
}
......
......@@ -34,7 +34,7 @@ $rcmail_config['kolab_auth_admin_login'] = '';
$rcmail_config['kolab_auth_admin_password'] = '';
// Enable audit logging for abuse of administrative privileges.
$rcmail_config['kolab_auth_auditlog'] = true;
$rcmail_config['kolab_auth_auditlog'] = false;
// Role field (from fieldmap configuration)
$rcmail_config['kolab_auth_role'] = 'role';
......
......@@ -59,9 +59,8 @@ class kolab_auth extends rcube_plugin
$this->add_hook('write_log', array($this, 'write_log'));
$this->username = $_SESSION['username'];
// Enable debug logs per-user, this enables logging only after
// user has logged in
if (!empty($_SESSION['username']) && $rcmail->config->get('kolab_auth_auditlog')) {
// Enable debug logs (per-user), when logged as another user
if (!empty($_SESSION['kolab_auth_admin']) && $rcmail->config->get('kolab_auth_auditlog')) {
$rcmail->config->set('debug_level', 1);
$rcmail->config->set('devel_mode', true);
$rcmail->config->set('smtp_log', true);
......
......@@ -160,7 +160,7 @@ class kolab_config extends rcube_plugin
if (isset($this->dicts[$lang]))
return $this->dicts[$lang];
$query = array(array('type','=','configuration.dictionary'), array('tags','=',$lang));
$query = array(array('type','=','dictionary'), array('tags','=',$lang));
foreach ($this->folders as $folder) {
// we only want to read from default folder
......
......@@ -427,6 +427,11 @@ class kolab_delegation_engine
$result = $ldap->search($fields, $search, $mode, (array)$this->ldap_login_field, $max);
foreach ($result as $record) {
// skip self
if ($record['dn'] == $_SESSION['kolab_dn']) {
continue;
}
$user = $this->parse_ldap_record($record);
if ($user['name']) {
......
......@@ -65,7 +65,7 @@ div.foldersblock h3.note {
padding: 2px;
padding-top: 4px;
min-width: 2em;
background: #d6eaf3;
background-color: #d6eaf3;
border-bottom: 2px solid #fff;
text-align: center;
}
......
......@@ -79,7 +79,7 @@ window.rcmail && rcmail.addEventListener('init', function() {
// document.onmouseup = function(e){ return p.doc_mouse_up(e); };
rcmail.gui_objects.filelist.parentNode.onmousedown = function(e){ return kolab_files_click_on_list(e); };
rcmail.enable_command('menu-open', 'menu-save', 'files-sort', 'files-search', 'files-search-reset', true);
rcmail.enable_command('menu-open', 'menu-save', 'files-sort', 'files-search', 'files-search-reset', 'folder-create', true);
rcmail.file_list.init();
kolab_files_list_coltypes();
......@@ -98,6 +98,7 @@ window.rcmail && rcmail.addEventListener('init', function() {
else {
file_api.folder_list();
file_api.browser_capabilities_check();
rcmail.enable_command('folder-mount', rcmail.env.external_sources);
}
}
});
......@@ -295,11 +296,7 @@ function kolab_files_folder_create_dialog()
// show dialog window
kolab_dialog_show(dialog, {
title: rcmail.gettext('kolab_files.foldercreate'),
buttons: buttons,
minWidth: 400,
minHeight: 300,
width: 500,
height: 400
buttons: buttons
});
// Fix submitting form with Enter
......@@ -320,6 +317,119 @@ function kolab_files_folder_create_dialog()
});
};
// folder edit dialog
function kolab_files_folder_edit_dialog()
{
var dialog = $('#files-folder-edit-dialog'),
buttons = {}, options = [],
separator = file_api.env.directory_separator,
arr = file_api.env.folder.split(separator),
folder = arr.pop(),
path = arr.join(separator),
select = $('select[name="parent"]', dialog).html(''),
input = $('input[name="name"]', dialog).val(folder);
buttons[rcmail.gettext('kolab_files.save')] = function () {
var folder = '', name = input.val(), parent = select.val();
if (!name)
return;
if (parent)
folder = parent + separator;
folder += name;
file_api.folder_rename(file_api.env.folder, folder);
kolab_dialog_close(this);
};
buttons[rcmail.gettext('kolab_files.cancel')] = function () {
kolab_dialog_close(this);
};
// show dialog window
kolab_dialog_show(dialog, {
title: rcmail.gettext('kolab_files.folderedit'),
buttons: buttons
});
// Fix submitting form with Enter
$('form', dialog).submit(kolab_dialog_submit_handler);
// build parent selector
options.push($('<option>').val('').text('---'));
$.each(file_api.env.folders, function(i, f) {
var n, name = escapeHTML(f.name);
for (n=0; n<f.depth; n++)
name = '&nbsp;&nbsp;&nbsp;' + name;
options.push($('<option>').val(i).html(name));
});
select.append(options).val(path);
};
// folder mounting dialog
function kolab_files_folder_mount_dialog()
{
var args = {buttons: {}, title: rcmail.gettext('kolab_files.foldermount')},
dialog = $('#files-folder-mount-dialog'),
input = $('#folder-mount-name').val('');
args.buttons[rcmail.gettext('kolab_files.save')] = function () {
var args = {}, folder = input.val(),
driver = $('input[name="driver"]:checked', dialog).val();
if (!folder || !driver)
return;
args.folder = folder;
args.driver = driver;
$('#source-' + driver + ' input').each(function() {
if (this.name.startsWith(driver + '[')) {
args[this.name.substring(driver.length + 1, this.name.length - 1)] = this.value;
}
});
$('.auth-options input', dialog).each(function() {
args[this.name] = this.type == 'checkbox' && !this.checked ? '' : this.value;
});
file_api.folder_mount(args);
kolab_dialog_close(this);
};
args.buttons[rcmail.gettext('kolab_files.cancel')] = function () {
kolab_dialog_close(this);
};
// close folderoption menu
rcmail.hide_menu('folderoptions');
// initialize drivers list
if (!rcmail.drivers_list_initialized) {
rcmail.drivers_list_initialized = true;
$('td.source', dialog).each(function() {
$(this).click(function() {
$('td.selected', dialog).removeClass('selected');
dialog.find('.driverform').hide();
$(this).addClass('selected').find('.driverform').show();
$('input[type="radio"]', this).prop('checked', true);
});
});
}
// show dialog window
kolab_dialog_show(dialog, args, function() {
$('td.source:first', dialog).click();
input.focus();
});
};
// file edition dialog
function kolab_files_file_edit_dialog(file)
{
......@@ -771,6 +881,21 @@ rcube_webmail.prototype.files_set_quota = function(p)
this.set_quota(p);
};
rcube_webmail.prototype.folder_create = function()
{
kolab_files_folder_create_dialog();
};
rcube_webmail.prototype.folder_rename = function()
{
kolab_files_folder_edit_dialog();
};
rcube_webmail.prototype.folder_mount = function()
{
kolab_files_folder_mount_dialog();
};
/**********************************************************/
/********* Files API handler **********/
......@@ -832,34 +957,10 @@ function kolab_files_ui()
elem.html('').append(list);
this.env.folders = this.folder_list_parse(response.result);
this.env.folders = this.folder_list_parse(response.result && response.result.list ? response.result.list : response.result);
$.each(this.env.folders, function(i, f) {
var row = $('<li class="mailbox"><span class="branch"></span></li>');
row.attr('id', f.id).data('folder', i)
.append($('<span class="name"></span>').text(f.name));
if (f.depth) {
$('span.branch', row).width(15 * f.depth);
row.addClass('child');
}
if (f.virtual)
row.addClass('virtual');
else
row.click(function() { file_api.folder_select(i); })
.mouseenter(function() {
if (rcmail.file_list && rcmail.file_list.drag_active && !$(this).hasClass('selected'))
$(this).addClass('droptarget');
})