Merge branch 'master' of ssh://git.kolabsys.com/git/roundcube

This commit is contained in:
Thomas Bruederli 2011-07-14 09:47:38 +02:00
commit ace9517958
8 changed files with 184 additions and 34 deletions

View file

@ -449,9 +449,9 @@ class calendar extends rcube_plugin
$event['uid'] = $this->generate_uid();
// set current user as organizer
if (!$event['attendees']) {
if (FALSE && !$event['attendees']) {
$identity = $this->rc->user->get_identity();
$event['attendees'][] = array('role' => 'OWNER', 'name' => $identity['name'], 'email' => $identity['email']);
$event['attendees'][] = array('role' => 'ORGANIZER', 'name' => $identity['name'], 'email' => $identity['email']);
}
$this->prepare_event($event);
@ -1163,6 +1163,9 @@ class calendar extends rcube_plugin
}
}
// let this information be cached for 15min
send_future_expire_header(90);
echo $status;
exit;
}

View file

@ -440,7 +440,7 @@ function rcube_calendar_ui(settings)
attendees_list = $('#edit-attendees-table > tbody').html('');
if (calendar.attendees && event.attendees) {
for (var j=0; j < event.attendees.length; j++)
add_attendee(event.attendees[j]);
add_attendee(event.attendees[j], true);
}
// attachments
@ -509,6 +509,12 @@ function rcube_calendar_ui(settings)
if (i.match(/^rcmfile([0-9a-z]+)/))
attachments.push(RegExp.$1);
data.attachments = attachments;
// read attendee roles
$('select.edit-attendee-role').each(function(i, elem){
if (data.attendees[i])
data.attendees[i].role = $(elem).val();
});
// gather recurrence settings
var freq;
@ -597,6 +603,19 @@ function rcube_calendar_ui(settings)
title.select();
};
// update event properties and attendees availability if event times have changed
var event_times_changed = function()
{
alert('event_times_changed')
if (me.selected_event) {
var allday = $('#edit-allday').get(0);
me.selected_event.start = parse_datetime(allday.checked ? '00:00' : $('#edit-starttime').val(), $('#edit-startdate').val());
me.selected_event.end = parse_datetime(allday.checked ? '23:59' : $('#edit-endtime').val(), $('#edit-enddate').val());
if (me.selected_event.attendees)
update_freebusy_status(me.selected_event);
}
};
// add the given list of participants
var add_attendees = function(names)
{
@ -623,7 +642,7 @@ function rcube_calendar_ui(settings)
}
if (email) {
add_attendee({ email:email, name:name, role:'REQUIRED', status:'unknown' });
add_attendee({ email:email, name:name, role:'REQ-PARTICIPANT', status:'NEEDS-ACTION' });
success = true;
}
else {
@ -635,33 +654,93 @@ function rcube_calendar_ui(settings)
};
// add the given attendee to the list
var add_attendee = function(data)
var add_attendee = function(data, edit)
{
var dispname = (data.email && data.name) ? data.name + ' <' + data.email + '>' : (data.email || data.name);
// check for dupes...
var exists = false;
$.each(event_attendees, function(i, v){ exists |= (v.email == data.email); });
if (exists)
return false;
var dispname = Q(data.name || data.email);
if (data.email)
dispname = '<span title="' + Q(data.email) + '">' + dispname + '</span>';
// role selection
var opts = {
'ORGANIZER': rcmail.gettext('calendar.roleorganizer'),
'REQ-PARTICIPANT': rcmail.gettext('calendar.rolerequired'),
'OPT-PARTICIPANT': rcmail.gettext('calendar.roleoptional'),
'CHAIR': rcmail.gettext('calendar.roleresource')
};
var select = '<select class="edit-attendee-role">';
for (var r in opts)
select += '<option value="'+ r +'" class="' + r.toLowerCase() + '"' + (data.role == r ? ' selected="selected"' : '') +'>' + Q(opts[r]) + '</option>';
select += '</select>';
// availability
var avail = data.email ? 'loading' : 'unknown';
if (edit && data.role == 'ORGANIZER' && data.status == 'ACCEPTED')
avail = 'free';
// delete icon
var icon = rcmail.env.deleteicon ? '<img src="' + rcmail.env.deleteicon + '" alt="" />' : rcmail.gettext('delete');
var dellink = '<a href="#delete" class="deletelink" title="' + Q(rcmail.gettext('delete')) + '">' + icon + '</a>';
var html = '<td class="role"></td>' +
'<td class="name">' + Q(dispname) + '</td>' +
'<td class="availability">' + '' + '</td>' +
'<td class="confirmstate">' + Q(data.status) + '</td>' +
'<td class="options">' + (data.role != 'OWNER' ? dellink : '') + '</td>';
var html = '<td class="role">' + select + '</td>' +
'<td class="name">' + dispname + '</td>' +
'<td class="availability"><img src="./program/blank.gif" class="availabilityicon ' + avail + '" /></td>' +
'<td class="confirmstate"><span class="' + String(data.status).toLowerCase() + '">' + Q(data.status) + '</span></td>' +
'<td class="options">' + dellink + '</td>';
$('<tr>')
var tr = $('<tr>')
.addClass(String(data.role).toLowerCase())
.html(html)
.appendTo(attendees_list)
.find('a.deletelink').click({ id:(data.email || data.name) }, function(e) { remove_attendee(this, e.data.id); return false; });
.appendTo(attendees_list);
tr.find('a.deletelink').click({ id:(data.email || data.name) }, function(e) { remove_attendee(this, e.data.id); return false; });
// check free-busy status
if (avail == 'loading') {
check_freebusy_status(tr.find('img.availabilityicon'), data.email, me.selected_event);
}
event_attendees.push(data);
};
// iterate over all attendees and update their free-busy status display
var update_freebusy_status = function(event)
{
var icons = attendees_list.find('img.availabilityicon');
for (var i=0; i < event.attendees.length; i++) {
if (icons.get(i) && event.attendees[i].email && event.attendees[i].status != 'ACCEPTED')
check_freebusy_status(icons.get(i), event.attendees[i].email, event);
}
};
// load free-busy status from server and update icon accordingly
var check_freebusy_status = function(icon, email, event)
{
icon = $(icon).removeClass().addClass('availabilityicon loading');
$.ajax({
type: 'GET',
dataType: 'html',
url: rcmail.url('freebusy-status'),
data: { email:email, start:date2unixtime(event.start), end:date2unixtime(event.end), _remote: 1 },
success: function(status){
icon.removeClass('loading').addClass(String(status).toLowerCase());
},
error: function(){
icon.removeClass('loading').addClass('unknown');
}
});
};
// remove an attendee from the list
var remove_attendee = function(elem, id)
{
$(elem).closest('tr').hide();
$(elem).closest('tr').remove();
event_attendees = $.grep(event_attendees, function(data){ return (data.name != id && data.email != id) });
};
@ -1234,7 +1313,8 @@ function rcube_calendar_ui(settings)
}
});
$('#edit-enddate, input.edit-alarm-date').datepicker(datepicker_settings);
$('#edit-startdate').datepicker(datepicker_settings).datepicker('option', 'onSelect', shift_enddate).change(function(){ shift_enddate(this.value); });
$('#edit-startdate').datepicker(datepicker_settings).datepicker('option', 'onSelect', shift_enddate).change(function(){ shift_enddate(this.value); event_times_changed(); });
$('#edit-enddate, #edit-starttime, #edit-endtime').change(function(){ event_times_changed(); });
$('#edit-allday').click(function(){ $('#edit-starttime, #edit-endtime')[(this.checked?'hide':'show')](); });
// configure drop-down menu on time input fields based on jquery UI autocomplete

