Overview

Packages

  • IMP
  • None

Classes

  • IMP
  • IMP_Ajax_Application
  • IMP_Ajax_Imple_ContactAutoCompleter
  • IMP_Ajax_Imple_PassphraseDialog
  • IMP_Ajax_Queue
  • IMP_Api
  • IMP_Auth
  • IMP_Block_Newmail
  • IMP_Block_Summary
  • IMP_Compose
  • IMP_Compose_Exception
  • IMP_Compose_Stationery
  • IMP_Contents
  • IMP_Crypt_Pgp
  • IMP_Crypt_Smime
  • IMP_Dimp
  • IMP_Exception
  • IMP_Factory_AuthImap
  • IMP_Factory_Compose
  • IMP_Factory_Contents
  • IMP_Factory_Flags
  • IMP_Factory_Identity
  • IMP_Factory_Imap
  • IMP_Factory_Imaptree
  • IMP_Factory_Mail
  • IMP_Factory_Mailbox
  • IMP_Factory_MailboxList
  • IMP_Factory_MimeViewer
  • IMP_Factory_Pgp
  • IMP_Factory_Quota
  • IMP_Factory_Search
  • IMP_Factory_Sentmail
  • IMP_Factory_Smime
  • IMP_Filter
  • IMP_Flag_Base
  • IMP_Flag_Imap
  • IMP_Flag_Imap_Answered
  • IMP_Flag_Imap_Deleted
  • IMP_Flag_Imap_Draft
  • IMP_Flag_Imap_Flagged
  • IMP_Flag_Imap_Forwarded
  • IMP_Flag_Imap_Junk
  • IMP_Flag_Imap_NotJunk
  • IMP_Flag_Imap_Seen
  • IMP_Flag_System_Attachment
  • IMP_Flag_System_Encrypted
  • IMP_Flag_System_HighPriority
  • IMP_Flag_System_List
  • IMP_Flag_System_LowPriority
  • IMP_Flag_System_Match_Address
  • IMP_Flag_System_Match_Flag
  • IMP_Flag_System_Match_Header
  • IMP_Flag_System_Personal
  • IMP_Flag_System_Signed
  • IMP_Flag_System_Unseen
  • IMP_Flag_User
  • IMP_Flags
  • IMP_Imap
  • IMP_Imap_Acl
  • IMP_Imap_Exception
  • IMP_Imap_PermanentFlags
  • IMP_Imap_Thread
  • IMP_Imap_Tree
  • IMP_Indices
  • IMP_Indices_Form
  • IMP_LoginTasks_SystemTask_GarbageCollection
  • IMP_LoginTasks_SystemTask_Upgrade
  • IMP_LoginTasks_SystemTask_UpgradeAuth
  • IMP_LoginTasks_Task_Autocreate
  • IMP_LoginTasks_Task_DeleteAttachmentsMonthly
  • IMP_LoginTasks_Task_DeleteSentmailMonthly
  • IMP_LoginTasks_Task_FilterOnLogin
  • IMP_LoginTasks_Task_PurgeSentmail
  • IMP_LoginTasks_Task_PurgeSpam
  • IMP_LoginTasks_Task_PurgeTrash
  • IMP_LoginTasks_Task_RecoverDraft
  • IMP_LoginTasks_Task_RenameSentmailMonthly
  • IMP_Mailbox
  • IMP_Mailbox_List
  • IMP_Mailbox_List_Track
  • IMP_Maillog
  • IMP_Menu_Dimp
  • IMP_Message
  • IMP_Mime_Status
  • IMP_Mime_Viewer_Alternative
  • IMP_Mime_Viewer_Appledouble
  • IMP_Mime_Viewer_Audio
  • IMP_Mime_Viewer_Enriched
  • IMP_Mime_Viewer_Externalbody
  • IMP_Mime_Viewer_Html
  • IMP_Mime_Viewer_Images
  • IMP_Mime_Viewer_Itip
  • IMP_Mime_Viewer_Mdn
  • IMP_Mime_Viewer_Partial
  • IMP_Mime_Viewer_Pdf
  • IMP_Mime_Viewer_Pgp
  • IMP_Mime_Viewer_Plain
  • IMP_Mime_Viewer_Related
  • IMP_Mime_Viewer_Rfc822
  • IMP_Mime_Viewer_Smil
  • IMP_Mime_Viewer_Smime
  • IMP_Mime_Viewer_Status
  • IMP_Mime_Viewer_Vcard
  • IMP_Mime_Viewer_Video
  • IMP_Mime_Viewer_Zip
  • IMP_Notification_Event_Status
  • IMP_Notification_Handler_Decorator_ImapAlerts
  • IMP_Notification_Handler_Decorator_NewmailNotify
  • IMP_Notification_Listener_AjaxStatus
  • Imp_Prefs_Identity
  • IMP_Prefs_Ui
  • IMP_Quota
  • IMP_Quota_Base
  • IMP_Quota_Command
  • IMP_Quota_Hook
  • IMP_Quota_Imap
  • IMP_Quota_Maildir
  • IMP_Quota_Mdaemon
  • IMP_Quota_Mercury32
  • IMP_Quota_Null
  • IMP_Quota_Sql
  • IMP_Search
  • IMP_Search_Element
  • IMP_Search_Element_Attachment
  • IMP_Search_Element_Autogenerated
  • IMP_Search_Element_Bulk
  • IMP_Search_Element_Contacts
  • IMP_Search_Element_Date
  • IMP_Search_Element_Flag
  • IMP_Search_Element_Header
  • IMP_Search_Element_Mailinglist
  • IMP_Search_Element_Or
  • IMP_Search_Element_Personal
  • IMP_Search_Element_Recipient
  • IMP_Search_Element_Size
  • IMP_Search_Element_Text
  • IMP_Search_Element_Within
  • IMP_Search_Filter
  • IMP_Search_Filter_Attachment
  • IMP_Search_Filter_Autogenerated
  • IMP_Search_Filter_Builtin
  • IMP_Search_Filter_Bulk
  • IMP_Search_Filter_Contacts
  • IMP_Search_Filter_Mailinglist
  • IMP_Search_Filter_Personal
  • IMP_Search_Query
  • IMP_Search_Vfolder
  • IMP_Search_Vfolder_Builtin
  • IMP_Search_Vfolder_Vinbox
  • IMP_Search_Vfolder_Vtrash
  • IMP_Sentmail
  • IMP_Sentmail_Base
  • IMP_Sentmail_Null
  • IMP_Sentmail_Sql
  • IMP_Spam
  • IMP_Test
  • IMP_Tree_Flist
  • IMP_Tree_Jquerymobile
  • IMP_Tree_Simplehtml
  • IMP_Ui_Compose
  • IMP_Ui_Editor
  • IMP_Ui_Folder
  • IMP_Ui_Headers
  • IMP_Ui_Imageview
  • IMP_Ui_Mailbox
  • IMP_Ui_Message
  • IMP_Ui_Mimp
  • IMP_Ui_Search
  • IMP_Views_Compose
  • IMP_Views_ListMessages
  • IMP_Views_ShowMessage
  • Overview
  • Package
  • Class
  • Tree
   1: <?php
   2: /**
   3:  * The IMP_Mime_Viewer_Itip class displays vCalendar/iCalendar data
   4:  * and provides an option to import the data into a calendar source,
   5:  * if one is available.
   6:  *
   7:  * Copyright 2002-2012 Horde LLC (http://www.horde.org/)
   8:  *
   9:  * See the enclosed file COPYING for license information (GPL). If you
  10:  * did not receive this file, see http://www.horde.org/licenses/gpl.
  11:  *
  12:  * @author   Chuck Hagenbuch <chuck@horde.org>
  13:  * @author   Mike Cochrane <mike@graftonhall.co.nz>
  14:  * @author   Gunnar Wrobel <wrobel@pardus.de>
  15:  * @category Horde
  16:  * @license  http://www.horde.org/licenses/gpl GPL
  17:  * @package  IMP
  18:  */
  19: class IMP_Mime_Viewer_Itip extends Horde_Mime_Viewer_Base
  20: {
  21:     /**
  22:      * This driver's display capabilities.
  23:      *
  24:      * @var array
  25:      */
  26:     protected $_capability = array(
  27:         'full' => true,
  28:         'info' => false,
  29:         'inline' => true,
  30:         'raw' => false
  31:     );
  32: 
  33:     /**
  34:      * Metadata for the current viewer/data.
  35:      *
  36:      * @var array
  37:      */
  38:     protected $_metadata = array(
  39:         'compressed' => false,
  40:         'embedded' => false,
  41:         'forceinline' => true
  42:     );
  43: 
  44:     /**
  45:      * Return the full rendered version of the Horde_Mime_Part object.
  46:      *
  47:      * @return array  See parent::render().
  48:      */
  49:     protected function _render()
  50:     {
  51:         $ret = $this->_renderInline(true);
  52:         if (!empty($ret)) {
  53:             $templates = $GLOBALS['registry']->get('templates', 'horde');
  54: 
  55:             reset($ret);
  56:             Horde::startBuffer();
  57:             include $templates . '/common-header.inc';
  58:             echo $ret[key($ret)]['data'];
  59:             include $templates . '/common-footer.inc';
  60: 
  61:             $ret[key($ret)]['data'] = Horde::endBuffer();
  62:         }
  63: 
  64:         return $ret;
  65:     }
  66: 
  67:     /**
  68:      * Return the rendered inline version of the Horde_Mime_Part object.
  69:      *
  70:      * URL parameters used by this function:
  71:      *   - ajax: (boolean) Is this an AJAX request?
  72:      *   - identity: (integer) Identity to use.
  73:      *   - itip_action: (array) List of actions.
  74:      *
  75:      * @return array  See parent::render().
  76:      */
  77:     protected function _renderInline($full = false)
  78:     {
  79:         global $registry;
  80: 
  81:         $charset = $this->getConfigParam('charset');
  82:         $data = $this->_mimepart->getContents();
  83:         $mime_id = $this->_mimepart->getMimeId();
  84: 
  85:         // Parse the iCal file.
  86:         $vCal = new Horde_Icalendar();
  87:         if (!$vCal->parsevCalendar($data, 'VCALENDAR', $this->_mimepart->getCharset())) {
  88:             return array(
  89:                 $mime_id => array(
  90:                     'data' => '<h1>' . _("The calendar data is invalid") . '</h1>' . '<pre>' . htmlspecialchars($data) . '</pre>',
  91:                     'type' => 'text/html; charset=' . $charset
  92:                 )
  93:             );
  94:         }
  95: 
  96:         // Check if we got vcard data with the wrong vcalendar mime type.
  97:         $c = $vCal->getComponentClasses();
  98:         if ((count($c) == 1) && !empty($c['horde_icalendar_vcard'])) {
  99:             return $this->getConfigParam('imp_contents')->renderMIMEPart($mime_id, IMP_Contents::RENDER_INLINE, array('type' => 'text/x-vcard'));
 100:         }
 101: 
 102:         // Get the method type.
 103:         try {
 104:             $method = $vCal->getAttribute('METHOD');
 105:         } catch (Horde_Icalendar_Exception $e) {
 106:             $method = '';
 107:         }
 108: 
 109:         // Get the iCalendar file components.
 110:         $components = $vCal->getComponents();
 111:         $msgs = array();
 112: 
 113:         // Handle the action requests.
 114:         $vars = Horde_Variables::getDefaultVariables();
 115:         foreach ($vars->get('itip_action', array()) as $key => $action) {
 116:             switch ($action) {
 117:             case 'delete':
 118:                 // vEvent cancellation.
 119:                 if ($registry->hasMethod('calendar/delete')) {
 120:                     $guid = $components[$key]->getAttribute('UID');
 121:                     $recurrenceId = null;
 122: 
 123:                     try {
 124:                         // This is a cancellation of a recurring event instance.
 125:                         $recurrenceId = $components[$key]->getAttribute('RECURRENCE-ID');
 126:                     } catch (Horde_Icalendar_Exception $e) {}
 127: 
 128:                     try {
 129:                         $registry->call('calendar/delete', array('guid' => $guid), $recurrenceId);
 130:                         $msgs[] = array('success', _("Event successfully deleted."));
 131:                     } catch (Horde_Exception $e) {
 132:                         $msgs[] = array('error', _("There was an error deleting the event:") . ' ' . $e->getMessage());
 133:                     }
 134:                 } else {
 135:                     $msgs[] = array('warning', _("This action is not supported."));
 136:                 }
 137:                 break;
 138: 
 139:             case 'update':
 140:                 // vEvent reply.
 141:                 if ($registry->hasMethod('calendar/updateAttendee')) {
 142:                     try {
 143:                         $sender = $this->getConfigParam('imp_contents')
 144:                             ->getHeader()
 145:                             ->getValue('From');
 146:                         $event = $registry->call('calendar/updateAttendee', array('response' => $components[$key], 'sender' => Horde_Mime_Address::bareAddress($sender)));
 147:                         $msgs[] = array('success', _("Respondent Status Updated."));
 148:                     } catch (Horde_Exception $e) {
 149:                         $msgs[] = array('error', _("There was an error updating the event:") . ' ' . $e->getMessage());
 150:                     }
 151:                 } else {
 152:                     $msgs[] = array('warning', _("This action is not supported."));
 153:                 }
 154:                 break;
 155: 
 156:             case 'import':
 157:             case 'accept-import':
 158:                 // vFreebusy reply.
 159:                 // vFreebusy publish.
 160:                 // vEvent request.
 161:                 // vEvent publish.
 162:                 // vTodo publish.
 163:                 // vJournal publish.
 164:                 switch ($components[$key]->getType()) {
 165:                 case 'vEvent':
 166:                     $handled = false;
 167:                     $guid = $components[$key]->getAttribute('UID');
 168: 
 169:                     // Check if this is an update.
 170:                     try {
 171:                         $registry->call('calendar/export', array($guid, 'text/calendar'));
 172:                         // Try to update in calendar.
 173:                         if ($registry->hasMethod('calendar/replace')) {
 174:                             try {
 175:                                 $registry->call('calendar/replace', array('uid' => $guid, 'content' => $components[$key], 'contentType' => $this->_mimepart->getType()));
 176:                                 $handled = true;
 177:                                 $url = Horde::url($registry->link('calendar/show', array('uid' => $guid)));
 178:                                 $msgs[] = array('success',
 179:                                                 _("The event was updated in your calendar.")
 180:                                                 . '&nbsp;'
 181:                                                 . Horde::link($url, _("View event"), null, '_blank')
 182:                                                 . Horde::img('mime/icalendar.png', _("View event"))
 183:                                                 . '</a>',
 184:                                                 array('content.raw'));
 185:                             } catch (Horde_Exception $e) {
 186:                                 // Could be a missing permission.
 187:                                 $msgs[] = array('warning', _("There was an error updating the event:") . ' ' . $e->getMessage() . '. ' . _("Trying to import the event instead."));
 188:                             }
 189:                         }
 190:                     } catch (Horde_Exception $e) {}
 191: 
 192:                     if (!$handled && $registry->hasMethod('calendar/import')) {
 193:                         // Import into calendar.
 194:                         $handled = true;
 195:                         try {
 196:                             $guid = $registry->call('calendar/import', array('content' => $components[$key], 'contentType' => $this->_mimepart->getType()));
 197:                             $url = Horde::url($registry->link('calendar/show', array('uid' => $guid)));
 198:                             $msgs[] = array('success',
 199:                                             _("The event was added to your calendar.")
 200:                                             . '&nbsp;'
 201:                                             . Horde::link($url, _("View event"), null, '_blank')
 202:                                             . Horde::img('mime/icalendar.png', _("View event"))
 203:                                             . '</a>',
 204:                                             array('content.raw'));
 205:                         } catch (Horde_Exception $e) {
 206:                             $msgs[] = array('error', _("There was an error importing the event:") . ' ' . $e->getMessage());
 207:                         }
 208:                     }
 209:                     if (!$handled) {
 210:                         $msgs[] = array('warning', _("This action is not supported."));
 211:                     }
 212:                     break;
 213: 
 214:                 case 'vFreebusy':
 215:                     // Import into Kronolith.
 216:                     if ($registry->hasMethod('calendar/import_vfreebusy')) {
 217:                         try {
 218:                             $registry->call('calendar/import_vfreebusy', array($components[$key]));
 219:                             $msgs[] = array('success', _("The user's free/busy information was sucessfully stored."));
 220:                         } catch (Horde_Exception $e) {
 221:                             $msgs[] = array('error', _("There was an error importing user's free/busy information:") . ' ' . $e->getMessage());
 222:                         }
 223:                     } else {
 224:                         $msgs[] = array('warning', _("This action is not supported."));
 225:                     }
 226:                     break;
 227: 
 228:                 case 'vTodo':
 229:                     // Import into Nag.
 230:                     if ($registry->hasMethod('tasks/import')) {
 231:                         try {
 232:                             $guid = $registry->call('tasks/import', array($components[$key], $this->_mimepart->getType()));
 233:                             $url = Horde::url($registry->link('tasks/show', array('uid' => $guid)));
 234:                             $msgs[] = array('success',
 235:                                             _("The task has been added to your tasklist.")
 236:                                             . '&nbsp;'
 237:                                             . Horde::link($url, _("View task"), null, '_blank')
 238:                                             . Horde::img('mime/icalendar.png', _("View task"))
 239:                                             . '</a>',
 240:                                             array('content.raw'));
 241:                         } catch (Horde_Exception $e) {
 242:                             $msgs[] = array('error', _("There was an error importing the task:") . ' ' . $e->getMessage());
 243:                         }
 244:                     } else {
 245:                         $msgs[] = array('warning', _("This action is not supported."));
 246:                     }
 247:                     break;
 248: 
 249:                 case 'vJournal':
 250:                 default:
 251:                     $msgs[] = array('warning', _("This action is not yet implemented."));
 252:                 }
 253: 
 254:                 if ($action != 'accept-import') {
 255:                     break;
 256:                 }
 257: 
 258:             case 'accept':
 259:             case 'accept-import':
 260:             case 'deny':
 261:             case 'tentative':
 262:                 // vEvent request.
 263:                 if (isset($components[$key]) &&
 264:                     $components[$key]->getType() == 'vEvent') {
 265: 
 266:                     $vEvent = $components[$key];
 267: 
 268:                     $resource = new Horde_Itip_Resource_Identity(
 269:                         $GLOBALS['injector']->getInstance('IMP_Identity'),
 270:                         $vEvent->getAttribute('ATTENDEE'),
 271:                         $vars->identity
 272:                     );
 273: 
 274:                     switch ($action) {
 275:                     case 'accept':
 276:                     case 'accept-import':
 277:                         $type = new Horde_Itip_Response_Type_Accept($resource);
 278:                         break;
 279:                     case 'deny':
 280:                         $type = new Horde_Itip_Response_Type_Decline($resource);
 281:                         break;
 282:                     case 'tentative':
 283:                         $type = new Horde_Itip_Response_Type_Tentative($resource);
 284:                         break;
 285:                     }
 286: 
 287:                     try {
 288:                         Horde_Itip::factory($vEvent, $resource)->sendMultiPartResponse(
 289:                             $type,
 290:                             new Horde_Itip_Response_Options_Horde(
 291:                                 $charset,
 292:                                 array(
 293:                                     'dns' => $GLOBALS['injector']->getInstance('Net_DNS2_Resolver'),
 294:                                     'server' => $GLOBALS['conf']['server']['name']
 295:                                 )
 296:                             ),
 297:                             $GLOBALS['injector']->getInstance('IMP_Mail')
 298:                         );
 299:                         $msgs[] = array('success', _("Reply Sent."));
 300:                     } catch (Horde_Itip_Exception $e) {
 301:                         $msgs[] = array('error', sprintf(_("Error sending reply: %s."), $e->getMessage()));
 302:                     }
 303:                 } else {
 304:                     $msgs[] = array('warning', _("This action is not supported."));
 305:                 }
 306:                 break;
 307: 
 308:             case 'send':
 309:             case 'reply':
 310:             case 'reply2m':
 311:                 // vfreebusy request.
 312:                 if (isset($components[$key]) &&
 313:                     $components[$key]->getType() == 'vFreebusy') {
 314:                     $vFb = $components[$key];
 315: 
 316:                     // Get the organizer details.
 317:                     try {
 318:                         $organizer = $vFb->getAttribute('ORGANIZER');
 319:                     } catch (Horde_Icalendar_Exception $e) {
 320:                         break;
 321:                     }
 322:                     $organizer = parse_url($organizer);
 323:                     $organizerEmail = $organizer['path'];
 324:                     $organizer = $vFb->getAttribute('ORGANIZER', true);
 325:                     $organizerName = isset($organizer['cn']) ? $organizer['cn'] : '';
 326: 
 327:                     if ($action == 'reply2m') {
 328:                         $startStamp = time();
 329:                         $endStamp = $startStamp + (60 * 24 * 3600);
 330:                     } else {
 331:                         try {
 332:                             $startStamp = $vFb->getAttribute('DTSTART');
 333:                         } catch (Horde_Icalendar_Exception $e) {
 334:                             $startStamp = time();
 335:                         }
 336: 
 337:                         try {
 338:                             $endStamp = $vFb->getAttribute('DTEND');
 339:                         } catch (Horde_Icalendar_Exception $e) {}
 340: 
 341:                         if (!$endStamp) {
 342:                             try {
 343:                                 $duration = $vFb->getAttribute('DURATION');
 344:                                 $endStamp = $startStamp + $duration;
 345:                             } catch (Horde_Icalendar_Exception $e) {
 346:                                 $endStamp = $startStamp + (60 * 24 * 3600);
 347:                             }
 348:                         }
 349:                     }
 350:                     $vfb_reply = $registry->call('calendar/getFreeBusy',
 351:                                                  array('startStamp' => $startStamp,
 352:                                                        'endStamp' => $endStamp));
 353:                     // Find out who we are and update status.
 354:                     $identity = $GLOBALS['injector']->getInstance('IMP_Identity');
 355:                     $email = $identity->getFromAddress();
 356: 
 357:                     // Build the reply.
 358:                     $msg_headers = new Horde_Mime_Headers();
 359:                     $vCal = new Horde_Icalendar();
 360:                     $vCal->setAttribute('PRODID', '-//The Horde Project//' . $msg_headers->getUserAgent() . '//EN');
 361:                     $vCal->setAttribute('METHOD', 'REPLY');
 362:                     $vCal->addComponent($vfb_reply);
 363: 
 364:                     $message = _("Attached is a reply to a calendar request you sent.");
 365:                     $body = new Horde_Mime_Part();
 366:                     $body->setType('text/plain');
 367:                     $body->setCharset($charset);
 368:                     $body->setContents(Horde_String::wrap($message, 76));
 369: 
 370:                     $ics = new Horde_Mime_Part();
 371:                     $ics->setType('text/calendar');
 372:                     $ics->setCharset($charset);
 373:                     $ics->setContents($vCal->exportvCalendar());
 374:                     $ics->setName('icalendar.ics');
 375:                     $ics->setContentTypeParameter('METHOD', 'REPLY');
 376: 
 377:                     $mime = new Horde_Mime_Part();
 378:                     $mime->addPart($body);
 379:                     $mime->addPart($ics);
 380: 
 381:                     // Build the reply headers.
 382:                     $msg_headers->addReceivedHeader(array(
 383:                         'dns' => $GLOBALS['injector']->getInstance('Net_DNS2_Resolver'),
 384:                         'server' => $GLOBALS['conf']['server']['name']
 385:                     ));
 386:                     $msg_headers->addMessageIdHeader();
 387:                     $msg_headers->addHeader('Date', date('r'));
 388:                     $msg_headers->addHeader('From', $email);
 389:                     $msg_headers->addHeader('To', $organizerEmail);
 390: 
 391:                     $identity->setDefault($vars->identity);
 392:                     $replyto = $identity->getValue('replyto_addr');
 393:                     if (!empty($replyto) && ($replyto != $email)) {
 394:                         $msg_headers->addHeader('Reply-to', $replyto);
 395:                     }
 396:                     $msg_headers->addHeader('Subject', _("Free/Busy Request Response"));
 397: 
 398:                     // Send the reply.
 399:                     try {
 400:                         $mime->send($organizerEmail, $msg_headers, $GLOBALS['injector']->getInstance('IMP_Mail'));
 401:                         $msgs[] = array('success', _("Reply Sent."));
 402:                     } catch (Exception $e) {
 403:                         $msgs[] = array('error', sprintf(_("Error sending reply: %s."), $e->getMessage()));
 404:                     }
 405:                 } else {
 406:                     $msgs[] = array('warning', _("Invalid Action selected for this component."));
 407:                 }
 408:                 break;
 409: 
 410:             case 'nosup':
 411:                 // vFreebusy request.
 412:             default:
 413:                 $msgs[] = array('warning', _("This action is not yet implemented."));
 414:                 break;
 415:             }
 416:         }
 417:         if ($vars->ajax) {
 418:             foreach ($msgs as $msg) {
 419:                 $GLOBALS['notification']->push($msg[1], 'horde.' . $msg[0], isset($msg[2]) ? $msg[2] : array());
 420:             }
 421: 
 422:             return array(
 423:                 $mime_id => array(
 424:                     'data' => Horde_String::convertCharset(Horde::escapeJson(Horde::prepareResponse(null, true), array('charset' => $this->getConfigParam('charset'))), $this->getConfigParam('charset'), 'UTF-8'),
 425:                     'name' => null,
 426:                     'type' => 'application/json'
 427:                 )
 428:             );
 429:         }
 430: 
 431:         // Create the HTML to display the iCal file.
 432:         if (!$full && (IMP::getViewMode() != 'imp')) {
 433:             $url = $this->getConfigParam('imp_contents')->urlView($this->_mimepart, 'view_attach', array('params' => array('ajax' => 1, 'mode' => IMP_Contents::RENDER_INLINE)));
 434:             $onsubmit = ' onsubmit="DimpCore.submitForm(\'impMimeViewerItip\');return false"';
 435:         } else {
 436:             $url = IMP::selfUrl();
 437:             $onsubmit = '';
 438:         }
 439:         $html = '<form method="post" id="impMimeViewerItip" action="' . $url . '"' . $onsubmit . '>';
 440: 
 441:         foreach ($components as $key => $component) {
 442:             switch ($component->getType()) {
 443:             case 'vEvent':
 444:                 $html .= $this->_vEvent($component, $key, $method, $msgs);
 445:                 break;
 446: 
 447:             case 'vTodo':
 448:                 $html .= $this->_vTodo($component, $key, $method, $msgs);
 449:                 break;
 450: 
 451:             case 'vTimeZone':
 452:                 // Ignore them.
 453:                 break;
 454: 
 455:             case 'vFreebusy':
 456:                 $html .= $this->_vFreebusy($component, $key, $method, $msgs);
 457:                 break;
 458: 
 459:             // @todo: handle stray vcards here as well.
 460:             default:
 461:                 $html .= sprintf(_("Unhandled component of type: %s"), $component->getType());
 462:             }
 463:         }
 464: 
 465:         $html .= '</form>';
 466: 
 467:         return array(
 468:             $mime_id => array(
 469:                 'data' => $html,
 470:                 'type' => 'text/html; charset=' . $charset
 471:             )
 472:         );
 473:     }
 474: 
 475:     /**
 476:      * Return the html for a vFreebusy.
 477:      */
 478:     protected function _vFreebusy($vfb, $id, $method, $msgs)
 479:     {
 480:         global $registry, $prefs;
 481: 
 482:         $desc = $html = '';
 483:         $options = array();
 484:         $sender = $vfb->getName();
 485: 
 486:         switch ($method) {
 487:         case 'PUBLISH':
 488:             $desc = _("%s has sent you free/busy information.");
 489:             break;
 490: 
 491:         case 'REQUEST':
 492:             $hdrs = $this->getConfigParam('imp_contents')->getHeader();
 493:             $sender = $hdrs->getValue('From');
 494:             $desc = _("%s requests your free/busy information.");
 495:             break;
 496: 
 497:         case 'REPLY':
 498:             $desc = _("%s has replied to a free/busy request.");
 499:             break;
 500:         }
 501: 
 502:         $html .= '<h1 class="header">' . sprintf($desc, $sender) . '</h1>';
 503: 
 504:         foreach ($msgs as $msg) {
 505:             $html .= '<p class="notice">' . Horde::img('alerts/' . $msg[0] . '.png') . $msg[1] . '</p>';
 506:         }
 507: 
 508:         try {
 509:             $start = $vfb->getAttribute('DTSTART');
 510:             if (is_array($start)) {
 511:                 $html .= '<p><strong>' . _("Start:") . '</strong> ' . strftime($prefs->getValue('date_format'), mktime(0, 0, 0, $start['month'], $start['mday'], $start['year'])) . '</p>';
 512:             } else {
 513:                 $html .= '<p><strong>' . _("Start:") . '</strong> ' . strftime($prefs->getValue('date_format'), $start) . ' ' . date($prefs->getValue('twentyFour') ? ' G:i' : ' g:i a', $start) . '</p>';
 514:             }
 515:         } catch (Horde_Icalendar_Exception $e) {}
 516: 
 517:         try {
 518:             $end = $vfb->getAttribute('DTEND');
 519:             if (is_array($end)) {
 520:                 $html .= '<p><strong>' . _("End:") . '</strong> ' . strftime($prefs->getValue('date_format'), mktime(0, 0, 0, $end['month'], $end['mday'], $end['year'])) . '</p>';
 521:             } else {
 522:                 $html .= '<p><strong>' . _("End:") . '</strong> ' . strftime($prefs->getValue('date_format'), $end) . ' ' . date($prefs->getValue('twentyFour') ? ' G:i' : ' g:i a', $end) . '</p>';
 523:             }
 524:         } catch (Horde_Icalendar_Exception $e) {}
 525: 
 526:         switch ($method) {
 527:         case 'PUBLISH':
 528:             if ($registry->hasMethod('calendar/import_vfreebusy')) {
 529:                 $options['import'] = _("Remember the free/busy information.");
 530:             } else {
 531:                 $options['nosup'] = _("Reply with Not Supported Message");
 532:             }
 533:             break;
 534: 
 535:         case 'REQUEST':
 536:             if ($registry->hasMethod('calendar/getFreeBusy')) {
 537:                 $options['reply'] = _("Reply with requested free/busy information.");
 538:                 $options['reply2m'] = _("Reply with free/busy for next 2 months.");
 539:             } else {
 540:                 $options['nosup'] = _("Reply with Not Supported Message");
 541:             }
 542: 
 543:             $options['deny'] = _("Deny request for free/busy information");
 544:             break;
 545: 
 546:         case 'REPLY':
 547:             if ($registry->hasMethod('calendar/import_vfreebusy')) {
 548:                 $options['import'] = _("Remember the free/busy information.");
 549:             } else {
 550:                 $options['nosup'] = _("Reply with Not Supported Message");
 551:             }
 552:             break;
 553:         }
 554: 
 555:         switch (count($options)) {
 556:         case 0:
 557:             return $html;
 558: 
 559:         case 1:
 560:             reset($options);
 561:             return $html .
 562:                 '<input type="hidden" name="itip_action[' . $id . ']" value="' .
 563:                 key($options) . '" />' .
 564:                 '<input type="submit" class="button" value="' .
 565:                     current($options) . '" />';
 566: 
 567:         default:
 568:             $html .= '<h2 class="smallheader">' . _("Actions") . '</h2>'
 569:                 . '<label for="action_' . $id . '" class="hidden">'
 570:                 . _("Actions") . '</label>' . '<select id="action_' . $id
 571:                 . '" name="itip_action[' . $id . ']"><option disabled="disabled" value="">'
 572:                 . _("-- select --") . '</option>';
 573:             foreach ($options as $k => $v) {
 574:                 $html .= '<option value="' . $k . '">' . $v . '</option>';
 575:             }
 576:             return $html . '</select>' .
 577:                 '<input type="submit" class="button" value="' . _("Go") . '"/>';
 578:         }
 579:     }
 580: 
 581:     /**
 582:      * Return the html for a vEvent.
 583:      */
 584:     protected function _vEvent($vevent, $id, $method, $msgs)
 585:     {
 586:         global $registry, $prefs;
 587: 
 588:         $attendees = null;
 589:         $desc = $html = '';
 590:         $sender = $vevent->organizerName();
 591:         $options = array();
 592: 
 593:         if (!$method) {
 594:             $method = 'PUBLISH';
 595:         }
 596: 
 597:         try {
 598:             $attendees = $vevent->getAttribute('ATTENDEE');
 599:             $attendee_params = $vevent->getAttribute('ATTENDEE', true);
 600:             if (!empty($attendees) && !is_array($attendees)) {
 601:                 $attendees = array($attendees);
 602:             }
 603:         } catch (Horde_Icalendar_Exception $e) {}
 604: 
 605:         switch ($method) {
 606:         case 'PUBLISH':
 607:             $desc = _("%s wishes to make you aware of \"%s\".");
 608:             if ($registry->hasMethod('calendar/import')) {
 609:                 $options['import'] = _("Add this to my calendar");
 610:             }
 611:             break;
 612: 
 613:         case 'REQUEST':
 614:             // Check if this is an update.
 615:             try {
 616:                 $registry->call('calendar/export', array($vevent->getAttribute('UID'), 'text/calendar'));
 617:                 $is_update = true;
 618:                 $desc = _("%s wants to notify you about changes of \"%s\".");
 619:             } catch (Horde_Exception $e) {
 620:                 $is_update = false;
 621: 
 622:                 // Check that you are one of the attendees here.
 623:                 $is_attendee = false;
 624:                 if (!empty($attendees)) {
 625:                     $identity = $GLOBALS['injector']->getInstance('IMP_Identity');
 626:                     for ($i = 0, $c = count($attendees); $i < $c; ++$i) {
 627:                         $attendee = parse_url($attendees[$i]);
 628:                         if (!empty($attendee['path']) &&
 629:                             $identity->hasAddress($attendee['path'])) {
 630:                             $is_attendee = true;
 631:                             break;
 632:                         }
 633:                     }
 634:                 }
 635: 
 636:                 $desc = $is_attendee
 637:                     ? _("%s requests your presence at \"%s\".")
 638:                     : _("%s wishes to make you aware of \"%s\".");
 639:             }
 640:             if ($is_update && $registry->hasMethod('calendar/replace')) {
 641:                 $options['accept-import'] = _("Accept and update in my calendar");
 642:                 $options['import'] = _("Update in my calendar");
 643:             } elseif ($registry->hasMethod('calendar/import')) {
 644:                 $options['accept-import'] = _("Accept and add to my calendar");
 645:                 $options['import'] = _("Add to my calendar");
 646:             }
 647:             $options['accept'] = _("Accept request");
 648:             $options['tentative'] = _("Tentatively Accept request");
 649:             $options['deny'] = _("Deny request");
 650:             break;
 651: 
 652:         case 'ADD':
 653:             $desc = _("%s wishes to amend \"%s\".");
 654:             if ($registry->hasMethod('calendar/import')) {
 655:                 $options['import'] = _("Update this event on my calendar");
 656:             }
 657:             break;
 658: 
 659:         case 'REFRESH':
 660:             $desc = _("%s wishes to receive the latest information about \"%s\".");
 661:             $options['send'] = _("Send Latest Information");
 662:             break;
 663: 
 664:         case 'REPLY':
 665:             $hdrs = $this->getConfigParam('imp_contents')->getHeader();
 666:             $desc = _("%s has replied to the invitation to \"%s\".");
 667:             $sender = $hdrs->getValue('From');
 668:             if ($registry->hasMethod('calendar/updateAttendee')) {
 669:                 $options['update'] = _("Update respondent status");
 670:             }
 671:             break;
 672: 
 673:         case 'CANCEL':
 674:             try {
 675:                 $vevent->getAttribute('RECURRENCE-ID');
 676:                 $desc = _("%s has cancelled an instance of the recurring \"%s\".");
 677:                 if ($registry->hasMethod('calendar/replace')) {
 678:                     $options['delete'] = _("Update in my calendar");
 679:                 }
 680:             } catch (Horde_Icalendar_Exception $e) {
 681:                 $desc = _("%s has cancelled \"%s\".");
 682:                 if ($registry->hasMethod('calendar/delete')) {
 683:                     $options['delete'] = _("Delete from my calendar");
 684:                 }
 685:             }
 686:             break;
 687:         }
 688: 
 689:         try {
 690:             $summary = $vevent->getAttribute('SUMMARY');
 691:             $desc = sprintf($desc, htmlspecialchars($sender), htmlspecialchars($summary));
 692:         } catch (Horde_Icalendar_Exception $e) {
 693:             $desc = sprintf($desc, htmlspecialchars($sender), _("Unknown Meeting"));
 694:         }
 695: 
 696:         $html .= '<h2 class="header">' . $desc . '</h2>';
 697: 
 698:         foreach ($msgs as $msg) {
 699:             $html .= '<p class="notice">' . Horde::img('alerts/' . $msg[0] . '.png') . $msg[1] . '</p>';
 700:         }
 701: 
 702:         try {
 703:             $start = $vevent->getAttribute('DTSTART');
 704:             if (is_array($start)) {
 705:                 $html .= '<p><strong>' . _("Start:") . '</strong> ' . strftime($prefs->getValue('date_format'), mktime(0, 0, 0, $start['month'], $start['mday'], $start['year'])) . '</p>';
 706:             } else {
 707:                 $html .= '<p><strong>' . _("Start:") . '</strong> ' . strftime($prefs->getValue('date_format'), $start) . ' ' . date($prefs->getValue('twentyFour') ? ' G:i' : ' g:i a', $start) . '</p>';
 708:             }
 709:         } catch (Horde_Icalendar_Exception $e) {
 710:             $start = null;
 711:         }
 712: 
 713:         try {
 714:             $end = $vevent->getAttribute('DTEND');
 715:             if (is_array($end)) {
 716:                 $html .= '<p><strong>' . _("End:") . '</strong> ' . strftime($prefs->getValue('date_format'), mktime(0, 0, 0, $end['month'], $end['mday'], $end['year'])) . '</p>';
 717:             } else {
 718:                 $html .= '<p><strong>' . _("End:") . '</strong> ' . strftime($prefs->getValue('date_format'), $end) . ' ' . date($prefs->getValue('twentyFour') ? ' G:i' : ' g:i a', $end) . '</p>';
 719:             }
 720:         } catch (Horde_Icalendar_Exception $e) {
 721:             $end = null;
 722:         }
 723: 
 724:         try {
 725:             $sum = $vevent->getAttribute('SUMMARY');
 726:             $html .= '<p><strong>' . _("Summary") . ':</strong> ' . htmlspecialchars($sum) . '</p>';
 727:         } catch (Horde_Icalendar_Exception $e) {
 728:             $html .= '<p><strong>' . _("Summary") . ':</strong> <em>' . _("None") . '</em></p>';
 729:         }
 730: 
 731:         try {
 732:             $desc = $vevent->getAttribute('DESCRIPTION');
 733:             $html .= '<p><strong>' . _("Description") . ':</strong> ' . nl2br(htmlspecialchars($desc)) . '</p>';
 734:         } catch (Horde_Icalendar_Exception $e) {}
 735: 
 736:         try {
 737:             $loc = $vevent->getAttribute('LOCATION');
 738:             $html .= '<p><strong>' . _("Location") . ':</strong> ' . htmlspecialchars($loc) . '</p>';
 739:         } catch (Horde_Icalendar_Exception $e) {}
 740: 
 741:         if (!empty($attendees)) {
 742:             $html .= '<h2 class="smallheader">' . _("Attendees") . '</h2>';
 743: 
 744:             $html .= '<table><thead class="leftAlign"><tr><th>' . _("Name") . '</th><th>' . _("Role") . '</th><th>' . _("Status") . '</th></tr></thead><tbody>';
 745:             foreach ($attendees as $key => $attendee) {
 746:                 $attendee = parse_url($attendee);
 747:                 $attendee = empty($attendee['path']) ? _("Unknown") : $attendee['path'];
 748: 
 749:                 if (!empty($attendee_params[$key]['CN'])) {
 750:                     $attendee = $attendee_params[$key]['CN'];
 751:                 }
 752: 
 753:                 $role = _("Required Participant");
 754:                 if (isset($attendee_params[$key]['ROLE'])) {
 755:                     switch ($attendee_params[$key]['ROLE']) {
 756:                     case 'CHAIR':
 757:                         $role = _("Chair Person");
 758:                         break;
 759: 
 760:                     case 'OPT-PARTICIPANT':
 761:                         $role = _("Optional Participant");
 762:                         break;
 763: 
 764:                     case 'NON-PARTICIPANT':
 765:                         $role = _("Non Participant");
 766:                         break;
 767: 
 768:                     case 'REQ-PARTICIPANT':
 769:                     default:
 770:                         // Already set above.
 771:                         break;
 772:                     }
 773:                 }
 774: 
 775:                 $status = _("Awaiting Response");
 776:                 if (isset($attendee_params[$key]['PARTSTAT'])) {
 777:                     $status = $this->_partstatToString($attendee_params[$key]['PARTSTAT'], $status);
 778:                 }
 779: 
 780:                 $html .= '<tr><td>' . htmlspecialchars($attendee) . '</td><td>' . htmlspecialchars($role) . '</td><td>' . htmlspecialchars($status) . '</td></tr>';
 781:             }
 782:             $html .= '</tbody></table>';
 783:         }
 784: 
 785:         if ($start && $end &&
 786:             ($method == 'PUBLISH' || $method == 'REQUEST' || $method == 'ADD') &&
 787:             $registry->hasMethod('calendar/getFbCalendars') &&
 788:             $registry->hasMethod('calendar/listEvents')) {
 789:             try {
 790:                 $calendars = $registry->call('calendar/getFbCalendars');
 791: 
 792:                 $vevent_allDay = true;
 793:                 $vevent_start = new Horde_Date($start);
 794:                 $vevent_end = new Horde_Date($end);
 795:                 // Check if it's an all-day event.
 796:                 if (is_array($start)) {
 797:                     $vevent_end = $vevent_end->sub(1);
 798:                 } else {
 799:                     $vevent_allDay = false;
 800:                     $time_span_start = new Horde_Date($start);
 801:                     $time_span_start = $time_span_start->sub($prefs->getValue('conflict_interval') * 60);
 802:                     $time_span_end = new Horde_Date($end);
 803:                     $time_span_end = $time_span_end->add($prefs->getValue('conflict_interval') * 60);
 804:                 }
 805:                 $events = $registry->call('calendar/listEvents', array($start, $vevent_end, $calendars, false));
 806: 
 807:                 // TODO: Check if there are too many events to show.
 808:                 $conflicts = '';
 809:                 foreach ($events as $calendar) {
 810:                     foreach ($calendar as $event) {
 811:                         if ($event->status == Kronolith::STATUS_CANCELLED ||
 812:                             $event->status == Kronolith::STATUS_FREE) {
 813:                             continue;
 814:                         }
 815:                         if ($vevent_allDay || $event->isAllDay()) {
 816:                             $conflicts .= '<tr class="itipcollision">';
 817:                         } else {
 818:                             if ($event->end->compareDateTime($time_span_start) <= -1 ||
 819:                                 $event->start->compareDateTime($time_span_end) >= 1) {
 820:                                 continue;
 821:                             }
 822:                             if ($event->end->compareDateTime($vevent_start) <= -1 ||
 823:                                 $event->start->compareDateTime($vevent_end) >= 1) {
 824:                                 $conflicts .= '<tr class="itipnearcollision">';
 825:                             } else {
 826:                                 $conflicts .= '<tr class="itipcollision">';
 827:                             }
 828:                         }
 829: 
 830:                         $conflicts .= '<td>'. $event->getTitle() . '</td><td>'
 831:                             . $event->getTimeRange() . '</td></tr>';
 832:                     }
 833:                 }
 834:                 if ($conflicts) {
 835:                     $html .= '<h2 class="smallheader">'
 836:                       . _("Possible Conflicts")
 837:                       . '</h2><table id="itipconflicts">'
 838:                       . $conflicts . '</table>';
 839:                 }
 840:             } catch (Horde_Exception $e) {}
 841:         }
 842: 
 843:         switch (count($options)) {
 844:         case 0:
 845:             return $html;
 846: 
 847:         case 1:
 848:             reset($options);
 849:             return $html .
 850:                 '<input type="hidden" name="itip_action[' . $id . ']" value="' .
 851:                 key($options) . '" />' .
 852:                 '<input type="submit" class="button" value="' .
 853:                     current($options) . '" />';
 854: 
 855:         default:
 856:             $html .= '<h2 class="smallheader">' . _("Actions") . '</h2>'
 857:                 . '<label for="action_' . $id . '" class="hidden">'
 858:                 . _("Actions") . '</label>' . '<select id="action_' . $id
 859:                 . '" name="itip_action[' . $id . ']"><option disabled="disabled" value="">'
 860:                 . _("-- select --") . '</option>';
 861:             foreach ($options as $k => $v) {
 862:                 $html .= '<option value="' . $k . '">' . $v . '</option>';
 863:             }
 864:             return $html . '</select>' .
 865:                 '<input type="submit" class="button" value="' . _("Go") . '"/>';
 866:         }
 867:     }
 868: 
 869:     /**
 870:      * Returns the html for a vEvent.
 871:      *
 872:      * @todo IMP 5: move organizerName() from Horde_Icalendar_Vevent to
 873:      *       Horde_Icalendar
 874:      */
 875:     protected function _vTodo($vtodo, $id, $method, $msgs)
 876:     {
 877:         global $registry, $prefs;
 878: 
 879:         $desc = $html = '';
 880:         $options = array();
 881: 
 882:         try {
 883:             $organizer = $vtodo->getAttribute('ORGANIZER', true);
 884:             if (isset($organizer[0]['CN'])) {
 885:                 $sender = $organizer[0]['CN'];
 886:             } else {
 887:                 $organizer = parse_url($vtodo->getAttribute('ORGANIZER'));
 888:                 $sender = $organizer['path'];
 889:             }
 890:         } catch (Horde_Icalendar_Exception $e) {
 891:             $sender = _("An unknown person");
 892:         }
 893: 
 894:         switch ($method) {
 895:         case 'PUBLISH':
 896:             $desc = _("%s wishes to make you aware of \"%s\".");
 897:             if ($registry->hasMethod('tasks/import')) {
 898:                 $options['import'] = _("Add this to my tasklist");
 899:             }
 900:             break;
 901:         }
 902: 
 903:         try {
 904:             $summary = $vtodo->getAttribute('SUMMARY');
 905:             $desc = sprintf($desc, htmlspecialchars($sender), htmlspecialchars($summary));
 906:         } catch (Horde_Icalendar_Exception $e) {
 907:             $desc = sprintf($desc, htmlspecialchars($sender), _("Unknown Task"));
 908:         }
 909: 
 910:         $html .= '<h2 class="header">' . $desc . '</h2>';
 911: 
 912:         foreach ($msgs as $msg) {
 913:             $html .= '<p class="notice">' . Horde::img('alerts/' . $msg[0] . '.png') . $msg[1] . '</p>';
 914:         }
 915: 
 916:         try {
 917:             $priority = $vtodo->getAttribute('PRIORITY');
 918:             $html .= '<p><strong>' . _("Priority") . ':</strong> ' . (int)$priority . '</p>';
 919:         } catch (Horde_Icalendar_Exception $e) {}
 920: 
 921:         try {
 922:             $sum = $vtodo->getAttribute('SUMMARY');
 923:             $html .= '<p><strong>' . _("Summary") . ':</strong> ' . htmlspecialchars($sum) . '</p>';
 924:         } catch (Horde_Icalendar_Exception $e) {
 925:             $html .= '<p><strong>' . _("Summary") . ':</strong> <em>' . _("None") . '</em></p>';
 926:         }
 927: 
 928:         try {
 929:             $desc = $vtodo->getAttribute('DESCRIPTION');
 930:             $html .= '<p><strong>' . _("Description") . ':</strong> ' . nl2br(htmlspecialchars($desc)) . '</p>';
 931:         } catch (Horde_Icalendar_Exception $e) {}
 932: 
 933:         try {
 934:             $attendees = $vtodo->getAttribute('ATTENDEE');
 935:             $params = $vtodo->getAttribute('ATTENDEE', true);
 936:         } catch (Horde_Icalendar_Exception $e) {
 937:             $attendees = null;
 938:         }
 939: 
 940:         if (!empty($attendees)) {
 941:             $html .= '<h2 class="smallheader">' . _("Attendees") . '</h2>';
 942:             if (!is_array($attendees)) {
 943:                 $attendees = array($attendees);
 944:             }
 945: 
 946:             $html .= '<table><thead class="leftAlign"><tr><th>' . _("Name") . '</th><th>' . _("Role") . '</th><th>' . _("Status") . '</th></tr></thead><tbody>';
 947:             foreach ($attendees as $key => $attendee) {
 948:                 $attendee = parse_url($attendee);
 949:                 $attendee = $attendee['path'];
 950: 
 951:                 if (isset($params[$key]['CN'])) {
 952:                     $attendee = $params[$key]['CN'];
 953:                 }
 954: 
 955:                 $role = _("Required Participant");
 956:                 if (isset($params[$key]['ROLE'])) {
 957:                     switch ($params[$key]['ROLE']) {
 958:                     case 'CHAIR':
 959:                         $role = _("Chair Person");
 960:                         break;
 961: 
 962:                     case 'OPT-PARTICIPANT':
 963:                         $role = _("Optional Participant");
 964:                         break;
 965: 
 966:                     case 'NON-PARTICIPANT':
 967:                         $role = _("Non Participant");
 968:                         break;
 969: 
 970:                     case 'REQ-PARTICIPANT':
 971:                     default:
 972:                         // Already set above.
 973:                         break;
 974:                     }
 975:                 }
 976: 
 977:                 $status = _("Awaiting Response");
 978:                 if (isset($params[$key]['PARTSTAT'])) {
 979:                     $status = $this->_partstatToString($params[$key]['PARTSTAT'], $status);
 980:                 }
 981: 
 982:                 $html .= '<tr><td>' . htmlspecialchars($attendee) . '</td><td>' . htmlspecialchars($role) . '</td><td>' . htmlspecialchars($status) . '</td></tr>';
 983:             }
 984:             $html .= '</tbody></table>';
 985:         }
 986: 
 987:         switch (count($options)) {
 988:         case 0:
 989:             return $html;
 990: 
 991:         case 1:
 992:             reset($options);
 993:             return $html .
 994:                 '<input type="hidden" name="itip_action[' . $id . ']" value="' .
 995:                 key($options) . '" />' .
 996:                 '<input type="submit" class="button" value="' .
 997:                     current($options) . '" />';
 998: 
 999:         default:
1000:             $html .= '<h2 class="smallheader">' . _("Actions") . '</h2>'
1001:                 . '<label for="action_' . $id . '" class="hidden">'
1002:                 . _("Actions") . '</label>' . '<select id="action_' . $id
1003:                 . '" name="itip_action[' . $id . ']"><option disabled="disabled" value="">'
1004:                 . _("-- select --") . '</option>';
1005:             foreach ($options as $k => $v) {
1006:                 $html .= '<option value="' . $k . '">' . $v . '</option>';
1007:             }
1008:             return $html . '</select>' .
1009:                 '<input type="submit" class="button" value="' . _("Go") . '"/>';
1010:         }
1011:     }
1012: 
1013:     /**
1014:      * Translate the Participation status to string.
1015:      *
1016:      * @param string $value    The value of PARTSTAT.
1017:      * @param string $default  The value to return as default.
1018:      *
1019:      * @return string   The translated string.
1020:      */
1021:     protected function _partstatToString($value, $default = null)
1022:     {
1023:         switch ($value) {
1024:         case 'ACCEPTED':
1025:             return _("Accepted");
1026: 
1027:         case 'DECLINED':
1028:             return _("Declined");
1029: 
1030:         case 'TENTATIVE':
1031:             return _("Tentatively Accepted");
1032: 
1033:         case 'DELEGATED':
1034:             return _("Delegated");
1035: 
1036:         case 'COMPLETED':
1037:             return _("Completed");
1038: 
1039:         case 'IN-PROCESS':
1040:             return _("In Process");
1041: 
1042:         case 'NEEDS-ACTION':
1043:         default:
1044:             return is_null($default) ? _("Needs Action") : $default;
1045:         }
1046:     }
1047: }
1048: 
API documentation generated by ApiGen