Merge branch 'master' of ssh://git.kolabsys.com/git/roundcube
This commit is contained in:
commit
1b9a25e84d
8 changed files with 121 additions and 44 deletions
|
@ -97,11 +97,13 @@ class calendar extends rcube_plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->rc->task == 'calendar' && $this->rc->action != 'save-pref') {
|
if ($this->rc->task == 'calendar' && $this->rc->action != 'save-pref') {
|
||||||
$this->load_driver();
|
if ($this->rc->action != 'upload') {
|
||||||
|
$this->load_driver();
|
||||||
|
|
||||||
// load iCalendar functions
|
// load iCalendar functions
|
||||||
require($this->home . '/lib/calendar_ical.php');
|
require($this->home . '/lib/calendar_ical.php');
|
||||||
$this->ical = new calendar_ical($this->rc, $this->driver);
|
$this->ical = new calendar_ical($this->rc, $this->driver);
|
||||||
|
}
|
||||||
|
|
||||||
// register calendar actions
|
// register calendar actions
|
||||||
$this->register_action('index', array($this, 'calendar_view'));
|
$this->register_action('index', array($this, 'calendar_view'));
|
||||||
|
@ -587,7 +589,7 @@ class calendar extends rcube_plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Construct the ics file for exporting events to iCalendar format;
|
||||||
*/
|
*/
|
||||||
function export_events()
|
function export_events()
|
||||||
{
|
{
|
||||||
|
@ -595,7 +597,7 @@ class calendar extends rcube_plugin
|
||||||
$end = get_input_value('end', RCUBE_INPUT_GET);
|
$end = get_input_value('end', RCUBE_INPUT_GET);
|
||||||
if (!$start) $start = mktime(0, 0, 0, 1, date('n'), date('Y')-1);
|
if (!$start) $start = mktime(0, 0, 0, 1, date('n'), date('Y')-1);
|
||||||
if (!$end) $end = mktime(0, 0, 0, 31, 12, date('Y')+10);
|
if (!$end) $end = mktime(0, 0, 0, 31, 12, date('Y')+10);
|
||||||
$events = $this->driver->load_events($start, $end, get_input_value('source', RCUBE_INPUT_GET));
|
$events = $this->driver->load_events($start, $end, null, get_input_value('source', RCUBE_INPUT_GET), 0);
|
||||||
|
|
||||||
header("Content-Type: text/calendar");
|
header("Content-Type: text/calendar");
|
||||||
header("Content-Disposition: inline; filename=calendar.ics");
|
header("Content-Disposition: inline; filename=calendar.ics");
|
||||||
|
@ -1156,6 +1158,26 @@ class calendar extends rcube_plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
$event['attachments'] = $attachments;
|
$event['attachments'] = $attachments;
|
||||||
|
|
||||||
|
// check for organizer in attendees
|
||||||
|
if ($event['attendees']) {
|
||||||
|
$identity = $this->rc->user->get_identity();
|
||||||
|
$organizer = $owner = false;
|
||||||
|
foreach ($event['attendees'] as $i => $attendee) {
|
||||||
|
if ($attendee['role'] == 'ORGANIZER')
|
||||||
|
$organizer = true;
|
||||||
|
if ($attendee['email'] == $identity['email'])
|
||||||
|
$owner = $i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set owner as organizer if yet missing
|
||||||
|
if (!$organizer && $owner !== false) {
|
||||||
|
$event['attendees'][$i]['role'] = 'ORGANIZER';
|
||||||
|
}
|
||||||
|
else if (!$organizer && $identity['email']) {
|
||||||
|
$event['attendees'][] = array('role' => 'ORGANIZER', 'name' => $identity['name'], 'email' => $identity['email'], 'status' => 'ACCEPTED');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -96,12 +96,12 @@ function rcube_calendar_ui(settings)
|
||||||
// we use the utility function from datepicker to parse dates
|
// we use the utility function from datepicker to parse dates
|
||||||
var date = date ? $.datepicker.parseDate(datepicker_settings.dateFormat, date, datepicker_settings) : new Date();
|
var date = date ? $.datepicker.parseDate(datepicker_settings.dateFormat, date, datepicker_settings) : new Date();
|
||||||
|
|
||||||
var time_arr = time.replace(/\s*[ap]m?/i, '').replace(/0([0-9])/g, '$1').split(/[:.]/);
|
var time_arr = time.replace(/\s*[ap][.m]*/i, '').replace(/0([0-9])/g, '$1').split(/[:.]/);
|
||||||
if (!isNaN(time_arr[0])) {
|
if (!isNaN(time_arr[0])) {
|
||||||
date.setHours(time_arr[0]);
|
date.setHours(time_arr[0]);
|
||||||
if (time.match(/pm?/i) && date.getHours() < 12)
|
if (time.match(/p[.m]*/i) && date.getHours() < 12)
|
||||||
date.setHours(parseInt(time_arr[0]) + 12);
|
date.setHours(parseInt(time_arr[0]) + 12);
|
||||||
else if (date.getHours() == 12)
|
else if (time.match(/a[.m]*/i) && date.getHours() == 12)
|
||||||
date.setHours(0);
|
date.setHours(0);
|
||||||
}
|
}
|
||||||
if (!isNaN(time_arr[1]))
|
if (!isNaN(time_arr[1]))
|
||||||
|
@ -256,7 +256,7 @@ function rcube_calendar_ui(settings)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (calendar.attachments) {
|
else if (calendar.attachments) {
|
||||||
// fetch attachments, some drivers doesn't set 'attachments' popr of the event
|
// fetch attachments, some drivers doesn't set 'attachments' prop of the event?
|
||||||
}
|
}
|
||||||
|
|
||||||
// list event attendees
|
// list event attendees
|
||||||
|
@ -456,7 +456,7 @@ function rcube_calendar_ui(settings)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$('#edit-attachments > ul').empty();
|
$('#edit-attachments > ul').empty();
|
||||||
// fetch attachments, some drivers doesn't set 'attachments' array for event
|
// fetch attachments, some drivers doesn't set 'attachments' array for event?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -489,7 +489,8 @@ function rcube_calendar_ui(settings)
|
||||||
recurrence: '',
|
recurrence: '',
|
||||||
alarms: '',
|
alarms: '',
|
||||||
attendees: event_attendees,
|
attendees: event_attendees,
|
||||||
deleted_attachments: rcmail.env.deleted_attachments
|
deleted_attachments: rcmail.env.deleted_attachments,
|
||||||
|
attachments: []
|
||||||
};
|
};
|
||||||
|
|
||||||
// serialize alarm settings
|
// serialize alarm settings
|
||||||
|
@ -504,11 +505,9 @@ function rcube_calendar_ui(settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
// uploaded attachments list
|
// uploaded attachments list
|
||||||
var attachments = [];
|
|
||||||
for (var i in rcmail.env.attachments)
|
for (var i in rcmail.env.attachments)
|
||||||
if (i.match(/^rcmfile([0-9a-z]+)/))
|
if (i.match(/^rcmfile(.+)/))
|
||||||
attachments.push(RegExp.$1);
|
data.attachments.push(RegExp.$1);
|
||||||
data.attachments = attachments;
|
|
||||||
|
|
||||||
// read attendee roles
|
// read attendee roles
|
||||||
$('select.edit-attendee-role').each(function(i, elem){
|
$('select.edit-attendee-role').each(function(i, elem){
|
||||||
|
|
|
@ -191,9 +191,10 @@ class kolab_calendar
|
||||||
* @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)
|
||||||
* @param string Search query (optional)
|
* @param string Search query (optional)
|
||||||
|
* @param boolean Strip virtual events (optional)
|
||||||
* @return array A list of event records
|
* @return array A list of event records
|
||||||
*/
|
*/
|
||||||
public function list_events($start, $end, $search = null)
|
public function list_events($start, $end, $search = null, $virtual = 1)
|
||||||
{
|
{
|
||||||
$this->_fetch_events();
|
$this->_fetch_events();
|
||||||
|
|
||||||
|
@ -224,7 +225,7 @@ class kolab_calendar
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolve recurring events
|
// resolve recurring events
|
||||||
if ($event['recurrence']) {
|
if ($event['recurrence'] && $virtual == 1) {
|
||||||
$events = array_merge($events, $this->_get_recurring_events($event, $start, $end));
|
$events = array_merge($events, $this->_get_recurring_events($event, $start, $end));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -505,9 +505,10 @@ class kolab_driver extends calendar_driver
|
||||||
* @param integer Event's new end (unix timestamp)
|
* @param integer Event's new end (unix timestamp)
|
||||||
* @param string Search query (optional)
|
* @param string Search query (optional)
|
||||||
* @param mixed List of calendar IDs to load events from (either as array or comma-separated string)
|
* @param mixed List of calendar IDs to load events from (either as array or comma-separated string)
|
||||||
|
* @param boolean Strip virtual events (optional)
|
||||||
* @return array A list of event records
|
* @return array A list of event records
|
||||||
*/
|
*/
|
||||||
public function load_events($start, $end, $search = null, $calendars = null)
|
public function load_events($start, $end, $search = null, $calendars = null, $virtual = 1)
|
||||||
{
|
{
|
||||||
if ($calendars && is_string($calendars))
|
if ($calendars && is_string($calendars))
|
||||||
$calendars = explode(',', $calendars);
|
$calendars = explode(',', $calendars);
|
||||||
|
@ -517,7 +518,7 @@ class kolab_driver extends calendar_driver
|
||||||
if ($calendars && !in_array($cid, $calendars))
|
if ($calendars && !in_array($cid, $calendars))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
$events = array_merge($events, $this->calendars[$cid]->list_events($start, $end, $search));
|
$events = array_merge($events, $this->calendars[$cid]->list_events($start, $end, $search, $virtual));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $events;
|
return $events;
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
| |
|
| |
|
||||||
+-------------------------------------------------------------------------+
|
+-------------------------------------------------------------------------+
|
||||||
| Author: Lazlo Westerhof <hello@lazlo.me> |
|
| Author: Lazlo Westerhof <hello@lazlo.me> |
|
||||||
|
| Bogomil "Bogo" Shopov <shopov@kolabsys.com> |
|
||||||
+-------------------------------------------------------------------------+
|
+-------------------------------------------------------------------------+
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -55,26 +56,32 @@ class calendar_ical
|
||||||
public function export($events)
|
public function export($events)
|
||||||
{
|
{
|
||||||
if (!empty($this->rc->user->ID)) {
|
if (!empty($this->rc->user->ID)) {
|
||||||
$ical = "BEGIN:VCALENDAR\n";
|
$ical = "BEGIN:VCALENDAR\r\n";
|
||||||
$ical .= "VERSION:2.0\n";
|
$ical .= "VERSION:2.0\r\n";
|
||||||
$ical .= "PRODID:-//Roundcube Webmail " . RCMAIL_VERSION . "//NONSGML Calendar//EN\n";
|
$ical .= "PRODID:-//Roundcube Webmail " . RCMAIL_VERSION . "//NONSGML Calendar//EN\r\n";
|
||||||
$ical .= "CALSCALE:GREGORIAN\n";
|
$ical .= "CALSCALE:GREGORIAN\r\n";
|
||||||
|
|
||||||
foreach ($events as $event) {
|
foreach ($events as $event) {
|
||||||
$ical .= "BEGIN:VEVENT\n";
|
$ical .= "BEGIN:VEVENT\r\n";
|
||||||
$ical .= "UID:" . self::escpape($event['uid']) . "\n";
|
$ical .= "UID:" . self::escpape($event['uid']) . "\r\n";
|
||||||
$ical .= "DTSTART:" . gmdate('Ymd\THis\Z', $event['start']) . "\n";
|
$ical .= "DTSTART:" . gmdate('Ymd\THis\Z', $event['start']) . "\r\n";
|
||||||
$ical .= "DTEND:" . gmdate('Ymd\THis\Z', $event['end']) . "\n";
|
$ical .= "DTEND:" . gmdate('Ymd\THis\Z', $event['end']) . "\r\n";
|
||||||
$ical .= "SUMMARY:" . self::escpape($event['title']) . "\n";
|
$ical .= "SUMMARY:" . self::escpape($event['title']) . "\r\n";
|
||||||
$ical .= "DESCRIPTION:" . self::escpape($event['description']) . "\n";
|
$ical .= "DESCRIPTION:" . wordwrap(self::escpape($event['description']),75,'\r\n ') . "\r\n";
|
||||||
|
|
||||||
|
if (!empty($event['attendees'])){
|
||||||
|
|
||||||
|
$ical .= $this->_get_attendees($event['attendees']);
|
||||||
|
}
|
||||||
|
|
||||||
if (!empty($event['location'])) {
|
if (!empty($event['location'])) {
|
||||||
$ical .= "LOCATION:" . self::escpape($event['location']) . "\n";
|
$ical .= "LOCATION:" . self::escpape($event['location']) . "\r\n";
|
||||||
}
|
}
|
||||||
if ($event['recurrence']) {
|
if ($event['recurrence']) {
|
||||||
$ical .= "RRULE:" . calendar::to_rrule($event['recurrence']) . "\n";
|
$ical .= "RRULE:" . calendar::to_rrule($event['recurrence']) . "\r\n";
|
||||||
}
|
}
|
||||||
if(!empty($event['categories'])) {
|
if(!empty($event['categories'])) {
|
||||||
$ical .= "CATEGORIES:" . self::escpape(strtoupper($event['categories'])) . "\n";
|
$ical .= "CATEGORIES:" . self::escpape(strtoupper($event['categories'])) . "\r\n";
|
||||||
}
|
}
|
||||||
if ($event['sensitivity'] > 0) {
|
if ($event['sensitivity'] > 0) {
|
||||||
$ical .= "X-CALENDARSERVER-ACCESS:CONFIDENTIAL";
|
$ical .= "X-CALENDARSERVER-ACCESS:CONFIDENTIAL";
|
||||||
|
@ -84,16 +91,16 @@ class calendar_ical
|
||||||
$val = calendar::parse_alaram_value($trigger);
|
$val = calendar::parse_alaram_value($trigger);
|
||||||
|
|
||||||
$ical .= "BEGIN:VALARM\n";
|
$ical .= "BEGIN:VALARM\n";
|
||||||
if ($val[1]) $ical .= "TRIGGER:" . preg_replace('/^([-+])(.+)/', '\\1PT\\2', $trigger) . "\n";
|
if ($val[1]) $ical .= "TRIGGER:" . preg_replace('/^([-+])(.+)/', '\\1PT\\2', $trigger) . "\r\n";
|
||||||
else $ical .= "TRIGGER;VALUE=DATE-TIME:" . gmdate('Ymd\THis\Z', $val[0]) . "\n";
|
else $ical .= "TRIGGER;VALUE=DATE-TIME:" . gmdate('Ymd\THis\Z', $val[0]) . "\r\n";
|
||||||
if ($action) $ical .= "ACTION:" . self::escpape(strtoupper($action)) . "\n";
|
if ($action) $ical .= "ACTION:" . self::escpape(strtoupper($action)) . "\r\n";
|
||||||
$ical .= "END:VALARM\n";
|
$ical .= "END:VALARM\n";
|
||||||
}
|
}
|
||||||
$ical .= "TRANSP:" . ($event['free_busy'] == 'free' ? 'TRANSPARENT' : 'OPAQUE') . "\n";
|
$ical .= "TRANSP:" . ($event['free_busy'] == 'free' ? 'TRANSPARENT' : 'OPAQUE') . "\r\n";
|
||||||
|
|
||||||
// TODO: export attachments
|
// TODO: export attachments
|
||||||
|
|
||||||
$ical .= "END:VEVENT\n";
|
$ical .= "END:VEVENT\r\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
$ical .= "END:VCALENDAR";
|
$ical .= "END:VCALENDAR";
|
||||||
|
@ -106,4 +113,45 @@ class calendar_ical
|
||||||
{
|
{
|
||||||
return preg_replace('/(?<!\\\\)([\:\;\,\\n\\r])/', '\\\$1', $str);
|
return preg_replace('/(?<!\\\\)([\:\;\,\\n\\r])/', '\\\$1', $str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct the orginizer of the event.
|
||||||
|
* @param Array Attendees and roles
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function _get_attendees($ats)
|
||||||
|
{
|
||||||
|
$organizer = "";
|
||||||
|
$attendees = "";
|
||||||
|
foreach ($ats as $at){
|
||||||
|
|
||||||
|
if ($at['role']=="ORGANIZER"){
|
||||||
|
//I am an orginizer
|
||||||
|
$organizer .= "ORGANIZER;";
|
||||||
|
if (!empty($at['name']))
|
||||||
|
$organizer .="CN=".$at['name'].":";
|
||||||
|
//handling limitations according to rfc2445#section-4.1
|
||||||
|
$organizer .="MAILTO:"."\r\n ".$at['email'];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}else{
|
||||||
|
//I am an attendee
|
||||||
|
$attendees .= "ATTENDEE;ROLE=".$at['role'].";PARTSTAT=".$at['status'];
|
||||||
|
$attendees .= "\r\n "; ////handling limitations according to rfc2445#section-4.1
|
||||||
|
if (!empty($at['name']))
|
||||||
|
$attendees .=";CN=".$at['name'].":";
|
||||||
|
|
||||||
|
$attendees .="MAILTO:".$at['email']."\r\n";
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return $organizer."\r\n".$attendees;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -279,7 +279,7 @@ class calendar_ui
|
||||||
$this->calendar->gettext(array('name' => $label, 'vars' => array('min' => $n % 60, 'hrs' => intval($n / 60))))));
|
$this->calendar->gettext(array('name' => $label, 'vars' => array('min' => $n % 60, 'hrs' => intval($n / 60))))));
|
||||||
}
|
}
|
||||||
|
|
||||||
return html::tag('ul', $attrib, join("\n", $items));
|
return html::tag('ul', $attrib, join("\n", $items), html::$common_attrib);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -57,10 +57,10 @@ $labels['selectdate'] = 'Select date';
|
||||||
$labels['printdescriptions'] = 'Print descriptions';
|
$labels['printdescriptions'] = 'Print descriptions';
|
||||||
|
|
||||||
// alarm/reminder settings
|
// alarm/reminder settings
|
||||||
$labels['alarmemail'] = 'Send E-mail';
|
$labels['alarmemail'] = 'Send Email';
|
||||||
$labels['alarmdisplay'] = 'Show message';
|
$labels['alarmdisplay'] = 'Show message';
|
||||||
$labels['alarmdisplayoption'] = 'Message';
|
$labels['alarmdisplayoption'] = 'Message';
|
||||||
$labels['alarmemailoption'] = 'E-mail';
|
$labels['alarmemailoption'] = 'Email';
|
||||||
$labels['alarmat'] = 'at $datetime';
|
$labels['alarmat'] = 'at $datetime';
|
||||||
$labels['trigger@'] = 'on date';
|
$labels['trigger@'] = 'on date';
|
||||||
$labels['trigger-M'] = 'minutes before';
|
$labels['trigger-M'] = 'minutes before';
|
||||||
|
|
|
@ -160,7 +160,8 @@ pre {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#agendalist td, th {
|
#agendalist td,
|
||||||
|
#agendalist th {
|
||||||
border-right: 1px solid #C1DAD7;
|
border-right: 1px solid #C1DAD7;
|
||||||
border-bottom: 1px solid #C1DAD7;
|
border-bottom: 1px solid #C1DAD7;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
@ -759,6 +760,11 @@ a.alarm-action-snooze:after {
|
||||||
padding-right: 0.5em;
|
padding-right: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ui-datepicker th {
|
||||||
|
padding: 0.3em 0;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.ui-datepicker td span,
|
.ui-datepicker td span,
|
||||||
.ui-datepicker td a {
|
.ui-datepicker td a {
|
||||||
padding-left: 0.1em;
|
padding-left: 0.1em;
|
||||||
|
|
Loading…
Add table
Reference in a new issue