Synchronized with upstream from git.kolab.org (v3.2.16)

This commit is contained in:
Thomas Bruederli 2016-11-20 17:39:39 +01:00
parent 84fde2ba4a
commit a0aacd4a73
42 changed files with 1514 additions and 789 deletions

View file

@ -58,9 +58,10 @@ class calendar extends rcube_plugin
'calendar_itip_after_action' => 0, 'calendar_itip_after_action' => 0,
); );
private $ical; // These are implemented with __get()
private $itip; // private $ical;
private $driver; // private $itip;
// private $driver;
/** /**
@ -68,27 +69,13 @@ class calendar extends rcube_plugin
*/ */
function init() function init()
{ {
$this->require_plugin('libcalendaring');
$this->rc = rcube::get_instance(); $this->rc = rcube::get_instance();
$this->lib = libcalendaring::get_instance();
$this->register_task('calendar', 'calendar'); $this->register_task('calendar', 'calendar');
// load calendar configuration // load calendar configuration
$this->load_config(); $this->load_config();
// load localizations
$this->add_texts('localization/', $this->rc->task == 'calendar' && (!$this->rc->action || $this->rc->action == 'print'));
$this->timezone = $this->lib->timezone;
$this->gmt_offset = $this->lib->gmt_offset;
$this->dst_active = $this->lib->dst_active;
$this->timezone_offset = $this->gmt_offset / 3600 - $this->dst_active;
require($this->home . '/lib/calendar_ui.php');
$this->ui = new calendar_ui($this);
// catch iTIP confirmation requests that don're require a valid session // catch iTIP confirmation requests that don're require a valid session
if ($this->rc->action == 'attend' && !empty($_REQUEST['_t'])) { if ($this->rc->action == 'attend' && !empty($_REQUEST['_t'])) {
$this->add_hook('startup', array($this, 'itip_attend_response')); $this->add_hook('startup', array($this, 'itip_attend_response'));
@ -96,7 +83,7 @@ class calendar extends rcube_plugin
else if ($this->rc->action == 'feed' && !empty($_REQUEST['_cal'])) { else if ($this->rc->action == 'feed' && !empty($_REQUEST['_cal'])) {
$this->add_hook('startup', array($this, 'ical_feed_export')); $this->add_hook('startup', array($this, 'ical_feed_export'));
} }
else { else if ($this->rc->task != 'login') {
// default startup routine // default startup routine
$this->add_hook('startup', array($this, 'startup')); $this->add_hook('startup', array($this, 'startup'));
} }
@ -104,6 +91,26 @@ class calendar extends rcube_plugin
$this->add_hook('user_delete', array($this, 'user_delete')); $this->add_hook('user_delete', array($this, 'user_delete'));
} }
/**
* Setup basic plugin environment and UI
*/
protected function setup()
{
$this->require_plugin('libcalendaring');
$this->lib = libcalendaring::get_instance();
$this->timezone = $this->lib->timezone;
$this->gmt_offset = $this->lib->gmt_offset;
$this->dst_active = $this->lib->dst_active;
$this->timezone_offset = $this->gmt_offset / 3600 - $this->dst_active;
// load localizations
$this->add_texts('localization/', $this->rc->task == 'calendar' && (!$this->rc->action || $this->rc->action == 'print'));
require($this->home . '/lib/calendar_ui.php');
$this->ui = new calendar_ui($this);
}
/** /**
* Startup hook * Startup hook
*/ */
@ -113,6 +120,8 @@ class calendar extends rcube_plugin
if ($this->rc->config->get('calendar_disabled', false) || !$this->rc->config->get('calendar_enabled', true)) if ($this->rc->config->get('calendar_disabled', false) || !$this->rc->config->get('calendar_enabled', true))
return; return;
$this->setup();
// load Calendar user interface // load Calendar user interface
if (!$this->rc->output->ajax_call && (!$this->rc->output->env['framed'] || $args['action'] == 'preview')) { if (!$this->rc->output->ajax_call && (!$this->rc->output->env['framed'] || $args['action'] == 'preview')) {
$this->ui->init(); $this->ui->init();
@ -260,11 +269,15 @@ class calendar extends rcube_plugin
/** /**
* Get properties of the calendar this user has specified as default * Get properties of the calendar this user has specified as default
*/ */
public function get_default_calendar($sensitivity = null) public function get_default_calendar($sensitivity = null, $calendars = null)
{ {
if ($calendars === null) {
$calendars = $this->driver->list_calendars(calendar_driver::FILTER_PERSONAL | calendar_driver::FILTER_WRITEABLE);
}
$default_id = $this->rc->config->get('calendar_default_calendar'); $default_id = $this->rc->config->get('calendar_default_calendar');
$calendars = $this->driver->list_calendars(calendar_driver::FILTER_PERSONAL | calendar_driver::FILTER_WRITEABLE); $calendar = $calendars[$default_id] ?: null;
$calendar = $calendars[$default_id] ?: null;
if (!$calendar || $sensitivity) { if (!$calendar || $sensitivity) {
foreach ($calendars as $cal) { foreach ($calendars as $cal) {
if ($sensitivity && $cal['subtype'] == $sensitivity) { if ($sensitivity && $cal['subtype'] == $sensitivity) {
@ -299,7 +312,6 @@ class calendar extends rcube_plugin
$this->ui->init_templates(); $this->ui->init_templates();
$this->rc->output->add_label('lowest','low','normal','high','highest','delete','cancel','uploading','noemailwarning','close'); $this->rc->output->add_label('lowest','low','normal','high','highest','delete','cancel','uploading','noemailwarning','close');
$this->rc->output->add_label('libcalendaring.itipaccepted','libcalendaring.itiptentative','libcalendaring.itipdeclined','libcalendaring.itipdelegated','libcalendaring.expandattendeegroup','libcalendaring.expandattendeegroupnodata');
// initialize attendees autocompletion // initialize attendees autocompletion
$this->rc->autocomplete_init(); $this->rc->autocomplete_init();
@ -307,7 +319,6 @@ class calendar extends rcube_plugin
$this->rc->output->set_env('timezone', $this->timezone->getName()); $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_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('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')))); $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); $view = rcube_utils::get_input_value('view', rcube_utils::INPUT_GPC);
@ -465,40 +476,42 @@ class calendar extends rcube_plugin
// loading driver is expensive, don't do it if not needed // loading driver is expensive, don't do it if not needed
$this->load_driver(); $this->load_driver();
if (!isset($no_override['calendar_default_alarm_type'])) { if (!isset($no_override['calendar_default_alarm_type']) || !isset($no_override['calendar_default_alarm_offset'])) {
if (!$p['current']) { if (!$p['current']) {
$p['blocks']['view']['content'] = true; $p['blocks']['view']['content'] = true;
return $p; return $p;
} }
$field_id = 'rcmfd_alarm'; $alarm_type = $alarm_offset = '';
$select_type = new html_select(array('name' => '_alarm_type', 'id' => $field_id));
$select_type->add($this->gettext('none'), ''); if (!isset($no_override['calendar_default_alarm_type'])) {
foreach ($this->driver->alarm_types as $type) $field_id = 'rcmfd_alarm';
$select_type->add($this->gettext(strtolower("alarm{$type}option"), 'libcalendaring'), $type); $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]);
}
$p['blocks']['view']['options']['alarmtype'] = array( $p['blocks']['view']['options']['alarmtype'] = array(
'title' => html::label($field_id, rcube::Q($this->gettext('defaultalarmtype'))), 'title' => html::label($field_id, rcube::Q($this->gettext('defaultalarmtype'))),
'content' => $select_type->show($this->rc->config->get('calendar_default_alarm_type', '')), 'content' => $alarm_type . ' ' . $alarm_offset,
);
}
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->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]),
); );
} }
@ -510,7 +523,7 @@ class calendar extends rcube_plugin
// default calendar selection // default calendar selection
$field_id = 'rcmfd_default_calendar'; $field_id = 'rcmfd_default_calendar';
$select_cal = new html_select(array('name' => '_default_calendar', 'id' => $field_id, 'is_escaped' => true)); $select_cal = new html_select(array('name' => '_default_calendar', 'id' => $field_id, 'is_escaped' => true));
foreach ((array)$this->driver->list_calendars(calendar_driver::FILTER_PERSONAL) as $id => $prop) { foreach ((array)$this->driver->list_calendars(calendar_driver::FILTER_PERSONAL | calendar_driver::FILTER_ACTIVE) as $id => $prop) {
$select_cal->add($prop['name'], strval($id)); $select_cal->add($prop['name'], strval($id));
if ($prop['default']) if ($prop['default'])
$default_calendar = $id; $default_calendar = $id;
@ -655,13 +668,13 @@ class calendar extends rcube_plugin
$select_type = new html_select(array('name' => '_birthdays_alarm_type', 'id' => $field_id) + $input_attrib); $select_type = new html_select(array('name' => '_birthdays_alarm_type', 'id' => $field_id) + $input_attrib);
$select_type->add($this->gettext('none'), ''); $select_type->add($this->gettext('none'), '');
foreach ($this->driver->alarm_types as $type) { foreach ($this->driver->alarm_types as $type) {
$select_type->add($this->gettext(strtolower("alarm{$type}option"), 'libcalendaring'), $type); $select_type->add($this->rc->gettext(strtolower("alarm{$type}option"), 'libcalendaring'), $type);
} }
$input_value = new html_inputfield(array('name' => '_birthdays_alarm_value', 'id' => $field_id . 'value', 'size' => 3) + $input_attrib); $input_value = new html_inputfield(array('name' => '_birthdays_alarm_value', 'id' => $field_id . 'value', 'size' => 3) + $input_attrib);
$select_offset = new html_select(array('name' => '_birthdays_alarm_offset', 'id' => $field_id . 'offset') + $input_attrib); $select_offset = new html_select(array('name' => '_birthdays_alarm_offset', 'id' => $field_id . 'offset') + $input_attrib);
foreach (array('-M','-H','-D') as $trigger) foreach (array('-M','-H','-D') as $trigger)
$select_offset->add($this->gettext('trigger' . $trigger, 'libcalendaring'), $trigger); $select_offset->add($this->rc->gettext('trigger' . $trigger, 'libcalendaring'), $trigger);
$preset = libcalendaring::parse_alarm_value($this->rc->config->get('calendar_birthdays_alarm_offset', '-1D')); $preset = libcalendaring::parse_alarm_value($this->rc->config->get('calendar_birthdays_alarm_offset', '-1D'));
$p['blocks']['birthdays']['options']['birthdays_alarmoffset'] = array( $p['blocks']['birthdays']['options']['birthdays_alarmoffset'] = array(
@ -786,6 +799,22 @@ class calendar extends rcube_plugin
case "subscribe": case "subscribe":
if (!$this->driver->subscribe_calendar($cal)) if (!$this->driver->subscribe_calendar($cal))
$this->rc->output->show_message($this->gettext('errorsaving'), 'error'); $this->rc->output->show_message($this->gettext('errorsaving'), 'error');
else {
$calendars = $this->driver->list_calendars();
$calendar = $calendars[$cal['id']];
// find parent folder and check if it's a "user calendar"
// if it's also activated we need to refresh it (#5340)
while ($calendar['parent']) {
if (isset($calendars[$calendar['parent']]))
$calendar = $calendars[$calendar['parent']];
else
break;
}
if ($calendar['id'] != $cal['id'] && $calendar['active'] && $calendar['group'] == "other user")
$this->rc->output->command('plugin.refresh_source', $calendar['id']);
}
return; return;
case "search": case "search":
$results = array(); $results = array();
@ -989,6 +1018,7 @@ class calendar extends rcube_plugin
$ev['attendees'] = $event['attendees']; $ev['attendees'] = $event['attendees'];
$ev['free_busy'] = $event['free_busy']; $ev['free_busy'] = $event['free_busy'];
$ev['_savemode'] = $event['_savemode']; $ev['_savemode'] = $event['_savemode'];
$ev['comment'] = $reply_comment;
// send invitation to delegatee + add it as attendee // send invitation to delegatee + add it as attendee
if ($status == 'delegated' && $event['to']) { if ($status == 'delegated' && $event['to']) {
@ -1025,7 +1055,6 @@ class calendar extends rcube_plugin
if (!$noreply) { if (!$noreply) {
$itip = $this->load_itip(); $itip = $this->load_itip();
$itip->set_sender_email($reply_sender); $itip->set_sender_email($reply_sender);
$event['comment'] = $reply_comment;
$event['thisandfuture'] = $event['_savemode'] == 'future'; $event['thisandfuture'] = $event['_savemode'] == 'future';
if ($organizer && $itip->send_itip_message($event, 'REPLY', $organizer, 'itipsubject' . $status, 'itipmailbody' . $status)) if ($organizer && $itip->send_itip_message($event, 'REPLY', $organizer, 'itipsubject' . $status, 'itipmailbody' . $status))
$this->rc->output->command('display_message', $this->gettext(array('name' => 'sentresponseto', 'vars' => array('mailto' => $organizer['name'] ? $organizer['name'] : $organizer['email']))), 'confirmation'); $this->rc->output->command('display_message', $this->gettext(array('name' => 'sentresponseto', 'vars' => array('mailto' => $organizer['name'] ? $organizer['name'] : $organizer['email']))), 'confirmation');
@ -1410,7 +1439,7 @@ class calendar extends rcube_plugin
} }
/** /**
* Handler for check-recent requests which are accidentally sent to calendar taks * Handler for check-recent requests which are accidentally sent to calendar
*/ */
function check_recent() function check_recent()
{ {
@ -1437,7 +1466,7 @@ class calendar extends rcube_plugin
{ {
// Upload progress update // Upload progress update
if (!empty($_GET['_progress'])) { if (!empty($_GET['_progress'])) {
rcube_upload_progress(); $this->rc->upload_progress();
} }
@set_time_limit(0); @set_time_limit(0);
@ -1503,11 +1532,11 @@ class calendar extends rcube_plugin
} }
else { else {
if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) { if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) {
$msg = $this->gettext(array('name' => 'filesizeerror', 'vars' => array( $msg = $this->rc->gettext(array('name' => 'filesizeerror', 'vars' => array(
'size' => show_bytes(parse_bytes(ini_get('upload_max_filesize')))))); 'size' => $this->rc->show_bytes(parse_bytes(ini_get('upload_max_filesize'))))));
} }
else { else {
$msg = $this->gettext('fileuploaderror'); $msg = $this->rc->gettext('fileuploaderror');
} }
$this->rc->output->command('plugin.import_error', array('message' => $msg)); $this->rc->output->command('plugin.import_error', array('message' => $msg));
@ -1649,6 +1678,7 @@ class calendar extends rcube_plugin
// sanity check user // sanity check user
if ($this->rc->user->get_username() == $user) { if ($this->rc->user->get_username() == $user) {
$this->setup();
$this->load_driver(); $this->load_driver();
$this->export_events(false); $this->export_events(false);
} }
@ -1662,7 +1692,6 @@ class calendar extends rcube_plugin
exit; exit;
} }
/** /**
* *
*/ */
@ -1719,7 +1748,7 @@ class calendar extends rcube_plugin
foreach ($events as $event) { foreach ($events as $event) {
$json[] = $this->_client_event($event, $addcss); $json[] = $this->_client_event($event, $addcss);
} }
return json_encode($json); return rcube_output::json_serialize($json);
} }
/** /**
@ -1740,15 +1769,21 @@ class calendar extends rcube_plugin
foreach ((array)$event['attachments'] as $k => $attachment) { foreach ((array)$event['attachments'] as $k => $attachment) {
$event['attachments'][$k]['classname'] = rcube_utils::file2class($attachment['mimetype'], $attachment['name']); $event['attachments'][$k]['classname'] = rcube_utils::file2class($attachment['mimetype'], $attachment['name']);
unset($event['attachments'][$k]['data'], $event['attachments'][$k]['content']);
if (!$attachment['id']) {
$event['attachments'][$k]['id'] = $k;
}
} }
// convert link URIs references into structs // convert link URIs references into structs
if (array_key_exists('links', $event)) { if (array_key_exists('links', $event)) {
foreach ((array)$event['links'] as $i => $link) { foreach ((array) $event['links'] as $i => $link) {
if (strpos($link, 'imap://') === 0 && ($msgref = $this->driver->get_message_reference($link))) { if (strpos($link, 'imap://') === 0 && ($msgref = $this->driver->get_message_reference($link))) {
$event['links'][$i] = $msgref; $event['links'][$i] = $msgref;
}
} }
}
} }
// check for organizer in attendees list // check for organizer in attendees list
@ -1890,7 +1925,19 @@ class calendar extends rcube_plugin
$rev = rcube_utils::get_input_value('_rev', rcube_utils::INPUT_GPC); $rev = rcube_utils::get_input_value('_rev', rcube_utils::INPUT_GPC);
$event = array('id' => $event_id, 'calendar' => $calendar, 'rev' => $rev); $event = array('id' => $event_id, 'calendar' => $calendar, 'rev' => $rev);
$attachment = $this->driver->get_attachment($id, $event);
if ($calendar == '--invitation--itip') {
$uid = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_GPC);
$part = rcube_utils::get_input_value('_part', rcube_utils::INPUT_GPC);
$mbox = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_GPC);
$event = $this->lib->mail_get_itip_object($mbox, $uid, $part, 'event');
$attachment = $event['attachments'][$id];
$attachment['body'] = &$attachment['data'];
}
else {
$attachment = $this->driver->get_attachment($id, $event);
}
// show part page // show part page
if (!empty($_GET['_frame'])) { if (!empty($_GET['_frame'])) {
@ -1901,7 +1948,10 @@ class calendar extends rcube_plugin
} }
// deliver attachment content // deliver attachment content
else if ($attachment) { else if ($attachment) {
$attachment['body'] = $this->driver->get_attachment_body($id, $event); if ($calendar != '--invitation--itip') {
$attachment['body'] = $this->driver->get_attachment_body($id, $event);
}
$this->lib->attachment_get($attachment); $this->lib->attachment_get($attachment);
} }
@ -1925,8 +1975,8 @@ class calendar extends rcube_plugin
private function write_preprocess(&$event, $action) private function write_preprocess(&$event, $action)
{ {
// convert dates into DateTime objects in user's current timezone // convert dates into DateTime objects in user's current timezone
$event['start'] = new DateTime($event['start'], $this->timezone); $event['start'] = new DateTime($event['start'], $this->timezone);
$event['end'] = new DateTime($event['end'], $this->timezone); $event['end'] = new DateTime($event['end'], $this->timezone);
$event['allday'] = (bool)$event['allday']; $event['allday'] = (bool)$event['allday'];
// start/end is all we need for 'move' action (#1480) // start/end is all we need for 'move' action (#1480)
@ -1976,7 +2026,7 @@ class calendar extends rcube_plugin
foreach ((array)$event['attendees'] as $i => $attendee) { foreach ((array)$event['attendees'] as $i => $attendee) {
if ($attendee['role'] == 'ORGANIZER') if ($attendee['role'] == 'ORGANIZER')
$organizer = $i; $organizer = $i;
if ($attendee['email'] == in_array(strtolower($attendee['email']), $emails)) if ($attendee['email'] && in_array(strtolower($attendee['email']), $emails))
$owner = $i; $owner = $i;
if (!isset($attendee['rsvp'])) if (!isset($attendee['rsvp']))
$event['attendees'][$i]['rsvp'] = true; $event['attendees'][$i]['rsvp'] = true;
@ -1984,17 +2034,28 @@ class calendar extends rcube_plugin
$event['attendees'][$i]['rsvp'] = $attendee['rsvp'] == 'true' || $attendee['rsvp'] == '1'; $event['attendees'][$i]['rsvp'] = $attendee['rsvp'] == 'true' || $attendee['rsvp'] == '1';
} }
if (!empty($event['_identity'])) {
$identity = $this->rc->user->get_identity($event['_identity']);
}
// set new organizer identity // set new organizer identity
if ($organizer !== false && !empty($event['_identity']) && ($identity = $this->rc->user->get_identity($event['_identity']))) { if ($organizer !== false && $identity) {
$event['attendees'][$organizer]['name'] = $identity['name']; $event['attendees'][$organizer]['name'] = $identity['name'];
$event['attendees'][$organizer]['email'] = $identity['email']; $event['attendees'][$organizer]['email'] = $identity['email'];
} }
// set owner as organizer if yet missing // set owner as organizer if yet missing
if ($organizer === false && $owner !== false) { else if ($organizer === false && $owner !== false) {
$event['attendees'][$owner]['role'] = 'ORGANIZER'; $event['attendees'][$owner]['role'] = 'ORGANIZER';
unset($event['attendees'][$owner]['rsvp']); unset($event['attendees'][$owner]['rsvp']);
} }
// fallback to the selected identity
else if ($organizer === false && $identity) {
$event['attendees'][] = array(
'role' => 'ORGANIZER',
'name' => $identity['name'],
'email' => $identity['email'],
);
}
} }
// mapping url => vurl because of the fullcalendar client script // mapping url => vurl because of the fullcalendar client script
@ -2128,6 +2189,7 @@ class calendar extends rcube_plugin
// if the backend has free-busy information // if the backend has free-busy information
$fblist = $this->driver->get_freebusy_list($email, $start, $end); $fblist = $this->driver->get_freebusy_list($email, $start, $end);
if (is_array($fblist)) { if (is_array($fblist)) {
$status = 'FREE'; $status = 'FREE';
@ -2141,7 +2203,7 @@ class calendar extends rcube_plugin
} }
// let this information be cached for 5min // let this information be cached for 5min
send_future_expire_header(300); $this->rc->output->future_expire_header(300);
echo $status; echo $status;
exit; exit;
@ -2179,11 +2241,24 @@ class calendar extends rcube_plugin
} }
$fblist = $this->driver->get_freebusy_list($email, $start, $end); $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 // build a list from $start till $end with blocks representing the fb-status
for ($s = 0, $t = $start; $t <= $end; $s++) { for ($s = 0, $t = $start; $t <= $end; $s++) {
$status = self::FREEBUSY_UNKNOWN;
$t_end = $t + $interval * 60; $t_end = $t + $interval * 60;
$dt = new DateTime('@'.$t); $dt = new DateTime('@'.$t);
$dt->setTimezone($this->timezone); $dt->setTimezone($this->timezone);
@ -2191,16 +2266,10 @@ class calendar extends rcube_plugin
// determine attendee's status // determine attendee's status
if (is_array($fblist)) { if (is_array($fblist)) {
$status = self::FREEBUSY_FREE; $status = self::FREEBUSY_FREE;
foreach ($fblist as $slot) { foreach ($fblist as $slot) {
list($from, $to, $type) = $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) { if ($from < $t_end && $to > $t) {
$status = isset($type) ? $type : self::FREEBUSY_BUSY; $status = isset($type) ? $type : self::FREEBUSY_BUSY;
if ($status == self::FREEBUSY_BUSY) // can't get any worse :-) if ($status == self::FREEBUSY_BUSY) // can't get any worse :-)
@ -2208,9 +2277,12 @@ class calendar extends rcube_plugin
} }
} }
} }
else {
$status = self::FREEBUSY_UNKNOWN;
}
$slots[$s] = $status; // use most compact format, assume $status is one digit/character
$times[$s] = intval($dt->format($strformat)); $slots .= $status;
$t = $t_end; $t = $t_end;
} }
@ -2218,15 +2290,14 @@ class calendar extends rcube_plugin
$dte->setTimezone($this->timezone); $dte->setTimezone($this->timezone);
// let this information be cached for 5min // let this information be cached for 5min
send_future_expire_header(300); $this->rc->output->future_expire_header(300);
echo json_encode(array( echo rcube_output::json_serialize(array(
'email' => $email, 'email' => $email,
'start' => $dts->format('c'), 'start' => $dts->format('c'),
'end' => $dte->format('c'), 'end' => $dte->format('c'),
'interval' => $interval, 'interval' => $interval,
'slots' => $slots, 'slots' => $slots,
'times' => $times,
)); ));
exit; exit;
} }
@ -2288,12 +2359,12 @@ class calendar extends rcube_plugin
$this->ui->calendar_list(); # set env['calendars'] $this->ui->calendar_list(); # set env['calendars']
echo $this->api->output->parse('calendar.eventedit', false, false); echo $this->api->output->parse('calendar.eventedit', false, false);
echo html::tag('script', array('type' => 'text/javascript'), echo html::tag('script', array('type' => 'text/javascript'),
"rcmail.set_env('calendars', " . json_encode($this->api->output->env['calendars']) . ");\n". "rcmail.set_env('calendars', " . rcube_output::json_serialize($this->api->output->env['calendars']) . ");\n".
"rcmail.set_env('deleteicon', '" . $this->api->output->env['deleteicon'] . "');\n". "rcmail.set_env('deleteicon', '" . $this->api->output->env['deleteicon'] . "');\n".
"rcmail.set_env('cancelicon', '" . $this->api->output->env['cancelicon'] . "');\n". "rcmail.set_env('cancelicon', '" . $this->api->output->env['cancelicon'] . "');\n".
"rcmail.set_env('loadingicon', '" . $this->api->output->env['loadingicon'] . "');\n". "rcmail.set_env('loadingicon', '" . $this->api->output->env['loadingicon'] . "');\n".
"rcmail.gui_object('attachmentlist', '" . $this->ui->attachmentlist_id . "');\n". "rcmail.gui_object('attachmentlist', '" . $this->ui->attachmentlist_id . "');\n".
"rcmail.add_label(" . json_encode($texts) . ");\n" "rcmail.add_label(" . rcube_output::json_serialize($texts) . ");\n"
); );
exit; exit;
} }
@ -2457,6 +2528,27 @@ class calendar extends rcube_plugin
/**** Event invitation plugin hooks ****/ /**** Event invitation plugin hooks ****/
/**
* Find an event in user calendars
*/
protected function find_event($event, &$mode)
{
$this->load_driver();
// We search for writeable calendars in personal namespace by default
$mode = calendar_driver::FILTER_WRITEABLE | calendar_driver::FILTER_PERSONAL;
$result = $this->driver->get_event($event, $mode);
// ... now check shared folders if not found
if (!$result) {
$result = $this->driver->get_event($event, calendar_driver::FILTER_WRITEABLE | calendar_driver::FILTER_SHARED);
if ($result) {
$mode |= calendar_driver::FILTER_SHARED;
}
}
return $result;
}
/** /**
* Handler for calendar/itip-status requests * Handler for calendar/itip-status requests
*/ */
@ -2464,16 +2556,20 @@ class calendar extends rcube_plugin
{ {
$data = rcube_utils::get_input_value('data', rcube_utils::INPUT_POST, true); $data = rcube_utils::get_input_value('data', rcube_utils::INPUT_POST, true);
// find local copy of the referenced event
$this->load_driver(); $this->load_driver();
$existing = $this->driver->get_event($data, calendar_driver::FILTER_WRITEABLE | calendar_driver::FILTER_PERSONAL);
$itip = $this->load_itip(); // find local copy of the referenced event (in personal namespace)
$response = $itip->get_itip_status($data, $existing); $existing = $this->find_event($data, $mode);
$is_shared = $mode & calendar_driver::FILTER_SHARED;
$itip = $this->load_itip();
$response = $itip->get_itip_status($data, $existing);
// get a list of writeable calendars to save new events to // get a list of writeable calendars to save new events to
if (!$existing && !$data['nosave'] && $response['action'] == 'rsvp' || $response['action'] == 'import') { if ((!$existing || $is_shared)
$calendars = $this->driver->list_calendars(calendar_driver::FILTER_PERSONAL); && !$data['nosave']
&& ($response['action'] == 'rsvp' || $response['action'] == 'import')
) {
$calendars = $this->driver->list_calendars($mode);
$calendar_select = new html_select(array('name' => 'calendar', 'id' => 'itip-saveto', 'is_escaped' => true)); $calendar_select = new html_select(array('name' => 'calendar', 'id' => 'itip-saveto', 'is_escaped' => true));
$calendar_select->add('--', ''); $calendar_select->add('--', '');
$numcals = 0; $numcals = 0;
@ -2483,14 +2579,14 @@ class calendar extends rcube_plugin
$numcals++; $numcals++;
} }
} }
if ($numcals <= 1) if ($numcals < 1)
$calendar_select = null; $calendar_select = null;
} }
if ($calendar_select) { if ($calendar_select) {
$default_calendar = $this->get_default_calendar($data['sensitivity']); $default_calendar = $this->get_default_calendar($data['sensitivity'], $calendars);
$response['select'] = html::span('folder-select', $this->gettext('saveincalendar') . '&nbsp;' . $response['select'] = html::span('folder-select', $this->gettext('saveincalendar') . '&nbsp;' .
$calendar_select->show($default_calendar['id'])); $calendar_select->show($is_shared ? $existing['calendar'] : $default_calendar['id']));
} }
else if ($data['nosave']) { else if ($data['nosave']) {
$response['select'] = html::tag('input', array('type' => 'hidden', 'name' => 'calendar', 'id' => 'itip-saveto', 'value' => '')); $response['select'] = html::tag('input', array('type' => 'hidden', 'name' => 'calendar', 'id' => 'itip-saveto', 'value' => ''));
@ -2539,9 +2635,10 @@ class calendar extends rcube_plugin
$uid = rcube_utils::get_input_value('uid', rcube_utils::INPUT_POST); $uid = rcube_utils::get_input_value('uid', rcube_utils::INPUT_POST);
$instance = rcube_utils::get_input_value('_instance', rcube_utils::INPUT_POST); $instance = rcube_utils::get_input_value('_instance', rcube_utils::INPUT_POST);
$savemode = rcube_utils::get_input_value('_savemode', rcube_utils::INPUT_POST); $savemode = rcube_utils::get_input_value('_savemode', rcube_utils::INPUT_POST);
$listmode = calendar_driver::FILTER_WRITEABLE | calendar_driver::FILTER_PERSONAL;
// search for event if only UID is given // search for event if only UID is given
if ($event = $this->driver->get_event(array('uid' => $uid, '_instance' => $instance), calendar_driver::FILTER_WRITEABLE)) { if ($event = $this->driver->get_event(array('uid' => $uid, '_instance' => $instance), $listmode)) {
$event['_savemode'] = $savemode; $event['_savemode'] = $savemode;
$success = $this->driver->remove_event($event, true); $success = $this->driver->remove_event($event, true);
} }
@ -2559,6 +2656,8 @@ class calendar extends rcube_plugin
*/ */
public function itip_attend_response($p) public function itip_attend_response($p)
{ {
$this->setup();
if ($p['action'] == 'attend') { if ($p['action'] == 'attend') {
$this->ui->init(); $this->ui->init();
@ -2772,17 +2871,17 @@ class calendar extends rcube_plugin
$noreply = $noreply || $status == 'needs-action' || $itip_sending === 0; $noreply = $noreply || $status == 'needs-action' || $itip_sending === 0;
$instance = rcube_utils::get_input_value('_instance', rcube_utils::INPUT_POST); $instance = rcube_utils::get_input_value('_instance', rcube_utils::INPUT_POST);
$savemode = rcube_utils::get_input_value('_savemode', rcube_utils::INPUT_POST); $savemode = rcube_utils::get_input_value('_savemode', rcube_utils::INPUT_POST);
$comment = rcube_utils::get_input_value('_comment', rcube_utils::INPUT_POST);
$error_msg = $this->gettext('errorimportingevent'); $error_msg = $this->gettext('errorimportingevent');
$success = false; $success = false;
$delegate = null;
if ($status == 'delegated') { if ($status == 'delegated') {
$delegates = rcube_mime::decode_address_list(rcube_utils::get_input_value('_to', rcube_utils::INPUT_POST, true), 1, false); $delegates = rcube_mime::decode_address_list(rcube_utils::get_input_value('_to', rcube_utils::INPUT_POST, true), 1, false);
$delegate = reset($delegates); $delegate = reset($delegates);
if (empty($delegate) || empty($delegate['mailto'])) { if (empty($delegate) || empty($delegate['mailto'])) {
$this->rc->output->command('display_message', $this->gettext('libcalendaring.delegateinvalidaddress'), 'error'); $this->rc->output->command('display_message', $this->rc->gettext('libcalendaring.delegateinvalidaddress'), 'error');
return; return;
} }
} }
@ -2791,29 +2890,37 @@ class calendar extends rcube_plugin
if ($event = $this->lib->mail_get_itip_object($mbox, $uid, $mime_id, 'event')) { if ($event = $this->lib->mail_get_itip_object($mbox, $uid, $mime_id, 'event')) {
// forward iTip request to delegatee // forward iTip request to delegatee
if ($delegate) { if ($delegate) {
$rsvpme = intval(rcube_utils::get_input_value('_rsvp', rcube_utils::INPUT_POST)); $rsvpme = rcube_utils::get_input_value('_rsvp', rcube_utils::INPUT_POST);
$itip = $this->load_itip();
$itip = $this->load_itip(); $event['comment'] = $comment;
if ($itip->delegate_to($event, $delegate, $rsvpme ? true : false)) {
if ($itip->delegate_to($event, $delegate, !empty($rsvpme))) {
$this->rc->output->show_message('calendar.itipsendsuccess', 'confirmation'); $this->rc->output->show_message('calendar.itipsendsuccess', 'confirmation');
} }
else { else {
$this->rc->output->command('display_message', $this->gettext('itipresponseerror'), 'error'); $this->rc->output->command('display_message', $this->gettext('itipresponseerror'), 'error');
} }
unset($event['comment']);
// the delegator is set to non-participant, thus save as non-blocking // the delegator is set to non-participant, thus save as non-blocking
$event['free_busy'] = 'free'; $event['free_busy'] = 'free';
} }
$mode = calendar_driver::FILTER_PERSONAL
| calendar_driver::FILTER_SHARED
| calendar_driver::FILTER_WRITEABLE;
// find writeable calendar to store event // find writeable calendar to store event
$cal_id = !empty($_REQUEST['_folder']) ? rcube_utils::get_input_value('_folder', rcube_utils::INPUT_POST) : null; $cal_id = rcube_utils::get_input_value('_folder', rcube_utils::INPUT_POST);
$dontsave = ($_REQUEST['_folder'] === '' && $event['_method'] == 'REQUEST'); $dontsave = $cal_id === '' && $event['_method'] == 'REQUEST';
$calendars = $this->driver->list_calendars(calendar_driver::FILTER_PERSONAL); $calendars = $this->driver->list_calendars($mode);
$calendar = $calendars[$cal_id]; $calendar = $calendars[$cal_id];
// select default calendar except user explicitly selected 'none' // select default calendar except user explicitly selected 'none'
if (!$calendar && !$dontsave) if (!$calendar && !$dontsave)
$calendar = $this->get_default_calendar($event['sensitivity']); $calendar = $this->get_default_calendar($event['sensitivity'], $calendars);
$metadata = array( $metadata = array(
'uid' => $event['uid'], 'uid' => $event['uid'],
@ -2861,9 +2968,16 @@ class calendar extends rcube_plugin
// save to calendar // save to calendar
if ($calendar && $calendar['editable']) { if ($calendar && $calendar['editable']) {
// check for existing event with the same UID // check for existing event with the same UID
$existing = $this->driver->get_event($event, calendar_driver::FILTER_WRITEABLE | calendar_driver::FILTER_PERSONAL); $existing = $this->find_event($event, $mode);
// we'll create a new copy if user decided to change the calendar
if ($existing && $cal_id && $calendar && $calendar['id'] != $existing['calendar']) {
$existing = null;
}
if ($existing) { if ($existing) {
$calendar = $calendars[$existing['calendar']];
// forward savemode for correct updates of recurring events // forward savemode for correct updates of recurring events
$existing['_savemode'] = $savemode ?: $event['_savemode']; $existing['_savemode'] = $savemode ?: $event['_savemode'];
@ -2874,14 +2988,14 @@ class calendar extends rcube_plugin
$existing_attendee_emails = array(); $existing_attendee_emails = array();
foreach ($existing['attendees'] as $i => $attendee) { foreach ($existing['attendees'] as $i => $attendee) {
$existing_attendee_emails[] = $attendee['email']; $existing_attendee_emails[] = $attendee['email'];
if ($event['_sender'] && ($attendee['email'] == $event['_sender'] || $attendee['email'] == $event['_sender_utf'])) { if ($this->itip->compare_email($attendee['email'], $event['_sender'], $event['_sender_utf'])) {
$existing_attendee = $i; $existing_attendee = $i;
} }
} }
$event_attendee = null; $event_attendee = null;
$update_attendees = array(); $update_attendees = array();
foreach ($event['attendees'] as $attendee) { foreach ($event['attendees'] as $attendee) {
if ($event['_sender'] && ($attendee['email'] == $event['_sender'] || $attendee['email'] == $event['_sender_utf'])) { if ($this->itip->compare_email($attendee['email'], $event['_sender'], $event['_sender_utf'])) {
$event_attendee = $attendee; $event_attendee = $attendee;
$update_attendees[] = $attendee; $update_attendees[] = $attendee;
$metadata['fallback'] = $attendee['status']; $metadata['fallback'] = $attendee['status'];
@ -2892,11 +3006,11 @@ class calendar extends rcube_plugin
} }
} }
// also copy delegate attendee // also copy delegate attendee
else if (!empty($attendee['delegated-from']) && else if (!empty($attendee['delegated-from'])
(stripos($attendee['delegated-from'], $event['_sender']) !== false || && $this->itip->compare_email($attendee['delegated-from'], $event['_sender'], $event['_sender_utf'])
stripos($attendee['delegated-from'], $event['_sender_utf']) !== false)) { ) {
$update_attendees[] = $attendee; $update_attendees[] = $attendee;
if (!in_array($attendee['email'], $existing_attendee_emails)) { if (!in_array_nocase($attendee['email'], $existing_attendee_emails)) {
$existing['attendees'][] = $attendee; $existing['attendees'][] = $attendee;
} }
} }
@ -2936,24 +3050,20 @@ class calendar extends rcube_plugin
$event['id'] = $existing['id']; $event['id'] = $existing['id'];
$event['calendar'] = $existing['calendar']; $event['calendar'] = $existing['calendar'];
// preserve my participant status for regular updates // merge attendees status
if (empty($status)) { // e.g. preserve my participant status for regular updates
$emails = $this->get_user_emails(); $this->lib->merge_attendees($event, $existing, $status);
foreach ($event['attendees'] as $i => $attendee) {
if ($attendee['email'] && in_array(strtolower($attendee['email']), $emails)) {
foreach ($existing['attendees'] as $j => $_attendee) {
if ($attendee['email'] == $_attendee['email']) {
$event['attendees'][$i] = $existing['attendees'][$j];
break;
}
}
}
}
}
// set status=CANCELLED on CANCEL messages // set status=CANCELLED on CANCEL messages
if ($event['_method'] == 'CANCEL') if ($event['_method'] == 'CANCEL')
$event['status'] = 'CANCELLED'; $event['status'] = 'CANCELLED';
// update attachments list, allow attachments update only on REQUEST (#5342)
if ($event['_method'] == 'REQUEST')
$event['deleted_attachments'] = true;
else
unset($event['attachments']);
// show me as free when declined (#1670) // show me as free when declined (#1670)
if ($status == 'declined' || $event['status'] == 'CANCELLED' || $event_attendee['role'] == 'NON-PARTICIPANT') if ($status == 'declined' || $event['status'] == 'CANCELLED' || $event_attendee['role'] == 'NON-PARTICIPANT')
$event['free_busy'] = 'free'; $event['free_busy'] = 'free';
@ -3039,7 +3149,7 @@ class calendar extends rcube_plugin
// send iTip reply // send iTip reply
if ($event['_method'] == 'REQUEST' && $organizer && !$noreply && !in_array(strtolower($organizer['email']), $emails) && !$error_msg) { if ($event['_method'] == 'REQUEST' && $organizer && !$noreply && !in_array(strtolower($organizer['email']), $emails) && !$error_msg) {
$event['comment'] = rcube_utils::get_input_value('_comment', rcube_utils::INPUT_POST); $event['comment'] = $comment;
$itip = $this->load_itip(); $itip = $this->load_itip();
$itip->set_sender_email($reply_sender); $itip->set_sender_email($reply_sender);
if ($itip->send_itip_message($event, 'REPLY', $organizer, 'itipsubject' . $status, 'itipmailbody' . $status)) if ($itip->send_itip_message($event, 'REPLY', $organizer, 'itipsubject' . $status, 'itipmailbody' . $status))
@ -3051,7 +3161,6 @@ class calendar extends rcube_plugin
$this->rc->output->send(); $this->rc->output->send();
} }
/** /**
* Handler for calendar/itip-remove requests * Handler for calendar/itip-remove requests
*/ */
@ -3104,7 +3213,7 @@ class calendar extends rcube_plugin
// establish imap connection // establish imap connection
$imap = $this->rc->get_storage(); $imap = $this->rc->get_storage();
$imap->set_mailbox($mbox); $imap->set_folder($mbox);
if ($uid && $mime_id) { if ($uid && $mime_id) {
$part = $imap->get_message_part($uid, $mime_id); $part = $imap->get_message_part($uid, $mime_id);
@ -3164,7 +3273,7 @@ class calendar extends rcube_plugin
// establish imap connection // establish imap connection
$imap = $this->rc->get_storage(); $imap = $this->rc->get_storage();
$imap->set_mailbox($mbox); $imap->set_folder($mbox);
$message = new rcube_message($uid); $message = new rcube_message($uid);
if ($message->headers) { if ($message->headers) {
@ -3241,7 +3350,12 @@ class calendar extends rcube_plugin
$tmp_path = tempnam($this->rc->config->get('temp_dir'), 'rcmAttmntCal'); $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'))); 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'); $args['attachments'][] = array(
'path' => $tmp_path,
'name' => $filename . '.ics',
'mimetype' => 'text/calendar',
'size' => filesize($tmp_path),
);
$args['param']['subject'] = $event['title']; $args['param']['subject'] = $event['title'];
} }
} }
@ -3284,6 +3398,7 @@ class calendar extends rcube_plugin
$table_itipinvitations = $db->table_name('itipinvitations', true); $table_itipinvitations = $db->table_name('itipinvitations', true);
$db->query("DELETE FROM $table_itipinvitations WHERE `user_id` = ?", $args['user']->ID); $db->query("DELETE FROM $table_itipinvitations WHERE `user_id` = ?", $args['user']->ID);
$this->setup();
$this->load_driver(); $this->load_driver();
return $this->driver->user_delete($args); return $this->driver->user_delete($args);
} }

View file

@ -34,7 +34,6 @@ function rcube_calendar(settings)
rcube_libcalendaring.call(this, settings); rcube_libcalendaring.call(this, settings);
// member vars // member vars
this.ui;
this.ui_loaded = false; this.ui_loaded = false;
this.selected_attachment = null; this.selected_attachment = null;
@ -50,7 +49,7 @@ function rcube_calendar(settings)
$.when( $.when(
$.getScript(rcmail.assets_path('plugins/calendar/calendar_ui.js')), $.getScript(rcmail.assets_path('plugins/calendar/calendar_ui.js')),
$.getScript(rcmail.assets_path('plugins/calendar/lib/js/fullcalendar.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() { ).then(function() {
// disable attendees feature (autocompletion and stuff is not initialized) // disable attendees feature (autocompletion and stuff is not initialized)
for (var c in rcmail.env.calendars) for (var c in rcmail.env.calendars)
@ -60,16 +59,16 @@ function rcube_calendar(settings)
me.ui = new rcube_calendar_ui(me.settings); me.ui = new rcube_calendar_ui(me.settings);
me.create_from_mail(uid); // start over me.create_from_mail(uid); // start over
}); });
return; return;
} }
else {
// get message contents for event dialog // get message contents for event dialog
var lock = rcmail.set_busy(true, 'loading'); var lock = rcmail.set_busy(true, 'loading');
rcmail.http_post('calendar/mailtoevent', { rcmail.http_post('calendar/mailtoevent', {
'_mbox': rcmail.env.mailbox, '_mbox': rcmail.env.mailbox,
'_uid': uid '_uid': uid
}, lock); }, lock);
}
} }
}; };
@ -91,7 +90,7 @@ function rcube_calendar(settings)
rcmail.http_post('calendar/mailimportattach', { rcmail.http_post('calendar/mailimportattach', {
_uid: rcmail.env.uid, _uid: rcmail.env.uid,
_mbox: rcmail.env.mailbox, _mbox: rcmail.env.mailbox,
_part: this.selected_attachment, _part: this.selected_attachment
// _calendar: $('#calendar-attachment-saveto').val(), // _calendar: $('#calendar-attachment-saveto').val(),
}, rcmail.set_busy(true, 'itip.savingdata')); }, rcmail.set_busy(true, 'itip.savingdata'));
} }
@ -106,10 +105,10 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
// register create-from-mail command to message_commands array // register create-from-mail command to message_commands array
if (rcmail.env.task == 'mail') { if (rcmail.env.task == 'mail') {
rcmail.register_command('calendar-create-from-mail', function() { cal.create_from_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.register_command('attachment-save-calendar', function() { cal.save_to_calendar(); });
rcmail.addEventListener('plugin.mail2event_dialog', function(p){ cal.mail2event_dialog(p) }); 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.addEventListener('plugin.unlock_saving', function(p) { cal.ui && cal.ui.unlock_saving(); });
if (rcmail.env.action != 'show') { if (rcmail.env.action != 'show') {
rcmail.env.message_commands.push('calendar-create-from-mail'); rcmail.env.message_commands.push('calendar-create-from-mail');
@ -131,7 +130,7 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
rcmail.register_command('plugin.calendar', function() { rcmail.switch_task('calendar'); }, true); 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; var action = p.action;
p.action = p.event = null; p.action = p.event = null;
new Image().src = rcmail.url(action, p); new Image().src = rcmail.url(action, p);

View file

@ -38,7 +38,7 @@ function rcube_calendar_ui(settings)
this.selected_event = null; this.selected_event = null;
this.selected_calendar = null; this.selected_calendar = null;
this.search_request = null; this.search_request = null;
this.saving_lock; this.saving_lock = null;
this.calendars = {}; this.calendars = {};
this.quickview_sources = []; this.quickview_sources = [];
@ -197,18 +197,18 @@ function rcube_calendar_ui(settings)
{ {
var result = [], var result = [],
strlen = str.length, strlen = str.length,
q, p, i, char, last; q, p, i, chr, last;
for (q = p = i = 0; i < strlen; i++) { for (q = p = i = 0; i < strlen; i++) {
char = str.charAt(i); chr = str.charAt(i);
if (char == '"' && last != '\\') { if (chr == '"' && last != '\\') {
q = !q; q = !q;
} }
else if (!q && char == delimiter) { else if (!q && chr == delimiter) {
result.push(str.substring(p, i)); result.push(str.substring(p, i));
p = i + 1; p = i + 1;
} }
last = char; last = chr;
} }
result.push(str.substr(p)); result.push(str.substr(p));
@ -335,6 +335,9 @@ function rcube_calendar_ui(settings)
if (event.rev) if (event.rev)
query._rev = event.rev; query._rev = event.rev;
if (event.calendar == "--invitation--itip")
$.extend(query, {_uid: event._uid, _part: event._part, _mbox: event._mbox});
// open attachment in frame if it's of a supported mimetype // open attachment in frame if it's of a supported mimetype
if (id && att.mimetype && $.inArray(att.mimetype, settings.mimetypes)>=0) { if (id && att.mimetype && $.inArray(att.mimetype, settings.mimetypes)>=0) {
if (rcmail.open_window(rcmail.url('get-attachment', query), true, true)) { if (rcmail.open_window(rcmail.url('get-attachment', query), true, true)) {
@ -420,7 +423,7 @@ function rcube_calendar_ui(settings)
if ($dialog.is(':ui-dialog')) if ($dialog.is(':ui-dialog'))
$dialog.dialog('close'); $dialog.dialog('close');
// remove status-* classes // remove status-* and sensitivity-* classes
$dialog.removeClass(function(i, oldclass) { $dialog.removeClass(function(i, oldclass) {
var oldies = String(oldclass).split(' '); var oldies = String(oldclass).split(' ');
return $.grep(oldies, function(cls) { return cls.indexOf('status-') === 0 || cls.indexOf('sensitivity-') === 0 }).join(' '); return $.grep(oldies, function(cls) { return cls.indexOf('status-') === 0 || cls.indexOf('sensitivity-') === 0 }).join(' ');
@ -468,11 +471,13 @@ function rcube_calendar_ui(settings)
if (event.status) { if (event.status) {
var status_lc = String(event.status).toLowerCase(); var status_lc = String(event.status).toLowerCase();
$('#event-status').show().children('.event-text').html(Q(rcmail.gettext('status-'+status_lc,'calendar'))); $('#event-status').show().children('.event-text').text(rcmail.gettext('status-'+status_lc,'calendar'));
$('#event-status-badge > span').text(rcmail.gettext('status-'+status_lc,'calendar'));
$dialog.addClass('status-'+status_lc); $dialog.addClass('status-'+status_lc);
} }
if (event.sensitivity && event.sensitivity != 'public') { if (event.sensitivity && event.sensitivity != 'public') {
$('#event-sensitivity').show().children('.event-text').html(Q(sensitivitylabels[event.sensitivity])); $('#event-sensitivity').show().children('.event-text').text(sensitivitylabels[event.sensitivity]);
$('#event-status-badge > span').text(sensitivitylabels[event.sensitivity]);
$dialog.addClass('sensitivity-'+event.sensitivity); $dialog.addClass('sensitivity-'+event.sensitivity);
} }
if (event.created || event.changed) { if (event.created || event.changed) {
@ -510,7 +515,7 @@ function rcube_calendar_ui(settings)
return (j - k); return (j - k);
}); });
var data, mystatus = null, rsvp, line, morelink, html = '', overflow = ''; var data, organizer, mystatus = null, rsvp, line, morelink, html = '', overflow = '';
for (var j=0; j < event.attendees.length; j++) { for (var j=0; j < event.attendees.length; j++) {
data = event.attendees[j]; data = event.attendees[j];
if (data.email) { if (data.email) {
@ -523,7 +528,7 @@ function rcube_calendar_ui(settings)
} }
} }
line = event_attendee_html(data); line = rcube_libcalendaring.attendee_html(data);
if (morelink) if (morelink)
overflow += line; overflow += line;
@ -562,7 +567,7 @@ function rcube_calendar_ui(settings)
.removeClass('accepted tentative declined delegated needs-action') .removeClass('accepted tentative declined delegated needs-action')
.addClass(mystatus) .addClass(mystatus)
.children('.event-text') .children('.event-text')
.html(Q(rcmail.gettext('itip' + mystatus, 'libcalendaring'))); .text(rcmail.gettext('status' + mystatus, 'libcalendaring'));
} }
var show_rsvp = rsvp && !is_organizer(event) && event.status != 'CANCELLED' && has_permission(calendar, 'v'); var show_rsvp = rsvp && !is_organizer(event) && event.status != 'CANCELLED' && has_permission(calendar, 'v');
@ -666,24 +671,8 @@ function rcube_calendar_ui(settings)
} }
rcmail.enable_command('event-history', calendar.history) rcmail.enable_command('event-history', calendar.history)
};
// render HTML code for displaying an attendee record rcmail.triggerEvent('calendar-event-dialog', {dialog: $dialog});
var event_attendee_html = function(data)
{
var dispname = Q(data.name || data.email), tooltip = '';
if (data.email) {
tooltip = data.email + '; ' + data.status;
dispname = '<a href="mailto:' + data.email + '" class="mailtolink" data-cutype="' + data.cutype + '">' + dispname + '</a>';
}
if (data['delegated-to'])
tooltip = rcmail.gettext('delegatedto', 'calendar') + data['delegated-to'];
else if (data['delegated-from'])
tooltip = rcmail.gettext('delegatedfrom', 'calendar') + data['delegated-from'];
return '<span class="attendee ' + String(data.role == 'ORGANIZER' ? 'organizer' : data.status).toLowerCase() + '" title="' + Q(tooltip) + '">' + dispname + '</span> ';
}; };
// event handler for clicks on an attendee link // event handler for clicks on an attendee link
@ -797,8 +786,8 @@ function rcube_calendar_ui(settings)
event_attendees = []; event_attendees = [];
attendees_list = $('#edit-attendees-table > tbody').html(''); attendees_list = $('#edit-attendees-table > tbody').html('');
resources_list = $('#edit-resources-table > tbody').html(''); resources_list = $('#edit-resources-table > tbody').html('');
$('#edit-attendees-notify')[(allow_invitations && has_attendees(event) && (settings.itip_notify & 2) ? 'show' : 'hide')](); $('#edit-attendees-notify')[(action != 'new' && allow_invitations && has_attendees(event) && (settings.itip_notify & 2) ? 'show' : 'hide')]();
$('#edit-localchanges-warning')[(has_attendees(event) && !(allow_invitations || (calendar.owner && is_organizer(event, calendar.owner))) ? 'show' : 'hide')](); $('#edit-localchanges-warning')[(action != 'new' && has_attendees(event) && !(allow_invitations || (calendar.owner && is_organizer(event, calendar.owner))) ? 'show' : 'hide')]();
var load_attendees_tab = function() var load_attendees_tab = function()
{ {
@ -906,10 +895,6 @@ function rcube_calendar_ui(settings)
if (organizer) if (organizer)
data._identity = $('#edit-identities-list option:selected').val(); data._identity = $('#edit-identities-list option:selected').val();
// don't submit attendees if only myself is added as organizer
if (data.attendees.length == 1 && data.attendees[0].role == 'ORGANIZER' && String(data.attendees[0].email).toLowerCase() == settings.identity.email)
data.attendees = [];
// per-attendee notification suppression // per-attendee notification suppression
var need_invitation = false; var need_invitation = false;
if (allow_invitations) { if (allow_invitations) {
@ -1013,6 +998,8 @@ function rcube_calendar_ui(settings)
window.setTimeout(load_attendees_tab, exec_deferred); window.setTimeout(load_attendees_tab, exec_deferred);
if (calendar.attachments) if (calendar.attachments)
window.setTimeout(load_attachments_tab, exec_deferred); window.setTimeout(load_attachments_tab, exec_deferred);
rcmail.triggerEvent('calendar-event-dialog', {dialog: $dialog});
}; };
// show event changelog in a dialog // show event changelog in a dialog
@ -1116,8 +1103,8 @@ function rcube_calendar_ui(settings)
} }
// format attendees struct // format attendees struct
else if (prop == 'attendees') { else if (prop == 'attendees') {
if (change['old']) change.old_ = event_attendee_html(change['old']); if (change['old']) change.old_ = rcube_libcalendaring.attendee_html(change['old']);
if (change['new']) change.new_ = event_attendee_html($.extend({}, change['old'] || {}, change['new'])); if (change['new']) change.new_ = rcube_libcalendaring.attendee_html($.extend({}, change['old'] || {}, change['new']));
html = true; html = true;
} }
// localize priority values // localize priority values
@ -1255,7 +1242,7 @@ function rcube_calendar_ui(settings)
freebusy_data = { required:{}, all:{} }; freebusy_data = { required:{}, all:{} };
freebusy_ui.loading = 1; // prevent render_freebusy_grid() to load data yet 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.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.start = fb_start;
freebusy_ui.end = new Date(freebusy_ui.start.getTime() + DAY_MS * freebusy_ui.numdays); freebusy_ui.end = new Date(freebusy_ui.start.getTime() + DAY_MS * freebusy_ui.numdays);
render_freebusy_grid(0); render_freebusy_grid(0);
@ -1339,6 +1326,7 @@ function rcube_calendar_ui(settings)
closeOnEscape: (!bw.ie6 && !bw.ie7), closeOnEscape: (!bw.ie6 && !bw.ie7),
title: rcmail.gettext('scheduletime', 'calendar'), title: rcmail.gettext('scheduletime', 'calendar'),
open: function() { open: function() {
rcmail.ksearch_blur();
$dialog.attr('aria-hidden', 'false').find('#shedule-find-next, #shedule-find-prev').not(':disabled').first().focus(); $dialog.attr('aria-hidden', 'false').find('#shedule-find-next, #shedule-find-prev').not(':disabled').first().focus();
}, },
close: function() { close: function() {
@ -1361,7 +1349,7 @@ function rcube_calendar_ui(settings)
// adjust dialog size to fit grid without scrolling // adjust dialog size to fit grid without scrolling
var gridw = $('#schedule-freebusy-times').width(); 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)); me.dialog_resize($dialog.get(0), $dialog.height() + (bw.ie ? 20 : 0), 800 + Math.max(0, overflow));
// fetch data from server // fetch data from server
@ -1391,10 +1379,12 @@ function rcube_calendar_ui(settings)
var lastdate, datestr, css, var lastdate, datestr, css,
curdate = new Date(), curdate = new Date(),
allday = (freebusy_ui.interval == 1440), allday = (freebusy_ui.interval == 1440),
interval = allday ? 1440 : (freebusy_ui.interval * (settings.timeslots || 1));
times_css = (allday ? 'allday ' : ''), times_css = (allday ? 'allday ' : ''),
dates_row = '<tr class="dates">', dates_row = '<tr class="dates">',
times_row = '<tr class="times">', times_row = '<tr class="times">',
slots_row = ''; slots_row = '';
for (var s = 0, t = freebusy_ui.start.getTime(); t < freebusy_ui.end.getTime(); s++) { for (var s = 0, t = freebusy_ui.start.getTime(); t < freebusy_ui.end.getTime(); s++) {
curdate.setTime(t); curdate.setTime(t);
datestr = fc.fullCalendar('formatDate', curdate, date_format); datestr = fc.fullCalendar('formatDate', curdate, date_format);
@ -1407,9 +1397,9 @@ function rcube_calendar_ui(settings)
// set css class according to working hours // set css class according to working hours
css = is_weekend(curdate) || (freebusy_ui.interval <= 60 && !is_workinghour(curdate)) ? 'offhours' : 'workinghours'; 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>'; 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">&nbsp;</td>'; slots_row += '<td class="' + css + '">&nbsp;</td>';
t += freebusy_ui.interval * 60000; t += interval * 60000;
} }
dates_row += '</tr>'; dates_row += '</tr>';
times_row += '</tr>'; times_row += '</tr>';
@ -1423,7 +1413,7 @@ function rcube_calendar_ui(settings)
} }
// add line for all/required attendees // add line for all/required attendees
times_html += '<tr class="spacer"><td colspan="' + (dayslots * freebusy_ui.numdays) + '">&nbsp;</td>'; times_html += '<tr class="spacer"><td colspan="' + (dayslots * freebusy_ui.numdays) + '"></td>';
times_html += '<tr id="fbrowall">' + slots_row + '</tr>'; times_html += '<tr id="fbrowall">' + slots_row + '</tr>';
var table = $('#schedule-freebusy-times'); var table = $('#schedule-freebusy-times');
@ -1445,7 +1435,7 @@ function rcube_calendar_ui(settings)
update_freebusy_dates(newstart, new Date(newstart.getTime() + freebusy_ui.startdate.data('duration') * 1000)); update_freebusy_dates(newstart, new Date(newstart.getTime() + freebusy_ui.startdate.data('duration') * 1000));
render_freebusy_overlay(); render_freebusy_overlay();
} }
}) });
} }
// if we have loaded free-busy data, show it // if we have loaded free-busy data, show it
@ -1476,38 +1466,40 @@ function rcube_calendar_ui(settings)
overlay.draggable('disable'); overlay.draggable('disable');
} }
else { else {
var table = $('#schedule-freebusy-times'), var i, n, table = $('#schedule-freebusy-times'),
width = 0, width = 0,
pos = { top:table.children('thead').height(), left:0 }, pos = { top:table.children('thead').height(), left:0 },
eventstart = date2unixtime(clone_date(me.selected_event.start, me.selected_event.allDay?1: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, eventend = date2unixtime(clone_date(me.selected_event.end, me.selected_event.allDay?2:0)) - 60,
slotstart = date2unixtime(freebusy_ui.start), slotstart = date2unixtime(freebusy_ui.start),
slotsize = freebusy_ui.interval * 60, slotsize = freebusy_ui.interval * 60,
slotend, fraction, $cell; slotnum = freebusy_ui.interval > 60 ? 1 : (60 / freebusy_ui.interval),
cells = table.children('thead').find('td'),
cell_width = cells.first().get(0).offsetWidth,
slotend;
// iterate through slots to determine position and size of the overlay // iterate through slots to determine position and size of the overlay
table.children('thead').find('td').each(function(i, cell){ for (i=0; i < cells.length; i++) {
slotend = slotstart + slotsize - 1; for (n=0; n < slotnum; n++) {
// event starts in this slot: compute left slotend = slotstart + slotsize - 1;
if (eventstart >= slotstart && eventstart <= slotend) { // event starts in this slot: compute left
fraction = 1 - (slotend - eventstart) / slotsize; if (eventstart >= slotstart && eventstart <= slotend) {
pos.left = Math.round(cell.offsetLeft + cell.offsetWidth * fraction); 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;
} }
// 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;
}
slotstart = slotstart + slotsize;
});
if (!width) if (!width)
width = table.width() - pos.left; width = table.width() - pos.left;
// overlay is visible // overlay is visible
if (width > 0) { 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 // configure draggable
if (!overlay.data('isdraggable')) { if (!overlay.data('isdraggable')) {
@ -1530,6 +1522,7 @@ function rcube_calendar_ui(settings)
} }
else { else {
// round to 5 minutes // round to 5 minutes
// @TODO: round to timeslots?
var round = newstart.getMinutes() % 5; var round = newstart.getMinutes() % 5;
if (round > 2.5) newstart.setTime(newstart.getTime() + (5 - round) * 60000); if (round > 2.5) newstart.setTime(newstart.getTime() + (5 - round) * 60000);
else if (round > 0) newstart.setTime(newstart.getTime() - round * 60000); else if (round > 0) newstart.setTime(newstart.getTime() - round * 60000);
@ -1547,10 +1540,8 @@ function rcube_calendar_ui(settings)
else else
overlay.draggable('disable').hide(); overlay.draggable('disable').hide();
} }
}; };
// fetch free-busy information for each attendee from server // fetch free-busy information for each attendee from server
var load_freebusy_data = function(from, interval) var load_freebusy_data = function(from, interval)
{ {
@ -1578,8 +1569,8 @@ function rcube_calendar_ui(settings)
freebusy_ui.loading--; freebusy_ui.loading--;
// find attendee // find attendee
var attendee = null; var i, attendee = null;
for (var i=0; i < event_attendees.length; i++) { for (i=0; i < event_attendees.length; i++) {
if (freebusy_ui.attendees[i].email == data.email) { if (freebusy_ui.attendees[i].email == data.email) {
attendee = freebusy_ui.attendees[i]; attendee = freebusy_ui.attendees[i];
break; break;
@ -1587,25 +1578,31 @@ function rcube_calendar_ui(settings)
} }
// copy data to member var // copy data to member var
var ts, req = attendee.role != 'OPT-PARTICIPANT'; var ts, status,
freebusy_data.start = parseISO8601(data.start); 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] = {}; freebusy_data[data.email] = {};
for (var i=0; i < data.slots.length; i++) {
ts = data.times[i] + ''; for (i=0; i < data.slots.length; i++) {
freebusy_data[data.email][ts] = data.slots[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 // set totals
if (!freebusy_data.required[ts]) if (!freebusy_data.required[ts])
freebusy_data.required[ts] = [0,0,0,0]; freebusy_data.required[ts] = [0,0,0,0];
if (req) if (req)
freebusy_data.required[ts][data.slots[i]]++; freebusy_data.required[ts][status]++;
if (!freebusy_data.all[ts]) if (!freebusy_data.all[ts])
freebusy_data.all[ts] = [0,0,0,0]; 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 // hide loading indicator
var domid = String(data.email).replace(rcmail.identifier_expr, ''); var domid = String(data.email).replace(rcmail.identifier_expr, '');
@ -1668,27 +1665,51 @@ function rcube_calendar_ui(settings)
if (fbdata && fbdata[ts] !== undefined && row.length) { if (fbdata && fbdata[ts] !== undefined && row.length) {
t = freebusy_ui.start.getTime(); t = freebusy_ui.start.getTime();
row.children().each(function(i, cell){ row.children().each(function(i, cell) {
curdate.setTime(t); var j, n, attr, last, all_slots = [], slots = [],
ts = date2timestring(curdate, dateonly); all_cell = rowall.get(i),
cell.className = cell.className.replace('unknown', fbdata[ts] ? status_classes[fbdata[ts]] : 'unknown'); cnt = dateonly ? 1 : (60 / freebusy_ui.interval),
percent = (100 / cnt);
// also update total row if all data was loaded for (n=0; n < cnt; n++) {
if (freebusy_ui.loading == 0 && freebusy_data.all[ts] && (cell = rowall.get(i))) { curdate.setTime(t);
var workinghours = cell.className.indexOf('workinghours') >= 0; ts = date2timestring(curdate, dateonly);
var all_status = freebusy_data.all[ts][2] ? 'busy' : 'unknown'; attr = {
req_status = freebusy_data.required[ts][2] ? 'busy' : 'free'; 'style': 'float:left; width:' + percent.toFixed(2) + '%',
for (var j=1; j < status_classes.length; j++) { 'class': fbdata[ts] ? status_classes[fbdata[ts]] : 'unknown'
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) slots.push($('<div>').attr(attr));
all_status = status_classes[j];
// 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);
}); });
} }
}; };
@ -1728,17 +1749,20 @@ function rcube_calendar_ui(settings)
sinterval = freebusy_data.interval * 60000, sinterval = freebusy_data.interval * 60000,
intvlslots = 1, intvlslots = 1,
numslots = Math.ceil(duration / sinterval), 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 // shift event times to next possible slot
eventstart += sinterval * intvlslots * dir; eventstart += sinterval * intvlslots * dir;
eventend += sinterval * intvlslots * dir; eventend += sinterval * intvlslots * dir;
// iterate through free-busy slots and find candidates // iterate through free-busy slots and find candidates
var candidatecount = 0, candidatestart = candidateend = success = false; for (slot = dir > 0 ? fb_start : fb_end - sinterval;
for (slot = dir > 0 ? freebusy_data.start.getTime() : freebusy_data.end.getTime() - sinterval; (dir > 0 && slot < fb_end) || (dir < 0 && slot >= fb_start);
(dir > 0 && slot < freebusy_data.end.getTime()) || (dir < 0 && slot >= freebusy_data.start.getTime()); slot += sinterval * dir
slot += sinterval * dir) { ) {
slotdate.setTime(slot); slotdate.setTime(slot);
// fix slot if just crossed a DST change // fix slot if just crossed a DST change
if (event.allDay) { if (event.allDay) {
@ -1750,10 +1774,10 @@ function rcube_calendar_ui(settings)
if ((dir > 0 && slotend <= eventstart) || (dir < 0 && slot >= eventend)) // skip if ((dir > 0 && slotend <= eventstart) || (dir < 0 && slot >= eventend)) // skip
continue; continue;
// respect workingours setting // respect workinghours setting
if (freebusy_ui.workinhoursonly) { if (freebusy_ui.workinhoursonly) {
if (is_weekend(slotdate) || (freebusy_data.interval <= 60 && !is_workinghour(slotdate))) { // skip off-hours if (is_weekend(slotdate) || (freebusy_data.interval <= 60 && !is_workinghour(slotdate))) { // skip off-hours
candidatestart = candidateend = false; candidatestart = false;
candidatecount = 0; candidatecount = 0;
continue; continue;
} }
@ -1762,11 +1786,12 @@ function rcube_calendar_ui(settings)
if (!candidatestart) if (!candidatestart)
candidatestart = slot; candidatestart = slot;
// check freebusy data for all attendees
ts = date2timestring(slotdate, freebusy_data.interval > 60); ts = date2timestring(slotdate, freebusy_data.interval > 60);
// check freebusy data for all attendees
for (var i=0; i < event_attendees.length; i++) { 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) { 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; break;
} }
} }
@ -1777,22 +1802,15 @@ function rcube_calendar_ui(settings)
candidatecount = 0; candidatecount = 0;
continue; continue;
} }
else if (dir < 0)
candidatestart = slot;
// set candidate end to slot end time
candidatecount++; candidatecount++;
if (dir < 0 && !candidateend)
candidateend = slotend;
// if candidate is big enough, this is it! // if candidate is big enough, this is it!
if (candidatecount == numslots) { if (candidatecount == numslots) {
if (dir > 0) { event.start.setTime(candidatestart);
event.start.setTime(candidatestart); event.end.setTime(candidatestart + duration);
event.end.setTime(candidatestart + duration);
}
else {
event.end.setTime(candidateend);
event.start.setTime(candidateend - duration);
}
success = true; success = true;
break; break;
} }
@ -1822,7 +1840,6 @@ function rcube_calendar_ui(settings)
} }
}; };
// update event properties and attendees availability if event times have changed // update event properties and attendees availability if event times have changed
var event_times_changed = function() var event_times_changed = function()
{ {
@ -1837,7 +1854,6 @@ function rcube_calendar_ui(settings)
} }
}; };
// add the given list of participants // add the given list of participants
var add_attendees = function(names, params) var add_attendees = function(names, params)
{ {
@ -1918,7 +1934,8 @@ function rcube_calendar_ui(settings)
// delete icon // delete icon
var icon = rcmail.env.deleteicon ? '<img src="' + rcmail.env.deleteicon + '" alt="" />' : rcmail.gettext('delete'); var icon = rcmail.env.deleteicon ? '<img src="' + rcmail.env.deleteicon + '" alt="" />' : rcmail.gettext('delete');
var dellink = '<a href="#delete" class="iconlink delete deletelink" title="' + Q(rcmail.gettext('delete')) + '">' + icon + '</a>'; var dellink = '<a href="#delete" class="iconlink delete deletelink" title="' + Q(rcmail.gettext('delete')) + '">' + icon + '</a>';
var tooltip = data.status || ''; var tooltip = '', status = (data.status || '').toLowerCase(),
status_label = rcmail.gettext('status' + status, 'libcalendaring');
// send invitation checkbox // send invitation checkbox
var invbox = '<input type="checkbox" class="edit-attendee-reply" value="' + Q(data.email) +'" title="' + Q(rcmail.gettext('calendar.sendinvitations')) + '" ' var invbox = '<input type="checkbox" class="edit-attendee-reply" value="' + Q(data.email) +'" title="' + Q(rcmail.gettext('calendar.sendinvitations')) + '" '
@ -1928,6 +1945,8 @@ function rcube_calendar_ui(settings)
tooltip = rcmail.gettext('delegatedto', 'calendar') + data['delegated-to']; tooltip = rcmail.gettext('delegatedto', 'calendar') + data['delegated-to'];
else if (data['delegated-from']) else if (data['delegated-from'])
tooltip = rcmail.gettext('delegatedfrom', 'calendar') + data['delegated-from']; tooltip = rcmail.gettext('delegatedfrom', 'calendar') + data['delegated-from'];
else if (status)
tooltip = status_label;
// add expand button for groups // add expand button for groups
if (data.cutype == 'GROUP') { if (data.cutype == 'GROUP') {
@ -1939,7 +1958,7 @@ function rcube_calendar_ui(settings)
var html = '<td class="role">' + select + '</td>' + var html = '<td class="role">' + select + '</td>' +
'<td class="name"><span class="attendee-name">' + dispname + '</span></td>' + '<td class="name"><span class="attendee-name">' + dispname + '</span></td>' +
'<td class="availability"><img src="' + img_src + '" class="availabilityicon ' + avail + '" data-email="' + data.email + '" alt="" /></td>' + '<td class="availability"><img src="' + img_src + '" class="availabilityicon ' + avail + '" data-email="' + data.email + '" alt="" /></td>' +
'<td class="confirmstate"><span class="' + String(data.status).toLowerCase() + '" title="' + Q(tooltip) + '">' + Q(data.status || '') + '</span></td>' + '<td class="confirmstate"><span class="' + status + '" title="' + Q(tooltip) + '">' + Q(status ? status_label : '') + '</span></td>' +
(data.cutype != 'RESOURCE' ? '<td class="invite">' + (organizer || readonly || !invbox ? '' : invbox) + '</td>' : '') + (data.cutype != 'RESOURCE' ? '<td class="invite">' + (organizer || readonly || !invbox ? '' : invbox) + '</td>' : '') +
'<td class="options">' + (organizer || readonly ? '' : dellink) + '</td>'; '<td class="options">' + (organizer || readonly ? '' : dellink) + '</td>';
@ -2045,6 +2064,7 @@ function rcube_calendar_ui(settings)
closeOnEscape: true, closeOnEscape: true,
title: rcmail.gettext('findresources', 'calendar'), title: rcmail.gettext('findresources', 'calendar'),
open: function() { open: function() {
rcmail.ksearch_blur();
$dialog.attr('aria-hidden', 'false'); $dialog.attr('aria-hidden', 'false');
}, },
close: function() { close: function() {
@ -2512,6 +2532,12 @@ function rcube_calendar_ui(settings)
// display confirm dialog when modifying/deleting an event // display confirm dialog when modifying/deleting an event
var update_event_confirm = function(action, event, data) var update_event_confirm = function(action, event, data)
{ {
// Allow other plugins to do actions here
// E.g. when you move/resize the event init wasn't called
// but we need it as some plugins may modify user identities
// we depend on here (kolab_delegation)
rcmail.triggerEvent('calendar-event-init', {o: event});
if (!data) data = event; if (!data) data = event;
var decline = false, notify = false, html = '', cal = me.calendars[event.calendar], var decline = false, notify = false, html = '', cal = me.calendars[event.calendar],
_has_attendees = has_attendees(event), _is_organizer = is_organizer(event); _has_attendees = has_attendees(event), _is_organizer = is_organizer(event);
@ -2519,6 +2545,7 @@ function rcube_calendar_ui(settings)
// event has attendees, ask whether to notify them // event has attendees, ask whether to notify them
if (_has_attendees) { if (_has_attendees) {
var checked = (settings.itip_notify & 1 ? ' checked="checked"' : ''); var checked = (settings.itip_notify & 1 ? ' checked="checked"' : '');
if (_is_organizer) { if (_is_organizer) {
notify = true; notify = true;
if (settings.itip_notify & 2) { if (settings.itip_notify & 2) {
@ -2958,6 +2985,15 @@ function rcube_calendar_ui(settings)
return false; return false;
}; };
this.calendar_refresh_source = function(id)
{
// got race-conditions fc.currentFetchID when using refetchEvents,
// so we remove and add the source instead
// fc.fullCalendar('refetchEvents', me.calendars[id]);
fc.fullCalendar('removeEventSource', me.calendars[id]);
fc.fullCalendar('addEventSource', me.calendars[id]);
};
this.calendar_destroy_source = function(id) this.calendar_destroy_source = function(id)
{ {
var delete_ids = []; var delete_ids = [];
@ -3042,7 +3078,6 @@ function rcube_calendar_ui(settings)
buttons: buttons, buttons: buttons,
width: 520 width: 520
}).show(); }).show();
}; };
// callback from server if import succeeded // callback from server if import succeeded
@ -3100,6 +3135,7 @@ function rcube_calendar_ui(settings)
rcmail.goto_url('export_events', { source:source, start:start, attachments:attachmt?1:0 }); rcmail.goto_url('export_events', { source:source, start:start, attachments:attachmt?1:0 });
} }
$dialog.dialog("close");
}; };
buttons[rcmail.gettext('cancel', 'calendar')] = function() { buttons[rcmail.gettext('cancel', 'calendar')] = function() {
@ -3122,7 +3158,6 @@ function rcube_calendar_ui(settings)
buttons: buttons, buttons: buttons,
width: 520 width: 520
}).show(); }).show();
}; };
// download the selected event as iCal // download the selected event as iCal
@ -3142,6 +3177,28 @@ 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 // show URL of the given calendar in a dialog box
this.showurl = function(calendar) this.showurl = function(calendar)
{ {
@ -3465,8 +3522,7 @@ function rcube_calendar_ui(settings)
this.dialog_resize = function(id, height, width) this.dialog_resize = function(id, height, width)
{ {
var win = $(window), w = win.width(), h = win.height(); 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) }) $(id).dialog('option', { height: Math.min(h-20, height+130), width: Math.min(w-20, width+50) });
.dialog('option', 'position', ['center', 'center']); // only works in a separate call (!?)
}; };
// adjust calendar view size // adjust calendar view size
@ -3832,77 +3888,6 @@ function rcube_calendar_ui(settings)
} }
})); }));
// format time string
var formattime = function(hour, minutes, start) {
var time, diff, unit, duration = '', d = new Date();
d.setHours(hour);
d.setMinutes(minutes);
time = $.fullCalendar.formatDate(d, settings['time_format']);
if (start) {
diff = Math.floor((d.getTime() - start.getTime()) / 60000);
if (diff > 0) {
unit = 'm';
if (diff >= 60) {
unit = 'h';
diff = Math.round(diff / 3) / 20;
}
duration = ' (' + diff + unit + ')';
}
}
return [time, duration];
};
var autocomplete_times = function(p, callback) {
/* Time completions */
var result = [];
var now = new Date();
var st, start = (String(this.element.attr('id')).indexOf('endtime') > 0
&& (st = $('#edit-starttime').val())
&& $('#edit-startdate').val() == $('#edit-enddate').val())
? parse_datetime(st, '') : null;
var full = p.term - 1 > 0 || p.term.length > 1;
var hours = start ? start.getHours() :
(full ? parse_datetime(p.term, '') : now).getHours();
var step = 15;
var minutes = hours * 60 + (full ? 0 : now.getMinutes());
var min = Math.ceil(minutes / step) * step % 60;
var hour = Math.floor(Math.ceil(minutes / step) * step / 60);
// list hours from 0:00 till now
for (var h = start ? start.getHours() : 0; h < hours; h++)
result.push(formattime(h, 0, start));
// list 15min steps for the next two hours
for (; h < hour + 2 && h < 24; h++) {
while (min < 60) {
result.push(formattime(h, min, start));
min += step;
}
min = 0;
}
// list the remaining hours till 23:00
while (h < 24)
result.push(formattime((h++), 0, start));
return callback(result);
};
var autocomplete_open = function(event, ui) {
// scroll to current time
var $this = $(this);
var widget = $this.autocomplete('widget');
var menu = $this.data('ui-autocomplete').menu;
var amregex = /^(.+)(a[.m]*)/i;
var pmregex = /^(.+)(a[.m]*)/i;
var val = $(this).val().replace(amregex, '0:$1').replace(pmregex, '1:$1');
var li, html;
widget.css('width', '10em');
widget.children().each(function(){
li = $(this);
html = li.children().first().html().replace(/\s+\(.+\)$/, '').replace(amregex, '0:$1').replace(pmregex, '1:$1');
if (html.indexOf(val) == 0)
menu._scrollIntoView(li);
});
};
// if start date is changed, shift end date according to initial duration // if start date is changed, shift end date according to initial duration
var shift_enddate = function(dateText) { var shift_enddate = function(dateText) {
var newstart = parse_datetime('0', dateText); var newstart = parse_datetime('0', dateText);
@ -3978,23 +3963,24 @@ function rcube_calendar_ui(settings)
// init event dialog // init event dialog
$('#eventtabs').tabs({ $('#eventtabs').tabs({
activate: function(event, ui) { activate: function(event, ui) {
if (ui.newPanel.selector == '#event-panel-attendees' || ui.newPanel.selector == '#event-panel-resources') { // newPanel.selector for jQuery-UI 1.10, newPanel.attr('id') for jQuery-UI 1.12
var tab = ui.newPanel.selector == '#event-panel-resources' ? 'resource' : 'attendee'; var tab = String(ui.newPanel.selector || ui.newPanel.attr('id'))
.replace(/^#?event-panel-/, '').replace(/s$/, '');
if (tab == 'attendee' || tab == 'resource') {
if (!rcube_event.is_keyboard(event)) if (!rcube_event.is_keyboard(event))
$('#edit-'+tab+'-name').select(); $('#edit-'+tab+'-name').select();
// update free-busy status if needed // update free-busy status if needed
if (freebusy_ui.needsupdate && me.selected_event) if (freebusy_ui.needsupdate && me.selected_event)
update_freebusy_status(me.selected_event); update_freebusy_status(me.selected_event);
// add current user as organizer if non added yet // add current user as organizer if non added yet
if (!event_attendees.length) { if (tab == 'attendee' && !event_attendees.length) {
add_attendee($.extend({ role:'ORGANIZER' }, settings.identity)); add_attendee($.extend({ role:'ORGANIZER' }, settings.identity));
$('#edit-attendees-form .attendees-invitebox').show(); $('#edit-attendees-form .attendees-invitebox').show();
} }
} }
// reset autocompletion on tab change (#3389) // reset autocompletion on tab change (#3389)
if (ui.oldPanel.selector == '#event-panel-attendees' || ui.oldPanel.selector == '#event-panel-resources') { rcmail.ksearch_blur();
rcmail.ksearch_blur();
}
} }
}); });
$('#edit-enddate').datepicker(datepicker_settings); $('#edit-enddate').datepicker(datepicker_settings);
@ -4003,30 +3989,12 @@ function rcube_calendar_ui(settings)
$('#edit-allday').click(function(){ $('#edit-starttime, #edit-endtime')[(this.checked?'hide':'show')](); event_times_changed(); }); $('#edit-allday').click(function(){ $('#edit-starttime, #edit-endtime')[(this.checked?'hide':'show')](); event_times_changed(); });
// configure drop-down menu on time input fields based on jquery UI autocomplete // configure drop-down menu on time input fields based on jquery UI autocomplete
$('#edit-starttime, #edit-endtime, #eventedit input.edit-alarm-time') $('#edit-starttime, #edit-endtime, #eventedit input.edit-alarm-time').each(function() {
.attr('autocomplete', "off") me.init_time_autocomplete(this, {
.autocomplete({ container: '#eventedit',
delay: 100, change: event_times_changed
minLength: 1,
appendTo: '#eventedit',
source: autocomplete_times,
open: autocomplete_open,
change: event_times_changed,
select: function(event, ui) {
$(this).val(ui.item[0]).change();
return false;
}
})
.click(function() { // show drop-down upon clicks
$(this).autocomplete('search', $(this).val() ? $(this).val().replace(/\D.*/, "") : " ");
}).each(function(){
$(this).data('ui-autocomplete')._renderItem = function(ul, item) {
return $('<li>')
.data('ui-autocomplete-item', item)
.append('<a>' + item[0] + item[1] + '</a>')
.appendTo(ul);
};
}); });
});
// adjust end time when changing start // adjust end time when changing start
$('#edit-starttime').change(function(e) { $('#edit-starttime').change(function(e) {
@ -4222,6 +4190,7 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
rcmail.register_command('calendar-showurl', function(){ cal.showurl(cal.calendars[cal.selected_calendar]); }, false); 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-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-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); rcmail.register_command('event-history', function(p, obj, e){ cal.event_history_dialog(cal.selected_event); }, false);
// search and export events // search and export events
@ -4235,6 +4204,7 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
rcmail.register_command('add-resource', function(){ cal.add_resource2event(); }, false); rcmail.register_command('add-resource', function(){ cal.add_resource2event(); }, false);
// register callback commands // register callback commands
rcmail.addEventListener('plugin.refresh_source', function(data) { cal.calendar_refresh_source(data); });
rcmail.addEventListener('plugin.destroy_source', function(p){ cal.calendar_destroy_source(p.id); }); rcmail.addEventListener('plugin.destroy_source', function(p){ cal.calendar_destroy_source(p.id); });
rcmail.addEventListener('plugin.unlock_saving', function(p){ cal.unlock_saving(); }); rcmail.addEventListener('plugin.unlock_saving', function(p){ cal.unlock_saving(); });
rcmail.addEventListener('plugin.refresh_calendar', function(p){ cal.refresh(p); }); rcmail.addEventListener('plugin.refresh_calendar', function(p){ cal.refresh(p); });

View file

@ -26,7 +26,7 @@
"require": { "require": {
"php": ">=5.3.0", "php": ">=5.3.0",
"roundcube/plugin-installer": ">=0.1.3", "roundcube/plugin-installer": ">=0.1.3",
"kolab/libcalendaring": ">=3.2.9" "kolab/libcalendaring": ">=3.2.16"
}, },
"extra": { "extra": {
"roundcube": { "roundcube": {

View file

@ -149,7 +149,7 @@ $config['calendar_itip_smtp_user'] = 'smtpauth';
$config['calendar_itip_smtp_pass'] = '123456'; $config['calendar_itip_smtp_pass'] = '123456';
// show virtual invitation calendars (Kolab driver only) // show virtual invitation calendars (Kolab driver only)
$config['kolab_invitation_calendars'] = true; $config['kolab_invitation_calendars'] = false;
// Base URL to build fully qualified URIs to access calendars via CALDAV // Base URL to build fully qualified URIs to access calendars via CALDAV
// The following replacement variables are supported: // The following replacement variables are supported:

View file

@ -101,6 +101,7 @@ abstract class calendar_driver
const FILTER_PERSONAL = 8; const FILTER_PERSONAL = 8;
const FILTER_PRIVATE = 16; const FILTER_PRIVATE = 16;
const FILTER_CONFIDENTIAL = 32; const FILTER_CONFIDENTIAL = 32;
const FILTER_SHARED = 64;
const BIRTHDAY_CALENDAR_ID = '__bdays__'; const BIRTHDAY_CALENDAR_ID = '__bdays__';
// features supported by backend // features supported by backend
@ -632,9 +633,9 @@ abstract class calendar_driver
$cache = $rcmail->get_cache('calendar.birthdays', 'db', 3600); $cache = $rcmail->get_cache('calendar.birthdays', 'db', 3600);
$cache->expunge(); $cache->expunge();
$alarm_type = $rcmail->config->get('calendar_birthdays_alarm_type', ''); $alarm_type = $rcmail->config->get('calendar_birthdays_alarm_type', '');
$alarm_offset = $rcmail->config->get('calendar_birthdays_alarm_offset', '-1D'); $alarm_offset = $rcmail->config->get('calendar_birthdays_alarm_offset', '-1D');
$alarms = $alarm_type ? $alarm_offset . ':' . $alarm_type : null; $alarms = $alarm_type ? $alarm_offset . ':' . $alarm_type : null;
// let the user select the address books to consider in prefs // let the user select the address books to consider in prefs
$selected_sources = $rcmail->config->get('calendar_birthday_adressbooks'); $selected_sources = $rcmail->config->get('calendar_birthday_adressbooks');
@ -655,70 +656,56 @@ abstract class calendar_driver
// iterate over (cached) contacts // iterate over (cached) contacts
foreach (($cached ?: $abook->search('*', '', 2, true, true, array('birthday'))) as $contact) { foreach (($cached ?: $abook->search('*', '', 2, true, true, array('birthday'))) as $contact) {
if (is_array($contact) && !empty($contact['birthday'])) { $event = self::parse_contact($contact, $source);
try {
if (is_array($contact['birthday']))
$contact['birthday'] = reset($contact['birthday']);
$bday = $contact['birthday'] instanceof DateTime ? $contact['birthday'] : if (empty($event)) {
new DateTime($contact['birthday'], new DateTimezone('UTC')); continue;
$birthyear = $bday->format('Y'); }
}
catch (Exception $e) { // add stripped record to cache
rcube::raise_error(array( if (empty($cached)) {
'code' => 600, 'type' => 'php', $cache_records[] = array(
'file' => __FILE__, 'line' => __LINE__, 'ID' => $contact['ID'],
'message' => 'BIRTHDAY PARSE ERROR: ' . $e), 'name' => $event['_displayname'],
true, false); 'birthday' => $event['start']->format('Y-m-d'),
continue; );
}
// filter by search term (only name is involved here)
if (!empty($search) && strpos(mb_strtolower($event['title']), $search) === false) {
continue;
}
$bday = clone $event['start'];
$byear = $bday->format('Y');
// quick-and-dirty recurrence computation: just replace the year
$bday->setDate($year, $bday->format('n'), $bday->format('j'));
$bday->setTime(12, 0, 0);
$this_year = $year;
// date range reaches over multiple years: use end year if not in range
if (($bday > $end || $bday < $start) && $year2 != $year) {
$bday->setDate($year2, $bday->format('n'), $bday->format('j'));
$this_year = $year2;
}
// birthday is within requested range
if ($bday <= $end && $bday >= $start) {
unset($event['_displayname']);
$event['alarms'] = $alarms;
// if this is not the first occurence modify event details
// but not when this is "all birthdays feed" request
if ($year2 - $year < 10 && ($age = ($this_year - $byear))) {
$event['description'] = $rcmail->gettext(array('name' => 'birthdayage', 'vars' => array('age' => $age)), 'calendar');
$event['start'] = $bday;
$event['end'] = clone $bday;
unset($event['recurrence']);
} }
$display_name = rcube_addressbook::compose_display_name($contact); // add the main instance
$event_title = $rcmail->gettext(array('name' => 'birthdayeventtitle', 'vars' => array('name' => $display_name)), 'calendar'); $events[] = $event;
// add stripped record to cache
if (empty($cached)) {
$cache_records[] = array(
'ID' => $contact['ID'],
'name' => $display_name,
'birthday' => $bday->format('Y-m-d'),
);
}
// filter by search term (only name is involved here)
if (!empty($search) && strpos(mb_strtolower($event_title), $search) === false) {
continue;
}
// quick-and-dirty recurrence computation: just replace the year
$bday->setDate($year, $bday->format('n'), $bday->format('j'));
$bday->setTime(12, 0, 0);
$this_year = $year;
// date range reaches over multiple years: use end year if not in range
if (($bday > $end || $bday < $start) && $year2 != $year) {
$bday->setDate($year2, $bday->format('n'), $bday->format('j'));
$this_year = $year2;
}
// birthday is within requested range
if ($bday <= $end && $bday >= $start) {
$age = $this_year - $birthyear;
$event = array(
'id' => rcube_ldap::dn_encode('bday:' . $source . ':' . $contact['ID'] . ':' . $this_year),
'calendar' => self::BIRTHDAY_CALENDAR_ID,
'title' => $event_title,
'description' => $rcmail->gettext(array('name' => 'birthdayage', 'vars' => array('age' => $age)), 'calendar'),
// Add more contact information to description block?
'allday' => true,
'start' => $bday,
'alarms' => $alarms,
);
$event['end'] = clone $bday;
$event['end']->add(new DateInterval('PT1H'));
$events[] = $event;
}
} }
} }
@ -742,49 +729,72 @@ abstract class calendar_driver
$rcmail = rcmail::get_instance(); $rcmail = rcmail::get_instance();
if ($source && $contact_id && ($abook = $rcmail->get_address_book($source))) { if ($source && $contact_id && ($abook = $rcmail->get_address_book($source))) {
$contact = $abook->get_record($contact_id, true); if ($contact = $abook->get_record($contact_id, true)) {
return self::parse_contact($contact, $source);
if (is_array($contact) && !empty($contact['birthday'])) {
try {
if (is_array($contact['birthday']))
$contact['birthday'] = reset($contact['birthday']);
$bday = $contact['birthday'] instanceof DateTime ? $contact['birthday'] :
new DateTime($contact['birthday'], new DateTimezone('UTC'));
$birthyear = $bday->format('Y');
}
catch (Exception $e) {
rcube::raise_error(array(
'code' => 600, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => 'BIRTHDAY PARSE ERROR: ' . $e),
true, false);
return null;
}
$display_name = rcube_addressbook::compose_display_name($contact);
$event_title = $rcmail->gettext(array('name' => 'birthdayeventtitle', 'vars' => array('name' => $display_name)), 'calendar');
$event = array(
'id' => rcube_ldap::dn_encode('bday:' . $source . ':' . $contact['ID'] . ':' . $year),
'uid' => rcube_ldap::dn_encode('bday:' . $source . ':' . $contact['ID'] . ':' . $birthyear),
'calendar' => self::BIRTHDAY_CALENDAR_ID,
'title' => $event_title,
'description' => '',
'allday' => true,
'start' => $bday,
'recurrence' => array('FREQ' => 'YEARLY', 'INTERVAL' => 1),
'free_busy' => 'free',
);
$event['end'] = clone $bday;
$event['end']->add(new DateInterval('PT1H'));
return $event;
} }
} }
}
return null; /**
* Parse contact and create an event for its birthday
*
* @param array $contact Contact data
* @param string $source Addressbook source ID
*
* @return array Birthday event data
*/
public static function parse_contact($contact, $source)
{
if (!is_array($contact)) {
return;
}
if (is_array($contact['birthday'])) {
$contact['birthday'] = reset($contact['birthday']);
}
if (empty($contact['birthday'])) {
return;
}
try {
$bday = $contact['birthday'];
if (!$bday instanceof DateTime) {
$bday = new DateTime($bday, new DateTimezone('UTC'));
}
$bday->_dateonly = true;
}
catch (Exception $e) {
rcube::raise_error(array(
'code' => 600, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => 'BIRTHDAY PARSE ERROR: ' . $e->getMessage()),
true, false);
return;
}
$rcmail = rcmail::get_instance();
$birthyear = $bday->format('Y');
$display_name = rcube_addressbook::compose_display_name($contact);
$label = array('name' => 'birthdayeventtitle', 'vars' => array('name' => $display_name));
$event_title = $rcmail->gettext($label, 'calendar');
$uid = rcube_ldap::dn_encode('bday:' . $source . ':' . $contact['ID'] . ':' . $birthyear);
$event = array(
'id' => $uid,
'uid' => $uid,
'calendar' => self::BIRTHDAY_CALENDAR_ID,
'title' => $event_title,
'description' => '',
'allday' => true,
'start' => $bday,
'end' => clone $bday,
'recurrence' => array('FREQ' => 'YEARLY', 'INTERVAL' => 1),
'free_busy' => 'free',
'_displayname' => $display_name,
);
return $event;
} }
/** /**

View file

@ -188,29 +188,34 @@ class kolab_calendar extends kolab_storage_folder_api
*/ */
public function get_event($id) public function get_event($id)
{ {
// directly access storage object // remove our occurrence identifier if it's there
if (!$this->events[$id] && ($record = $this->storage->get_object($id))) $master_id = preg_replace('/-\d{8}(T\d{6})?$/', '', $id);
$this->events[$id] = $this->_to_driver_event($record, true);
// event not found, maybe a recurring instance is requested // directly access storage object
if (!$this->events[$id]) { if (!$this->events[$id] && $master_id == $id && ($record = $this->storage->get_object($id))) {
$master_id = preg_replace('/-\d+(T\d{6})?$/', '', $id); $this->events[$id] = $this->_to_driver_event($record, true);
}
// maybe a recurring instance is requested
if (!$this->events[$id] && $master_id != $id) {
$instance_id = substr($id, strlen($master_id) + 1); $instance_id = substr($id, strlen($master_id) + 1);
if ($master_id != $id && ($record = $this->storage->get_object($master_id))) { if ($record = $this->storage->get_object($master_id)) {
$master = $this->_to_driver_event($record); $master = $this->_to_driver_event($record);
} }
// check for match in top-level exceptions (aka loose single occurrences) if ($master) {
if ($master && $master['_formatobj'] && ($instance = $master['_formatobj']->get_instance($instance_id))) { // check for match in top-level exceptions (aka loose single occurrences)
$this->events[$id] = $this->_to_driver_event($instance); if ($master['_formatobj'] && ($instance = $master['_formatobj']->get_instance($instance_id))) {
} $this->events[$id] = $this->_to_driver_event($instance);
// check for match on the first instance already }
else if ($master['_instance'] && $master['_instance'] == $instance_id) { // check for match on the first instance already
$this->events[$id] = $master; else if ($master['_instance'] && $master['_instance'] == $instance_id) {
} $this->events[$id] = $master;
else if ($master && is_array($master['recurrence'])) { }
$this->get_recurring_events($record, $master['start'], null, $id); else if (is_array($master['recurrence'])) {
$this->get_recurring_events($record, $master['start'], null, $id);
}
} }
} }
@ -289,7 +294,9 @@ class kolab_calendar extends kolab_storage_folder_api
} }
// set partstat filter to skip pending and declined invitations // set partstat filter to skip pending and declined invitations
if (empty($filter_query) && $this->get_namespace() != 'other') { if (empty($filter_query) && $this->cal->rc->config->get('kolab_invitation_calendars')
&& $this->get_namespace() != 'other'
) {
$partstat_exclude = array('NEEDS-ACTION','DECLINED'); $partstat_exclude = array('NEEDS-ACTION','DECLINED');
} }
else { else {
@ -298,13 +305,13 @@ class kolab_calendar extends kolab_storage_folder_api
$events = array(); $events = array();
foreach ($this->storage->select($query) as $record) { foreach ($this->storage->select($query) as $record) {
$event = $this->_to_driver_event($record, !$virtual); $event = $this->_to_driver_event($record, !$virtual, false);
// remember seen categories // remember seen categories
if ($event['categories']) { if ($event['categories']) {
$cat = is_array($event['categories']) ? $event['categories'][0] : $event['categories']; $cat = is_array($event['categories']) ? $event['categories'][0] : $event['categories'];
$this->categories[$cat]++; $this->categories[$cat]++;
} }
// list events in requested time window // list events in requested time window
if ($event['start'] <= $end && $event['end'] >= $start) { if ($event['start'] <= $end && $event['end'] >= $start) {
@ -347,7 +354,7 @@ class kolab_calendar extends kolab_storage_folder_api
// add top-level exceptions (aka loose single occurrences) // add top-level exceptions (aka loose single occurrences)
else if (is_array($record['exceptions'])) { else if (is_array($record['exceptions'])) {
foreach ($record['exceptions'] as $ex) { foreach ($record['exceptions'] as $ex) {
$component = $this->_to_driver_event($ex); $component = $this->_to_driver_event($ex, false, false);
if ($component['start'] <= $end && $component['end'] >= $start) { if ($component['start'] <= $end && $component['end'] >= $start) {
$events[] = $component; $events[] = $component;
} }
@ -381,6 +388,10 @@ class kolab_calendar extends kolab_storage_folder_api
return true; 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 // avoid session race conditions that will loose temporary subscriptions
$this->cal->rc->session->nowrite = true; $this->cal->rc->session->nowrite = true;
@ -451,7 +462,7 @@ class kolab_calendar extends kolab_storage_folder_api
//generate new event from RC input //generate new event from RC input
$object = $this->_from_driver_event($event); $object = $this->_from_driver_event($event);
$saved = $this->storage->save($object, 'event'); $saved = $this->storage->save($object, 'event');
if (!$saved) { if (!$saved) {
rcube::raise_error(array( rcube::raise_error(array(
@ -463,7 +474,9 @@ class kolab_calendar extends kolab_storage_folder_api
} }
else { else {
// save links in configuration.relation object // save links in configuration.relation object
$this->save_links($event['uid'], $links); if ($this->save_links($event['uid'], $links)) {
$object['links'] = $links;
}
$this->events = array($event['uid'] => $this->_to_driver_event($object, true)); $this->events = array($event['uid'] => $this->_to_driver_event($object, true));
} }
@ -490,7 +503,7 @@ class kolab_calendar extends kolab_storage_folder_api
unset($event['links']); unset($event['links']);
$object = $this->_from_driver_event($event, $old); $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) { if (!$saved) {
rcube::raise_error(array( rcube::raise_error(array(
@ -501,7 +514,9 @@ class kolab_calendar extends kolab_storage_folder_api
} }
else { else {
// save links in configuration.relation object // save links in configuration.relation object
$this->save_links($event['uid'], $links); if ($this->save_links($event['uid'], $links)) {
$object['links'] = $links;
}
$updated = true; $updated = true;
$this->events = array($event['uid'] => $this->_to_driver_event($object, true)); $this->events = array($event['uid'] => $this->_to_driver_event($object, true));
@ -572,14 +587,8 @@ class kolab_calendar extends kolab_storage_folder_api
*/ */
protected function save_links($uid, $links) protected function save_links($uid, $links)
{ {
// make sure we have a valid array
if (empty($links)) {
$links = array();
}
$storage = kolab_storage_config::get_instance(); $storage = kolab_storage_config::get_instance();
$remove = array_diff($storage->get_object_links($uid), $links); return $storage->save_object_links($uid, (array) $links);
return $storage->save_object_links($uid, $links, $remove);
} }
/** /**
@ -603,14 +612,8 @@ class kolab_calendar extends kolab_storage_folder_api
// determine a reasonable end date if none given // determine a reasonable end date if none given
if (!$end) { if (!$end) {
switch ($event['recurrence']['FREQ']) {
case 'YEARLY': $intvl = 'P100Y'; break;
case 'MONTHLY': $intvl = 'P20Y'; break;
default: $intvl = 'P10Y'; break;
}
$end = clone $event['start']; $end = clone $event['start'];
$end->add(new DateInterval($intvl)); $end->add(new DateInterval('P100Y'));
} }
// copy the recurrence rule from the master event (to be used in the UI) // copy the recurrence rule from the master event (to be used in the UI)
@ -628,7 +631,7 @@ class kolab_calendar extends kolab_storage_folder_api
if (!$exception['_instance']) if (!$exception['_instance'])
$exception['_instance'] = libcalendaring::recurrence_instance_identifier($exception); $exception['_instance'] = libcalendaring::recurrence_instance_identifier($exception);
$rec_event = $this->_to_driver_event($exception); $rec_event = $this->_to_driver_event($exception, false, false);
$rec_event['id'] = $event['uid'] . '-' . $exception['_instance']; $rec_event['id'] = $event['uid'] . '-' . $exception['_instance'];
$rec_event['isexception'] = 1; $rec_event['isexception'] = 1;
@ -667,22 +670,34 @@ class kolab_calendar extends kolab_storage_folder_api
$i = 0; $i = 0;
while ($next_event = $recurrence->next_instance()) { while ($next_event = $recurrence->next_instance()) {
$datestr = $next_event['start']->format('Ymd'); $datestr = $next_event['start']->format('Ymd');
$instance_id = $next_event['start']->format($recurrence_id_format); $instance_id = $next_event['start']->format($recurrence_id_format);
// use this event data for future recurring instances // use this event data for future recurring instances
if ($futuredata[$datestr]) if ($futuredata[$datestr])
$overlay_data = $futuredata[$datestr]; $overlay_data = $futuredata[$datestr];
$rec_id = $event['uid'] . '-' . $instance_id;
$exception = $exdata[$datestr] ?: $overlay_data;
$event_start = $next_event['start'];
$event_end = $next_event['end'];
// copy some event from exception to get proper start/end dates
if ($exception) {
$event_copy = $next_event;
kolab_driver::merge_exception_dates($event_copy, $exception);
$event_start = $event_copy['start'];
$event_end = $event_copy['end'];
}
// add to output if in range // add to output if in range
$rec_id = $event['uid'] . '-' . $instance_id; if (($event_start <= $end && $event_end >= $start) || ($event_id && $rec_id == $event_id)) {
if (($next_event['start'] <= $end && $next_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);
$rec_event['_instance'] = $instance_id; $rec_event['_instance'] = $instance_id;
$rec_event['_count'] = $i + 1; $rec_event['_count'] = $i + 1;
if ($overlay_data || $exdata[$datestr]) // copy data from exception if ($exception) // copy data from exception
kolab_driver::merge_exception_data($rec_event, $exdata[$datestr] ?: $overlay_data); kolab_driver::merge_exception_data($rec_event, $exception);
$rec_event['id'] = $rec_id; $rec_event['id'] = $rec_id;
$rec_event['recurrence_id'] = $event['uid']; $rec_event['recurrence_id'] = $event['uid'];
@ -699,7 +714,7 @@ class kolab_calendar extends kolab_storage_folder_api
break; break;
// avoid endless recursion loops // avoid endless recursion loops
if (++$i > 1000) if (++$i > 100000)
break; break;
} }
@ -709,10 +724,13 @@ class kolab_calendar extends kolab_storage_folder_api
/** /**
* Convert from Kolab_Format to internal representation * Convert from Kolab_Format to internal representation
*/ */
private function _to_driver_event($record, $noinst = false) private function _to_driver_event($record, $noinst = false, $links = true)
{ {
$record['calendar'] = $this->id; $record['calendar'] = $this->id;
$record['links'] = $this->get_links($record['uid']);
if ($links && !array_key_exists('links', $record)) {
$record['links'] = $this->get_links($record['uid']);
}
if ($this->get_namespace() == 'other') { if ($this->get_namespace() == 'other') {
$record['className'] = 'fc-event-ns-other'; $record['className'] = 'fc-event-ns-other';
@ -745,11 +763,25 @@ class kolab_calendar extends kolab_storage_folder_api
private function _from_driver_event($event, $old = array()) private function _from_driver_event($event, $old = array())
{ {
// set current user as ORGANIZER // set current user as ORGANIZER
$identity = $this->cal->rc->user->list_emails(true); if ($identity = $this->cal->rc->user->list_emails(true)) {
if (empty($event['attendees']) && $identity['email']) $event['attendees'] = (array) $event['attendees'];
$event['attendees'] = array(array('role' => 'ORGANIZER', 'name' => $identity['name'], 'email' => $identity['email'])); $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 // remove EXDATE values if RDATE is given
if (!empty($event['recurrence']['RDATE'])) { if (!empty($event['recurrence']['RDATE'])) {
@ -774,7 +806,6 @@ class kolab_calendar extends kolab_storage_folder_api
}); });
} }
// remove some internal properties which should not be saved // remove some internal properties which should not be saved
unset($event['_savemode'], $event['_fromcalendar'], $event['_identity'], $event['_folder_id'], unset($event['_savemode'], $event['_fromcalendar'], $event['_identity'], $event['_folder_id'],
$event['recurrence_id'], $event['attachments'], $event['deleted_attachments'], $event['className']); $event['recurrence_id'], $event['attachments'], $event['deleted_attachments'], $event['className']);

View file

@ -57,8 +57,7 @@ class kolab_driver extends calendar_driver
require_once(dirname(__FILE__) . '/kolab_invitation_calendar.php'); require_once(dirname(__FILE__) . '/kolab_invitation_calendar.php');
$this->cal = $cal; $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('push-freebusy', array($this, 'push_freebusy'));
$this->cal->register_action('calendar-acl', array($this, 'calendar_acl')); $this->cal->register_action('calendar-acl', array($this, 'calendar_acl'));
@ -89,11 +88,11 @@ class kolab_driver extends calendar_driver
// get all folders that have "event" type, sorted by namespace/name // 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)); $folders = kolab_storage::sort_folders(kolab_storage::get_folders('event') + kolab_storage::get_user_folders('event', true));
$this->calendars = array();
$this->calendars = array();
foreach ($folders as $folder) { foreach ($folders as $folder) {
if ($folder instanceof kolab_storage_folder_user) { if ($folder instanceof kolab_storage_folder_user) {
$calendar = new kolab_user_calendar($folder->name, $this->cal); $calendar = new kolab_user_calendar($folder, $this->cal);
$calendar->subscriptions = count($folder->children) > 0; $calendar->subscriptions = count($folder->children) > 0;
} }
else { else {
@ -120,10 +119,12 @@ class kolab_driver extends calendar_driver
*/ */
public function list_calendars($filter = 0, &$tree = null) public function list_calendars($filter = 0, &$tree = null)
{ {
$this->_read_calendars();
// attempt to create a default calendar for this user // attempt to create a default calendar for this user
if (!$this->has_writeable) { if (!$this->has_writeable) {
if ($this->create_calendar(array('name' => 'Calendar', 'color' => 'cc0000'))) { if ($this->create_calendar(array('name' => 'Calendar', 'color' => 'cc0000'))) {
unset($this->calendars); unset($this->calendars);
$this->_read_calendars(); $this->_read_calendars();
} }
} }
@ -162,8 +163,8 @@ class kolab_driver extends calendar_driver
// special handling for user or virtual folders // special handling for user or virtual folders
if ($cal instanceof kolab_storage_folder_user) { if ($cal instanceof kolab_storage_folder_user) {
$calendars[$cal->id] = array( $calendars[$cal->id] = array(
'id' => $cal->id, 'id' => $cal->id,
'name' => kolab_storage::object_name($fullname), 'name' => $fullname,
'listname' => $listname, 'listname' => $listname,
'editname' => $cal->get_foldername(), 'editname' => $cal->get_foldername(),
'color' => $cal->get_color(), 'color' => $cal->get_color(),
@ -287,22 +288,23 @@ class kolab_driver extends calendar_driver
*/ */
protected function filter_calendars($filter) protected function filter_calendars($filter)
{ {
$this->_read_calendars();
$calendars = array(); $calendars = array();
$plugin = $this->rc->plugins->exec_hook('calendar_list_filter', array( $plugin = $this->rc->plugins->exec_hook('calendar_list_filter', array(
'list' => $this->calendars, 'list' => $this->calendars,
'calendars' => $calendars, 'calendars' => $calendars,
'filter' => $filter, 'filter' => $filter,
'editable' => ($filter & self::FILTER_WRITEABLE),
'insert' => ($filter & self::FILTER_INSERTABLE),
'active' => ($filter & self::FILTER_ACTIVE),
'personal' => ($filter & self::FILTER_PERSONAL)
)); ));
if ($plugin['abort']) { if ($plugin['abort']) {
return $plugin['calendars']; return $plugin['calendars'];
} }
$personal = $filter & self::FILTER_PERSONAL;
$shared = $filter & self::FILTER_SHARED;
foreach ($this->calendars as $cal) { foreach ($this->calendars as $cal) {
if (!$cal->ready) { if (!$cal->ready) {
continue; continue;
@ -322,9 +324,13 @@ class kolab_driver extends calendar_driver
if (($filter & self::FILTER_CONFIDENTIAL) && $cal->subtype != 'confidential') { if (($filter & self::FILTER_CONFIDENTIAL) && $cal->subtype != 'confidential') {
continue; continue;
} }
if (($filter & self::FILTER_PERSONAL) && $cal->get_namespace() != 'personal') { if ($personal || $shared) {
continue; $ns = $cal->get_namespace();
if (!(($personal && $ns == 'personal') || ($shared && $ns == 'shared'))) {
continue;
}
} }
$calendars[$cal->id] = $cal; $calendars[$cal->id] = $cal;
} }
@ -340,14 +346,19 @@ class kolab_driver extends calendar_driver
*/ */
public function get_calendar($id) public function get_calendar($id)
{ {
$this->_read_calendars();
// create calendar object if necesary // create calendar object if necesary
if (!$this->calendars[$id] && in_array($id, array(self::INVITATIONS_CALENDAR_PENDING, self::INVITATIONS_CALENDAR_DECLINED))) { if (!$this->calendars[$id]) {
$this->calendars[$id] = new kolab_invitation_calendar($id, $this->cal); 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 (!$this->calendars[$id] && $id !== self::BIRTHDAY_CALENDAR_ID) { }
$calendar = kolab_calendar::factory($id, $this->cal); else if ($id !== self::BIRTHDAY_CALENDAR_ID) {
if ($calendar->ready) $calendar = kolab_calendar::factory($id, $this->cal);
$this->calendars[$calendar->id] = $calendar; if ($calendar->ready) {
$this->calendars[$calendar->id] = $calendar;
}
}
} }
return $this->calendars[$id]; return $this->calendars[$id];
@ -592,8 +603,12 @@ class kolab_driver extends calendar_driver
$event = self::from_rcube_event($event); $event = self::from_rcube_event($event);
$cid = $event['calendar'] ? $event['calendar'] : reset(array_keys($this->calendars)); if (!$event['calendar']) {
if ($storage = $this->get_calendar($cid)) { $this->_read_calendars();
$event['calendar'] = reset(array_keys($this->calendars));
}
if ($storage = $this->get_calendar($event['calendar'])) {
// if this is a recurrence instance, append as exception to an already existing object for this UID // 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']))) { if (!empty($event['recurrence_date']) && ($master = $storage->get_event($event['uid']))) {
self::add_exception($master, $event); self::add_exception($master, $event);
@ -1378,7 +1393,6 @@ class kolab_driver extends calendar_driver
} }
} }
/** /**
* Merge certain properties from the overlay event to the base event object * Merge certain properties from the overlay event to the base event object
* *
@ -1393,25 +1407,9 @@ class kolab_driver extends calendar_driver
if (is_array($blacklist)) if (is_array($blacklist))
$forbidden = array_merge($forbidden, $blacklist); $forbidden = array_merge($forbidden, $blacklist);
// compute date offset from the exception
if ($overlay['start'] instanceof DateTime && $overlay['recurrence_date'] instanceof DateTime) {
$date_offset = $overlay['recurrence_date']->diff($overlay['start']);
}
foreach ($overlay as $prop => $value) { foreach ($overlay as $prop => $value) {
if ($prop == 'start' || $prop == 'end') { if ($prop == 'start' || $prop == 'end') {
if (is_object($event[$prop]) && $event[$prop] instanceof DateTime) { // handled by merge_exception_dates() below
// set date value if overlay is an exception of the current instance
if (substr($overlay['_instance'], 0, 8) == substr($event['_instance'], 0, 8)) {
$event[$prop]->setDate(intval($value->format('Y')), intval($value->format('n')), intval($value->format('j')));
}
// apply date offset
else if ($date_offset) {
$event[$prop]->add($date_offset);
}
// adjust time of the recurring event instance
$event[$prop]->setTime($value->format('G'), intval($value->format('i')), intval($value->format('s')));
}
} }
else if ($prop == 'thisandfuture' && $overlay['_instance'] == $event['_instance']) { else if ($prop == 'thisandfuture' && $overlay['_instance'] == $event['_instance']) {
$event[$prop] = $value; $event[$prop] = $value;
@ -1419,6 +1417,38 @@ class kolab_driver extends calendar_driver
else if ($prop[0] != '_' && !in_array($prop, $forbidden)) else if ($prop[0] != '_' && !in_array($prop, $forbidden))
$event[$prop] = $value; $event[$prop] = $value;
} }
self::merge_exception_dates($event, $overlay);
}
/**
* Merge start/end date from the overlay event to the base event object
*
* @param array The event object to be altered
* @param array The overlay event object to be merged over $event
*/
public static function merge_exception_dates(&$event, $overlay)
{
// compute date offset from the exception
if ($overlay['start'] instanceof DateTime && $overlay['recurrence_date'] instanceof DateTime) {
$date_offset = $overlay['recurrence_date']->diff($overlay['start']);
}
foreach (array('start', 'end') as $prop) {
$value = $overlay[$prop];
if (is_object($event[$prop]) && $event[$prop] instanceof DateTime) {
// set date value if overlay is an exception of the current instance
if (substr($overlay['_instance'], 0, 8) == substr($event['_instance'], 0, 8)) {
$event[$prop]->setDate(intval($value->format('Y')), intval($value->format('n')), intval($value->format('j')));
}
// apply date offset
else if ($date_offset) {
$event[$prop]->add($date_offset);
}
// adjust time of the recurring event instance
$event[$prop]->setTime($value->format('G'), intval($value->format('i')), intval($value->format('s')));
}
}
} }
/** /**
@ -1436,8 +1466,10 @@ class kolab_driver extends calendar_driver
{ {
if ($calendars && is_string($calendars)) if ($calendars && is_string($calendars))
$calendars = explode(',', $calendars); $calendars = explode(',', $calendars);
else if (!$calendars) else if (!$calendars) {
$this->_read_calendars();
$calendars = array_keys($this->calendars); $calendars = array_keys($this->calendars);
}
$query = array(); $query = array();
if ($modifiedsince) if ($modifiedsince)
@ -1482,8 +1514,10 @@ class kolab_driver extends calendar_driver
if ($calendars && is_string($calendars)) if ($calendars && is_string($calendars))
$calendars = explode(',', $calendars); $calendars = explode(',', $calendars);
else if (!$calendars) else if (!$calendars) {
$this->_read_calendars();
$calendars = array_keys($this->calendars); $calendars = array_keys($this->calendars);
}
foreach ($calendars as $cid) { foreach ($calendars as $cid) {
if ($storage = $this->get_calendar($cid)) { if ($storage = $this->get_calendar($cid)) {
@ -1521,6 +1555,9 @@ class kolab_driver extends calendar_driver
$candidates = array(); $candidates = array();
$query = array(array('tags', '=', 'x-has-alarms')); $query = array(array('tags', '=', 'x-has-alarms'));
$this->_read_calendars();
foreach ($this->calendars as $cid => $calendar) { foreach ($this->calendars as $cid => $calendar) {
// skip calendars with alarms disabled // skip calendars with alarms disabled
if (!$calendar->alarms || ($calendars && !in_array($cid, $calendars))) if (!$calendar->alarms || ($calendars && !in_array($cid, $calendars)))
@ -1633,15 +1670,14 @@ class kolab_driver extends calendar_driver
$event = $storage->get_event($event['id']); $event = $storage->get_event($event['id']);
} }
if ($event && !empty($event['_attachments'])) { if ($event) {
foreach ($event['_attachments'] as $att) { $attachments = isset($event['_attachments']) ? $event['_attachments'] : $event['attachments'];
foreach ((array) $attachments as $att) {
if ($att['id'] == $id) { if ($att['id'] == $id) {
return $att; return $att;
} }
} }
} }
return null;
} }
/** /**
@ -1953,46 +1989,7 @@ class kolab_driver extends calendar_driver
*/ */
public static function from_rcube_event($event, $old = array()) public static function from_rcube_event($event, $old = array())
{ {
// in kolab_storage attachments are indexed by content-id kolab_format::merge_attachments($event, $old);
if (is_array($event['attachments']) || !empty($event['deleted_attachments'])) {
$event['_attachments'] = array();
foreach ($event['attachments'] as $attachment) {
$key = null;
// Roundcube ID has nothing to do with the storage ID, remove it
if ($attachment['content'] || $attachment['path']) {
unset($attachment['id']);
}
else {
foreach ((array)$old['_attachments'] as $cid => $oldatt) {
if ($attachment['id'] == $oldatt['id'])
$key = $cid;
}
}
// flagged for deletion => set to false
if ($attachment['_deleted'] || in_array($attachment['id'], (array)$event['deleted_attachments'])) {
$event['_attachments'][$key] = false;
}
// replace existing entry
else if ($key) {
$event['_attachments'][$key] = $attachment;
}
// append as new attachment
else {
$event['_attachments'][] = $attachment;
}
}
$event['_attachments'] = array_merge((array)$old['_attachments'], $event['_attachments']);
// attachments flagged for deletion => set to false
foreach ($event['_attachments'] as $key => $attachment) {
if ($attachment['_deleted'] || in_array($attachment['id'], (array)$event['deleted_attachments'])) {
$event['_attachments'][$key] = false;
}
}
}
return $event; return $event;
} }
@ -2317,6 +2314,8 @@ class kolab_driver extends calendar_driver
return parent::calendar_form($action, $calendar, $formfields); return parent::calendar_form($action, $calendar, $formfields);
} }
$this->_read_calendars();
if ($calendar['id'] && ($cal = $this->calendars[$calendar['id']])) { if ($calendar['id'] && ($cal = $this->calendars[$calendar['id']])) {
$folder = $cal->get_realname(); // UTF7 $folder = $cal->get_realname(); // UTF7
$color = $cal->get_color(); $color = $cal->get_color();
@ -2450,7 +2449,7 @@ class kolab_driver extends calendar_driver
if (is_array($form['content']) && !empty($form['content'])) { if (is_array($form['content']) && !empty($form['content'])) {
$table = new html_table(array('cols' => 2)); $table = new html_table(array('cols' => 2));
foreach ($form['content'] as $col => $colprop) { foreach ($form['content'] as $col => $colprop) {
$label = !empty($colprop['label']) ? $colprop['label'] : $this->rc->gettext($col); $label = !empty($colprop['label']) ? $colprop['label'] : $this->cal->gettext($col);
$table->add('title', html::label($colprop['id'], rcube::Q($label))); $table->add('title', html::label($colprop['id'], rcube::Q($label)));
$table->add(null, $colprop['value']); $table->add(null, $colprop['value']);

View file

@ -34,6 +34,7 @@ class kolab_invitation_calendar
public $categories = array(); public $categories = array();
public $name = 'Invitations'; public $name = 'Invitations';
/** /**
* Default constructor * Default constructor
*/ */
@ -62,7 +63,6 @@ class kolab_invitation_calendar
$this->alarms = $prefs[$this->id]['showalarms']; $this->alarms = $prefs[$this->id]['showalarms'];
} }
/** /**
* Getter for a nice and human readable name for this calendar * Getter for a nice and human readable name for this calendar
* *
@ -73,7 +73,6 @@ class kolab_invitation_calendar
return $this->name; return $this->name;
} }
/** /**
* Getter for the IMAP folder owner * Getter for the IMAP folder owner
* *
@ -84,7 +83,6 @@ class kolab_invitation_calendar
return $this->cal->rc->get_user_name(); return $this->cal->rc->get_user_name();
} }
/** /**
* *
*/ */
@ -93,7 +91,6 @@ class kolab_invitation_calendar
return $this->get_name(); return $this->get_name();
} }
/** /**
* Getter for the name of the namespace to which the IMAP folder belongs * Getter for the name of the namespace to which the IMAP folder belongs
* *
@ -104,7 +101,6 @@ class kolab_invitation_calendar
return 'x-special'; return 'x-special';
} }
/** /**
* Getter for the top-end calendar folder name (not the entire path) * Getter for the top-end calendar folder name (not the entire path)
* *
@ -171,7 +167,6 @@ class kolab_invitation_calendar
return $prop['id']; return $prop['id'];
} }
/** /**
* Getter for a single event object * Getter for a single event object
*/ */
@ -202,7 +197,7 @@ class kolab_invitation_calendar
else { else {
$cal = null; $cal = null;
foreach (kolab_storage::list_folders('', '*', 'event', null) as $foldername) { foreach (kolab_storage::list_folders('', '*', 'event', null) as $foldername) {
$cal = new kolab_calendar($foldername, $this->cal); $cal = $this->_get_calendar($foldername);
if ($cal->ready && $cal->storage && $cal->get_event($event['id'])) { if ($cal->ready && $cal->storage && $cal->get_event($event['id'])) {
break; break;
} }
@ -216,7 +211,6 @@ class kolab_invitation_calendar
return false; return false;
} }
/** /**
* @param integer Event's new start (unix timestamp) * @param integer Event's new start (unix timestamp)
* @param integer Event's new end (unix timestamp) * @param integer Event's new end (unix timestamp)
@ -239,7 +233,7 @@ class kolab_invitation_calendar
// aggregate events from all calendar folders // aggregate events from all calendar folders
$events = array(); $events = array();
foreach (kolab_storage::list_folders('', '*', 'event', null) as $foldername) { foreach (kolab_storage::list_folders('', '*', 'event', null) as $foldername) {
$cal = new kolab_calendar($foldername, $this->cal); $cal = $this->_get_calendar($foldername);
if ($cal->get_namespace() == 'other') if ($cal->get_namespace() == 'other')
continue; continue;
@ -257,7 +251,7 @@ class kolab_invitation_calendar
} }
if ($match) { if ($match) {
$events[$event['id']] = $this->_mod_event($event); $events[$event['id'] ?: $event['uid']] = $this->_mod_event($event);
} }
} }
@ -293,7 +287,7 @@ class kolab_invitation_calendar
// aggregate counts from all calendar folders // aggregate counts from all calendar folders
$count = 0; $count = 0;
foreach (kolab_storage::list_folders('', '*', 'event', null) as $foldername) { foreach (kolab_storage::list_folders('', '*', 'event', null) as $foldername) {
$cal = new kolab_calendar($foldername, $this->cal); $cal = $this->_get_calendar($foldername);
if ($cal->get_namespace() == 'other') if ($cal->get_namespace() == 'other')
continue; continue;
@ -303,6 +297,15 @@ class kolab_invitation_calendar
return $count; 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 * Helper method to modify some event properties
*/ */
@ -318,7 +321,6 @@ class kolab_invitation_calendar
return $event; return $event;
} }
/** /**
* Create a new event record * Create a new event record
* *
@ -337,7 +339,6 @@ class kolab_invitation_calendar
* @see calendar_driver::new_event() * @see calendar_driver::new_event()
* @return boolean True on success, False on error * @return boolean True on success, False on error
*/ */
public function update_event($event, $exception_id = null) public function update_event($event, $exception_id = null)
{ {
// forward call to the actual storage folder // forward call to the actual storage folder
@ -372,6 +373,4 @@ class kolab_invitation_calendar
{ {
return false; return false;
} }
} }

View file

@ -5,7 +5,7 @@
* *
* @author Thomas Bruederli <bruederli@kolabsys.com> * @author Thomas Bruederli <bruederli@kolabsys.com>
* *
* Copyright (C) 2014-2015, Kolab Systems AG <contact@kolabsys.com> * Copyright (C) 2014-2016, Kolab Systems AG <contact@kolabsys.com>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as * it under the terms of the GNU Affero General Public License as
@ -45,8 +45,12 @@ class kolab_user_calendar extends kolab_calendar
$this->userdata = $user_or_folder; $this->userdata = $user_or_folder;
$this->storage = new kolab_storage_folder_user($this->userdata['kolabtargetfolder'], '', $this->userdata); $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 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; $this->userdata = $this->storage->ldaprec;
} }
@ -57,7 +61,7 @@ class kolab_user_calendar extends kolab_calendar
// ID is derrived from the user's kolabtargetfolder attribute // ID is derrived from the user's kolabtargetfolder attribute
$this->id = kolab_storage::folder_id($this->userdata['kolabtargetfolder'], true); $this->id = kolab_storage::folder_id($this->userdata['kolabtargetfolder'], true);
$this->imap_folder = $this->userdata['kolabtargetfolder']; $this->imap_folder = $this->userdata['kolabtargetfolder'];
$this->name = $this->storage->get_name(); $this->name = $this->storage->name;
$this->parent = ''; // user calendars are top level $this->parent = ''; // user calendars are top level
// user-specific alarms settings win // user-specific alarms settings win
@ -67,7 +71,6 @@ class kolab_user_calendar extends kolab_calendar
} }
} }
/** /**
* Getter for a nice and human readable name for this calendar * Getter for a nice and human readable name for this calendar
* *
@ -78,7 +81,6 @@ class kolab_user_calendar extends kolab_calendar
return $this->userdata['displayname'] ?: ($this->userdata['name'] ?: $this->userdata['mail']); return $this->userdata['displayname'] ?: ($this->userdata['name'] ?: $this->userdata['mail']);
} }
/** /**
* Getter for the IMAP folder owner * Getter for the IMAP folder owner
* *
@ -89,7 +91,6 @@ class kolab_user_calendar extends kolab_calendar
return $this->userdata['mail']; return $this->userdata['mail'];
} }
/** /**
* *
*/ */
@ -98,7 +99,6 @@ class kolab_user_calendar extends kolab_calendar
return trim($this->userdata['displayname'] . '; ' . $this->userdata['mail'], '; '); return trim($this->userdata['displayname'] . '; ' . $this->userdata['mail'], '; ');
} }
/** /**
* Getter for the name of the namespace to which the IMAP folder belongs * Getter for the name of the namespace to which the IMAP folder belongs
* *
@ -109,7 +109,6 @@ class kolab_user_calendar extends kolab_calendar
return 'other user'; return 'other user';
} }
/** /**
* Getter for the top-end calendar folder name (not the entire path) * Getter for the top-end calendar folder name (not the entire path)
* *
@ -164,7 +163,6 @@ class kolab_user_calendar extends kolab_calendar
return $prop['id']; return $prop['id'];
} }
/** /**
* Getter for a single event object * Getter for a single event object
*/ */
@ -225,12 +223,13 @@ class kolab_user_calendar extends kolab_calendar
} }
} }
// aggregate all calendar folders the user shares (but are not subscribed) // aggregate all calendar folders the user shares (but are not activated)
foreach (kolab_storage::list_user_folders($this->userdata, 'event', false) as $foldername) { foreach (kolab_storage::list_user_folders($this->userdata, 'event', 2) as $foldername) {
$cal = new kolab_calendar($foldername, $this->cal); $cal = new kolab_calendar($foldername, $this->cal);
foreach ($cal->list_events($start, $end, $search, 1) as $event) { foreach ($cal->list_events($start, $end, $search, 1) as $event) {
$this->events[$event['id']] = $event; $uid = $event['id'] ?: $event['uid'];
$this->timeindex[$this->time_key($event)] = $event['id']; $this->events[$uid] = $event;
$this->timeindex[$this->time_key($event)] = $uid;
} }
} }
@ -318,7 +317,7 @@ class kolab_user_calendar extends kolab_calendar
'X-OUT-OF-OFFICE' => $this->cal->gettext('availoutofoffice'), 'X-OUT-OF-OFFICE' => $this->cal->gettext('availoutofoffice'),
); );
// rcmail::console('_fetch_freebusy', kolab_storage::get_freebusy_url($this->userdata['mail']), $fbdata); // rcube::console('_fetch_freebusy', kolab_storage::get_freebusy_url($this->userdata['mail']), $fbdata);
// parse free-busy information // parse free-busy information
$count = 0; $count = 0;
@ -334,7 +333,7 @@ class kolab_user_calendar extends kolab_calendar
foreach ($fb['periods'] as $tuple) { foreach ($fb['periods'] as $tuple) {
list($from, $to, $type) = $tuple; list($from, $to, $type) = $tuple;
$event = array( $event = array(
'id' => md5($this->id . $from->format('U') . '/' . $to->format('U')), 'uid' => md5($this->id . $from->format('U') . '/' . $to->format('U')),
'calendar' => $this->id, 'calendar' => $this->id,
'changed' => $fb['created'] ?: new DateTime(), 'changed' => $fb['created'] ?: new DateTime(),
'title' => $this->get_name() . ' ' . ($titlemap[$type] ?: $type), 'title' => $this->get_name() . ' ' . ($titlemap[$type] ?: $type),
@ -351,8 +350,8 @@ class kolab_user_calendar extends kolab_calendar
// avoid duplicate entries // avoid duplicate entries
$key = $this->time_key($event); $key = $this->time_key($event);
if (!$this->timeindex[$key]) { if (!$this->timeindex[$key]) {
$this->events[$event['id']] = $event; $this->events[$event['uid']] = $event;
$this->timeindex[$key] = $event['id']; $this->timeindex[$key] = $event['uid'];
$count++; $count++;
} }
} }
@ -367,10 +366,9 @@ class kolab_user_calendar extends kolab_calendar
*/ */
private function time_key($event) private function time_key($event)
{ {
return sprintf('%s/%s', $event['start']->format('U'), is_object($event['end']->format('U')) ?: '0'); return sprintf('%s/%s', $event['start']->format('U'), is_object($event['end']) ? $event['end']->format('U') : '0');
} }
/** /**
* Create a new event record * Create a new event record
* *
@ -389,7 +387,6 @@ class kolab_user_calendar extends kolab_calendar
* @see calendar_driver::new_event() * @see calendar_driver::new_event()
* @return boolean True on success, False on error * @return boolean True on success, False on error
*/ */
public function update_event($event, $exception_id = null) public function update_event($event, $exception_id = null)
{ {
return false; return false;
@ -416,17 +413,4 @@ class kolab_user_calendar extends kolab_calendar
{ {
return false; return false;
} }
/**
* Convert from Kolab_Format to internal representation
*/
private function _to_rcube_event($record)
{
$record['id'] = $record['uid'];
$record['calendar'] = $this->id;
return kolab_driver::to_rcube_event($record);
}
} }

View file

@ -177,7 +177,7 @@ class calendar_ui
{ {
$color = $prop['color']; $color = $prop['color'];
$class = 'cal-' . asciiwords($id, true); $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 != 1) {
if ($mode == 3) { if ($mode == 3) {
@ -189,7 +189,7 @@ class calendar_ui
$css .= ".fc-event-$class, "; $css .= ".fc-event-$class, ";
$css .= ".fc-event-$class .fc-event-inner {"; $css .= ".fc-event-$class .fc-event-inner {";
} }
if (!$attrib['printmode']) if (!$prop['printmode'])
$css .= " background-color: #$color;"; $css .= " background-color: #$color;";
if ($mode % 2 == 0) if ($mode % 2 == 0)
$css .= " border-color: #$color;"; $css .= " border-color: #$color;";
@ -585,7 +585,7 @@ class calendar_ui
$checkbox = new html_checkbox(array('name' => 'attachments', 'id' => 'event-export-attachments', 'value' => 1)); $checkbox = new html_checkbox(array('name' => 'attachments', 'id' => 'event-export-attachments', 'value' => 1));
$html .= html::div('form-section', $html .= html::div('form-section',
html::label('event-export-range', $this->cal->gettext('exportattachments')) . html::label('event-export-attachments', $this->cal->gettext('exportattachments')) .
$checkbox->show(1) $checkbox->show(1)
); );
@ -607,7 +607,7 @@ class calendar_ui
$attrib['id'] = 'rcmUploadForm'; $attrib['id'] = 'rcmUploadForm';
// Get max filesize, enable upload progress bar // 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')); $button = new html_inputfield(array('type' => 'button'));
$input = new html_inputfield(array( $input = new html_inputfield(array(
@ -616,7 +616,7 @@ class calendar_ui
return html::div($attrib, return html::div($attrib,
html::div(null, $input->show()) . html::div(null, $input->show()) .
html::div('formbuttons', $button->show($this->rc->gettext('upload'), array('class' => 'button mainaction', html::div('buttons', $button->show($this->rc->gettext('upload'), array('class' => 'button mainaction',
'onclick' => rcmail_output::JS_OBJECT_NAME . ".upload_file(this.form)"))) . 'onclick' => rcmail_output::JS_OBJECT_NAME . ".upload_file(this.form)"))) .
html::div('hint', $this->rc->gettext(array('name' => 'maxuploadsize', 'vars' => array('size' => $max_filesize)))) html::div('hint', $this->rc->gettext(array('name' => 'maxuploadsize', 'vars' => array('size' => $max_filesize))))
); );

View file

@ -8,6 +8,7 @@
*/ */
$labels['default_view'] = 'Изглед по подразбиране'; $labels['default_view'] = 'Изглед по подразбиране';
$labels['time_format'] = 'Формат на часовете'; $labels['time_format'] = 'Формат на часовете';
$labels['timeslots'] = 'Деления за час';
$labels['first_day'] = 'Първи ден от седмицата'; $labels['first_day'] = 'Първи ден от седмицата';
$labels['first_hour'] = 'Първи час при показване'; $labels['first_hour'] = 'Първи час при показване';
$labels['workinghours'] = 'Работни часове'; $labels['workinghours'] = 'Работни часове';
@ -19,6 +20,12 @@ $labels['coloringmode0'] = 'Според календара';
$labels['coloringmode1'] = 'Относно категорията'; $labels['coloringmode1'] = 'Относно категорията';
$labels['coloringmode2'] = 'Календар за очертание, категория за съдържание'; $labels['coloringmode2'] = 'Календар за очертание, категория за съдържание';
$labels['coloringmode3'] = 'Категория за очертание, календар за съдържание'; $labels['coloringmode3'] = 'Категория за очертание, календар за съдържание';
$labels['afternothing'] = 'Не предприемай нищо';
$labels['aftertrash'] = 'Премести в Кошчето';
$labels['afterdelete'] = 'Изтрий съобщението';
$labels['afterflagdeleted'] = 'Маркирай като изтрито';
$labels['aftermoveto'] = 'Премести в...';
$labels['itipoptions'] = 'Покани за събитие';
$labels['calendar'] = 'Календар'; $labels['calendar'] = 'Календар';
$labels['calendars'] = 'Календари'; $labels['calendars'] = 'Календари';
$labels['category'] = 'Категория'; $labels['category'] = 'Категория';
@ -36,6 +43,7 @@ $labels['new_event'] = 'Добавяне на събитие';
$labels['edit_event'] = 'Промяна на събитие'; $labels['edit_event'] = 'Промяна на събитие';
$labels['edit'] = 'Промяна'; $labels['edit'] = 'Промяна';
$labels['save'] = 'Запис'; $labels['save'] = 'Запис';
$labels['removelist'] = 'Премахни от списъка';
$labels['cancel'] = 'Отказ'; $labels['cancel'] = 'Отказ';
$labels['select'] = 'Избиране'; $labels['select'] = 'Избиране';
$labels['print'] = 'Печат'; $labels['print'] = 'Печат';
@ -46,30 +54,47 @@ $labels['all-day'] = 'цял ден';
$labels['export'] = 'Извличане'; $labels['export'] = 'Извличане';
$labels['exporttitle'] = 'Извличане към iCalendar'; $labels['exporttitle'] = 'Извличане към iCalendar';
$labels['exportrange'] = 'Събития от'; $labels['exportrange'] = 'Събития от';
$labels['exportattachments'] = 'С прикачени файлове';
$labels['customdate'] = 'Дата по избор';
$labels['location'] = 'Местоположение'; $labels['location'] = 'Местоположение';
$labels['url'] = 'URL адрес';
$labels['date'] = 'Дата'; $labels['date'] = 'Дата';
$labels['start'] = 'Начало'; $labels['start'] = 'Начало';
$labels['starttime'] = 'Време за начало';
$labels['end'] = 'Край'; $labels['end'] = 'Край';
$labels['endtime'] = 'Време за край';
$labels['repeat'] = 'Повтори';
$labels['selectdate'] = 'Избор на дата'; $labels['selectdate'] = 'Избор на дата';
$labels['freebusy'] = 'Показване като'; $labels['freebusy'] = 'Показване като';
$labels['free'] = 'Свободно'; $labels['free'] = 'Свободно';
$labels['busy'] = 'Заето'; $labels['busy'] = 'Заето';
$labels['outofoffice'] = 'Извън офиса'; $labels['outofoffice'] = 'Извън офиса';
$labels['tentative'] = 'Предварително'; $labels['tentative'] = 'Предварително';
$labels['mystatus'] = 'Моят статус';
$labels['status'] = 'Статус'; $labels['status'] = 'Статус';
$labels['status-confirmed'] = 'Потвърдено';
$labels['status-cancelled'] = 'Отхвърлено';
$labels['priority'] = 'Приоритет'; $labels['priority'] = 'Приоритет';
$labels['sensitivity'] = 'Частност'; $labels['sensitivity'] = 'Поверителност';
$labels['public'] = 'публично'; $labels['public'] = 'публично';
$labels['private'] = 'частно'; $labels['private'] = 'частно';
$labels['confidential'] = 'конфиденциално'; $labels['confidential'] = 'конфиденциално';
$labels['links'] = 'Цикличност';
$labels['alarms'] = 'Напомняне'; $labels['alarms'] = 'Напомняне';
$labels['comment'] = 'Коментар';
$labels['created'] = 'Създаден';
$labels['changed'] = 'Последна промяна';
$labels['unknown'] = 'Неизвестно'; $labels['unknown'] = 'Неизвестно';
$labels['eventoptions'] = 'Опции';
$labels['generated'] = 'генерирано в'; $labels['generated'] = 'генерирано в';
$labels['eventhistory'] = 'История';
$labels['removelink'] = 'Премахни е-мейл препратките';
$labels['printdescriptions'] = 'Печат на описанията'; $labels['printdescriptions'] = 'Печат на описанията';
$labels['parentcalendar'] = 'Внасяне вътре'; $labels['parentcalendar'] = 'Внасяне вътре';
$labels['searchearlierdates'] = '« Търсене за по- стари събития'; $labels['searchearlierdates'] = '« Търсене за по- стари събития';
$labels['searchlaterdates'] = 'Търсене за по- нови събития »'; $labels['searchlaterdates'] = 'Търсене за по- нови събития »';
$labels['andnmore'] = '$nr повече...'; $labels['andnmore'] = '$nr повече...';
$labels['togglerole'] = 'Натиснете за превключване на роля';
$labels['createfrommail'] = 'Запазване като събитие'; $labels['createfrommail'] = 'Запазване като събитие';
$labels['importevents'] = 'Внасяне на събития'; $labels['importevents'] = 'Внасяне на събития';
$labels['importrange'] = 'Събития от'; $labels['importrange'] = 'Събития от';
@ -77,6 +102,15 @@ $labels['onemonthback'] = '1 месец назад';
$labels['nmonthsback'] = '$nr месеца назад'; $labels['nmonthsback'] = '$nr месеца назад';
$labels['showurl'] = 'Показване на URL на календара'; $labels['showurl'] = 'Показване на URL на календара';
$labels['showurldescription'] = 'Използвайте следния адрес, за да достъпвате (само за четене) вашия календар от други приложения. Можете да копирате и поставяте това във всеки календарен софтуер, поддържащ форматът iCal'; $labels['showurldescription'] = 'Използвайте следния адрес, за да достъпвате (само за четене) вашия календар от други приложения. Можете да копирате и поставяте това във всеки календарен софтуер, поддържащ форматът iCal';
$labels['searchterms'] = 'Търсене за';
$labels['calendarsubscribe'] = 'Постоянен списък';
$labels['nocalendarsfound'] = 'Не бяха намерени календари';
$labels['nrcalendarsfound'] = 'Бяха намерени $nr календара';
$labels['quickview'] = 'Покажи само този календар';
$labels['invitationspending'] = 'Чакащи покани';
$labels['invitationsdeclined'] = 'Отхвърлени покани';
$labels['changepartstat'] = 'Промени състоянието на участник';
$labels['rsvpcomment'] = 'Текст на поканата';
$labels['listrange'] = 'Оразмеряване към екран:'; $labels['listrange'] = 'Оразмеряване към екран:';
$labels['listsections'] = 'Разделяне на:'; $labels['listsections'] = 'Разделяне на:';
$labels['smartsections'] = 'Интелигентни секции'; $labels['smartsections'] = 'Интелигентни секции';
@ -85,38 +119,79 @@ $labels['today'] = 'Днес';
$labels['tomorrow'] = 'Утре'; $labels['tomorrow'] = 'Утре';
$labels['thisweek'] = 'Тази седмица'; $labels['thisweek'] = 'Тази седмица';
$labels['nextweek'] = 'Следващата седмица'; $labels['nextweek'] = 'Следващата седмица';
$labels['prevweek'] = 'Миналата седмица';
$labels['thismonth'] = 'Този месец'; $labels['thismonth'] = 'Този месец';
$labels['nextmonth'] = 'Следващия месец'; $labels['nextmonth'] = 'Следващия месец';
$labels['weekofyear'] = 'Седмица'; $labels['weekofyear'] = 'Седмица';
$labels['pastevents'] = 'Минали'; $labels['pastevents'] = 'Минали';
$labels['futureevents'] = 'Бъдещи'; $labels['futureevents'] = 'Бъдещи';
$labels['showalarms'] = 'Покажи напомняния';
$labels['defaultalarmtype'] = 'Настройка за напомняне по подразбиране'; $labels['defaultalarmtype'] = 'Настройка за напомняне по подразбиране';
$labels['defaultalarmoffset'] = 'Време за напомняне по подразбиране'; $labels['defaultalarmoffset'] = 'Време за напомняне по подразбиране';
$labels['attendee'] = 'Участник'; $labels['attendee'] = 'Участник';
$labels['role'] = 'Роля'; $labels['role'] = 'Роля';
$labels['availability'] = 'Налич.';
$labels['confirmstate'] = 'Статус'; $labels['confirmstate'] = 'Статус';
$labels['addattendee'] = 'Добавяне на участник'; $labels['addattendee'] = 'Добавяне на участник';
$labels['roleorganizer'] = 'Организатор'; $labels['roleorganizer'] = 'Организатор';
$labels['rolerequired'] = 'Задължителен'; $labels['rolerequired'] = 'Задължителен';
$labels['roleoptional'] = 'По избор'; $labels['roleoptional'] = 'По избор';
$labels['availfree'] = 'Свободно'; $labels['rolechair'] = 'Председател';
$labels['availbusy'] = 'Заето'; $labels['rolenonparticipant'] = 'Липсващ';
$labels['availunknown'] = 'Неизвестно'; $labels['cutypeindividual'] = 'Индивидуален';
$labels['cutypegroup'] = 'Група';
$labels['cutyperesource'] = 'Ресурс';
$labels['cutyperoom'] = 'Стая';
$labels['availfree'] = 'Свободен';
$labels['availbusy'] = 'Зает';
$labels['availunknown'] = 'Няма информация';
$labels['availtentative'] = 'Предварително'; $labels['availtentative'] = 'Предварително';
$labels['availoutofoffice'] = 'Извън офиса'; $labels['availoutofoffice'] = 'Извън офиса';
$labels['delegatedto'] = 'Делегирано към:';
$labels['delegatedfrom'] = 'Делегирано от:';
$labels['sendinvitations'] = 'Изпращане на покани'; $labels['sendinvitations'] = 'Изпращане на покани';
$labels['sendnotifications'] = 'Известяване на участниците относно промените'; $labels['sendnotifications'] = 'Известяване на участниците относно промените';
$labels['sendcancellation'] = 'Известяване на участниците относно отмяна на събития'; $labels['sendcancellation'] = 'Известяване на участниците относно отмяна на събития';
$labels['invitationsubject'] = 'Бяхте поканен на "$title"';
$labels['eventupdatesubject'] = '"$title" беше отмнено';
$labels['itipdeclineevent'] = 'Искате ли да отхвърлите поканата за това събитие?'; $labels['itipdeclineevent'] = 'Искате ли да отхвърлите поканата за това събитие?';
$labels['saveincalendar'] = 'запазване в'; $labels['saveincalendar'] = 'запазване в';
$labels['updatemycopy'] = 'Обнови в моя календар';
$labels['savetocalendar'] = 'Запази в календар';
$labels['openpreview'] = 'Провери календар';
$labels['noearlierevents'] = 'Няма по-ранни събития';
$labels['nolaterevents'] = 'Няма по-късни събития';
$labels['resource'] = 'Ресурс';
$labels['resourcedetails'] = 'Детайли';
$labels['resourceowner'] = 'Собственик';
$labels['tabsummary'] = 'Заглавие'; $labels['tabsummary'] = 'Заглавие';
$labels['tabrecurrence'] = 'Да се повтаря';
$labels['tabattendees'] = 'Участници'; $labels['tabattendees'] = 'Участници';
$labels['tabresources'] = 'Ресурси';
$labels['tabattachments'] = 'Прикрепени файлове'; $labels['tabattachments'] = 'Прикрепени файлове';
$labels['tabsharing'] = 'Споделяне'; $labels['tabsharing'] = 'Споделяне';
$labels['deleteobjectconfirm'] = 'Наистина ли искате да премахнете това събитие?'; $labels['deleteobjectconfirm'] = 'Наистина ли искате да премахнете това събитие?';
$labels['deleteventconfirm'] = 'Наистина ли искате да премахнете това събитие?'; $labels['deleteventconfirm'] = 'Наистина ли искате да премахнете това събитие?';
$labels['deletecalendarconfirm'] = 'Наистина ли искате да премахнете този календар с всичките му събития?'; $labels['deletecalendarconfirm'] = 'Наистина ли искате да премахнете този календар с всичките му събития?';
$labels['deletecalendarconfirmrecursive'] = 'Наистина ли искате да премахнете този календар с всичките му събития и допълнителни календари?';
$labels['savingdata'] = 'Запазване на данни...'; $labels['savingdata'] = 'Запазване на данни...';
$labels['errorsaving'] = 'Неуспешно записването на промените.'; $labels['errorsaving'] = 'Неуспешно записването на промените.';
$labels['successremoval'] = 'Събитието беше премахнато успешно.';
$labels['successrestore'] = 'Събитието беше възстановено успешно.';
$labels['importedsuccessfully'] = 'Събитието е добавено успешно към \'$calendar\'';
$labels['updatedsuccessfully'] = 'Събитието беше обновено успешно в \'$calendar\'';
$labels['importsuccess'] = '$nr събития бяха внесени успешно.';
$labels['importnone'] = 'Не бяха намерени събития за внасяне';
$labels['importerror'] = 'Възникна грешка при внасянето на събития';
$labels['aclnorights'] = 'Не разполагате с административни права върху този календар.';
$labels['changeeventconfirm'] = 'Промяна на събитие';
$labels['removeeventconfirm'] = 'Изтриване на събитие';
$labels['currentevent'] = 'Настоящи';
$labels['futurevents'] = 'Бъдещи'; $labels['futurevents'] = 'Бъдещи';
$labels['allevents'] = 'Всички';
$labels['saveasnew'] = 'Запази като нов';
$labels['birthdays'] = 'Рождени дни';
$labels['arialabelcalendarview'] = 'Преглед на календара';
$labels['arialabeleventattendees'] = 'Списък с участниците в събитието';
$labels['arialabelresourceselection'] = 'Налични ресурси';
?> ?>

View file

@ -169,7 +169,6 @@ $labels['invitationattendlinks'] = "En cas que el vostre client de correu electr
$labels['eventupdatesubject'] = '"$title" ha estat actualitzat'; $labels['eventupdatesubject'] = '"$title" ha estat actualitzat';
$labels['eventupdatesubjectempty'] = 'Un esdeveniment que us afecta ha estat actualitzat'; $labels['eventupdatesubjectempty'] = 'Un esdeveniment que us afecta ha estat actualitzat';
$labels['eventupdatemailbody'] = "*\$title*\n\nQuan: \$date\n\nConvidats: \$attendees\n\nSi us plau cerqueu el fitxer iCalendar adjunt dins dels detalls actualitzats de l'esdeveniment per poder-lo importar a la vostra aplicació de calendari."; $labels['eventupdatemailbody'] = "*\$title*\n\nQuan: \$date\n\nConvidats: \$attendees\n\nSi us plau cerqueu el fitxer iCalendar adjunt dins dels detalls actualitzats de l'esdeveniment per poder-lo importar a la vostra aplicació de calendari.";
$labels['eventcancelsubject'] = '"$title" ha estat cancel·lat';
$labels['eventcancelmailbody'] = "*\$title*\n\nQuan: \$date\n\nConvidats: \$attendees\n\nL'esdeveniment ha estat cancel·lat per \$organizer.\n\nSi us plau cerqueu el fitxer iCalendar adjunt amb els detalls actualitzats de l'esdeveniment."; $labels['eventcancelmailbody'] = "*\$title*\n\nQuan: \$date\n\nConvidats: \$attendees\n\nL'esdeveniment ha estat cancel·lat per \$organizer.\n\nSi us plau cerqueu el fitxer iCalendar adjunt amb els detalls actualitzats de l'esdeveniment.";
$labels['itipobjectnotfound'] = 'L\'esdeveniment que fa referència aquest missatge no s\'ha trobat al vostre calendari.'; $labels['itipobjectnotfound'] = 'L\'esdeveniment que fa referència aquest missatge no s\'ha trobat al vostre calendari.';
$labels['itipmailbodyaccepted'] = "\$sender ha acceptat la invitació al següent esdeveniment:\n\n*\$title*\n\nQuan: \$date\n\nConvidats: \$attendees"; $labels['itipmailbodyaccepted'] = "\$sender ha acceptat la invitació al següent esdeveniment:\n\n*\$title*\n\nQuan: \$date\n\nConvidats: \$attendees";

View file

@ -236,6 +236,7 @@ $labels['changeeventconfirm'] = 'Změnit událost';
$labels['removeeventconfirm'] = 'Smazat událost'; $labels['removeeventconfirm'] = 'Smazat událost';
$labels['changerecurringeventwarning'] = 'Toto je opakovaná událost. Chcete upravit jen toto konání, toto a všechna následující konání, úplně všechna konání nebo uložit událost jako novou?'; $labels['changerecurringeventwarning'] = 'Toto je opakovaná událost. Chcete upravit jen toto konání, toto a všechna následující konání, úplně všechna konání nebo uložit událost jako novou?';
$labels['removerecurringeventwarning'] = 'Toto je opakovaná událost. Chcete smazat jen toto konání, toto a všechna následující konání, nebo úplně všechna konání?'; $labels['removerecurringeventwarning'] = 'Toto je opakovaná událost. Chcete smazat jen toto konání, toto a všechna následující konání, nebo úplně všechna konání?';
$labels['removerecurringallonly'] = 'Toto je opakovaná událost. Jako účastník můžete smazat pouze celou událost se všemi konáními.';
$labels['currentevent'] = 'Nynější'; $labels['currentevent'] = 'Nynější';
$labels['futurevents'] = 'Budoucí'; $labels['futurevents'] = 'Budoucí';
$labels['allevents'] = 'Vše'; $labels['allevents'] = 'Vše';
@ -247,10 +248,13 @@ $labels['birthdayscalendarsources'] = 'Z těchto adresářů';
$labels['birthdayeventtitle'] = 'Narozeniny $name'; $labels['birthdayeventtitle'] = 'Narozeniny $name';
$labels['birthdayage'] = 'Věk $age'; $labels['birthdayage'] = 'Věk $age';
$labels['objectchangelog'] = 'Historie změn'; $labels['objectchangelog'] = 'Historie změn';
$labels['objectdiff'] = 'Změny od $rev1 do $rev2';
$labels['objectnotfound'] = 'Nepodařilo se nahrát data události'; $labels['objectnotfound'] = 'Nepodařilo se nahrát data události';
$labels['objectchangelognotavailable'] = 'Historie změn není pro tuto událost dostupná'; $labels['objectchangelognotavailable'] = 'Historie změn není pro tuto událost dostupná';
$labels['objectdiffnotavailable'] = 'Pro vybrané verze není žádné srovnání možné'; $labels['objectdiffnotavailable'] = 'Pro vybrané verze není žádné srovnání možné';
$labels['revisionrestoreconfirm'] = 'Opravdu chcete obnovit změnu $rev této události? Tímto dojde k nahrazení nynější události starou verzí.'; $labels['revisionrestoreconfirm'] = 'Opravdu chcete obnovit změnu $rev této události? Tímto dojde k nahrazení nynější události starou verzí.';
$labels['objectrestoresuccess'] = 'Pozměnění $rev úspěšně obnoveno';
$labels['objectrestoreerror'] = 'Nepodařilo se obnovit staré pozměnění';
$labels['arialabelminical'] = 'Výběr data v kalendáři'; $labels['arialabelminical'] = 'Výběr data v kalendáři';
$labels['arialabelcalendarview'] = 'Pohled na kalendář'; $labels['arialabelcalendarview'] = 'Pohled na kalendář';
$labels['arialabelsearchform'] = 'Hledání události'; $labels['arialabelsearchform'] = 'Hledání události';

View file

@ -169,7 +169,6 @@ $labels['invitationattendlinks'] = "Hvis dit e-postprogram ikke understøtter iT
$labels['eventupdatesubject'] = '"$title" er blevet opdateret'; $labels['eventupdatesubject'] = '"$title" er blevet opdateret';
$labels['eventupdatesubjectempty'] = 'Et arrangement der vedrører dig er blevet opdateret'; $labels['eventupdatesubjectempty'] = 'Et arrangement der vedrører dig er blevet opdateret';
$labels['eventupdatemailbody'] = "*\$title*\n\nTidspunkt: \$date\n\nInviterede: \$attendees\n\nBemærk venligst vedhæftede iCalendar-fil med alle detaljer om arrangementet, som du kan importere til dit kalenderprogram."; $labels['eventupdatemailbody'] = "*\$title*\n\nTidspunkt: \$date\n\nInviterede: \$attendees\n\nBemærk venligst vedhæftede iCalendar-fil med alle detaljer om arrangementet, som du kan importere til dit kalenderprogram.";
$labels['eventcancelsubject'] = '"$title" er blevet aflyst';
$labels['eventcancelmailbody'] = "*\$title*\n\nTidspunkt: \$date\n\nInviterede: \$attendees\n\nDette arrangement er blevet aflyst af \$organizer.\n\nBemærk venligst vedhæftede iCalendard-fil med de opdaterede detaljer om arrangementet."; $labels['eventcancelmailbody'] = "*\$title*\n\nTidspunkt: \$date\n\nInviterede: \$attendees\n\nDette arrangement er blevet aflyst af \$organizer.\n\nBemærk venligst vedhæftede iCalendard-fil med de opdaterede detaljer om arrangementet.";
$labels['itipobjectnotfound'] = 'Begivenheden som denne besked henviser til, blev ikke fundet i din kalender.'; $labels['itipobjectnotfound'] = 'Begivenheden som denne besked henviser til, blev ikke fundet i din kalender.';
$labels['itipmailbodyaccepted'] = "\$sender har accepteret invitationen til det følgende arrangement:\n\n*\$title*\n\nTidspunkt: \$date\n\nInviterede: \$attendees"; $labels['itipmailbodyaccepted'] = "\$sender har accepteret invitationen til det følgende arrangement:\n\n*\$title*\n\nTidspunkt: \$date\n\nInviterede: \$attendees";

View file

@ -176,10 +176,9 @@ $labels['itipmailbodyaccepted'] = "\$sender hat die Einladung zum folgenden Term
$labels['itipmailbodytentative'] = "\$sender hat die Einladung mit Vorbehalt zum folgenden Termin angenommen:\n\n*\$title*\n\nWann: \$date\n\nTeilnehmer: \$attendees"; $labels['itipmailbodytentative'] = "\$sender hat die Einladung mit Vorbehalt zum folgenden Termin angenommen:\n\n*\$title*\n\nWann: \$date\n\nTeilnehmer: \$attendees";
$labels['itipmailbodydeclined'] = "\$sender hat die Einladung zum folgenden Termin abgelehnt:\n\n*\$title*\n\nWann: \$date\n\nTeilnehmer: \$attendees"; $labels['itipmailbodydeclined'] = "\$sender hat die Einladung zum folgenden Termin abgelehnt:\n\n*\$title*\n\nWann: \$date\n\nTeilnehmer: \$attendees";
$labels['itipmailbodycancel'] = "\$sender hat ihre Teilnahme bei der folgenden Veranstaltung zurückgewiesen:\n\n*\$title*\n\nam: \$date"; $labels['itipmailbodycancel'] = "\$sender hat ihre Teilnahme bei der folgenden Veranstaltung zurückgewiesen:\n\n*\$title*\n\nam: \$date";
$labels['itipmailbodydelegated'] = "\$sender hat die Teilnahme an folgendem Event delegiert:\n\n*\$title*\n\nWhen: \$date"; $labels['itipmailbodydelegated'] = "\$sender hat die Teilnahme an folgendem Event delegiert:\n\n*\$title*\n\nWann: \$date";
$labels['itipmailbodydelegatedto'] = "\$sender hat die Teilnahme an folgendem Event an Sie delegiert:\n\n*\$title*\n\nWhen: \$date"; $labels['itipmailbodydelegatedto'] = "\$sender hat die Teilnahme an folgendem Event an Sie delegiert:\n\n*\$title*\n\nWann: \$date";
$labels['itipdeclineevent'] = 'Möchten Sie die Einladung zu diesem Termin ablehnen?'; $labels['itipdeclineevent'] = 'Möchten Sie die Einladung zu diesem Termin ablehnen?';
$labels['declinedeleteconfirm'] = 'Do you also want to delete this declined event from your calendar?';
$labels['itipcomment'] = 'Kommentar zur Einladungs/Benachrichtigung'; $labels['itipcomment'] = 'Kommentar zur Einladungs/Benachrichtigung';
$labels['itipcommenttitle'] = 'Dieser Kommentar wird an die Einladungs/Benachrichtigung angehängt, die an die Teilnehmer verschickt wird'; $labels['itipcommenttitle'] = 'Dieser Kommentar wird an die Einladungs/Benachrichtigung angehängt, die an die Teilnehmer verschickt wird';
$labels['notanattendee'] = 'Sie sind nicht in der Liste der Teilnehmer aufgeführt'; $labels['notanattendee'] = 'Sie sind nicht in der Liste der Teilnehmer aufgeführt';

View file

@ -8,6 +8,7 @@
*/ */
$labels['default_view'] = 'Standardansicht'; $labels['default_view'] = 'Standardansicht';
$labels['time_format'] = 'Zeitformatierung'; $labels['time_format'] = 'Zeitformatierung';
$labels['timeslots'] = 'Zeitfenster pro Stunde';
$labels['first_day'] = 'Erster Wochentag'; $labels['first_day'] = 'Erster Wochentag';
$labels['first_hour'] = 'Erste angezeigte Stunde'; $labels['first_hour'] = 'Erste angezeigte Stunde';
$labels['workinghours'] = 'Arbeitszeiten'; $labels['workinghours'] = 'Arbeitszeiten';
@ -174,9 +175,9 @@ $labels['itipobjectnotfound'] = 'Der Termin auf den sich diese Nachricht bezieht
$labels['itipmailbodyaccepted'] = "\$sender hat die Einladung zum folgenden Termin angenommen:\n\n*\$title*\n\nWann: \$date\n\nTeilnehmer: \$attendees"; $labels['itipmailbodyaccepted'] = "\$sender hat die Einladung zum folgenden Termin angenommen:\n\n*\$title*\n\nWann: \$date\n\nTeilnehmer: \$attendees";
$labels['itipmailbodytentative'] = "\$sender hat die Einladung mit Vorbehalt zum folgenden Termin angenommen:\n\n*\$title*\n\nWann: \$date\n\nTeilnehmer: \$attendees"; $labels['itipmailbodytentative'] = "\$sender hat die Einladung mit Vorbehalt zum folgenden Termin angenommen:\n\n*\$title*\n\nWann: \$date\n\nTeilnehmer: \$attendees";
$labels['itipmailbodydeclined'] = "\$sender hat die Einladung zum folgenden Termin abgelehnt:\n\n*\$title*\n\nWann: \$date\n\nTeilnehmer: \$attendees"; $labels['itipmailbodydeclined'] = "\$sender hat die Einladung zum folgenden Termin abgelehnt:\n\n*\$title*\n\nWann: \$date\n\nTeilnehmer: \$attendees";
$labels['itipmailbodycancel'] = "\$sender hat Ihre Teilnahme bei der folgenden Veranstaltung zurückgewiesen:\n\n*\$title*\n\nam: \$date"; $labels['itipmailbodycancel'] = "\$sender hat Ihre Teilnahme bei der folgenden Veranstaltung zurückgewiesen:\n\n*\$title*\n\nWann: \$date";
$labels['itipmailbodydelegated'] = "\$sender hat die Teilnahme an folgendem Event delegiert:\n\n*\$title*\n\nWhen: \$date"; $labels['itipmailbodydelegated'] = "\$sender hat die Teilnahme an folgendem Event delegiert:\n\n*\$title*\n\nWhen: \$date";
$labels['itipmailbodydelegatedto'] = "\$sender hat die Teilnahme an folgendem Event an Sie delegiert:\n\n*\$title*\n\nWhen: \$date"; $labels['itipmailbodydelegatedto'] = "\$sender hat die Teilnahme an folgendem Event an Sie delegiert:\n\n*\$title*\n\nWann: \$date";
$labels['itipdeclineevent'] = 'Möchten Sie die Einladung zu diesem Termin ablehnen?'; $labels['itipdeclineevent'] = 'Möchten Sie die Einladung zu diesem Termin ablehnen?';
$labels['declinedeleteconfirm'] = 'Soll der abgelehnte Termin zusätzlich aus dem Kalender gelöscht werden?'; $labels['declinedeleteconfirm'] = 'Soll der abgelehnte Termin zusätzlich aus dem Kalender gelöscht werden?';
$labels['itipcomment'] = 'Kommentar zur Einladungs-/Benachrichtigungsnachricht'; $labels['itipcomment'] = 'Kommentar zur Einladungs-/Benachrichtigungsnachricht';

View file

@ -182,7 +182,7 @@ $labels['invitationattendlinks'] = "In case your email client doesn't support iT
$labels['eventupdatesubject'] = '"$title" has been updated'; $labels['eventupdatesubject'] = '"$title" has been updated';
$labels['eventupdatesubjectempty'] = 'An event that concerns you 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['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 canceled'; $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."; $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) // invitation handling (overrides labels from libcalendaring)

View file

@ -80,6 +80,7 @@ $labels['sensitivity'] = 'Privacidad';
$labels['public'] = 'público'; $labels['public'] = 'público';
$labels['private'] = 'privado'; $labels['private'] = 'privado';
$labels['confidential'] = 'confidencial'; $labels['confidential'] = 'confidencial';
$labels['links'] = 'Referencia';
$labels['alarms'] = 'Recordatorio'; $labels['alarms'] = 'Recordatorio';
$labels['comment'] = 'Comentario'; $labels['comment'] = 'Comentario';
$labels['created'] = 'Creado'; $labels['created'] = 'Creado';
@ -88,6 +89,7 @@ $labels['unknown'] = 'Desconocido';
$labels['eventoptions'] = 'Opciones'; $labels['eventoptions'] = 'Opciones';
$labels['generated'] = 'generado en'; $labels['generated'] = 'generado en';
$labels['eventhistory'] = 'Historial'; $labels['eventhistory'] = 'Historial';
$labels['removelink'] = 'Eliminar referencia de correo';
$labels['printdescriptions'] = 'Imprimir descripciones'; $labels['printdescriptions'] = 'Imprimir descripciones';
$labels['parentcalendar'] = 'Insertar dentro'; $labels['parentcalendar'] = 'Insertar dentro';
$labels['searchearlierdates'] = '« Buscar eventos anteriores'; $labels['searchearlierdates'] = '« Buscar eventos anteriores';
@ -167,13 +169,15 @@ $labels['invitationattendlinks'] = "En caso que su cliente de correo electrónic
$labels['eventupdatesubject'] = '"$title" ha sido actualizado'; $labels['eventupdatesubject'] = '"$title" ha sido actualizado';
$labels['eventupdatesubjectempty'] = 'Un evento que le interesa ha sido actualizado'; $labels['eventupdatesubjectempty'] = 'Un evento que le interesa ha sido actualizado';
$labels['eventupdatemailbody'] = "*\$title*\n\nCuándo: \$date\n\nInvitados: \$attendees\n\nEncontrará adjunto un archivo iCalendar con todos los detalles del evento, el cual puede importar a su aplicación de calendario."; $labels['eventupdatemailbody'] = "*\$title*\n\nCuándo: \$date\n\nInvitados: \$attendees\n\nEncontrará adjunto un archivo iCalendar con todos los detalles del evento, el cual puede importar a su aplicación de calendario.";
$labels['eventcancelsubject'] = '"$title" has been canceled'; $labels['eventcancelsubject'] = '"$title" ha sido cancelado';
$labels['eventcancelmailbody'] = "*\$title*\n\nCuándo: \$date\n\nInvitados: \$attendees\n\nEl evento ha sido cancelado por \$organizer.\n\nEncontrará adjunto un archivo iCalendar con todos los detalles actualizados del evento."; $labels['eventcancelmailbody'] = "*\$title*\n\nCuándo: \$date\n\nInvitados: \$attendees\n\nEl evento ha sido cancelado por \$organizer.\n\nEncontrará adjunto un archivo iCalendar con todos los detalles actualizados del evento.";
$labels['itipobjectnotfound'] = 'El evento referido por este mensaje no fue encontrado en su calendario.'; $labels['itipobjectnotfound'] = 'El evento referido por este mensaje no fue encontrado en su calendario.';
$labels['itipmailbodyaccepted'] = "\$sender ha aceptado la invitación al siguiente evento:\n\n*\$title*\n\nCuándo: \$date\n\nInvitados: \$attendees"; $labels['itipmailbodyaccepted'] = "\$sender ha aceptado la invitación al siguiente evento:\n\n*\$title*\n\nCuándo: \$date\n\nInvitados: \$attendees";
$labels['itipmailbodytentative'] = "\$sender ha tentativamente aceptado la invitación al siguiente evento:\n\n*\$title*\n\nCuándo: \$date\n\nInvitados: \$attendees"; $labels['itipmailbodytentative'] = "\$sender ha tentativamente aceptado la invitación al siguiente evento:\n\n*\$title*\n\nCuándo: \$date\n\nInvitados: \$attendees";
$labels['itipmailbodydeclined'] = "\$sender ha rechazado la invitación al siguiente evento:\n\n*\$title*\n\nCuándo: \$date\n\nInvitados: \$attendees"; $labels['itipmailbodydeclined'] = "\$sender ha rechazado la invitación al siguiente evento:\n\n*\$title*\n\nCuándo: \$date\n\nInvitados: \$attendees";
$labels['itipmailbodycancel'] = "\$sender ha rechazado tu participación en el siguiente evento:\n\n*\$title*\n\nCuándo:\$date"; $labels['itipmailbodycancel'] = "\$sender ha rechazado tu participación en el siguiente evento:\n\n*\$title*\n\nCuándo:\$date";
$labels['itipmailbodydelegated'] = "\$sender ha delegado la invitación en el siguiente evento:\n\n*\$title*\n\nCuándo: \$date";
$labels['itipmailbodydelegatedto'] = "\$sender ha delegado la participación en el siguiente evento a usted:\n\n*\$title*\n\nCuándo: \$date";
$labels['itipdeclineevent'] = '¿Quiere rechazar la invitación a este evento?'; $labels['itipdeclineevent'] = '¿Quiere rechazar la invitación a este evento?';
$labels['declinedeleteconfirm'] = '¿Quiere también eliminar este evento rechazado de su calendario?'; $labels['declinedeleteconfirm'] = '¿Quiere también eliminar este evento rechazado de su calendario?';
$labels['itipcomment'] = 'Comentario de la invitación/notificación'; $labels['itipcomment'] = 'Comentario de la invitación/notificación';
@ -232,6 +236,7 @@ $labels['changeeventconfirm'] = 'Cambiar evento';
$labels['removeeventconfirm'] = 'Eliminar evento'; $labels['removeeventconfirm'] = 'Eliminar evento';
$labels['changerecurringeventwarning'] = 'Este es un evento recurrente. ¿Desea editar solo el evento actual, este y las ocurrencias futuras, todas las ocurrencias o guardarlo como un evento nuevo?'; $labels['changerecurringeventwarning'] = 'Este es un evento recurrente. ¿Desea editar solo el evento actual, este y las ocurrencias futuras, todas las ocurrencias o guardarlo como un evento nuevo?';
$labels['removerecurringeventwarning'] = 'Este es un evento recurrente. ¿Desea eliminar solo el evento actual, este y las ocurrencias futuras o todas las ocurrencias del evento?'; $labels['removerecurringeventwarning'] = 'Este es un evento recurrente. ¿Desea eliminar solo el evento actual, este y las ocurrencias futuras o todas las ocurrencias del evento?';
$labels['removerecurringallonly'] = 'Este es un evento recurrente. Como participante, usted solamente puede eliminar el evento completo con todas las ocurrencias.';
$labels['currentevent'] = 'Actual'; $labels['currentevent'] = 'Actual';
$labels['futurevents'] = 'Futuro'; $labels['futurevents'] = 'Futuro';
$labels['allevents'] = 'Todos'; $labels['allevents'] = 'Todos';
@ -243,10 +248,13 @@ $labels['birthdayscalendarsources'] = 'De estas libretas de direcciones';
$labels['birthdayeventtitle'] = 'Cumpleaños de $name'; $labels['birthdayeventtitle'] = 'Cumpleaños de $name';
$labels['birthdayage'] = 'Edad $age'; $labels['birthdayage'] = 'Edad $age';
$labels['objectchangelog'] = 'Cambiar Historial'; $labels['objectchangelog'] = 'Cambiar Historial';
$labels['objectdiff'] = 'Cambios de $rev1 a $rev2';
$labels['objectnotfound'] = 'Fallo al cargar datos del evento'; $labels['objectnotfound'] = 'Fallo al cargar datos del evento';
$labels['objectchangelognotavailable'] = 'Cambiar historial no esta disponible para este evento'; $labels['objectchangelognotavailable'] = 'Cambiar historial no esta disponible para este evento';
$labels['objectdiffnotavailable'] = 'No es posible comparar las revisiones seleccionadas'; $labels['objectdiffnotavailable'] = 'No es posible comparar las revisiones seleccionadas';
$labels['revisionrestoreconfirm'] = 'Confirme que quiere recuperar la revisión $rev de este evento. Esta acción reemplazará el evento actual con la versión anterior.'; $labels['revisionrestoreconfirm'] = 'Confirme que quiere recuperar la revisión $rev de este evento. Esta acción reemplazará el evento actual con la versión anterior.';
$labels['objectrestoresuccess'] = 'Revisión $rev recuperada exitosamente';
$labels['objectrestoreerror'] = 'Fallo al restaurar el evento anterior';
$labels['arialabelminical'] = 'Selección de fecha del calendario'; $labels['arialabelminical'] = 'Selección de fecha del calendario';
$labels['arialabelcalendarview'] = 'Vista del calendario'; $labels['arialabelcalendarview'] = 'Vista del calendario';
$labels['arialabelsearchform'] = 'Formulario de búsqueda de evento'; $labels['arialabelsearchform'] = 'Formulario de búsqueda de evento';

View file

@ -6,19 +6,198 @@
* *
* For translation see https://www.transifex.com/projects/p/kolab/resource/calendar/ * For translation see https://www.transifex.com/projects/p/kolab/resource/calendar/
*/ */
$labels['default_view'] = 'Vista predeterminada';
$labels['time_format'] = 'Formato de tiempo';
$labels['timeslots'] = 'Los intervalos de tiempo por hora';
$labels['first_day'] = 'Primer día de la semana';
$labels['first_hour'] = 'Primera hora para mostrar';
$labels['workinghours'] = 'Horas laborales';
$labels['add_category'] = 'Añadir categoría';
$labels['remove_category'] = 'Borrar la categoría';
$labels['defaultcalendar'] = 'Crear nuevos eventos en';
$labels['eventcoloring'] = 'Evento para colorear';
$labels['coloringmode0'] = 'De acuerdo con el calendario';
$labels['coloringmode1'] = 'De acuerdo con la categoría';
$labels['coloringmode2'] = 'Calendario para el esquema, la categoría de contenido';
$labels['coloringmode3'] = 'Calendario para el esquema, la categoría de contenido';
$labels['afternothing'] = 'No hacer nada';
$labels['aftertrash'] = 'Mover a la papelera';
$labels['afterdelete'] = 'Eliminar el mensaje';
$labels['afterflagdeleted'] = 'Marcar como eliminado';
$labels['aftermoveto'] = 'Mover a...';
$labels['itipoptions'] = 'Invitaciones para el evento';
$labels['afteraction'] = 'Se procesa después de un mensaje de invitación o actualización';
$labels['calendar'] = 'Calendario';
$labels['calendars'] = 'Calendarios'; $labels['calendars'] = 'Calendarios';
$labels['category'] = 'Categoría';
$labels['categories'] = 'Categorías';
$labels['createcalendar'] = 'Crear nuevo calendario';
$labels['editcalendar'] = 'Editar propiedades del calendario';
$labels['name'] = 'Nombre'; $labels['name'] = 'Nombre';
$labels['color'] = 'Color';
$labels['day'] = 'Día';
$labels['week'] = 'Semana';
$labels['month'] = 'Mes';
$labels['agenda'] = 'Agenda';
$labels['new'] = 'Nuevo';
$labels['new_event'] = 'Nuevo evento';
$labels['edit_event'] = 'Editar evento';
$labels['edit'] = 'Editar'; $labels['edit'] = 'Editar';
$labels['save'] = 'Guardar'; $labels['save'] = 'Guardar';
$labels['removelist'] = 'Borrar de la lista';
$labels['cancel'] = 'Cancelar'; $labels['cancel'] = 'Cancelar';
$labels['select'] = 'Seleccionar';
$labels['print'] = 'Imprimir';
$labels['printtitle'] = 'Imprimir calendarios';
$labels['title'] = 'Resumen';
$labels['description'] = 'Descripción';
$labels['all-day'] = 'Todo el día';
$labels['export'] = 'Exportar';
$labels['exporttitle'] = 'Exportar a iCalendar';
$labels['exportrange'] = 'Eventos de';
$labels['exportattachments'] = 'con los adjuntos';
$labels['customdate'] = 'Fecha personalizada';
$labels['location'] = 'Ubicación';
$labels['url'] = 'URL';
$labels['date'] = 'Fecha';
$labels['start'] = 'Inicio';
$labels['starttime'] = 'Tiempo de inicio';
$labels['end'] = 'Fin';
$labels['endtime'] = 'Hora de finalización';
$labels['repeat'] = 'Repetir';
$labels['selectdate'] = 'Seleccione la fecha';
$labels['freebusy'] = 'Mostrarme como';
$labels['free'] = 'Disponible';
$labels['busy'] = 'Ocupado';
$labels['outofoffice'] = 'Fuera de la oficina';
$labels['tentative'] = 'Provisional';
$labels['mystatus'] = 'Mi estado';
$labels['status'] = 'Estado';
$labels['status-confirmed'] = 'Confirmado';
$labels['status-cancelled'] = 'Cancelado';
$labels['priority'] = 'Prioridad';
$labels['sensitivity'] = 'Privacidad';
$labels['public'] = 'Pública';
$labels['private'] = 'Privada';
$labels['confidential'] = 'Confidencial';
$labels['links'] = 'Referencia';
$labels['alarms'] = 'Recordatorio';
$labels['comment'] = 'Comentario'; $labels['comment'] = 'Comentario';
$labels['created'] = 'Creado';
$labels['changed'] = 'Última modificación';
$labels['unknown'] = 'Desconocido';
$labels['eventoptions'] = 'Opciones'; $labels['eventoptions'] = 'Opciones';
$labels['generated'] = 'Creado a las';
$labels['eventhistory'] = 'Historial';
$labels['removelink'] = 'Eliminar la referencia de correo electrónico';
$labels['printdescriptions'] = 'Imprimir descripción ';
$labels['parentcalendar'] = 'Inserte en el interior';
$labels['searchearlierdates'] = '« Búsqueda de eventos anteriores';
$labels['searchlaterdates'] = 'Búsqueda de eventos posteriores »';
$labels['andnmore'] = '$nr más...';
$labels['togglerole'] = 'Haga clic para cambiar el rol';
$labels['createfrommail'] = 'Guardar como evento';
$labels['importevents'] = 'Importar eventos';
$labels['importrange'] = 'Eventos de';
$labels['onemonthback'] = '1 mes atrás';
$labels['nmonthsback'] = '$nr meses atrás';
$labels['showurl'] = 'Mostrar URL del calendario';
$labels['showurldescription'] = 'Usar la siguiente dirección para acceder (sólo lectura) en su calendario desde otras aplicaciones. Puede copiar y pegar esto en cualquier software de calendario que admita el formato iCal.';
$labels['caldavurldescription'] = 'Copie esta dirección en un <a href="http://en.wikipedia.org/wiki/CalDAV" target="_blank">CalDAV</a> cliente (Evolution o Mozilla Thunderbird) para sincronizar esta tarea con su ordenador o celular.';
$labels['findcalendars'] = 'Buscar calendarios ...';
$labels['searchterms'] = 'Buscar términos';
$labels['calsearchresults'] = 'Calendarios disponibles';
$labels['calendarsubscribe'] = 'Lista Permanente';
$labels['nocalendarsfound'] = 'No se han encontrado calendarios';
$labels['nrcalendarsfound'] = '$nr calendarios encontrados';
$labels['quickview'] = 'Ver solo este calendario';
$labels['invitationspending'] = 'Invitaciones pendientes';
$labels['invitationsdeclined'] = 'Invitaciones rechazada';
$labels['changepartstat'] = 'Cambiar el estado del participante';
$labels['rsvpcomment'] = 'Texto de la invitación';
$labels['listrange'] = 'Rango de visualización:';
$labels['listsections'] = 'Dividir en:';
$labels['smartsections'] = 'Secciones inteligentes';
$labels['until'] = 'hasta';
$labels['today'] = 'Hoy';
$labels['tomorrow'] = 'Mañana';
$labels['thisweek'] = 'Esta semana';
$labels['nextweek'] = 'Próxima semana';
$labels['prevweek'] = 'Semana pasada';
$labels['thismonth'] = 'Este mes';
$labels['nextmonth'] = 'Próximo mes';
$labels['weekofyear'] = 'Semana';
$labels['pastevents'] = 'Pasado';
$labels['futureevents'] = 'Futuro';
$labels['showalarms'] = 'Mostrar recordatorios';
$labels['defaultalarmtype'] = 'Configuración predeterminada del recordatorio';
$labels['defaultalarmoffset'] = 'Tiempo de aviso predeterminado';
$labels['attendee'] = 'Participante';
$labels['role'] = 'Rol';
$labels['availability'] = 'Disponible';
$labels['confirmstate'] = 'Estado';
$labels['addattendee'] = 'Añada participante';
$labels['roleorganizer'] = 'Organizador';
$labels['rolerequired'] = 'Requerido'; $labels['rolerequired'] = 'Requerido';
$labels['roleoptional'] = 'Opcional'; $labels['roleoptional'] = 'Opcional';
$labels['rolechair'] = 'Silla';
$labels['rolenonparticipant'] = 'Ausente';
$labels['cutypeindividual'] = 'Individual';
$labels['cutypegroup'] = 'Grupo'; $labels['cutypegroup'] = 'Grupo';
$labels['cutyperesource'] = 'Recurso'; $labels['cutyperesource'] = 'Recurso';
$labels['cutyperoom'] = 'Habitación';
$labels['availfree'] = 'Disponible';
$labels['availbusy'] = 'Ocupado';
$labels['availunknown'] = 'Desconocido';
$labels['availtentative'] = 'Provisional';
$labels['availoutofoffice'] = 'Fuera de la oficina';
$labels['delegatedto'] = 'Delegar a: ';
$labels['delegatedfrom'] = 'Delegado de:';
$labels['scheduletime'] = 'Buscar disponibilidad';
$labels['sendinvitations'] = 'Enviar invitaciones';
$labels['sendnotifications'] = 'Notificar a los participantes acerca de las modificaciones';
$labels['sendcancellation'] = 'Notificar a los participantes sobre la cancelación de eventos';
$labels['onlyworkinghours'] = 'Encuentra disponibilidad dentro de mis horas de trabajo';
$labels['reqallattendees'] = 'Requeridos/todos los participantes';
$labels['prevslot'] = 'Ranura anterior';
$labels['nextslot'] = 'Siguiente ranura';
$labels['suggestedslot'] = 'Ranura sugerida';
$labels['noslotfound'] = 'Incapaz de encontrar un intervalo de tiempo libre';
$labels['invitationsubject'] = 'Usted sido invitado a "$title"';
$labels['invitationmailbody'] = "*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees\n\nSe adjunta un archivo iCalendar con todos los detalles del evento que se puede importar a la aplicación de calendario.";
$labels['invitationattendlinks'] = "En caso de que su cliente de correo electrónico no admite solicitudes iTIP que puede utilizar el siguiente enlace para aceptar o rechazar esta invitación:\n\$url";
$labels['eventupdatesubject'] = '"$title" Ha sido actualizado';
$labels['eventupdatesubjectempty'] = 'Un evento que le concierne ha sido actualizado';
$labels['eventupdatemailbody'] = "*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees\n\nSe adjunta un archivo iCalendar con los detalles del evento actualizados que se puede importar a la aplicación de calendario.";
$labels['eventcancelmailbody'] = "*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees\n\nEl evento ha sido cancelado por\$organizer.\n\nSe adjunta un archivo iCalendar con los detalles del evento actualizados.";
$labels['itipobjectnotfound'] = 'El evento referido por este mensaje no se encontró en su calendario.';
$labels['itipmailbodyaccepted'] = "\$sender ha aceptado la invitación al evento siguiente:\n\n*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees";
$labels['itipmailbodytentative'] = "\$sender ha aceptado provisionalmente la invitación al evento siguiente:\n\n*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees";
$labels['itipmailbodydeclined'] = "\$sender ha declinado la invitación al evento siguiente:\n\n*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees";
$labels['itipmailbodycancel'] = "\$sender ha rechazado su participación en el evento siguiente:\n\n*\$title*\n\nWhen: \$date";
$labels['itipmailbodydelegated'] = "\$sender ha delegado la participación en el evento siguiente:\n\n*\$title*\n\nWhen: \$date";
$labels['itipmailbodydelegatedto'] = "\$sender ha delegado la participación en el siguiente evento a usted:\n\n*\$title*\n\nWhen: \$date";
$labels['itipdeclineevent'] = '¿Quieres rechazar la invitación a este evento?';
$labels['declinedeleteconfirm'] = '¿Usted también desea eliminar este declinado evento del calendario?';
$labels['itipcomment'] = 'Añadir comentarios a la Invitación/notificación';
$labels['itipcommenttitle'] = 'Este comentario se adjunta al mensaje de invitación/notificación enviada a los participantes';
$labels['notanattendee'] = 'Usted no está en la lista como un asistente de este evento';
$labels['eventcancelled'] = 'El evento ha sido cancelado';
$labels['resource'] = 'Recurso'; $labels['resource'] = 'Recurso';
$labels['resourcedetails'] = 'Detalles'; $labels['resourcedetails'] = 'Detalles';
$labels['tabsummary'] = 'Sumario';
$labels['tabrecurrence'] = 'Recurrencia ';
$labels['tabresources'] = 'Recursos'; $labels['tabresources'] = 'Recursos';
$labels['tabattachments'] = 'Adjuntos';
$labels['tabsharing'] = 'Compartir';
$labels['savingdata'] = 'Guardando datos...'; $labels['savingdata'] = 'Guardando datos...';
$labels['attendeupdateesuccess'] = 'Se ha actualizado correctamente el estado del participante';
$labels['itipinvalidrequest'] = 'Esta invitación ya no es válida';
$labels['futurevents'] = 'Futuro';
$labels['allevents'] = 'Todo';
$labels['objectchangelog'] = 'Cambiar historial';
$labels['objectdiff'] = 'Cambiar de $rev1 a $rev2';
$labels['objectdiffnotavailable'] = 'No hay comparación posible que las revisiones seleccionadas';
$labels['objectrestoresuccess'] = 'Revisión $rev restaurado correctamente';
$labels['objectrestoreerror'] = 'No se pudo restaurar la revisión antigua';
?> ?>

View file

@ -169,13 +169,14 @@ $labels['invitationattendlinks'] = "Dans le cas où votre application de message
$labels['eventupdatesubject'] = '"$title" a été modifié'; $labels['eventupdatesubject'] = '"$title" a été modifié';
$labels['eventupdatesubjectempty'] = 'Un évènement vous concernant a été modifié'; $labels['eventupdatesubjectempty'] = 'Un évènement vous concernant a été modifié';
$labels['eventupdatemailbody'] = "*\$title*\n\nQuand: \$date\n\nParticipants: \$attendees\n\nVous trouverez ci-joint un fichier iCalendar avec tous les modifications de l'évènement que vous pourrez importer dans votre agenda électronique."; $labels['eventupdatemailbody'] = "*\$title*\n\nQuand: \$date\n\nParticipants: \$attendees\n\nVous trouverez ci-joint un fichier iCalendar avec tous les modifications de l'évènement que vous pourrez importer dans votre agenda électronique.";
$labels['eventcancelsubject'] = '"$title" a été annulé';
$labels['eventcancelmailbody'] = "*\$title*\n\nQuand: \$date\n\nParticipants: \$attendees\n\nL'évènement a été annulé par \$organizer.\n\nVous trouverez en pièce jointe un fichier iCalendar avec les modifications de l'évènement que vous pourrez importer dans votre agenda électronique."; $labels['eventcancelmailbody'] = "*\$title*\n\nQuand: \$date\n\nParticipants: \$attendees\n\nL'évènement a été annulé par \$organizer.\n\nVous trouverez en pièce jointe un fichier iCalendar avec les modifications de l'évènement que vous pourrez importer dans votre agenda électronique.";
$labels['itipobjectnotfound'] = 'L\'évènement lié à ce message n\'a pas été trouvé dans votre calendrier.'; $labels['itipobjectnotfound'] = 'L\'évènement lié à ce message n\'a pas été trouvé dans votre calendrier.';
$labels['itipmailbodyaccepted'] = "\$sender a accepté l'invitation à l'évènement suivant :\n\n*\$title*\n\nQuand: \$date\n\nParticipants: \$attendees"; $labels['itipmailbodyaccepted'] = "\$sender a accepté l'invitation à l'évènement suivant :\n\n*\$title*\n\nQuand: \$date\n\nParticipants: \$attendees";
$labels['itipmailbodytentative'] = "\$sender a accepté provisoirement l'invitation à l'évènement suivant :\n\n*\$title*\n\nQuand: \$date\n\nParticipants: \$attendees"; $labels['itipmailbodytentative'] = "\$sender a accepté provisoirement l'invitation à l'évènement suivant :\n\n*\$title*\n\nQuand: \$date\n\nParticipants: \$attendees";
$labels['itipmailbodydeclined'] = "\$sender a refusé l'invitation à l'évènement suivant :\n\n*\$title*\n\nQuand: \$date\n\nParticipants: \$attendees"; $labels['itipmailbodydeclined'] = "\$sender a refusé l'invitation à l'évènement suivant :\n\n*\$title*\n\nQuand: \$date\n\nParticipants: \$attendees";
$labels['itipmailbodycancel'] = "\$sender a rejeté votre participation à lévènement suivant :\n\n*\$title*\n\nLe: \$date"; $labels['itipmailbodycancel'] = "\$sender a rejeté votre participation à lévènement suivant :\n\n*\$title*\n\nLe: \$date";
$labels['itipmailbodydelegated'] = "\$sender a délégué la participation à l'événement suivant : \n\n*\$title*\n\nQuand: \$date";
$labels['itipmailbodydelegatedto'] = "\$sender vous a délégué la participation à l'événement suivant : \n\n*\$title*\n\nQuand : \$date";
$labels['itipdeclineevent'] = 'Voulez-vous refuser l\'invitation à cet évènement?'; $labels['itipdeclineevent'] = 'Voulez-vous refuser l\'invitation à cet évènement?';
$labels['declinedeleteconfirm'] = 'Voulez-vous aussi supprimer cet évènement annulé, de votre calendrier ?'; $labels['declinedeleteconfirm'] = 'Voulez-vous aussi supprimer cet évènement annulé, de votre calendrier ?';
$labels['itipcomment'] = 'Commentaire dinvitation ou de notification'; $labels['itipcomment'] = 'Commentaire dinvitation ou de notification';
@ -233,7 +234,8 @@ $labels['aclnorights'] = 'Vous n\'avez pas les droits d\'administration sur cet
$labels['changeeventconfirm'] = 'Modifier l\'évènement'; $labels['changeeventconfirm'] = 'Modifier l\'évènement';
$labels['removeeventconfirm'] = 'Supprimer l\'évènement'; $labels['removeeventconfirm'] = 'Supprimer l\'évènement';
$labels['changerecurringeventwarning'] = 'Ceci est un évènement récurant. Voulez vous éditer seulement cette occurrence, celle-ci et toutes les suivantes, toutes les occurrences ou l\'enregistrer comme un nouvel évènement? '; $labels['changerecurringeventwarning'] = 'Ceci est un évènement récurant. Voulez vous éditer seulement cette occurrence, celle-ci et toutes les suivantes, toutes les occurrences ou l\'enregistrer comme un nouvel évènement? ';
$labels['removerecurringeventwarning'] = 'Ceci est un évènement recrurent. Voulez-vous supprimer uniquement l\'évènement courent, lévènement courent et toutes ces occurrences futures ou toutes les occurrences ?'; $labels['removerecurringeventwarning'] = 'Ceci est un évènement récurent. Voulez-vous supprimer l\'évènement courant uniquement, lévènement courant et toutes les occurrences futures, ou toutes les occurrences ?';
$labels['removerecurringallonly'] = 'Ceci est un évènement récurent. En tant que participant vous pouvez seulement supprimer l\'évènement entier avec toutes les occurrences.';
$labels['currentevent'] = 'Cette occurrence'; $labels['currentevent'] = 'Cette occurrence';
$labels['futurevents'] = 'Cette occurrence et toutes les suivantes'; $labels['futurevents'] = 'Cette occurrence et toutes les suivantes';
$labels['allevents'] = 'Toutes les occurrences'; $labels['allevents'] = 'Toutes les occurrences';
@ -245,10 +247,13 @@ $labels['birthdayscalendarsources'] = 'Depuis ces carnets d\'adresses';
$labels['birthdayeventtitle'] = 'Anniversaire de $name'; $labels['birthdayeventtitle'] = 'Anniversaire de $name';
$labels['birthdayage'] = 'Age $age'; $labels['birthdayage'] = 'Age $age';
$labels['objectchangelog'] = 'Historique des modifications'; $labels['objectchangelog'] = 'Historique des modifications';
$labels['objectdiff'] = 'Modifications depuis $rev1 jusqu\'à $rev2';
$labels['objectnotfound'] = 'Impossible de charger les données de lévènement'; $labels['objectnotfound'] = 'Impossible de charger les données de lévènement';
$labels['objectchangelognotavailable'] = 'Il n\'y a pas d\'historique des modifications pour cet évènement'; $labels['objectchangelognotavailable'] = 'Il n\'y a pas d\'historique des modifications pour cet évènement';
$labels['objectdiffnotavailable'] = 'La comparaison des versions sélectionnées est impossible'; $labels['objectdiffnotavailable'] = 'La comparaison des versions sélectionnées est impossible';
$labels['revisionrestoreconfirm'] = 'Voulez-vous vraiment restaurer le version $rev de cet évènement ? Cette action va remplacer l\'évènement courent par l\'ancienne version.'; $labels['revisionrestoreconfirm'] = 'Voulez-vous vraiment restaurer le version $rev de cet évènement ? Cette action va remplacer l\'évènement courant par l\'ancienne version.';
$labels['objectrestoresuccess'] = 'La révision $rev a été restaurée avec succès';
$labels['objectrestoreerror'] = 'Échec lors de la restauration de la précédente révision';
$labels['arialabelminical'] = 'Sélection de la date du calendrier'; $labels['arialabelminical'] = 'Sélection de la date du calendrier';
$labels['arialabelcalendarview'] = 'Vue du calendrier'; $labels['arialabelcalendarview'] = 'Vue du calendrier';
$labels['arialabelsearchform'] = 'Recherche d\'évènements depuis'; $labels['arialabelsearchform'] = 'Recherche d\'évènements depuis';

View file

@ -147,7 +147,6 @@ $labels['invitationattendlinks'] = "Amennyiben a levelezőben nem lát elfogadó
$labels['eventupdatesubject'] = '$title - módosítva'; $labels['eventupdatesubject'] = '$title - módosítva';
$labels['eventupdatesubjectempty'] = 'Egy Önt érintő esemény módosítva lett'; $labels['eventupdatesubjectempty'] = 'Egy Önt érintő esemény módosítva lett';
$labels['eventupdatemailbody'] = "Módosítás érkezett '\$title' eseményre vonatkozóan.\n\nIdőpont: \$date\nSzervező: \$organizer\nRésztvevők: \$attendees\n\n\nMellékletben egy frissített iCalendar naptárbejegyzés, mely tetszőleges naptárprogramba importálható."; $labels['eventupdatemailbody'] = "Módosítás érkezett '\$title' eseményre vonatkozóan.\n\nIdőpont: \$date\nSzervező: \$organizer\nRésztvevők: \$attendees\n\n\nMellékletben egy frissített iCalendar naptárbejegyzés, mely tetszőleges naptárprogramba importálható.";
$labels['eventcancelsubject'] = '$title - lemondva';
$labels['eventcancelmailbody'] = "'\$title' eseményre \$organizer szervező visszavonta a meghívást.\n\nIdőpont: \$date\nRésztvevők: \$attendees\n\n\nMellékletben egy frissített iCalendar naptárbejegyzés, mely tetszőleges naptárprogramba importálható."; $labels['eventcancelmailbody'] = "'\$title' eseményre \$organizer szervező visszavonta a meghívást.\n\nIdőpont: \$date\nRésztvevők: \$attendees\n\n\nMellékletben egy frissített iCalendar naptárbejegyzés, mely tetszőleges naptárprogramba importálható.";
$labels['itipobjectnotfound'] = 'Az üzenetben hivatkozott esemény nem található a naptárban.'; $labels['itipobjectnotfound'] = 'Az üzenetben hivatkozott esemény nem található a naptárban.';
$labels['itipmailbodyaccepted'] = "\$sender elfogadta a meghívást '\$title' eseményre.\n\nIdőpont: \$date\nRésztvevők: \$attendees"; $labels['itipmailbodyaccepted'] = "\$sender elfogadta a meghívást '\$title' eseményre.\n\nIdőpont: \$date\nRésztvevők: \$attendees";

View file

@ -169,7 +169,6 @@ $labels['invitationattendlinks'] = "Se il tuo client di posta elettronica non su
$labels['eventupdatesubject'] = '"$title" è stato aggiornato'; $labels['eventupdatesubject'] = '"$title" è stato aggiornato';
$labels['eventupdatesubjectempty'] = 'Un evento che ti riguarda è stato aggiornato'; $labels['eventupdatesubjectempty'] = 'Un evento che ti riguarda è stato aggiornato';
$labels['eventupdatemailbody'] = "*\$title*\n\nQuando: \$date\n\nInvitati: \$attendees\n\nIn allegato un file iCalendar con i dettagli aggiornati dell'evento che puoi importare nella tua applicazione calendario."; $labels['eventupdatemailbody'] = "*\$title*\n\nQuando: \$date\n\nInvitati: \$attendees\n\nIn allegato un file iCalendar con i dettagli aggiornati dell'evento che puoi importare nella tua applicazione calendario.";
$labels['eventcancelsubject'] = '"$title" è stato annullato';
$labels['eventcancelmailbody'] = "*\$title*\n\nQuando: \$date\n\nInvitati: \$attendees\n\nL'evento è stato cancellato da \$organizer.\n\nIn allegato un file iCalendar con i dettagli aggiornati dell'evento ."; $labels['eventcancelmailbody'] = "*\$title*\n\nQuando: \$date\n\nInvitati: \$attendees\n\nL'evento è stato cancellato da \$organizer.\n\nIn allegato un file iCalendar con i dettagli aggiornati dell'evento .";
$labels['itipobjectnotfound'] = 'L\'evento al quale questo messaggio fa riferimento non è stato trovato nel tuo calendario.'; $labels['itipobjectnotfound'] = 'L\'evento al quale questo messaggio fa riferimento non è stato trovato nel tuo calendario.';
$labels['itipmailbodyaccepted'] = "\$sender ha accettato l'invito al seguente evento:\n\n*\$title*\n\nQuando: \$date\n\nInvitati: \$attendees"; $labels['itipmailbodyaccepted'] = "\$sender ha accettato l'invito al seguente evento:\n\n*\$title*\n\nQuando: \$date\n\nInvitati: \$attendees";

View file

@ -8,6 +8,7 @@
*/ */
$labels['default_view'] = 'デフォルトビュー'; $labels['default_view'] = 'デフォルトビュー';
$labels['time_format'] = '時刻表示形式'; $labels['time_format'] = '時刻表示形式';
$labels['timeslots'] = '一時間毎のタイムスロット';
$labels['first_day'] = '最初の平日'; $labels['first_day'] = '最初の平日';
$labels['first_hour'] = '最初の時間を表示'; $labels['first_hour'] = '最初の時間を表示';
$labels['workinghours'] = '労働時間'; $labels['workinghours'] = '労働時間';
@ -19,6 +20,13 @@ $labels['coloringmode0'] = 'カレンダーの説明';
$labels['coloringmode1'] = 'カテゴリの説明'; $labels['coloringmode1'] = 'カテゴリの説明';
$labels['coloringmode2'] = 'アウトライン用カレンダー、コンテンツ用カテゴリ'; $labels['coloringmode2'] = 'アウトライン用カレンダー、コンテンツ用カテゴリ';
$labels['coloringmode3'] = 'アウトライン用カレンダー、コンテンツ用カテゴリ'; $labels['coloringmode3'] = 'アウトライン用カレンダー、コンテンツ用カテゴリ';
$labels['afternothing'] = '何もしない';
$labels['aftertrash'] = 'ゴミ箱へ移動';
$labels['afterdelete'] = 'メッセージを削除';
$labels['afterflagdeleted'] = '削除フラグ';
$labels['aftermoveto'] = '移動...';
$labels['itipoptions'] = 'イベント招待';
$labels['afteraction'] = '招待もしくは更新の後にメッセージは送達されます';
$labels['calendar'] = 'カレンダー'; $labels['calendar'] = 'カレンダー';
$labels['calendars'] = 'カレンダー'; $labels['calendars'] = 'カレンダー';
$labels['category'] = 'カテゴリ'; $labels['category'] = 'カテゴリ';
@ -47,12 +55,15 @@ $labels['all-day'] = '全日';
$labels['export'] = 'エクスポート'; $labels['export'] = 'エクスポート';
$labels['exporttitle'] = 'iカレンダーへエクスポート'; $labels['exporttitle'] = 'iカレンダーへエクスポート';
$labels['exportrange'] = 'イベント元'; $labels['exportrange'] = 'イベント元';
$labels['exportattachments'] = '添付する';
$labels['customdate'] = 'カスタム日時';
$labels['location'] = '場所'; $labels['location'] = '場所';
$labels['url'] = 'URL'; $labels['url'] = 'URL';
$labels['date'] = '期日'; $labels['date'] = '期日';
$labels['start'] = '開始'; $labels['start'] = '開始';
$labels['starttime'] = '開始時間'; $labels['starttime'] = '開始時間';
$labels['end'] = '終了'; $labels['end'] = '終了';
$labels['endtime'] = '終了日';
$labels['repeat'] = '繰返し'; $labels['repeat'] = '繰返し';
$labels['selectdate'] = '日付選択'; $labels['selectdate'] = '日付選択';
$labels['freebusy'] = '表示する'; $labels['freebusy'] = '表示する';
@ -60,7 +71,9 @@ $labels['free'] = '空';
$labels['busy'] = 'ビジー'; $labels['busy'] = 'ビジー';
$labels['outofoffice'] = '外出'; $labels['outofoffice'] = '外出';
$labels['tentative'] = '仮'; $labels['tentative'] = '仮';
$labels['mystatus'] = 'マイ ステータス';
$labels['status'] = '状態'; $labels['status'] = '状態';
$labels['status-confirmed'] = '確認済';
$labels['status-cancelled'] = 'キャンセル済'; $labels['status-cancelled'] = 'キャンセル済';
$labels['priority'] = '優先度'; $labels['priority'] = '優先度';
$labels['sensitivity'] = 'プライバシー'; $labels['sensitivity'] = 'プライバシー';
@ -69,6 +82,7 @@ $labels['private'] = 'プライベート';
$labels['confidential'] = '親展'; $labels['confidential'] = '親展';
$labels['links'] = '参照'; $labels['links'] = '参照';
$labels['alarms'] = '通知'; $labels['alarms'] = '通知';
$labels['comment'] = 'コメント';
$labels['created'] = '作成済'; $labels['created'] = '作成済';
$labels['changed'] = '最終変更'; $labels['changed'] = '最終変更';
$labels['unknown'] = '不明'; $labels['unknown'] = '不明';
@ -89,8 +103,18 @@ $labels['onemonthback'] = '1 ヶ月戻る';
$labels['nmonthsback'] = '$nr ヶ月戻る'; $labels['nmonthsback'] = '$nr ヶ月戻る';
$labels['showurl'] = 'カレンダーURL表示'; $labels['showurl'] = 'カレンダーURL表示';
$labels['showurldescription'] = '以下のアドレスを使用して他のアプリケーションからカレンダーにアクセス(読込のみ)できます。iCal形式をサポートしたカレンダーソフトウェアへコピーアンドペーストができます。'; $labels['showurldescription'] = '以下のアドレスを使用して他のアプリケーションからカレンダーにアクセス(読込のみ)できます。iCal形式をサポートしたカレンダーソフトウェアへコピーアンドペーストができます。';
$labels['caldavurldescription'] = 'この指定したカレンダーをコンピュータもしくはモバイルデバイスと全同期するためにはこのアドレスを <a href="http://en.wikipedia.org/wiki/CalDAV" target="_blank">CalDAV</a> クライアントアプリケーション(たとえばエボリューションやMozilla サンダーバード)へコピーしてください。';
$labels['findcalendars'] = 'カレンダーの検索';
$labels['searchterms'] = '用語検索'; $labels['searchterms'] = '用語検索';
$labels['calsearchresults'] = '有効なカレンダー';
$labels['calendarsubscribe'] = '持続的なもののリスト'; $labels['calendarsubscribe'] = '持続的なもののリスト';
$labels['nocalendarsfound'] = 'カレンダーが見つかりませんでした';
$labels['nrcalendarsfound'] = '$nr カレンダーが見つかりました';
$labels['quickview'] = 'このカレンダーは閲覧のみ';
$labels['invitationspending'] = '保留中の招待';
$labels['invitationsdeclined'] = '断った招待';
$labels['changepartstat'] = '参加者のステータス変更';
$labels['rsvpcomment'] = '招待テキスト';
$labels['listrange'] = '表示範囲:'; $labels['listrange'] = '表示範囲:';
$labels['listsections'] = '分割:'; $labels['listsections'] = '分割:';
$labels['smartsections'] = 'スマートセクション'; $labels['smartsections'] = 'スマートセクション';
@ -99,6 +123,7 @@ $labels['today'] = '今日';
$labels['tomorrow'] = '明日'; $labels['tomorrow'] = '明日';
$labels['thisweek'] = '今週'; $labels['thisweek'] = '今週';
$labels['nextweek'] = '来週'; $labels['nextweek'] = '来週';
$labels['prevweek'] = '前週';
$labels['thismonth'] = '今月'; $labels['thismonth'] = '今月';
$labels['nextmonth'] = '来月'; $labels['nextmonth'] = '来月';
$labels['weekofyear'] = '週'; $labels['weekofyear'] = '週';
@ -116,13 +141,18 @@ $labels['roleorganizer'] = '編成者';
$labels['rolerequired'] = '要件'; $labels['rolerequired'] = '要件';
$labels['roleoptional'] = 'オプション'; $labels['roleoptional'] = 'オプション';
$labels['rolechair'] = '議長'; $labels['rolechair'] = '議長';
$labels['rolenonparticipant'] = '欠席';
$labels['cutypeindividual'] = '個人';
$labels['cutypegroup'] = 'グループ'; $labels['cutypegroup'] = 'グループ';
$labels['cutyperesource'] = 'リソース'; $labels['cutyperesource'] = 'リソース';
$labels['cutyperoom'] = 'ルーム';
$labels['availfree'] = '空'; $labels['availfree'] = '空';
$labels['availbusy'] = 'ビジー'; $labels['availbusy'] = 'ビジー';
$labels['availunknown'] = '不明'; $labels['availunknown'] = '不明';
$labels['availtentative'] = '仮'; $labels['availtentative'] = '仮';
$labels['availoutofoffice'] = '外出'; $labels['availoutofoffice'] = '外出';
$labels['delegatedto'] = '委任先:';
$labels['delegatedfrom'] = '委任元:';
$labels['scheduletime'] = '利用可検索'; $labels['scheduletime'] = '利用可検索';
$labels['sendinvitations'] = '招待を送る'; $labels['sendinvitations'] = '招待を送る';
$labels['sendnotifications'] = '変更を参加者へ通知する'; $labels['sendnotifications'] = '変更を参加者へ通知する';
@ -131,6 +161,7 @@ $labels['onlyworkinghours'] = '労働時間内の利用可検索';
$labels['reqallattendees'] = '要件/全参加者'; $labels['reqallattendees'] = '要件/全参加者';
$labels['prevslot'] = '前のスロット'; $labels['prevslot'] = '前のスロット';
$labels['nextslot'] = '次のスロット'; $labels['nextslot'] = '次のスロット';
$labels['suggestedslot'] = '指示されたスロット';
$labels['noslotfound'] = '空スロットを見つけられません'; $labels['noslotfound'] = '空スロットを見つけられません';
$labels['invitationsubject'] = '"$title" に招待されました'; $labels['invitationsubject'] = '"$title" に招待されました';
$labels['invitationmailbody'] = "*\$title*\n\nいつ: \$date\n\n招待者: \$attendees\n\nあなたのカレンダーアプリケーションにインポートできる全イベントの詳細がインポートできる添付されたiカレンダーファイルを見つけてください。"; $labels['invitationmailbody'] = "*\$title*\n\nいつ: \$date\n\n招待者: \$attendees\n\nあなたのカレンダーアプリケーションにインポートできる全イベントの詳細がインポートできる添付されたiカレンダーファイルを見つけてください。";
@ -138,17 +169,33 @@ $labels['invitationattendlinks'] = "この場合あなたのメールクライ
$labels['eventupdatesubject'] = '"$title" はアップデートされました'; $labels['eventupdatesubject'] = '"$title" はアップデートされました';
$labels['eventupdatesubjectempty'] = 'あなたに関連するイベントが更新されました'; $labels['eventupdatesubjectempty'] = 'あなたに関連するイベントが更新されました';
$labels['eventupdatemailbody'] = "*\$title*\n\nいつ: \$date\n\n招待者: \$attendees\n\nあなたのカレンダーアプリケーションにインポートできるアップデートされた全イベントの詳細が添付されたiカレンダーファイルを見つけてください。"; $labels['eventupdatemailbody'] = "*\$title*\n\nいつ: \$date\n\n招待者: \$attendees\n\nあなたのカレンダーアプリケーションにインポートできるアップデートされた全イベントの詳細が添付されたiカレンダーファイルを見つけてください。";
$labels['eventcancelsubject'] = '"$title" は変更されました';
$labels['eventcancelmailbody'] = "*\$title*\n\nいつ: \$date\n\n招待者: \$attendees\n\nイベントが \$organizer によってキャンセルされました。\n\n更新されたイベントの詳細とともに添付されたiカレンダーファイルを見つけてください。"; $labels['eventcancelmailbody'] = "*\$title*\n\nいつ: \$date\n\n招待者: \$attendees\n\nイベントが \$organizer によってキャンセルされました。\n\n更新されたイベントの詳細とともに添付されたiカレンダーファイルを見つけてください。";
$labels['itipobjectnotfound'] = 'このメッセージから参照されるイベントはカレンダーには見つかりません。';
$labels['itipmailbodyaccepted'] = "\$sender は以下のイベントへの招待を承諾しました:\n\n*\$title*\n\nいつ: \$date\n\n招待者: \$attendees"; $labels['itipmailbodyaccepted'] = "\$sender は以下のイベントへの招待を承諾しました:\n\n*\$title*\n\nいつ: \$date\n\n招待者: \$attendees";
$labels['itipmailbodytentative'] = "\$sender は以下のイベントへの招待を仮承諾しました:\n\n*\$title*\n\nいつ: \$date\n\n招待者: \$attendees"; $labels['itipmailbodytentative'] = "\$sender は以下のイベントへの招待を仮承諾しました:\n\n*\$title*\n\nいつ: \$date\n\n招待者: \$attendees";
$labels['itipmailbodydeclined'] = "\$sender は以下のイベントへの招待を辞退しました:\n\n*\$title*\n\nいつ: \$date\n\n招待者: \$attendees"; $labels['itipmailbodydeclined'] = "\$sender は以下のイベントへの招待を辞退しました:\n\n*\$title*\n\nいつ: \$date\n\n招待者: \$attendees";
$labels['itipmailbodycancel'] = "\$sender は以下のイベントへの参加を断りました:\n\n*\$title*\n\nいつ: \$date";
$labels['itipmailbodydelegated'] = "\$sender は以下のイベントへの参加を委任しました:\n\n*\$title*\n\nいつ: \$date";
$labels['itipmailbodydelegatedto'] = "\$sender は以下のイベントへの参加をあなたへ委任しました:\n\n*\$title*\n\nいつ: \$date";
$labels['itipdeclineevent'] = 'このイベントへの招待を辞退しますか?'; $labels['itipdeclineevent'] = 'このイベントへの招待を辞退しますか?';
$labels['declinedeleteconfirm'] = 'この断ったイベントもカレンダーから削除しますか?';
$labels['itipcomment'] = '招待/通知コメント';
$labels['itipcommenttitle'] = 'このコメントは参加者へ送られた招待/通知メッセージへ添付されます';
$labels['notanattendee'] = 'このイベントの出席者として一覧にありません'; $labels['notanattendee'] = 'このイベントの出席者として一覧にありません';
$labels['eventcancelled'] = 'このイベントはキャンセルされました'; $labels['eventcancelled'] = 'このイベントはキャンセルされました';
$labels['saveincalendar'] = '保存'; $labels['saveincalendar'] = '保存';
$labels['updatemycopy'] = 'マイカレンダーの更新';
$labels['savetocalendar'] = 'カレンダーへ保存';
$labels['openpreview'] = 'カレンダーにチェック';
$labels['noearlierevents'] = '以前のイベントはありません';
$labels['nolaterevents'] = '以降のイベントはありません';
$labels['resource'] = 'リソース'; $labels['resource'] = 'リソース';
$labels['addresource'] = 'リソースの記帳';
$labels['findresources'] = 'リソースの検索';
$labels['resourcedetails'] = '詳細'; $labels['resourcedetails'] = '詳細';
$labels['resourceavailability'] = '利用可';
$labels['resourceowner'] = '所有者';
$labels['resourceadded'] = 'リソースはイベントへ追加されました';
$labels['tabsummary'] = '要約'; $labels['tabsummary'] = '要約';
$labels['tabrecurrence'] = '繰返し'; $labels['tabrecurrence'] = '繰返し';
$labels['tabattendees'] = '参加者'; $labels['tabattendees'] = '参加者';
@ -158,6 +205,7 @@ $labels['tabsharing'] = '共有';
$labels['deleteobjectconfirm'] = '本当にこのイベントを削除しますか?'; $labels['deleteobjectconfirm'] = '本当にこのイベントを削除しますか?';
$labels['deleteventconfirm'] = '本当にこのイベントを削除しますか?'; $labels['deleteventconfirm'] = '本当にこのイベントを削除しますか?';
$labels['deletecalendarconfirm'] = '本当にこのカレンダーを全イベントとともに削除しますか?'; $labels['deletecalendarconfirm'] = '本当にこのカレンダーを全イベントとともに削除しますか?';
$labels['deletecalendarconfirmrecursive'] = 'このカレンダーとカレンダー内のすべてのイベントやサブカレンダーを削除しますか?';
$labels['savingdata'] = 'データを保存中…'; $labels['savingdata'] = 'データを保存中…';
$labels['errorsaving'] = '変更が保存できませんでした。'; $labels['errorsaving'] = '変更が保存できませんでした。';
$labels['operationfailed'] = '要求された操作ができませんでした。'; $labels['operationfailed'] = '要求された操作ができませんでした。';
@ -168,27 +216,52 @@ $labels['successremoval'] = 'イベントを削除しました';
$labels['successrestore'] = 'イベントを復旧しました'; $labels['successrestore'] = 'イベントを復旧しました';
$labels['errornotifying'] = 'イベント参加者への通知が送信できませんでした'; $labels['errornotifying'] = 'イベント参加者への通知が送信できませんでした';
$labels['errorimportingevent'] = 'イベントのインポートができませんでした'; $labels['errorimportingevent'] = 'イベントのインポートができませんでした';
$labels['importwarningexists'] = 'このイベントのコピーはすでにカレンダーにあります。';
$labels['newerversionexists'] = 'このイベントのより新しいヴァージョンがすでにあります。中断されました。'; $labels['newerversionexists'] = 'このイベントのより新しいヴァージョンがすでにあります。中断されました。';
$labels['nowritecalendarfound'] = 'イベントを保存するカレンダーが見つかりません'; $labels['nowritecalendarfound'] = 'イベントを保存するカレンダーが見つかりません';
$labels['importedsuccessfully'] = '\'$calendar\' へイベントを追加しました'; $labels['importedsuccessfully'] = '\'$calendar\' へイベントを追加しました';
$labels['updatedsuccessfully'] = '\'$calendar\' 内のイベントの更新に成功しました';
$labels['attendeupdateesuccess'] = '出席者状況を更新しました'; $labels['attendeupdateesuccess'] = '出席者状況を更新しました';
$labels['itipsendsuccess'] = '出席者へ招待を送信しました。'; $labels['itipsendsuccess'] = '出席者へ招待を送信しました。';
$labels['itipresponseerror'] = 'この招待の返信できませんでした'; $labels['itipresponseerror'] = 'この招待の返信できませんでした';
$labels['itipinvalidrequest'] = 'この招待は間もなく無効になります'; $labels['itipinvalidrequest'] = 'この招待は間もなく無効になります';
$labels['sentresponseto'] = '$mailto への招待の返信しました'; $labels['sentresponseto'] = '$mailto への招待の返信しました';
$labels['localchangeswarning'] = 'あなたのカレンダーにのみ反映されイベントのまとめ役に通知されない変更を行おうとしています';
$labels['importsuccess'] = '$nr イベントをインポートしました'; $labels['importsuccess'] = '$nr イベントをインポートしました';
$labels['importnone'] = 'インポートされたイベントはありません'; $labels['importnone'] = 'インポートされたイベントはありません';
$labels['importerror'] = 'インポート中にエラーが発生しました。'; $labels['importerror'] = 'インポート中にエラーが発生しました。';
$labels['aclnorights'] = 'このカレンダーの管理権限がありません。'; $labels['aclnorights'] = 'このカレンダーの管理権限がありません。';
$labels['changeeventconfirm'] = 'イベント変更'; $labels['changeeventconfirm'] = 'イベント変更';
$labels['removeeventconfirm'] = 'イベントを削除';
$labels['changerecurringeventwarning'] = 'これは繰返しイベントです。現在のイベントのみ、このイベントと今後の全イベント、全イベント、編集したい、もしくは新しいイベントとして保存したい?'; $labels['changerecurringeventwarning'] = 'これは繰返しイベントです。現在のイベントのみ、このイベントと今後の全イベント、全イベント、編集したい、もしくは新しいイベントとして保存したい?';
$labels['removerecurringeventwarning'] = 'これは繰返しのイベントです。現在のイベントのみ削除しますか? これと将来のすべてのイベントを削除しますか? または全てを削除しますか?';
$labels['removerecurringallonly'] = 'これは繰返しイベントです。参加者は全ての出来事に対して参加のみを取り消せます。';
$labels['currentevent'] = '現在'; $labels['currentevent'] = '現在';
$labels['futurevents'] = '今後'; $labels['futurevents'] = '今後';
$labels['allevents'] = '全て'; $labels['allevents'] = '全て';
$labels['saveasnew'] = '新規保存'; $labels['saveasnew'] = '新規保存';
$labels['birthdays'] = '誕生日';
$labels['birthdayscalendar'] = '誕生日カレンダー';
$labels['displaybirthdayscalendar'] = '誕生日をカレンダーに表示';
$labels['birthdayscalendarsources'] = 'これらのアドレス帳から';
$labels['birthdayeventtitle'] = '$name さんの誕生日';
$labels['birthdayage'] = '年齢 $age';
$labels['objectchangelog'] = '変更履歴'; $labels['objectchangelog'] = '変更履歴';
$labels['objectdiff'] = '$rev1 から $rev2 への変更'; $labels['objectdiff'] = '$rev1 から $rev2 への変更';
$labels['objectnotfound'] = 'イベントデータのロードに失敗しました';
$labels['objectchangelognotavailable'] = 'このイベントの更新履歴は利用できません';
$labels['objectdiffnotavailable'] = '選択されたリビジョンでは比較できません'; $labels['objectdiffnotavailable'] = '選択されたリビジョンでは比較できません';
$labels['revisionrestoreconfirm'] = '本当にリビジョン $rev のイベントを復旧しますか? 現在のイベントは古いバージョンのイベントに置換えられます。';
$labels['objectrestoresuccess'] = 'リビジョン $rev は復旧されました'; $labels['objectrestoresuccess'] = 'リビジョン $rev は復旧されました';
$labels['objectrestoreerror'] = '古いバージョンの復旧に失敗しました'; $labels['objectrestoreerror'] = '古いバージョンの復旧に失敗しました';
$labels['arialabelminical'] = 'カレンダー日付選択';
$labels['arialabelcalendarview'] = 'カレンダー表示';
$labels['arialabelsearchform'] = 'イベント検索元';
$labels['arialabelquicksearchbox'] = 'イベント検索入力';
$labels['arialabelcalsearchform'] = 'カレンダー検索元';
$labels['calendaractions'] = '行事カレンダー';
$labels['arialabeleventattendees'] = 'イベント参加者リスト';
$labels['arialabeleventresources'] = 'イベントリソースリスト';
$labels['arialabelresourcesearchform'] = 'リソース検索元';
$labels['arialabelresourceselection'] = '利用可能なリソース';
?> ?>

View file

@ -152,7 +152,6 @@ $labels['invitationattendlinks'] = "In het geval dat uw email programma geen iTi
$labels['eventupdatesubject'] = '"$title" is gewijzigd'; $labels['eventupdatesubject'] = '"$title" is gewijzigd';
$labels['eventupdatesubjectempty'] = 'Een afspraak is gewijzigd'; $labels['eventupdatesubjectempty'] = 'Een afspraak is gewijzigd';
$labels['eventupdatemailbody'] = "*\$title*\n\nWanneer: \$date\n\nGenodigden: \$attendees\n\nBijgevoegd vindt u een iCalendar bestand met de gewijzigde details omtrent de afspraak die u kunt importeren in uw kalender programma."; $labels['eventupdatemailbody'] = "*\$title*\n\nWanneer: \$date\n\nGenodigden: \$attendees\n\nBijgevoegd vindt u een iCalendar bestand met de gewijzigde details omtrent de afspraak die u kunt importeren in uw kalender programma.";
$labels['eventcancelsubject'] = '"$title" is geannuleerd';
$labels['eventcancelmailbody'] = "*\$title*\n\nWanneer: \$date\n\nGenodigden: \$attendees\n\nDeze afspraak is geannuleerd door \$organizer.\n\nBijgevoegd vindt u een iCalendar bestand met de gewijzigde details omtrent de afspraak"; $labels['eventcancelmailbody'] = "*\$title*\n\nWanneer: \$date\n\nGenodigden: \$attendees\n\nDeze afspraak is geannuleerd door \$organizer.\n\nBijgevoegd vindt u een iCalendar bestand met de gewijzigde details omtrent de afspraak";
$labels['itipobjectnotfound'] = 'De afspraak waaraan door dit bericht wordt gereferreerd is niet gevonden in uw kalender.'; $labels['itipobjectnotfound'] = 'De afspraak waaraan door dit bericht wordt gereferreerd is niet gevonden in uw kalender.';
$labels['itipmailbodyaccepted'] = "\$sender heeft de uitnodiging geaccepteerd voor de volgende afspraak:\n\n*\$title*\n\nWanneer: \$date\n\nGenodigden: \$attendees"; $labels['itipmailbodyaccepted'] = "\$sender heeft de uitnodiging geaccepteerd voor de volgende afspraak:\n\n*\$title*\n\nWanneer: \$date\n\nGenodigden: \$attendees";

View file

@ -176,6 +176,8 @@ $labels['itipmailbodyaccepted'] = "\$sender zaakceptował zaproszenie do następ
$labels['itipmailbodytentative'] = "\$sender warunkowo zaakceptował zaproszenie do następującego zdarzenia:\n\n*\$title*\n\nKiedy: \$date\n\nZaproszeni: \$attendees"; $labels['itipmailbodytentative'] = "\$sender warunkowo zaakceptował zaproszenie do następującego zdarzenia:\n\n*\$title*\n\nKiedy: \$date\n\nZaproszeni: \$attendees";
$labels['itipmailbodydeclined'] = "\$sender odrzucił zaproszenie na następujące zdarzenie:\n\n*\$title*\n\nKiedy: \$date\n\nZaproszeni: \$attendees"; $labels['itipmailbodydeclined'] = "\$sender odrzucił zaproszenie na następujące zdarzenie:\n\n*\$title*\n\nKiedy: \$date\n\nZaproszeni: \$attendees";
$labels['itipmailbodycancel'] = "\$sender odrzucił twój udział w zastępującym zdarzeniu:\n\n*\$title*\n\nKiedy: \$date"; $labels['itipmailbodycancel'] = "\$sender odrzucił twój udział w zastępującym zdarzeniu:\n\n*\$title*\n\nKiedy: \$date";
$labels['itipmailbodydelegated'] = "\$sender oddelegował udział w następującym wydarzeniu:\n\n*\$title*\n\nKiedy: \$date";
$labels['itipmailbodydelegatedto'] = "\$sender oddelegował do ciebie udział w następującym wydarzeniu:\n\n*\$title*\n\nKiedy: \$date";
$labels['itipdeclineevent'] = 'Czy chcesz odrzucić zaproszenie na to zdarzenie?'; $labels['itipdeclineevent'] = 'Czy chcesz odrzucić zaproszenie na to zdarzenie?';
$labels['declinedeleteconfirm'] = 'Czy chcesz także usunąć to odrzucone zdarzenie ze swojego kalendarza?'; $labels['declinedeleteconfirm'] = 'Czy chcesz także usunąć to odrzucone zdarzenie ze swojego kalendarza?';
$labels['itipcomment'] = 'Komentarz zaproszenia/powiadomienia'; $labels['itipcomment'] = 'Komentarz zaproszenia/powiadomienia';
@ -246,10 +248,13 @@ $labels['birthdayscalendarsources'] = 'Z tych książek adresowych';
$labels['birthdayeventtitle'] = 'Urodziny $name\'s'; $labels['birthdayeventtitle'] = 'Urodziny $name\'s';
$labels['birthdayage'] = 'Wiek $age'; $labels['birthdayage'] = 'Wiek $age';
$labels['objectchangelog'] = 'Historia zmian'; $labels['objectchangelog'] = 'Historia zmian';
$labels['objectdiff'] = 'Zmiany od $rev1 do $rev2';
$labels['objectnotfound'] = 'Nie udało się wczytać zdarzenia'; $labels['objectnotfound'] = 'Nie udało się wczytać zdarzenia';
$labels['objectchangelognotavailable'] = 'Historia zmian jest niedostępna dla tego zdarzenia'; $labels['objectchangelognotavailable'] = 'Historia zmian jest niedostępna dla tego zdarzenia';
$labels['objectdiffnotavailable'] = 'Nie można porównać wybranych wersji'; $labels['objectdiffnotavailable'] = 'Nie można porównać wybranych wersji';
$labels['revisionrestoreconfirm'] = 'Czy na pewno chcesz przywrócić wersję $rev tego zdarzenia? Bierzące zdarzenie zostanie zastąpione starszą wersją.'; $labels['revisionrestoreconfirm'] = 'Czy na pewno chcesz przywrócić wersję $rev tego zdarzenia? Bierzące zdarzenie zostanie zastąpione starszą wersją.';
$labels['objectrestoresuccess'] = 'Wersja $rev została pomyślnie przywrócona';
$labels['objectrestoreerror'] = 'Nie udało się przywrócić starej wersji';
$labels['arialabelminical'] = 'Wybór daty kalendarza'; $labels['arialabelminical'] = 'Wybór daty kalendarza';
$labels['arialabelcalendarview'] = 'Podgląd kalendarza'; $labels['arialabelcalendarview'] = 'Podgląd kalendarza';
$labels['arialabelsearchform'] = 'Formularz wyszukiwania zdarzeń'; $labels['arialabelsearchform'] = 'Formularz wyszukiwania zdarzeń';

View file

@ -141,7 +141,6 @@ $labels['invitationattendlinks'] = "No caso do seu cliente de e-mail não suport
$labels['eventupdatesubject'] = '"$title" foi atualizado'; $labels['eventupdatesubject'] = '"$title" foi atualizado';
$labels['eventupdatesubjectempty'] = 'Um evento do seu interesse foi atualizado'; $labels['eventupdatesubjectempty'] = 'Um evento do seu interesse foi atualizado';
$labels['eventupdatemailbody'] = "*\$title*\n\nQuando: \$date\n\nConvidados: \$attendees\n\nSegue em anexo um arquivo iCalendar com os detalhes atualizados do evento na qual você pode importar para sua aplicação de calendário."; $labels['eventupdatemailbody'] = "*\$title*\n\nQuando: \$date\n\nConvidados: \$attendees\n\nSegue em anexo um arquivo iCalendar com os detalhes atualizados do evento na qual você pode importar para sua aplicação de calendário.";
$labels['eventcancelsubject'] = '"$title" foi cancelado';
$labels['eventcancelmailbody'] = "*\$title*\n\nQuando: \$date\n\nConvidados: \$attendees\n\nO evento foi cancelado por \$organizer.\n\nSegue em anexo um arquivo iCalendar com os detalhes atualizados do evento."; $labels['eventcancelmailbody'] = "*\$title*\n\nQuando: \$date\n\nConvidados: \$attendees\n\nO evento foi cancelado por \$organizer.\n\nSegue em anexo um arquivo iCalendar com os detalhes atualizados do evento.";
$labels['itipobjectnotfound'] = 'O evento referenciado por esta mensagem não foi encontrado em seu calendário.'; $labels['itipobjectnotfound'] = 'O evento referenciado por esta mensagem não foi encontrado em seu calendário.';
$labels['itipmailbodyaccepted'] = "\$sender aceitou o convite para o seguinte evento:\n\n*\$title*\n\nQuando: \$date\n\nConvidados: \$attendees"; $labels['itipmailbodyaccepted'] = "\$sender aceitou o convite para o seguinte evento:\n\n*\$title*\n\nQuando: \$date\n\nConvidados: \$attendees";

View file

@ -6,7 +6,7 @@
* *
* For translation see https://www.transifex.com/projects/p/kolab/resource/calendar/ * For translation see https://www.transifex.com/projects/p/kolab/resource/calendar/
*/ */
$labels['default_view'] = 'Visualização padrão'; $labels['default_view'] = 'Visualização predefinida';
$labels['time_format'] = 'Formato da hora'; $labels['time_format'] = 'Formato da hora';
$labels['timeslots'] = 'Entradas por hora'; $labels['timeslots'] = 'Entradas por hora';
$labels['first_day'] = 'Primeiro dia da semana'; $labels['first_day'] = 'Primeiro dia da semana';
@ -130,8 +130,8 @@ $labels['weekofyear'] = 'Semana';
$labels['pastevents'] = 'Passado'; $labels['pastevents'] = 'Passado';
$labels['futureevents'] = 'Futuro'; $labels['futureevents'] = 'Futuro';
$labels['showalarms'] = 'Mostrar lembretes'; $labels['showalarms'] = 'Mostrar lembretes';
$labels['defaultalarmtype'] = 'Configuração padrão de lembrete'; $labels['defaultalarmtype'] = 'Notificação predefinida dos lembretes';
$labels['defaultalarmoffset'] = 'Horário padrão de lembrete'; $labels['defaultalarmoffset'] = 'Agendamento predefinido das notificações';
$labels['attendee'] = 'Participante'; $labels['attendee'] = 'Participante';
$labels['role'] = 'Papel'; $labels['role'] = 'Papel';
$labels['availability'] = 'Disp.'; $labels['availability'] = 'Disp.';
@ -169,7 +169,7 @@ $labels['invitationattendlinks'] = "No caso do seu cliente de e-mail não suport
$labels['eventupdatesubject'] = '"$title" foi atualizado.'; $labels['eventupdatesubject'] = '"$title" foi atualizado.';
$labels['eventupdatesubjectempty'] = 'Um evento do seu interesse foi atualizado'; $labels['eventupdatesubjectempty'] = 'Um evento do seu interesse foi atualizado';
$labels['eventupdatemailbody'] = "*\$title*\n\nQuando: \$date\n\nConvidados: \$attendees\n\nSegue em anexo um arquivo iCalendar atualizado com os detalhes de um evento, o qual pode importar para o seu calendário."; $labels['eventupdatemailbody'] = "*\$title*\n\nQuando: \$date\n\nConvidados: \$attendees\n\nSegue em anexo um arquivo iCalendar atualizado com os detalhes de um evento, o qual pode importar para o seu calendário.";
$labels['eventcancelsubject'] = '"$title" foi cancelado.'; $labels['eventcancelsubject'] = '"$title" foi cancelado';
$labels['eventcancelmailbody'] = "*\$title*\n\nQuando: \$date\n\nConvidados: \$attendees\n\nO evento foi cancelado por \$organizer.\n\nSegue em anexo um arquivo iCalendar com os detalhes atualizados do evento."; $labels['eventcancelmailbody'] = "*\$title*\n\nQuando: \$date\n\nConvidados: \$attendees\n\nO evento foi cancelado por \$organizer.\n\nSegue em anexo um arquivo iCalendar com os detalhes atualizados do evento.";
$labels['itipobjectnotfound'] = 'O evento citado nesta mensagem não foi encontrado no seu calendário.'; $labels['itipobjectnotfound'] = 'O evento citado nesta mensagem não foi encontrado no seu calendário.';
$labels['itipmailbodyaccepted'] = "\$sender aceitou o convite para o seguinte evento:\n\n*\$title*\n\nQuando: \$date\n\nConvidados: \$attendees"; $labels['itipmailbodyaccepted'] = "\$sender aceitou o convite para o seguinte evento:\n\n*\$title*\n\nQuando: \$date\n\nConvidados: \$attendees";
@ -244,23 +244,17 @@ $labels['saveasnew'] = 'Guardar como';
$labels['birthdays'] = 'Aniversários'; $labels['birthdays'] = 'Aniversários';
$labels['birthdayscalendar'] = 'Calendário de aniversários'; $labels['birthdayscalendar'] = 'Calendário de aniversários';
$labels['displaybirthdayscalendar'] = 'Mostrar calendário de aniversários'; $labels['displaybirthdayscalendar'] = 'Mostrar calendário de aniversários';
$labels['birthdayscalendarsources'] = 'From these address books'; $labels['birthdayscalendarsources'] = 'Agendas a incluir:';
$labels['birthdayeventtitle'] = 'Aniversário de $name'; $labels['birthdayeventtitle'] = 'Aniversário de $name';
$labels['birthdayage'] = 'Idade $age'; $labels['birthdayage'] = 'Idade $age';
$labels['objectchangelog'] = 'Alterar histórico'; $labels['objectchangelog'] = 'Alterar histórico';
$labels['revision'] = 'Revisão'; $labels['objectdiff'] = 'Diferenças entre $rev1 e $rev2';
$labels['user'] = 'Utilizador';
$labels['operation'] = 'Ação';
$labels['actionappend'] = 'Guardado';
$labels['actionmove'] = 'Movido';
$labels['actiondelete'] = 'Eliminado';
$labels['compare'] = 'Comparar';
$labels['showrevision'] = 'Mostrar esta versão';
$labels['restore'] = 'Restaurar esta versão';
$labels['objectnotfound'] = 'Falha ao ler os dados do evento'; $labels['objectnotfound'] = 'Falha ao ler os dados do evento';
$labels['objectchangelognotavailable'] = 'Não é possível alterar o histórico deste evento'; $labels['objectchangelognotavailable'] = 'Não é possível alterar o histórico deste evento';
$labels['objectdiffnotavailable'] = 'Não é possível comparar as revisões selecionadas'; $labels['objectdiffnotavailable'] = 'Não é possível comparar as versões selecionadas';
$labels['revisionrestoreconfirm'] = 'Confirma o restauro da revisão $rev deste evento? Os dados atuais serão substituídos pelos da versão anterior.'; $labels['revisionrestoreconfirm'] = 'Confirma a reposição das alterações $rev deste evento? Os dados atuais serão substituídos pela versão anterior.';
$labels['objectrestoresuccess'] = 'A versão $rev foi reposta com sucesso.';
$labels['objectrestoreerror'] = 'Não foi possível repor a versão anterior.';
$labels['arialabelminical'] = 'Seleção da data do calendário'; $labels['arialabelminical'] = 'Seleção da data do calendário';
$labels['arialabelcalendarview'] = 'Vista do calendário'; $labels['arialabelcalendarview'] = 'Vista do calendário';
$labels['arialabelsearchform'] = 'Quadro de pesquisa de eventos'; $labels['arialabelsearchform'] = 'Quadro de pesquisa de eventos';

View file

@ -169,7 +169,6 @@ $labels['invitationattendlinks'] = "V kolikor vaš email klient ne podpira iTip
$labels['eventupdatesubject'] = '"$title" je bil posodobljen'; $labels['eventupdatesubject'] = '"$title" je bil posodobljen';
$labels['eventupdatesubjectempty'] = 'Dogodek, ki vas zadeva je bil posodobljen'; $labels['eventupdatesubjectempty'] = 'Dogodek, ki vas zadeva je bil posodobljen';
$labels['eventupdatemailbody'] = "*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees\n\nProsim preglejte pripeto iCalendar datoteko s posodobljenimi informacijami o dogodku. Datoteko lahko uvozite v vašo koledar aplikacijo."; $labels['eventupdatemailbody'] = "*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees\n\nProsim preglejte pripeto iCalendar datoteko s posodobljenimi informacijami o dogodku. Datoteko lahko uvozite v vašo koledar aplikacijo.";
$labels['eventcancelsubject'] = '"$title" je bil preklican';
$labels['eventcancelmailbody'] = "*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees\n\nDogodek je bil preklican s strani \$organizer.\n\nProsim preglejte pripeto iCalendar datoteko s posodobljenimi informacijami o dogodku."; $labels['eventcancelmailbody'] = "*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees\n\nDogodek je bil preklican s strani \$organizer.\n\nProsim preglejte pripeto iCalendar datoteko s posodobljenimi informacijami o dogodku.";
$labels['itipobjectnotfound'] = 'Dogodek, na katerega se nanaša to sporočilo, ni bil najden v vašem koledarju.'; $labels['itipobjectnotfound'] = 'Dogodek, na katerega se nanaša to sporočilo, ni bil najden v vašem koledarju.';
$labels['itipmailbodyaccepted'] = "\$sender je sprejel vabilo na dogodek:\n\n*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees"; $labels['itipmailbodyaccepted'] = "\$sender je sprejel vabilo na dogodek:\n\n*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees";

View file

@ -169,7 +169,6 @@ $labels['invitationattendlinks'] = "Om din e-postklient inte stöder iTip-förfr
$labels['eventupdatesubject'] = '"$title" har uppdaterats'; $labels['eventupdatesubject'] = '"$title" har uppdaterats';
$labels['eventupdatesubjectempty'] = 'En händelse som berör dig har uppdaterats'; $labels['eventupdatesubjectempty'] = 'En händelse som berör dig har uppdaterats';
$labels['eventupdatemailbody'] = "*\$title*\n\nNär: \$date\n\nInbjudna: \$attendees\n\nHärmed bifogas en iCalendar-fil med uppdaterad information som du kan importera till din kalenderapplikation."; $labels['eventupdatemailbody'] = "*\$title*\n\nNär: \$date\n\nInbjudna: \$attendees\n\nHärmed bifogas en iCalendar-fil med uppdaterad information som du kan importera till din kalenderapplikation.";
$labels['eventcancelsubject'] = '"$title" har ställts in';
$labels['eventcancelmailbody'] = "*\$title*\n\nNär: \$date\n\nInbjudna: \$attendees\n\nHändelsen har ställts in av \$organizer.\n\nHärmed bifogas en iCalendar-fil med uppdaterad information om händelsen."; $labels['eventcancelmailbody'] = "*\$title*\n\nNär: \$date\n\nInbjudna: \$attendees\n\nHändelsen har ställts in av \$organizer.\n\nHärmed bifogas en iCalendar-fil med uppdaterad information om händelsen.";
$labels['itipobjectnotfound'] = 'Den händelse som avses i detta meddelande hittades inte i din kalender.'; $labels['itipobjectnotfound'] = 'Den händelse som avses i detta meddelande hittades inte i din kalender.';
$labels['itipmailbodyaccepted'] = "\$sender har accepterat inbjudan till följande händelse:\n\n*\$title*\n\nNär: \$date\n\nInbjudna: \$attendees"; $labels['itipmailbodyaccepted'] = "\$sender har accepterat inbjudan till följande händelse:\n\n*\$title*\n\nNär: \$date\n\nInbjudna: \$attendees";

View file

@ -155,7 +155,6 @@ $labels['invitationsubject'] = 'คุณได้รับเชิญไปย
$labels['invitationattendlinks'] = "ในกรณีที่โปรแกรมอีเมล์ของคุณไม่รองรับ 'การร้องขอ iTip' คุณสามารถใช้ลิงค์ต่อไปนี้ในการตอบรับหรือปฎิเสธจดหมายเชิญฉบับนี้ :\n\$url"; $labels['invitationattendlinks'] = "ในกรณีที่โปรแกรมอีเมล์ของคุณไม่รองรับ 'การร้องขอ iTip' คุณสามารถใช้ลิงค์ต่อไปนี้ในการตอบรับหรือปฎิเสธจดหมายเชิญฉบับนี้ :\n\$url";
$labels['eventupdatesubject'] = '"$title" ได้รับการปรับปรุงสถานะ'; $labels['eventupdatesubject'] = '"$title" ได้รับการปรับปรุงสถานะ';
$labels['eventupdatesubjectempty'] = 'เหตุการณ์ที่คุณเป็นห่วงได้ถูกปรับปรุงสถานะแล้ว'; $labels['eventupdatesubjectempty'] = 'เหตุการณ์ที่คุณเป็นห่วงได้ถูกปรับปรุงสถานะแล้ว';
$labels['eventcancelsubject'] = '"$title" ถูกยกเลิก';
$labels['eventcancelmailbody'] = "*\$title*\n\nเมื่อ: \$date\n\nInvitees: \$attendees\n\n เหตุการณ์ได้ถูกยกเลิกโดย \$organizer.\n\n ไฟล์ iCalendar ที่แนบมาด้วย ได้รับการปรับปรุงรายละเอียดเรียบร้อยแล้ว"; $labels['eventcancelmailbody'] = "*\$title*\n\nเมื่อ: \$date\n\nInvitees: \$attendees\n\n เหตุการณ์ได้ถูกยกเลิกโดย \$organizer.\n\n ไฟล์ iCalendar ที่แนบมาด้วย ได้รับการปรับปรุงรายละเอียดเรียบร้อยแล้ว";
$labels['itipobjectnotfound'] = 'เหตุการณ์ที่อ้างถึงโดยข้อความนี้ไม่ถูกตรวจพบในปฎิทินของคุณ'; $labels['itipobjectnotfound'] = 'เหตุการณ์ที่อ้างถึงโดยข้อความนี้ไม่ถูกตรวจพบในปฎิทินของคุณ';
$labels['itipmailbodyaccepted'] = "\$sender ได้ตอบรับคำเชิญสำหรับเหตุการณ์ต่อไปนี้:\n\n*\$title*\n\nเมื่อ: \$date\n\nInvitees: \$attendees"; $labels['itipmailbodyaccepted'] = "\$sender ได้ตอบรับคำเชิญสำหรับเหตุการณ์ต่อไปนี้:\n\n*\$title*\n\nเมื่อ: \$date\n\nInvitees: \$attendees";

View file

@ -164,7 +164,6 @@ $labels['invitationattendlinks'] = "У разі, якщо Ваш поштови
$labels['eventupdatesubject'] = '"$title" була оновлена'; $labels['eventupdatesubject'] = '"$title" була оновлена';
$labels['eventupdatesubjectempty'] = 'Подія, яка стосується Вас, була оновленна '; $labels['eventupdatesubjectempty'] = 'Подія, яка стосується Вас, була оновленна ';
$labels['eventupdatemailbody'] = "*\$title*\n\nКоли: \$date\n\nЗапрошені: \$attendees\n\nУ вкладенні Ви знайдете файл iCalendar з усіма змінами у події, який Ви можете імпортувати у Вашу програму-щоденник."; $labels['eventupdatemailbody'] = "*\$title*\n\nКоли: \$date\n\nЗапрошені: \$attendees\n\nУ вкладенні Ви знайдете файл iCalendar з усіма змінами у події, який Ви можете імпортувати у Вашу програму-щоденник.";
$labels['eventcancelsubject'] = '"$title" була відмінена';
$labels['eventcancelmailbody'] = "*\$title*\n\nКоли: \$date\n\nЗапрошені: \$attendees\n\nЦя подія скасована \$organizer.\n\nУ вкладенні Ви знайдете файл iCalendar з усіма змінами у події."; $labels['eventcancelmailbody'] = "*\$title*\n\nКоли: \$date\n\nЗапрошені: \$attendees\n\nЦя подія скасована \$organizer.\n\nУ вкладенні Ви знайдете файл iCalendar з усіма змінами у події.";
$labels['itipobjectnotfound'] = 'Згадану в даному повідомленні подію, не знайдено у Вашому календарі.'; $labels['itipobjectnotfound'] = 'Згадану в даному повідомленні подію, не знайдено у Вашому календарі.';
$labels['itipdeclineevent'] = 'Ви хочете відхилити запрошення на дану подію?'; $labels['itipdeclineevent'] = 'Ви хочете відхилити запрошення на дану подію?';

View file

@ -8,9 +8,15 @@
*/ */
$labels['default_view'] = '默认视图'; $labels['default_view'] = '默认视图';
$labels['time_format'] = '时间格式'; $labels['time_format'] = '时间格式';
$labels['timeslots'] = '每小时分成几段';
$labels['first_day'] = '周几排列在前';
$labels['first_hour'] = '几点最前显示';
$labels['workinghours'] = '工作时间'; $labels['workinghours'] = '工作时间';
$labels['add_category'] = '增加分类'; $labels['add_category'] = '增加分类';
$labels['remove_category'] = '移除分类'; $labels['remove_category'] = '移除分类';
$labels['defaultcalendar'] = '创建事件于';
$labels['eventcoloring'] = '事件标色';
$labels['coloringmode0'] = '根据日历';
$labels['aftertrash'] = '移到回收站'; $labels['aftertrash'] = '移到回收站';
$labels['afterdelete'] = '删除此信息'; $labels['afterdelete'] = '删除此信息';
$labels['afterflagdeleted'] = '被删除标签'; $labels['afterflagdeleted'] = '被删除标签';
@ -36,6 +42,8 @@ $labels['title'] = '汇总';
$labels['description'] = '描述'; $labels['description'] = '描述';
$labels['export'] = '导出'; $labels['export'] = '导出';
$labels['exporttitle'] = '导出到ICalendar'; $labels['exporttitle'] = '导出到ICalendar';
$labels['location'] = '地点';
$labels['url'] = '网址';
$labels['date'] = '日期'; $labels['date'] = '日期';
$labels['start'] = '开始'; $labels['start'] = '开始';
$labels['starttime'] = '开始时间'; $labels['starttime'] = '开始时间';
@ -63,14 +71,49 @@ $labels['created'] = '已创建';
$labels['changed'] = '最后更改'; $labels['changed'] = '最后更改';
$labels['unknown'] = '未知'; $labels['unknown'] = '未知';
$labels['eventoptions'] = '选项'; $labels['eventoptions'] = '选项';
$labels['eventhistory'] = '历史';
$labels['searchearlierdates'] = '<< 查找以前的事件';
$labels['searchlaterdates'] = '查找以后的时间 >>';
$labels['invitationspending'] = '未决邀请';
$labels['invitationsdeclined'] = '已拒绝邀请';
$labels['rsvpcomment'] = '邀请文字';
$labels['until'] = '直到';
$labels['today'] = '今天';
$labels['tomorrow'] = '明天';
$labels['thisweek'] = '本周';
$labels['nextweek'] = '下周';
$labels['prevweek'] = '上周';
$labels['thismonth'] = '本月';
$labels['nextmonth'] = '下月';
$labels['weekofyear'] = '周'; $labels['weekofyear'] = '周';
$labels['pastevents'] = '过去';
$labels['futureevents'] = '未来';
$labels['showalarms'] = '显示提醒';
$labels['defaultalarmtype'] = '默认提醒设置';
$labels['defaultalarmoffset'] = '默认提醒时间';
$labels['attendee'] = '参与者';
$labels['role'] = '身份';
$labels['availability'] = '有空';
$labels['confirmstate'] = '状态'; $labels['confirmstate'] = '状态';
$labels['addattendee'] = '添加参与者';
$labels['roleorganizer'] = '组织者';
$labels['rolerequired'] = '必须';
$labels['roleoptional'] = '可选';
$labels['rolenonparticipant'] = '缺席';
$labels['cutypeindividual'] = '个人';
$labels['cutypegroup'] = '组织';
$labels['cutyperesource'] = '资源';
$labels['availfree'] = '空闲'; $labels['availfree'] = '空闲';
$labels['availbusy'] = '忙碌'; $labels['availbusy'] = '忙碌';
$labels['availunknown'] = '未知'; $labels['availunknown'] = '未知';
$labels['availtentative'] = '临时'; $labels['availtentative'] = '临时';
$labels['availoutofoffice'] = '外出'; $labels['availoutofoffice'] = '外出';
$labels['scheduletime'] = '查找是否有空';
$labels['sendinvitations'] = '发送邀请';
$labels['sendnotifications'] = '通知参加者修改事项';
$labels['resource'] = '资源';
$labels['tabsummary'] = '汇总'; $labels['tabsummary'] = '汇总';
$labels['tabsharing'] = '分享'; $labels['tabsharing'] = '分享';
$labels['savingdata'] = '保存数据...'; $labels['savingdata'] = '保存数据...';
$labels['futurevents'] = '未来';
?> ?>

View file

@ -373,6 +373,7 @@ pre {
right: 4px; right: 4px;
} }
#eventedit.uidialog,
.calendarmain div.uidialog { .calendarmain div.uidialog {
display: none; display: none;
} }
@ -547,24 +548,56 @@ a.miniColors-trigger {
margin: 0 -0.2em; margin: 0 -0.2em;
} }
#eventshow.status-cancelled { #event-status-badge {
background: url(images/badge_cancelled.png) top right no-repeat; width: 100px;
height: 100px;
position: absolute;
top: 0;
right: 0;
overflow: hidden;
} }
#eventshow.sensitivity-private { #event-status-badge span {
background: url(images/badge_private.png) top right no-repeat; display: none;
text-transform: uppercase;
width: 150px;
height: 20px;
line-height: 20px;
position: absolute;
left: -20px;
top: 35px;
padding-left: 10px;
text-align: center;
font-weight: bold;
font-size: 12px;
color: #fff;
box-shadow: 1px 1px 2px #ccc, -1px -1px 2px #ccc;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
-o-transform: rotate(45deg);
transform: rotate(45deg);
} }
#eventshow.sensitivity-confidential { #eventshow.status-cancelled #event-status-badge span {
background: url(images/badge_confidential.png) top right no-repeat; background: url(images/badge.png) 26px -24px no-repeat #cc0000;
display: block;
} }
.sensitivity-private #event-title { #eventshow.sensitivity-private #event-status-badge span {
margin-right: 50px; background: url(images/badge.png) 40px -52px no-repeat #0066ff;
display: block;
} }
.sensitivity-confidential #event-title { #eventshow.sensitivity-confidential #event-status-badge span {
margin-right: 60px; background: url(images/badge.png) 20px 2px no-repeat #cc0000;
display: block;
}
#eventshow.status-cancelled #event-title,
#eventshow.sensitivity-private #event-title,
#eventshow.sensitivity-confidential #event-title {
margin-right: 80px;
} }
#eventshow div.event-line { #eventshow div.event-line {

View file

@ -44,14 +44,6 @@ html #calendartoolbar a.buttonPas {
width: 102%; width: 102%;
} }
#eventshow.sensitivity-private {
background-image: url(images/badge_private.gif);
}
#eventshow.sensitivity-confidential {
background-image: url(images/badge_confidential.gif);
}
.fc-day-content { .fc-day-content {
cursor: default; cursor: default;
} }

View file

@ -51,7 +51,7 @@
<li><roundcube:button command="calendar-edit" label="calendar.edit" classAct="active" /></li> <li><roundcube:button command="calendar-edit" label="calendar.edit" classAct="active" /></li>
<li><roundcube:button command="calendar-delete" label="delete" classAct="active" /></li> <li><roundcube:button command="calendar-delete" label="delete" classAct="active" /></li>
<roundcube:if condition="env:calendar_driver == 'kolab'" /> <roundcube:if condition="env:calendar_driver == 'kolab'" />
<li><roundcube:button command="calendar-remove" label="calendar.remove" classAct="active" /></li> <li><roundcube:button command="calendar-remove" label="calendar.removelist" classAct="active" /></li>
<roundcube:endif /> <roundcube:endif />
<li><roundcube:button command="calendar-showurl" label="calendar.showurl" classAct="active" /></li> <li><roundcube:button command="calendar-showurl" label="calendar.showurl" classAct="active" /></li>
<roundcube:if condition="env:calendar_driver == 'kolab'" /> <roundcube:if condition="env:calendar_driver == 'kolab'" />
@ -62,6 +62,7 @@
<div id="eventshow" class="uidialog"> <div id="eventshow" class="uidialog">
<h1 id="event-title">Event Title</h1> <h1 id="event-title">Event Title</h1>
<div id="event-status-badge"><span></span></div>
<div class="event-section" id="event-location">Location</div> <div class="event-section" id="event-location">Location</div>
<div class="event-section" id="event-date">From-To</div> <div class="event-section" id="event-date">From-To</div>
<div class="event-section" id="event-description"> <div class="event-section" id="event-description">
@ -122,6 +123,7 @@
<ul> <ul>
<li><roundcube:button command="event-download" label="download" classAct="active" /></li> <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-sendbymail" label="send" classAct="active" /></li>
<li><roundcube:button command="event-copy" label="copy" classAct="active" /></li>
</ul> </ul>
</div> </div>

View file

@ -115,6 +115,7 @@ body.calendarmain #mainscreen {
width: 6px; width: 6px;
top: 40px !important; top: 40px !important;
bottom: 0; bottom: 0;
height: auto;
background: url(images/toggle.gif) -1px 48% no-repeat transparent; background: url(images/toggle.gif) -1px 48% no-repeat transparent;
} }
@ -453,7 +454,8 @@ pre {
#calendars .searchresults .boxtitle { #calendars .searchresults .boxtitle {
background: none; background: none;
padding: 2px 8px 2px 8px; padding: 2px 8px;
border-radius: 0;
} }
#calendars .searchresults .listing li { #calendars .searchresults .listing li {
@ -538,6 +540,7 @@ body.calendarmain #searchmenulink {
width: 15px; width: 15px;
} }
#eventedit.uidialog,
.calendarmain div.uidialog { .calendarmain div.uidialog {
display: none; display: none;
} }
@ -640,7 +643,7 @@ a.miniColors-trigger {
border-top: 2px solid #fafafa; border-top: 2px solid #fafafa;
} }
#edit-attachments-form .formbuttons { #edit-attachments-form .buttons {
margin: 0.5em 0; margin: 0.5em 0;
} }
@ -681,7 +684,7 @@ a.miniColors-trigger {
} }
.event-attendees span.attendee { .event-attendees span.attendee {
padding-right: 18px; padding: 1px 18px 1px 0;
margin-right: 0.5em; margin-right: 0.5em;
background: url(images/attendee-status.png) right 0 no-repeat; background: url(images/attendee-status.png) right 0 no-repeat;
} }
@ -769,24 +772,56 @@ a.miniColors-trigger {
margin: 0 -0.2em; margin: 0 -0.2em;
} }
.calendarmain .eventdialog.status-cancelled { #event-status-badge {
background: url(images/badge_cancelled.png) top right no-repeat; width: 100px;
height: 100px;
position: absolute;
top: 0;
right: 0;
overflow: hidden;
} }
.calendarmain .eventdialog.sensitivity-private { #event-status-badge span {
background: url(images/badge_private.png) top right no-repeat; display: none;
text-transform: uppercase;
width: 150px;
height: 20px;
line-height: 20px;
position: absolute;
left: -20px;
top: 35px;
padding-left: 10px;
text-align: center;
font-weight: bold;
font-size: 12px;
color: #fff;
box-shadow: 1px 1px 2px #ccc, -1px -1px 2px #ccc;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
-o-transform: rotate(45deg);
transform: rotate(45deg);
} }
.calendarmain .eventdialog.sensitivity-confidential { .eventdialog.status-cancelled #event-status-badge span {
background: url(images/badge_confidential.png) top right no-repeat; background: url(images/badge.png) 26px -24px no-repeat #cc0000;
display: block;
} }
.calendarmain .sensitivity-private #event-title { .eventdialog.sensitivity-private #event-status-badge span {
margin-right: 50px; background: url(images/badge.png) 40px -52px no-repeat #0066ff;
display: block;
} }
.eventdialog.sensitivity-confidential #event-status-badge span {
background: url(images/badge.png) 20px 2px no-repeat #cc0000;
display: block;
}
.calendarmain .status-cancelled #event-title,
.calendarmain .sensitivity-private #event-title,
.calendarmain .sensitivity-confidential #event-title { .calendarmain .sensitivity-confidential #event-title {
margin-right: 60px; margin-right: 80px;
} }
.calendarmain .eventdialog div.event-line { .calendarmain .eventdialog div.event-line {
@ -803,11 +838,6 @@ a.miniColors-trigger {
margin-left: 2em; margin-left: 2em;
} }
.calendarmain .eventdialog #event-rsvp-comment,
.calendarmain .eventdialog #event-created-changed {
margin-top: 0.6em;
}
.eventdialog .event-text-old, .eventdialog .event-text-old,
.eventdialog .event-text-new, .eventdialog .event-text-new,
.eventdialog .event-text-diff { .eventdialog .event-text-diff {
@ -1110,7 +1140,7 @@ td.topalign {
#eventedit .edit-attendees-table th.invite, #eventedit .edit-attendees-table th.invite,
#eventedit .edit-attendees-table td.invite { #eventedit .edit-attendees-table td.invite {
width: 44px; width: 50px;
padding: 2px; padding: 2px;
} }
@ -1210,50 +1240,51 @@ td.topalign {
height: 14px; height: 14px;
border-radius: 4px; border-radius: 4px;
-moz-border-radius: 4px; -moz-border-radius: 4px;
vertical-align: middle;
} }
.availability img.availabilityicon.loading { .availability img.availabilityicon.loading {
background: url(images/loading_blue.gif) center no-repeat; background: url(images/loading_blue.gif) center no-repeat;
} }
#schedule-freebusy-times td.unknown, #schedule-freebusy-times td div.unknown,
.availability img.availabilityicon.unknown { .availability img.availabilityicon.unknown {
background: #ddd; background: #ddd;
} }
#schedule-freebusy-times td.free, #schedule-freebusy-times td div.free,
.availability img.availabilityicon.free { .availability img.availabilityicon.free {
background: #abd640; background: #abd640;
} }
#schedule-freebusy-times td.busy, #schedule-freebusy-times td div.busy,
.availability img.availabilityicon.busy { .availability img.availabilityicon.busy {
background: #e26569; background: #e26569;
} }
#schedule-freebusy-times td.tentative, #schedule-freebusy-times td div.tentative,
.availability img.availabilityicon.tentative { .availability img.availabilityicon.tentative {
background: #8383fc; background: #8383fc;
} }
#schedule-freebusy-times td.out-of-office, #schedule-freebusy-times td div.out-of-office,
.availability img.availabilityicon.out-of-office { .availability img.availabilityicon.out-of-office {
background: #fbaa68; background: #fbaa68;
} }
#schedule-freebusy-times td.all-busy, #schedule-freebusy-times td div.all-busy,
#schedule-freebusy-times td.all-tentative, #schedule-freebusy-times td div.all-tentative,
#schedule-freebusy-times td.all-out-of-office { #schedule-freebusy-times td div.all-out-of-office {
background-image: url(images/freebusy-colors.png); background-image: url(images/freebusy-colors.png);
background-position: top right; background-position: top right;
background-repeat: no-repeat; background-repeat: no-repeat;
} }
#schedule-freebusy-times td.all-tentative { #schedule-freebusy-times td div.all-tentative {
background-position: right -40px; 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; background-position: right -80px;
} }
@ -1267,10 +1298,6 @@ td.topalign {
white-space: nowrap; white-space: nowrap;
} }
#edit-attendees-legend img.availabilityicon {
vertical-align: middle;
}
.edit-attendees-table tbody td.confirmstate { .edit-attendees-table tbody td.confirmstate {
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
@ -1284,22 +1311,27 @@ td.topalign {
} }
.edit-attendees-table td.confirmstate span.needs-action { .edit-attendees-table td.confirmstate span.needs-action {
height: 14px;
} }
.edit-attendees-table td.confirmstate span.accepted { .edit-attendees-table td.confirmstate span.accepted {
background-position: 5px -20px; background-position: 5px -20px;
height: 14px;
} }
.edit-attendees-table td.confirmstate span.declined { .edit-attendees-table td.confirmstate span.declined {
background-position: 5px -40px; background-position: 5px -40px;
height: 14px;
} }
.edit-attendees-table td.confirmstate span.tentative { .edit-attendees-table td.confirmstate span.tentative {
background-position: 5px -60px; background-position: 5px -60px;
height: 14px;
} }
.edit-attendees-table td.confirmstate span.delegated { .edit-attendees-table td.confirmstate span.delegated {
background-position: 5px -180px; background-position: 5px -180px;
height: 14px;
} }
#attendees-freebusy-table { #attendees-freebusy-table {
@ -1380,7 +1412,8 @@ td.topalign {
.attendees-list .spacer, .attendees-list .spacer,
#schedule-freebusy-times tr.spacer td { #schedule-freebusy-times tr.spacer td {
background: 0; background: 0;
font-size: 50%; padding: 0;
height: 10px;
} }
#schedule-freebusy-times { #schedule-freebusy-times {
@ -1393,6 +1426,15 @@ td.topalign {
border: 1px solid #ccc; 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, #attendees-freebusy-table div.timesheader,
#schedule-freebusy-times tr.times td { #schedule-freebusy-times tr.times td {
min-width: 30px; min-width: 30px;
@ -1410,6 +1452,10 @@ td.topalign {
cursor: pointer; cursor: pointer;
} }
#schedule-freebusy-times #fbrowall td {
border-bottom: none;
}
#schedule-event-time { #schedule-event-time {
position: absolute; position: absolute;
border: 2px solid #333; border: 2px solid #333;

