Skip to content

Commit

Permalink
Merge branch 'nag'
Browse files Browse the repository at this point in the history
  • Loading branch information
mrubinsk committed Feb 27, 2015
2 parents 5924ccd + 8747822 commit 58823a5
Show file tree
Hide file tree
Showing 22 changed files with 687 additions and 51 deletions.
12 changes: 11 additions & 1 deletion imp/config/mime_drivers.php
Expand Up @@ -262,7 +262,17 @@
* automatically updated. All other domains require the
* free/busy data to be explicitly updated by user
* action. */
'auto_update_fbreply' => false
'auto_update_fbreply' => false,

/* How task replies are handled when a user opens the message.
* - false: Reply status is never automatically updated; requires
* explicit action by user.
* - true: Reply status is always automatically updated.
* - Array: An array of domains for which replies are always
* automatically updated. All other domains require the
* reply data to be explicitly updated by user
* action. */
'auto_update_taskreply' => false,
),

/* Audio data. */
Expand Down
49 changes: 36 additions & 13 deletions imp/lib/Ajax/Imple/ItipRequest.php
Expand Up @@ -116,21 +116,44 @@ protected function _handle(Horde_Variables $vars)

case 'update':
// vEvent reply.
if ($registry->hasMethod('calendar/updateAttendee')) {
try {
if ($tmp = $contents->getHeader()->getHeader('from')) {
$registry->call('calendar/updateAttendee', array(
$components[$key],
$tmp->getAddressList(true)->first()->bare_address
));
$notification->push(_("Respondent Status Updated."), 'horde.success');
$result = true;
// vTodo reply.
switch ($components[$key]->getType()) {
case 'vEvent':
if ($registry->hasMethod('calendar/updateAttendee')) {
try {
if ($tmp = $contents->getHeader()->getHeader('from')) {
$registry->call('calendar/updateAttendee', array(
$components[$key],
$tmp->getAddressList(true)->first()->bare_address
));
$notification->push(_("Respondent Status Updated."), 'horde.success');
$result = true;
}
} catch (Horde_Exception $e) {
$notification->push(sprintf(_("There was an error updating the event: %s"), $e->getMessage()), 'horde.error');
}
} catch (Horde_Exception $e) {
$notification->push(sprintf(_("There was an error updating the event: %s"), $e->getMessage()), 'horde.error');
} else {
$notification->push(_("This action is not supported."), 'horde.warning');
}
} else {
$notification->push(_("This action is not supported."), 'horde.warning');
break;
case 'vTodo':
if ($registry->hasMethod('tasks/updateAttendee')) {
try {
if ($tmp = $contents->getHeader()->getHeader('from')) {
$registry->call('tasks/updateAttendee', array(
$components[$key],
$tmp->getAddressList(true)->first()->bare_address
));
$notification->push(_("Respondent Status Updated."), 'horde.success');
$result = true;
}
} catch (Horde_Exception $e) {
$notification->push(sprintf(_("There was an error updating the task: %s"), $e->getMessage()), 'horde.error');
}
} else {
$notification->push(_("This action is not supported."), 'horde.warning');
}
break;
}
break;

Expand Down
57 changes: 49 additions & 8 deletions imp/lib/Mime/Viewer/Itip.php
Expand Up @@ -27,6 +27,11 @@
*/
class IMP_Mime_Viewer_Itip extends Horde_Mime_Viewer_Base
{
const AUTO_UPDATE_EVENT_REPLY = 'auto_update_eventreply';
const AUTO_UPDATE_FB_PUBLISH = 'auto_update_fbpublish';
const AUTO_UPDATE_FB_REPLY = 'auto_update_fbreply';
const AUTO_UPDATE_TASK_REPLY = 'auto_update_taskreply';

/**
* This driver's display capabilities.
*
Expand Down Expand Up @@ -225,7 +230,7 @@ protected function _vFreebusy($vfb, $id, $method)
case 'PUBLISH':
case 'REPLY':
if ($registry->hasMethod('calendar/import_vfreebusy')) {
if ($this->_autoUpdateReply(($method == 'PUBLISH') ? 'auto_update_fbpublish' : 'auto_update_fbreply', $sender)) {
if ($this->_autoUpdateReply(($method == 'PUBLISH') ? self::AUTO_UPDATE_FB_PUBLISH : self::AUTO_UPDATE_EVENT_REPLY, $sender)) {
try {
$registry->call('calendar/import_vfreebusy', array($vfb));
$notification->push(_("The user's free/busy information was sucessfully stored."), 'horde.success');
Expand Down Expand Up @@ -346,7 +351,7 @@ protected function _vEvent($vevent, $id, $method = 'PUBLISH', $components = arra
? $from->getAddressList(true)->first()->bare_address
: null;
if ($registry->hasMethod('calendar/updateAttendee') &&
$this->_autoUpdateReply('auto_update_eventreply', $sender)) {
$this->_autoUpdateReply(self::AUTO_UPDATE_EVENT_REPLY, $sender)) {
try {
$registry->call('calendar/updateAttendee', array(
$vevent,
Expand Down Expand Up @@ -688,6 +693,13 @@ protected function _vTodo($vtodo, $id, $method)
$sender = _("An unknown person");
}

try {
if (($attendees = $vtodo->getAttribute('ATTENDEE')) &&
!is_array($attendees)) {
$attendees = array($attendees);
}
} catch (Horde_Icalendar_Exception $e) {}

switch ($method) {
case 'PUBLISH':
$desc = _("%s wishes to make you aware of \"%s\".");
Expand All @@ -704,6 +716,29 @@ protected function _vTodo($vtodo, $id, $method)
$options['deny'] = _("Deny task assignment");
}
break;

case 'REPLY':
$desc = _("%s has replied to the assignment of task \"%s\".");
$from = $this->getConfigParam('imp_contents')->getHeader()->getHeader('from');
$sender = $from
? $from->getAddressList(true)->first()->bare_address
: null;

if ($registry->hasMethod('tasks/updateAttendee') &&
$this->_autoUpdateReply(self::AUTO_UPDATE_TASK_REPLY, $sender)) {
try {
$registry->call('tasks/updateAttendee', array(
$vtodo,
$sender
));
$notification->push(_("Respondent Status Updated."), 'horde.success');
} catch (Horde_Exception $e) {
$notification->push(sprintf(_("There was an error updating the task: %s"), $e->getMessage()), 'horde.error');
}
} elseif ($registry->hasMethod('tasks/updateAttendee')) {
$options['update'] = _("Update respondent status");
}
break;
}

$view = $this->_getViewOb();
Expand All @@ -725,16 +760,14 @@ protected function _vTodo($vtodo, $id, $method)
$view->desc2 = $vtodo->getAttribute('DESCRIPTION');
} catch (Horde_Icalendar_Exception $e) {}

try {
$attendees = $vtodo->getAttribute('ATTENDEE');
} catch (Horde_Icalendar_Exception $e) {
$attendees = null;
}

try {
$view->percentComplete = $vtodo->getAttribute('PERCENT-COMPLETE');
} catch (Horde_Icalendar_Exception $e) {}

if (!empty($attendees)) {
$view->attendees = $this->_parseAttendees($vtodo, $attendees);
}

if (!empty($options)) {
reset($options);
$view->options = $options;
Expand Down Expand Up @@ -851,6 +884,14 @@ protected function _parseAttendees($data, $attendees)
}

/**
* Determine if we are going to auto-update the reply.
*
* @param string $type The type of reply. Must match one of the
* 'auto_update_*' configuration keys in the iTip
* mime viewer configuration.
* @param string $sender The sender.
*
* @return boolean
*/
protected function _autoUpdateReply($type, $sender)
{
Expand Down
5 changes: 4 additions & 1 deletion nag/app/controllers/SaveTask.php
Expand Up @@ -3,7 +3,7 @@ class Nag_SaveTask_Controller extends Horde_Controller_Base
{
public function processRequest(Horde_Controller_Request $request, Horde_Controller_Response $response)
{
global $nag_shares, $prefs;
global $nag_shares, $prefs, $conf;

$vars = Horde_Variables::getDefaultVariables();
$registry = $this->getInjector()->getInstance('Horde_Registry');
Expand Down Expand Up @@ -91,6 +91,9 @@ public function processRequest(Horde_Controller_Request $request, Horde_Controll
$notification->push(sprintf(_("There was a problem saving the task: %s."), $e->getMessage()), 'horde.error');
Horde::url('list.php', true)->redirect();
}
if ($conf['assignees']['allow_external']) {
Nag::sendITipNotifications($storage->get($newid[0]), $notification, Nag::ITIP_REQUEST);
}
}

$notification->push(sprintf(_("Saved %s."), $info['name']), 'horde.success');
Expand Down
8 changes: 8 additions & 0 deletions nag/config/conf.xml
Expand Up @@ -21,6 +21,14 @@
</configswitch>
</configsection>

<configsection name="assignees">
<configheader>Assignee Handling</configheader>
<configboolean name="allow_external" desc="Should we allow assingment of tasks
to external users? This enables sending vTodo/iTip messages for task
assignments. Turning this off will restrict assignees to Horde users that have
read access to the parent tasklist.">true</configboolean>
</configsection>

<configsection name="menu">
<configheader>Menu settings</configheader>
<configboolean name="import_export" desc="Should we display an Import/Export
Expand Down
8 changes: 7 additions & 1 deletion nag/config/prefs.php
Expand Up @@ -183,13 +183,19 @@
'read' => _("On all task lists I have read access to")
),
'desc' => _("Choose if you want to be notified of new, edited, and deleted tasks by email:"),
'locked' => function() {
return $GLOBALS['conf']['assignees']['allow_external'];
}
);

$_prefs['task_notification_exclude_self'] = array(
'value' => 0,
'locked' => false,
'type' => 'checkbox',
'desc' => _("Don't send me a notification if I've added, changed or deleted the task?")
'desc' => _("Don't send me a notification if I've added, changed or deleted the task?"),
'locked' => function() {
return $GLOBALS['conf']['assignees']['allow_external'];
}
);

// alarm methods
Expand Down
82 changes: 82 additions & 0 deletions nag/lib/Api.php
Expand Up @@ -184,6 +184,88 @@ public static function updateTasklist($name, $info)
return Nag::updateTasklist($tasklist, $info);
}

/**
* Updates an attendee's response status for a specified task assignment.
*
* @param Horde_Icalendar_Vtodo $response A Horde_Icalendar_Vtodo
* object, with a valid UID
* attribute that points to an
* existing task. This is
* typically the vTodo portion
* of an iTip task-request
* response, with the attendee's
* response in an ATTENDEE
* parameter.
* @param string $sender The email address of the
* person initiating the
* update. Attendees are only
* updated if this address
* matches.
*
* @throws Nag_Exception, Horde_Exception_PermissionDenied
*/
public function updateAttendee($response, $sender = null)
{
try {
$uid = $response->getAttribute('UID');
} catch (Horde_Icalendar_Exception $e) {
throw new Kronolith_Exception($e);
}

$task = $GLOBALS['injector']
->getInstance('Nag_Factory_Driver')
->create('')->getByUID($uid);
$taskId = $task->id;
$owner = $task->owner;
if (!Nag::hasPermission($task->tasklist, Horde_Perms::EDIT)) {
throw new Horde_Exception_PermissionDenied();
}

try {
$atnames = $response->getAttribute('ATTENDEE');
} catch (Horde_Icalendar_Exception $e) {
throw new Nag_Exception($e->getMessage());
}
if (!is_array($atnames)) {
$atnames = array($atnames);
}

$atparms = $response->getAttribute('ATTENDEE', true);
$found = false;
$error = _("No attendees have been updated because none of the provided email addresses have been found in the event's attendees list.");

foreach ($atnames as $index => $attendee) {
if ($response->getAttribute('VERSION') < 2) {
$addr_ob = new Horde_Mail_Rfc822_Address($attendee);
if (!$addr_ob->valid) {
continue;
}

$attendee = $addr_ob->bare_address;
$name = $addr_ob->personal;
} else {
$attendee = str_ireplace('mailto:', '', $attendee);
$name = isset($atparms[$index]['CN']) ? $atparms[$index]['CN'] : null;
}
$identity = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Identity')->create($task->assignee);
$all_addrs = $identity->getAll('from_addr');
if (in_array($attendee, $all_addrs)) {
if (is_null($sender) || $sender == $attendee) {
$task->status = Nag::responseFromICal($atparms[$index]['PARTSTAT']);
$found = true;
break;
} else {
$error = _("The attendee hasn't been updated because the update was not sent from the attendee.");
}
}
}
$task->save();

if (!$found) {
throw new Nag_Exception($error);
}
}

/**
* Deletes a task list.
*
Expand Down

0 comments on commit 58823a5

Please sign in to comment.