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 } |