Binary file not shown.

After

Width:  |  Height:  |  Size: 746 B

View file

@ -82,6 +82,7 @@
<div id="eventshow" class="uidialog eventdialog" aria-hidden="true"> <div id="eventshow" class="uidialog eventdialog" aria-hidden="true">
<h1 id="event-title">Event Title</h1> <h1 id="event-title">Event Title</h1>
<div id="event-status-badge"><span></span></div>
<div class="event-section" id="event-location">Location</div> <div class="event-section" id="event-location">Location</div>
<div class="event-section" id="event-date">From-To</div> <div class="event-section" id="event-date">From-To</div>
<div class="event-section" id="event-description"> <div class="event-section" id="event-description">
@ -163,6 +164,7 @@
<ul id="eventoptionsmenu-menu" class="toolbarmenu" role="menu" aria-labelledby="aria-label-eventoptions"> <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-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-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" /> <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> <li role="menuitem"><roundcube:button command="event-history" type="link" label="calendar.eventhistory" classAct="active" /></li>
<roundcube:endif /> <roundcube:endif />

View file

@ -0,0 +1,87 @@
<roundcube:object name="doctype" value="html5" />
<html>
<head>
<title><roundcube:object name="pagetitle" /></title>
<roundcube:include file="/includes/links.html" />
<!--[if lte IE 7]><link rel="stylesheet" type="text/css" href="/this/iehacks.css" /><![endif]-->
</head>
<body class="calendarmain dialog">
<div id="mainscreen">
<div id="calendarsidebar">
<h2 id="aria-label-minical" class="voice"><roundcube:label name="calendar.arialabelminical" /></h2>
<div id="datepicker" class="uibox" role="presentation"></div>
<div id="calendars" class="uibox listbox" style="visibility:hidden" role="navigation" aria-labelledby="aria-label-calendarlist">
<h2 class="boxtitle" id="aria-label-calendarlist"><roundcube:label name="calendar.calendars" />
<a href="#calendars" class="iconbutton search" title="<roundcube:label name='calendar.findcalendars' />" tabindex="0"><roundcube:label name='calendar.findcalendars' /></a>
</h2>
<div class="listsearchbox">
<div class="searchbox" role="search" aria-labelledby="aria-label-calsearchform" aria-controls="calendarslist">
<h3 id="aria-label-calsearchform" class="voice"><roundcube:label name="calendar.arialabelcalsearchform" /></h3>
<label for="calendarlistsearch" class="voice"><roundcube:label name="calendar.searchterms" /></label>
<input type="text" name="q" id="calendarlistsearch" placeholder="<roundcube:label name='calendar.findcalendars' />" />
<a class="iconbutton searchicon"></a>
<roundcube:button command="reset-listsearch" id="calendarlistsearch-reset" class="iconbutton reset" title="resetsearch" label="resetsearch" />
</div>
</div>
<div class="scroller">
<roundcube:object name="plugin.calendar_list" id="calendarslist" class="treelist listing" />
</div>
</div>
</div>
<h2 id="aria-label-calendarview" class="voice"><roundcube:label name="calendar.arialabelcalendarview" /></h2>
<div id="calendar" role="main" aria-labelledby="aria-label-calendarview">
<roundcube:object name="plugin.angenda_options" class="boxfooter" id="agendaoptions" />
</div>
</div>
<div id="timezonedisplay"><roundcube:var name="env:timezone" /></div>
<roundcube:include file="/templates/eventshow.html" />
<roundcube:include file="/templates/eventedit.html" />
<roundcube:object name="plugin.calendar_css" />
<script type="text/javascript">
// UI startup
var UI = new rcube_mail_ui();
$(document).ready(function(e) {
UI.init();
// animation to unfold list search box
$('#calendars .boxtitle a.search').click(function(e){
var title = $('#calendars .boxtitle'),
box = $('#calendars .listsearchbox'),
dir = box.is(':visible') ? -1 : 1;
box.slideToggle({
duration: 160,
progress: function(animation, progress) {
if (dir < 0) progress = 1 - progress;
$('#calendars .scroller').css('top', (title.outerHeight() + 34 * progress) + 'px');
},
complete: function() {
box.toggleClass('expanded');
if (box.is(':visible')) {
box.find('input[type=text]').focus();
}
else {
$('#calendarlistsearch-reset').click();
}
// TODO: save state in localStorage
}
});
return false;
});
});
</script>
</body>
</html>

