Skip to content

Commit

Permalink
Merge branch 'oakey-b1-master' into krono-organizer
Browse files Browse the repository at this point in the history
  • Loading branch information
mrubinsk committed Dec 18, 2015
2 parents 8985dc0 + 3382c1e commit 4f13f3b
Show file tree
Hide file tree
Showing 10 changed files with 197 additions and 15 deletions.
17 changes: 17 additions & 0 deletions kronolith/js/kronolith.js
Expand Up @@ -5770,6 +5770,8 @@ KronolithCore = {
}
$('kronolithEventId').clear();
$('kronolithEventCalendar').clear();
$('kronolithEventStatus').disabled = false;
$('kronolithEventAttend').hide();
$('kronolithEventTarget').setValue(Kronolith.conf.default_calendar);
$('kronolithEventDelete').hide();
$('kronolithEventStartDate').setValue(d.toString(Kronolith.conf.date_format));
Expand Down Expand Up @@ -6077,6 +6079,21 @@ KronolithCore = {
$('kronolithEventLinkExport').up('li').show();
$('kronolithEventExport').href = Kronolith.conf.URI_EVENT_EXPORT.interpolate({ id: ev.id, calendar: ev.c, type: ev.ty });

/* Attendance and Status */
if (!ev.oy) {
$('kronolithEventStatus').disabled = true;
$('kronolithEventAttend').show();
} else {
$('kronolithEventStatus').disabled = false;
$('kronolithEventAttend').hide();
}
if (ev.cr) {
$('kronolithEventAttendance').setValue(ev.cr);
}
if (ev.o) {
$('kronolithEventOrganizer').setValue(ev.o);
}

/* Alarm */
if (ev.a) {
this.enableAlarm('Event', ev.a);
Expand Down
13 changes: 9 additions & 4 deletions kronolith/lib/Ajax/Application/Handler.php
Expand Up @@ -352,10 +352,15 @@ public function saveEvent()
}

if (($result !== true) && $this->vars->sendupdates) {
$type = $event->status == Kronolith::STATUS_CANCELLED
? Kronolith::ITIP_CANCEL
: Kronolith::ITIP_REQUEST;
Kronolith::sendITipNotifications($event, $notification, $type);
if ($this->vars->attendance) {
Kronolith::sendITipNotifications($event, $notification, Kronolith::ITIP_REPLY);
}
if (!$event->organizer || Kronolith::isUserEmail($this->creator, $event->organizer)) {
$type = $event->status == Kronolith::STATUS_CANCELLED
? Kronolith::ITIP_CANCEL
: Kronolith::ITIP_REQUEST;
Kronolith::sendITipNotifications($event, $notification, $type);
}
}

// Send a CANCEL iTip for attendees that have been removed, but only if
Expand Down
10 changes: 9 additions & 1 deletion kronolith/lib/Application.php
Expand Up @@ -818,6 +818,11 @@ public function davGetObject($collection, $object)
}

/**
* Add or update an event from DAV
*
* @param string $collection An external collection ID.
* @param string $object An external object ID.
* @param string $data Icalendar data
*/
public function davPutObject($collection, $object, $data)
{
Expand Down Expand Up @@ -865,9 +870,12 @@ public function davDeleteObject($collection, $object)
} catch (Horde_Dav_Exception $e) {
}

