Mercurial > hg > rc1
comparison plugins/calendar/drivers/kolab/kolab_user_calendar.php @ 3:f6fe4b6ae66a
calendar plugin nearly as distributed
| author | Charlie Root |
|---|---|
| date | Sat, 13 Jan 2018 08:56:12 -0500 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 2:c828b0fd4a6e | 3:f6fe4b6ae66a |
|---|---|
| 1 <?php | |
| 2 | |
| 3 /** | |
| 4 * Kolab calendar storage class simulating a virtual user calendar | |
| 5 * | |
| 6 * @author Thomas Bruederli <bruederli@kolabsys.com> | |
| 7 * | |
| 8 * Copyright (C) 2014-2016, Kolab Systems AG <contact@kolabsys.com> | |
| 9 * | |
| 10 * This program is free software: you can redistribute it and/or modify | |
| 11 * it under the terms of the GNU Affero General Public License as | |
| 12 * published by the Free Software Foundation, either version 3 of the | |
| 13 * License, or (at your option) any later version. | |
| 14 * | |
| 15 * This program is distributed in the hope that it will be useful, | |
| 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 18 * GNU Affero General Public License for more details. | |
| 19 * | |
| 20 * You should have received a copy of the GNU Affero General Public License | |
| 21 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| 22 */ | |
| 23 | |
| 24 class kolab_user_calendar extends kolab_calendar | |
| 25 { | |
| 26 public $id = 'unknown'; | |
| 27 public $ready = false; | |
| 28 public $editable = false; | |
| 29 public $attachments = false; | |
| 30 public $subscriptions = false; | |
| 31 | |
| 32 protected $userdata = array(); | |
| 33 protected $timeindex = array(); | |
| 34 | |
| 35 | |
| 36 /** | |
| 37 * Default constructor | |
| 38 */ | |
| 39 public function __construct($user_or_folder, $calendar) | |
| 40 { | |
| 41 $this->cal = $calendar; | |
| 42 | |
| 43 // full user record is provided | |
| 44 if (is_array($user_or_folder)) { | |
| 45 $this->userdata = $user_or_folder; | |
| 46 $this->storage = new kolab_storage_folder_user($this->userdata['kolabtargetfolder'], '', $this->userdata); | |
| 47 } | |
| 48 else if ($user_or_folder instanceof kolab_storage_folder_user) { | |
| 49 $this->storage = $user_or_folder; | |
| 50 $this->userdata = $this->storage->ldaprec; | |
| 51 } | |
| 52 else { // get user record from LDAP | |
| 53 $this->storage = new kolab_storage_folder_user($user_or_folder); | |
| 54 $this->userdata = $this->storage->ldaprec; | |
| 55 } | |
| 56 | |
| 57 $this->ready = !empty($this->userdata['kolabtargetfolder']); | |
| 58 $this->storage->type = 'event'; | |
| 59 | |
| 60 if ($this->ready) { | |
| 61 // ID is derrived from the user's kolabtargetfolder attribute | |
| 62 $this->id = kolab_storage::folder_id($this->userdata['kolabtargetfolder'], true); | |
| 63 $this->imap_folder = $this->userdata['kolabtargetfolder']; | |
| 64 $this->name = $this->storage->name; | |
| 65 $this->parent = ''; // user calendars are top level | |
| 66 | |
| 67 // user-specific alarms settings win | |
| 68 $prefs = $this->cal->rc->config->get('kolab_calendars', array()); | |
| 69 if (isset($prefs[$this->id]['showalarms'])) | |
| 70 $this->alarms = $prefs[$this->id]['showalarms']; | |
| 71 } | |
| 72 } | |
| 73 | |
| 74 /** | |
| 75 * Getter for a nice and human readable name for this calendar | |
| 76 * | |
| 77 * @return string Name of this calendar | |
| 78 */ | |
| 79 public function get_name() | |
| 80 { | |
| 81 return $this->userdata['displayname'] ?: ($this->userdata['name'] ?: $this->userdata['mail']); | |
| 82 } | |
| 83 | |
| 84 /** | |
| 85 * Getter for the IMAP folder owner | |
| 86 * | |
| 87 * @param bool Return a fully qualified owner name (unused) | |
| 88 * | |
| 89 * @return string Name of the folder owner | |
| 90 */ | |
| 91 public function get_owner($fully_qualified = false) | |
| 92 { | |
| 93 return $this->userdata['mail']; | |
| 94 } | |
| 95 | |
| 96 /** | |
| 97 * | |
| 98 */ | |
| 99 public function get_title() | |
| 100 { | |
| 101 return trim($this->userdata['displayname'] . '; ' . $this->userdata['mail'], '; '); | |
| 102 } | |
| 103 | |
| 104 /** | |
| 105 * Getter for the name of the namespace to which the IMAP folder belongs | |
| 106 * | |
| 107 * @return string Name of the namespace (personal, other, shared) | |
| 108 */ | |
| 109 public function get_namespace() | |
| 110 { | |
| 111 return 'other user'; | |
| 112 } | |
| 113 | |
| 114 /** | |
| 115 * Getter for the top-end calendar folder name (not the entire path) | |
| 116 * | |
| 117 * @return string Name of this calendar | |
| 118 */ | |
| 119 public function get_foldername() | |
| 120 { | |
| 121 return $this->get_name(); | |
| 122 } | |
| 123 | |
| 124 /** | |
| 125 * Return color to display this calendar | |
| 126 */ | |
| 127 public function get_color($default = null) | |
| 128 { | |
| 129 // calendar color is stored in local user prefs | |
| 130 $prefs = $this->cal->rc->config->get('kolab_calendars', array()); | |
| 131 | |
| 132 if (!empty($prefs[$this->id]) && !empty($prefs[$this->id]['color'])) | |
| 133 return $prefs[$this->id]['color']; | |
| 134 | |
| 135 return $default ?: 'cc0000'; | |
| 136 } | |
| 137 | |
| 138 /** | |
| 139 * Compose an URL for CalDAV access to this calendar (if configured) | |
| 140 */ | |
| 141 public function get_caldav_url() | |
| 142 { | |
| 143 return false; | |
| 144 } | |
| 145 | |
| 146 /** | |
| 147 * Check subscription status of this folder | |
| 148 * | |
| 149 * @return boolean True if subscribed, false if not | |
| 150 */ | |
| 151 public function is_subscribed() | |
| 152 { | |
| 153 return $this->storage->is_subscribed(); | |
| 154 } | |
| 155 | |
| 156 /** | |
| 157 * Update properties of this calendar folder | |
| 158 * | |
| 159 * @see calendar_driver::edit_calendar() | |
| 160 */ | |
| 161 public function update(&$prop) | |
| 162 { | |
| 163 // don't change anything. | |
| 164 // let kolab_driver save props in local prefs | |
| 165 return $prop['id']; | |
| 166 } | |
| 167 | |
| 168 /** | |
| 169 * Getter for a single event object | |
| 170 */ | |
| 171 public function get_event($id) | |
| 172 { | |
| 173 // TODO: implement this | |
| 174 return $this->events[$id]; | |
| 175 } | |
| 176 | |
| 177 /** | |
| 178 * Get attachment body | |
| 179 * @see calendar_driver::get_attachment_body() | |
| 180 */ | |
| 181 public function get_attachment_body($id, $event) | |
| 182 { | |
| 183 if (!$event['calendar'] && ($ev = $this->get_event($event['id']))) { | |
| 184 $event['calendar'] = $ev['calendar']; | |
| 185 } | |
| 186 | |
| 187 if ($event['calendar'] && ($cal = $this->cal->get_calendar($event['calendar']))) { | |
| 188 return $cal->get_attachment_body($id, $event); | |
| 189 } | |
| 190 | |
| 191 return false; | |
| 192 } | |
| 193 | |
| 194 /** | |
| 195 * @param integer Event's new start (unix timestamp) | |
| 196 * @param integer Event's new end (unix timestamp) | |
| 197 * @param string Search query (optional) | |
| 198 * @param boolean Include virtual events (optional) | |
| 199 * @param array Additional parameters to query storage | |
| 200 * @param array Additional query to filter events | |
| 201 * | |
| 202 * @return array A list of event records | |
| 203 */ | |
| 204 public function list_events($start, $end, $search = null, $virtual = 1, $query = array(), $filter_query = null) | |
| 205 { | |
| 206 // convert to DateTime for comparisons | |
| 207 try { | |
| 208 $start_dt = new DateTime('@'.$start); | |
| 209 } | |
| 210 catch (Exception $e) { | |
| 211 $start_dt = new DateTime('@0'); | |
| 212 } | |
| 213 try { | |
| 214 $end_dt = new DateTime('@'.$end); | |
| 215 } | |
| 216 catch (Exception $e) { | |
| 217 $end_dt = new DateTime('today +10 years'); | |
| 218 } | |
| 219 | |
| 220 $limit_changed = null; | |
| 221 if (!empty($query)) { | |
| 222 foreach ($query as $q) { | |
| 223 if ($q[0] == 'changed' && $q[1] == '>=') { | |
| 224 try { $limit_changed = new DateTime('@'.$q[2]); } | |
| 225 catch (Exception $e) { /* ignore */ } | |
| 226 } | |
| 227 } | |
| 228 } | |
| 229 | |
| 230 // aggregate all calendar folders the user shares (but are not activated) | |
| 231 foreach (kolab_storage::list_user_folders($this->userdata, 'event', 2) as $foldername) { | |
| 232 $cal = new kolab_calendar($foldername, $this->cal); | |
| 233 foreach ($cal->list_events($start, $end, $search, 1) as $event) { | |
| 234 $uid = $event['id'] ?: $event['uid']; | |
| 235 $this->events[$uid] = $event; | |
| 236 $this->timeindex[$this->time_key($event)] = $uid; | |
| 237 } | |
| 238 } | |
| 239 | |
| 240 // get events from the user's free/busy feed (for quickview only) | |
| 241 $fbview = $this->cal->rc->config->get('calendar_include_freebusy_data', 1); | |
| 242 if ($fbview && ($fbview == 1 || !empty($_REQUEST['_quickview'])) && empty($search)) { | |
| 243 $this->fetch_freebusy($limit_changed); | |
| 244 } | |
| 245 | |
| 246 $events = array(); | |
| 247 foreach ($this->events as $event) { | |
| 248 // list events in requested time window | |
| 249 if ($event['start'] <= $end_dt && $event['end'] >= $start_dt && | |
| 250 (!$limit_changed || !$event['changed'] || $event['changed'] >= $limit_changed)) { | |
| 251 $events[] = $event; | |
| 252 } | |
| 253 } | |
| 254 | |
| 255 // avoid session race conditions that will loose temporary subscriptions | |
| 256 $this->cal->rc->session->nowrite = true; | |
| 257 | |
| 258 return $events; | |
| 259 } | |
| 260 | |
| 261 /** | |
| 262 * | |
| 263 * @param integer Date range start (unix timestamp) | |
| 264 * @param integer Date range end (unix timestamp) | |
| 265 * @param array Additional query to filter events | |
| 266 * @return integer Count | |
| 267 */ | |
| 268 public function count_events($start, $end = null, $filter_query = null) | |
| 269 { | |
| 270 // not implemented | |
| 271 return 0; | |
| 272 } | |
| 273 | |
| 274 /** | |
| 275 * Helper method to fetch free/busy data for the user and turn it into calendar data | |
| 276 */ | |
| 277 private function fetch_freebusy($limit_changed = null) | |
| 278 { | |
| 279 // ask kolab server first | |
| 280 try { | |
| 281 $request_config = array( | |
| 282 'store_body' => true, | |
| 283 'follow_redirects' => true, | |
| 284 ); | |
| 285 $request = libkolab::http_request(kolab_storage::get_freebusy_url($this->userdata['mail']), 'GET', $request_config); | |
| 286 $response = $request->send(); | |
| 287 | |
| 288 // authentication required | |
| 289 if ($response->getStatus() == 401) { | |
| 290 $request->setAuth($this->cal->rc->user->get_username(), $this->cal->rc->decrypt($_SESSION['password'])); | |
| 291 $response = $request->send(); | |
| 292 } | |
| 293 | |
| 294 if ($response->getStatus() == 200) | |
| 295 $fbdata = $response->getBody(); | |
| 296 | |
| 297 unset($request, $response); | |
| 298 } | |
| 299 catch (Exception $e) { | |
| 300 rcube::raise_error(array( | |
| 301 'code' => 900, | |
| 302 'type' => 'php', | |
| 303 'file' => __FILE__, | |
| 304 'line' => __LINE__, | |
| 305 'message' => "Error fetching free/busy information: " . $e->getMessage()), | |
| 306 true, false); | |
| 307 | |
| 308 return false; | |
| 309 } | |
| 310 | |
| 311 $statusmap = array( | |
| 312 'FREE' => 'free', | |
| 313 'BUSY' => 'busy', | |
| 314 'BUSY-TENTATIVE' => 'tentative', | |
| 315 'X-OUT-OF-OFFICE' => 'outofoffice', | |
| 316 'OOF' => 'outofoffice', | |
| 317 ); | |
| 318 $titlemap = array( | |
| 319 'FREE' => $this->cal->gettext('availfree'), | |
| 320 'BUSY' => $this->cal->gettext('availbusy'), | |
| 321 'BUSY-TENTATIVE' => $this->cal->gettext('availtentative'), | |
| 322 'X-OUT-OF-OFFICE' => $this->cal->gettext('availoutofoffice'), | |
| 323 ); | |
| 324 | |
| 325 // rcube::console('_fetch_freebusy', kolab_storage::get_freebusy_url($this->userdata['mail']), $fbdata); | |
| 326 | |
| 327 // parse free-busy information | |
| 328 $count = 0; | |
| 329 if ($fbdata) { | |
| 330 $ical = $this->cal->get_ical(); | |
| 331 $ical->import($fbdata); | |
| 332 if ($fb = $ical->freebusy) { | |
| 333 // consider 'changed >= X' queries | |
| 334 if ($limit_changed && $fb['created'] && $fb['created'] < $limit_changed) { | |
| 335 return 0; | |
| 336 } | |
| 337 | |
| 338 foreach ($fb['periods'] as $tuple) { | |
| 339 list($from, $to, $type) = $tuple; | |
| 340 $event = array( | |
| 341 'uid' => md5($this->id . $from->format('U') . '/' . $to->format('U')), | |
| 342 'calendar' => $this->id, | |
| 343 'changed' => $fb['created'] ?: new DateTime(), | |
| 344 'title' => $this->get_name() . ' ' . ($titlemap[$type] ?: $type), | |
| 345 'start' => $from, | |
| 346 'end' => $to, | |
| 347 'free_busy' => $statusmap[$type] ?: 'busy', | |
| 348 'className' => 'fc-type-freebusy', | |
| 349 'organizer' => array( | |
| 350 'email' => $this->userdata['mail'], | |
| 351 'name' => $this->userdata['displayname'], | |
| 352 ), | |
| 353 ); | |
| 354 | |
| 355 // avoid duplicate entries | |
| 356 $key = $this->time_key($event); | |
| 357 if (!$this->timeindex[$key]) { | |
| 358 $this->events[$event['uid']] = $event; | |
| 359 $this->timeindex[$key] = $event['uid']; | |
| 360 $count++; | |
| 361 } | |
| 362 } | |
| 363 } | |
| 364 } | |
| 365 | |
| 366 return $count; | |
| 367 } | |
| 368 | |
| 369 /** | |
| 370 * Helper to build a key for the absolute time slot the given event convers | |
| 371 */ | |
| 372 private function time_key($event) | |
| 373 { | |
| 374 return sprintf('%s/%s', $event['start']->format('U'), is_object($event['end']) ? $event['end']->format('U') : '0'); | |
| 375 } | |
| 376 | |
| 377 /** | |
| 378 * Create a new event record | |
| 379 * | |
| 380 * @see calendar_driver::new_event() | |
| 381 * | |
| 382 * @return mixed The created record ID on success, False on error | |
| 383 */ | |
| 384 public function insert_event($event) | |
| 385 { | |
| 386 return false; | |
| 387 } | |
| 388 | |
| 389 /** | |
| 390 * Update a specific event record | |
| 391 * | |
| 392 * @see calendar_driver::new_event() | |
| 393 * @return boolean True on success, False on error | |
| 394 */ | |
| 395 public function update_event($event, $exception_id = null) | |
| 396 { | |
| 397 return false; | |
| 398 } | |
| 399 | |
| 400 /** | |
| 401 * Delete an event record | |
| 402 * | |
| 403 * @see calendar_driver::remove_event() | |
| 404 * @return boolean True on success, False on error | |
| 405 */ | |
| 406 public function delete_event($event, $force = true) | |
| 407 { | |
| 408 return false; | |
| 409 } | |
| 410 | |
| 411 /** | |
| 412 * Restore deleted event record | |
| 413 * | |
| 414 * @see calendar_driver::undelete_event() | |
| 415 * @return boolean True on success, False on error | |
| 416 */ | |
| 417 public function restore_event($event) | |
| 418 { | |
| 419 return false; | |
| 420 } | |
| 421 } |