View file

@ -117,10 +117,10 @@
<!-- attachments list (with upload form) --> <!-- attachments list (with upload form) -->
<div id="event-panel-attachments"> <div id="event-panel-attachments">
<div id="edit-attachments"> <div id="edit-attachments">
<roundcube:object name="plugin.attachments_list" id="attachment-list" class="attachmentslist" /> <roundcube:object name="plugin.attachments_list" id="attachmentlist" class="attachmentslist" />
</div> </div>
<div id="edit-attachments-form" role="region" aria-labelledby="aria-label-attachmentuploadform"> <div id="edit-attachments-form" role="region" aria-labelledby="aria-label-attachmentuploadform">
<h3 id="aria-label-attachmentuploadform" class="voice"><roundcube:label name="arialabelattachmentuploadform" /></h2> <h3 id="aria-label-attachmentuploadform" class="voice"><roundcube:label name="arialabelattachmentuploadform" /></h3>
<roundcube:object name="plugin.attachments_form" id="calendar-attachment-form" attachmentFieldSize="30" /> <roundcube:object name="plugin.attachments_form" id="calendar-attachment-form" attachmentFieldSize="30" />
</div> </div>
<roundcube:object name="plugin.filedroparea" id="event-panel-attachments" /> <roundcube:object name="plugin.filedroparea" id="event-panel-attachments" />