// Send iTip messages.
// Send iTip messages unless organizer is external.
// Notifications will get lost, there is no way to return messages to
// clients.
if ($event->organizer && !Kronolith::isUserEmail($event->creator, $event->organizer)) {
return;
}
Kronolith::sendITipNotifications(
$event,
new Horde_Notification_Handler(new Horde_Notification_Storage_Object()),
Expand Down
6 changes: 3 additions & 3 deletions kronolith/lib/Driver/Sql.php
Expand Up @@ -343,7 +343,7 @@ private function _listEventsConditional(Horde_Date $startInterval = null,
}
}
$q = 'SELECT event_id, event_uid, event_description, event_location,' .
' event_private, event_status, event_attendees,' .
' event_private, event_status, event_attendees, event_organizer,' .
' event_title, event_recurcount, event_url, event_timezone,' .
' event_recurtype, event_recurenddate, event_recurinterval,' .
' event_recurdays, event_start, event_end, event_allday,' .
Expand Down Expand Up @@ -458,7 +458,7 @@ public function getEvent($eventId = null)
' event_recurdays, event_start, event_end, event_allday,' .
' event_alarm, event_alarm_methods, event_modified,' .
' event_exceptions, event_creator_id, event_resources,' .
' event_baseid, event_exceptionoriginaldate FROM ' .
' event_baseid, event_exceptionoriginaldate, event_organizer FROM ' .
'kronolith_events WHERE event_id = ? AND calendar_id = ?';

$values = array($eventId, $this->calendar);
Expand Down Expand Up @@ -499,7 +499,7 @@ public function getByUID($uid, $calendars = null, $getAll = false)
' event_recurdays, event_start, event_end, event_allday,' .
' event_alarm, event_alarm_methods, event_modified,' .
' event_exceptions, event_creator_id, event_resources, event_baseid,' .
' event_exceptionoriginaldate FROM kronolith_events' .
' event_exceptionoriginaldate, event_organizer FROM kronolith_events' .
' WHERE event_uid = ?';
$values = array((string)$uid);

Expand Down
51 changes: 46 additions & 5 deletions kronolith/lib/Event.php
Expand Up @@ -63,6 +63,13 @@ abstract class Kronolith_Event
*/
protected $_creator = null;

/**
* The email address of the organizer of the event, if known.
*
* @var string
*/
public $organizer = null;

/**
* The title of this event.
*
Expand Down Expand Up @@ -711,7 +718,9 @@ public function toiCalendar($calendar)
$vEvent->setAttribute('SUMMARY', $this->getTitle());

// Organizer
if (count($this->attendees)) {
if ($this->organizer) {
$vEvent->setAttribute('ORGANIZER', 'mailto:' . $this->organizer, array());
} elseif (count($this->attendees)) {
$name = Kronolith::getUserName($this->creator);
$email = Kronolith::getUserEmail($this->creator);
$params = array();
Expand Down Expand Up @@ -1013,6 +1022,14 @@ public function fromiCalendar($vEvent, $parseAttendees = false)
}
} catch (Horde_Icalendar_Exception $e) {}

// Organizer
try {
$organizer = $vEvent->getAttribute('ORGANIZER');
if (!empty($organizer)) {
$this->organizer = str_replace(array('MAILTO:', 'mailto:'), '', $organizer);
}
} catch (Horde_Icalendar_Exception $e) {}

// Sequence.
try {
$seq = $vEvent->getAttribute('SEQUENCE');
Expand Down Expand Up @@ -1417,6 +1434,11 @@ public function fromASAppointment(Horde_ActiveSync_Message_Appointment $message)
$this->uid = $message->getUid();
}

$organizer = $message->getOrganizer();
if ($organizer['email']) {
$this->organizer = $organizer['email'];
}

if (strlen($title = $message->getSubject())) {
$this->title = $title;
}
Expand Down Expand Up @@ -1642,7 +1664,9 @@ public function toASAppointment(array $options = array())
$message->setTimezone($this->start);

// Organizer
if (count($this->attendees)) {
if ($this->organizer) {
$message->setOrganizer(array('email' => $this->organizer));
} elseif (count($this->attendees)) {
if ($this->creator == $registry->getAuth()) {
$as_ident = $prefs->getValue('activesync_identity') == 'horde'
? $prefs->getValue('default_identity')
Expand Down Expand Up @@ -2010,11 +2034,9 @@ public function fromHash($hash)
}

// Import once we support organizers.
/*
if (!empty($hash['organizer'])) {
$this->organizer = $hash['organizer'];
}
*/

