Merge branch 'master' of ssh://git.kolabsys.com/git/roundcube
This commit is contained in:
commit
a881405e40
14 changed files with 331 additions and 93 deletions
|
@ -193,6 +193,7 @@ class calendar extends rcube_plugin
|
|||
$this->register_handler('plugin.attendees_list', array($this->ui, 'attendees_list'));
|
||||
$this->register_handler('plugin.attendees_form', array($this->ui, 'attendees_form'));
|
||||
$this->register_handler('plugin.attendees_freebusy_table', array($this->ui, 'attendees_freebusy_table'));
|
||||
$this->register_handler('plugin.edit_attendees_notify', array($this->ui, 'edit_attendees_notify'));
|
||||
$this->register_handler('plugin.edit_recurring_warning', array($this->ui, 'recurring_event_warning'));
|
||||
$this->register_handler('plugin.searchform', array($this->rc->output, 'search_form')); // use generic method from rcube_template
|
||||
|
||||
|
@ -485,6 +486,10 @@ class calendar extends rcube_plugin
|
|||
$action = get_input_value('action', RCUBE_INPUT_GPC);
|
||||
$event = get_input_value('e', RCUBE_INPUT_POST);
|
||||
$success = $reload = $got_msg = false;
|
||||
|
||||
// read old event data in order to find changes
|
||||
if ($event['_notify'])
|
||||
$old = $this->driver->get_event($event);
|
||||
|
||||
switch ($action) {
|
||||
case "new":
|
||||
|
@ -558,6 +563,18 @@ class calendar extends rcube_plugin
|
|||
$success |= $this->driver->dismiss_alarm($id, $event['snooze']);
|
||||
break;
|
||||
}
|
||||
|
||||
// send out notifications
|
||||
if ($success && $event['_notify'] && $event['attendees']) {
|
||||
// make sure we have the complete record
|
||||
$event = $this->driver->get_event($event);
|
||||
|
||||
// only notify if data really changed (TODO: do diff check on client already)
|
||||
if (self::event_diff($event, $old)) {
|
||||
if ($this->notify_attendees($event, $old) < 0)
|
||||
$this->rc->output->show_message('calendar.errornotifying', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// show confirmation/error message
|
||||
if (!$got_msg) {
|
||||
|
@ -1217,7 +1234,117 @@ class calendar extends rcube_plugin
|
|||
unset($_SESSION['event_session']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send out an invitation/notification to all event attendees
|
||||
*/
|
||||
private function notify_attendees($event, $old)
|
||||
{
|
||||
$sent = 0;
|
||||
$myself = $this->rc->user->get_identity();
|
||||
$from = rcube_idn_to_ascii($myself['email']);
|
||||
$sender = format_email_recipient($from, $myself['name']);
|
||||
|
||||
// compose multipart message using PEAR:Mail_Mime
|
||||
$message = new Mail_mime("\r\n");
|
||||
$message->setParam('text_encoding', 'quoted-printable');
|
||||
$message->setParam('head_encoding', 'quoted-printable');
|
||||
$message->setParam('head_charset', RCMAIL_CHARSET);
|
||||
$message->setParam('text_charset', RCMAIL_CHARSET);
|
||||
|
||||
// compose common headers array
|
||||
$headers = array(
|
||||
'From' => $sender,
|
||||
'Date' => rcmail_user_date(),
|
||||
'Message-ID' => rcmail_gen_message_id(),
|
||||
'X-Sender' => $from,
|
||||
);
|
||||
if ($agent = $this->rc->config->get('useragent'))
|
||||
$headers['User-Agent'] = $agent;
|
||||
|
||||
|
||||
// attach ics file for this event
|
||||
$vcal = $this->ical->export(array($event), 'REQUEST');
|
||||
$message->addAttachment($vcal, 'text/calendar', 'event.ics', false, '8bit', 'attachment', RCMAIL_CHARSET);
|
||||
|
||||
// list existing attendees from $old event
|
||||
$old_attendees = array();
|
||||
foreach ((array)$old['attendees'] as $attendee) {
|
||||
$old_attendees[] = $attendee['email'];
|
||||
}
|
||||
|
||||
// compose a list of all event attendees
|
||||
$attendees_list = array();
|
||||
foreach ((array)$event['attendees'] as $attendee) {
|
||||
$attendees_list[] = ($attendee['name'] && $attendee['email']) ?
|
||||
$attendee['name'] . ' <' . $attendee['email'] . '>' :
|
||||
($attendee['name'] ? $attendee['name'] : $attendee['email']);
|
||||
}
|
||||
|
||||
// send to every attendee
|
||||
foreach ((array)$event['attendees'] as $attendee) {
|
||||
// skip myself for obvious reasons
|
||||
if (!$attendee['email'] || $attendee['email'] == $myself['email'])
|
||||
continue;
|
||||
|
||||
$is_new = !in_array($attendee['email'], $old_attendees);
|
||||
$mailto = rcube_idn_to_ascii($attendee['email']);
|
||||
$headers['To'] = format_email_recipient($mailto, $attendee['name']);
|
||||
|
||||
$headers['Subject'] = $this->gettext(array(
|
||||
'name' => $is_new ? 'invitationsubject' : 'eventupdatesubject',
|
||||
'vars' => array('title' => $event['title']),
|
||||
));
|
||||
|
||||
// compose message body
|
||||
$body = $this->gettext(array(
|
||||
'name' => $is_new ? 'invitationmailbody' : 'eventupdatemailbody',
|
||||
'vars' => array(
|
||||
'title' => $event['title'],
|
||||
'date' => $this->event_date_text($event),
|
||||
'attendees' => join(', ', $attendees_list),
|
||||
)
|
||||
));
|
||||
|
||||
$message->headers($headers);
|
||||
$message->setTXTBody(rc_wordwrap($body, 75, "\r\n"));
|
||||
|
||||
// finally send the message
|
||||
if (rcmail_deliver_message($message, $from, $mailto, $smtp_error))
|
||||
$sent++;
|
||||
else
|
||||
$sent = -100;
|
||||
}
|
||||
|
||||
return $sent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose a date string for the given event
|
||||
*/
|
||||
public function event_date_text($event)
|
||||
{
|
||||
$fromto = '';
|
||||
$duration = $event['end'] - $event['start'];
|
||||
$date_format = self::to_php_date_format($this->rc->config->get('calendar_date_format'));
|
||||
$time_format = self::to_php_date_format($this->rc->config->get('calendar_time_format'));
|
||||
|
||||
if ($event['allday']) {
|
||||
$fromto = format_date($event['start'], $date_format) .
|
||||
($duration > 86400 || date('d', $event['start']) != date('d', $event['end']) ? ' - ' . format_date($event['end'], $date_format) : '');
|
||||
}
|
||||
else if ($duration < 86400 && date('d', $event['start']) == date('d', $event['end'])) {
|
||||
$fromto = format_date($event['start'], $date_format) . ' ' . format_date($event['start'], $time_format) .
|
||||
' - ' . format_date($event['end'], $time_format);
|
||||
}
|
||||
else {
|
||||
$fromto = format_date($event['start'], $date_format) . ' ' . format_date($event['start'], $time_format) .
|
||||
' - ' . format_date($event['end'], $date_format) . ' ' . format_date($event['end'], $time_format);
|
||||
}
|
||||
|
||||
return $fromto;
|
||||
}
|
||||
|
||||
/**
|
||||
* Echo simple free/busy status text for the given user and time range
|
||||
*/
|
||||
|
@ -1333,4 +1460,27 @@ class calendar extends rcube_plugin
|
|||
$this->rc->output->send("calendar.print");
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two event objects and return differing properties
|
||||
*
|
||||
* @param array Event A
|
||||
* @param array Event B
|
||||
* @return array List of differing event properties
|
||||
*/
|
||||
public static function event_diff($a, $b)
|
||||
{
|
||||
$diff = array();
|
||||
$ignore = array('attachments' => 1);
|
||||
foreach (array_unique(array_merge(array_keys($a), array_keys($b))) as $key) {
|
||||
if (!$ignore[$key] && $a[$key] != $b[$key])
|
||||
$diff[] = $key;
|
||||
}
|
||||
|
||||
// only compare number of attachments
|
||||
if (count($a['attachments']) != count($b['attachments']))
|
||||
$diff[] = 'attachments';
|
||||
|
||||
return $diff;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -369,6 +369,9 @@ function rcube_calendar_ui(settings)
|
|||
var enddate = $('#edit-enddate').val($.fullCalendar.formatDate(event.end, settings['date_format']));
|
||||
var endtime = $('#edit-endtime').val($.fullCalendar.formatDate(event.end, settings['time_format'])).show();
|
||||
var allday = $('#edit-allday').get(0);
|
||||
var notify = $('#edit-attendees-donotify').get(0);
|
||||
var invite = $('#edit-attendees-invite').get(0);
|
||||
notify.checked = invite.checked = true; // enable notification by default
|
||||
|
||||
if (event.allDay) {
|
||||
starttime.val("00:00").hide();
|
||||
|
@ -461,7 +464,11 @@ function rcube_calendar_ui(settings)
|
|||
if (calendar.attendees && event.attendees) {
|
||||
for (var j=0; j < event.attendees.length; j++)
|
||||
add_attendee(event.attendees[j], true);
|
||||
$('#edit-attendees-notify').show();
|
||||
}
|
||||
else
|
||||
$('#edit-attendees-notify').hide();
|
||||
|
||||
$('#edit-attendee-schedule')[(calendar.freebusy?'show':'hide')]();
|
||||
|
||||
// attachments
|
||||
|
@ -535,6 +542,11 @@ function rcube_calendar_ui(settings)
|
|||
if (data.attendees[i])
|
||||
data.attendees[i].role = $(elem).val();
|
||||
});
|
||||
|
||||
// tell server to send notifications
|
||||
if (data.attendees.length && ((event.id && notify.checked) || (!event.id && invite.checked))) {
|
||||
data._notify = 1;
|
||||
}
|
||||
|
||||
// gather recurrence settings
|
||||
var freq;
|
||||
|
@ -624,6 +636,7 @@ function rcube_calendar_ui(settings)
|
|||
title.select();
|
||||
};
|
||||
|
||||
// open a dialog to display detailed free-busy information and to find free slots
|
||||
var event_freebusy_dialog = function()
|
||||
{
|
||||
var $dialog = $('#eventfreebusy').dialog('close');
|
||||
|
@ -1056,7 +1069,7 @@ function rcube_calendar_ui(settings)
|
|||
// parse name/email pairs
|
||||
var item, email, name, success = false;
|
||||
for (var i=0; i < names.length; i++) {
|
||||
email = name = null;
|
||||
email = name = '';
|
||||
item = $.trim(names[i]);
|
||||
|
||||
if (!item.length) {
|
||||
|
@ -1525,12 +1538,17 @@ function rcube_calendar_ui(settings)
|
|||
.data('id', id);
|
||||
}
|
||||
|
||||
if (!cal.readonly && !this.selected_calendar && (!settings.default_calendar || settings.default_calendar == id)) {
|
||||
if (!cal.readonly && !this.selected_calendar) {
|
||||
this.selected_calendar = id;
|
||||
rcmail.enable_command('addevent', true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// select default calendar
|
||||
if (settings.default_calendar && this.calendars[settings.default_calendar] && !this.calendars[settings.default_calendar].readonly)
|
||||
this.selected_calendar = settings.default_calendar;
|
||||
|
||||
|
||||
// initalize the fullCalendar plugin
|
||||
var fc = $('#calendar').fullCalendar({
|
||||
header: {
|
||||
|
@ -1904,6 +1922,11 @@ function rcube_calendar_ui(settings)
|
|||
input.val('');
|
||||
});
|
||||
|
||||
// keep these two checkboxes in sync
|
||||
$('#edit-attendees-donotify, #edit-attendees-invite').click(function(){
|
||||
$('#edit-attendees-donotify, #edit-attendees-invite').prop('checked', this.checked);
|
||||
});
|
||||
|
||||
$('#edit-attendee-schedule').click(function(){
|
||||
event_freebusy_dialog();
|
||||
});
|
||||
|
|
|
@ -180,6 +180,16 @@ abstract class calendar_driver
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return data of a single event
|
||||
*
|
||||
* @param array Hash array with event properties:
|
||||
* id: Event identifier
|
||||
* calendar: Calendar identifier
|
||||
* @return array Event object as hash array
|
||||
*/
|
||||
abstract function get_event($event);
|
||||
|
||||
/**
|
||||
* Get events from source.
|
||||
*
|
||||
|
|
|
@ -173,7 +173,7 @@ class database_driver extends calendar_driver
|
|||
* Add a single event to the database
|
||||
*
|
||||
* @param array Hash array with event properties
|
||||
* @see Driver:new_event()
|
||||
* @see calendar_driver::new_event()
|
||||
*/
|
||||
public function new_event($event)
|
||||
{
|
||||
|
@ -233,7 +233,7 @@ class database_driver extends calendar_driver
|
|||
* Update an event entry with the given data
|
||||
*
|
||||
* @param array Hash array with event properties
|
||||
* @see Driver:edit_event()
|
||||
* @see calendar_driver::edit_event()
|
||||
*/
|
||||
public function edit_event($event)
|
||||
{
|
||||
|
@ -502,7 +502,7 @@ class database_driver extends calendar_driver
|
|||
* Move a single event
|
||||
*
|
||||
* @param array Hash array with event properties
|
||||
* @see Driver:move_event()
|
||||
* @see calendar_driver::move_event()
|
||||
*/
|
||||
public function move_event($event)
|
||||
{
|
||||
|
@ -514,7 +514,7 @@ class database_driver extends calendar_driver
|
|||
* Resize a single event
|
||||
*
|
||||
* @param array Hash array with event properties
|
||||
* @see Driver:resize_event()
|
||||
* @see calendar_driver::resize_event()
|
||||
*/
|
||||
public function resize_event($event)
|
||||
{
|
||||
|
@ -528,7 +528,7 @@ class database_driver extends calendar_driver
|
|||
* @param array Hash array with event properties
|
||||
* @param boolean Remove record irreversible (@TODO)
|
||||
*
|
||||
* @see Driver:remove_event()
|
||||
* @see calendar_driver::remove_event()
|
||||
*/
|
||||
public function remove_event($event, $force = true)
|
||||
{
|
||||
|
@ -601,13 +601,15 @@ class database_driver extends calendar_driver
|
|||
|
||||
/**
|
||||
* Return data of a specific event
|
||||
* @param string Event ID
|
||||
* @param mixed Hash array with event properties or event ID
|
||||
* @return array Hash array with event properties
|
||||
*/
|
||||
public function get_event($id)
|
||||
public function get_event($event)
|
||||
{
|
||||
static $cache = array();
|
||||
|
||||
$id = is_array($event) ? $event['id'] : $event;
|
||||
|
||||
if ($cache[$id])
|
||||
return $cache[$id];
|
||||
|
||||
|
@ -630,7 +632,7 @@ class database_driver extends calendar_driver
|
|||
/**
|
||||
* Get event data
|
||||
*
|
||||
* @see Driver:load_events()
|
||||
* @see calendar_driver::load_events()
|
||||
*/
|
||||
public function load_events($start, $end, $query = null, $calendars = null)
|
||||
{
|
||||
|
@ -726,7 +728,7 @@ class database_driver extends calendar_driver
|
|||
/**
|
||||
* Get a list of pending alarms to be displayed to the user
|
||||
*
|
||||
* @see Driver:pending_alarms()
|
||||
* @see calendar_driver::pending_alarms()
|
||||
*/
|
||||
public function pending_alarms($time, $calendars = null)
|
||||
{
|
||||
|
@ -759,7 +761,7 @@ class database_driver extends calendar_driver
|
|||
/**
|
||||
* Feedback after showing/sending an alarm notification
|
||||
*
|
||||
* @see Driver:dismiss_alarm()
|
||||
* @see calendar_driver::dismiss_alarm()
|
||||
*/
|
||||
public function dismiss_alarm($event_id, $snooze = 0)
|
||||
{
|
||||
|
|
|
@ -242,7 +242,7 @@ class kolab_calendar
|
|||
/**
|
||||
* Create a new event record
|
||||
*
|
||||
* @see Driver:new_event()
|
||||
* @see calendar_driver::new_event()
|
||||
*
|
||||
* @return mixed The created record ID on success, False on error
|
||||
*/
|
||||
|
@ -263,6 +263,9 @@ class kolab_calendar
|
|||
true, false);
|
||||
$saved = false;
|
||||
}
|
||||
else {
|
||||
$this->events[$event['uid']] = $event;
|
||||
}
|
||||
|
||||
return $saved;
|
||||
}
|
||||
|
@ -270,7 +273,7 @@ class kolab_calendar
|
|||
/**
|
||||
* Update a specific event record
|
||||
*
|
||||
* @see Driver:new_event()
|
||||
* @see calendar_driver::new_event()
|
||||
* @return boolean True on success, False on error
|
||||
*/
|
||||
|
||||
|
@ -289,6 +292,7 @@ class kolab_calendar
|
|||
}
|
||||
else {
|
||||
$updated = true;
|
||||
$this->events[$event['id']] = $this->_to_rcube_event($object);
|
||||
}
|
||||
|
||||
return $updated;
|
||||
|
@ -297,7 +301,7 @@ class kolab_calendar
|
|||
/**
|
||||
* Delete an event record
|
||||
*
|
||||
* @see Driver:remove_event()
|
||||
* @see calendar_driver::remove_event()
|
||||
* @return boolean True on success, False on error
|
||||
*/
|
||||
public function delete_event($event, $force = true)
|
||||
|
@ -332,7 +336,7 @@ class kolab_calendar
|
|||
/**
|
||||
* Restore deleted event record
|
||||
*
|
||||
* @see Driver:undelete_event()
|
||||
* @see calendar_driver::undelete_event()
|
||||
* @return boolean True on success, False on error
|
||||
*/
|
||||
public function restore_event($event)
|
||||
|
|
|
@ -274,10 +274,24 @@ class kolab_driver extends calendar_driver
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Move a single event
|
||||
*
|
||||
* @see calendar_driver::get_event()
|
||||
* @return array Hash array with event properties, false if not found
|
||||
*/
|
||||
public function get_event($event)
|
||||
{
|
||||
if ($storage = $this->calendars[$event['calendar']])
|
||||
return $storage->get_event($event['id']);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a single event to the database
|
||||
*
|
||||
* @see Driver:new_event()
|
||||
* @see calendar_driver::new_event()
|
||||
*/
|
||||
public function new_event($event)
|
||||
{
|
||||
|
@ -307,7 +321,7 @@ class kolab_driver extends calendar_driver
|
|||
/**
|
||||
* Update an event entry with the given data
|
||||
*
|
||||
* @see Driver:new_event()
|
||||
* @see calendar_driver::new_event()
|
||||
* @return boolean True on success, False on error
|
||||
*/
|
||||
public function edit_event($event)
|
||||
|
@ -318,7 +332,7 @@ class kolab_driver extends calendar_driver
|
|||
/**
|
||||
* Move a single event
|
||||
*
|
||||
* @see Driver:move_event()
|
||||
* @see calendar_driver::move_event()
|
||||
* @return boolean True on success, False on error
|
||||
*/
|
||||
public function move_event($event)
|
||||
|
@ -332,7 +346,7 @@ class kolab_driver extends calendar_driver
|
|||
/**
|
||||
* Resize a single event
|
||||
*
|
||||
* @see Driver:resize_event()
|
||||
* @see calendar_driver::resize_event()
|
||||
* @return boolean True on success, False on error
|
||||
*/
|
||||
public function resize_event($event)
|
||||
|
@ -578,7 +592,7 @@ class kolab_driver extends calendar_driver
|
|||
/**
|
||||
* Get a list of pending alarms to be displayed to the user
|
||||
*
|
||||
* @see Driver:pending_alarms()
|
||||
* @see calendar_driver::pending_alarms()
|
||||
*/
|
||||
public function pending_alarms($time, $calendars = null)
|
||||
{
|
||||
|
@ -649,7 +663,7 @@ class kolab_driver extends calendar_driver
|
|||
/**
|
||||
* Feedback after showing/sending an alarm notification
|
||||
*
|
||||
* @see Driver:dismiss_alarm()
|
||||
* @see calendar_driver::dismiss_alarm()
|
||||
*/
|
||||
public function dismiss_alarm($event_id, $snooze = 0)
|
||||
{
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
|
||||
class calendar_ical
|
||||
{
|
||||
const EOL = "\r\n";
|
||||
|
||||
private $rc;
|
||||
private $driver;
|
||||
|
||||
|
@ -53,35 +55,37 @@ class calendar_ical
|
|||
* @param array Events as array
|
||||
* @return string Events in iCalendar format (http://tools.ietf.org/html/rfc5545)
|
||||
*/
|
||||
public function export($events)
|
||||
public function export($events, $method = null)
|
||||
{
|
||||
if (!empty($this->rc->user->ID)) {
|
||||
$ical = "BEGIN:VCALENDAR\r\n";
|
||||
$ical .= "VERSION:2.0\r\n";
|
||||
$ical .= "PRODID:-//Roundcube Webmail " . RCMAIL_VERSION . "//NONSGML Calendar//EN\r\n";
|
||||
$ical .= "CALSCALE:GREGORIAN\r\n";
|
||||
$ical = "BEGIN:VCALENDAR" . self::EOL;
|
||||
$ical .= "VERSION:2.0" . self::EOL;
|
||||
$ical .= "PRODID:-//Roundcube Webmail " . RCMAIL_VERSION . "//NONSGML Calendar//EN" . self::EOL;
|
||||
$ical .= "CALSCALE:GREGORIAN" . self::EOL;
|
||||
|
||||
if ($method)
|
||||
$ical .= "METHOD:" . strtoupper($method) . self::EOL;
|
||||
|
||||
foreach ($events as $event) {
|
||||
$ical .= "BEGIN:VEVENT\r\n";
|
||||
$ical .= "UID:" . self::escpape($event['uid']) . "\r\n";
|
||||
$ical .= "DTSTART:" . gmdate('Ymd\THis\Z', $event['start']) . "\r\n";
|
||||
$ical .= "DTEND:" . gmdate('Ymd\THis\Z', $event['end']) . "\r\n";
|
||||
$ical .= "SUMMARY:" . self::escpape($event['title']) . "\r\n";
|
||||
$ical .= "DESCRIPTION:" . wordwrap(self::escpape($event['description']),75,"\r\n ") . "\r\n";
|
||||
|
||||
if (!empty($event['attendees'])){
|
||||
|
||||
$ical .= $this->_get_attendees($event['attendees']);
|
||||
}
|
||||
|
||||
$ical .= "BEGIN:VEVENT" . self::EOL;
|
||||
$ical .= "UID:" . self::escpape($event['uid']) . self::EOL;
|
||||
$ical .= "DTSTART:" . gmdate('Ymd\THis\Z', $event['start']) . self::EOL;
|
||||
$ical .= "DTEND:" . gmdate('Ymd\THis\Z', $event['end']) . self::EOL;
|
||||
$ical .= "SUMMARY:" . self::escpape($event['title']) . self::EOL;
|
||||
$ical .= "DESCRIPTION:" . self::escpape($event['description']) . self::EOL;
|
||||
|
||||
if (!empty($event['attendees'])){
|
||||
$ical .= $this->_get_attendees($event['attendees']);
|
||||
}
|
||||
|
||||
if (!empty($event['location'])) {
|
||||
$ical .= "LOCATION:" . self::escpape($event['location']) . "\r\n";
|
||||
$ical .= "LOCATION:" . self::escpape($event['location']) . self::EOL;
|
||||
}
|
||||
if ($event['recurrence']) {
|
||||
$ical .= "RRULE:" . calendar::to_rrule($event['recurrence']) . "\r\n";
|
||||
$ical .= "RRULE:" . calendar::to_rrule($event['recurrence']) . self::EOL;
|
||||
}
|
||||
if(!empty($event['categories'])) {
|
||||
$ical .= "CATEGORIES:" . self::escpape(strtoupper($event['categories'])) . "\r\n";
|
||||
$ical .= "CATEGORIES:" . self::escpape(strtoupper($event['categories'])) . self::EOL;
|
||||
}
|
||||
if ($event['sensitivity'] > 0) {
|
||||
$ical .= "X-CALENDARSERVER-ACCESS:CONFIDENTIAL";
|
||||
|
@ -91,21 +95,22 @@ class calendar_ical
|
|||
$val = calendar::parse_alaram_value($trigger);
|
||||
|
||||
$ical .= "BEGIN:VALARM\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]) . "\r\n";
|
||||
if ($action) $ical .= "ACTION:" . self::escpape(strtoupper($action)) . "\r\n";
|
||||
if ($val[1]) $ical .= "TRIGGER:" . preg_replace('/^([-+])(.+)/', '\\1PT\\2', $trigger) . self::EOL;
|
||||
else $ical .= "TRIGGER;VALUE=DATE-TIME:" . gmdate('Ymd\THis\Z', $val[0]) . self::EOL;
|
||||
if ($action) $ical .= "ACTION:" . self::escpape(strtoupper($action)) . self::EOL;
|
||||
$ical .= "END:VALARM\n";
|
||||
}
|
||||
$ical .= "TRANSP:" . ($event['free_busy'] == 'free' ? 'TRANSPARENT' : 'OPAQUE') . "\r\n";
|
||||
$ical .= "TRANSP:" . ($event['free_busy'] == 'free' ? 'TRANSPARENT' : 'OPAQUE') . self::EOL;
|
||||
|
||||
// TODO: export attachments
|
||||
|
||||
$ical .= "END:VEVENT\r\n";
|
||||
$ical .= "END:VEVENT" . self::EOL;
|
||||
}
|
||||
|
||||
$ical .= "END:VCALENDAR";
|
||||
$ical .= "END:VCALENDAR" . self::EOL;
|
||||
|
||||
return $ical;
|
||||
// fold lines to 75 chars
|
||||
return rcube_vcard::rfc2425_fold($ical);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,44 +119,33 @@ class calendar_ical
|
|||
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";
|
||||
|
||||
|
||||
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'] . '"';
|
||||
$organizer .= ":mailto:". $at['email'] . self::EOL;
|
||||
}
|
||||
else {
|
||||
//I am an attendee
|
||||
$attendees .= "ATTENDEE;ROLE=" . $at['role'] . ";PARTSTAT=" . $at['status'];
|
||||
if (!empty($at['name']))
|
||||
$attendees .= ';CN="' . $at['name'] . '"';
|
||||
$attendees .= ":mailto:" . $at['email'] . self::EOL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return $organizer . $attendees;
|
||||
}
|
||||
|
||||
|
||||
return $organizer."\r\n".$attendees;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -284,6 +284,15 @@ class calendar_ui
|
|||
|
||||
return html::tag('ul', $attrib, join("\n", $items), html::$common_attrib);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function edit_attendees_notify($attrib = array())
|
||||
{
|
||||
$checkbox = new html_checkbox(array('name' => 'notify', 'id' => 'edit-attendees-donotify', 'value' => 1));
|
||||
return html::div($attrib, html::label(null, $checkbox->show(1) . ' ' . $this->calendar->gettext('sendnotifications')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the form for recurrence settings
|
||||
|
@ -581,13 +590,13 @@ class calendar_ui
|
|||
function attendees_form($attrib = array())
|
||||
{
|
||||
$input = new html_inputfield(array('name' => 'participant', 'id' => 'edit-attendee-name', 'size' => 30));
|
||||
$checkbox = new html_checkbox(array('name' => 'notify', 'id' => 'edit-attendees-notify', 'value' => 1, 'disabled' => true)); // disabled for now
|
||||
$checkbox = new html_checkbox(array('name' => 'invite', 'id' => 'edit-attendees-invite', 'value' => 1));
|
||||
|
||||
return html::div($attrib,
|
||||
html::div(null, $input->show() . " " .
|
||||
html::tag('input', array('type' => 'button', 'class' => 'button', 'id' => 'edit-attendee-add', 'value' => $this->calendar->gettext('addattendee'))) . " " .
|
||||
html::tag('input', array('type' => 'button', 'class' => 'button', 'id' => 'edit-attendee-schedule', 'value' => $this->calendar->gettext('scheduletime').'...'))) .
|
||||
html::p('attendees-notifybox', html::label(null, $checkbox->show(1) . $this->calendar->gettext('sendnotifications')))
|
||||
html::p('attendees-invitebox', html::label(null, $checkbox->show(1) . $this->calendar->gettext('sendinvitations')))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ $labels['edit'] = 'Bearbeiten';
|
|||
$labels['title'] = 'Titel';
|
||||
$labels['description'] = 'Beschreibung';
|
||||
$labels['all-day'] = 'ganztägig';
|
||||
$labels['export'] = 'Als ICS exportieren';
|
||||
$labels['export'] = 'Als iCalendar exportieren';
|
||||
$labels['category'] = 'Kategorie';
|
||||
$labels['location'] = 'Ort';
|
||||
$labels['date'] = 'Datum';
|
||||
|
|
|
@ -39,7 +39,7 @@ $labels['print'] = 'Print calendars';
|
|||
$labels['title'] = 'Summary';
|
||||
$labels['description'] = 'Description';
|
||||
$labels['all-day'] = 'all-day';
|
||||
$labels['export'] = 'Export to ICS';
|
||||
$labels['export'] = 'Export to iCalendar';
|
||||
$labels['location'] = 'Location';
|
||||
$labels['date'] = 'Date';
|
||||
$labels['start'] = 'Start';
|
||||
|
@ -103,11 +103,16 @@ $labels['availunknown'] = 'Unknown';
|
|||
$labels['availtentative'] = 'Tentative';
|
||||
$labels['availoutofoffice'] = 'Out of Office';
|
||||
$labels['scheduletime'] = 'Find availability';
|
||||
$labels['sendnotifications'] = 'Send notifications';
|
||||
$labels['sendinvitations'] = 'Send invitations';
|
||||
$labels['sendnotifications'] = 'Notify attendees about modifications';
|
||||
$labels['onlyworkinghours'] = 'Find availability within my working hours';
|
||||
$labels['prevslot'] = 'Previous Slot';
|
||||
$labels['nextslot'] = 'Next Slot';
|
||||
$labels['noslotfound'] = 'Unable to find a free time slot';
|
||||
$labels['invitationsubject'] = 'You\'ve been invited to "$title"';
|
||||
$labels['invitationmailbody'] = "*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees\n\nPlease find attached an iCalendar file with all the event details which you can import to your calendar application.";
|
||||
$labels['eventupdatesubject'] = '"$title" 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.";
|
||||
|
||||
// event dialog tabs
|
||||
$labels['tabsummary'] = 'Summary';
|
||||
|
@ -126,6 +131,7 @@ $labels['invalidcalendarproperties'] = 'Invalid calendar properties! Please set
|
|||
$labels['searchnoresults'] = 'No events found in the selected calendars.';
|
||||
$labels['successremoval'] = 'The event has been deleted successfully.';
|
||||
$labels['successrestore'] = 'The event has been restored successfully.';
|
||||
$labels['errornotifying'] = 'Failed to send notifications to event participants';
|
||||
|
||||
// recurrence form
|
||||
$labels['repeat'] = 'Repeat';
|
||||
|
|
|
@ -502,6 +502,13 @@ td.topalign {
|
|||
min-width: 5em;
|
||||
}
|
||||
|
||||
#edit-attendees-notify {
|
||||
margin: 0.3em 0;
|
||||
padding: 0.5em;
|
||||
background-color: #F7FDCB;
|
||||
border: 1px solid #C2D071;
|
||||
}
|
||||
|
||||
#edit-attendees-table {
|
||||
width: 100%;
|
||||
display: table;
|
||||
|
|
|
@ -181,6 +181,7 @@
|
|||
</div>
|
||||
</form>
|
||||
|
||||
<roundcube:object name="plugin.edit_attendees_notify" id="edit-attendees-notify" style="display:none" />
|
||||
<roundcube:object name="plugin.edit_recurring_warning" class="edit-recurring-warning" style="display:none" />
|
||||
</div>
|
||||
|
||||
|
|
|
@ -135,10 +135,17 @@ class kolab_addressbook extends rcube_plugin
|
|||
|
||||
// Add personal address sources to the list
|
||||
if ($abook_prio == self::PERSONAL_FIRST) {
|
||||
$p['sources'] = array_merge($sources, $p['sources']);
|
||||
// $p['sources'] = array_merge($sources, $p['sources']);
|
||||
// Don't use array_merge(), because if you have folders name
|
||||
// that resolve to numeric identifier it will break output array keys
|
||||
foreach ($p['sources'] as $idx => $value)
|
||||
$sources[$idx] = $value;
|
||||
$p['sources'] = $sources;
|
||||
}
|
||||
else {
|
||||
$p['sources'] = array_merge($p['sources'], $sources);
|
||||
// $p['sources'] = array_merge($p['sources'], $sources);
|
||||
foreach ($sources as $idx => $value)
|
||||
$p['sources'][$idx] = $value;
|
||||
}
|
||||
|
||||
return $p;
|
||||
|
|
|
@ -128,7 +128,7 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
}
|
||||
else {
|
||||
$acl = $this->storagefolder->getACL();
|
||||
if (is_array($acl)) {
|
||||
if (!PEAR::isError($acl) && is_array($acl)) {
|
||||
$acl = $acl[$_SESSION['username']];
|
||||
if (strpos($acl, 'i') !== false)
|
||||
$this->readonly = false;
|
||||
|
@ -1092,10 +1092,13 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
}
|
||||
}
|
||||
|
||||
$object['address'] = array();
|
||||
|
||||
foreach ($this->get_col_values('address', $contact) as $type => $values) {
|
||||
if ($this->addresstypemap[$type])
|
||||
$type = $this->addresstypemap[$type];
|
||||
|
||||
$updated = false;
|
||||
$basekey = 'addr-' . $type . '-';
|
||||
foreach ((array)$values as $adr) {
|
||||
// switch type if slot is already taken
|
||||
|
@ -1111,8 +1114,16 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
$object[$basekey . 'postal-code'] = $adr['zipcode'];
|
||||
$object[$basekey . 'region'] = $adr['region'];
|
||||
$object[$basekey . 'country'] = $adr['country'];
|
||||
|
||||
// Update existing address entry of this type
|
||||
foreach($object['address'] as $index => $address) {
|
||||
if ($address['type'] == $type) {
|
||||
$object['address'][$index] = $new_address;
|
||||
$updated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!$updated) {
|
||||
$object['address'][] = array(
|
||||
'type' => $type,
|
||||
'street' => $adr['street'],
|
||||
|
|
Loading…
Add table
Reference in a new issue