1: | <?php |
2: | |
3: | |
4: | |
5: | |
6: | |
7: | |
8: | namespace Aurora\Modules\Dav; |
9: | |
10: | use Afterlogic\DAV\CalDAV\Plugin; |
11: | use Aurora\Modules\Calendar\Enums\Permission; |
12: | |
13: | use function Sabre\Uri\split; |
14: | |
15: | |
16: | |
17: | |
18: | |
19: | |
20: | |
21: | |
22: | |
23: | if (!defined('DAV_ROOT')) { |
24: | define('DAV_ROOT', 'dav/server.php/'); |
25: | } |
26: | if (!defined('DAV_EMAIL_DEV')) { |
27: | define('DAV_EMAIL_DEV', '@'); |
28: | } |
29: | |
30: | class Client |
31: | { |
32: | public const PROP_RESOURCETYPE = '{DAV:}resourcetype'; |
33: | public const PROP_DISPLAYNAME = '{DAV:}displayname'; |
34: | public const PROP_CALENDAR_DESCRIPTION = '{urn:ietf:params:xml:ns:caldav}calendar-description'; |
35: | public const PROP_CALENDAR_ORDER = '{http://apple.com/ns/ical/}calendar-order'; |
36: | public const PROP_CALENDAR_COLOR = '{http://apple.com/ns/ical/}calendar-color'; |
37: | public const PROP_CALENDAR_DATA = '{urn:ietf:params:xml:ns:caldav}calendar-data'; |
38: | public const PROP_ADDRESSBOOK_DATA = '{urn:ietf:params:xml:ns:carddav}address-data'; |
39: | public const PROP_OWNER = '{DAV:}owner'; |
40: | public const PROP_CTAG = '{http://calendarserver.org/ns/}getctag'; |
41: | public const PROP_ETAG = '{DAV:}getetag'; |
42: | public const PROP_CURRENT_USER_PRINCIPAL = '{DAV:}current-user-principal'; |
43: | public const PROP_CALENDAR_HOME_SET = '{urn:ietf:params:xml:ns:caldav}calendar-home-set'; |
44: | public const PROP_CALENDAR_INVITE = '{' . Plugin::NS_CALENDARSERVER . '}invite'; |
45: | public const PROP_ADDRESSBOOK_HOME_SET = '{urn:ietf:params:xml:ns:carddav}addressbook-home-set'; |
46: | public const PROP_GROUP_MEMBERSHIP = '{DAV:}group-membership'; |
47: | public const PROP_GROUP_MEMBER_SET = '{DAV:}group-member-set'; |
48: | |
49: | |
50: | |
51: | |
52: | |
53: | public $baseUrl; |
54: | |
55: | |
56: | |
57: | |
58: | protected $user; |
59: | |
60: | |
61: | |
62: | |
63: | protected $connected; |
64: | |
65: | |
66: | |
67: | |
68: | protected $protocol = 'http'; |
69: | |
70: | |
71: | |
72: | |
73: | protected $port = 80; |
74: | |
75: | |
76: | |
77: | |
78: | protected $server; |
79: | |
80: | |
81: | |
82: | |
83: | public $isCustomServer = false; |
84: | |
85: | |
86: | |
87: | |
88: | public $client; |
89: | |
90: | |
91: | |
92: | |
93: | |
94: | |
95: | public function __construct($baseUrl, $user, $pass) |
96: | { |
97: | $this->client = new \Afterlogic\DAV\Client( |
98: | array( |
99: | 'baseUri' => $baseUrl, |
100: | 'userName' => $user, |
101: | 'password' => $pass |
102: | ) |
103: | ); |
104: | |
105: | $this->user = $user; |
106: | |
107: | $aUrlParts = parse_url($baseUrl); |
108: | |
109: | if (isset($aUrlParts['port'])) { |
110: | $this->port = $aUrlParts['port']; |
111: | } |
112: | |
113: | $matches = array(); |
114: | if (preg_match('#^(https?)://([a-z0-9.-]+)(:([0-9]+))?(/.*)$#', $baseUrl, $matches)) { |
115: | $this->protocol = $matches[1]; |
116: | $this->server = $matches[2]; |
117: | $this->baseUrl = $matches[5]; |
118: | } |
119: | |
120: | $this->connected = false; |
121: | } |
122: | |
123: | |
124: | |
125: | |
126: | public function Connect() |
127: | { |
128: | if ($this->connected === false) { |
129: | $this->connected = $this->testConnection(); |
130: | } |
131: | return $this->connected; |
132: | } |
133: | |
134: | |
135: | |
136: | |
137: | public function testConnection() |
138: | { |
139: | $res = $this->client->options_ex(); |
140: | $this->isCustomServer = $res['custom-server']; |
141: | |
142: | return true; |
143: | } |
144: | |
145: | |
146: | |
147: | |
148: | public function GetBaseUrl() |
149: | { |
150: | return $this->baseUrl; |
151: | } |
152: | |
153: | |
154: | |
155: | |
156: | public function GetRootUrl($url) |
157: | { |
158: | $urlParts = parse_url($url); |
159: | $path = '/'; |
160: | if (isset($urlParts['path'])) { |
161: | $path = $urlParts['path']; |
162: | } |
163: | return $path; |
164: | } |
165: | |
166: | |
167: | |
168: | |
169: | public function GetUser() |
170: | { |
171: | return $this->user; |
172: | } |
173: | |
174: | |
175: | |
176: | |
177: | public function GetServer() |
178: | { |
179: | return $this->server; |
180: | } |
181: | |
182: | |
183: | |
184: | |
185: | public function GetServerUrl() |
186: | { |
187: | $sPort = ''; |
188: | if ((int)$this->port != 80) { |
189: | $sPort = ':' . $this->port; |
190: | } |
191: | return $this->protocol . '://' . $_SERVER['SERVER_NAME'] . $sPort; |
192: | } |
193: | |
194: | |
195: | |
196: | |
197: | |
198: | |
199: | |
200: | public function CreateItem($sUrl, $sData) |
201: | { |
202: | return $this->client->request( |
203: | 'PUT', |
204: | $sUrl, |
205: | $sData, |
206: | array( |
207: | 'If-None-Match' => '*' |
208: | ) |
209: | ); |
210: | } |
211: | |
212: | |
213: | |
214: | |
215: | |
216: | |
217: | |
218: | public function UpdateItem($sUrl, $sData, $sEtag = '*') |
219: | { |
220: | return $this->client->request( |
221: | 'PUT', |
222: | $sUrl, |
223: | $sData, |
224: | array( |
225: | |
226: | ) |
227: | ); |
228: | } |
229: | |
230: | |
231: | |
232: | |
233: | |
234: | |
235: | |
236: | public function MoveItem($url, $newUrl) |
237: | { |
238: | return $this->client->request( |
239: | 'MOVE', |
240: | $url, |
241: | '', |
242: | array( |
243: | 'destination' => $newUrl |
244: | ) |
245: | ); |
246: | } |
247: | |
248: | |
249: | |
250: | |
251: | |
252: | |
253: | |
254: | |
255: | |
256: | |
257: | |
258: | public function createCalendar( |
259: | $sSystemName, |
260: | $sDisplayName, |
261: | $sDescription, |
262: | $iOrder, |
263: | $sColor, |
264: | $sCalendarHome |
265: | ) |
266: | { |
267: | $xml = |
268: | '<?xml version="1.0" encoding="UTF-8" ?> |
269: | <c:mkcalendar xmlns:c="'.\Sabre\CalDAV\Plugin::NS_CALDAV.'" xmlns:d="DAV:" xmlns:ic="http://apple.com/ns/ical/"> |
270: | <d:set> |
271: | <d:prop> |
272: | <d:displayname>'.$sDisplayName.'</d:displayname> |
273: | <c:calendar-description>'.$sDescription.'</c:calendar-description> |
274: | <ic:calendar-order>'.$iOrder.'</ic:calendar-order> |
275: | <ic:calendar-color>'.$sColor.'</ic:calendar-color> |
276: | </d:prop> |
277: | </d:set> |
278: | </c:mkcalendar>'; |
279: | |
280: | return $this->client->request( |
281: | 'MKCALENDAR', |
282: | $sCalendarHome.$sSystemName, |
283: | $xml, |
284: | array( |
285: | 'Content-Type' => 'application/xml', |
286: | 'Depth' => '1' |
287: | ) |
288: | ); |
289: | } |
290: | |
291: | |
292: | |
293: | |
294: | |
295: | |
296: | |
297: | |
298: | |
299: | |
300: | public function updateCalendar($sCalendarId, $sDisplayName, $sDescription, $iOrder, $sColor) |
301: | { |
302: | return $this->client->propPatch( |
303: | $sCalendarId, |
304: | array( |
305: | self::PROP_DISPLAYNAME => $sDisplayName, |
306: | self::PROP_CALENDAR_DESCRIPTION => $sDescription, |
307: | self::PROP_CALENDAR_ORDER => $iOrder, |
308: | self::PROP_CALENDAR_COLOR => $sColor |
309: | ) |
310: | ); |
311: | } |
312: | |
313: | |
314: | |
315: | |
316: | |
317: | |
318: | |
319: | public function updateCalendarColor($sCalendarId, $sColor) |
320: | { |
321: | return $this->client->propPatch( |
322: | $sCalendarId, |
323: | array( |
324: | self::PROP_CALENDAR_COLOR => $sColor |
325: | ) |
326: | ); |
327: | } |
328: | |
329: | |
330: | |
331: | |
332: | public function DeleteItem($url) |
333: | { |
334: | return $this->client->request('DELETE', $url); |
335: | } |
336: | |
337: | |
338: | |
339: | |
340: | |
341: | |
342: | |
343: | public function QueryCal($filter, $url = '') |
344: | { |
345: | $xml = |
346: | '<?xml version="1.0" encoding="utf-8" ?> |
347: | <c:calendar-query xmlns:d="DAV:" xmlns:c="'.\Sabre\CalDAV\Plugin::NS_CALDAV.'"> |
348: | <d:prop> |
349: | <d:getetag/> |
350: | <c:calendar-data/> |
351: | </d:prop> |
352: | '.$filter.' |
353: | </c:calendar-query>'; |
354: | |
355: | $res = array(); |
356: | try { |
357: | $res = $this->client->request( |
358: | 'REPORT', |
359: | $url, |
360: | $xml, |
361: | array( |
362: | 'Content-Type' => 'application/xml', |
363: | 'Depth' => '1' |
364: | ) |
365: | ); |
366: | } catch(\Sabre\DAV\Exception $ex) { |
367: | return false; |
368: | } |
369: | |
370: | $aStatus = $this->client->parseMultiStatus($res['body']); |
371: | |
372: | $report = array(); |
373: | foreach ($aStatus as $key => $props) { |
374: | $response = array(); |
375: | if (count($props) > 0) { |
376: | $response['url'] = $url; |
377: | $response['href'] = basename($key); |
378: | $response['etag'] = isset($props[200]) ? preg_replace('/^"?([^"]+)"?/', '$1', $props[200][self::PROP_ETAG]) : ''; |
379: | $response['data'] = isset($props[200]) ? $props[200][self::PROP_CALENDAR_DATA] : ''; |
380: | |
381: | $report[] = $response; |
382: | } |
383: | } |
384: | return $report; |
385: | } |
386: | |
387: | |
388: | |
389: | |
390: | |
391: | |
392: | |
393: | public function QueryCardsInfo($filter, $url = '') |
394: | { |
395: | $xml = |
396: | '<?xml version="1.0" encoding="utf-8" ?> |
397: | <c:addressbook-query xmlns:d="DAV:" xmlns:c="'.\Sabre\CardDAV\Plugin::NS_CARDDAV.'"> |
398: | <d:prop> |
399: | <d:getetag/> |
400: | </d:prop> |
401: | '.$filter.' |
402: | </c:addressbook-query>'; |
403: | |
404: | $res = array(); |
405: | try { |
406: | $res = $this->client->request( |
407: | 'REPORT', |
408: | $url, |
409: | $xml, |
410: | array( |
411: | 'Content-Type' => 'application/xml', |
412: | 'Depth' => '1' |
413: | ) |
414: | ); |
415: | } catch(\Sabre\DAV\Exception $ex) { |
416: | return false; |
417: | } |
418: | |
419: | $aStatus = $this->client->parseMultiStatus($res['body']); |
420: | |
421: | $report = array(); |
422: | foreach ($aStatus as $key => $props) { |
423: | $response = array(); |
424: | $response['href'] = basename($key); |
425: | $response['etag'] = isset($props[200]) ? preg_replace('/^"?([^"]+)"?/', '$1', $props[200][self::PROP_ETAG]) : ''; |
426: | $response['data'] = isset($props[200]) && isset($props[200][self::PROP_ADDRESSBOOK_DATA]) ? $props[200][self::PROP_ADDRESSBOOK_DATA] : ''; |
427: | |
428: | $report[] = $response; |
429: | } |
430: | return $report; |
431: | } |
432: | |
433: | |
434: | |
435: | |
436: | |
437: | |
438: | |
439: | public function QueryCards($calendar_url, $urls = []) |
440: | { |
441: | $aHrefs = []; |
442: | foreach ($urls as $url) { |
443: | $aHrefs[] = ' <d:href>' . $url . '</d:href>'; |
444: | } |
445: | $sHrefs = implode("\n", $aHrefs); |
446: | $xml = |
447: | '<?xml version="1.0" encoding="utf-8" ?> |
448: | <c:addressbook-multiget xmlns:d="DAV:" xmlns:c="'.\Sabre\CardDAV\Plugin::NS_CARDDAV.'"> |
449: | <d:prop> |
450: | <d:getetag /> |
451: | <c:address-data /> |
452: | </d:prop> |
453: | ' . $sHrefs . ' |
454: | </c:addressbook-multiget>'; |
455: | |
456: | $res = array(); |
457: | try { |
458: | $res = $this->client->request( |
459: | 'REPORT', |
460: | $calendar_url, |
461: | $xml, |
462: | array( |
463: | 'Content-Type' => 'application/xml', |
464: | 'Depth' => '1' |
465: | ) |
466: | ); |
467: | } catch(\Sabre\DAV\Exception $ex) { |
468: | return false; |
469: | } |
470: | |
471: | $aStatus = $this->client->parseMultiStatus($res['body']); |
472: | |
473: | $report = array(); |
474: | foreach ($aStatus as $key => $props) { |
475: | $response = array(); |
476: | $response['href'] = basename($key); |
477: | $response['etag'] = isset($props[200]) ? preg_replace('/^"?([^"]+)"?/', '$1', $props[200][self::PROP_ETAG]) : ''; |
478: | $response['data'] = isset($props[200]) && isset($props[200][self::PROP_ADDRESSBOOK_DATA]) ? $props[200][self::PROP_ADDRESSBOOK_DATA] : ''; |
479: | |
480: | $report[] = $response; |
481: | } |
482: | return $report; |
483: | } |
484: | |
485: | public function GetVcardsInfo($url = '', $sSearch = '', $sGroupId = '') |
486: | { |
487: | $sFilter = ''; |
488: | $sGroupFilter = ''; |
489: | $sSearchFilter = ''; |
490: | |
491: | if (!empty($sGroupId)) { |
492: | $sGroupFilter = |
493: | ' <c:prop-filter name="CATEGORIES"> |
494: | <c:text-match icollation="i;octet" match-type="contains">'.$sGroupId.'</c:text-match> |
495: | </c:prop-filter>'; |
496: | } |
497: | |
498: | if (!empty($sSearch)) { |
499: | $sSearchFilter = |
500: | ' <c:prop-filter name="FN"> |
501: | <c:text-match icollation="i;octet" match-type="contains">'.$sSearch.'</c:text-match> |
502: | </c:prop-filter> |
503: | <c:prop-filter name="N"> |
504: | <c:text-match icollation="i;octet" match-type="contains">'.$sSearch.'</c:text-match> |
505: | </c:prop-filter> |
506: | <c:prop-filter name="EMAIL"> |
507: | <c:text-match icollation="i;octet" match-type="contains">'.$sSearch.'</c:text-match> |
508: | </c:prop-filter> |
509: | <c:prop-filter name="NICKNAME"> |
510: | <c:text-match icollation="i;octet" match-type="contains">'.$sSearch.'</c:text-match> |
511: | </c:prop-filter>'; |
512: | } |
513: | |
514: | if (!empty($sSearch) || !empty($sGroupId)) { |
515: | $sFilter = |
516: | ' <c:filter> |
517: | ' . $sSearchFilter |
518: | . $sGroupFilter . ' |
519: | </c:filter>'; |
520: | } |
521: | |
522: | return $this->QueryCardsInfo($sFilter, $url); |
523: | } |
524: | |
525: | public function GetVcards($url, $urls = []) |
526: | { |
527: | list($path, $name) = split($url); |
528: | $url = $path . '/' . rawurlencode($name); |
529: | |
530: | return $this->QueryCards($url, $urls); |
531: | } |
532: | |
533: | |
534: | |
535: | |
536: | |
537: | |
538: | |
539: | public function GetTimeRange($start = null, $end = null) |
540: | { |
541: | $timeRange = ''; |
542: | $startRange = ''; |
543: | $endRange = ''; |
544: | if (isset($start) || isset($end)) { |
545: | if (isset($start)) { |
546: | $startRange = 'start="'.$start.'"'; |
547: | } |
548: | if (isset($end)) { |
549: | $endRange = 'end="'.$end.'"'; |
550: | } |
551: | $timeRange = sprintf('<c:time-range %s %s/>', $startRange, $endRange); |
552: | } |
553: | return $timeRange; |
554: | } |
555: | |
556: | |
557: | |
558: | |
559: | |
560: | |
561: | |
562: | |
563: | public function getEvents($url = '', $start = null, $finish = null) |
564: | { |
565: | $timeRange = $this->GetTimeRange($start, $finish); |
566: | $url = rtrim($url, '/') . '/'; |
567: | list($path, $name) = split($url); |
568: | $url = $path . '/' . rawurlencode($name); |
569: | $filter = |
570: | '<c:filter> |
571: | <c:comp-filter name="VCALENDAR"> |
572: | <c:comp-filter name="VEVENT"> |
573: | '.$timeRange.' |
574: | </c:comp-filter> |
575: | </c:comp-filter> |
576: | </c:filter>'; |
577: | return $this->QueryCal($filter, $url); |
578: | } |
579: | |
580: | |
581: | |
582: | |
583: | |
584: | |
585: | |
586: | public function GetEventByUid($sCalendarUrl, $sUid) |
587: | { |
588: | $filter = |
589: | ' <c:filter> |
590: | <c:comp-filter name="VCALENDAR"> |
591: | <c:comp-filter name="VEVENT"> |
592: | <c:prop-filter name="UID"> |
593: | <c:text-match icollation="i;octet">'.$sUid.'</c:text-match> |
594: | </c:prop-filter> |
595: | </c:comp-filter> |
596: | </c:comp-filter> |
597: | </c:filter>'; |
598: | $result = $this->QueryCal($filter, $sCalendarUrl); |
599: | if ($result !== false) { |
600: | return current($result); |
601: | } else { |
602: | return false; |
603: | } |
604: | } |
605: | |
606: | |
607: | |
608: | |
609: | |
610: | |
611: | public function GetItem($url = '') |
612: | { |
613: | $res = $this->client->request('GET', $url); |
614: | if ($res !== false) { |
615: | return $res['body']; |
616: | } |
617: | return $res; |
618: | } |
619: | |
620: | |
621: | |
622: | |
623: | |
624: | |
625: | |
626: | |
627: | public function GetAlarms($url = '', $start = null, $finish = null) |
628: | { |
629: | $timeRange = $this->GetTimeRange($start, $finish); |
630: | $url = rtrim($url, '/') . '/'; |
631: | |
632: | $filter = |
633: | '<c:filter> |
634: | <c:comp-filter name="VCALENDAR"> |
635: | <c:comp-filter name="VEVENT"> |
636: | <c:comp-filter name="VALARM"> |
637: | '.$timeRange.' |
638: | </c:comp-filter> |
639: | </c:comp-filter> |
640: | </c:comp-filter> |
641: | </c:filter>'; |
642: | |
643: | |
644: | return $this->QueryCal($filter, $url); |
645: | } |
646: | |
647: | |
648: | |
649: | |
650: | |
651: | |
652: | |
653: | |
654: | |
655: | |
656: | |
657: | public function GetTodos($start, $finish, $completed = false, $cancelled = false, $url = '') |
658: | { |
659: | $timeRange = $this->GetTimeRange($start, $finish); |
660: | |
661: | |
662: | $negateCancelled = ($cancelled === true ? 'no' : 'yes'); |
663: | $negateCompleted = ($cancelled === true ? 'no' : 'yes'); |
664: | |
665: | $filter = |
666: | '<c:filter> |
667: | <c:comp-filter name="VCALENDAR"> |
668: | <c:comp-filter name="VTODO"> |
669: | <c:prop-filter name="STATUS"> |
670: | <c:text-match negate-condition="'.$negateCompleted.'">COMPLETED</c:text-match> |
671: | </c:prop-filter> |
672: | <c:prop-filter name="STATUS"> |
673: | <C:text-match negate-condition="'.$negateCancelled.'">CANCELLED</c:text-match> |
674: | </c:prop-filter> |
675: | ' . $timeRange . ' |
676: | </c:comp-filter> |
677: | </c:comp-filter> |
678: | </c:filter>'; |
679: | |
680: | return $this->QueryCal($filter, $url); |
681: | } |
682: | |
683: | |
684: | |
685: | |
686: | |
687: | |
688: | public function getCalendar($url = '') |
689: | { |
690: | $filter = |
691: | '<c:filter> |
692: | <c:comp-filter name="VCALENDAR"></c:comp-filter> |
693: | </c:filter>'; |
694: | |
695: | return $this->QueryCal($filter, $url); |
696: | } |
697: | |
698: | public function GetCurrentPrincipal() |
699: | { |
700: | $res = $this->client->propFind('', array(self::PROP_CURRENT_USER_PRINCIPAL)); |
701: | |
702: | return $res[self::PROP_CURRENT_USER_PRINCIPAL]; |
703: | } |
704: | |
705: | |
706: | |
707: | |
708: | public function GetPrincipalMembers($principal = '') |
709: | { |
710: | $res = array(); |
711: | try { |
712: | $res = $this->client->propFind( |
713: | $principal, |
714: | array( |
715: | self::PROP_GROUP_MEMBERSHIP |
716: | ), |
717: | 1 |
718: | ); |
719: | } catch(\Exception $ex) { |
720: | return []; |
721: | } |
722: | |
723: | return $res[self::PROP_GROUP_MEMBERSHIP]; |
724: | } |
725: | |
726: | |
727: | |
728: | |
729: | public function GetCalendarHomeSet($principalUrl = '') |
730: | { |
731: | $props = $this->client->propFind( |
732: | $principalUrl, |
733: | array( |
734: | self::PROP_CALENDAR_HOME_SET |
735: | ) |
736: | ); |
737: | return $props[self::PROP_CALENDAR_HOME_SET]; |
738: | } |
739: | |
740: | |
741: | |
742: | |
743: | public function GetAddressBookHomeSet($principalUrl = '') |
744: | { |
745: | $props = $this->client->propFind( |
746: | $principalUrl, |
747: | array( |
748: | self::PROP_ADDRESSBOOK_HOME_SET |
749: | ) |
750: | ); |
751: | return $props[self::PROP_ADDRESSBOOK_HOME_SET]; |
752: | } |
753: | |
754: | public function GetAddressBooks($url = '') |
755: | { |
756: | $aProps = $this->client->propFind( |
757: | $url, |
758: | array( |
759: | self::PROP_RESOURCETYPE, |
760: | self::PROP_DISPLAYNAME |
761: | ), |
762: | 1 |
763: | ); |
764: | return $aProps; |
765: | } |
766: | |
767: | public function getCalendars($url = '') |
768: | { |
769: | $calendars = array(); |
770: | |
771: | if (class_exists('\Aurora\Modules\Calendar\Classes\Calendar')) { |
772: | $aProps = $this->client->propFind( |
773: | $url, |
774: | array( |
775: | self::PROP_RESOURCETYPE, |
776: | self::PROP_DISPLAYNAME, |
777: | self::PROP_OWNER, |
778: | self::PROP_CTAG, |
779: | self::PROP_CALENDAR_DESCRIPTION, |
780: | self::PROP_CALENDAR_COLOR, |
781: | self::PROP_CALENDAR_ORDER, |
782: | self::PROP_CALENDAR_INVITE |
783: | ), |
784: | 1 |
785: | ); |
786: | |
787: | foreach ($aProps as $key => $props) { |
788: | if ($props['{DAV:}resourcetype']->is('{'.\Sabre\CalDAV\Plugin::NS_CALDAV.'}calendar') && |
789: | !$props['{DAV:}resourcetype']->is('{'.\Sabre\CalDAV\Plugin::NS_CALENDARSERVER.'}shared')) { |
790: | $calendar = new \Aurora\Modules\Calendar\Classes\Calendar($key); |
791: | |
792: | $calendar->CTag = $props[self::PROP_CTAG]; |
793: | $calendar->DisplayName = $props[self::PROP_DISPLAYNAME]; |
794: | $calendar->Principals[] = isset($props[self::PROP_OWNER]) ? $props[self::PROP_OWNER] : ''; |
795: | $calendar->Description = isset($props[self::PROP_CALENDAR_DESCRIPTION]) ? $props[self::PROP_CALENDAR_DESCRIPTION] : ''; |
796: | $calendar->Color = isset($props[self::PROP_CALENDAR_COLOR]) ? $props[self::PROP_CALENDAR_COLOR] : ''; |
797: | if (strlen($calendar->Color) > 7) { |
798: | $calendar->Color = substr($calendar->Color, 0, 7); |
799: | } |
800: | |
801: | if (isset($props[self::PROP_CALENDAR_ORDER])) { |
802: | $calendar->Order = $props[self::PROP_CALENDAR_ORDER]; |
803: | } |
804: | if (isset($props[self::PROP_CALENDAR_INVITE])) { |
805: | $aInvitesProp = $props[self::PROP_CALENDAR_INVITE]; |
806: | |
807: | foreach ($aInvitesProp as $aInviteProp) { |
808: | if ($aInviteProp['name'] === '{' . Plugin::NS_CALENDARSERVER . '}user') { |
809: | $aShare = []; |
810: | |
811: | foreach ($aInviteProp['value'] as $aValue) { |
812: | switch ($aValue['name']) { |
813: | case '{DAV:}href': |
814: | list(, $aShare['email']) = split($aValue['value']); |
815: | |
816: | break; |
817: | case '{http://calendarserver.org/ns/}access': |
818: | if (isset($aValue['value'][0])) { |
819: | $aShare['access'] = $aValue['value'][0]['name'] === '{' . Plugin::NS_CALENDARSERVER . '}read-write' |
820: | ? \Afterlogic\DAV\Permission::Write |
821: | : \Afterlogic\DAV\Permission::Read; |
822: | } |
823: | break; |
824: | } |
825: | } |
826: | if (!empty($aShare)) { |
827: | $calendar->Shares[] = $aShare; |
828: | } |
829: | } |
830: | } |
831: | } |
832: | |
833: | $calendars[$calendar->Id] = $calendar; |
834: | } |
835: | } |
836: | } |
837: | return $calendars; |
838: | } |
839: | |
840: | public function GetVcardByUid($uid, $url = '') |
841: | { |
842: | $filter = ""; |
843: | |
844: | if ($uid) { |
845: | $filter = |
846: | '<c:filter> |
847: | <c:prop-filter name="UID"> |
848: | <c:text-match collation="i;unicode-casemap" match-type="equals">'.$uid.'</c:text-match> |
849: | </c:prop-filter> |
850: | </c:filter>'; |
851: | } |
852: | |
853: | return $this->QueryCardsInfo($filter, $url); |
854: | } |
855: | |
856: | public function GetProxies($sProxy) |
857: | { |
858: | $res = array(); |
859: | try { |
860: | $res = $this->client->propFind( |
861: | $sProxy, |
862: | array( |
863: | self::PROP_GROUP_MEMBER_SET |
864: | ), |
865: | 1 |
866: | ); |
867: | } catch(\Exception $ex) { |
868: | return []; |
869: | } |
870: | |
871: | return $res[self::PROP_GROUP_MEMBER_SET]; |
872: | } |
873: | |
874: | public function AddProxy($proxy, $to) |
875: | { |
876: | $sProxyStr = ''; |
877: | $aCurrentProxies = array(); |
878: | $duplicate = false; |
879: | |
880: | $aCurrentProxies = $this->GetProxies($proxy); |
881: | |
882: | if ($aCurrentProxies) { |
883: | foreach ($aCurrentProxies as $sCurrentProxy => $val) { |
884: | $sCurrentProxy = ltrim($sCurrentProxy, DAV_ROOT); |
885: | if ($sCurrentProxy == $proxy) { |
886: | $duplicate = true; |
887: | } |
888: | $sProxyStr .= '<d:href>' . $sCurrentProxy . '</d:href>'; |
889: | } |
890: | } |
891: | if ($duplicate) { |
892: | return false; |
893: | } |
894: | |
895: | $sProxyStr .= '<d:href>' . \Afterlogic\DAV\Constants::PRINCIPALS_PREFIX . $to . '</d:href>'; |
896: | |
897: | return $this->client->propPatch($proxy, array('group-member-set'=>$sProxyStr)); |
898: | } |
899: | |
900: | public function DeleteProxy($proxy, $to) |
901: | { |
902: | $aCurrentProxies = $this->GetProxies($proxy); |
903: | |
904: | $sProxyStr = ""; |
905: | |
906: | if ($aCurrentProxies) { |
907: | foreach ($aCurrentProxies as $sCurrentProxy => $val) { |
908: | $sCurrentProxy = ltrim($sCurrentProxy, DAV_ROOT); |
909: | $sProxyStr .= '<d:href>' . $sCurrentProxy . '</d:href>'; |
910: | } |
911: | } |
912: | |
913: | $sProxyStr = str_replace('<d:href>' . \Afterlogic\DAV\Constants::PRINCIPALS_PREFIX . $to . '</d:href>', '', $sProxyStr); |
914: | |
915: | return $this->client->propPatch( |
916: | $proxy, |
917: | array( |
918: | 'group-member-set'=>$sProxyStr |
919: | ) |
920: | ); |
921: | } |
922: | |
923: | |
924: | |
925: | |
926: | |
927: | |
928: | |
929: | public function GetCalendarProxiedFor($principalUrl) |
930: | { |
931: | $body = |
932: | '<?xml version="1.0"?> |
933: | <d:expand-property xmlns:d="DAV:"> |
934: | <d:property name="calendar-proxy-read-for" namespace="'.\Sabre\CalDAV\Plugin::NS_CALENDARSERVER.'"> |
935: | <d:property name="calendar-home-set" namespace="'.\Sabre\CalDAV\Plugin::NS_CALDAV.'" /> |
936: | </d:property> |
937: | <d:property name="calendar-proxy-write-for" namespace="'.\Sabre\CalDAV\Plugin::NS_CALENDARSERVER.'"> |
938: | <d:property name="calendar-home-set" namespace="'.\Sabre\CalDAV\Plugin::NS_CALDAV.'" /> |
939: | </d:property> |
940: | </d:expand-property>'; |
941: | |
942: | $res = $this->client->request( |
943: | 'REPORT', |
944: | $principalUrl, |
945: | $body, |
946: | array( |
947: | 'Content-Type' => 'application/xml', |
948: | 'Depth' => '1' |
949: | ) |
950: | ); |
951: | |
952: | if (isset($res['body'])) { |
953: | $data = new \DOMDocument(); |
954: | |
955: | $data->loadXML($res['body'], LIBXML_NOERROR | LIBXML_NOWARNING | LIBXML_NSCLEAN); |
956: | $xp = new \DOMXPath($data); |
957: | |
958: | $xp->registerNamespace('c', \Sabre\CalDAV\Plugin::NS_CALDAV); |
959: | $xp->registerNamespace('cs', \Sabre\CalDAV\Plugin::NS_CALENDARSERVER); |
960: | $xp->registerNamespace('d', 'urn:dav'); |
961: | $values = array(); |
962: | |
963: | $result = $xp->query("/d:multistatus/d:response/d:propstat/d:prop/cs:calendar-proxy-read-for/d:response/d:propstat/d:prop/c:calendar-home-set/d:href"); |
964: | foreach ($result as $elem) { |
965: | $values[] = array( |
966: | 'href' => $elem->nodeValue, |
967: | 'mode' => 'read' |
968: | ); |
969: | } |
970: | |
971: | $result = $xp->query("/d:multistatus/d:response/d:propstat/d:prop/cs:calendar-proxy-write-for/d:response/d:propstat/d:prop/c:calendar-home-set/d:href"); |
972: | foreach ($result as $elem) { |
973: | $values[] = array( |
974: | 'href' => $elem->nodeValue, |
975: | 'mode' => 'write' |
976: | ); |
977: | } |
978: | |
979: | return $values; |
980: | } |
981: | return array(); |
982: | } |
983: | } |
984: | |