if (!empty($hash['private'])) {
$this->private = true;
Expand Down Expand Up @@ -2249,6 +2271,9 @@ public function toAlarm($time, $user = null, $prefs = null)
* - mt: meeting (Boolean true if event has attendees, false otherwise).
* - cb: created by (string describing when and who created the event).
* - mb: modified by (string describing when and who last modified event).
* - o: organizer (if known)
* - oy: organizer you
* - cr: creator's attendance response
*
* @param array $options An array of options:
*
Expand Down Expand Up @@ -2374,16 +2399,26 @@ public function toJson(array $options = array())
$tmp->personal = $info['name'];
}

if (Kronolith::isUserEmail($this->creator, $email)) {
$json->cr = intval($info['response']);
}

$attendees[] = array(
'a' => intval($info['attendance']),
'e' => $tmp->bare_address,
'r' => intval($info['response']),
'l' => strval($tmp)
);
$json->at = $attendees;
}
$json->at = $attendees;
}
}
if ($this->organizer) {
$json->o = $this->organizer;
$json->oy = Kronolith::isUserEmail($this->creator, $this->organizer);
} else {
$json->oy = true;
}
if ($this->methods) {
$json->m = $this->methods;
}
Expand Down Expand Up @@ -2806,6 +2841,9 @@ public function readForm(Kronolith_Event $existing = null)
$this->timezone = Horde_Util::getFormData('timezone', $this->timezone);
$this->private = (bool)Horde_Util::getFormData('private');

// if the field is empty you are the organizer (and so organizer should be null)
$this->organizer = Horde_Util::getFormData('organizer', $this->organizer) ?: null;

// URL.
$url = Horde_Util::getFormData('eventurl', $this->url);
if (strlen($url)) {
Expand Down Expand Up @@ -2870,6 +2908,9 @@ public function readForm(Kronolith_Event $existing = null)
if (!isset($newattendees[$email])) {
unset($attendees[$email]);
}
if (Kronolith::isUserEmail($this->creator, $email)) {
$attendees[$email]['response'] = Horde_Util::getFormData('attendance');
}
}
}
$this->attendees = $attendees;
Expand Down
2 changes: 2 additions & 0 deletions kronolith/lib/Event/Sql.php
Expand Up @@ -79,6 +79,7 @@ public function fromDriver($SQLEvent)
$this->id = $SQLEvent['event_id'];
$this->uid = $SQLEvent['event_uid'];
$this->creator = $SQLEvent['event_creator_id'];
$this->organizer = $SQLEvent['event_organizer'];

if (!empty($SQLEvent['event_recurtype'])) {
$this->recurrence = new Horde_Date_Recurrence($this->start);
Expand Down Expand Up @@ -198,6 +199,7 @@ public function toProperties($full = false)
$properties['event_attendees'] = serialize($driver->convertToDriver($this->attendees));
$properties['event_resources'] = serialize($driver->convertToDriver($this->getResources()));
$properties['event_modified'] = $_SERVER['REQUEST_TIME'];
$properties['event_organizer'] = $this->organizer;

if ($this->isAllDay()) {
$properties['event_start'] = $this->start->strftime('%Y-%m-%d %H:%M:%S');
Expand Down
7 changes: 5 additions & 2 deletions kronolith/lib/Icalendar/Handler/Dav.php
Expand Up @@ -137,14 +137,17 @@ protected function _postSave(Kronolith_Event $event)
$this->_dav->addObjectMap($event->id, $this->_params['object'], $this->_calendar);
}

// Send iTip messages.
// Send iTip messages unless organizer is external.
// Notifications will get lost, there is no way to return messages
// to clients.
if ($event->organizer && !Kronolith::isUserEmail($event->creator, $event->organizer)) {
return;
}
Kronolith::sendITipNotifications(
$event,
new Horde_Notification_Handler(new Horde_Notification_Storage_Object()),
Kronolith::ITIP_REQUEST
);
}

}
}
48 changes: 48 additions & 0 deletions kronolith/lib/Kronolith.php
Expand Up @@ -44,6 +44,7 @@ class Kronolith
/** iTip requests */
const ITIP_REQUEST = 1;
const ITIP_CANCEL = 2;
const ITIP_REPLY = 3;

