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