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: | $xml = |
267: | '<?xml version="1.0" encoding="UTF-8" ?> |
268: | <c:mkcalendar xmlns:c="' . \Sabre\CalDAV\Plugin::NS_CALDAV . '" xmlns:d="DAV:" xmlns:ic="http://apple.com/ns/ical/"> |
269: | <d:set> |
270: | <d:prop> |
271: | <d:displayname>' . $sDisplayName . '</d:displayname> |
272: | <c:calendar-description>' . $sDescription . '</c:calendar-description> |
273: | <ic:calendar-order>' . $iOrder . '</ic:calendar-order> |
274: | <ic:calendar-color>' . $sColor . '</ic:calendar-color> |
275: | </d:prop> |
276: | </d:set> |
277: | </c:mkcalendar>'; |
278: | |
279: | return $this->client->request( |
280: | 'MKCALENDAR', |
281: | $sCalendarHome . $sSystemName, |
282: | $xml, |
283: | array( |
284: | 'Content-Type' => 'application/xml', |
285: | 'Depth' => '1' |
286: | ) |
287: | ); |
288: | } |
289: | |
290: | |
291: | |
292: | |
293: | |
294: | |
295: | |
296: | |
297: | |
298: | |
299: | public function updateCalendar($sCalendarId, $sDisplayName, $sDescription, $iOrder, $sColor) |
300: | { |
301: | return $this->client->propPatch( |
302: | $sCalendarId, |
303: | array( |
304: | self::PROP_DISPLAYNAME => $sDisplayName, |
305: | self::PROP_CALENDAR_DESCRIPTION => $sDescription, |
306: | self::PROP_CALENDAR_ORDER => $iOrder, |
307: | self::PROP_CALENDAR_COLOR => $sColor |
308: | ) |
309: | ); |
310: | } |
311: | |
312: | |
313: | |
314: | |
315: | |
316: | |
317: | |
318: | public function updateCalendarColor($sCalendarId, $sColor) |
319: | { |
320: | return $this->client->propPatch( |
321: | $sCalendarId, |
322: | array( |
323: | self::PROP_CALENDAR_COLOR => $sColor |
324: | ) |
325: | ); |
326: | } |
327: | |
328: | |
329: | |
330: | |
331: | public function DeleteItem($url) |
332: | { |
333: | return $this->client->request('DELETE', $url); |
334: | } |
335: | |
336: | |
337: | |
338: | |
339: | |
340: | |
341: | |
342: | public function QueryCal($filter, $url = '') |
343: | { |
344: | $xml = |
345: | '<?xml version="1.0" encoding="utf-8" ?> |
346: | <c:calendar-query xmlns:d="DAV:" xmlns:c="' . \Sabre\CalDAV\Plugin::NS_CALDAV . '"> |
347: | <d:prop> |
348: | <d:getetag/> |
349: | <c:calendar-data/> |
350: | </d:prop> |
351: | ' . $filter . ' |
352: | </c:calendar-query>'; |
353: | |
354: | $res = array(); |
355: | try { |
356: | $res = $this->client->request( |
357: | 'REPORT', |
358: | $url, |
359: | $xml, |
360: | array( |
361: | 'Content-Type' => 'application/xml', |
362: | 'Depth' => '1' |
363: | ) |
364: | ); |
365: | } catch(\Sabre\DAV\Exception $ex) { |
366: | return false; |
367: | } |
368: | |
369: | $aStatus = $this->client->parseMultiStatus($res['body']); |
370: | |
371: | $report = array(); |
372: | foreach ($aStatus as $key => $props) { |
373: | $response = array(); |
374: | if (count($props) > 0) { |
375: | $response['url'] = $url; |
376: | $response['href'] = basename($key); |
377: | $response['etag'] = isset($props[200]) ? preg_replace('/^"?([^"]+)"?/', '$1', $props[200][self::PROP_ETAG]) : ''; |
378: | $response['data'] = isset($props[200]) ? $props[200][self::PROP_CALENDAR_DATA] : ''; |
379: | |
380: | $report[] = $response; |
381: | } |
382: | } |
383: | return $report; |
384: | } |
385: | |
386: | |
387: | |
388: | |
389: | |
390: | |
391: | |
392: | public function QueryCardsInfo($filter, $url = '') |
393: | { |
394: | $xml = |
395: | '<?xml version="1.0" encoding="utf-8" ?> |
396: | <c:addressbook-query xmlns:d="DAV:" xmlns:c="' . \Sabre\CardDAV\Plugin::NS_CARDDAV . '"> |
397: | <d:prop> |
398: | <d:getetag/> |
399: | </d:prop> |
400: | ' . $filter . ' |
401: | </c:addressbook-query>'; |
402: | |
403: | $res = array(); |
404: | try { |
405: | $res = $this->client->request( |
406: | 'REPORT', |
407: | $url, |
408: | $xml, |
409: | array( |
410: | 'Content-Type' => 'application/xml', |
411: | 'Depth' => '1' |
412: | ) |
413: | ); |
414: | } catch(\Sabre\DAV\Exception $ex) { |
415: | return false; |
416: | } |
417: | |
418: | $aStatus = $this->client->parseMultiStatus($res['body']); |
419: | |
420: | $report = array(); |
421: | foreach ($aStatus as $key => $props) { |
422: | $response = array(); |
423: | $response['href'] = basename($key); |
424: | $response['etag'] = isset($props[200]) ? preg_replace('/^"?([^"]+)"?/', '$1', $props[200][self::PROP_ETAG]) : ''; |
425: | $response['data'] = isset($props[200]) && isset($props[200][self::PROP_ADDRESSBOOK_DATA]) ? $props[200][self::PROP_ADDRESSBOOK_DATA] : ''; |
426: | |
427: | $report[] = $response; |
428: | } |
429: | return $report; |
430: | } |
431: | |
432: | |
433: | |
434: | |
435: | |
436: | |
437: | |
438: | public function QueryCards($calendar_url, $urls = []) |
439: | { |
440: | $aHrefs = []; |
441: | foreach ($urls as $url) { |
442: | $aHrefs[] = ' <d:href>' . $url . '</d:href>'; |
443: | } |
444: | $sHrefs = implode("\n", $aHrefs); |
445: | $xml = |
446: | '<?xml version="1.0" encoding="utf-8" ?> |
447: | <c:addressbook-multiget xmlns:d="DAV:" xmlns:c="' . \Sabre\CardDAV\Plugin::NS_CARDDAV . '"> |
448: | <d:prop> |
449: | <d:getetag /> |
450: | <c:address-data /> |
451: | </d:prop> |
452: | ' . $sHrefs . ' |
453: | </c:addressbook-multiget>'; |
454: | |
455: | $res = array(); |
456: | try { |
457: | $res = $this->client->request( |
458: | 'REPORT', |
459: | $calendar_url, |
460: | $xml, |
461: | array( |
462: | 'Content-Type' => 'application/xml', |
463: | 'Depth' => '1' |
464: | ) |
465: | ); |
466: | } catch(\Sabre\DAV\Exception $ex) { |
467: | return false; |
468: | } |
469: | |
470: | $aStatus = $this->client->parseMultiStatus($res['body']); |
471: | |
472: | $report = array(); |
473: | foreach ($aStatus as $key => $props) { |
474: | $response = array(); |
475: | $response['href'] = basename($key); |
476: | $response['etag'] = isset($props[200]) ? preg_replace('/^"?([^"]+)"?/', '$1', $props[200][self::PROP_ETAG]) : ''; |
477: | $response['data'] = isset($props[200]) && isset($props[200][self::PROP_ADDRESSBOOK_DATA]) ? $props[200][self::PROP_ADDRESSBOOK_DATA] : ''; |
478: | |
479: | $report[] = $response; |
480: | } |
481: | return $report; |
482: | } |
483: | |
484: | public function GetVcardsInfo($url = '', $sSearch = '', $sGroupId = '') |
485: | { |
486: | $sFilter = ''; |
487: | $sGroupFilter = ''; |
488: | $sSearchFilter = ''; |
489: | |
490: | if (!empty($sGroupId)) { |
491: | $sGroupFilter = |
492: | ' <c:prop-filter name="CATEGORIES"> |
493: | <c:text-match icollation="i;octet" match-type="contains">' . $sGroupId . '</c:text-match> |
494: | </c:prop-filter>'; |
495: | } |
496: | |
497: | if (!empty($sSearch)) { |
498: | $sSearchFilter = |
499: | ' <c:prop-filter name="FN"> |
500: | <c:text-match icollation="i;octet" match-type="contains">' . $sSearch . '</c:text-match> |
501: | </c:prop-filter> |
502: | <c:prop-filter name="N"> |
503: | <c:text-match icollation="i;octet" match-type="contains">' . $sSearch . '</c:text-match> |
504: | </c:prop-filter> |
505: | <c:prop-filter name="EMAIL"> |
506: | <c:text-match icollation="i;octet" match-type="contains">' . $sSearch . '</c:text-match> |
507: | </c:prop-filter> |
508: | <c:prop-filter name="NICKNAME"> |
509: | <c:text-match icollation="i;octet" match-type="contains">' . $sSearch . '</c:text-match> |
510: | </c:prop-filter>'; |
511: | } |
512: | |
513: | if (!empty($sSearch) || !empty($sGroupId)) { |
514: | $sFilter = |
515: | ' <c:filter> |
516: | ' . $sSearchFilter |
517: | . $sGroupFilter . ' |
518: | </c:filter>'; |
519: | } |
520: | |
521: | return $this->QueryCardsInfo($sFilter, $url); |
522: | } |
523: | |
524: | public function GetVcards($url, $urls = []) |
525: | { |
526: | list($path, $name) = split($url); |
527: | $url = $path . '/' . rawurlencode($name); |
528: | |
529: | return $this->QueryCards($url, $urls); |
530: | } |
531: | |
532: | |
533: | |
534: | |
535: | |
536: | |
537: | |
538: | public function GetTimeRange($start = null, $end = null) |
539: | { |
540: | $timeRange = ''; |
541: | $startRange = ''; |
542: | $endRange = ''; |
543: | if (isset($start) || isset($end)) { |
544: | if (isset($start)) { |
545: | $startRange = 'start="' . $start . '"'; |
546: | } |
547: | if (isset($end)) { |
548: | $endRange = 'end="' . $end . '"'; |
549: | } |
550: | $timeRange = sprintf('<c:time-range %s %s/>', $startRange, $endRange); |
551: | } |
552: | return $timeRange; |
553: | } |
554: | |
555: | |
556: | |
557: | |
558: | |
559: | |
560: | |
561: | |
562: | public function getEvents($url = '', $start = null, $finish = null) |
563: | { |
564: | $timeRange = $this->GetTimeRange($start, $finish); |
565: | $url = rtrim($url, '/') . '/'; |
566: | list($path, $name) = split($url); |
567: | $url = $path . '/' . rawurlencode($name); |
568: | $filter = |
569: | '<c:filter> |
570: | <c:comp-filter name="VCALENDAR"> |
571: | <c:comp-filter name="VEVENT"> |
572: | ' . $timeRange . ' |
573: | </c:comp-filter> |
574: | </c:comp-filter> |
575: | </c:filter>'; |
576: | return $this->QueryCal($filter, $url); |
577: | } |
578: | |
579: | |
580: | |
581: | |
582: | |
583: | |
584: | |
585: | public function GetEventByUid($sCalendarUrl, $sUid) |
586: | { |
587: | $filter = |
588: | ' <c:filter> |
589: | <c:comp-filter name="VCALENDAR"> |
590: | <c:comp-filter name="VEVENT"> |
591: | <c:prop-filter name="UID"> |
592: | <c:text-match icollation="i;octet">' . $sUid . '</c:text-match> |
593: | </c:prop-filter> |
594: | </c:comp-filter> |
595: | </c:comp-filter> |
596: | </c:filter>'; |
597: | $result = $this->QueryCal($filter, $sCalendarUrl); |
598: | if ($result !== false) { |
599: | return current($result); |
600: | } else { |
601: | return false; |
602: | } |
603: | } |
604: | |
605: | |
606: | |
607: | |
608: | |
609: | |
610: | public function GetItem($url = '') |
611: | { |
612: | $res = $this->client->request('GET', $url); |
613: | if ($res !== false) { |
614: | return $res['body']; |
615: | } |
616: | return $res; |
617: | } |
618: | |
619: | |
620: | |
621: | |
622: | |
623: | |
624: | |
625: | |
626: | public function GetAlarms($url = '', $start = null, $finish = null) |
627: | { |
628: | $timeRange = $this->GetTimeRange($start, $finish); |
629: | $url = rtrim($url, '/') . '/'; |
630: | |
631: | $filter = |
632: | '<c:filter> |
633: | <c:comp-filter name="VCALENDAR"> |
634: | <c:comp-filter name="VEVENT"> |
635: | <c:comp-filter name="VALARM"> |
636: | ' . $timeRange . ' |
637: | </c:comp-filter> |
638: | </c:comp-filter> |
639: | </c:comp-filter> |
640: | </c:filter>'; |
641: | |
642: | |
643: | return $this->QueryCal($filter, $url); |
644: | } |
645: | |
646: | |
647: | |
648: | |
649: | |
650: | |
651: | |
652: | |
653: | |
654: | |
655: | |
656: | public function GetTodos($start, $finish, $completed = false, $cancelled = false, $url = '') |
657: | { |
658: | $timeRange = $this->GetTimeRange($start, $finish); |
659: | |
660: | |
661: | $negateCancelled = ($cancelled === true ? 'no' : 'yes'); |
662: | $negateCompleted = ($cancelled === true ? 'no' : 'yes'); |
663: | |
664: | $filter = |
665: | '<c:filter> |
666: | <c:comp-filter name="VCALENDAR"> |
667: | <c:comp-filter name="VTODO"> |
668: | <c:prop-filter name="STATUS"> |
669: | <c:text-match negate-condition="' . $negateCompleted . '">COMPLETED</c:text-match> |
670: | </c:prop-filter> |
671: | <c:prop-filter name="STATUS"> |
672: | <C:text-match negate-condition="' . $negateCancelled . '">CANCELLED</c:text-match> |
673: | </c:prop-filter> |
674: | ' . $timeRange . ' |
675: | </c:comp-filter> |
676: | </c:comp-filter> |
677: | </c:filter>'; |
678: | |
679: | return $this->QueryCal($filter, $url); |
680: | } |
681: | |
682: | |
683: | |
684: | |
685: | |
686: | |
687: | public function getCalendar($url = '') |
688: | { |
689: | $filter = |
690: | '<c:filter> |
691: | <c:comp-filter name="VCALENDAR"></c:comp-filter> |
692: | </c:filter>'; |
693: | |
694: | return $this->QueryCal($filter, $url); |
695: | } |
696: | |
697: | public function GetCurrentPrincipal() |
698: | { |
699: | $res = $this->client->propFind('', array(self::PROP_CURRENT_USER_PRINCIPAL)); |
700: | |
701: | return $res[self::PROP_CURRENT_USER_PRINCIPAL]; |
702: | } |
703: | |
704: | |
705: | |
706: | |
707: | public function GetPrincipalMembers($principal = '') |
708: | { |
709: | $res = array(); |
710: | try { |
711: | $res = $this->client->propFind( |
712: | $principal, |
713: | array( |
714: | self::PROP_GROUP_MEMBERSHIP |
715: | ), |
716: | 1 |
717: | ); |
718: | } catch(\Exception $ex) { |
719: | return []; |
720: | } |
721: | |
722: | return $res[self::PROP_GROUP_MEMBERSHIP]; |
723: | } |
724: | |
725: | |
726: | |
727: | |
728: | public function GetCalendarHomeSet($principalUrl = '') |
729: | { |
730: | $props = $this->client->propFind( |
731: | $principalUrl, |
732: | array( |
733: | self::PROP_CALENDAR_HOME_SET |
734: | ) |
735: | ); |
736: | return $props[self::PROP_CALENDAR_HOME_SET]; |
737: | } |
738: | |
739: | |
740: | |
741: | |
742: | public function GetAddressBookHomeSet($principalUrl = '') |
743: | { |
744: | $props = $this->client->propFind( |
745: | $principalUrl, |
746: | array( |
747: | self::PROP_ADDRESSBOOK_HOME_SET |
748: | ) |
749: | ); |
750: | return $props[self::PROP_ADDRESSBOOK_HOME_SET]; |
751: | } |
752: | |
753: | public function GetAddressBooks($url = '') |
754: | { |
755: | $aProps = $this->client->propFind( |
756: | $url, |
757: | array( |
758: | self::PROP_RESOURCETYPE, |
759: | self::PROP_DISPLAYNAME |
760: | ), |
761: | 1 |
762: | ); |
763: | return $aProps; |
764: | } |
765: | |
766: | public function getCalendars($url = '') |
767: | { |
768: | $calendars = array(); |
769: | |
770: | if (class_exists('\Aurora\Modules\Calendar\Classes\Calendar')) { |
771: | $aProps = $this->client->propFind( |
772: | $url, |
773: | array( |
774: | self::PROP_RESOURCETYPE, |
775: | self::PROP_DISPLAYNAME, |
776: | self::PROP_OWNER, |
777: | self::PROP_CTAG, |
778: | self::PROP_CALENDAR_DESCRIPTION, |
779: | self::PROP_CALENDAR_COLOR, |
780: | self::PROP_CALENDAR_ORDER, |
781: | self::PROP_CALENDAR_INVITE |
782: | ), |
783: | 1 |
784: | ); |
785: | |
786: | foreach ($aProps as $key => $props) { |
787: | if ($props['{DAV:}resourcetype']->is('{' . \Sabre\CalDAV\Plugin::NS_CALDAV . '}calendar') && |
788: | !$props['{DAV:}resourcetype']->is('{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}shared')) { |
789: | $calendar = new \Aurora\Modules\Calendar\Classes\Calendar($key); |
790: | |
791: | $calendar->CTag = $props[self::PROP_CTAG]; |
792: | $calendar->DisplayName = $props[self::PROP_DISPLAYNAME]; |
793: | $calendar->Principals[] = isset($props[self::PROP_OWNER]) ? $props[self::PROP_OWNER] : ''; |
794: | $calendar->Description = isset($props[self::PROP_CALENDAR_DESCRIPTION]) ? $props[self::PROP_CALENDAR_DESCRIPTION] : ''; |
795: | $calendar->Color = isset($props[self::PROP_CALENDAR_COLOR]) ? $props[self::PROP_CALENDAR_COLOR] : ''; |
796: | if (strlen($calendar->Color) > 7) { |
797: | $calendar->Color = substr($calendar->Color, 0, 7); |
798: | } |
799: | |
800: | if (isset($props[self::PROP_CALENDAR_ORDER])) { |
801: | $calendar->Order = $props[self::PROP_CALENDAR_ORDER]; |
802: | } |
803: | if (isset($props[self::PROP_CALENDAR_INVITE])) { |
804: | $aInvitesProp = $props[self::PROP_CALENDAR_INVITE]; |
805: | |
806: | foreach ($aInvitesProp as $aInviteProp) { |
807: | if ($aInviteProp['name'] === '{' . Plugin::NS_CALENDARSERVER . '}user') { |
808: | $aShare = []; |
809: | |
810: | foreach ($aInviteProp['value'] as $aValue) { |
811: | switch ($aValue['name']) { |
812: | case '{DAV:}href': |
813: | list(, $aShare['email']) = split($aValue['value']); |
814: | |
815: | break; |
816: | case '{http://calendarserver.org/ns/}access': |
817: | if (isset($aValue['value'][0])) { |
818: | $aShare['access'] = $aValue['value'][0]['name'] === '{' . Plugin::NS_CALENDARSERVER . '}read-write' |
819: | ? \Afterlogic\DAV\Permission::Write |
820: | : \Afterlogic\DAV\Permission::Read; |
821: | } |
822: | break; |
823: | } |
824: | } |
825: | if (!empty($aShare)) { |
826: | $calendar->Shares[] = $aShare; |
827: | } |
828: | } |
829: | } |
830: | } |
831: | |
832: | $calendars[$calendar->Id] = $calendar; |
833: | } |
834: | } |
835: | } |
836: | return $calendars; |
837: | } |
838: | |
839: | public function GetVcardByUid($uid, $url = '') |
840: | { |
841: | $filter = ""; |
842: | |
843: | if ($uid) { |
844: | $filter = |
845: | '<c:filter> |
846: | <c:prop-filter name="UID"> |
847: | <c:text-match collation="i;unicode-casemap" match-type="equals">' . $uid . '</c:text-match> |
848: | </c:prop-filter> |
849: | </c:filter>'; |
850: | } |
851: | |
852: | return $this->QueryCardsInfo($filter, $url); |
853: | } |
854: | |
855: | public function GetProxies($sProxy) |
856: | { |
857: | $res = array(); |
858: | try { |
859: | $res = $this->client->propFind( |
860: | $sProxy, |
861: | array( |
862: | self::PROP_GROUP_MEMBER_SET |
863: | ), |
864: | 1 |
865: | ); |
866: | } catch(\Exception $ex) { |
867: | return []; |
868: | } |
869: | |
870: | return $res[self::PROP_GROUP_MEMBER_SET]; |
871: | } |
872: | |
873: | public function AddProxy($proxy, $to) |
874: | { |
875: | $sProxyStr = ''; |
876: | $aCurrentProxies = array(); |
877: | $duplicate = false; |
878: | |
879: | $aCurrentProxies = $this->GetProxies($proxy); |
880: | |
881: | if ($aCurrentProxies) { |
882: | foreach ($aCurrentProxies as $sCurrentProxy => $val) { |
883: | $sCurrentProxy = ltrim($sCurrentProxy, DAV_ROOT); |
884: | if ($sCurrentProxy == $proxy) { |
885: | $duplicate = true; |
886: | } |
887: | $sProxyStr .= '<d:href>' . $sCurrentProxy . '</d:href>'; |
888: | } |
889: | } |
890: | if ($duplicate) { |
891: | return false; |
892: | } |
893: | |
894: | $sProxyStr .= '<d:href>' . \Afterlogic\DAV\Constants::PRINCIPALS_PREFIX . $to . '</d:href>'; |
895: | |
896: | return $this->client->propPatch($proxy, array('group-member-set' => $sProxyStr)); |
897: | } |
898: | |
899: | public function DeleteProxy($proxy, $to) |
900: | { |
901: | $aCurrentProxies = $this->GetProxies($proxy); |
902: | |
903: | $sProxyStr = ""; |
904: | |
905: | if ($aCurrentProxies) { |
906: | foreach ($aCurrentProxies as $sCurrentProxy => $val) { |
907: | $sCurrentProxy = ltrim($sCurrentProxy, DAV_ROOT); |
908: | $sProxyStr .= '<d:href>' . $sCurrentProxy . '</d:href>'; |
909: | } |
910: | } |
911: | |
912: | $sProxyStr = str_replace('<d:href>' . \Afterlogic\DAV\Constants::PRINCIPALS_PREFIX . $to . '</d:href>', '', $sProxyStr); |
913: | |
914: | return $this->client->propPatch( |
915: | $proxy, |
916: | array( |
917: | 'group-member-set' => $sProxyStr |
918: | ) |
919: | ); |
920: | } |
921: | |
922: | |
923: | |
924: | |
925: | |
926: | |
927: | |
928: | public function GetCalendarProxiedFor($principalUrl) |
929: | { |
930: | $body = |
931: | '<?xml version="1.0"?> |
932: | <d:expand-property xmlns:d="DAV:"> |
933: | <d:property name="calendar-proxy-read-for" namespace="' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '"> |
934: | <d:property name="calendar-home-set" namespace="' . \Sabre\CalDAV\Plugin::NS_CALDAV . '" /> |
935: | </d:property> |
936: | <d:property name="calendar-proxy-write-for" namespace="' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '"> |
937: | <d:property name="calendar-home-set" namespace="' . \Sabre\CalDAV\Plugin::NS_CALDAV . '" /> |
938: | </d:property> |
939: | </d:expand-property>'; |
940: | |
941: | $res = $this->client->request( |
942: | 'REPORT', |
943: | $principalUrl, |
944: | $body, |
945: | array( |
946: | 'Content-Type' => 'application/xml', |
947: | 'Depth' => '1' |
948: | ) |
949: | ); |
950: | |
951: | if (isset($res['body'])) { |
952: | $data = new \DOMDocument(); |
953: | |
954: | $data->loadXML($res['body'], LIBXML_NOERROR | LIBXML_NOWARNING | LIBXML_NSCLEAN); |
955: | $xp = new \DOMXPath($data); |
956: | |
957: | $xp->registerNamespace('c', \Sabre\CalDAV\Plugin::NS_CALDAV); |
958: | $xp->registerNamespace('cs', \Sabre\CalDAV\Plugin::NS_CALENDARSERVER); |
959: | $xp->registerNamespace('d', 'urn:dav'); |
960: | $values = array(); |
961: | |
962: | $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"); |
963: | foreach ($result as $elem) { |
964: | $values[] = array( |
965: | 'href' => $elem->nodeValue, |
966: | 'mode' => 'read' |
967: | ); |
968: | } |
969: | |
970: | $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"); |
971: | foreach ($result as $elem) { |
972: | $values[] = array( |
973: | 'href' => $elem->nodeValue, |
974: | 'mode' => 'write' |
975: | ); |
976: | } |
977: | |
978: | return $values; |
979: | } |
980: | return array(); |
981: | } |
982: | } |
983: | |