const RANGE_THISANDFUTURE = 'THISANDFUTURE';

Expand Down Expand Up @@ -782,6 +783,9 @@ public static function getUserEmail($uid)

/**
* Checks if an email address belongs to a user.
*
* @param string $uid user id to check
* @param string $email email address to check
*/
public static function isUserEmail($uid, $email)
{
Expand Down Expand Up @@ -1919,11 +1923,17 @@ public static function sendITipNotifications(
$view->identity = $ident;
$view->event = $event;
$view->imageId = $image->getContentId();

if ($action == self::ITIP_CANCEL && !empty($cancellations)) {
$mail_attendees = $cancellations;
} elseif ($event->organizer && !self::isUserEmail($event->creator, $event->organizer)) {
/* Only send updates to organizer if the user is not the organizer */
/* TODO: Guess organizer name and status if he is one of the attendees */
$mail_attendees = array($event->organizer => array('name' => $event->organizer));
} else {
$mail_attendees = $event->attendees;
}

foreach ($mail_attendees as $email => $status) {
/* Don't bother sending an invitation/update if the recipient does
* not need to participate, or has declined participating, or
Expand All @@ -1947,6 +1957,44 @@ public static function sendITipNotifications(
}
break;

case self::ITIP_REPLY:
$filename = 'event-reply.ics';
$vEvent = array_shift($event->toiCalendar(new Horde_Icalendar()));
$attendee = new Horde_Itip_Resource_Identity(
$ident,
$vEvent->getAttribute('ATTENDEE'),
$ident->getFromAddress()
);
/* Find which of the creator's mail addresses is used here */
foreach ($event->attendees as $attendeeEmail => $status) {
if (self::isUserEmail($event->creator, $attendeeEmail)) {
switch ($status['response']) {
case self::RESPONSE_ACCEPTED:
$type = new Horde_Itip_Response_Type_Accept($attendee);
break;
case self::RESPONSE_DECLINED:
$type = new Horde_Itip_Response_Type_Decline($attendee);
break;
case self::RESPONSE_TENTATIVE:
$type = new Horde_Itip_Response_Type_Tentative($attendee);
break;
default:
return;
}
try {
// Send the reply.
Horde_Itip::factory($vEvent, $attendee)->sendMultiPartResponse(
$type,
new Horde_Core_Itip_Response_Options_Horde('UTF-8', array()),
$injector->getInstance('Horde_Mail')
);
} catch (Horde_Itip_Exception $e) {
$notification->push(sprintf(_("Error sending reply: %s."), $e->getMessage()), 'horde.error');
}
}
}
return;

case self::ITIP_REQUEST:
default:
$method = 'REQUEST';
Expand Down
39 changes: 39 additions & 0 deletions kronolith/migration/26_kronolith_remote_organizer.php
@@ -0,0 +1,39 @@
<?php
/**
* Copyright 2014-2015 Horde LLC (http://www.horde.org/)
*
* See the enclosed file COPYING for license information (GPL). If you
* did not receive this file, see http://www.horde.org/licenses/gpl.
*
* @author Ralf Lang <lang@b1-systems.de>
* @category Horde
* @license http://www.horde.org/licenses/gpl GPL
* @package Kronolith
*/

/**
* Fixes the type of the parents column.
*
* @author Ralf Lang <lang@b1-systems.de>
* @category Horde
* @license http://www.horde.org/licenses/gpl GPL
* @package Kronolith
*/
class KronolithRemoteOrganizer extends Horde_Db_Migration_Base
{
/**
* Upgrade.
*/
public function up()
{
$this->addColumn('kronolith_events', 'event_organizer', 'string', array('limit' => 255));
}

/**
* Downgrade
*/
public function down()
{
$this->removeColumn('kronolith_events', 'event_organizer', 'text');
}
}

0 comments on commit 4f13f3b

Please sign in to comment.