View file

@ -282,6 +282,7 @@ abstract class calendar_driver
*/
public function get_freebusy_list($email, $start, $end)
{
sleep(2);
return false;
}

View file

@ -35,6 +35,8 @@ class kolab_calendar
private $search_fields = array('title', 'description', 'location');
private $sensitivity_map = array('public', 'private', 'confidential');
private $priority_map = array('low', 'normal', 'high');
private $role_map = array('REQ-PARTICIPANT' => 'required', 'OPT-PARTICIPANT' => 'optional', 'CHAIR' => 'resource');
private $status_map = array('NEEDS-ACTION' => 'none', 'TENTATIVE' => 'tentative', 'CONFIRMED' => 'accepted', 'DECLINED' => 'declined');
private $month_map = array('', 'january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december');
private $weekday_map = array('MO'=>'monday', 'TU'=>'tuesday', 'WE'=>'wednesday', 'TH'=>'thursday', 'FR'=>'friday', 'SA'=>'saturday', 'SU'=>'sunday');
@ -430,6 +432,8 @@ class kolab_calendar
$sensitivity_map = array_flip($this->sensitivity_map);
$priority_map = array_flip($this->priority_map);
$status_map = array_flip($this->status_map);
$role_map = array_flip($this->role_map);
if (!empty($rec['_attachments'])) {
foreach ($rec['_attachments'] as $name => $attachment) {
@ -444,22 +448,22 @@ class kolab_calendar
if ($rec['organizer']) {
$attendees[] = array(
'role' => 'OWNER',
'role' => 'ORGANIZER',
'name' => $rec['organizer']['display-name'],
'email' => $rec['organizer']['smtp-address'],
'status' => 'accepted',
'status' => 'ACCEPTED',
);
}
foreach ((array)$rec['attendee'] as $attendee) {
$attendees[] = array(
'role' => strtoupper($attendee['role']),
'role' => $role_map[$attendee['role']],
'name' => $attendee['display-name'],
'email' => $attendee['smtp-address'],
'status' => $attendee['status'],
'status' => $status_map[$attendee['status']],
);
}
return array(
'id' => $rec['uid'],
'uid' => $rec['uid'],
@ -624,7 +628,7 @@ class kolab_calendar
// process event attendees
foreach ((array)$event['attendees'] as $attendee) {
$role = $attendee['role'];
if ($role == 'OWNER') {
if ($role == 'ORGANIZER') {
$object['organizer'] = array(
'display-name' => $attendee['name'],
'smtp-address' => $attendee['email'],
@ -634,8 +638,8 @@ class kolab_calendar
$object['attendee'][] = array(
'display-name' => $attendee['name'],
'smtp-address' => $attendee['email'],
'status' => $attendee['status'],
'role' => strtolower($role),
'status' => $this->status_map[$attendee['status']],
'role' => $this->role_map[$role],
);
}
}

View file

@ -1539,8 +1539,8 @@ function formatDates(date1, date2, format, options) {
for (i2=i+1; i2<len; i2++) {
if (format.charAt(i2) == ']') {
var subformat = format.substring(i+1, i2);
var subres = formatDate(otherDate, subformat, options);
if (subres != formatDate(date, subformat, options)) {
var subres = formatDate(date, subformat, options);
if (subres != formatDate(otherDate, subformat, options)) {
res += subres;
}
i = i2;
@ -5334,6 +5334,8 @@ function ListEventRenderer() {
segHash = opt('listTexts', 'thisMonth');
} else if (md == 1) {
segHash = opt('listTexts', 'nextMonth');
} else if (md > 1) {
segHash = opt('listTexts', 'future');
}
} else if (segmode == 'month') {
segHash = formatDate(segDate, 'MMMM yyyy');
@ -5447,7 +5449,7 @@ function ListEventRenderer() {
if (event.start < seg.start) {
datestr = opt('listTexts', 'until') + ' ' + formatDate(event.end, (event.allDay || event.end.getDate() != seg.start.getDate()) ? dateFormat : timeFormat);
} else if (duration > DAY_MS) {
datestr = formatDates(event.start, event.end, dateFormat + '[ - ' + dateFormat + ']');
datestr = formatDates(event.start, event.end, dateFormat + '{ - ' + dateFormat + '}');
} else if (seg.daydiff == 0) {
datestr = opt('listTexts', 'today');
} else if (seg.daydiff == 1) {
@ -5458,7 +5460,7 @@ function ListEventRenderer() {
datestr = formatDate(event.start, dateFormat);
}
} else if (segmode != 'day') {
datestr = formatDates(event.start, event.end, dateFormat + (duration > DAY_MS ? '[ - ' + dateFormat + ']' : ''));
datestr = formatDates(event.start, event.end, dateFormat + (duration > DAY_MS ? '{ - ' + dateFormat + '}' : ''));
}
if (!datestr && event.allDay) {

View file

@ -90,8 +90,12 @@ $labels['alarmtitle'] = 'Upcoming events';
$labels['attendee'] = 'Participant';
$labels['role'] = 'Role';
$labels['availability'] = 'Avail.';
$labels['confirmstate'] = 'Confirmed';
$labels['confirmstate'] = 'Status';
$labels['addattendee'] = 'Add participant';
$labels['roleorganizer'] = 'Organizer';
$labels['rolerequired'] = 'Required';
$labels['roleoptional'] = 'Optional';
$labels['roleresource'] = 'Resource';
// event dialog tabs
$labels['tabsummary'] = 'Summary';

View file

@ -106,7 +106,7 @@ pre {
#calendarslist li span {
cursor: default;
background: url(images/calendars.png) 0 -3px no-repeat;
background: url('images/calendars.png') 0 -3px no-repeat;
padding-left: 18px;
}
@ -480,11 +480,10 @@ td.topalign {
border-bottom: 1px solid #ddd;
}
#edit-attendees-table tr.owner td {
color: #999;
#edit-attendees-table td.role {
width: 8em;
}
#edit-attendees-table td.role,
#edit-attendees-table td.availability {
width: 4em;
}
@ -507,13 +506,70 @@ td.topalign {
}
#edit-attendees-table thead td {
background: url(images/listheader.gif) top left repeat-x #CCC;
background: url('images/listheader.gif') top left repeat-x #CCC;
}
#edit-attendees-form {
margin-top: 1em;
}
#edit-attendees-table select.edit-attendee-role {
border: 0;
padding: 2px;
background: white;
}
#edit-attendees-table img.availabilityicon {
width: 16px;
height: 16px;
border-radius: 4px;
-moz-border-radius: 4px;
}
#edit-attendees-table img.availabilityicon.loading {
background: url('images/loading-small.gif') top left no-repeat;
}
#edit-attendees-table img.availabilityicon.unknown {
background: #ccc;
}
#edit-attendees-table img.availabilityicon.free {
background: #0c0;
}
#edit-attendees-table img.availabilityicon.busy {
background: #c00;
}
#edit-attendees-table tbody td.confirmstate {
overflow: hidden;
white-space: nowrap;
font-size: 75%;
/* text-indent: -2000%; */
}
#edit-attendees-table td.confirmstate span {
display: block;
width: 20px;
}
#edit-attendees-table td.confirmstate span.needs-action {
}
#edit-attendees-table td.confirmstate span.tentative {
}
#edit-attendees-table td.confirmstate span.declined {
}
#edit-attendees-table td.confirmstate span.accepted {
}
span.edit-alarm-set {
white-space: nowrap;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB