Synchronized with upstream from git.kolab.org (3.2.x)
This commit is contained in:
parent
9fa243f2c8
commit
71db023cf1
21 changed files with 721 additions and 449 deletions
162
calendar.php
162
calendar.php
|
@ -319,6 +319,7 @@ class calendar extends rcube_plugin
|
|||
$this->rc->output->set_env('timezone', $this->timezone->getName());
|
||||
$this->rc->output->set_env('calendar_driver', $this->rc->config->get('calendar_driver'), false);
|
||||
$this->rc->output->set_env('calendar_resources', (bool)$this->rc->config->get('calendar_resources_driver'));
|
||||
$this->rc->output->set_env('mscolors', jqueryui::get_color_values());
|
||||
$this->rc->output->set_env('identities-selector', $this->ui->identity_select(array('id' => 'edit-identities-list', 'aria-label' => $this->gettext('roleorganizer'))));
|
||||
|
||||
$view = rcube_utils::get_input_value('view', rcube_utils::INPUT_GPC);
|
||||
|
@ -476,42 +477,40 @@ class calendar extends rcube_plugin
|
|||
// loading driver is expensive, don't do it if not needed
|
||||
$this->load_driver();
|
||||
|
||||
if (!isset($no_override['calendar_default_alarm_type']) || !isset($no_override['calendar_default_alarm_offset'])) {
|
||||
if (!isset($no_override['calendar_default_alarm_type'])) {
|
||||
if (!$p['current']) {
|
||||
$p['blocks']['view']['content'] = true;
|
||||
return $p;
|
||||
}
|
||||
|
||||
$alarm_type = $alarm_offset = '';
|
||||
|
||||
if (!isset($no_override['calendar_default_alarm_type'])) {
|
||||
$field_id = 'rcmfd_alarm';
|
||||
$select_type = new html_select(array('name' => '_alarm_type', 'id' => $field_id));
|
||||
$select_type->add($this->gettext('none'), '');
|
||||
|
||||
foreach ($this->driver->alarm_types as $type) {
|
||||
$select_type->add($this->rc->gettext(strtolower("alarm{$type}option"), 'libcalendaring'), $type);
|
||||
}
|
||||
|
||||
$alarm_type = $select_type->show($this->rc->config->get('calendar_default_alarm_type', ''));
|
||||
}
|
||||
|
||||
if (!isset($no_override['calendar_default_alarm_offset'])) {
|
||||
$field_id = 'rcmfd_alarm';
|
||||
$input_value = new html_inputfield(array('name' => '_alarm_value', 'id' => $field_id . 'value', 'size' => 3));
|
||||
$select_offset = new html_select(array('name' => '_alarm_offset', 'id' => $field_id . 'offset'));
|
||||
|
||||
foreach (array('-M','-H','-D','+M','+H','+D') as $trigger) {
|
||||
$select_offset->add($this->rc->gettext('trigger' . $trigger, 'libcalendaring'), $trigger);
|
||||
}
|
||||
|
||||
$preset = libcalendaring::parse_alarm_value($this->rc->config->get('calendar_default_alarm_offset', '-15M'));
|
||||
$alarm_offset = $input_value->show($preset[0]) . ' ' . $select_offset->show($preset[1]);
|
||||
}
|
||||
$field_id = 'rcmfd_alarm';
|
||||
$select_type = new html_select(array('name' => '_alarm_type', 'id' => $field_id));
|
||||
$select_type->add($this->gettext('none'), '');
|
||||
foreach ($this->driver->alarm_types as $type)
|
||||
$select_type->add($this->rc->gettext(strtolower("alarm{$type}option"), 'libcalendaring'), $type);
|
||||
|
||||
$p['blocks']['view']['options']['alarmtype'] = array(
|
||||
'title' => html::label($field_id, rcube::Q($this->gettext('defaultalarmtype'))),
|
||||
'content' => $alarm_type . ' ' . $alarm_offset,
|
||||
'content' => $select_type->show($this->rc->config->get('calendar_default_alarm_type', '')),
|
||||
);
|
||||
}
|
||||
|
||||
if (!isset($no_override['calendar_default_alarm_offset'])) {
|
||||
if (!$p['current']) {
|
||||
$p['blocks']['view']['content'] = true;
|
||||
return $p;
|
||||
}
|
||||
|
||||
$field_id = 'rcmfd_alarm';
|
||||
$input_value = new html_inputfield(array('name' => '_alarm_value', 'id' => $field_id . 'value', 'size' => 3));
|
||||
$select_offset = new html_select(array('name' => '_alarm_offset', 'id' => $field_id . 'offset'));
|
||||
foreach (array('-M','-H','-D','+M','+H','+D') as $trigger)
|
||||
$select_offset->add($this->rc->gettext('trigger' . $trigger, 'libcalendaring'), $trigger);
|
||||
|
||||
$preset = libcalendaring::parse_alarm_value($this->rc->config->get('calendar_default_alarm_offset', '-15M'));
|
||||
$p['blocks']['view']['options']['alarmoffset'] = array(
|
||||
'title' => html::label($field_id . 'value', rcube::Q($this->gettext('defaultalarmoffset'))),
|
||||
'content' => $input_value->show($preset[0]) . ' ' . $select_offset->show($preset[1]),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1466,7 +1465,7 @@ class calendar extends rcube_plugin
|
|||
{
|
||||
// Upload progress update
|
||||
if (!empty($_GET['_progress'])) {
|
||||
$this->rc->upload_progress();
|
||||
rcube_upload_progress();
|
||||
}
|
||||
|
||||
@set_time_limit(0);
|
||||
|
@ -1532,11 +1531,11 @@ class calendar extends rcube_plugin
|
|||
}
|
||||
else {
|
||||
if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) {
|
||||
$msg = $this->rc->gettext(array('name' => 'filesizeerror', 'vars' => array(
|
||||
'size' => $this->rc->show_bytes(parse_bytes(ini_get('upload_max_filesize'))))));
|
||||
$msg = $this->gettext(array('name' => 'filesizeerror', 'vars' => array(
|
||||
'size' => show_bytes(parse_bytes(ini_get('upload_max_filesize'))))));
|
||||
}
|
||||
else {
|
||||
$msg = $this->rc->gettext('fileuploaderror');
|
||||
$msg = $this->gettext('fileuploaderror');
|
||||
}
|
||||
|
||||
$this->rc->output->command('plugin.import_error', array('message' => $msg));
|
||||
|
@ -1779,11 +1778,11 @@ class calendar extends rcube_plugin
|
|||
|
||||
// convert link URIs references into structs
|
||||
if (array_key_exists('links', $event)) {
|
||||
foreach ((array) $event['links'] as $i => $link) {
|
||||
if (strpos($link, 'imap://') === 0 && ($msgref = $this->driver->get_message_reference($link))) {
|
||||
$event['links'][$i] = $msgref;
|
||||
}
|
||||
foreach ((array)$event['links'] as $i => $link) {
|
||||
if (strpos($link, 'imap://') === 0 && ($msgref = $this->driver->get_message_reference($link))) {
|
||||
$event['links'][$i] = $msgref;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check for organizer in attendees list
|
||||
|
@ -1975,8 +1974,8 @@ class calendar extends rcube_plugin
|
|||
private function write_preprocess(&$event, $action)
|
||||
{
|
||||
// convert dates into DateTime objects in user's current timezone
|
||||
$event['start'] = new DateTime($event['start'], $this->timezone);
|
||||
$event['end'] = new DateTime($event['end'], $this->timezone);
|
||||
$event['start'] = new DateTime($event['start'], $this->timezone);
|
||||
$event['end'] = new DateTime($event['end'], $this->timezone);
|
||||
$event['allday'] = (bool)$event['allday'];
|
||||
|
||||
// start/end is all we need for 'move' action (#1480)
|
||||
|
@ -2026,7 +2025,7 @@ class calendar extends rcube_plugin
|
|||
foreach ((array)$event['attendees'] as $i => $attendee) {
|
||||
if ($attendee['role'] == 'ORGANIZER')
|
||||
$organizer = $i;
|
||||
if ($attendee['email'] == in_array(strtolower($attendee['email']), $emails))
|
||||
if ($attendee['email'] && in_array(strtolower($attendee['email']), $emails))
|
||||
$owner = $i;
|
||||
if (!isset($attendee['rsvp']))
|
||||
$event['attendees'][$i]['rsvp'] = true;
|
||||
|
@ -2189,6 +2188,7 @@ class calendar extends rcube_plugin
|
|||
|
||||
// if the backend has free-busy information
|
||||
$fblist = $this->driver->get_freebusy_list($email, $start, $end);
|
||||
|
||||
if (is_array($fblist)) {
|
||||
$status = 'FREE';
|
||||
|
||||
|
@ -2202,7 +2202,7 @@ class calendar extends rcube_plugin
|
|||
}
|
||||
|
||||
// let this information be cached for 5min
|
||||
$this->rc->output->future_expire_header(300);
|
||||
send_future_expire_header(300);
|
||||
|
||||
echo $status;
|
||||
exit;
|
||||
|
@ -2238,13 +2238,26 @@ class calendar extends rcube_plugin
|
|||
$dts = new DateTime('@'.$start);
|
||||
$dts->setTimezone($this->timezone);
|
||||
}
|
||||
|
||||
|
||||
$fblist = $this->driver->get_freebusy_list($email, $start, $end);
|
||||
$slots = array();
|
||||
|
||||
$slots = '';
|
||||
|
||||
// prepare freebusy list before use (for better performance)
|
||||
if (is_array($fblist)) {
|
||||
foreach ($fblist as $idx => $slot) {
|
||||
list($from, $to, ) = $slot;
|
||||
|
||||
// check for possible all-day times
|
||||
if (gmdate('His', $from) == '000000' && gmdate('His', $to) == '235959') {
|
||||
// shift into the user's timezone for sane matching
|
||||
$fblist[$idx][0] -= $this->gmt_offset;
|
||||
$fblist[$idx][1] -= $this->gmt_offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// build a list from $start till $end with blocks representing the fb-status
|
||||
for ($s = 0, $t = $start; $t <= $end; $s++) {
|
||||
$status = self::FREEBUSY_UNKNOWN;
|
||||
$t_end = $t + $interval * 60;
|
||||
$dt = new DateTime('@'.$t);
|
||||
$dt->setTimezone($this->timezone);
|
||||
|
@ -2252,16 +2265,10 @@ class calendar extends rcube_plugin
|
|||
// determine attendee's status
|
||||
if (is_array($fblist)) {
|
||||
$status = self::FREEBUSY_FREE;
|
||||
|
||||
foreach ($fblist as $slot) {
|
||||
list($from, $to, $type) = $slot;
|
||||
|
||||
// check for possible all-day times
|
||||
if (gmdate('His', $from) == '000000' && gmdate('His', $to) == '235959') {
|
||||
// shift into the user's timezone for sane matching
|
||||
$from -= $this->gmt_offset;
|
||||
$to -= $this->gmt_offset;
|
||||
}
|
||||
|
||||
if ($from < $t_end && $to > $t) {
|
||||
$status = isset($type) ? $type : self::FREEBUSY_BUSY;
|
||||
if ($status == self::FREEBUSY_BUSY) // can't get any worse :-)
|
||||
|
@ -2269,9 +2276,12 @@ class calendar extends rcube_plugin
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
$slots[$s] = $status;
|
||||
$times[$s] = $dt->format($strformat);
|
||||
else {
|
||||
$status = self::FREEBUSY_UNKNOWN;
|
||||
}
|
||||
|
||||
// use most compact format, assume $status is one digit/character
|
||||
$slots .= $status;
|
||||
$t = $t_end;
|
||||
}
|
||||
|
||||
|
@ -2279,7 +2289,7 @@ class calendar extends rcube_plugin
|
|||
$dte->setTimezone($this->timezone);
|
||||
|
||||
// let this information be cached for 5min
|
||||
$this->rc->output->future_expire_header(300);
|
||||
send_future_expire_header(300);
|
||||
|
||||
echo rcube_output::json_serialize(array(
|
||||
'email' => $email,
|
||||
|
@ -2287,7 +2297,6 @@ class calendar extends rcube_plugin
|
|||
'end' => $dte->format('c'),
|
||||
'interval' => $interval,
|
||||
'slots' => $slots,
|
||||
'times' => $times,
|
||||
));
|
||||
exit;
|
||||
}
|
||||
|
@ -2686,15 +2695,47 @@ class calendar extends rcube_plugin
|
|||
$this->rc->output->command('display_message', $this->gettext('errorsaving'), 'error', -1);
|
||||
|
||||
// if user is logged in...
|
||||
// FIXME: we should really consider removing this functionality
|
||||
// it's confusing that it creates/updates an event only for logged-in user
|
||||
// what if the logged-in user is not the same as the attendee?
|
||||
if ($this->rc->user->ID) {
|
||||
$this->load_driver();
|
||||
|
||||
$invitation = $itip->get_invitation($token);
|
||||
$existing = $this->driver->get_event($this->event);
|
||||
|
||||
// save the event to his/her default calendar if not yet present
|
||||
if (!$this->driver->get_event($this->event) && ($calendar = $this->get_default_calendar($invitation['event']['sensitivity']))) {
|
||||
if (!$existing && ($calendar = $this->get_default_calendar($invitation['event']['sensitivity']))) {
|
||||
$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');
|
||||
else
|
||||
$this->rc->output->command('display_message', $this->gettext('errorimportingevent'), 'error');
|
||||
}
|
||||
else if ($existing
|
||||
&& ($this->event['sequence'] >= $existing['sequence'] || $this->event['changed'] >= $existing['changed'])
|
||||
&& ($calendar = $this->driver->get_calendar($existing['calendar']))
|
||||
) {
|
||||
$this->event = $invitation['event'];
|
||||
$this->event['id'] = $existing['id'];
|
||||
|
||||
unset($this->event['comment']);
|
||||
|
||||
// merge attendees status
|
||||
// e.g. preserve my participant status for regular updates
|
||||
$this->lib->merge_attendees($this->event, $existing, $status);
|
||||
|
||||
// update attachments list
|
||||
$event['deleted_attachments'] = true;
|
||||
|
||||
// show me as free when declined (#1670)
|
||||
if ($status == 'declined')
|
||||
$this->event['free_busy'] = 'free';
|
||||
|
||||
if ($this->driver->edit_event($this->event))
|
||||
$this->rc->output->command('display_message', $this->gettext(array('name' => 'updatedsuccessfully', 'vars' => array('calendar' => $calendar->get_name()))), 'confirmation');
|
||||
else
|
||||
$this->rc->output->command('display_message', $this->gettext('errorimportingevent'), 'error');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3340,12 +3381,7 @@ class calendar extends rcube_plugin
|
|||
$tmp_path = tempnam($this->rc->config->get('temp_dir'), 'rcmAttmntCal');
|
||||
file_put_contents($tmp_path, $this->get_ical()->export(array($event), '', false, array($this->driver, 'get_attachment_body')));
|
||||
|
||||
$args['attachments'][] = array(
|
||||
'path' => $tmp_path,
|
||||
'name' => $filename . '.ics',
|
||||
'mimetype' => 'text/calendar',
|
||||
'size' => filesize($tmp_path),
|
||||
);
|
||||
$args['attachments'][] = array('path' => $tmp_path, 'name' => $filename . '.ics', 'mimetype' => 'text/calendar');
|
||||
$args['param']['subject'] = $event['title'];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ function rcube_calendar(settings)
|
|||
rcube_libcalendaring.call(this, settings);
|
||||
|
||||
// member vars
|
||||
this.ui;
|
||||
this.ui_loaded = false;
|
||||
this.selected_attachment = null;
|
||||
|
||||
|
@ -49,29 +50,29 @@ function rcube_calendar(settings)
|
|||
$.when(
|
||||
$.getScript(rcmail.assets_path('plugins/calendar/calendar_ui.js')),
|
||||
$.getScript(rcmail.assets_path('plugins/calendar/lib/js/fullcalendar.js')),
|
||||
$.get(rcmail.url('calendar/inlineui'), function(html) { $(document.body).append(html); }, 'html')
|
||||
$.get(rcmail.url('calendar/inlineui'), function(html){ $(document.body).append(html); }, 'html')
|
||||
).then(function() {
|
||||
// disable attendees feature (autocompletion and stuff is not initialized)
|
||||
for (var c in rcmail.env.calendars)
|
||||
rcmail.env.calendars[c].attendees = rcmail.env.calendars[c].resources = false;
|
||||
|
||||
|
||||
me.ui_loaded = true;
|
||||
me.ui = new rcube_calendar_ui(me.settings);
|
||||
me.create_from_mail(uid); // start over
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// get message contents for event dialog
|
||||
var lock = rcmail.set_busy(true, 'loading');
|
||||
rcmail.http_post('calendar/mailtoevent', {
|
||||
'_mbox': rcmail.env.mailbox,
|
||||
'_uid': uid
|
||||
}, lock);
|
||||
else {
|
||||
// get message contents for event dialog
|
||||
var lock = rcmail.set_busy(true, 'loading');
|
||||
rcmail.http_post('calendar/mailtoevent', {
|
||||
'_mbox': rcmail.env.mailbox,
|
||||
'_uid': uid
|
||||
}, lock);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// callback function triggered from server with contents for the new event
|
||||
this.mail2event_dialog = function(event)
|
||||
{
|
||||
|
@ -90,7 +91,7 @@ function rcube_calendar(settings)
|
|||
rcmail.http_post('calendar/mailimportattach', {
|
||||
_uid: rcmail.env.uid,
|
||||
_mbox: rcmail.env.mailbox,
|
||||
_part: this.selected_attachment
|
||||
_part: this.selected_attachment,
|
||||
// _calendar: $('#calendar-attachment-saveto').val(),
|
||||
}, rcmail.set_busy(true, 'itip.savingdata'));
|
||||
}
|
||||
|
@ -105,11 +106,11 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
|||
|
||||
// register create-from-mail command to message_commands array
|
||||
if (rcmail.env.task == 'mail') {
|
||||
rcmail.register_command('calendar-create-from-mail', function() { cal.create_from_mail(); });
|
||||
rcmail.register_command('attachment-save-calendar', function() { cal.save_to_calendar(); });
|
||||
rcmail.addEventListener('plugin.mail2event_dialog', function(p) { cal.mail2event_dialog(p); });
|
||||
rcmail.addEventListener('plugin.unlock_saving', function(p) { cal.ui && cal.ui.unlock_saving(); });
|
||||
|
||||
rcmail.register_command('calendar-create-from-mail', function() { cal.create_from_mail() });
|
||||
rcmail.register_command('attachment-save-calendar', function() { cal.save_to_calendar() });
|
||||
rcmail.addEventListener('plugin.mail2event_dialog', function(p){ cal.mail2event_dialog(p) });
|
||||
rcmail.addEventListener('plugin.unlock_saving', function(p){ cal.ui && cal.ui.unlock_saving(); });
|
||||
|
||||
if (rcmail.env.action != 'show') {
|
||||
rcmail.env.message_commands.push('calendar-create-from-mail');
|
||||
rcmail.add_element($('<a>'));
|
||||
|
@ -129,8 +130,8 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
|||
}
|
||||
|
||||
rcmail.register_command('plugin.calendar', function() { rcmail.switch_task('calendar'); }, true);
|
||||
|
||||
rcmail.addEventListener('plugin.ping_url', function(p) {
|
||||
|
||||
rcmail.addEventListener('plugin.ping_url', function(p){
|
||||
var action = p.action;
|
||||
p.action = p.event = null;
|
||||
new Image().src = rcmail.url(action, p);
|
||||
|
|
377
calendar_ui.js
377
calendar_ui.js
|
@ -38,7 +38,7 @@ function rcube_calendar_ui(settings)
|
|||
this.selected_event = null;
|
||||
this.selected_calendar = null;
|
||||
this.search_request = null;
|
||||
this.saving_lock = null;
|
||||
this.saving_lock;
|
||||
this.calendars = {};
|
||||
this.quickview_sources = [];
|
||||
|
||||
|
@ -197,18 +197,18 @@ function rcube_calendar_ui(settings)
|
|||
{
|
||||
var result = [],
|
||||
strlen = str.length,
|
||||
q, p, i, chr, last;
|
||||
q, p, i, char, last;
|
||||
|
||||
for (q = p = i = 0; i < strlen; i++) {
|
||||
chr = str.charAt(i);
|
||||
if (chr == '"' && last != '\\') {
|
||||
char = str.charAt(i);
|
||||
if (char == '"' && last != '\\') {
|
||||
q = !q;
|
||||
}
|
||||
else if (!q && chr == delimiter) {
|
||||
else if (!q && char == delimiter) {
|
||||
result.push(str.substring(p, i));
|
||||
p = i + 1;
|
||||
}
|
||||
last = chr;
|
||||
last = char;
|
||||
}
|
||||
|
||||
result.push(str.substr(p));
|
||||
|
@ -285,49 +285,6 @@ function rcube_calendar_ui(settings)
|
|||
else
|
||||
return date.getHours() >= settings['work_start'] && date.getHours() < settings['work_end'];
|
||||
};
|
||||
|
||||
// check if the event has 'real' attendees, excluding the current user
|
||||
var has_attendees = function(event)
|
||||
{
|
||||
return (event.attendees && event.attendees.length && (event.attendees.length > 1 || String(event.attendees[0].email).toLowerCase() != settings.identity.email));
|
||||
};
|
||||
|
||||
// check if the current user is an attendee of this event
|
||||
var is_attendee = function(event, role, email)
|
||||
{
|
||||
var emails = email ? ';'+email.toLowerCase() : settings.identity.emails;
|
||||
for (var i=0; event.attendees && i < event.attendees.length; i++) {
|
||||
if ((!role || event.attendees[i].role == role) && event.attendees[i].email && emails.indexOf(';'+event.attendees[i].email.toLowerCase()) >= 0)
|
||||
return event.attendees[i];
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// check if the current user is the organizer
|
||||
var is_organizer = function(event, email)
|
||||
{
|
||||
return is_attendee(event, 'ORGANIZER', email) || !event.id;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check permissions on the given calendar object
|
||||
*/
|
||||
var has_permission = function(cal, perm)
|
||||
{
|
||||
// multiple chars means "either of"
|
||||
if (String(perm).length > 1) {
|
||||
for (var i=0; i < perm.length; i++) {
|
||||
if (has_permission(cal, perm[i]))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (cal.rights && String(cal.rights).indexOf(perm) >= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (perm == 'i' && cal.editable) || (perm == 'v' && cal.editable);
|
||||
}
|
||||
|
||||
var load_attachment = function(event, att)
|
||||
{
|
||||
|
@ -515,13 +472,13 @@ function rcube_calendar_ui(settings)
|
|||
return (j - k);
|
||||
});
|
||||
|
||||
var data, organizer, mystatus = null, rsvp, line, morelink, html = '', overflow = '';
|
||||
var data, mystatus = null, rsvp, line, morelink, html = '', overflow = '',
|
||||
organizer = me.is_organizer(event);
|
||||
|
||||
for (var j=0; j < event.attendees.length; j++) {
|
||||
data = event.attendees[j];
|
||||
if (data.email) {
|
||||
if (data.role == 'ORGANIZER')
|
||||
organizer = true;
|
||||
else if (settings.identity.emails.indexOf(';'+data.email) >= 0) {
|
||||
if (data.role != 'ORGANIZER' && settings.identity.emails.indexOf(';'+data.email) >= 0) {
|
||||
mystatus = data.status.toLowerCase();
|
||||
if (data.status == 'NEEDS-ACTION' || data.status == 'TENTATIVE' || data.rsvp)
|
||||
rsvp = mystatus;
|
||||
|
@ -540,7 +497,7 @@ function rcube_calendar_ui(settings)
|
|||
morelink = $('<a href="#more" class="morelink"></a>').html(rcmail.gettext('andnmore', 'calendar').replace('$nr', event.attendees.length - j - 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (html && (event.attendees.length > 1 || !organizer)) {
|
||||
$('#event-attendees').show()
|
||||
.children('.event-text')
|
||||
|
@ -570,7 +527,7 @@ function rcube_calendar_ui(settings)
|
|||
.text(rcmail.gettext('status' + mystatus, 'libcalendaring'));
|
||||
}
|
||||
|
||||
var show_rsvp = rsvp && !is_organizer(event) && event.status != 'CANCELLED' && has_permission(calendar, 'v');
|
||||
var show_rsvp = rsvp && !organizer && event.status != 'CANCELLED' && me.has_permission(calendar, 'v');
|
||||
$('#event-rsvp')[(show_rsvp ? 'show' : 'hide')]();
|
||||
$('#event-rsvp .rsvp-buttons input').prop('disabled', false).filter('input[rel='+mystatus+']').prop('disabled', true);
|
||||
|
||||
|
@ -598,7 +555,7 @@ function rcube_calendar_ui(settings)
|
|||
}
|
||||
});
|
||||
}
|
||||
if (!temp && has_permission(calendar, 'td') && event.editable !== false) {
|
||||
if (!temp && me.has_permission(calendar, 'td') && event.editable !== false) {
|
||||
buttons.push({
|
||||
text: rcmail.gettext('delete', 'calendar'),
|
||||
'class': 'delete',
|
||||
|
@ -671,8 +628,6 @@ function rcube_calendar_ui(settings)
|
|||
}
|
||||
|
||||
rcmail.enable_command('event-history', calendar.history)
|
||||
|
||||
rcmail.triggerEvent('calendar-event-dialog', {dialog: $dialog});
|
||||
};
|
||||
|
||||
// event handler for clicks on an attendee link
|
||||
|
@ -734,8 +689,12 @@ function rcube_calendar_ui(settings)
|
|||
var invite = $('#edit-attendees-invite').get(0);
|
||||
var comment = $('#edit-attendees-comment');
|
||||
|
||||
// make sure any calendar is selected
|
||||
if (!calendars.val())
|
||||
calendars.val($('option:first', calendars).attr('value'));
|
||||
|
||||
invite.checked = settings.itip_notify & 1 > 0;
|
||||
notify.checked = has_attendees(event) && invite.checked;
|
||||
notify.checked = me.has_attendees(event) && invite.checked;
|
||||
|
||||
if (event.allDay) {
|
||||
starttime.val("12:00").hide();
|
||||
|
@ -749,7 +708,7 @@ function rcube_calendar_ui(settings)
|
|||
// set calendar selection according to permissions
|
||||
calendars.find('option').each(function(i, opt) {
|
||||
var cal = me.calendars[opt.value] || {};
|
||||
$(opt).prop('disabled', !(cal.editable || (action == 'new' && has_permission(cal, 'i'))))
|
||||
$(opt).prop('disabled', !(cal.editable || (action == 'new' && me.has_permission(cal, 'i'))))
|
||||
});
|
||||
|
||||
// set alarm(s)
|
||||
|
@ -781,17 +740,17 @@ function rcube_calendar_ui(settings)
|
|||
$('#edit-recurring-warning').hide();
|
||||
|
||||
// init attendees tab
|
||||
var organizer = !event.attendees || is_organizer(event),
|
||||
var organizer = !event.attendees || me.is_organizer(event),
|
||||
allow_invitations = organizer || (calendar.owner && calendar.owner == 'anonymous') || settings.invite_shared;
|
||||
event_attendees = [];
|
||||
attendees_list = $('#edit-attendees-table > tbody').html('');
|
||||
resources_list = $('#edit-resources-table > tbody').html('');
|
||||
$('#edit-attendees-notify')[(action != 'new' && allow_invitations && has_attendees(event) && (settings.itip_notify & 2) ? 'show' : 'hide')]();
|
||||
$('#edit-localchanges-warning')[(action != 'new' && has_attendees(event) && !(allow_invitations || (calendar.owner && is_organizer(event, calendar.owner))) ? 'show' : 'hide')]();
|
||||
$('#edit-attendees-notify')[(action != 'new' && allow_invitations && me.has_attendees(event) && (settings.itip_notify & 2) ? 'show' : 'hide')]();
|
||||
$('#edit-localchanges-warning')[(action != 'new' && me.has_attendees(event) && !(allow_invitations || (calendar.owner && me.is_organizer(event, calendar.owner))) ? 'show' : 'hide')]();
|
||||
|
||||
var load_attendees_tab = function()
|
||||
{
|
||||
var j, data, reply_selected = 0;
|
||||
var j, data, organizer_attendee, reply_selected = 0;
|
||||
if (event.attendees) {
|
||||
for (j=0; j < event.attendees.length; j++) {
|
||||
data = event.attendees[j];
|
||||
|
@ -803,6 +762,9 @@ function rcube_calendar_ui(settings)
|
|||
add_attendee(data, !allow_invitations);
|
||||
if (allow_invitations && data.role != 'ORGANIZER' && !data.noreply)
|
||||
reply_selected++;
|
||||
|
||||
if (data.role == 'ORGANIZER')
|
||||
organizer_attendee = data;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -818,6 +780,15 @@ function rcube_calendar_ui(settings)
|
|||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// In case the user is not the (shared) event organizer we'll add the organizer to the selection list
|
||||
if (!identity_id && !organizer && organizer_attendee) {
|
||||
var organizer_name = organizer_attendee.email;
|
||||
if (organizer_attendee.name)
|
||||
organizer_name = '"' + organizer_attendee.name + '" <' + organizer_name + '>';
|
||||
$('#edit-identities-list').append($('<option value="0">').text(organizer_name));
|
||||
}
|
||||
|
||||
$('#edit-identities-list').val(identity_id);
|
||||
$('#edit-attendees-form')[(allow_invitations?'show':'hide')]();
|
||||
$('#edit-attendee-schedule')[(calendar.freebusy?'show':'hide')]();
|
||||
|
@ -998,8 +969,6 @@ function rcube_calendar_ui(settings)
|
|||
window.setTimeout(load_attendees_tab, exec_deferred);
|
||||
if (calendar.attachments)
|
||||
window.setTimeout(load_attachments_tab, exec_deferred);
|
||||
|
||||
rcmail.triggerEvent('calendar-event-dialog', {dialog: $dialog});
|
||||
};
|
||||
|
||||
// show event changelog in a dialog
|
||||
|
@ -1242,7 +1211,7 @@ function rcube_calendar_ui(settings)
|
|||
freebusy_data = { required:{}, all:{} };
|
||||
freebusy_ui.loading = 1; // prevent render_freebusy_grid() to load data yet
|
||||
freebusy_ui.numdays = Math.max(allday.checked ? 14 : 1, Math.ceil(duration * 2 / 86400));
|
||||
freebusy_ui.interval = allday.checked ? 1440 : 60;
|
||||
freebusy_ui.interval = allday.checked ? 1440 : (60 / (settings.timeslots || 1));
|
||||
freebusy_ui.start = fb_start;
|
||||
freebusy_ui.end = new Date(freebusy_ui.start.getTime() + DAY_MS * freebusy_ui.numdays);
|
||||
render_freebusy_grid(0);
|
||||
|
@ -1349,7 +1318,7 @@ function rcube_calendar_ui(settings)
|
|||
|
||||
// adjust dialog size to fit grid without scrolling
|
||||
var gridw = $('#schedule-freebusy-times').width();
|
||||
var overflow = gridw - $('#attendees-freebusy-table td.times').width() + 1;
|
||||
var overflow = gridw - $('#attendees-freebusy-table td.times').width();
|
||||
me.dialog_resize($dialog.get(0), $dialog.height() + (bw.ie ? 20 : 0), 800 + Math.max(0, overflow));
|
||||
|
||||
// fetch data from server
|
||||
|
@ -1379,10 +1348,12 @@ function rcube_calendar_ui(settings)
|
|||
var lastdate, datestr, css,
|
||||
curdate = new Date(),
|
||||
allday = (freebusy_ui.interval == 1440),
|
||||
interval = allday ? 1440 : (freebusy_ui.interval * (settings.timeslots || 1));
|
||||
times_css = (allday ? 'allday ' : ''),
|
||||
dates_row = '<tr class="dates">',
|
||||
times_row = '<tr class="times">',
|
||||
slots_row = '';
|
||||
|
||||
for (var s = 0, t = freebusy_ui.start.getTime(); t < freebusy_ui.end.getTime(); s++) {
|
||||
curdate.setTime(t);
|
||||
datestr = fc.fullCalendar('formatDate', curdate, date_format);
|
||||
|
@ -1395,9 +1366,9 @@ function rcube_calendar_ui(settings)
|
|||
// set css class according to working hours
|
||||
css = is_weekend(curdate) || (freebusy_ui.interval <= 60 && !is_workinghour(curdate)) ? 'offhours' : 'workinghours';
|
||||
times_row += '<td class="' + times_css + css + '" id="t-' + Math.floor(t/1000) + '">' + Q(allday ? rcmail.gettext('all-day','calendar') : $.fullCalendar.formatDate(curdate, settings['time_format'])) + '</td>';
|
||||
slots_row += '<td class="' + css + ' unknown"> </td>';
|
||||
slots_row += '<td class="' + css + '"> </td>';
|
||||
|
||||
t += freebusy_ui.interval * 60000;
|
||||
t += interval * 60000;
|
||||
}
|
||||
dates_row += '</tr>';
|
||||
times_row += '</tr>';
|
||||
|
@ -1411,7 +1382,7 @@ function rcube_calendar_ui(settings)
|
|||
}
|
||||
|
||||
// add line for all/required attendees
|
||||
times_html += '<tr class="spacer"><td colspan="' + (dayslots * freebusy_ui.numdays) + '"> </td>';
|
||||
times_html += '<tr class="spacer"><td colspan="' + (dayslots * freebusy_ui.numdays) + '"></td>';
|
||||
times_html += '<tr id="fbrowall">' + slots_row + '</tr>';
|
||||
|
||||
var table = $('#schedule-freebusy-times');
|
||||
|
@ -1433,7 +1404,7 @@ function rcube_calendar_ui(settings)
|
|||
update_freebusy_dates(newstart, new Date(newstart.getTime() + freebusy_ui.startdate.data('duration') * 1000));
|
||||
render_freebusy_overlay();
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// if we have loaded free-busy data, show it
|
||||
|
@ -1464,38 +1435,40 @@ function rcube_calendar_ui(settings)
|
|||
overlay.draggable('disable');
|
||||
}
|
||||
else {
|
||||
var table = $('#schedule-freebusy-times'),
|
||||
var i, n, table = $('#schedule-freebusy-times'),
|
||||
width = 0,
|
||||
pos = { top:table.children('thead').height(), left:0 },
|
||||
eventstart = date2unixtime(clone_date(me.selected_event.start, me.selected_event.allDay?1:0)),
|
||||
eventend = date2unixtime(clone_date(me.selected_event.end, me.selected_event.allDay?2:0)) - 60,
|
||||
slotstart = date2unixtime(freebusy_ui.start),
|
||||
slotsize = freebusy_ui.interval * 60,
|
||||
slotend, fraction, $cell;
|
||||
|
||||
// iterate through slots to determine position and size of the overlay
|
||||
table.children('thead').find('td').each(function(i, cell){
|
||||
slotend = slotstart + slotsize - 1;
|
||||
// event starts in this slot: compute left
|
||||
if (eventstart >= slotstart && eventstart <= slotend) {
|
||||
fraction = 1 - (slotend - eventstart) / slotsize;
|
||||
pos.left = Math.round(cell.offsetLeft + cell.offsetWidth * fraction);
|
||||
}
|
||||
// event ends in this slot: compute width
|
||||
if (eventend >= slotstart && eventend <= slotend) {
|
||||
fraction = 1 - (slotend - eventend) / slotsize;
|
||||
width = Math.round(cell.offsetLeft + cell.offsetWidth * fraction) - pos.left;
|
||||
}
|
||||
slotnum = freebusy_ui.interval > 60 ? 1 : (60 / freebusy_ui.interval),
|
||||
cells = table.children('thead').find('td'),
|
||||
cell_width = cells.first().get(0).offsetWidth,
|
||||
slotend;
|
||||
|
||||
slotstart = slotstart + slotsize;
|
||||
});
|
||||
// iterate through slots to determine position and size of the overlay
|
||||
for (i=0; i < cells.length; i++) {
|
||||
for (n=0; n < slotnum; n++) {
|
||||
slotend = slotstart + slotsize - 1;
|
||||
// event starts in this slot: compute left
|
||||
if (eventstart >= slotstart && eventstart <= slotend) {
|
||||
pos.left = Math.round(i * cell_width + (cell_width / slotnum) * n);
|
||||
}
|
||||
// event ends in this slot: compute width
|
||||
if (eventend >= slotstart && eventend <= slotend) {
|
||||
width = Math.round(i * cell_width + (cell_width / slotnum) * (n + 1)) - pos.left;
|
||||
}
|
||||
slotstart += slotsize;
|
||||
}
|
||||
}
|
||||
|
||||
if (!width)
|
||||
width = table.width() - pos.left;
|
||||
|
||||
// overlay is visible
|
||||
if (width > 0) {
|
||||
overlay.css({ width: (width-5)+'px', height:(table.children('tbody').height() - 4)+'px', left:pos.left+'px', top:pos.top+'px' }).show();
|
||||
overlay.css({ width: (width-4)+'px', height:(table.children('tbody').height() - 4)+'px', left:pos.left+'px', top:pos.top+'px' }).show();
|
||||
|
||||
// configure draggable
|
||||
if (!overlay.data('isdraggable')) {
|
||||
|
@ -1518,6 +1491,7 @@ function rcube_calendar_ui(settings)
|
|||
}
|
||||
else {
|
||||
// round to 5 minutes
|
||||
// @TODO: round to timeslots?
|
||||
var round = newstart.getMinutes() % 5;
|
||||
if (round > 2.5) newstart.setTime(newstart.getTime() + (5 - round) * 60000);
|
||||
else if (round > 0) newstart.setTime(newstart.getTime() - round * 60000);
|
||||
|
@ -1535,10 +1509,8 @@ function rcube_calendar_ui(settings)
|
|||
else
|
||||
overlay.draggable('disable').hide();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
// fetch free-busy information for each attendee from server
|
||||
var load_freebusy_data = function(from, interval)
|
||||
{
|
||||
|
@ -1565,9 +1537,9 @@ function rcube_calendar_ui(settings)
|
|||
success: function(data) {
|
||||
freebusy_ui.loading--;
|
||||
|
||||
// find attendee
|
||||
var attendee = null;
|
||||
for (var i=0; i < event_attendees.length; i++) {
|
||||
// find attendee
|
||||
var i, attendee = null;
|
||||
for (i=0; i < event_attendees.length; i++) {
|
||||
if (freebusy_ui.attendees[i].email == data.email) {
|
||||
attendee = freebusy_ui.attendees[i];
|
||||
break;
|
||||
|
@ -1575,25 +1547,31 @@ function rcube_calendar_ui(settings)
|
|||
}
|
||||
|
||||
// copy data to member var
|
||||
var ts, req = attendee.role != 'OPT-PARTICIPANT';
|
||||
freebusy_data.start = parseISO8601(data.start);
|
||||
var ts, status,
|
||||
req = attendee.role != 'OPT-PARTICIPANT',
|
||||
start = parseISO8601(data.start);
|
||||
|
||||
freebusy_data.start = new Date(start);
|
||||
freebusy_data.end = parseISO8601(data.end);
|
||||
freebusy_data.interval = data.interval;
|
||||
freebusy_data[data.email] = {};
|
||||
for (var i=0; i < data.slots.length; i++) {
|
||||
ts = data.times[i] + '';
|
||||
freebusy_data[data.email][ts] = data.slots[i];
|
||||
|
||||
for (i=0; i < data.slots.length; i++) {
|
||||
ts = date2timestring(start, data.interval > 60);
|
||||
status = data.slots.charAt(i);
|
||||
freebusy_data[data.email][ts] = status
|
||||
start = new Date(start.getTime() + data.interval * 60000);
|
||||
|
||||
// set totals
|
||||
if (!freebusy_data.required[ts])
|
||||
freebusy_data.required[ts] = [0,0,0,0];
|
||||
if (req)
|
||||
freebusy_data.required[ts][data.slots[i]]++;
|
||||
freebusy_data.required[ts][status]++;
|
||||
|
||||
if (!freebusy_data.all[ts])
|
||||
freebusy_data.all[ts] = [0,0,0,0];
|
||||
freebusy_data.all[ts][data.slots[i]]++;
|
||||
freebusy_data.all[ts][status]++;
|
||||
}
|
||||
freebusy_data.end = parseISO8601(data.end);
|
||||
freebusy_data.interval = data.interval;
|
||||
|
||||
// hide loading indicator
|
||||
var domid = String(data.email).replace(rcmail.identifier_expr, '');
|
||||
|
@ -1656,27 +1634,51 @@ function rcube_calendar_ui(settings)
|
|||
|
||||
if (fbdata && fbdata[ts] !== undefined && row.length) {
|
||||
t = freebusy_ui.start.getTime();
|
||||
row.children().each(function(i, cell){
|
||||
curdate.setTime(t);
|
||||
ts = date2timestring(curdate, dateonly);
|
||||
cell.className = cell.className.replace('unknown', fbdata[ts] ? status_classes[fbdata[ts]] : 'unknown');
|
||||
row.children().each(function(i, cell) {
|
||||
var j, n, attr, last, all_slots = [], slots = [],
|
||||
all_cell = rowall.get(i),
|
||||
cnt = dateonly ? 1 : (60 / freebusy_ui.interval),
|
||||
percent = (100 / cnt);
|
||||
|
||||
// also update total row if all data was loaded
|
||||
if (freebusy_ui.loading == 0 && freebusy_data.all[ts] && (cell = rowall.get(i))) {
|
||||
var workinghours = cell.className.indexOf('workinghours') >= 0;
|
||||
var all_status = freebusy_data.all[ts][2] ? 'busy' : 'unknown';
|
||||
req_status = freebusy_data.required[ts][2] ? 'busy' : 'free';
|
||||
for (var j=1; j < status_classes.length; j++) {
|
||||
if (freebusy_ui.numrequired && freebusy_data.required[ts][j] >= freebusy_ui.numrequired)
|
||||
req_status = status_classes[j];
|
||||
if (freebusy_data.all[ts][j] == event_attendees.length)
|
||||
all_status = status_classes[j];
|
||||
for (n=0; n < cnt; n++) {
|
||||
curdate.setTime(t);
|
||||
ts = date2timestring(curdate, dateonly);
|
||||
attr = {
|
||||
'style': 'float:left; width:' + percent.toFixed(2) + '%',
|
||||
'class': fbdata[ts] ? status_classes[fbdata[ts]] : 'unknown'
|
||||
};
|
||||
|
||||
slots.push($('<div>').attr(attr));
|
||||
|
||||
// also update total row if all data was loaded
|
||||
if (!freebusy_ui.loading && freebusy_data.all[ts] && all_cell) {
|
||||
var all_status = freebusy_data.all[ts][2] ? 'busy' : 'unknown',
|
||||
req_status = freebusy_data.required[ts][2] ? 'busy' : 'free';
|
||||
|
||||
for (j=1; j < status_classes.length; j++) {
|
||||
if (freebusy_ui.numrequired && freebusy_data.required[ts][j] >= freebusy_ui.numrequired)
|
||||
req_status = status_classes[j];
|
||||
if (freebusy_data.all[ts][j] == event_attendees.length)
|
||||
all_status = status_classes[j];
|
||||
}
|
||||
|
||||
attr['class'] = req_status + ' all-' + all_status;
|
||||
|
||||
// these elements use some specific styling, so we want to minimize their number
|
||||
if (last && last.attr('class') == attr['class'])
|
||||
last.css('width', (percent + parseFloat(last.css('width').replace('%', ''))).toFixed(2) + '%');
|
||||
else {
|
||||
last = $('<div>').attr(attr);
|
||||
all_slots.push(last);
|
||||
}
|
||||
}
|
||||
|
||||
cell.className = (workinghours ? 'workinghours ' : 'offhours ') + req_status + ' all-' + all_status;
|
||||
|
||||
t += freebusy_ui.interval * 60000;
|
||||
}
|
||||
|
||||
t += freebusy_ui.interval * 60000;
|
||||
|
||||
$(cell).html('').append(slots);
|
||||
if (all_slots.length)
|
||||
$(all_cell).html('').append(all_slots);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -1716,17 +1718,20 @@ function rcube_calendar_ui(settings)
|
|||
sinterval = freebusy_data.interval * 60000,
|
||||
intvlslots = 1,
|
||||
numslots = Math.ceil(duration / sinterval),
|
||||
checkdate, slotend, email, ts, slot, slotdate = new Date();
|
||||
fb_start = freebusy_data.start.getTime(),
|
||||
fb_end = freebusy_data.end.getTime(),
|
||||
checkdate, slotend, email, ts, slot, slotdate = new Date(),
|
||||
candidatecount = 0, candidatestart = false, success = false;
|
||||
|
||||
// shift event times to next possible slot
|
||||
eventstart += sinterval * intvlslots * dir;
|
||||
eventend += sinterval * intvlslots * dir;
|
||||
|
||||
// iterate through free-busy slots and find candidates
|
||||
var candidatecount = 0, candidatestart = candidateend = success = false;
|
||||
for (slot = dir > 0 ? freebusy_data.start.getTime() : freebusy_data.end.getTime() - sinterval;
|
||||
(dir > 0 && slot < freebusy_data.end.getTime()) || (dir < 0 && slot >= freebusy_data.start.getTime());
|
||||
slot += sinterval * dir) {
|
||||
for (slot = dir > 0 ? fb_start : fb_end - sinterval;
|
||||
(dir > 0 && slot < fb_end) || (dir < 0 && slot >= fb_start);
|
||||
slot += sinterval * dir
|
||||
) {
|
||||
slotdate.setTime(slot);
|
||||
// fix slot if just crossed a DST change
|
||||
if (event.allDay) {
|
||||
|
@ -1738,10 +1743,10 @@ function rcube_calendar_ui(settings)
|
|||
if ((dir > 0 && slotend <= eventstart) || (dir < 0 && slot >= eventend)) // skip
|
||||
continue;
|
||||
|
||||
// respect workingours setting
|
||||
// respect workinghours setting
|
||||
if (freebusy_ui.workinhoursonly) {
|
||||
if (is_weekend(slotdate) || (freebusy_data.interval <= 60 && !is_workinghour(slotdate))) { // skip off-hours
|
||||
candidatestart = candidateend = false;
|
||||
candidatestart = false;
|
||||
candidatecount = 0;
|
||||
continue;
|
||||
}
|
||||
|
@ -1750,11 +1755,12 @@ function rcube_calendar_ui(settings)
|
|||
if (!candidatestart)
|
||||
candidatestart = slot;
|
||||
|
||||
// check freebusy data for all attendees
|
||||
ts = date2timestring(slotdate, freebusy_data.interval > 60);
|
||||
|
||||
// check freebusy data for all attendees
|
||||
for (var i=0; i < event_attendees.length; i++) {
|
||||
if (freebusy_ui.attendees[i].role != 'OPT-PARTICIPANT' && (email = freebusy_ui.attendees[i].email) && freebusy_data[email] && freebusy_data[email][ts] > 1) {
|
||||
candidatestart = candidateend = false;
|
||||
candidatestart = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1765,22 +1771,15 @@ function rcube_calendar_ui(settings)
|
|||
candidatecount = 0;
|
||||
continue;
|
||||
}
|
||||
else if (dir < 0)
|
||||
candidatestart = slot;
|
||||
|
||||
// set candidate end to slot end time
|
||||
candidatecount++;
|
||||
if (dir < 0 && !candidateend)
|
||||
candidateend = slotend;
|
||||
|
||||
// if candidate is big enough, this is it!
|
||||
if (candidatecount == numslots) {
|
||||
if (dir > 0) {
|
||||
event.start.setTime(candidatestart);
|
||||
event.end.setTime(candidatestart + duration);
|
||||
}
|
||||
else {
|
||||
event.end.setTime(candidateend);
|
||||
event.start.setTime(candidateend - duration);
|
||||
}
|
||||
event.start.setTime(candidatestart);
|
||||
event.end.setTime(candidatestart + duration);
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
@ -1810,7 +1809,6 @@ function rcube_calendar_ui(settings)
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
// update event properties and attendees availability if event times have changed
|
||||
var event_times_changed = function()
|
||||
{
|
||||
|
@ -1825,7 +1823,6 @@ function rcube_calendar_ui(settings)
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
// add the given list of participants
|
||||
var add_attendees = function(names, params)
|
||||
{
|
||||
|
@ -1914,9 +1911,9 @@ function rcube_calendar_ui(settings)
|
|||
+ (!data.noreply && settings.itip_notify & 1 ? 'checked="checked" ' : '') + '/>';
|
||||
|
||||
if (data['delegated-to'])
|
||||
tooltip = rcmail.gettext('delegatedto', 'calendar') + data['delegated-to'];
|
||||
tooltip = rcmail.gettext('libcalendaring.delegatedto') + ' ' + data['delegated-to'];
|
||||
else if (data['delegated-from'])
|
||||
tooltip = rcmail.gettext('delegatedfrom', 'calendar') + data['delegated-from'];
|
||||
tooltip = rcmail.gettext('libcalendaring.delegatedfrom') + ' ' + data['delegated-from'];
|
||||
else if (status)
|
||||
tooltip = status_label;
|
||||
|
||||
|
@ -2512,25 +2509,15 @@ function rcube_calendar_ui(settings)
|
|||
|
||||
if (!data) data = event;
|
||||
var decline = false, notify = false, html = '', cal = me.calendars[event.calendar],
|
||||
_has_attendees = has_attendees(event), _is_organizer = is_organizer(event);
|
||||
_has_attendees = me.has_attendees(event),
|
||||
_is_attendee = _has_attendees && me.is_attendee(event),
|
||||
_is_organizer = me.is_organizer(event);
|
||||
|
||||
// event has attendees, ask whether to notify them
|
||||
if (_has_attendees) {
|
||||
var checked = (settings.itip_notify & 1 ? ' checked="checked"' : '');
|
||||
|
||||
if (_is_organizer) {
|
||||
notify = true;
|
||||
if (settings.itip_notify & 2) {
|
||||
html += '<div class="message">' +
|
||||
'<label><input class="confirm-attendees-donotify" type="checkbox"' + checked + ' value="1" name="notify" /> ' +
|
||||
rcmail.gettext((action == 'remove' ? 'sendcancellation' : 'sendnotifications'), 'calendar') +
|
||||
'</label></div>';
|
||||
}
|
||||
else {
|
||||
data._notify = settings.itip_notify;
|
||||
}
|
||||
}
|
||||
else if (action == 'remove' && is_attendee(event)) {
|
||||
if (action == 'remove' && cal.group != 'shared' && !_is_organizer && _is_attendee) {
|
||||
decline = true;
|
||||
checked = event.status != 'CANCELLED' ? checked : '';
|
||||
html += '<div class="message">' +
|
||||
|
@ -2538,8 +2525,17 @@ function rcube_calendar_ui(settings)
|
|||
rcmail.gettext('itipdeclineevent', 'calendar') +
|
||||
'</label></div>';
|
||||
}
|
||||
else {
|
||||
html += '<div class="message">' + rcmail.gettext('localchangeswarning', 'calendar') + '</div>';
|
||||
else if (_is_organizer) {
|
||||
notify = true;
|
||||
if (settings.itip_notify & 2) {
|
||||
html += '<div class="message">' +
|
||||
'<label><input class="confirm-attendees-donotify" type="checkbox"' + checked + ' value="1" name="notify" /> ' +
|
||||
rcmail.gettext((action == 'remove' ? 'sendcancellation' : 'sendnotifications'), 'calendar') +
|
||||
'</label></div>';
|
||||
}
|
||||
else {
|
||||
data._notify = settings.itip_notify;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2549,7 +2545,7 @@ function rcube_calendar_ui(settings)
|
|||
|
||||
// disable the 'future' savemode if I'm an attendee
|
||||
// reason: no calendaring system supports the thisandfuture range parameter in iTip REPLY
|
||||
if (action == 'remove' && _has_attendees && !_is_organizer && is_attendee(event)) {
|
||||
if (action == 'remove' && !_is_organizer && _is_attendee) {
|
||||
future_disabled = ' disabled';
|
||||
}
|
||||
|
||||
|
@ -3149,28 +3145,6 @@ function rcube_calendar_ui(settings)
|
|||
}
|
||||
};
|
||||
|
||||
// display the edit dialog, request 'new' action and pass the selected event
|
||||
this.event_copy = function(event) {
|
||||
if (event && event.id) {
|
||||
var copy = $.extend(true, {}, event);
|
||||
|
||||
delete copy.id;
|
||||
delete copy._id;
|
||||
delete copy.created;
|
||||
delete copy.changed;
|
||||
delete copy.recurrence_id;
|
||||
delete copy.attachments; // @TODO
|
||||
|
||||
$.each(copy.attendees, function (k, v) {
|
||||
if (v.role != 'ORGANIZER') {
|
||||
v.status = 'NEEDS-ACTION';
|
||||
}
|
||||
})
|
||||
|
||||
event_edit_dialog('new', copy);
|
||||
}
|
||||
};
|
||||
|
||||
// show URL of the given calendar in a dialog box
|
||||
this.showurl = function(calendar)
|
||||
{
|
||||
|
@ -3490,13 +3464,6 @@ function rcube_calendar_ui(settings)
|
|||
this.fisheye_view(this.fisheye_date);
|
||||
};
|
||||
|
||||
// resize and reposition (center) the dialog window
|
||||
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+130), width: Math.min(w-20, width+50) });
|
||||
};
|
||||
|
||||
// adjust calendar view size
|
||||
this.view_resize = function()
|
||||
{
|
||||
|
@ -3514,6 +3481,8 @@ function rcube_calendar_ui(settings)
|
|||
rcmail.triggerEvent('selectfolder', { folder:id, prefix:'rcmlical' });
|
||||
|
||||
this.selected_calendar = id;
|
||||
|
||||
rcmail.update_state({source: id});
|
||||
};
|
||||
|
||||
// register the given calendar to the current view
|
||||
|
@ -3552,7 +3521,7 @@ function rcube_calendar_ui(settings)
|
|||
|
||||
// insert to #calendar-select options if writeable
|
||||
select = $('#edit-calendar');
|
||||
if (fc && has_permission(cal, 'i') && select.length && !select.find('option[value="'+id+'"]').length) {
|
||||
if (fc && me.has_permission(cal, 'i') && select.length && !select.find('option[value="'+id+'"]').length) {
|
||||
$('<option>').attr('value', id).html(cal.name).appendTo(select);
|
||||
}
|
||||
}
|
||||
|
@ -3720,7 +3689,9 @@ function rcube_calendar_ui(settings)
|
|||
});
|
||||
|
||||
// select default calendar
|
||||
if (settings.default_calendar && this.calendars[settings.default_calendar] && this.calendars[settings.default_calendar].editable)
|
||||
if (rcmail.env.source && this.calendars[rcmail.env.source])
|
||||
this.selected_calendar = rcmail.env.source;
|
||||
else if (settings.default_calendar && this.calendars[settings.default_calendar] && this.calendars[settings.default_calendar].editable)
|
||||
this.selected_calendar = settings.default_calendar;
|
||||
|
||||
if (this.selected_calendar)
|
||||
|
@ -3935,24 +3906,23 @@ function rcube_calendar_ui(settings)
|
|||
// init event dialog
|
||||
$('#eventtabs').tabs({
|
||||
activate: function(event, ui) {
|
||||
// newPanel.selector for jQuery-UI 1.10, newPanel.attr('id') for jQuery-UI 1.12
|
||||
var tab = String(ui.newPanel.selector || ui.newPanel.attr('id'))
|
||||
.replace(/^#?event-panel-/, '').replace(/s$/, '');
|
||||
|
||||
if (tab == 'attendee' || tab == 'resource') {
|
||||
if (ui.newPanel.selector == '#event-panel-attendees' || ui.newPanel.selector == '#event-panel-resources') {
|
||||
var tab = ui.newPanel.selector == '#event-panel-resources' ? 'resource' : 'attendee';
|
||||
if (!rcube_event.is_keyboard(event))
|
||||
$('#edit-'+tab+'-name').select();
|
||||
// update free-busy status if needed
|
||||
if (freebusy_ui.needsupdate && me.selected_event)
|
||||
update_freebusy_status(me.selected_event);
|
||||
// add current user as organizer if non added yet
|
||||
if (tab == 'attendee' && !event_attendees.length) {
|
||||
if (!event_attendees.length) {
|
||||
add_attendee($.extend({ role:'ORGANIZER' }, settings.identity));
|
||||
$('#edit-attendees-form .attendees-invitebox').show();
|
||||
}
|
||||
}
|
||||
// reset autocompletion on tab change (#3389)
|
||||
rcmail.ksearch_blur();
|
||||
if (ui.oldPanel.selector == '#event-panel-attendees' || ui.oldPanel.selector == '#event-panel-resources') {
|
||||
rcmail.ksearch_blur();
|
||||
}
|
||||
}
|
||||
});
|
||||
$('#edit-enddate').datepicker(datepicker_settings);
|
||||
|
@ -4162,7 +4132,6 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
|
|||
rcmail.register_command('calendar-showurl', function(){ cal.showurl(cal.calendars[cal.selected_calendar]); }, false);
|
||||
rcmail.register_command('event-download', function(){ cal.event_download(cal.selected_event); }, true);
|
||||
rcmail.register_command('event-sendbymail', function(p, obj, e){ cal.event_sendbymail(cal.selected_event, e); }, true);
|
||||
rcmail.register_command('event-copy', function(){ cal.event_copy(cal.selected_event); }, true);
|
||||
rcmail.register_command('event-history', function(p, obj, e){ cal.event_history_dialog(cal.selected_event); }, false);
|
||||
|
||||
// search and export events
|
||||
|
|
|
@ -7,13 +7,13 @@
|
|||
"license": "AGPLv3",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Thomas Bruederli",
|
||||
"email": "bruederli@kolabsys.com",
|
||||
"name": "Alensader Machniak",
|
||||
"email": "machniak@kolabsys.com",
|
||||
"role": "Lead"
|
||||
},
|
||||
{
|
||||
"name": "Alensader Machniak",
|
||||
"email": "machniak@kolabsys.com",
|
||||
"name": "Thomas Bruederli",
|
||||
"email": "thomas@roundcube.net",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
|
@ -31,6 +31,7 @@
|
|||
"extra": {
|
||||
"roundcube": {
|
||||
"min-version": "1.1.0",
|
||||
"max-version": "1.2.99",
|
||||
"sql-dir": "drivers/database/SQL"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -149,7 +149,7 @@ $config['calendar_itip_smtp_user'] = 'smtpauth';
|
|||
$config['calendar_itip_smtp_pass'] = '123456';
|
||||
|
||||
// show virtual invitation calendars (Kolab driver only)
|
||||
$config['kolab_invitation_calendars'] = false;
|
||||
$config['kolab_invitation_calendars'] = true;
|
||||
|
||||
// Base URL to build fully qualified URIs to access calendars via CALDAV
|
||||
// The following replacement variables are supported:
|
||||
|
|
|
@ -391,7 +391,7 @@ class database_driver extends calendar_driver
|
|||
if ($event['id'] == $master['id']) {
|
||||
$event += $old;
|
||||
$event['recurrence_id'] = $master['id'];
|
||||
$event['_instance'] = libcalendaring::recurrence_instance_identifier($old);
|
||||
$event['_instance'] = libcalendaring::recurrence_instance_identifier($old, $master['allday']);
|
||||
$event['isexception'] = 1;
|
||||
$event_id = $this->_insert_event($event);
|
||||
return $event_id;
|
||||
|
|
|
@ -188,26 +188,23 @@ class kolab_calendar extends kolab_storage_folder_api
|
|||
*/
|
||||
public function get_event($id)
|
||||
{
|
||||
// remove our occurrence identifier if it's there
|
||||
$master_id = preg_replace('/-\d{8}(T\d{6})?$/', '', $id);
|
||||
|
||||
// directly access storage object
|
||||
if (!$this->events[$id] && $master_id == $id && ($record = $this->storage->get_object($id))) {
|
||||
$this->events[$id] = $this->_to_driver_event($record, true);
|
||||
}
|
||||
if (!$this->events[$id] && ($record = $this->storage->get_object($id)))
|
||||
$this->events[$id] = $this->_to_driver_event($record, true);
|
||||
|
||||
// maybe a recurring instance is requested
|
||||
if (!$this->events[$id] && $master_id != $id) {
|
||||
// event not found, maybe a recurring instance is requested
|
||||
if (!$this->events[$id]) {
|
||||
$master_id = preg_replace('/-\d+(T\d{6})?$/', '', $id);
|
||||
$instance_id = substr($id, strlen($master_id) + 1);
|
||||
|
||||
if ($record = $this->storage->get_object($master_id)) {
|
||||
if ($master_id != $id && ($record = $this->storage->get_object($master_id))) {
|
||||
$master = $this->_to_driver_event($record);
|
||||
}
|
||||
|
||||
if ($master) {
|
||||
// check for match in top-level exceptions (aka loose single occurrences)
|
||||
if ($master['_formatobj'] && ($instance = $master['_formatobj']->get_instance($instance_id))) {
|
||||
$this->events[$id] = $this->_to_driver_event($instance);
|
||||
$this->events[$id] = $this->_to_driver_event($instance, false, true, $master);
|
||||
}
|
||||
// check for match on the first instance already
|
||||
else if ($master['_instance'] && $master['_instance'] == $instance_id) {
|
||||
|
@ -305,13 +302,13 @@ class kolab_calendar extends kolab_storage_folder_api
|
|||
|
||||
$events = array();
|
||||
foreach ($this->storage->select($query) as $record) {
|
||||
$event = $this->_to_driver_event($record, !$virtual, false);
|
||||
$event = $this->_to_driver_event($record, !$virtual);
|
||||
|
||||
// remember seen categories
|
||||
if ($event['categories']) {
|
||||
$cat = is_array($event['categories']) ? $event['categories'][0] : $event['categories'];
|
||||
$this->categories[$cat]++;
|
||||
}
|
||||
}
|
||||
|
||||
// list events in requested time window
|
||||
if ($event['start'] <= $end && $event['end'] >= $start) {
|
||||
|
@ -354,7 +351,7 @@ class kolab_calendar extends kolab_storage_folder_api
|
|||
// add top-level exceptions (aka loose single occurrences)
|
||||
else if (is_array($record['exceptions'])) {
|
||||
foreach ($record['exceptions'] as $ex) {
|
||||
$component = $this->_to_driver_event($ex, false, false);
|
||||
$component = $this->_to_driver_event($ex, false, false, $record);
|
||||
if ($component['start'] <= $end && $component['end'] >= $start) {
|
||||
$events[] = $component;
|
||||
}
|
||||
|
@ -388,10 +385,6 @@ class kolab_calendar extends kolab_storage_folder_api
|
|||
return true;
|
||||
});
|
||||
|
||||
// Apply event-to-mail relations
|
||||
$config = kolab_storage_config::get_instance();
|
||||
$config->apply_links($events);
|
||||
|
||||
// avoid session race conditions that will loose temporary subscriptions
|
||||
$this->cal->rc->session->nowrite = true;
|
||||
|
||||
|
@ -462,8 +455,8 @@ class kolab_calendar extends kolab_storage_folder_api
|
|||
|
||||
//generate new event from RC input
|
||||
$object = $this->_from_driver_event($event);
|
||||
$saved = $this->storage->save($object, 'event');
|
||||
|
||||
$saved = $this->storage->save($object, 'event');
|
||||
|
||||
if (!$saved) {
|
||||
rcube::raise_error(array(
|
||||
'code' => 600, 'type' => 'php',
|
||||
|
@ -474,13 +467,11 @@ class kolab_calendar extends kolab_storage_folder_api
|
|||
}
|
||||
else {
|
||||
// save links in configuration.relation object
|
||||
if ($this->save_links($event['uid'], $links)) {
|
||||
$object['links'] = $links;
|
||||
}
|
||||
$this->save_links($event['uid'], $links);
|
||||
|
||||
$this->events = array($event['uid'] => $this->_to_driver_event($object, true));
|
||||
}
|
||||
|
||||
|
||||
return $saved;
|
||||
}
|
||||
|
||||
|
@ -503,7 +494,7 @@ class kolab_calendar extends kolab_storage_folder_api
|
|||
unset($event['links']);
|
||||
|
||||
$object = $this->_from_driver_event($event, $old);
|
||||
$saved = $this->storage->save($object, 'event', $old['uid']);
|
||||
$saved = $this->storage->save($object, 'event', $old['uid']);
|
||||
|
||||
if (!$saved) {
|
||||
rcube::raise_error(array(
|
||||
|
@ -514,9 +505,7 @@ class kolab_calendar extends kolab_storage_folder_api
|
|||
}
|
||||
else {
|
||||
// save links in configuration.relation object
|
||||
if ($this->save_links($event['uid'], $links)) {
|
||||
$object['links'] = $links;
|
||||
}
|
||||
$this->save_links($event['uid'], $links);
|
||||
|
||||
$updated = true;
|
||||
$this->events = array($event['uid'] => $this->_to_driver_event($object, true));
|
||||
|
@ -587,8 +576,14 @@ class kolab_calendar extends kolab_storage_folder_api
|
|||
*/
|
||||
protected function save_links($uid, $links)
|
||||
{
|
||||
// make sure we have a valid array
|
||||
if (empty($links)) {
|
||||
$links = array();
|
||||
}
|
||||
|
||||
$storage = kolab_storage_config::get_instance();
|
||||
return $storage->save_object_links($uid, (array) $links);
|
||||
$remove = array_diff($storage->get_object_links($uid), $links);
|
||||
return $storage->save_object_links($uid, $links, $remove);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -629,9 +624,9 @@ class kolab_calendar extends kolab_storage_folder_api
|
|||
if (is_array($event['recurrence']['EXCEPTIONS'])) {
|
||||
foreach ($event['recurrence']['EXCEPTIONS'] as $exception) {
|
||||
if (!$exception['_instance'])
|
||||
$exception['_instance'] = libcalendaring::recurrence_instance_identifier($exception);
|
||||
$exception['_instance'] = libcalendaring::recurrence_instance_identifier($exception, $event['allday']);
|
||||
|
||||
$rec_event = $this->_to_driver_event($exception, false, false);
|
||||
$rec_event = $this->_to_driver_event($exception, false, false, $event);
|
||||
$rec_event['id'] = $event['uid'] . '-' . $exception['_instance'];
|
||||
$rec_event['isexception'] = 1;
|
||||
|
||||
|
@ -692,7 +687,7 @@ class kolab_calendar extends kolab_storage_folder_api
|
|||
|
||||
// add to output if in range
|
||||
if (($event_start <= $end && $event_end >= $start) || ($event_id && $rec_id == $event_id)) {
|
||||
$rec_event = $this->_to_driver_event($next_event, false, false);
|
||||
$rec_event = $this->_to_driver_event($next_event, false, false, $event);
|
||||
$rec_event['_instance'] = $instance_id;
|
||||
$rec_event['_count'] = $i + 1;
|
||||
|
||||
|
@ -724,7 +719,7 @@ class kolab_calendar extends kolab_storage_folder_api
|
|||
/**
|
||||
* Convert from Kolab_Format to internal representation
|
||||
*/
|
||||
private function _to_driver_event($record, $noinst = false, $links = true)
|
||||
private function _to_driver_event($record, $noinst = false, $links = true, $master_event = null)
|
||||
{
|
||||
$record['calendar'] = $this->id;
|
||||
|
||||
|
@ -738,7 +733,7 @@ class kolab_calendar extends kolab_storage_folder_api
|
|||
}
|
||||
|
||||
// add instance identifier to first occurrence (master event)
|
||||
$recurrence_id_format = libcalendaring::recurrence_id_format($record);
|
||||
$recurrence_id_format = libcalendaring::recurrence_id_format($master_event ? $master_event : $record);
|
||||
if (!$noinst && $record['recurrence'] && !$record['recurrence_id'] && !$record['_instance']) {
|
||||
$record['_instance'] = $record['start']->format($recurrence_id_format);
|
||||
}
|
||||
|
@ -763,11 +758,25 @@ class kolab_calendar extends kolab_storage_folder_api
|
|||
private function _from_driver_event($event, $old = array())
|
||||
{
|
||||
// set current user as ORGANIZER
|
||||
$identity = $this->cal->rc->user->list_emails(true);
|
||||
if (empty($event['attendees']) && $identity['email'])
|
||||
$event['attendees'] = array(array('role' => 'ORGANIZER', 'name' => $identity['name'], 'email' => $identity['email']));
|
||||
if ($identity = $this->cal->rc->user->list_emails(true)) {
|
||||
$event['attendees'] = (array) $event['attendees'];
|
||||
$found = false;
|
||||
|
||||
$event['_owner'] = $identity['email'];
|
||||
// there can be only resources on attendees list (T1484)
|
||||
// let's check the existence of an organizer
|
||||
foreach ($event['attendees'] as $attendee) {
|
||||
if ($attendee['role'] == 'ORGANIZER') {
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$found) {
|
||||
$event['attendees'][] = array('role' => 'ORGANIZER', 'name' => $identity['name'], 'email' => $identity['email']);
|
||||
}
|
||||
|
||||
$event['_owner'] = $identity['email'];
|
||||
}
|
||||
|
||||
// remove EXDATE values if RDATE is given
|
||||
if (!empty($event['recurrence']['RDATE'])) {
|
||||
|
@ -792,7 +801,6 @@ class kolab_calendar extends kolab_storage_folder_api
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
// remove some internal properties which should not be saved
|
||||
unset($event['_savemode'], $event['_fromcalendar'], $event['_identity'], $event['_folder_id'],
|
||||
$event['recurrence_id'], $event['attachments'], $event['deleted_attachments'], $event['className']);
|
||||
|
|
|
@ -57,11 +57,12 @@ class kolab_driver extends calendar_driver
|
|||
require_once(dirname(__FILE__) . '/kolab_invitation_calendar.php');
|
||||
|
||||
$this->cal = $cal;
|
||||
$this->rc = $cal->rc;
|
||||
|
||||
$this->rc = $cal->rc;
|
||||
$this->_read_calendars();
|
||||
|
||||
$this->cal->register_action('push-freebusy', array($this, 'push_freebusy'));
|
||||
$this->cal->register_action('calendar-acl', array($this, 'calendar_acl'));
|
||||
|
||||
|
||||
$this->freebusy_trigger = $this->rc->config->get('calendar_freebusy_trigger', false);
|
||||
|
||||
if (kolab_storage::$version == '2.0') {
|
||||
|
@ -88,11 +89,11 @@ class kolab_driver extends calendar_driver
|
|||
|
||||
// get all folders that have "event" type, sorted by namespace/name
|
||||
$folders = kolab_storage::sort_folders(kolab_storage::get_folders('event') + kolab_storage::get_user_folders('event', true));
|
||||
|
||||
$this->calendars = array();
|
||||
|
||||
foreach ($folders as $folder) {
|
||||
if ($folder instanceof kolab_storage_folder_user) {
|
||||
$calendar = new kolab_user_calendar($folder, $this->cal);
|
||||
$calendar = new kolab_user_calendar($folder->name, $this->cal);
|
||||
$calendar->subscriptions = count($folder->children) > 0;
|
||||
}
|
||||
else {
|
||||
|
@ -119,12 +120,10 @@ class kolab_driver extends calendar_driver
|
|||
*/
|
||||
public function list_calendars($filter = 0, &$tree = null)
|
||||
{
|
||||
$this->_read_calendars();
|
||||
|
||||
// attempt to create a default calendar for this user
|
||||
if (!$this->has_writeable) {
|
||||
if ($this->create_calendar(array('name' => 'Calendar', 'color' => 'cc0000'))) {
|
||||
unset($this->calendars);
|
||||
unset($this->calendars);
|
||||
$this->_read_calendars();
|
||||
}
|
||||
}
|
||||
|
@ -163,8 +162,8 @@ class kolab_driver extends calendar_driver
|
|||
// special handling for user or virtual folders
|
||||
if ($cal instanceof kolab_storage_folder_user) {
|
||||
$calendars[$cal->id] = array(
|
||||
'id' => $cal->id,
|
||||
'name' => $fullname,
|
||||
'id' => $cal->id,
|
||||
'name' => kolab_storage::object_name($fullname),
|
||||
'listname' => $listname,
|
||||
'editname' => $cal->get_foldername(),
|
||||
'color' => $cal->get_color(),
|
||||
|
@ -288,8 +287,6 @@ class kolab_driver extends calendar_driver
|
|||
*/
|
||||
protected function filter_calendars($filter)
|
||||
{
|
||||
$this->_read_calendars();
|
||||
|
||||
$calendars = array();
|
||||
|
||||
$plugin = $this->rc->plugins->exec_hook('calendar_list_filter', array(
|
||||
|
@ -346,19 +343,14 @@ class kolab_driver extends calendar_driver
|
|||
*/
|
||||
public function get_calendar($id)
|
||||
{
|
||||
$this->_read_calendars();
|
||||
|
||||
// create calendar object if necesary
|
||||
if (!$this->calendars[$id]) {
|
||||
if (in_array($id, array(self::INVITATIONS_CALENDAR_PENDING, self::INVITATIONS_CALENDAR_DECLINED))) {
|
||||
$this->calendars[$id] = new kolab_invitation_calendar($id, $this->cal);
|
||||
}
|
||||
else if ($id !== self::BIRTHDAY_CALENDAR_ID) {
|
||||
$calendar = kolab_calendar::factory($id, $this->cal);
|
||||
if ($calendar->ready) {
|
||||
$this->calendars[$calendar->id] = $calendar;
|
||||
}
|
||||
}
|
||||
if (!$this->calendars[$id] && in_array($id, array(self::INVITATIONS_CALENDAR_PENDING, self::INVITATIONS_CALENDAR_DECLINED))) {
|
||||
$this->calendars[$id] = new kolab_invitation_calendar($id, $this->cal);
|
||||
}
|
||||
else if (!$this->calendars[$id] && $id !== self::BIRTHDAY_CALENDAR_ID) {
|
||||
$calendar = kolab_calendar::factory($id, $this->cal);
|
||||
if ($calendar->ready)
|
||||
$this->calendars[$calendar->id] = $calendar;
|
||||
}
|
||||
|
||||
return $this->calendars[$id];
|
||||
|
@ -603,12 +595,8 @@ class kolab_driver extends calendar_driver
|
|||
|
||||
$event = self::from_rcube_event($event);
|
||||
|
||||
if (!$event['calendar']) {
|
||||
$this->_read_calendars();
|
||||
$event['calendar'] = reset(array_keys($this->calendars));
|
||||
}
|
||||
|
||||
if ($storage = $this->get_calendar($event['calendar'])) {
|
||||
$cid = $event['calendar'] ? $event['calendar'] : reset(array_keys($this->calendars));
|
||||
if ($storage = $this->get_calendar($cid)) {
|
||||
// if this is a recurrence instance, append as exception to an already existing object for this UID
|
||||
if (!empty($event['recurrence_date']) && ($master = $storage->get_event($event['uid']))) {
|
||||
self::add_exception($master, $event);
|
||||
|
@ -900,7 +888,7 @@ class kolab_driver extends calendar_driver
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($success && $this->freebusy_trigger)
|
||||
$this->rc->output->command('plugin.ping_url', array('action' => 'calendar/push-freebusy', 'source' => $storage->id));
|
||||
|
||||
|
@ -1027,7 +1015,7 @@ class kolab_driver extends calendar_driver
|
|||
|
||||
// copy attachment metadata to new event
|
||||
$event = self::from_rcube_event($event, $master);
|
||||
|
||||
|
||||
// remove recurrence exceptions on re-scheduling
|
||||
if ($reschedule) {
|
||||
unset($event['recurrence']['EXCEPTIONS'], $event['exceptions'], $master['recurrence']['EXDATE']);
|
||||
|
@ -1141,21 +1129,21 @@ class kolab_driver extends calendar_driver
|
|||
// use start date from master but try to be smart on time or duration changes
|
||||
$old_start_date = $old['start']->format('Y-m-d');
|
||||
$old_start_time = $old['allday'] ? '' : $old['start']->format('H:i');
|
||||
$old_duration = $old['end']->format('U') - $old['start']->format('U');
|
||||
|
||||
$old_duration = self::event_duration($old['start'], $old['end'], $old['allday']);
|
||||
|
||||
$new_start_date = $event['start']->format('Y-m-d');
|
||||
$new_start_time = $event['allday'] ? '' : $event['start']->format('H:i');
|
||||
$new_duration = $event['end']->format('U') - $event['start']->format('U');
|
||||
|
||||
$new_duration = self::event_duration($event['start'], $event['end'], $event['allday']);
|
||||
|
||||
$diff = $old_start_date != $new_start_date || $old_start_time != $new_start_time || $old_duration != $new_duration;
|
||||
$date_shift = $old['start']->diff($event['start']);
|
||||
|
||||
|
||||
// shifted or resized
|
||||
if ($diff && ($old_start_date == $new_start_date || $old_duration == $new_duration)) {
|
||||
$event['start'] = $master['start']->add($date_shift);
|
||||
$event['end'] = clone $event['start'];
|
||||
$event['end']->add(new DateInterval('PT'.$new_duration.'S'));
|
||||
|
||||
$event['end']->add(new DateInterval($new_duration));
|
||||
|
||||
// remove fixed weekday, will be re-set to the new weekday in kolab_calendar::update_event()
|
||||
if ($old_start_date != $new_start_date) {
|
||||
if (strlen($event['recurrence']['BYDAY']) == 2)
|
||||
|
@ -1173,6 +1161,7 @@ class kolab_driver extends calendar_driver
|
|||
// when saving an instance in 'all' mode, copy recurrence exceptions over
|
||||
if ($old['recurrence_id']) {
|
||||
$event['recurrence']['EXCEPTIONS'] = $master['recurrence']['EXCEPTIONS'];
|
||||
$event['recurrence']['EXDATE'] = $master['recurrence']['EXDATE'];
|
||||
}
|
||||
else if ($master['_instance']) {
|
||||
$event['_instance'] = $master['_instance'];
|
||||
|
@ -1225,10 +1214,23 @@ class kolab_driver extends calendar_driver
|
|||
|
||||
if ($success && $this->freebusy_trigger)
|
||||
$this->rc->output->command('plugin.ping_url', array('action' => 'calendar/push-freebusy', 'source' => $storage->id));
|
||||
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate event duration, returns string in DateInterval format
|
||||
*/
|
||||
protected static function event_duration($start, $end, $allday = false)
|
||||
{
|
||||
if ($allday) {
|
||||
$diff = $start->diff($end);
|
||||
return 'P' . $diff->days . 'D';
|
||||
}
|
||||
|
||||
return 'PT' . ($end->format('U') - $start->format('U')) . 'S';
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the current change affects scheduling and reset attendee status accordingly
|
||||
*/
|
||||
|
@ -1361,7 +1363,7 @@ class kolab_driver extends calendar_driver
|
|||
}
|
||||
|
||||
if (!$event['_instance'] && is_a($event['recurrence_date'], 'DateTime')) {
|
||||
$event['_instance'] = libcalendaring::recurrence_instance_identifier($event);
|
||||
$event['_instance'] = libcalendaring::recurrence_instance_identifier($event, $master['allday']);
|
||||
}
|
||||
|
||||
if (!is_array($master['exceptions']) && is_array($master['recurrence']['EXCEPTIONS'])) {
|
||||
|
@ -1466,10 +1468,8 @@ class kolab_driver extends calendar_driver
|
|||
{
|
||||
if ($calendars && is_string($calendars))
|
||||
$calendars = explode(',', $calendars);
|
||||
else if (!$calendars) {
|
||||
$this->_read_calendars();
|
||||
else if (!$calendars)
|
||||
$calendars = array_keys($this->calendars);
|
||||
}
|
||||
|
||||
$query = array();
|
||||
if ($modifiedsince)
|
||||
|
@ -1514,10 +1514,8 @@ class kolab_driver extends calendar_driver
|
|||
|
||||
if ($calendars && is_string($calendars))
|
||||
$calendars = explode(',', $calendars);
|
||||
else if (!$calendars) {
|
||||
$this->_read_calendars();
|
||||
else if (!$calendars)
|
||||
$calendars = array_keys($this->calendars);
|
||||
}
|
||||
|
||||
foreach ($calendars as $cid) {
|
||||
if ($storage = $this->get_calendar($cid)) {
|
||||
|
@ -1555,9 +1553,6 @@ class kolab_driver extends calendar_driver
|
|||
|
||||
$candidates = array();
|
||||
$query = array(array('tags', '=', 'x-has-alarms'));
|
||||
|
||||
$this->_read_calendars();
|
||||
|
||||
foreach ($this->calendars as $cid => $calendar) {
|
||||
// skip calendars with alarms disabled
|
||||
if (!$calendar->alarms || ($calendars && !in_array($cid, $calendars)))
|
||||
|
@ -2314,8 +2309,6 @@ class kolab_driver extends calendar_driver
|
|||
return parent::calendar_form($action, $calendar, $formfields);
|
||||
}
|
||||
|
||||
$this->_read_calendars();
|
||||
|
||||
if ($calendar['id'] && ($cal = $this->calendars[$calendar['id']])) {
|
||||
$folder = $cal->get_realname(); // UTF7
|
||||
$color = $cal->get_color();
|
||||
|
@ -2449,7 +2442,7 @@ class kolab_driver extends calendar_driver
|
|||
if (is_array($form['content']) && !empty($form['content'])) {
|
||||
$table = new html_table(array('cols' => 2));
|
||||
foreach ($form['content'] as $col => $colprop) {
|
||||
$label = !empty($colprop['label']) ? $colprop['label'] : $this->cal->gettext($col);
|
||||
$label = !empty($colprop['label']) ? $colprop['label'] : $this->rc->gettext($col);
|
||||
|
||||
$table->add('title', html::label($colprop['id'], rcube::Q($label)));
|
||||
$table->add(null, $colprop['value']);
|
||||
|
|
|
@ -34,7 +34,6 @@ class kolab_invitation_calendar
|
|||
public $categories = array();
|
||||
public $name = 'Invitations';
|
||||
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
|
@ -63,6 +62,7 @@ class kolab_invitation_calendar
|
|||
$this->alarms = $prefs[$this->id]['showalarms'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Getter for a nice and human readable name for this calendar
|
||||
*
|
||||
|
@ -73,6 +73,7 @@ class kolab_invitation_calendar
|
|||
return $this->name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Getter for the IMAP folder owner
|
||||
*
|
||||
|
@ -83,6 +84,7 @@ class kolab_invitation_calendar
|
|||
return $this->cal->rc->get_user_name();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -91,6 +93,7 @@ class kolab_invitation_calendar
|
|||
return $this->get_name();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Getter for the name of the namespace to which the IMAP folder belongs
|
||||
*
|
||||
|
@ -101,6 +104,7 @@ class kolab_invitation_calendar
|
|||
return 'x-special';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Getter for the top-end calendar folder name (not the entire path)
|
||||
*
|
||||
|
@ -167,6 +171,7 @@ class kolab_invitation_calendar
|
|||
return $prop['id'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Getter for a single event object
|
||||
*/
|
||||
|
@ -197,7 +202,7 @@ class kolab_invitation_calendar
|
|||
else {
|
||||
$cal = null;
|
||||
foreach (kolab_storage::list_folders('', '*', 'event', null) as $foldername) {
|
||||
$cal = $this->_get_calendar($foldername);
|
||||
$cal = new kolab_calendar($foldername, $this->cal);
|
||||
if ($cal->ready && $cal->storage && $cal->get_event($event['id'])) {
|
||||
break;
|
||||
}
|
||||
|
@ -211,6 +216,7 @@ class kolab_invitation_calendar
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param integer Event's new start (unix timestamp)
|
||||
* @param integer Event's new end (unix timestamp)
|
||||
|
@ -233,7 +239,7 @@ class kolab_invitation_calendar
|
|||
// aggregate events from all calendar folders
|
||||
$events = array();
|
||||
foreach (kolab_storage::list_folders('', '*', 'event', null) as $foldername) {
|
||||
$cal = $this->_get_calendar($foldername);
|
||||
$cal = new kolab_calendar($foldername, $this->cal);
|
||||
if ($cal->get_namespace() == 'other')
|
||||
continue;
|
||||
|
||||
|
@ -287,7 +293,7 @@ class kolab_invitation_calendar
|
|||
// aggregate counts from all calendar folders
|
||||
$count = 0;
|
||||
foreach (kolab_storage::list_folders('', '*', 'event', null) as $foldername) {
|
||||
$cal = $this->_get_calendar($foldername);
|
||||
$cal = new kolab_calendar($foldername, $this->cal);
|
||||
if ($cal->get_namespace() == 'other')
|
||||
continue;
|
||||
|
||||
|
@ -297,15 +303,6 @@ class kolab_invitation_calendar
|
|||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get calendar object instance (that maybe already initialized)
|
||||
*/
|
||||
private function _get_calendar($folder_name)
|
||||
{
|
||||
$id = kolab_storage::folder_id($folder_name, true);
|
||||
return $this->cal->driver->get_calendar($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to modify some event properties
|
||||
*/
|
||||
|
@ -321,6 +318,7 @@ class kolab_invitation_calendar
|
|||
return $event;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new event record
|
||||
*
|
||||
|
@ -339,6 +337,7 @@ class kolab_invitation_calendar
|
|||
* @see calendar_driver::new_event()
|
||||
* @return boolean True on success, False on error
|
||||
*/
|
||||
|
||||
public function update_event($event, $exception_id = null)
|
||||
{
|
||||
// forward call to the actual storage folder
|
||||
|
@ -373,4 +372,6 @@ class kolab_invitation_calendar
|
|||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*
|
||||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2014-2016, Kolab Systems AG <contact@kolabsys.com>
|
||||
* Copyright (C) 2014-2015, 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
|
||||
|
@ -45,12 +45,8 @@ class kolab_user_calendar extends kolab_calendar
|
|||
$this->userdata = $user_or_folder;
|
||||
$this->storage = new kolab_storage_folder_user($this->userdata['kolabtargetfolder'], '', $this->userdata);
|
||||
}
|
||||
else if ($user_or_folder instanceof kolab_storage_folder_user) {
|
||||
$this->storage = $user_or_folder;
|
||||
$this->userdata = $this->storage->ldaprec;
|
||||
}
|
||||
else { // get user record from LDAP
|
||||
$this->storage = new kolab_storage_folder_user($user_or_folder);
|
||||
$this->storage = new kolab_storage_folder_user($user_or_folder);
|
||||
$this->userdata = $this->storage->ldaprec;
|
||||
}
|
||||
|
||||
|
@ -61,7 +57,7 @@ class kolab_user_calendar extends kolab_calendar
|
|||
// ID is derrived from the user's kolabtargetfolder attribute
|
||||
$this->id = kolab_storage::folder_id($this->userdata['kolabtargetfolder'], true);
|
||||
$this->imap_folder = $this->userdata['kolabtargetfolder'];
|
||||
$this->name = $this->storage->name;
|
||||
$this->name = $this->storage->get_name();
|
||||
$this->parent = ''; // user calendars are top level
|
||||
|
||||
// user-specific alarms settings win
|
||||
|
@ -71,6 +67,7 @@ class kolab_user_calendar extends kolab_calendar
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Getter for a nice and human readable name for this calendar
|
||||
*
|
||||
|
@ -81,6 +78,7 @@ class kolab_user_calendar extends kolab_calendar
|
|||
return $this->userdata['displayname'] ?: ($this->userdata['name'] ?: $this->userdata['mail']);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Getter for the IMAP folder owner
|
||||
*
|
||||
|
@ -91,6 +89,7 @@ class kolab_user_calendar extends kolab_calendar
|
|||
return $this->userdata['mail'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -99,6 +98,7 @@ class kolab_user_calendar extends kolab_calendar
|
|||
return trim($this->userdata['displayname'] . '; ' . $this->userdata['mail'], '; ');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Getter for the name of the namespace to which the IMAP folder belongs
|
||||
*
|
||||
|
@ -109,6 +109,7 @@ class kolab_user_calendar extends kolab_calendar
|
|||
return 'other user';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Getter for the top-end calendar folder name (not the entire path)
|
||||
*
|
||||
|
@ -163,6 +164,7 @@ class kolab_user_calendar extends kolab_calendar
|
|||
return $prop['id'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Getter for a single event object
|
||||
*/
|
||||
|
@ -317,7 +319,7 @@ class kolab_user_calendar extends kolab_calendar
|
|||
'X-OUT-OF-OFFICE' => $this->cal->gettext('availoutofoffice'),
|
||||
);
|
||||
|
||||
// rcube::console('_fetch_freebusy', kolab_storage::get_freebusy_url($this->userdata['mail']), $fbdata);
|
||||
// rcmail::console('_fetch_freebusy', kolab_storage::get_freebusy_url($this->userdata['mail']), $fbdata);
|
||||
|
||||
// parse free-busy information
|
||||
$count = 0;
|
||||
|
@ -369,6 +371,7 @@ class kolab_user_calendar extends kolab_calendar
|
|||
return sprintf('%s/%s', $event['start']->format('U'), is_object($event['end']) ? $event['end']->format('U') : '0');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new event record
|
||||
*
|
||||
|
@ -387,6 +390,7 @@ class kolab_user_calendar extends kolab_calendar
|
|||
* @see calendar_driver::new_event()
|
||||
* @return boolean True on success, False on error
|
||||
*/
|
||||
|
||||
public function update_event($event, $exception_id = null)
|
||||
{
|
||||
return false;
|
||||
|
|
|
@ -75,7 +75,7 @@ class calendar_recurrence extends libcalendaring_recurrence
|
|||
}
|
||||
|
||||
$next['recurrence_date'] = clone $next_start;
|
||||
$next['_instance'] = libcalendaring::recurrence_instance_identifier($next);
|
||||
$next['_instance'] = libcalendaring::recurrence_instance_identifier($next, $this->event['allday']);
|
||||
|
||||
unset($next['_formatobj']);
|
||||
|
||||
|
|
|
@ -177,7 +177,7 @@ class calendar_ui
|
|||
{
|
||||
$color = $prop['color'];
|
||||
$class = 'cal-' . asciiwords($id, true);
|
||||
$css .= "li .$class, #eventshow .$class { color: #$color; }\n";
|
||||
$css .= "li .$class, #eventshow .$class { color: #$color }\n";
|
||||
|
||||
if ($mode != 1) {
|
||||
if ($mode == 3) {
|
||||
|
@ -189,7 +189,7 @@ class calendar_ui
|
|||
$css .= ".fc-event-$class, ";
|
||||
$css .= ".fc-event-$class .fc-event-inner {";
|
||||
}
|
||||
if (!$prop['printmode'])
|
||||
if (!$attrib['printmode'])
|
||||
$css .= " background-color: #$color;";
|
||||
if ($mode % 2 == 0)
|
||||
$css .= " border-color: #$color;";
|
||||
|
@ -235,6 +235,7 @@ class calendar_ui
|
|||
);
|
||||
}
|
||||
|
||||
$this->rc->output->set_env('source', rcube_utils::get_input_value('source', rcube_utils::INPUT_GET));
|
||||
$this->rc->output->set_env('calendars', $jsenv);
|
||||
$this->rc->output->add_gui_object('calendarslist', $attrib['id']);
|
||||
|
||||
|
@ -410,7 +411,7 @@ class calendar_ui
|
|||
$select->add('---', '');
|
||||
$select->add($this->cal->gettext('status-confirmed'), 'CONFIRMED');
|
||||
$select->add($this->cal->gettext('status-cancelled'), 'CANCELLED');
|
||||
//$select->add($this->cal->gettext('tentative'), 'TENTATIVE');
|
||||
$select->add($this->cal->gettext('status-tentative'), 'TENTATIVE');
|
||||
return $select->show(null);
|
||||
}
|
||||
|
||||
|
@ -585,7 +586,7 @@ class calendar_ui
|
|||
|
||||
$checkbox = new html_checkbox(array('name' => 'attachments', 'id' => 'event-export-attachments', 'value' => 1));
|
||||
$html .= html::div('form-section',
|
||||
html::label('event-export-attachments', $this->cal->gettext('exportattachments')) .
|
||||
html::label('event-export-range', $this->cal->gettext('exportattachments')) .
|
||||
$checkbox->show(1)
|
||||
);
|
||||
|
||||
|
@ -607,7 +608,7 @@ class calendar_ui
|
|||
$attrib['id'] = 'rcmUploadForm';
|
||||
|
||||
// Get max filesize, enable upload progress bar
|
||||
$max_filesize = $this->rc->upload_init();
|
||||
$max_filesize =$this->rc->upload_init();
|
||||
|
||||
$button = new html_inputfield(array('type' => 'button'));
|
||||
$input = new html_inputfield(array(
|
||||
|
@ -616,7 +617,7 @@ class calendar_ui
|
|||
|
||||
return html::div($attrib,
|
||||
html::div(null, $input->show()) .
|
||||
html::div('buttons', $button->show($this->rc->gettext('upload'), array('class' => 'button mainaction',
|
||||
html::div('formbuttons', $button->show($this->rc->gettext('upload'), array('class' => 'button mainaction',
|
||||
'onclick' => rcmail_output::JS_OBJECT_NAME . ".upload_file(this.form)"))) .
|
||||
html::div('hint', $this->rc->gettext(array('name' => 'maxuploadsize', 'vars' => array('size' => $max_filesize))))
|
||||
);
|
||||
|
|
|
@ -82,6 +82,7 @@ $labels['mystatus'] = 'My status';
|
|||
$labels['status'] = 'Status';
|
||||
$labels['status-confirmed'] = 'Confirmed';
|
||||
$labels['status-cancelled'] = 'Cancelled';
|
||||
$labels['status-tentative'] = 'Tentative';
|
||||
$labels['priority'] = 'Priority';
|
||||
$labels['sensitivity'] = 'Privacy';
|
||||
$labels['public'] = 'public';
|
||||
|
@ -164,8 +165,6 @@ $labels['availbusy'] = 'Busy';
|
|||
$labels['availunknown'] = 'Unknown';
|
||||
$labels['availtentative'] = 'Tentative';
|
||||
$labels['availoutofoffice'] = 'Out of Office';
|
||||
$labels['delegatedto'] = 'Delegated to: ';
|
||||
$labels['delegatedfrom'] = 'Delegated from: ';
|
||||
$labels['scheduletime'] = 'Find availability';
|
||||
$labels['sendinvitations'] = 'Send invitations';
|
||||
$labels['sendnotifications'] = 'Notify participants about modifications';
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Localizations for Kolab Calendar plugin
|
||||
*
|
||||
|
@ -6,6 +7,10 @@
|
|||
*
|
||||
* For translation see https://www.transifex.com/projects/p/kolab/resource/calendar/
|
||||
*/
|
||||
|
||||
$labels = array();
|
||||
|
||||
// preferences
|
||||
$labels['default_view'] = 'Oletusnäkymä';
|
||||
$labels['time_format'] = 'Aikamuoto';
|
||||
$labels['timeslots'] = 'Ajankohdat per tunti';
|
||||
|
@ -18,6 +23,8 @@ $labels['defaultcalendar'] = 'Luo uudet tapahtumat kohteeseen';
|
|||
$labels['eventcoloring'] = 'Tapahtuman väritys';
|
||||
$labels['coloringmode0'] = 'Kalenterin mukaan';
|
||||
$labels['coloringmode1'] = 'Luokan mukaan';
|
||||
$labels['coloringmode2'] = 'Calendar for outline, category for content';
|
||||
$labels['coloringmode3'] = 'Category for outline, calendar for content';
|
||||
$labels['afternothing'] = 'Älä tee mitään';
|
||||
$labels['aftertrash'] = 'Siirrä roskakoriin';
|
||||
$labels['afterdelete'] = 'Poista viesti';
|
||||
|
@ -25,6 +32,8 @@ $labels['afterflagdeleted'] = 'Merkitse poistettavaksi';
|
|||
$labels['aftermoveto'] = 'Siirrä...';
|
||||
$labels['itipoptions'] = 'Tapahtuman kutsut';
|
||||
$labels['afteraction'] = 'Kun kutsu tai päivitysviesti on käsitelty';
|
||||
|
||||
// calendar
|
||||
$labels['calendar'] = 'Kalenteri';
|
||||
$labels['calendars'] = 'Kalenterit';
|
||||
$labels['category'] = 'Luokka';
|
||||
|
@ -113,6 +122,8 @@ $labels['invitationspending'] = 'Odottavat kutsut';
|
|||
$labels['invitationsdeclined'] = 'Torjutut kutsut';
|
||||
$labels['changepartstat'] = 'Muuta osallistujan tilaa';
|
||||
$labels['rsvpcomment'] = 'Kutsuteksti';
|
||||
|
||||
// agenda view
|
||||
$labels['listrange'] = 'Näytettävä aikaväli';
|
||||
$labels['listsections'] = 'Jaa osiin:';
|
||||
$labels['smartsections'] = 'Älykkäät osiot';
|
||||
|
@ -127,9 +138,13 @@ $labels['nextmonth'] = 'Ensi kuussa';
|
|||
$labels['weekofyear'] = 'Viikko';
|
||||
$labels['pastevents'] = 'Menneet';
|
||||
$labels['futureevents'] = 'Tulevat';
|
||||
|
||||
// alarm/reminder settings
|
||||
$labels['showalarms'] = 'Näytä muistutukset';
|
||||
$labels['defaultalarmtype'] = 'Muistutuksen oletusasetus';
|
||||
$labels['defaultalarmoffset'] = 'Muistutuksen oletusaika';
|
||||
|
||||
// attendees
|
||||
$labels['attendee'] = 'Osallistujat';
|
||||
$labels['role'] = 'Rooli';
|
||||
$labels['availability'] = 'Saatavilla';
|
||||
|
@ -169,17 +184,22 @@ $labels['eventupdatesubjectempty'] = 'Sinua koskeva tapahtuma on päivitetty';
|
|||
$labels['eventupdatemailbody'] = "*\$title*\n\nMilloin: \$date\n\nKutsutut: \$attendees\n\nOhessa iCalendar -tiedosto mistä löytyvät kaikki päivitetyn tapahtuman yksityistiedot. Voit tuoda tämän tiedoston kalenteriohjelmaasi.";
|
||||
$labels['eventcancelsubject'] = '"$title" on peruttu';
|
||||
$labels['eventcancelmailbody'] = "*\$title*\n\nMilloin: \$date\n\nKutsutut: \$attendees\n\nTämä tapahtuma on peruttu \$organizer toimesta.\n\nLöydät liitteenä iCalendar -tiedoston tapahtuman päivitetyin tiedoin.";
|
||||
|
||||
// invitation handling (overrides labels from libcalendaring)
|
||||
$labels['itipobjectnotfound'] = 'Viestissä mainittua tapahtumaa ei löydy kalenteristasi.';
|
||||
|
||||
$labels['itipmailbodyaccepted'] = "\$sender on hyväksynyt kutsun seuraavaan tapahtumaan:\n\n*\$title*\n\nMilloin: \$date\n\nKutsutut: \$attendees";
|
||||
$labels['itipmailbodytentative'] = "\$sender on alustavasti hyväksynyt kutsun seuraavaan tapahtumaan:\n\n*\$title*\n\nMilloin: \$date\n\nKutsutut: \$attendees";
|
||||
$labels['itipmailbodydeclined'] = "\$sender on hylännyt kutsun seuraavaan tapahtumaan:\n\n*\$title*\n\nMilloin: \$date\n\nKutsutut: \$attendees";
|
||||
$labels['itipmailbodycancel'] = "\$sender on hylännyt osallistumisesi tapahtumaan:\n\n*\$title*\n\nMilloin: \$date";
|
||||
$labels['itipmailbodydelegated'] = "\$sender on delegoinut osallistumisensa seuraavaan tapahtumaan:\n\n*\$title*\n\nAjankohta: \$date";
|
||||
$labels['itipmailbodydelegatedto'] = "\$sender on delegoinut osallistumisensa sinulle seuraavaan tapahtumaan:\n\n*\$title*\n\nAjankohta: \$date";
|
||||
|
||||
$labels['itipdeclineevent'] = 'Haluatko perua kutsun tähän tapahtumaan?';
|
||||
$labels['declinedeleteconfirm'] = 'Haluatko poistaa tämän hylätyn tapahtuman kalenteristasi?';
|
||||
$labels['itipcomment'] = 'Kutsun/herätteen kommentit';
|
||||
$labels['itipcommenttitle'] = 'Tämä kommentti liitetään osallistujille lähetettävään kutsuun/heräteviestiin';
|
||||
|
||||
$labels['notanattendee'] = 'Sinua ei ole määritetty tapahtuman osanottajaksi';
|
||||
$labels['eventcancelled'] = 'Tapahtuma on peruttu';
|
||||
$labels['saveincalendar'] = 'tallennuskohde';
|
||||
|
@ -188,6 +208,8 @@ $labels['savetocalendar'] = 'Tallenna kalenteriin';
|
|||
$labels['openpreview'] = 'Tarkista kalenteri';
|
||||
$labels['noearlierevents'] = 'Ei aiempia tapahtumia';
|
||||
$labels['nolaterevents'] = 'Ei myöhempiä tapahtumia';
|
||||
|
||||
// resources
|
||||
$labels['resource'] = 'Resurssi';
|
||||
$labels['addresource'] = 'Varaa resurssi';
|
||||
$labels['findresources'] = 'Etsi resursseja';
|
||||
|
@ -195,12 +217,16 @@ $labels['resourcedetails'] = 'Tiedot';
|
|||
$labels['resourceavailability'] = 'Saatavuus';
|
||||
$labels['resourceowner'] = 'Omistaja';
|
||||
$labels['resourceadded'] = 'Resurssi on liitetty tapahtumaasi';
|
||||
|
||||
// event dialog tabs
|
||||
$labels['tabsummary'] = 'Yhteenveto';
|
||||
$labels['tabrecurrence'] = 'Toistuminen';
|
||||
$labels['tabattendees'] = 'Osallistujat';
|
||||
$labels['tabresources'] = 'Resurssit';
|
||||
$labels['tabattachments'] = 'Liitteet';
|
||||
$labels['tabsharing'] = 'Jakaminen';
|
||||
|
||||
// messages
|
||||
$labels['deleteobjectconfirm'] = 'Haluatko varmasti poistaa tämän tapahtuman?';
|
||||
$labels['deleteventconfirm'] = 'Haluatko varmasti poistaa tämän tapahtuman?';
|
||||
$labels['deletecalendarconfirm'] = 'Haluatko varmasti poistaa tämän kalenterin ja kaikki sen tapahtumat?';
|
||||
|
@ -230,6 +256,7 @@ $labels['importsuccess'] = '$nr tapahtumaa tuotiin onnistuneesti';
|
|||
$labels['importnone'] = 'Tuotavaksi tarkoitettuja tapahtumia ei löytynyt';
|
||||
$labels['importerror'] = 'Tuotaessa tapahtui virhe';
|
||||
$labels['aclnorights'] = 'Sinulla ei ole ylläpitäjän oikeuksia tähän kalenteriin.';
|
||||
|
||||
$labels['changeeventconfirm'] = 'Vaihda tapahtuma';
|
||||
$labels['removeeventconfirm'] = 'Poista tapahtuma';
|
||||
$labels['changerecurringeventwarning'] = 'Tämä on tuistuva tapahtuma. Haluatko muokata vain tätä ajankohtaa, tätä ja tulevia tapahtuman ajankohtia, kaikkia tapahtuman ajankohtia vai tallentaa kokonaan uutena tapahtumana? ';
|
||||
|
@ -239,12 +266,16 @@ $labels['currentevent'] = 'Nykyinen';
|
|||
$labels['futurevents'] = 'Tulevat';
|
||||
$labels['allevents'] = 'Kaikki';
|
||||
$labels['saveasnew'] = 'Tallenna uutena';
|
||||
|
||||
// birthdays calendar
|
||||
$labels['birthdays'] = 'Syntymäpäivät';
|
||||
$labels['birthdayscalendar'] = 'Syntymäpäivät kalenteri';
|
||||
$labels['displaybirthdayscalendar'] = 'Näytä syntymäpäivät kalenterissa';
|
||||
$labels['birthdayscalendarsources'] = 'Näistä osoitekirjoista';
|
||||
$labels['birthdayeventtitle'] = 'Syntymäpäivä: $name';
|
||||
$labels['birthdayage'] = 'Ikä $age';
|
||||
|
||||
// history dialog
|
||||
$labels['objectchangelog'] = 'Muutoshistoria';
|
||||
$labels['objectdiff'] = 'Muutokset versiosta $rev1 versioon $rev2';
|
||||
$labels['objectnotfound'] = 'Tapahtuman tietojen lataaminen epäonnistui';
|
||||
|
@ -253,6 +284,9 @@ $labels['objectdiffnotavailable'] = 'Valituille versioille ei ole mahdollista te
|
|||
$labels['revisionrestoreconfirm'] = 'Haluatko varmasti palauttaa tämän tapahtuman version $rev? Nykyinen tapahtuma korvataan vanhalla versiolla.';
|
||||
$labels['objectrestoresuccess'] = 'Versio $rev palautettu onnistuneesti';
|
||||
$labels['objectrestoreerror'] = 'Vanhan version palauttaminen epäonnistui';
|
||||
|
||||
|
||||
// (hidden) titles and labels for accessibility annotations
|
||||
$labels['arialabelminical'] = 'Kalenterin ajankohdan valinta';
|
||||
$labels['arialabelcalendarview'] = 'Kalenterinäkymä';
|
||||
$labels['arialabelsearchform'] = 'Tapahtumahaun lomake';
|
||||
|
@ -263,4 +297,5 @@ $labels['arialabeleventattendees'] = 'Tapahtuman osallistujalista';
|
|||
$labels['arialabeleventresources'] = 'Tapahtuman resurssilista';
|
||||
$labels['arialabelresourcesearchform'] = 'Resurssien hakulomake';
|
||||
$labels['arialabelresourceselection'] = 'Saatavilla olevat resurssit';
|
||||
|
||||
?>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Localizations for Kolab Calendar plugin
|
||||
*
|
||||
|
@ -6,6 +7,10 @@
|
|||
*
|
||||
* For translation see https://www.transifex.com/projects/p/kolab/resource/calendar/
|
||||
*/
|
||||
|
||||
$labels = array();
|
||||
|
||||
// preferences
|
||||
$labels['default_view'] = 'Prednastavené zobrazenie';
|
||||
$labels['time_format'] = 'Formát času';
|
||||
$labels['timeslots'] = 'Časových úsekov za hodinu';
|
||||
|
@ -16,12 +21,19 @@ $labels['add_category'] = 'Pridať kategóriu';
|
|||
$labels['remove_category'] = 'Odstrániť kategóriu';
|
||||
$labels['defaultcalendar'] = 'Vytvoriť novú udalosť v';
|
||||
$labels['eventcoloring'] = 'Farba udalosti';
|
||||
$labels['coloringmode0'] = 'According to calendar';
|
||||
$labels['coloringmode1'] = 'According to category';
|
||||
$labels['coloringmode2'] = 'Calendar for outline, category for content';
|
||||
$labels['coloringmode3'] = 'Category for outline, calendar for content';
|
||||
$labels['afternothing'] = 'Neurobiť nič';
|
||||
$labels['aftertrash'] = 'Presunúť do koša';
|
||||
$labels['afterdelete'] = 'Vymazať správu';
|
||||
$labels['afterflagdeleted'] = 'Označiť ako vymazané';
|
||||
$labels['aftermoveto'] = 'Presunúť do...';
|
||||
$labels['itipoptions'] = 'Pozvánky na udalosť';
|
||||
$labels['afteraction'] = 'After an invitation or update message is processed';
|
||||
|
||||
// calendar
|
||||
$labels['calendar'] = 'Kalendár';
|
||||
$labels['calendars'] = 'Kalendáre';
|
||||
$labels['category'] = 'Kategória';
|
||||
|
@ -71,12 +83,219 @@ $labels['status'] = 'Stav';
|
|||
$labels['status-confirmed'] = 'Potvrdené';
|
||||
$labels['status-cancelled'] = 'Zrušené';
|
||||
$labels['priority'] = 'Priorita';
|
||||
$labels['sensitivity'] = 'Privacy';
|
||||
$labels['public'] = 'public';
|
||||
$labels['private'] = 'private';
|
||||
$labels['confidential'] = 'confidential';
|
||||
$labels['links'] = 'Reference';
|
||||
$labels['alarms'] = 'Reminder';
|
||||
$labels['comment'] = 'Comment';
|
||||
$labels['created'] = 'Created';
|
||||
$labels['changed'] = 'Last Modified';
|
||||
$labels['unknown'] = 'Unknown';
|
||||
$labels['eventoptions'] = 'Options';
|
||||
$labels['generated'] = 'generated at';
|
||||
$labels['eventhistory'] = 'History';
|
||||
$labels['removelink'] = 'Remove email reference';
|
||||
$labels['printdescriptions'] = 'Print descriptions';
|
||||
$labels['parentcalendar'] = 'Insert inside';
|
||||
$labels['searchearlierdates'] = '« Search for earlier events';
|
||||
$labels['searchlaterdates'] = 'Search for later events »';
|
||||
$labels['andnmore'] = '$nr more...';
|
||||
$labels['togglerole'] = 'Click to toggle role';
|
||||
$labels['createfrommail'] = 'Save as event';
|
||||
$labels['importevents'] = 'Import events';
|
||||
$labels['importrange'] = 'Udalosti z';
|
||||
$labels['onemonthback'] = '1 month back';
|
||||
$labels['nmonthsback'] = '$nr months back';
|
||||
$labels['showurl'] = 'Show calendar URL';
|
||||
$labels['showurldescription'] = 'Use the following address to access (read only) your calendar from other applications. You can copy and paste this into any calendar software that supports the iCal format.';
|
||||
$labels['caldavurldescription'] = 'Copy this address to a <a href="http://en.wikipedia.org/wiki/CalDAV" target="_blank">CalDAV</a> client application (e.g. Evolution or Mozilla Thunderbird) to fully synchronize this specific calendar with your computer or mobile device.';
|
||||
$labels['findcalendars'] = 'Find calendars...';
|
||||
$labels['searchterms'] = 'Search terms';
|
||||
$labels['calsearchresults'] = 'Available Calendars';
|
||||
$labels['calendarsubscribe'] = 'List permanently';
|
||||
$labels['nocalendarsfound'] = 'No calendars found';
|
||||
$labels['nrcalendarsfound'] = '$nr calendars found';
|
||||
$labels['quickview'] = 'View only this calendar';
|
||||
$labels['invitationspending'] = 'Pending invitations';
|
||||
$labels['invitationsdeclined'] = 'Declined invitations';
|
||||
$labels['changepartstat'] = 'Change participant status';
|
||||
$labels['rsvpcomment'] = 'Invitation text';
|
||||
|
||||
// agenda view
|
||||
$labels['listrange'] = 'Range to display:';
|
||||
$labels['listsections'] = 'Divide into:';
|
||||
$labels['smartsections'] = 'Smart sections';
|
||||
$labels['until'] = 'until';
|
||||
$labels['today'] = 'Today';
|
||||
$labels['tomorrow'] = 'Tomorrow';
|
||||
$labels['thisweek'] = 'This week';
|
||||
$labels['nextweek'] = 'Next week';
|
||||
$labels['prevweek'] = 'Previous week';
|
||||
$labels['thismonth'] = 'This month';
|
||||
$labels['nextmonth'] = 'Next month';
|
||||
$labels['weekofyear'] = 'Týždeň';
|
||||
$labels['pastevents'] = 'Past';
|
||||
$labels['futureevents'] = 'Future';
|
||||
|
||||
// alarm/reminder settings
|
||||
$labels['showalarms'] = 'Show reminders';
|
||||
$labels['defaultalarmtype'] = 'Default reminder setting';
|
||||
$labels['defaultalarmoffset'] = 'Default reminder time';
|
||||
|
||||
// attendees
|
||||
$labels['attendee'] = 'Participant';
|
||||
$labels['role'] = 'Role';
|
||||
$labels['availability'] = 'Avail.';
|
||||
$labels['confirmstate'] = 'Stav';
|
||||
$labels['addattendee'] = 'Add participant';
|
||||
$labels['roleorganizer'] = 'Organizer';
|
||||
$labels['rolerequired'] = 'Required';
|
||||
$labels['roleoptional'] = 'Optional';
|
||||
$labels['rolechair'] = 'Chair';
|
||||
$labels['rolenonparticipant'] = 'Absent';
|
||||
$labels['cutypeindividual'] = 'Individual';
|
||||
$labels['cutypegroup'] = 'Group';
|
||||
$labels['cutyperesource'] = 'Resource';
|
||||
$labels['cutyperoom'] = 'Room';
|
||||
$labels['availfree'] = 'Voľný';
|
||||
$labels['availbusy'] = 'Zaneprázdnený';
|
||||
$labels['availunknown'] = 'Unknown';
|
||||
$labels['availtentative'] = 'Nezáväzne';
|
||||
$labels['availoutofoffice'] = 'Mimo kancelárie';
|
||||
$labels['delegatedto'] = 'Delegated to: ';
|
||||
$labels['delegatedfrom'] = 'Delegated from: ';
|
||||
$labels['scheduletime'] = 'Find availability';
|
||||
$labels['sendinvitations'] = 'Send invitations';
|
||||
$labels['sendnotifications'] = 'Notify participants about modifications';
|
||||
$labels['sendcancellation'] = 'Notify participants about event cancellation';
|
||||
$labels['onlyworkinghours'] = 'Find availability within my working hours';
|
||||
$labels['reqallattendees'] = 'Required/all participants';
|
||||
$labels['prevslot'] = 'Previous Slot';
|
||||
$labels['nextslot'] = 'Next Slot';
|
||||
$labels['suggestedslot'] = 'Suggested Slot';
|
||||
$labels['noslotfound'] = 'Unable to find a free time slot';
|
||||
$labels['invitationsubject'] = 'You\'ve been invited to "$title"';
|
||||
$labels['invitationmailbody'] = "*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees\n\nPlease find attached an iCalendar file with all the event details which you can import to your calendar application.";
|
||||
$labels['invitationattendlinks'] = "In case your email client doesn't support iTip requests you can use the following link to either accept or decline this invitation:\n\$url";
|
||||
$labels['eventupdatesubject'] = '"$title" has been updated';
|
||||
$labels['eventupdatesubjectempty'] = 'An event that concerns you has been updated';
|
||||
$labels['eventupdatemailbody'] = "*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees\n\nPlease find attached an iCalendar file with the updated event details which you can import to your calendar application.";
|
||||
$labels['eventcancelsubject'] = '"$title" has been cancelled';
|
||||
$labels['eventcancelmailbody'] = "*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees\n\nThe event has been cancelled by \$organizer.\n\nPlease find attached an iCalendar file with the updated event details.";
|
||||
|
||||
// invitation handling (overrides labels from libcalendaring)
|
||||
$labels['itipobjectnotfound'] = 'The event referred by this message was not found in your calendar.';
|
||||
|
||||
$labels['itipmailbodyaccepted'] = "\$sender has accepted the invitation to the following event:\n\n*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees";
|
||||
$labels['itipmailbodytentative'] = "\$sender has tentatively accepted the invitation to the following event:\n\n*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees";
|
||||
$labels['itipmailbodydeclined'] = "\$sender has declined the invitation to the following event:\n\n*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees";
|
||||
$labels['itipmailbodycancel'] = "\$sender has rejected your participation in the following event:\n\n*\$title*\n\nWhen: \$date";
|
||||
$labels['itipmailbodydelegated'] = "\$sender has delegated the participation in the following event:\n\n*\$title*\n\nWhen: \$date";
|
||||
$labels['itipmailbodydelegatedto'] = "\$sender has delegated the participation in the following event to you:\n\n*\$title*\n\nWhen: \$date";
|
||||
|
||||
$labels['itipdeclineevent'] = 'Do you want to decline your invitation to this event?';
|
||||
$labels['declinedeleteconfirm'] = 'Do you also want to delete this declined event from your calendar?';
|
||||
$labels['itipcomment'] = 'Invitation/notification comment';
|
||||
$labels['itipcommenttitle'] = 'This comment will be attached to the invitation/notification message sent to participants';
|
||||
|
||||
$labels['notanattendee'] = 'You\'re not listed as an attendee of this event';
|
||||
$labels['eventcancelled'] = 'The event has been cancelled';
|
||||
$labels['saveincalendar'] = 'save in';
|
||||
$labels['updatemycopy'] = 'Update in my calendar';
|
||||
$labels['savetocalendar'] = 'Save to calendar';
|
||||
$labels['openpreview'] = 'Check Calendar';
|
||||
$labels['noearlierevents'] = 'No earlier events';
|
||||
$labels['nolaterevents'] = 'No later events';
|
||||
|
||||
// resources
|
||||
$labels['resource'] = 'Resource';
|
||||
$labels['addresource'] = 'Book resource';
|
||||
$labels['findresources'] = 'Find resources';
|
||||
$labels['resourcedetails'] = 'Details';
|
||||
$labels['resourceavailability'] = 'Availability';
|
||||
$labels['resourceowner'] = 'Owner';
|
||||
$labels['resourceadded'] = 'The resource was added to your event';
|
||||
|
||||
// event dialog tabs
|
||||
$labels['tabsummary'] = 'Sumár';
|
||||
$labels['tabrecurrence'] = 'Recurrence';
|
||||
$labels['tabattendees'] = 'Participants';
|
||||
$labels['tabresources'] = 'Resources';
|
||||
$labels['tabattachments'] = 'Attachments';
|
||||
$labels['tabsharing'] = 'Sharing';
|
||||
|
||||
// messages
|
||||
$labels['deleteobjectconfirm'] = 'Do you really want to delete this event?';
|
||||
$labels['deleteventconfirm'] = 'Do you really want to delete this event?';
|
||||
$labels['deletecalendarconfirm'] = 'Do you really want to delete this calendar with all its events?';
|
||||
$labels['deletecalendarconfirmrecursive'] = 'Do you really want to delete this calendar with all its events and sub-calendars?';
|
||||
$labels['savingdata'] = 'Saving data...';
|
||||
$labels['errorsaving'] = 'Failed to save changes.';
|
||||
$labels['operationfailed'] = 'The requested operation failed.';
|
||||
$labels['invalideventdates'] = 'Invalid dates entered! Please check your input.';
|
||||
$labels['invalidcalendarproperties'] = 'Invalid calendar properties! Please set a valid name.';
|
||||
$labels['searchnoresults'] = 'No events found in the selected calendars.';
|
||||
$labels['successremoval'] = 'The event has been deleted successfully.';
|
||||
$labels['successrestore'] = 'The event has been restored successfully.';
|
||||
$labels['errornotifying'] = 'Failed to send notifications to event participants';
|
||||
$labels['errorimportingevent'] = 'Failed to import the event';
|
||||
$labels['importwarningexists'] = 'A copy of this event already exists in your calendar.';
|
||||
$labels['newerversionexists'] = 'A newer version of this event already exists! Aborted.';
|
||||
$labels['nowritecalendarfound'] = 'No calendar found to save the event';
|
||||
$labels['importedsuccessfully'] = 'The event was successfully added to \'$calendar\'';
|
||||
$labels['updatedsuccessfully'] = 'The event was successfully updated in \'$calendar\'';
|
||||
$labels['attendeupdateesuccess'] = 'Successfully updated the participant\'s status';
|
||||
$labels['itipsendsuccess'] = 'Invitation sent to participants.';
|
||||
$labels['itipresponseerror'] = 'Failed to send the response to this event invitation';
|
||||
$labels['itipinvalidrequest'] = 'This invitation is no longer valid';
|
||||
$labels['sentresponseto'] = 'Successfully sent invitation response to $mailto';
|
||||
$labels['localchangeswarning'] = 'You are about to make changes that will only be reflected on your calendar and not be sent to the organizer of the event.';
|
||||
$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.';
|
||||
|
||||
$labels['changeeventconfirm'] = 'Change event';
|
||||
$labels['removeeventconfirm'] = 'Delete event';
|
||||
$labels['changerecurringeventwarning'] = 'This is a recurring event. Would you like to edit the current event only, this and all future occurences, all occurences or save it as a new event?';
|
||||
$labels['removerecurringeventwarning'] = 'This is a recurring event. Would you like to delete the current event only, this and all future occurences or all occurences of this event?';
|
||||
$labels['removerecurringallonly'] = 'This is a recurring event. As a participant, you can only delete the entire event with all occurences.';
|
||||
$labels['currentevent'] = 'Current';
|
||||
$labels['futurevents'] = 'Future';
|
||||
$labels['allevents'] = 'All';
|
||||
$labels['saveasnew'] = 'Save as new';
|
||||
|
||||
// birthdays calendar
|
||||
$labels['birthdays'] = 'Birthdays';
|
||||
$labels['birthdayscalendar'] = 'Birthdays Calendar';
|
||||
$labels['displaybirthdayscalendar'] = 'Display birthdays calendar';
|
||||
$labels['birthdayscalendarsources'] = 'From these address books';
|
||||
$labels['birthdayeventtitle'] = '$name\'s Birthday';
|
||||
$labels['birthdayage'] = 'Age $age';
|
||||
|
||||
// history dialog
|
||||
$labels['objectchangelog'] = 'Change History';
|
||||
$labels['objectdiff'] = 'Changes from $rev1 to $rev2';
|
||||
$labels['objectnotfound'] = 'Failed to load event data';
|
||||
$labels['objectchangelognotavailable'] = 'Change history is not available for this event';
|
||||
$labels['objectdiffnotavailable'] = 'No comparison possible for the selected revisions';
|
||||
$labels['revisionrestoreconfirm'] = 'Do you really want to restore revision $rev of this event? This will replace the current event with the old version.';
|
||||
$labels['objectrestoresuccess'] = 'Revision $rev successfully restored';
|
||||
$labels['objectrestoreerror'] = 'Failed to restore the old revision';
|
||||
|
||||
|
||||
// (hidden) titles and labels for accessibility annotations
|
||||
$labels['arialabelminical'] = 'Calendar date selection';
|
||||
$labels['arialabelcalendarview'] = 'Calendar view';
|
||||
$labels['arialabelsearchform'] = 'Event search form';
|
||||
$labels['arialabelquicksearchbox'] = 'Event search input';
|
||||
$labels['arialabelcalsearchform'] = 'Calendars search form';
|
||||
$labels['calendaractions'] = 'Calendar actions';
|
||||
$labels['arialabeleventattendees'] = 'Event participants list';
|
||||
$labels['arialabeleventresources'] = 'Event resources list';
|
||||
$labels['arialabelresourcesearchform'] = 'Resources search form';
|
||||
$labels['arialabelresourceselection'] = 'Available resources';
|
||||
|
||||
?>
|
||||
|
|
|
@ -373,7 +373,6 @@ pre {
|
|||
right: 4px;
|
||||
}
|
||||
|
||||
#eventedit.uidialog,
|
||||
.calendarmain div.uidialog {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -123,7 +123,6 @@
|
|||
<ul>
|
||||
<li><roundcube:button command="event-download" label="download" classAct="active" /></li>
|
||||
<li><roundcube:button command="event-sendbymail" label="send" classAct="active" /></li>
|
||||
<li><roundcube:button command="event-copy" label="copy" classAct="active" /></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -115,6 +115,7 @@ body.calendarmain #mainscreen {
|
|||
width: 6px;
|
||||
top: 40px !important;
|
||||
bottom: 0;
|
||||
height: auto;
|
||||
background: url(images/toggle.gif) -1px 48% no-repeat transparent;
|
||||
}
|
||||
|
||||
|
@ -453,8 +454,7 @@ pre {
|
|||
|
||||
#calendars .searchresults .boxtitle {
|
||||
background: none;
|
||||
padding: 2px 8px;
|
||||
border-radius: 0;
|
||||
padding: 2px 8px 2px 8px;
|
||||
}
|
||||
|
||||
#calendars .searchresults .listing li {
|
||||
|
@ -539,7 +539,6 @@ body.calendarmain #searchmenulink {
|
|||
width: 15px;
|
||||
}
|
||||
|
||||
#eventedit.uidialog,
|
||||
.calendarmain div.uidialog {
|
||||
display: none;
|
||||
}
|
||||
|
@ -642,7 +641,7 @@ a.miniColors-trigger {
|
|||
border-top: 2px solid #fafafa;
|
||||
}
|
||||
|
||||
#edit-attachments-form .buttons {
|
||||
#edit-attachments-form .formbuttons {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
|
@ -683,7 +682,7 @@ a.miniColors-trigger {
|
|||
}
|
||||
|
||||
.event-attendees span.attendee {
|
||||
padding: 1px 18px 1px 0;
|
||||
padding-right: 18px;
|
||||
margin-right: 0.5em;
|
||||
background: url(images/attendee-status.png) right 0 no-repeat;
|
||||
}
|
||||
|
@ -1139,7 +1138,7 @@ td.topalign {
|
|||
|
||||
#eventedit .edit-attendees-table th.invite,
|
||||
#eventedit .edit-attendees-table td.invite {
|
||||
width: 50px;
|
||||
width: 44px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
|
@ -1239,51 +1238,50 @@ td.topalign {
|
|||
height: 14px;
|
||||
border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.availability img.availabilityicon.loading {
|
||||
background: url(images/loading_blue.gif) center no-repeat;
|
||||
}
|
||||
|
||||
#schedule-freebusy-times td.unknown,
|
||||
#schedule-freebusy-times td div.unknown,
|
||||
.availability img.availabilityicon.unknown {
|
||||
background: #ddd;
|
||||
}
|
||||
|
||||
#schedule-freebusy-times td.free,
|
||||
#schedule-freebusy-times td div.free,
|
||||
.availability img.availabilityicon.free {
|
||||
background: #abd640;
|
||||
}
|
||||
|
||||
#schedule-freebusy-times td.busy,
|
||||
#schedule-freebusy-times td div.busy,
|
||||
.availability img.availabilityicon.busy {
|
||||
background: #e26569;
|
||||
}
|
||||
|
||||
#schedule-freebusy-times td.tentative,
|
||||
#schedule-freebusy-times td div.tentative,
|
||||
.availability img.availabilityicon.tentative {
|
||||
background: #8383fc;
|
||||
}
|
||||
|
||||
#schedule-freebusy-times td.out-of-office,
|
||||
#schedule-freebusy-times td div.out-of-office,
|
||||
.availability img.availabilityicon.out-of-office {
|
||||
background: #fbaa68;
|
||||
}
|
||||
|
||||
#schedule-freebusy-times td.all-busy,
|
||||
#schedule-freebusy-times td.all-tentative,
|
||||
#schedule-freebusy-times td.all-out-of-office {
|
||||
#schedule-freebusy-times td div.all-busy,
|
||||
#schedule-freebusy-times td div.all-tentative,
|
||||
#schedule-freebusy-times td div.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 {
|
||||
#schedule-freebusy-times td div.all-tentative {
|
||||
background-position: right -40px;
|
||||
}
|
||||
|
||||
#schedule-freebusy-times td.all-out-of-office {
|
||||
#schedule-freebusy-times td div.all-out-of-office {
|
||||
background-position: right -80px;
|
||||
}
|
||||
|
||||
|
@ -1297,6 +1295,10 @@ td.topalign {
|
|||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#edit-attendees-legend img.availabilityicon {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.edit-attendees-table tbody td.confirmstate {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
@ -1310,27 +1312,22 @@ td.topalign {
|
|||
}
|
||||
|
||||
.edit-attendees-table td.confirmstate span.needs-action {
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.edit-attendees-table td.confirmstate span.accepted {
|
||||
background-position: 5px -20px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.edit-attendees-table td.confirmstate span.declined {
|
||||
background-position: 5px -40px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.edit-attendees-table td.confirmstate span.tentative {
|
||||
background-position: 5px -60px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.edit-attendees-table td.confirmstate span.delegated {
|
||||
background-position: 5px -180px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
#attendees-freebusy-table {
|
||||
|
@ -1411,7 +1408,8 @@ td.topalign {
|
|||
.attendees-list .spacer,
|
||||
#schedule-freebusy-times tr.spacer td {
|
||||
background: 0;
|
||||
font-size: 50%;
|
||||
padding: 0;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
#schedule-freebusy-times {
|
||||
|
@ -1424,6 +1422,15 @@ td.topalign {
|
|||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
#schedule-freebusy-times tbody td {
|
||||
padding: 0;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
#schedule-freebusy-times tbody td div {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#attendees-freebusy-table div.timesheader,
|
||||
#schedule-freebusy-times tr.times td {
|
||||
min-width: 30px;
|
||||
|
@ -1441,6 +1448,10 @@ td.topalign {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
#schedule-freebusy-times #fbrowall td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
#schedule-event-time {
|
||||
position: absolute;
|
||||
border: 2px solid #333;
|
||||
|
@ -2207,6 +2218,11 @@ div.calendar-invitebox td em {
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.calendar-invitebox td.date.modified {
|
||||
font-weight: bold;
|
||||
color: red;
|
||||
}
|
||||
|
||||
#event-rsvp .rsvp-buttons,
|
||||
div.calendar-invitebox .itip-buttons div {
|
||||
margin-top: 0.5em;
|
||||
|
|
|
@ -164,7 +164,6 @@
|
|||
<ul id="eventoptionsmenu-menu" class="toolbarmenu" role="menu" aria-labelledby="aria-label-eventoptions">
|
||||
<li role="menuitem"><roundcube:button command="event-download" label="download" classAct="active" /></li>
|
||||
<li role="menuitem"><roundcube:button command="event-sendbymail" label="send" classAct="active" /></li>
|
||||
<li role="menuitem"><roundcube:button command="event-copy" label="copy" classAct="active" /></li>
|
||||
<roundcube:if condition="env:calendar_driver == 'kolab' && config:kolab_bonnie_api" />
|
||||
<li role="menuitem"><roundcube:button command="event-history" type="link" label="calendar.eventhistory" classAct="active" /></li>
|
||||
<roundcube:endif />
|
||||
|
|
|
@ -117,10 +117,10 @@
|
|||
<!-- attachments list (with upload form) -->
|
||||
<div id="event-panel-attachments">
|
||||
<div id="edit-attachments">
|
||||
<roundcube:object name="plugin.attachments_list" id="attachmentlist" class="attachmentslist" />
|
||||
<roundcube:object name="plugin.attachments_list" id="attachment-list" class="attachmentslist" />
|
||||
</div>
|
||||
<div id="edit-attachments-form" role="region" aria-labelledby="aria-label-attachmentuploadform">
|
||||
<h3 id="aria-label-attachmentuploadform" class="voice"><roundcube:label name="arialabelattachmentuploadform" /></h3>
|
||||
<h3 id="aria-label-attachmentuploadform" class="voice"><roundcube:label name="arialabelattachmentuploadform" /></h2>
|
||||
<roundcube:object name="plugin.attachments_form" id="calendar-attachment-form" attachmentFieldSize="30" />
|
||||
</div>
|
||||
<roundcube:object name="plugin.filedroparea" id="event-panel-attachments" />
|
||||
|
|
|
@ -7,17 +7,9 @@
|
|||
<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">
|
||||
|
|
Loading…
Reference in a new issue