Overview

Packages

  • Kronolith
  • None

Classes

  • Kronolith
  • Kronolith_Ajax_Application
  • Kronolith_Ajax_Imple_ContactAutoCompleter
  • Kronolith_Ajax_Imple_Embed
  • Kronolith_Ajax_Imple_TagActions
  • Kronolith_Ajax_Imple_TagAutoCompleter
  • Kronolith_Api
  • Kronolith_Calendar
  • Kronolith_Calendar_External
  • Kronolith_Calendar_External_Tasks
  • Kronolith_Calendar_Holiday
  • Kronolith_Calendar_Internal
  • Kronolith_Calendar_Remote
  • Kronolith_Calendar_Resource
  • Kronolith_Calendars_Base
  • Kronolith_Calendars_Default
  • Kronolith_Calendars_Kolab
  • Kronolith_Day
  • Kronolith_Driver
  • Kronolith_Driver_Holidays
  • Kronolith_Driver_Horde
  • Kronolith_Driver_Ical
  • Kronolith_Driver_Kolab
  • Kronolith_Driver_Mock
  • Kronolith_Driver_Resource
  • Kronolith_Driver_Sql
  • Kronolith_Event
  • Kronolith_Event_Holidays
  • Kronolith_Event_Horde
  • Kronolith_Event_Ical
  • Kronolith_Event_Kolab
  • Kronolith_Event_Resource
  • Kronolith_Event_Sql
  • Kronolith_Exception
  • Kronolith_Factory_Calendars
  • Kronolith_Factory_Geo
  • Kronolith_Form_CreateCalendar
  • Kronolith_Form_CreateResource
  • Kronolith_Form_CreateResourceGroup
  • Kronolith_Form_DeleteCalendar
  • Kronolith_Form_DeleteResource
  • Kronolith_Form_DeleteResourceGroup
  • Kronolith_Form_EditCalendar
  • Kronolith_Form_EditRemoteCalendar
  • Kronolith_Form_EditResource
  • Kronolith_Form_EditResourceGroup
  • Kronolith_Form_SubscribeRemoteCalendar
  • Kronolith_Form_UnsubscribeRemoteCalendar
  • Kronolith_FreeBusy
  • Kronolith_FreeBusy_View
  • Kronolith_FreeBusy_View_Day
  • Kronolith_FreeBusy_View_Month
  • Kronolith_FreeBusy_View_Week
  • Kronolith_FreeBusy_View_Workweek
  • Kronolith_Geo_Base
  • Kronolith_Geo_Mysql
  • Kronolith_Geo_Sql
  • Kronolith_LoginTasks_SystemTask_Upgrade
  • Kronolith_LoginTasks_Task_PurgeEvents
  • Kronolith_Notification_Listener_AjaxStatus
  • Kronolith_Resource
  • Kronolith_Resource_Base
  • Kronolith_Resource_Group
  • Kronolith_Resource_Single
  • Kronolith_Storage
  • Kronolith_Storage_Kolab
  • Kronolith_Storage_Sql
  • Kronolith_Tagger
  • Kronolith_Test
  • Kronolith_View_Day
  • Kronolith_View_DeleteEvent
  • Kronolith_View_EditEvent
  • Kronolith_View_Event
  • Kronolith_View_ExportEvent
  • Kronolith_View_Month
  • Kronolith_View_Week
  • Kronolith_View_WorkWeek
  • Kronolith_View_Year
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * Free/Busy functionality.
  4:  *
  5:  * @author  Chuck Hagenbuch <chuck@horde.org>
  6:  * @package Kronolith
  7:  */
  8: class Kronolith_FreeBusy
  9: {
 10:     /**
 11:      * Generates the free/busy text for $calendars. Cache it for at least an
 12:      * hour, as well.
 13:      *
 14:      * @param string|array $calendars  The calendar to view free/busy slots for.
 15:      * @param integer $startstamp     The start of the time period to retrieve.
 16:      * @param integer $endstamp       The end of the time period to retrieve.
 17:      * @param boolean $returnObj      Default false. Return a vFreebusy object
 18:      *                                instead of text.
 19:      * @param string $user            Set organizer to this user.
 20:      *
 21:      * @return string  The free/busy text.
 22:      * @throws Horde_Exception
 23:      */
 24:     public static function generate($calendars, $startstamp = null,
 25:                                     $endstamp = null, $returnObj = false,
 26:                                     $user = null)
 27:     {
 28:         global $kronolith_shares;
 29: 
 30:         if (!is_array($calendars)) {
 31:             $calendars = array($calendars);
 32:         }
 33: 
 34:         if (!$user) {
 35:             /* Find a share and retrieve owner. */
 36:             foreach ($calendars as $calendar) {
 37:                 if (strpos($calendar, 'internal_') !== 0) {
 38:                     continue;
 39:                 }
 40:                 $calendar = substr($calendar, 9);
 41:                 try {
 42:                     $share = $kronolith_shares->getShare($calendar);
 43:                     $user = $share->get('owner');
 44:                     break;
 45:                 } catch (Horde_Exception $e) {
 46:                 }
 47:             }
 48:         }
 49: 
 50:         /* Default the start date to today. */
 51:         if (is_null($startstamp)) {
 52:             $startstamp = mktime(0, 0, 0);
 53:         }
 54: 
 55:         /* Default the end date to the start date + freebusy_days. */
 56:         if (is_null($endstamp) || $endstamp < $startstamp) {
 57:             $enddate = new Horde_Date($startstamp);
 58:             $enddate->mday += $GLOBALS['prefs']->getValue('freebusy_days');
 59:             $endstamp = $enddate->timestamp();
 60:         } else {
 61:             $enddate = new Horde_Date($endstamp);
 62:         }
 63: 
 64:         /* Get the Identity for the owner of the share. */
 65:         $identity = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Identity')->create($user);
 66:         $email = $identity->getValue('from_addr');
 67:         $cn = $identity->getValue('fullname');
 68:         if (empty($email) && empty($cn)) {
 69:             $cn = $user;
 70:         }
 71: 
 72:         /* Fetch events. */
 73:         $busy = array();
 74:         foreach ($calendars as $calendar) {
 75:             if (strpos($calendar, '_')) {
 76:                 @list($type, $calendar) = explode('_', $calendar, 2);
 77:             } else {
 78:                 $type = 'internal';
 79:             }
 80:             try {
 81:                 $driver = Kronolith::getDriver($type, $calendar);
 82:                 $events = $driver->listEvents(new Horde_Date($startstamp),
 83:                                               $enddate, true);
 84:                 Kronolith::mergeEvents($busy, $events);
 85:             } catch (Exception $e) {
 86:             }
 87:         }
 88: 
 89:         /* Create the new iCalendar. */
 90:         $vCal = new Horde_Icalendar();
 91:         $vCal->setAttribute('PRODID', '-//The Horde Project//Kronolith ' . $GLOBALS['registry']->getVersion() . '//EN');
 92:         $vCal->setAttribute('METHOD', 'PUBLISH');
 93: 
 94:         /* Create new vFreebusy. */
 95:         $vFb = Horde_Icalendar::newComponent('vfreebusy', $vCal);
 96:         $params = array();
 97:         if (!empty($cn)) {
 98:             $params['CN'] = $cn;
 99:         }
100:         if (!empty($email)) {
101:             $vFb->setAttribute('ORGANIZER', 'mailto:' . $email, $params);
102:         } else {
103:             $vFb->setAttribute('ORGANIZER', '', $params);
104:         }
105: 
106:         $vFb->setAttribute('DTSTAMP', $_SERVER['REQUEST_TIME']);
107:         $vFb->setAttribute('DTSTART', $startstamp);
108:         $vFb->setAttribute('DTEND', $endstamp);
109:         $vFb->setAttribute('URL', Horde::url('fb.php?u=' . $user, true, -1));
110: 
111:         /* Add all the busy periods. */
112:         foreach ($busy as $events) {
113:             foreach ($events as $event) {
114:                 if ($event->status == Kronolith::STATUS_FREE) {
115:                     continue;
116:                 }
117:                 if ($event->status == Kronolith::STATUS_CANCELLED) {
118:                     continue;
119:                 }
120: 
121:                 /* Horde_Icalendar_Vfreebusy only supports timestamps at the
122:                  * moment. */
123:                 $vFb->addBusyPeriod('BUSY', $event->start->timestamp(), null,
124:                                     $event->end->timestamp() - $event->start->timestamp());
125:             }
126:         }
127: 
128:         /* Remove the overlaps. */
129:         $vFb->simplify();
130:         $vCal->addComponent($vFb);
131: 
132:         /* Return the vFreebusy object if requested. */
133:         if ($returnObj) {
134:             return $vFb;
135:         }
136: 
137:         /* Generate the vCal file. */
138:         return $vCal->exportvCalendar();
139:     }
140: 
141:     /**
142:      * Retrieves the free/busy information for a given email address, if any
143:      * information is available.
144:      *
145:      * @param string $email  The email address to look for.
146:      * @param boolean $json  Whether to return the free/busy data as a simple
147:      *                       object suitable to be transferred as json.
148:      *
149:      * @return Horde_Icalendar_Vfreebusy  Free/busy component.
150:      * @throws Kronolith_Exception
151:      */
152:     public static function get($email, $json = false)
153:     {
154:         $default_domain = empty($GLOBALS['conf']['storage']['default_domain']) ? null : $GLOBALS['conf']['storage']['default_domain'];
155:         $rfc822 = new Horde_Mail_Rfc822();
156: 
157:         try {
158:             $res = $rfc822->parseAddressList($email, array(
159:                 'default_domain' => $default_domain
160:             ));
161:         } catch (Horde_Mail_Exception $e) {
162:             throw new Kronolith_Exception($e);
163:         }
164: 
165:         if (!count($res)) {
166:             throw new Kronolith_Exception(_("No valid email address found"));
167:         }
168: 
169:         $email = Horde_Mime_Address::writeAddress($res[0]->mailbox, $res[0]->host);
170: 
171:         /* Check if we can retrieve a VFB from the Free/Busy URL, if one is
172:          * set. */
173:         $url = self::getUrl($email);
174:         if ($url) {
175:             $url = trim($url);
176:             $http = $GLOBALS['injector']->getInstance('Horde_Core_Factory_HttpClient')->create();
177:             try {
178:                 $response = $http->get($url);
179:             } catch (Horde_Http_Client_Exception $e) {
180:                 throw new Kronolith_Exception(sprintf(_("The free/busy url for %s cannot be retrieved."), $email));
181:             }
182:             if ($response->code == 200 && $data = $response->getBody()) {
183:                 // Detect the charset of the iCalendar data.
184:                 $contentType = $response->getHeader('Content-Type');
185:                 if ($contentType && strpos($contentType, ';') !== false) {
186:                     list(,$charset,) = explode(';', $contentType);
187:                     $data = Horde_String::convertCharset($data, trim(str_replace('charset=', '', $charset)), 'UTF-8');
188:                 }
189: 
190:                 $vCal = new Horde_Icalendar();
191:                 $vCal->parsevCalendar($data, 'VCALENDAR');
192:                 $components = $vCal->getComponents();
193: 
194:                 $vCal = new Horde_Icalendar();
195:                 $vFb = Horde_Icalendar::newComponent('vfreebusy', $vCal);
196:                 $vFb->setAttribute('ORGANIZER', $email);
197:                 $found = false;
198:                 foreach ($components as $component) {
199:                     if ($component instanceof Horde_Icalendar_Vfreebusy) {
200:                         $found = true;
201:                         $vFb->merge($component);
202:                     }
203:                 }
204: 
205:                 if ($found) {
206:                     // @todo: actually store the results in the storage, so
207:                     // that they can be retrieved later. We should store the
208:                     // plain iCalendar data though, to avoid versioning
209:                     // problems with serialize iCalendar objects.
210:                     return $json ? self::toJson($vFb) : $vFb;
211:                 }
212:             }
213:         }
214: 
215:         /* Check storage driver. */
216:         $storage = $GLOBALS['injector']->getInstance('Kronolith_Factory_Storage')->create();
217: 
218:         try {
219:             $fb = $storage->search($email);
220:             return $json ? self::toJson($fb) : $fb;
221:         } catch (Horde_Exception_NotFound $e) {
222:             if ($url) {
223:                 throw new Kronolith_Exception(sprintf(_("No free/busy information found at the free/busy url of %s."), $email));
224:             }
225:             throw new Kronolith_Exception(sprintf(_("No free/busy url found for %s."), $email));
226:         }
227: 
228:         /* Or else return an empty VFB object. */
229:         $vCal = new Horde_Icalendar();
230:         $vFb = Horde_Icalendar::newComponent('vfreebusy', $vCal);
231:         $vFb->setAttribute('ORGANIZER', $email);
232: 
233:         return $json ? self::toJson($vFb) : $vFb;
234:     }
235: 
236:     /**
237:      * Searches address books for the freebusy URL for a given email address.
238:      *
239:      * @param string $email  The email address to look for.
240:      *
241:      * @return mixed  The url on success or false on failure.
242:      */
243:     public static function getUrl($email)
244:     {
245:         $sources = json_decode($GLOBALS['prefs']->getValue('search_sources'));
246:         if (empty($sources)) {
247:             $sources = array();
248:         }
249: 
250:         try {
251:             $result = $GLOBALS['registry']->call('contacts/getField',
252:                                                  array($email, 'freebusyUrl', $sources, true, true));
253:         } catch (Horde_Exception $e) {
254:             return false;
255:         }
256:         if (is_array($result)) {
257:             return array_shift($result);
258:         }
259: 
260:         return $result;
261:     }
262: 
263:     /**
264:      * Converts free/busy data to a simple object suitable to be transferred
265:      * as json.
266:      *
267:      * @param Horde_Icalendar_Vfreebusy $fb  A Free/busy component.
268:      *
269:      * @return object  A simple object representation.
270:      */
271:     function toJson(Horde_Icalendar_Vfreebusy $fb)
272:     {
273:         $json = new stdClass;
274:         $start = $fb->getStart();
275:         if ($start) {
276:             $start = new Horde_Date($start);
277:             $json->s = $start->dateString();
278:         }
279:         $end = $fb->getEnd();
280:         if ($end) {
281:             $end = new Horde_Date($end);
282:             $json->e = $end->dateString();
283:         }
284:         $b = $fb->getBusyPeriods();
285:         if (empty($b)) {
286:             $b = new StdClass();
287:         }
288:         $json->b = $b;
289:         return $json;
290:     }
291: 
292: }
293: 
API documentation generated by ApiGen