View file

@ -0,0 +1,80 @@
<div id="eventshow" class="uidialog eventdialog" aria-hidden="true">
<h1 id="event-title">Event Title</h1>
<div class="event-section" id="event-location">Location</div>
<div class="event-section" id="event-date">From-To</div>
<div class="event-section" id="event-description">
<h5 class="label"><roundcube:label name="calendar.description" /></h5>
<div class="event-text"></div>
</div>
<div class="event-section" id="event-url">
<h5 class="label"><roundcube:label name="calendar.url" /></h5>
<div class="event-text"></div>
</div>
<div class="event-section" id="event-repeat">
<h5 class="label"><roundcube:label name="calendar.repeat" /></h5>
<div class="event-text"></div>
</div>
<div class="event-section" id="event-alarm">
<h5 class="label"><roundcube:label name="calendar.alarms" /></h5>
<div class="event-text"></div>
</div>
<div class="event-section event-attendees" id="event-attendees">
<h5 class="label"><roundcube:label name="calendar.tabattendees" /></h5>
<div class="event-text"></div>
</div>
<div class="event-line" id="event-partstat">
<label><roundcube:label name="calendar.mystatus" /></label>
<span class="changersvp" role="button" tabindex="0" title="<roundcube:label name='calendar.changepartstat' />">
<span class="event-text"></span>
<a class="iconbutton edit"><roundcube:label name='calendar.changepartstat' /></a>
</span>
</div>
<div class="event-line" id="event-calendar">
<label><roundcube:label name="calendar.calendar" /></label>
<span class="event-text">Default</span>
</div>
<div class="event-line" id="event-category">
<label><roundcube:label name="calendar.category" /></label>
<span class="event-text"></span>
</div>
<div class="event-line" id="event-status">
<label><roundcube:label name="calendar.status" /></label>
<span class="event-text"></span>
</div>
<div class="event-line" id="event-free-busy">
<label><roundcube:label name="calendar.freebusy" /></label>
<span class="event-text"></span>
</div>
<div class="event-line" id="event-priority">
<label><roundcube:label name="calendar.priority" /></label>
<span class="event-text"></span>
</div>
<div class="event-line" id="event-sensitivity">
<label><roundcube:label name="calendar.sensitivity" /></label>
<span class="event-text"></span>
</div>
<div class="event-section" id="event-attachments">
<label><roundcube:label name="attachments" /></label>
<div class="event-text"></div>
</div>
<div class="event-line" id="event-created-changed">
<label><roundcube:label name="calendar.created" /></label>
<span class="event-text event-created"></span>
<label><roundcube:label name="calendar.changed" /></label>
<span class="event-text event-changed"></span>
</div>
<div class="event-line" id="event-rsvp-comment">
<label><roundcube:label name="calendar.rsvpcomment" /></label>
<span class="event-text"></span>
</div>
<roundcube:object name="plugin.event_rsvp_buttons" id="event-rsvp" class="event-dialog-message" style="display:none" />
</div>
<div id="eventoptionsmenu" class="popupmenu" aria-hidden="true">
<h3 id="aria-label-eventoptions" class="voice"><roundcube:label name="calendar.eventoptions" /></h3>
<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>
</ul>
</div>