diff --git a/calendar.php b/calendar.php index a24f353..242fcc0 100644 --- a/calendar.php +++ b/calendar.php @@ -1975,8 +1975,8 @@ class calendar extends rcube_plugin private function write_preprocess(&$event, $action) { // convert dates into DateTime objects in user's current timezone - $event['start'] = new DateTime($event['start'], $this->timezone); - $event['end'] = new DateTime($event['end'], $this->timezone); + $event['start'] = new DateTime($event['start'], $this->timezone); + $event['end'] = new DateTime($event['end'], $this->timezone); $event['allday'] = (bool)$event['allday']; // start/end is all we need for 'move' action (#1480) @@ -2026,7 +2026,7 @@ class calendar extends rcube_plugin foreach ((array)$event['attendees'] as $i => $attendee) { if ($attendee['role'] == 'ORGANIZER') $organizer = $i; - if ($attendee['email'] && in_array(strtolower($attendee['email']), $emails)) + if ($attendee['email'] == in_array(strtolower($attendee['email']), $emails)) $owner = $i; if (!isset($attendee['rsvp'])) $event['attendees'][$i]['rsvp'] = true; @@ -2189,7 +2189,6 @@ class calendar extends rcube_plugin // if the backend has free-busy information $fblist = $this->driver->get_freebusy_list($email, $start, $end); - if (is_array($fblist)) { $status = 'FREE'; @@ -2239,26 +2238,13 @@ class calendar extends rcube_plugin $dts = new DateTime('@'.$start); $dts->setTimezone($this->timezone); } - + $fblist = $this->driver->get_freebusy_list($email, $start, $end); - $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; - } - } - } - + $slots = array(); + // build a list from $start till $end with blocks representing the fb-status for ($s = 0, $t = $start; $t <= $end; $s++) { + $status = self::FREEBUSY_UNKNOWN; $t_end = $t + $interval * 60; $dt = new DateTime('@'.$t); $dt->setTimezone($this->timezone); @@ -2266,10 +2252,16 @@ class calendar extends rcube_plugin // determine attendee's status if (is_array($fblist)) { $status = self::FREEBUSY_FREE; - foreach ($fblist as $slot) { list($from, $to, $type) = $slot; + // check for possible all-day times + if (gmdate('His', $from) == '000000' && gmdate('His', $to) == '235959') { + // shift into the user's timezone for sane matching + $from -= $this->gmt_offset; + $to -= $this->gmt_offset; + } + if ($from < $t_end && $to > $t) { $status = isset($type) ? $type : self::FREEBUSY_BUSY; if ($status == self::FREEBUSY_BUSY) // can't get any worse :-) @@ -2277,12 +2269,9 @@ class calendar extends rcube_plugin } } } - else { - $status = self::FREEBUSY_UNKNOWN; - } - - // use most compact format, assume $status is one digit/character - $slots .= $status; + + $slots[$s] = $status; + $times[$s] = $dt->format($strformat); $t = $t_end; } @@ -2298,6 +2287,7 @@ class calendar extends rcube_plugin 'end' => $dte->format('c'), 'interval' => $interval, 'slots' => $slots, + 'times' => $times, )); exit; } diff --git a/calendar_ui.js b/calendar_ui.js index 5e34d86..8db9b2e 100644 --- a/calendar_ui.js +++ b/calendar_ui.js @@ -1242,7 +1242,7 @@ function rcube_calendar_ui(settings) freebusy_data = { required:{}, all:{} }; freebusy_ui.loading = 1; // prevent render_freebusy_grid() to load data yet freebusy_ui.numdays = Math.max(allday.checked ? 14 : 1, Math.ceil(duration * 2 / 86400)); - freebusy_ui.interval = allday.checked ? 1440 : (60 / (settings.timeslots || 1)); + freebusy_ui.interval = allday.checked ? 1440 : 60; freebusy_ui.start = fb_start; freebusy_ui.end = new Date(freebusy_ui.start.getTime() + DAY_MS * freebusy_ui.numdays); render_freebusy_grid(0); @@ -1349,7 +1349,7 @@ function rcube_calendar_ui(settings) // adjust dialog size to fit grid without scrolling var gridw = $('#schedule-freebusy-times').width(); - var overflow = gridw - $('#attendees-freebusy-table td.times').width(); + var overflow = gridw - $('#attendees-freebusy-table td.times').width() + 1; me.dialog_resize($dialog.get(0), $dialog.height() + (bw.ie ? 20 : 0), 800 + Math.max(0, overflow)); // fetch data from server @@ -1379,12 +1379,10 @@ function rcube_calendar_ui(settings) var lastdate, datestr, css, curdate = new Date(), allday = (freebusy_ui.interval == 1440), - interval = allday ? 1440 : (freebusy_ui.interval * (settings.timeslots || 1)); times_css = (allday ? 'allday ' : ''), dates_row = '', times_row = '', slots_row = ''; - for (var s = 0, t = freebusy_ui.start.getTime(); t < freebusy_ui.end.getTime(); s++) { curdate.setTime(t); datestr = fc.fullCalendar('formatDate', curdate, date_format); @@ -1397,9 +1395,9 @@ function rcube_calendar_ui(settings) // set css class according to working hours css = is_weekend(curdate) || (freebusy_ui.interval <= 60 && !is_workinghour(curdate)) ? 'offhours' : 'workinghours'; times_row += '' + Q(allday ? rcmail.gettext('all-day','calendar') : $.fullCalendar.formatDate(curdate, settings['time_format'])) + ''; - slots_row += ' '; + slots_row += ' '; - t += interval * 60000; + t += freebusy_ui.interval * 60000; } dates_row += ''; times_row += ''; @@ -1413,7 +1411,7 @@ function rcube_calendar_ui(settings) } // add line for all/required attendees - times_html += ''; + times_html += ' '; times_html += '' + slots_row + ''; var table = $('#schedule-freebusy-times'); @@ -1435,7 +1433,7 @@ function rcube_calendar_ui(settings) update_freebusy_dates(newstart, new Date(newstart.getTime() + freebusy_ui.startdate.data('duration') * 1000)); render_freebusy_overlay(); } - }); + }) } // if we have loaded free-busy data, show it @@ -1466,40 +1464,38 @@ function rcube_calendar_ui(settings) overlay.draggable('disable'); } else { - var i, n, table = $('#schedule-freebusy-times'), + var table = $('#schedule-freebusy-times'), width = 0, pos = { top:table.children('thead').height(), left:0 }, eventstart = date2unixtime(clone_date(me.selected_event.start, me.selected_event.allDay?1:0)), eventend = date2unixtime(clone_date(me.selected_event.end, me.selected_event.allDay?2:0)) - 60, slotstart = date2unixtime(freebusy_ui.start), slotsize = freebusy_ui.interval * 60, - slotnum = freebusy_ui.interval > 60 ? 1 : (60 / freebusy_ui.interval), - cells = table.children('thead').find('td'), - cell_width = cells.first().get(0).offsetWidth, - slotend; - + slotend, fraction, $cell; + // iterate through slots to determine position and size of the overlay - for (i=0; i < cells.length; i++) { - for (n=0; n < slotnum; n++) { - slotend = slotstart + slotsize - 1; - // event starts in this slot: compute left - if (eventstart >= slotstart && eventstart <= slotend) { - pos.left = Math.round(i * cell_width + (cell_width / slotnum) * n); - } - // event ends in this slot: compute width - if (eventend >= slotstart && eventend <= slotend) { - width = Math.round(i * cell_width + (cell_width / slotnum) * (n + 1)) - pos.left; - } - slotstart += slotsize; + table.children('thead').find('td').each(function(i, cell){ + slotend = slotstart + slotsize - 1; + // event starts in this slot: compute left + if (eventstart >= slotstart && eventstart <= slotend) { + fraction = 1 - (slotend - eventstart) / slotsize; + pos.left = Math.round(cell.offsetLeft + cell.offsetWidth * fraction); } - } + // event ends in this slot: compute width + if (eventend >= slotstart && eventend <= slotend) { + fraction = 1 - (slotend - eventend) / slotsize; + width = Math.round(cell.offsetLeft + cell.offsetWidth * fraction) - pos.left; + } + + slotstart = slotstart + slotsize; + }); if (!width) width = table.width() - pos.left; // overlay is visible if (width > 0) { - overlay.css({ width: (width-4)+'px', height:(table.children('tbody').height() - 4)+'px', left:pos.left+'px', top:pos.top+'px' }).show(); + overlay.css({ width: (width-5)+'px', height:(table.children('tbody').height() - 4)+'px', left:pos.left+'px', top:pos.top+'px' }).show(); // configure draggable if (!overlay.data('isdraggable')) { @@ -1522,7 +1518,6 @@ function rcube_calendar_ui(settings) } else { // round to 5 minutes - // @TODO: round to timeslots? var round = newstart.getMinutes() % 5; if (round > 2.5) newstart.setTime(newstart.getTime() + (5 - round) * 60000); else if (round > 0) newstart.setTime(newstart.getTime() - round * 60000); @@ -1540,8 +1535,10 @@ function rcube_calendar_ui(settings) else overlay.draggable('disable').hide(); } + }; - + + // fetch free-busy information for each attendee from server var load_freebusy_data = function(from, interval) { @@ -1568,9 +1565,9 @@ function rcube_calendar_ui(settings) success: function(data) { freebusy_ui.loading--; - // find attendee - var i, attendee = null; - for (i=0; i < event_attendees.length; i++) { + // find attendee + var attendee = null; + for (var i=0; i < event_attendees.length; i++) { if (freebusy_ui.attendees[i].email == data.email) { attendee = freebusy_ui.attendees[i]; break; @@ -1578,31 +1575,25 @@ function rcube_calendar_ui(settings) } // copy data to member var - var ts, status, - req = attendee.role != 'OPT-PARTICIPANT', - start = parseISO8601(data.start); - - freebusy_data.start = new Date(start); - freebusy_data.end = parseISO8601(data.end); - freebusy_data.interval = data.interval; + var ts, req = attendee.role != 'OPT-PARTICIPANT'; + freebusy_data.start = parseISO8601(data.start); freebusy_data[data.email] = {}; - - for (i=0; i < data.slots.length; i++) { - ts = date2timestring(start, data.interval > 60); - status = data.slots.charAt(i); - freebusy_data[data.email][ts] = status - start = new Date(start.getTime() + data.interval * 60000); + for (var i=0; i < data.slots.length; i++) { + ts = data.times[i] + ''; + freebusy_data[data.email][ts] = data.slots[i]; // set totals if (!freebusy_data.required[ts]) freebusy_data.required[ts] = [0,0,0,0]; if (req) - freebusy_data.required[ts][status]++; + freebusy_data.required[ts][data.slots[i]]++; if (!freebusy_data.all[ts]) freebusy_data.all[ts] = [0,0,0,0]; - freebusy_data.all[ts][status]++; + freebusy_data.all[ts][data.slots[i]]++; } + freebusy_data.end = parseISO8601(data.end); + freebusy_data.interval = data.interval; // hide loading indicator var domid = String(data.email).replace(rcmail.identifier_expr, ''); @@ -1665,51 +1656,27 @@ function rcube_calendar_ui(settings) if (fbdata && fbdata[ts] !== undefined && row.length) { t = freebusy_ui.start.getTime(); - row.children().each(function(i, cell) { - var j, n, attr, last, all_slots = [], slots = [], - all_cell = rowall.get(i), - cnt = dateonly ? 1 : (60 / freebusy_ui.interval), - percent = (100 / cnt); + row.children().each(function(i, cell){ + curdate.setTime(t); + ts = date2timestring(curdate, dateonly); + cell.className = cell.className.replace('unknown', fbdata[ts] ? status_classes[fbdata[ts]] : 'unknown'); - for (n=0; n < cnt; n++) { - curdate.setTime(t); - ts = date2timestring(curdate, dateonly); - attr = { - 'style': 'float:left; width:' + percent.toFixed(2) + '%', - 'class': fbdata[ts] ? status_classes[fbdata[ts]] : 'unknown' - }; - - slots.push($('
').attr(attr)); - - // also update total row if all data was loaded - if (!freebusy_ui.loading && freebusy_data.all[ts] && all_cell) { - var all_status = freebusy_data.all[ts][2] ? 'busy' : 'unknown', - req_status = freebusy_data.required[ts][2] ? 'busy' : 'free'; - - for (j=1; j < status_classes.length; j++) { - if (freebusy_ui.numrequired && freebusy_data.required[ts][j] >= freebusy_ui.numrequired) - req_status = status_classes[j]; - if (freebusy_data.all[ts][j] == event_attendees.length) - all_status = status_classes[j]; - } - - attr['class'] = req_status + ' all-' + all_status; - - // these elements use some specific styling, so we want to minimize their number - if (last && last.attr('class') == attr['class']) - last.css('width', (percent + parseFloat(last.css('width').replace('%', ''))).toFixed(2) + '%'); - else { - last = $('
').attr(attr); - all_slots.push(last); - } + // also update total row if all data was loaded + if (freebusy_ui.loading == 0 && freebusy_data.all[ts] && (cell = rowall.get(i))) { + var workinghours = cell.className.indexOf('workinghours') >= 0; + var all_status = freebusy_data.all[ts][2] ? 'busy' : 'unknown'; + req_status = freebusy_data.required[ts][2] ? 'busy' : 'free'; + for (var j=1; j < status_classes.length; j++) { + if (freebusy_ui.numrequired && freebusy_data.required[ts][j] >= freebusy_ui.numrequired) + req_status = status_classes[j]; + if (freebusy_data.all[ts][j] == event_attendees.length) + all_status = status_classes[j]; } - - t += freebusy_ui.interval * 60000; + + cell.className = (workinghours ? 'workinghours ' : 'offhours ') + req_status + ' all-' + all_status; } - - $(cell).html('').append(slots); - if (all_slots.length) - $(all_cell).html('').append(all_slots); + + t += freebusy_ui.interval * 60000; }); } }; @@ -1749,20 +1716,17 @@ function rcube_calendar_ui(settings) sinterval = freebusy_data.interval * 60000, intvlslots = 1, numslots = Math.ceil(duration / sinterval), - 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; + checkdate, slotend, email, ts, slot, slotdate = new Date(); // shift event times to next possible slot eventstart += sinterval * intvlslots * dir; eventend += sinterval * intvlslots * dir; // iterate through free-busy slots and find candidates - for (slot = dir > 0 ? fb_start : fb_end - sinterval; - (dir > 0 && slot < fb_end) || (dir < 0 && slot >= fb_start); - slot += sinterval * dir - ) { + var candidatecount = 0, candidatestart = candidateend = success = false; + for (slot = dir > 0 ? freebusy_data.start.getTime() : freebusy_data.end.getTime() - sinterval; + (dir > 0 && slot < freebusy_data.end.getTime()) || (dir < 0 && slot >= freebusy_data.start.getTime()); + slot += sinterval * dir) { slotdate.setTime(slot); // fix slot if just crossed a DST change if (event.allDay) { @@ -1774,10 +1738,10 @@ function rcube_calendar_ui(settings) if ((dir > 0 && slotend <= eventstart) || (dir < 0 && slot >= eventend)) // skip continue; - // respect workinghours setting + // respect workingours setting if (freebusy_ui.workinhoursonly) { if (is_weekend(slotdate) || (freebusy_data.interval <= 60 && !is_workinghour(slotdate))) { // skip off-hours - candidatestart = false; + candidatestart = candidateend = false; candidatecount = 0; continue; } @@ -1786,12 +1750,11 @@ function rcube_calendar_ui(settings) if (!candidatestart) candidatestart = slot; - ts = date2timestring(slotdate, freebusy_data.interval > 60); - // check freebusy data for all attendees + ts = date2timestring(slotdate, freebusy_data.interval > 60); for (var i=0; i < event_attendees.length; i++) { if (freebusy_ui.attendees[i].role != 'OPT-PARTICIPANT' && (email = freebusy_ui.attendees[i].email) && freebusy_data[email] && freebusy_data[email][ts] > 1) { - candidatestart = false; + candidatestart = candidateend = false; break; } } @@ -1802,15 +1765,22 @@ function rcube_calendar_ui(settings) candidatecount = 0; continue; } - else if (dir < 0) - candidatestart = slot; + // set candidate end to slot end time candidatecount++; + if (dir < 0 && !candidateend) + candidateend = slotend; // if candidate is big enough, this is it! if (candidatecount == numslots) { - event.start.setTime(candidatestart); - event.end.setTime(candidatestart + duration); + if (dir > 0) { + event.start.setTime(candidatestart); + event.end.setTime(candidatestart + duration); + } + else { + event.end.setTime(candidateend); + event.start.setTime(candidateend - duration); + } success = true; break; } @@ -1840,6 +1810,7 @@ function rcube_calendar_ui(settings) } }; + // update event properties and attendees availability if event times have changed var event_times_changed = function() { @@ -1854,6 +1825,7 @@ function rcube_calendar_ui(settings) } }; + // add the given list of participants var add_attendees = function(names, params) { diff --git a/drivers/kolab/kolab_calendar.php b/drivers/kolab/kolab_calendar.php index fe2ede5..107a764 100644 --- a/drivers/kolab/kolab_calendar.php +++ b/drivers/kolab/kolab_calendar.php @@ -763,25 +763,11 @@ class kolab_calendar extends kolab_storage_folder_api private function _from_driver_event($event, $old = array()) { // set current user as ORGANIZER - if ($identity = $this->cal->rc->user->list_emails(true)) { - $event['attendees'] = (array) $event['attendees']; - $found = false; + $identity = $this->cal->rc->user->list_emails(true); + if (empty($event['attendees']) && $identity['email']) + $event['attendees'] = array(array('role' => 'ORGANIZER', 'name' => $identity['name'], 'email' => $identity['email'])); - // 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']; - } + $event['_owner'] = $identity['email']; // remove EXDATE values if RDATE is given if (!empty($event['recurrence']['RDATE'])) { @@ -806,6 +792,7 @@ class kolab_calendar extends kolab_storage_folder_api }); } + // remove some internal properties which should not be saved unset($event['_savemode'], $event['_fromcalendar'], $event['_identity'], $event['_folder_id'], $event['recurrence_id'], $event['attachments'], $event['deleted_attachments'], $event['className']); diff --git a/localization/da_DK.inc b/localization/da_DK.inc index 4e70bf6..45dd35d 100644 --- a/localization/da_DK.inc +++ b/localization/da_DK.inc @@ -1,4 +1,5 @@ CalDAV-klientprogram (eks. Evolution eller Mozilla Thunderbird) for at synkronisere denne kalender komplet med din computer eller mobilenhed.'; $labels['findcalendars'] = 'Find kalendere ...'; -$labels['searchterms'] = 'Search terms'; +$labels['searchterms'] = 'Søgetermer'; $labels['calsearchresults'] = 'Tilgængelige kalendere'; -$labels['calendarsubscribe'] = 'List permanently'; +$labels['calendarsubscribe'] = 'List permanent'; $labels['nocalendarsfound'] = 'Der blev ikke fundet nogen kalender'; $labels['nrcalendarsfound'] = '$nr kalendere blev fundet'; $labels['quickview'] = 'Vis kun denne kalender'; @@ -115,6 +122,8 @@ $labels['invitationspending'] = 'Afventende invitationer'; $labels['invitationsdeclined'] = 'Afviste invitationer'; $labels['changepartstat'] = 'Skift deltagerstatus'; $labels['rsvpcomment'] = 'Invitationstekst'; + +// agenda view $labels['listrange'] = 'Interval som skal vises:'; $labels['listsections'] = 'Del op i:'; $labels['smartsections'] = 'Smarte sektioner'; @@ -129,9 +138,13 @@ $labels['nextmonth'] = 'Næste måned'; $labels['weekofyear'] = 'Uge'; $labels['pastevents'] = 'Tidligere'; $labels['futureevents'] = 'Fremtid'; -$labels['showalarms'] = 'Show reminders'; + +// alarm/reminder settings +$labels['showalarms'] = 'Vis påmindelser'; $labels['defaultalarmtype'] = 'Standardindstilling for påmindelse'; $labels['defaultalarmoffset'] = 'Standardtidspunkt for påmindelse'; + +// attendees $labels['attendee'] = 'Deltager'; $labels['role'] = 'Rolle'; $labels['availability'] = 'Tilg.'; @@ -151,8 +164,8 @@ $labels['availbusy'] = 'Optaget'; $labels['availunknown'] = 'Ukendt'; $labels['availtentative'] = 'Forsøgsvis'; $labels['availoutofoffice'] = 'Ikke på kontoret'; -$labels['delegatedto'] = 'Delegated to: '; -$labels['delegatedfrom'] = 'Delegated from: '; +$labels['delegatedto'] = 'Delegere til:'; +$labels['delegatedfrom'] = 'Delegere fra:'; $labels['scheduletime'] = 'Find ledigt tidspunkt'; $labels['sendinvitations'] = 'Send invitationer'; $labels['sendnotifications'] = 'Gør deltagere opmærksom på ændringer'; @@ -169,18 +182,24 @@ $labels['invitationattendlinks'] = "Hvis dit e-postprogram ikke understøtter iT $labels['eventupdatesubject'] = '"$title" 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['eventcancelsubject'] = '"$title" er blevet annulleret'; $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."; + +// invitation handling (overrides labels from libcalendaring) $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['itipmailbodytentative'] = "\$sender har forsøgsvist accepteret invitationen til det følgende arrangement:\n\n*\$title*\n\nTidspunkt: \$date\n\nInviterede: \$attendees"; $labels['itipmailbodydeclined'] = "\$sender har afvist invitationen til det følgende arrangement:\n\n*\$title*\n\nTidspunkt: \$date\n\nInviterede: \$attendees"; $labels['itipmailbodycancel'] = "\$sender har afvist din deltagelse i følgende begivenhed:\n\n*\$title*\n\nTidspunkt: \$date"; $labels['itipmailbodydelegated'] = "\$sender har delegeret deltagelsen i følgende begivenhed:\n\n*\$title*\n\nTidspunkt: \$date"; $labels['itipmailbodydelegatedto'] = "\$sender har delegeret deltagelsen i følgende begivenhed til dig:\n\n*\$title*\n\nTidspunkt: \$date"; + $labels['itipdeclineevent'] = 'Sikker på at du vil afvise dette arrangement?'; $labels['declinedeleteconfirm'] = 'Vil du også slette dette afviste arrangement fra din kalender?'; $labels['itipcomment'] = 'Invitation/notification comment'; $labels['itipcommenttitle'] = 'Denne kommentar vil blive føjet til den besked med invitation/notifikation, der sendes til deltagerne'; + $labels['notanattendee'] = 'Du er ikke opført som deltager for dette arrangement'; $labels['eventcancelled'] = 'Arrangementet er blevet aflyst'; $labels['saveincalendar'] = 'gem i'; @@ -189,6 +208,8 @@ $labels['savetocalendar'] = 'Gem til kalender'; $labels['openpreview'] = 'Tjek kalender'; $labels['noearlierevents'] = 'Ingen tidligere begivenheder'; $labels['nolaterevents'] = 'Ingen senere begivenheder'; + +// resources $labels['resource'] = 'Ressource'; $labels['addresource'] = 'Booking af ressource'; $labels['findresources'] = 'Find ressourcer'; @@ -196,12 +217,16 @@ $labels['resourcedetails'] = 'Detaljer'; $labels['resourceavailability'] = 'Tilgængelighed'; $labels['resourceowner'] = 'Ejer'; $labels['resourceadded'] = 'Ressourcen til føjet til din begivenhed'; + +// event dialog tabs $labels['tabsummary'] = 'Resumé'; $labels['tabrecurrence'] = 'Gentagelse'; $labels['tabattendees'] = 'Deltagere'; $labels['tabresources'] = 'Ressourcer'; $labels['tabattachments'] = 'Vedhæftninger'; $labels['tabsharing'] = 'Deling'; + +// messages $labels['deleteobjectconfirm'] = 'Sikker på at du vil slette dette arrangement?'; $labels['deleteventconfirm'] = 'Sikker på at du vil slette dette arrangement?'; $labels['deletecalendarconfirm'] = 'Sikker på at du vil slette denne kalender med alle dets arrangementer?'; @@ -231,6 +256,7 @@ $labels['importsuccess'] = 'Gennemførte import af $nr arrangementer'; $labels['importnone'] = 'Fandt ingen arrangementer som kunne importeres'; $labels['importerror'] = 'Der opstod en fejl under import'; $labels['aclnorights'] = 'Du har ikke administratorrettigheder for denne kalender.'; + $labels['changeeventconfirm'] = 'Tilpas arrangement'; $labels['removeeventconfirm'] = 'Slet begivenhed'; $labels['changerecurringeventwarning'] = 'Dette er et tilbagevendende arrangement. Ønsker du kun at redige det aktuelle arrangement, dette og alle fremtidige forekomster, alle forekomster eller gemme det som et nyt arrangement?'; @@ -240,12 +266,16 @@ $labels['currentevent'] = 'Nuværende'; $labels['futurevents'] = 'Fremtid'; $labels['allevents'] = 'Alle'; $labels['saveasnew'] = 'Gem som ny'; + +// birthdays calendar $labels['birthdays'] = 'Fødselsdage'; $labels['birthdayscalendar'] = 'Fødselsdagskalender'; $labels['displaybirthdayscalendar'] = 'Vis fødselsdagskalender'; $labels['birthdayscalendarsources'] = 'Fra disse adressebøger'; $labels['birthdayeventtitle'] = '$name har fødselsdag'; $labels['birthdayage'] = '$age år'; + +// history dialog $labels['objectchangelog'] = 'Ændringshistorik'; $labels['objectdiff'] = 'Ændringer fra $rev1 til $rev2'; $labels['objectnotfound'] = 'Kunne ikke indlæse begivenhedsdata'; @@ -254,6 +284,9 @@ $labels['objectdiffnotavailable'] = 'Det er ikke muligt at sammenligne de valgte $labels['revisionrestoreconfirm'] = 'Sikker på at du vil genskabe revision $rev af denne begivenhed? Dette vil erstatte den nuværende begivenhed med den tidligere version.'; $labels['objectrestoresuccess'] = 'Revision $rev blev genskabt'; $labels['objectrestoreerror'] = 'Mislykkedes med at genskabe den gamle revision'; + + +// (hidden) titles and labels for accessibility annotations $labels['arialabelminical'] = 'Valg af kalenderdato'; $labels['arialabelcalendarview'] = 'Kalendervisning'; $labels['arialabelsearchform'] = 'Søgeformular for begivenheder'; @@ -264,4 +297,5 @@ $labels['arialabeleventattendees'] = 'Deltagerliste for begivenhed'; $labels['arialabeleventresources'] = 'Ressourceliste for begivenhed'; $labels['arialabelresourcesearchform'] = 'Søgeformular for ressourcer'; $labels['arialabelresourceselection'] = 'Tilgængelige ressourcer'; + ?> diff --git a/localization/de_DE.inc b/localization/de_DE.inc index f9ff3b4..619fb09 100644 --- a/localization/de_DE.inc +++ b/localization/de_DE.inc @@ -1,4 +1,5 @@ CalDAV-Klienten (z.B. Evolution oder Mozilla Thunderbird) kopieren, um den Kalender in Gänze mit einem mobilen Gerät zu synchronisieren.'; -$labels['findcalendars'] = 'Kalender finden...'; +$labels['findcalendars'] = 'Kalender finden …'; $labels['searchterms'] = 'Suchbegriffe'; $labels['calsearchresults'] = 'Verfügbare Kalender'; $labels['calendarsubscribe'] = 'Permanent anzeigen'; @@ -115,6 +122,8 @@ $labels['invitationspending'] = 'Ausstehende Einladungen'; $labels['invitationsdeclined'] = 'Abgelehnte Einladungen'; $labels['changepartstat'] = 'Teilnehmerstatus ändern'; $labels['rsvpcomment'] = 'Einladungstext'; + +// agenda view $labels['listrange'] = 'Angezeigter Bereich:'; $labels['listsections'] = 'Unterteilung:'; $labels['smartsections'] = 'Intelligent'; @@ -129,9 +138,13 @@ $labels['nextmonth'] = 'Nächsten Monat'; $labels['weekofyear'] = 'Woche'; $labels['pastevents'] = 'Vergangene'; $labels['futureevents'] = 'Zukünftige'; + +// alarm/reminder settings $labels['showalarms'] = 'Erinnerungen anzeigen'; $labels['defaultalarmtype'] = 'Standard-Erinnerungseinstellung'; $labels['defaultalarmoffset'] = 'Standard-Erinnerungszeit'; + +// attendees $labels['attendee'] = 'Teilnehmer'; $labels['role'] = 'Rolle'; $labels['availability'] = 'Verfüg.'; @@ -163,25 +176,30 @@ $labels['prevslot'] = 'Vorheriger Vorschlag'; $labels['nextslot'] = 'Nächster Vorschlag'; $labels['suggestedslot'] = 'Empfohlener Slot'; $labels['noslotfound'] = 'Es konnten keine freien Zeiten gefunden werden'; -$labels['invitationsubject'] = 'Sie wurden zu "$title" eingeladen'; +$labels['invitationsubject'] = 'Sie wurden zu »$title« eingeladen'; $labels['invitationmailbody'] = "*\$title*\n\nWann: \$date\n\nTeilnehmer: \$attendees\n\nIm Anhang finden Sie eine iCalendar-Datei mit allen Details des Termins. Diese können Sie in Ihre Kalenderanwendung importieren."; $labels['invitationattendlinks'] = "Falls Ihr E-Mail-Programm keine iTip-Anfragen unterstützt, können Sie den folgenden Link verwenden, um den Termin zu bestätigen oder abzulehnen:\n\$url"; -$labels['eventupdatesubject'] = '"$title" wurde aktualisiert'; +$labels['eventupdatesubject'] = '»$title« wurde aktualisiert'; $labels['eventupdatesubjectempty'] = 'Termin wurde aktualisiert'; $labels['eventupdatemailbody'] = "*\$title*\n\nWann: \$date\n\nTeilnehmer: \$attendees\n\nIm Anhang finden Sie eine iCalendar-Datei mit den aktualisiereten Termindaten. Diese können Sie in Ihre Kalenderanwendung importieren."; -$labels['eventcancelsubject'] = '"$title" wurde abgesagt'; +$labels['eventcancelsubject'] = '»$title« wurde abgesagt'; $labels['eventcancelmailbody'] = "*\$title*\n\nWann: \$date\n\nTeilnehmer: \$attendees\n\nDer Termin wurde von \$organizer abgesagt.\n\nIm Anhang finden Sie eine iCalendar-Datei mit den Termindaten."; + +// invitation handling (overrides labels from libcalendaring) $labels['itipobjectnotfound'] = 'Der Termin auf den sich diese Nachricht bezieht, wurde in Ihrem Kalender nicht gefunden.'; + $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['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\nWann: \$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\nWann: \$date"; + $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['itipcomment'] = 'Kommentar zur Einladungs-/Benachrichtigungsnachricht'; $labels['itipcommenttitle'] = 'Dieser Kommentar wird an die Einladungs-/Benachrichtigungsnachricht angehängt, die an die Teilnehmer verschickt wird'; + $labels['notanattendee'] = 'Sie sind nicht in der Liste der Teilnehmer aufgeführt'; $labels['eventcancelled'] = 'Der Termin wurde vom Organisator abgesagt'; $labels['saveincalendar'] = 'speichern in'; @@ -190,6 +208,8 @@ $labels['savetocalendar'] = 'In Kalender übernehmen'; $labels['openpreview'] = 'Kalender überprüfen'; $labels['noearlierevents'] = 'Keine früheren Ereignisse'; $labels['nolaterevents'] = 'Keine späteren Ereignisse'; + +// resources $labels['resource'] = 'Ressource'; $labels['addresource'] = 'Ressource buchen'; $labels['findresources'] = 'Ressourcen finden'; @@ -197,17 +217,21 @@ $labels['resourcedetails'] = 'Details'; $labels['resourceavailability'] = 'Verfügbarkeit'; $labels['resourceowner'] = 'Eigentümer'; $labels['resourceadded'] = 'Diese Ressource wurde Ihrem Termin hinzugefügt'; + +// event dialog tabs $labels['tabsummary'] = 'Übersicht'; $labels['tabrecurrence'] = 'Wiederholung'; $labels['tabattendees'] = 'Teilnehmer'; $labels['tabresources'] = 'Ressourcen'; $labels['tabattachments'] = 'Anhänge'; $labels['tabsharing'] = 'Freigabe'; + +// messages $labels['deleteobjectconfirm'] = 'Möchten Sie diesen Termin wirklich löschen?'; $labels['deleteventconfirm'] = 'Möchten Sie diesen Termin wirklich löschen?'; $labels['deletecalendarconfirm'] = 'Möchten Sie diesen Kalender mit allen Terminen wirklich löschen?'; $labels['deletecalendarconfirmrecursive'] = 'Soll dieser Kalender wirklich mit allen Terminen und Unterkalendern gelöscht werden?'; -$labels['savingdata'] = 'Speichere Daten...'; +$labels['savingdata'] = 'Daten werden gespeichert …'; $labels['errorsaving'] = 'Fehler beim Speichern.'; $labels['operationfailed'] = 'Die Aktion ist fehlgeschlagen.'; $labels['invalideventdates'] = 'Ungültige Daten eingegeben! Bitte überprüfen Sie die Eingaben.'; @@ -220,8 +244,8 @@ $labels['errorimportingevent'] = 'Fehler beim Importieren'; $labels['importwarningexists'] = 'Eine Kopie dieses Termins existiert bereits in Ihrem Kalender.'; $labels['newerversionexists'] = 'Eine neuere Version dieses Termins exisitert bereits! Import abgebrochen.'; $labels['nowritecalendarfound'] = 'Kein Kalender zum Speichern gefunden'; -$labels['importedsuccessfully'] = 'Der Termin wurde erfolgreich in \'$calendar\' gespeichert'; -$labels['updatedsuccessfully'] = 'Der Termin wurde erfolgreich in \'$calendar\' geändert'; +$labels['importedsuccessfully'] = 'Der Termin wurde erfolgreich in »$calendar« gespeichert'; +$labels['updatedsuccessfully'] = 'Der Termin wurde erfolgreich in »$calendar« geändert'; $labels['attendeupdateesuccess'] = 'Teilnehmerstatus erfolgreich aktualisiert'; $labels['itipsendsuccess'] = 'Einladung an Teilnehmer versendet.'; $labels['itipresponseerror'] = 'Die Antwort auf diese Einladung konnte nicht versendet werden'; @@ -232,6 +256,7 @@ $labels['importsuccess'] = 'Es wurden $nr Termine erfolgreich importiert'; $labels['importnone'] = 'Keine Termine zum Importieren gefunden'; $labels['importerror'] = 'Fehler beim Importieren'; $labels['aclnorights'] = 'Der Zugriff auf diesen Kalender erfordert Administrator-Rechte.'; + $labels['changeeventconfirm'] = 'Termin ändern'; $labels['removeeventconfirm'] = 'Termin löschen'; $labels['changerecurringeventwarning'] = 'Dies ist eine Terminreihe. Möchten Sie nur den aktuellen, diesen und alle zukünftigen oder alle Termine bearbeiten oder die Änderungen als neuen Termin speichern?'; @@ -241,13 +266,17 @@ $labels['currentevent'] = 'Aktuellen'; $labels['futurevents'] = 'Zukünftige'; $labels['allevents'] = 'Alle'; $labels['saveasnew'] = 'Als neu speichern'; + +// birthdays calendar $labels['birthdays'] = 'Geburtstage'; -$labels['birthdayscalendar'] = 'Geburtstags-Kalender'; -$labels['displaybirthdayscalendar'] = 'Geburtstags-Kalender anzeigen'; +$labels['birthdayscalendar'] = 'Geburtstagskalender'; +$labels['displaybirthdayscalendar'] = 'Geburtstagskalender anzeigen'; $labels['birthdayscalendarsources'] = 'Für diese Adressbücher'; $labels['birthdayeventtitle'] = '$names Geburtstag'; $labels['birthdayage'] = 'Alter $age'; -$labels['objectchangelog'] = 'Änderungshistorie'; + +// history dialog +$labels['objectchangelog'] = 'Änderungsverlauf'; $labels['objectdiff'] = 'Änderungen aus $rev1 nach $rev2'; $labels['objectnotfound'] = 'Termindaten sind leider nicht vergübar'; $labels['objectchangelognotavailable'] = 'Änderungshistorie ist nicht verfügbar für diesen Termin'; @@ -255,6 +284,9 @@ $labels['objectdiffnotavailable'] = 'Vergleich für die gewählten Versionen nic $labels['revisionrestoreconfirm'] = 'Wollen Sie wirklich die Version $rev dieses Termins wiederherstellen? Diese Aktion wird die aktuelle Kopie mit der älteren Version ersetzen.'; $labels['objectrestoresuccess'] = 'Revision $rev erfolgreich wiederhergestellt'; $labels['objectrestoreerror'] = 'Fehler beim Wiederherstellen der alten Revision'; + + +// (hidden) titles and labels for accessibility annotations $labels['arialabelminical'] = 'Kalender Datumswahl'; $labels['arialabelcalendarview'] = 'Kalender Ansicht'; $labels['arialabelsearchform'] = 'Suchformular für Termine'; @@ -265,4 +297,5 @@ $labels['arialabeleventattendees'] = 'Teilehmerliste'; $labels['arialabeleventresources'] = 'Liste der Terminressourcen'; $labels['arialabelresourcesearchform'] = 'Suchformular für Ressourcen'; $labels['arialabelresourceselection'] = 'Verfügbare Ressourcen'; + ?> diff --git a/localization/ru_RU.inc b/localization/ru_RU.inc index 716af10..cce82d6 100644 --- a/localization/ru_RU.inc +++ b/localization/ru_RU.inc @@ -1,4 +1,5 @@ diff --git a/skins/larry/calendar.css b/skins/larry/calendar.css index 4a035cb..71b2575 100644 --- a/skins/larry/calendar.css +++ b/skins/larry/calendar.css @@ -115,7 +115,6 @@ body.calendarmain #mainscreen { width: 6px; top: 40px !important; bottom: 0; - height: auto; background: url(images/toggle.gif) -1px 48% no-repeat transparent; } @@ -1247,44 +1246,44 @@ td.topalign { background: url(images/loading_blue.gif) center no-repeat; } -#schedule-freebusy-times td div.unknown, +#schedule-freebusy-times td.unknown, .availability img.availabilityicon.unknown { background: #ddd; } -#schedule-freebusy-times td div.free, +#schedule-freebusy-times td.free, .availability img.availabilityicon.free { background: #abd640; } -#schedule-freebusy-times td div.busy, +#schedule-freebusy-times td.busy, .availability img.availabilityicon.busy { background: #e26569; } -#schedule-freebusy-times td div.tentative, +#schedule-freebusy-times td.tentative, .availability img.availabilityicon.tentative { background: #8383fc; } -#schedule-freebusy-times td div.out-of-office, +#schedule-freebusy-times td.out-of-office, .availability img.availabilityicon.out-of-office { background: #fbaa68; } -#schedule-freebusy-times td div.all-busy, -#schedule-freebusy-times td div.all-tentative, -#schedule-freebusy-times td div.all-out-of-office { +#schedule-freebusy-times td.all-busy, +#schedule-freebusy-times td.all-tentative, +#schedule-freebusy-times td.all-out-of-office { background-image: url(images/freebusy-colors.png); background-position: top right; background-repeat: no-repeat; } -#schedule-freebusy-times td div.all-tentative { +#schedule-freebusy-times td.all-tentative { background-position: right -40px; } -#schedule-freebusy-times td div.all-out-of-office { +#schedule-freebusy-times td.all-out-of-office { background-position: right -80px; } @@ -1412,8 +1411,7 @@ td.topalign { .attendees-list .spacer, #schedule-freebusy-times tr.spacer td { background: 0; - padding: 0; - height: 10px; + font-size: 50%; } #schedule-freebusy-times { @@ -1426,15 +1424,6 @@ td.topalign { border: 1px solid #ccc; } -#schedule-freebusy-times tbody td { - padding: 0; - height: 20px; -} - -#schedule-freebusy-times tbody td div { - height: 100%; -} - #attendees-freebusy-table div.timesheader, #schedule-freebusy-times tr.times td { min-width: 30px; @@ -1452,10 +1441,6 @@ td.topalign { cursor: pointer; } -#schedule-freebusy-times #fbrowall td { - border-bottom: none; -} - #schedule-event-time { position: absolute; border: 2px solid #333;