1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
19: class IMP_Mime_Viewer_Itip extends Horde_Mime_Viewer_Base
20: {
21: 22: 23: 24: 25:
26: protected $_capability = array(
27: 'full' => true,
28: 'info' => false,
29: 'inline' => true,
30: 'raw' => false
31: );
32:
33: 34: 35: 36: 37:
38: protected $_metadata = array(
39: 'compressed' => false,
40: 'embedded' => false,
41: 'forceinline' => true
42: );
43:
44: 45: 46: 47: 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: 69: 70: 71: 72: 73: 74: 75: 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:
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:
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:
103: try {
104: $method = $vCal->getAttribute('METHOD');
105: } catch (Horde_Icalendar_Exception $e) {
106: $method = '';
107: }
108:
109:
110: $components = $vCal->getComponents();
111: $msgs = array();
112:
113:
114: $vars = Horde_Variables::getDefaultVariables();
115: foreach ($vars->get('itip_action', array()) as $key => $action) {
116: switch ($action) {
117: case 'delete':
118:
119: if ($registry->hasMethod('calendar/delete')) {
120: $guid = $components[$key]->getAttribute('UID');
121: $recurrenceId = null;
122:
123: try {
124:
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:
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:
159:
160:
161:
162:
163:
164: switch ($components[$key]->getType()) {
165: case 'vEvent':
166: $handled = false;
167: $guid = $components[$key]->getAttribute('UID');
168:
169:
170: try {
171: $registry->call('calendar/export', array($guid, 'text/calendar'));
172:
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: . ' '
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:
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:
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: . ' '
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:
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:
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: . ' '
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:
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:
312: if (isset($components[$key]) &&
313: $components[$key]->getType() == 'vFreebusy') {
314: $vFb = $components[$key];
315:
316:
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:
354: $identity = $GLOBALS['injector']->getInstance('IMP_Identity');
355: $email = $identity->getFromAddress();
356:
357:
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:
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:
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:
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:
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:
453: break;
454:
455: case 'vFreebusy':
456: $html .= $this->_vFreebusy($component, $key, $method, $msgs);
457: break;
458:
459:
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: 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: 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:
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:
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:
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:
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:
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: 871: 872: 873: 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:
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: 1015: 1016: 1017: 1018: 1019: 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: