1: <?php
2: /**
3: * This code is licensed under Afterlogic Software License.
4: * For full statements of the license see LICENSE file.
5: */
6:
7: namespace Aurora\Modules\Calendar;
8:
9: use Aurora\System\Exceptions\ApiException;
10:
11: /**
12: * @license https://afterlogic.com/products/common-licensing Afterlogic Software License
13: * @copyright Copyright (c) 2023, Afterlogic Corp.
14: *
15: * @property Settings $oModuleSettings
16: *
17: * @package Modules
18: */
19: class Module extends \Aurora\System\Module\AbstractLicensedModule
20: {
21: public $oManager = null;
22: public $oFilecacheManager = null;
23: protected $oUserForDelete = null;
24:
25: public const DEFAULT_PERIOD_IN_DAYS = 30;
26:
27: /**
28: * @return Module
29: */
30: public static function getInstance()
31: {
32: return parent::getInstance();
33: }
34:
35: /**
36: * @return Module
37: */
38: public static function Decorator()
39: {
40: return parent::Decorator();
41: }
42:
43: /**
44: * @return Settings
45: */
46: public function getModuleSettings()
47: {
48: return $this->oModuleSettings;
49: }
50:
51: /**
52: *
53: * @return Manager
54: */
55: public function getManager()
56: {
57: if ($this->oManager === null) {
58: $this->oManager = new Manager($this);
59: }
60:
61: return $this->oManager;
62: }
63:
64: public function getFilecacheManager()
65: {
66: if ($this->oFilecacheManager === null) {
67: $this->oFilecacheManager = new \Aurora\System\Managers\Filecache();
68: }
69:
70: return $this->oFilecacheManager;
71: }
72:
73: public function init()
74: {
75: $this->aErrors = [
76: Enums\ErrorCodes::CannotFindCalendar => $this->i18N('ERROR_NO_CALENDAR'),
77: Enums\ErrorCodes::InvalidSubscribedIcs => $this->i18N('ERROR_INVALID_SUBSCRIBED_ICS')
78: ];
79:
80: $this->AddEntries(
81: array(
82: 'calendar-pub' => 'EntryCalendarPub',
83: 'calendar-download' => 'EntryCalendarDownload'
84: )
85: );
86:
87: $this->subscribeEvent('Mail::GetBodyStructureParts', array($this, 'onGetBodyStructureParts'));
88: $this->subscribeEvent('MobileSync::GetInfo', array($this, 'onGetMobileSyncInfo'));
89: $this->subscribeEvent('Mail::ExtendMessageData', array($this, 'onExtendMessageData'));
90: $this->subscribeEvent('Core::DeleteUser::before', array($this, 'onBeforeDeleteUser'));
91: $this->subscribeEvent('Core::DeleteUser::after', array($this, 'onAfterDeleteUser'));
92: }
93:
94: /**
95: * Obtains list of module settings for authenticated user.
96: *
97: * @return array
98: */
99: public function GetSettings()
100: {
101: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::Anonymous);
102:
103: $aSettings = array(
104: 'AddDescriptionToTitle' => $this->oModuleSettings->AddDescriptionToTitle,
105: 'AllowTasks' => $this->oModuleSettings->AllowTasks,
106: 'DefaultTab' => $this->oModuleSettings->DefaultTab,
107: 'HighlightWorkingDays' => $this->oModuleSettings->HighlightWorkingDays,
108: 'HighlightWorkingHours' => $this->oModuleSettings->HighlightWorkingHours,
109: 'ShowWeekNumbers' => $this->oModuleSettings->ShowWeekNumbers,
110: 'PublicCalendarId' => $this->oHttp->GetQuery('calendar-pub', ''),
111: 'WeekStartsOn' => $this->oModuleSettings->WeekStartsOn,
112: 'WorkdayEnds' => $this->oModuleSettings->WorkdayEnds,
113: 'WorkdayStarts' => $this->oModuleSettings->WorkdayStarts,
114: 'AllowSubscribedCalendars' => $this->oModuleSettings->AllowSubscribedCalendars,
115: 'AllowPrivateEvents' => $this->oModuleSettings->AllowPrivateEvents,
116: 'AllowDefaultReminders' => $this->oModuleSettings->AllowDefaultReminders,
117: 'DefaultReminders' => [],
118: 'CalendarColors' => $this->oModuleSettings->CalendarColors,
119: 'ShowTasksInCalendars' => $this->oModuleSettings->ShowTasksInCalendars,
120: );
121:
122: $oUser = \Aurora\System\Api::getAuthenticatedUser();
123: if ($oUser && $oUser->isNormalOrTenant()) {
124: if (null !== $oUser->getExtendedProp(self::GetName() . '::HighlightWorkingDays')) {
125: $aSettings['HighlightWorkingDays'] = $oUser->getExtendedProp(self::GetName() . '::HighlightWorkingDays');
126: }
127: if (null !== $oUser->getExtendedProp(self::GetName() . '::HighlightWorkingHours')) {
128: $aSettings['HighlightWorkingHours'] = $oUser->getExtendedProp(self::GetName() . '::HighlightWorkingHours');
129: }
130: if (null !== $oUser->getExtendedProp(self::GetName() . '::ShowWeekNumbers')) {
131: $aSettings['ShowWeekNumbers'] = $oUser->getExtendedProp(self::GetName() . '::ShowWeekNumbers');
132: }
133: if (null !== $oUser->getExtendedProp(self::GetName() . '::WorkdayStarts')) {
134: $aSettings['WorkdayStarts'] = $oUser->getExtendedProp(self::GetName() . '::WorkdayStarts');
135: }
136: if (null !== $oUser->getExtendedProp(self::GetName() . '::WorkdayEnds')) {
137: $aSettings['WorkdayEnds'] = $oUser->getExtendedProp(self::GetName() . '::WorkdayEnds');
138: }
139: if (null !== $oUser->getExtendedProp(self::GetName() . '::WeekStartsOn')) {
140: $aSettings['WeekStartsOn'] = $oUser->getExtendedProp(self::GetName() . '::WeekStartsOn');
141: }
142: if (null !== $oUser->getExtendedProp(self::GetName() . '::DefaultTab')) {
143: $aSettings['DefaultTab'] = $oUser->getExtendedProp(self::GetName() . '::DefaultTab');
144: }
145: if (null !== $oUser->getExtendedProp(self::GetName() . '::DefaultReminders')) {
146: $aSettings['DefaultReminders'] = $oUser->getExtendedProp(self::GetName() . '::DefaultReminders');
147: }
148:
149: $oUser->save();
150: }
151:
152: return $aSettings;
153: }
154:
155: public function UpdateSettings($HighlightWorkingDays, $HighlightWorkingHours, $WorkdayStarts, $WorkdayEnds, $WeekStartsOn, $DefaultTab, $DefaultReminders, $ShowWeekNumbers)
156: {
157: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
158:
159: $oUser = \Aurora\System\Api::getAuthenticatedUser();
160: if ($oUser) {
161: if ($oUser->isNormalOrTenant()) {
162: $oCoreDecorator = \Aurora\Modules\Core\Module::Decorator();
163: $oUser->setExtendedProps([
164: self::GetName() . '::HighlightWorkingDays' => $HighlightWorkingDays,
165: self::GetName() . '::HighlightWorkingHours' => $HighlightWorkingHours,
166: self::GetName() . '::ShowWeekNumbers' => $ShowWeekNumbers,
167: self::GetName() . '::WorkdayStarts' => $WorkdayStarts,
168: self::GetName() . '::WorkdayEnds' => $WorkdayEnds,
169: self::GetName() . '::WeekStartsOn' => $WeekStartsOn,
170: self::GetName() . '::DefaultTab' => $DefaultTab,
171: self::GetName() . '::DefaultReminders' => $DefaultReminders,
172: ]);
173: return $oCoreDecorator->UpdateUserObject($oUser);
174: }
175: if ($oUser->Role === \Aurora\System\Enums\UserRole::SuperAdmin) {
176: $this->setConfig('HighlightWorkingDays', $HighlightWorkingDays);
177: $this->setConfig('HighlightWorkingHours', $HighlightWorkingHours);
178: $this->setConfig('ShowWeekNumbers', $ShowWeekNumbers);
179: $this->setConfig('WorkdayStarts', $WorkdayStarts);
180: $this->setConfig('WorkdayEnds', $WorkdayEnds);
181: $this->setConfig('WeekStartsOn', $WeekStartsOn);
182: $this->setConfig('DefaultTab', $DefaultTab);
183: return $this->saveModuleConfig();
184: }
185: }
186:
187: return false;
188: }
189:
190: /**
191: * Loads calendar.
192: *
193: * @param int $UserId
194: * @param string $CalendarId Calendar ID
195: *
196: * @return Classes\Calendar|false $oCalendar
197: */
198: public function GetCalendar($UserId, $CalendarId)
199: {
200: \Aurora\System\Api::CheckAccess($UserId);
201: $oUser = \Aurora\System\Api::getUserById($UserId);
202:
203: $oCalendar = $this->getManager()->getCalendar($oUser->PublicId, $CalendarId);
204: if ($oCalendar) {
205: // $oCalendar = $this->getManager()->populateCalendarShares($UserId, $oCalendar);
206: }
207: return $oCalendar;
208: }
209:
210: /**
211: *
212: * @param string $CalendarId
213: *
214: * @return array|false
215: */
216: public function GetPublicCalendar($CalendarId)
217: {
218: $mResult = false;
219:
220: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::Anonymous);
221: $oPublicCalendar = $this->getManager()->getPublicCalendar($CalendarId);
222: if ($oPublicCalendar) {
223: $mResult = [
224: 'Calendars' => [$oPublicCalendar]
225: ];
226: }
227:
228: return $mResult;
229: }
230:
231: /**
232: *
233: * @param int $UserId
234: * @return array|boolean
235: */
236: public function GetCalendars($UserId)
237: {
238: $mResult = false;
239: $mCalendars = false;
240:
241: \Aurora\System\Api::CheckAccess($UserId);
242: $oUser = \Aurora\System\Api::getUserById($UserId);
243: if ($oUser) {
244: $mCalendars = $this->getManager()->getCalendars($oUser->PublicId);
245: }
246:
247: // When $mCalendars is an empty array with condition "if ($mCalendars)" $mResult will be false
248: if (is_array($mCalendars)) {
249: $mResult = array(
250: 'Calendars' => $mCalendars
251: );
252: }
253:
254: return $mResult;
255: }
256:
257: /**
258: *
259: * @return void
260: */
261: public function EntryCalendarDownload()
262: {
263: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
264:
265: $RawKey = \Aurora\System\Router::getItemByIndex(1, '');
266: $aValues = \Aurora\System\Api::DecodeKeyValues($RawKey);
267:
268: $sUserPublicId = \Aurora\System\Api::getUserPublicIdById(\Aurora\System\Api::getAuthenticatedUserId());
269:
270: if (isset($aValues['CalendarId'])) {
271: $sCalendarId = $aValues['CalendarId'];
272: $sOutput = $this->getManager()->exportCalendarToIcs($sUserPublicId, $sCalendarId);
273: if (false !== $sOutput) {
274: header('Pragma: public');
275: header('Content-Type: text/calendar');
276: header('Content-Disposition: attachment; filename="' . $sCalendarId . '.ics";');
277: header('Content-Transfer-Encoding: binary');
278: echo $sOutput;
279: }
280: }
281: }
282:
283: /**
284: *
285: * @param int $UserId
286: * @param string $Name
287: * @param string $Description
288: * @param string $Color
289: * @return array|boolean
290: */
291: public function CreateCalendar($UserId, $Name, $Description, $Color, $UUID = null)
292: {
293: \Aurora\System\Api::CheckAccess($UserId);
294: $sUserPublicId = \Aurora\System\Api::getUserPublicIdById($UserId);
295: $mResult = false;
296:
297: $mCalendarId = $this->getManager()->createCalendar($sUserPublicId, $Name, $Description, 1, $Color, $UUID);
298: if ($mCalendarId) {
299: $oCalendar = $this->getManager()->getCalendar($sUserPublicId, $mCalendarId);
300: if ($oCalendar instanceof Classes\Calendar) {
301: $mResult = $oCalendar->toResponseArray($sUserPublicId);
302: }
303: }
304:
305: return $mResult;
306: }
307:
308: public function CreateSubscribedCalendar($UserId, $Name, $Source, $Color, $UUID = null)
309: {
310: \Aurora\System\Api::CheckAccess($UserId);
311: $sUserPublicId = \Aurora\System\Api::getUserPublicIdById($UserId);
312: $mResult = false;
313:
314: if (!$this->getManager()->validateSubscribedCalebdarSource($Source)) {
315: throw new ApiException(Enums\ErrorCodes::InvalidSubscribedIcs);
316: }
317:
318: $mCalendarId = $this->getManager()->createSubscribedCalendar($sUserPublicId, $Name, $Source, 1, $Color, $UUID);
319: if ($mCalendarId) {
320: $oCalendar = $this->getManager()->getCalendar($sUserPublicId, $mCalendarId);
321: if ($oCalendar instanceof Classes\Calendar) {
322: $mResult = $oCalendar->toResponseArray($sUserPublicId);
323: }
324: }
325:
326: return $mResult;
327: }
328:
329: /**
330: *
331: * @param int $UserId
332: * @param string $Id
333: * @param string $Name
334: * @param string $Description
335: * @param string $Color
336: * @return array|boolean
337: */
338: public function UpdateCalendar($UserId, $Id, $Name, $Description, $Color)
339: {
340: \Aurora\System\Api::CheckAccess($UserId);
341: $sUserPublicId = \Aurora\System\Api::getUserPublicIdById($UserId);
342: return $this->getManager()->updateCalendar($sUserPublicId, $Id, $Name, $Description, 0, $Color);
343: }
344:
345: /**
346: *
347: * @param int $UserId
348: * @param string $Id
349: * @param string $Name
350: * @param string $Source
351: * @param string $Color
352: * @return array|boolean
353: */
354: public function UpdateSubscribedCalendar($UserId, $Id, $Name, $Source, $Color)
355: {
356: \Aurora\System\Api::CheckAccess($UserId);
357: $sUserPublicId = \Aurora\System\Api::getUserPublicIdById($UserId);
358:
359: if (!$this->getManager()->validateSubscribedCalebdarSource($Source)) {
360: throw new ApiException(Enums\ErrorCodes::InvalidSubscribedIcs);
361: }
362:
363: return $this->getManager()->updateSubscribedCalendar($sUserPublicId, $Id, $Name, $Source, 0, $Color);
364: }
365:
366: /**
367: *
368: * @param int $UserId
369: * @param string $Id
370: * @param string $Color
371: * @return array|boolean
372: */
373: public function UpdateCalendarColor($UserId, $Id, $Color)
374: {
375: \Aurora\System\Api::CheckAccess($UserId);
376: $sUserPublicId = \Aurora\System\Api::getUserPublicIdById($UserId);
377: return $this->getManager()->updateCalendarColor($sUserPublicId, $Id, $Color);
378: }
379:
380: /**
381: *
382: * @param int $UserId
383: * @param string $Id
384: * @param boolean $IsPublic
385: * @param string $Shares
386: * @param boolean $ShareToAll
387: * @param int $ShareToAllAccess
388: * @return array|boolean
389: */
390: public function UpdateCalendarShare($UserId, $Id, $IsPublic, $Shares, $ShareToAll = false, $ShareToAllAccess = Enums\Permission::Read)
391: {
392: \Aurora\System\Api::CheckAccess($UserId);
393: $sUserPublicId = \Aurora\System\Api::getUserPublicIdById($UserId);
394: $aShares = json_decode($Shares, true) ;
395: $oUser = null;
396: $oAuthenticatedUser = \Aurora\System\Api::getAuthenticatedUser();
397: if ($oAuthenticatedUser->Id !== $UserId && $oAuthenticatedUser->Role === \Aurora\System\Enums\UserRole::SuperAdmin) {
398: $oUser = \Aurora\System\Api::getUserById($UserId);
399: } else {
400: $oUser = $oAuthenticatedUser;
401: }
402: $oCalendar = $this->getManager()->getCalendar($oUser->PublicId, $Id);
403: if (!$oCalendar) {
404: throw new \Aurora\System\Exceptions\ApiException(\Aurora\System\Notifications::InvalidInputParameter);
405: }
406: //Calendar can be shared by owner or user with write access except SharedWithAll calendars
407: if ($oCalendar->Owner !== $sUserPublicId
408: && $oCalendar->Access !== Enums\Permission::Write) {
409: return false;
410: }
411: // Share calendar to all users
412: if ($ShareToAll) {
413: $aShares[] = array(
414: 'email' => $this->getManager()->getTenantUser($oUser),
415: 'access' => $ShareToAllAccess
416: );
417: } else {
418: $aShares[] = array(
419: 'email' => $this->getManager()->getTenantUser($oUser),
420: 'access' => Enums\Permission::RemovePermission
421: );
422: }
423:
424: return $this->getManager()->updateCalendarShares($sUserPublicId, $Id, $aShares);
425: }
426:
427: /**
428: *
429: * @param string $Id User publicId
430: * @param boolean $IsPublic
431: * @param int $UserId
432: * @return array|boolean
433: */
434: public function UpdateCalendarPublic($Id, $IsPublic, $UserId = null)
435: {
436: \Aurora\System\Api::CheckAccess($UserId);
437: $oUser = null;
438: $oAuthenticatedUser = \Aurora\System\Api::getAuthenticatedUser();
439: if ($oAuthenticatedUser->PublicId !== $Id && $oAuthenticatedUser->Role === \Aurora\System\Enums\UserRole::SuperAdmin) {
440: $oUser = \Aurora\System\Api::getUserById($UserId);
441: }
442: return $this->getManager()->publicCalendar($Id, $IsPublic, $oUser);
443: }
444:
445: /**
446: *
447: * @param int $UserId
448: * @param string $Id
449: * @return array|boolean
450: */
451: public function DeleteCalendar($UserId, $Id)
452: {
453: \Aurora\System\Api::CheckAccess($UserId);
454: $sUserPublicId = \Aurora\System\Api::getUserPublicIdById($UserId);
455: return $this->getManager()->deleteCalendar($sUserPublicId, $Id);
456: }
457:
458: /**
459: *
460: * @param int $UserId
461: * @param string $calendarId
462: * @param string $uid
463: * @return array|boolean
464: */
465: public function GetBaseEvent($UserId, $calendarId, $uid)
466: {
467: \Aurora\System\Api::CheckAccess($UserId);
468: $sUserPublicId = \Aurora\System\Api::getUserPublicIdById($UserId);
469: return $this->getManager()->getBaseEvent($sUserPublicId, $calendarId, $uid);
470: }
471:
472: /**
473: *
474: * @param int $UserId
475: * @param array $CalendarIds
476: * @param int $Start
477: * @param int $End
478: * @param boolean $IsPublic
479: * @param boolean $Expand
480: * @return array|boolean
481: */
482: public function GetEvents($UserId, $CalendarIds, $Start, $End, $IsPublic, $Expand = true, $DefaultTimeZone = null)
483: {
484: $mResult = false;
485: if ($IsPublic) {
486: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::Anonymous);
487: $mResult = $this->getManager()->getPublicEvents($CalendarIds, $Start, $End, $Expand, $DefaultTimeZone);
488: } else {
489: \Aurora\System\Api::CheckAccess($UserId);
490: $sUserPublicId = \Aurora\System\Api::getUserPublicIdById($UserId);
491: $mResult = $this->getManager()->getEvents($sUserPublicId, $CalendarIds, $Start, $End);
492: }
493:
494: $aResult = [];
495: if (is_array($mResult)) {
496: foreach ($mResult as $event) {
497: if (TextUtils::isHtml($event['description'])) {
498: $event['description'] = TextUtils::clearHtml($event['description']);
499: }
500:
501: if (TextUtils::isHtml($event['location'])) {
502: $event['location'] = TextUtils::clearHtml($event['location']);
503: }
504: $aResult[] = $event;
505: }
506: }
507:
508: return $aResult;
509: }
510:
511: /**
512: *
513: * @param int $UserId
514: * @param string $CalendarId
515: * @param array $EventUids
516: * @param int|null $Start
517: * @param int|null $End
518: * @param boolean $Expand
519: * @return array|boolean
520: */
521: public function GetEventsByUids($UserId, $CalendarId, $EventUids, $Start = null, $End = null, $Expand = false)
522: {
523: \Aurora\System\Api::CheckAccess($UserId);
524: $sUserPublicId = \Aurora\System\Api::getUserPublicIdById($UserId);
525: $EventUrls = [];
526: if (is_array($EventUids)) {
527: $EventUrls = array_map(function ($EventUid) {
528: return $EventUid . '.ics';
529: }, $EventUids);
530: }
531:
532: $Start = ($Start != null) ? date('Ymd\T000000\Z', intval($Start) - 86400) : null;
533: $End = ($End != null) ? date('Ymd\T235959\Z', intval($End)) : null;
534:
535: $mResult = $this->getManager()->getEventsByUrls($sUserPublicId, $CalendarId, $EventUrls, $Start, $End, $Expand);
536:
537: $aResult = [];
538: if (is_array($mResult)) {
539: foreach ($mResult as $event) {
540: if (TextUtils::isHtml($event['description'])) {
541: $event['description'] = TextUtils::clearHtml($event['description']);
542: }
543:
544: if (TextUtils::isHtml($event['location'])) {
545: $event['location'] = TextUtils::clearHtml($event['location']);
546: }
547: $aResult[] = $event;
548: }
549: }
550:
551: return $aResult;
552: }
553:
554: /**
555: *
556: * @param int $UserId
557: * @param array $CalendarIds
558: * @param int $Start
559: * @param int $End
560: * @param boolean $Expand
561: * @return array|boolean
562: */
563: public function GetTasks($UserId, $CalendarIds, $Completed = true, $Search = '', $Start = null, $End = null, $Expand = true)
564: {
565: $mResult = [];
566: if ($this->oModuleSettings->AllowTasks) {
567: \Aurora\System\Api::CheckAccess($UserId);
568: $sUserPublicId = \Aurora\System\Api::getUserPublicIdById($UserId);
569:
570: $mResult = $this->getManager()->getTasks($sUserPublicId, $CalendarIds, $Completed, $Search, $Start, $End, $Expand);
571: }
572:
573: return $mResult;
574: }
575:
576: private function _checkUserCalendar($sUserPublicId, $sCalendarId)
577: {
578: $oCalendar = $this->getManager()->getCalendar($sUserPublicId, $sCalendarId);
579: if (!$oCalendar) {
580: throw new Exceptions\Exception(Enums\ErrorCodes::CannotFindCalendar);
581: } elseif ($oCalendar->Access === Enums\Permission::Read) {
582: throw new Exceptions\Exception(Enums\ErrorCodes::NoWriteAccessForCalendar);
583: }
584: }
585:
586: /**
587: *
588: * @param int $UserId
589: * @param string $newCalendarId
590: * @param string $subject
591: * @param string $description
592: * @param string $location
593: * @param int $startTS
594: * @param int $endTS
595: * @param boolean $allDay
596: * @param string $alarms
597: * @param string $attendees
598: * @param string $rrule
599: * @param int $selectStart
600: * @param int $selectEnd
601: * @return array|boolean
602: */
603: public function CreateEvent(
604: $UserId,
605: $newCalendarId,
606: $subject,
607: $description,
608: $location,
609: $startTS,
610: $endTS,
611: $allDay,
612: $alarms,
613: $attendees,
614: $rrule,
615: $selectStart,
616: $selectEnd,
617: $type = 'VEVENT',
618: $status = false,
619: $withDate = true,
620: $owner = '',
621: $isPrivate = false
622: ) {
623: \Aurora\System\Api::CheckAccess($UserId);
624: $sUserPublicId = \Aurora\System\Api::getUserPublicIdById($UserId);
625:
626: $this->_checkUserCalendar($sUserPublicId, $newCalendarId);
627:
628: $now = new \DateTime('now');
629: $now->setTime(0, 0);
630: if ($selectStart === null) {
631: $selectStart = $now->getTimestamp() - 86400 * self::DEFAULT_PERIOD_IN_DAYS;
632: }
633: if ($selectEnd === null) {
634: $selectEnd = $now->getTimestamp() + 86400 * self::DEFAULT_PERIOD_IN_DAYS;
635: }
636:
637: $oEvent = new Classes\Event();
638: $oEvent->IdCalendar = $newCalendarId;
639: $oEvent->Name = $subject;
640: $oEvent->Description = TextUtils::isHtml($description) ? TextUtils::clearHtml($description) : $description;
641: $oEvent->Location = TextUtils::isHtml($location) ? TextUtils::clearHtml($location) : $location;
642: $oEvent->IsPrivate = $isPrivate;
643: if ($withDate) {
644: $oEvent->Start = $startTS;
645: $oEvent->End = $endTS;
646: $oEvent->AllDay = $allDay;
647: $oEvent->Alarms = @json_decode($alarms, true);
648: $aRRule = !empty($rrule) ? @json_decode($rrule, true) : false;
649: if ($aRRule) {
650: $oUser = \Aurora\System\Api::getAuthenticatedUser();
651: $oRRule = new Classes\RRule($oUser->DefaultTimeZone);
652: $oRRule->Populate($aRRule);
653: $oEvent->RRule = $oRRule;
654: }
655: }
656: $oEvent->Attendees = [];
657: $oEvent->Type = $type;
658: $oEvent->Status = $status && $type === 'VTODO';
659: if ($type === 'VTODO') {
660: $attendees = json_encode([]);
661: }
662: $aArgs = [
663: 'attendees' => $attendees,
664: 'owner' => $owner,
665: 'UserPublicId' => $sUserPublicId
666: ];
667: $this->broadcastEvent(
668: 'UpdateEventAttendees',
669: $aArgs,
670: $oEvent
671: );
672:
673: $mResult = $this->getManager()->createEvent($sUserPublicId, $oEvent);
674:
675: if ($mResult) {
676: $aArgs = ['Event' => $oEvent];
677: $this->broadcastEvent(
678: 'CreateEvent',
679: $aArgs
680: );
681: $mResult = $this->getManager()->getExpandedEvent($sUserPublicId, $oEvent->IdCalendar, $mResult, $selectStart, $selectEnd);
682: }
683:
684: return $mResult;
685: }
686:
687: /**
688: *
689: * @param int $UserId
690: * @param string $CalendarId
691: * @param string $EventId
692: * @param array $Data
693: * @return mixed
694: */
695: public function CreateEventFromData($UserId, $CalendarId, $EventId, $Data)
696: {
697: \Aurora\System\Api::CheckAccess($UserId);
698: $sUserPublicId = \Aurora\System\Api::getUserPublicIdById($UserId);
699:
700: $this->_checkUserCalendar($sUserPublicId, $CalendarId);
701:
702: return $this->getManager()->createEventFromRaw($sUserPublicId, $CalendarId, $EventId, $Data);
703: }
704:
705: /**
706: *
707: * @param int $UserId
708: * @param string $CalendarId
709: * @param string $Subject
710: * @return array|boolean
711: */
712: public function CreateTask($UserId, $CalendarId, $Subject)
713: {
714: $mResult = false;
715: if ($this->oModuleSettings->AllowTasks) {
716: \Aurora\System\Api::CheckAccess($UserId);
717: $sUserPublicId = \Aurora\System\Api::getUserPublicIdById($UserId);
718:
719: $this->_checkUserCalendar($sUserPublicId, $CalendarId);
720:
721: $oEvent = new Classes\Event();
722: $oEvent->IdCalendar = $CalendarId;
723: $oEvent->Name = $Subject;
724: $oEvent->Start = \time();
725: $oEvent->End = \time();
726: $oEvent->Type = 'VTODO';
727:
728: $mResult = $this->getManager()->createEvent($sUserPublicId, $oEvent);
729: }
730:
731: return $mResult;
732: }
733:
734: /**
735: *
736: * @param int $UserId
737: * @param string $CalendarId
738: * @param string $TaskId
739: * @param string $Subject
740: * @param string $Status
741: * @param bool $WithDate
742: * @return array|boolean
743: */
744: public function UpdateTask($UserId, $CalendarId, $TaskId, $Subject, $Status, $WithDate = false)
745: {
746: $bResult = false;
747: if ($this->oModuleSettings->AllowTasks) {
748: \Aurora\System\Api::CheckAccess($UserId);
749: $sUserPublicId = \Aurora\System\Api::getUserPublicIdById($UserId);
750:
751: $this->_checkUserCalendar($sUserPublicId, $CalendarId);
752:
753: $oEvent = new Classes\Event();
754: $oEvent->IdCalendar = $CalendarId;
755: $oEvent->Id = $TaskId;
756: $oEvent->Name = $Subject;
757: $oEvent->Type = 'VTODO';
758: $oEvent->Status = $Status ? 'COMPLETED' : '';
759:
760: if ($WithDate) {
761: $aEvent = $this->GetBaseEvent($UserId, $CalendarId, $TaskId);
762: if ($aEvent) {
763: $oEvent->Start = $aEvent['startTS'];
764: $oEvent->End = $aEvent['endTS'];
765: }
766: }
767:
768: if ($this->getManager()->updateEvent($sUserPublicId, $oEvent)) {
769: return $this->GetBaseEvent($UserId, $CalendarId, $TaskId);
770: }
771: }
772:
773: return $bResult;
774: }
775:
776:
777: /**
778: *
779: * @param int $UserId
780: * @param string $newCalendarId
781: * @param string $calendarId
782: * @param string $uid
783: * @param string $subject
784: * @param string $description
785: * @param string $location
786: * @param int $startTS
787: * @param int $endTS
788: * @param boolean $allDay
789: * @param string|null $alarms
790: * @param string $attendees
791: * @param string|null $rrule
792: * @param int $allEvents
793: * @param string $recurrenceId
794: * @param int $selectStart
795: * @param int $selectEnd
796: * @return array|boolean
797: */
798: public function UpdateEvent(
799: $UserId,
800: $newCalendarId,
801: $calendarId,
802: $uid,
803: $subject,
804: $description,
805: $location,
806: $startTS,
807: $endTS,
808: $allDay,
809: $alarms,
810: $attendees,
811: $rrule,
812: $allEvents,
813: $recurrenceId,
814: $selectStart,
815: $selectEnd,
816: $type = 'VEVENT',
817: $status = false,
818: $withDate = true,
819: $isPrivate = false,
820: $owner = ''
821: ) {
822: \Aurora\System\Api::CheckAccess($UserId);
823: $sUserPublicId = \Aurora\System\Api::getUserPublicIdById($UserId);
824: $mResult = false;
825:
826: $this->_checkUserCalendar($sUserPublicId, $calendarId);
827: if ($calendarId !== $newCalendarId) {
828: $this->_checkUserCalendar($sUserPublicId, $newCalendarId);
829: }
830:
831: $now = new \DateTime('now');
832: $now->setTime(0, 0);
833: if ($selectStart === null) {
834: $selectStart = $now->getTimestamp() - 86400 * self::DEFAULT_PERIOD_IN_DAYS;
835: }
836: if ($selectEnd === null) {
837: $selectEnd = $now->getTimestamp() + 86400 * self::DEFAULT_PERIOD_IN_DAYS;
838: }
839:
840: $oEvent = new Classes\Event();
841: $oEvent->IdCalendar = $calendarId;
842: $oEvent->Id = $uid;
843: $oEvent->Name = $subject;
844: $oEvent->Description = TextUtils::isHtml($description) ? TextUtils::clearHtml($description) : $description;
845: $oEvent->Location = TextUtils::isHtml($location) ? TextUtils::clearHtml($location) : $location;
846: $oEvent->IsPrivate = $isPrivate;
847: if ($withDate) {
848: $oEvent->Start = $startTS;
849: $oEvent->End = $endTS;
850: $oEvent->AllDay = $allDay;
851: if (isset($alarms)) {
852: $oEvent->Alarms = @json_decode($alarms, true);
853: }
854:
855: $aRRule = isset($rrule) ? @json_decode($rrule, true) : false;
856: if ($aRRule) {
857: $oUser = \Aurora\System\Api::getAuthenticatedUser();
858: $oRRule = new Classes\RRule($oUser->DefaultTimeZone);
859: $oRRule->Populate($aRRule);
860: $oEvent->RRule = $oRRule;
861: }
862: }
863: $oEvent->Attendees = [];
864: $oEvent->Type = $type;
865: if ($type === 'VTODO') {
866: $attendees = json_encode([]);
867: }
868: $aArgs = [
869: 'attendees' => $attendees,
870: 'owner' => $owner,
871: 'UserPublicId' => $sUserPublicId
872: ];
873: $this->broadcastEvent(
874: 'UpdateEventAttendees',
875: $aArgs,
876: $oEvent
877: );
878: if (!empty($status)) {
879: $oEvent->Status = $status && $type === 'VTODO';
880: }
881:
882: if ($allEvents === 1) {
883: $mResult = $this->getManager()->updateExclusion($sUserPublicId, $oEvent, $recurrenceId);
884: } else {
885: $mResult = $this->getManager()->updateEvent($sUserPublicId, $oEvent);
886: if ($mResult && $newCalendarId !== $oEvent->IdCalendar) {
887: $mResult = $this->getManager()->moveEvent($sUserPublicId, $oEvent->IdCalendar, $newCalendarId, $oEvent->Id);
888: $oEvent->IdCalendar = $newCalendarId;
889: }
890: }
891: if ($mResult) {
892: $aArgs = ['Event' => $oEvent];
893: $this->broadcastEvent(
894: 'CreateEvent',
895: $aArgs
896: );
897: $mResult = $this->getManager()->getExpandedEvent($sUserPublicId, $oEvent->IdCalendar, $oEvent->Id, $selectStart, $selectEnd);
898: }
899:
900: return $mResult;
901: }
902:
903: /**
904: *
905: * @param int $UserId
906: * @param string $calendarId
907: * @param string $uid
908: * @param boolean $allEvents
909: * @param string $recurrenceId
910: * @return array|boolean
911: */
912: public function DeleteEvent($UserId, $calendarId, $uid, $allEvents, $recurrenceId)
913: {
914: \Aurora\System\Api::CheckAccess($UserId);
915: $sUserPublicId = \Aurora\System\Api::getUserPublicIdById($UserId);
916:
917: $this->_checkUserCalendar($sUserPublicId, $calendarId);
918:
919: $mResult = false;
920: if ($sUserPublicId) {
921: if ($allEvents === 1) {
922: $oEvent = new Classes\Event();
923: $oEvent->IdCalendar = $calendarId;
924: $oEvent->Id = $uid;
925: $mResult = $this->getManager()->updateExclusion($sUserPublicId, $oEvent, $recurrenceId, true);
926: } else {
927: $mResult = $this->getManager()->deleteEvent($sUserPublicId, $calendarId, $uid);
928: }
929: }
930:
931: return $mResult;
932: }
933:
934: /**
935: *
936: * @param int $UserId
937: * @param string $CalendarId
938: * @param string $File
939: * @return array|boolean
940: * @throws \Aurora\System\Exceptions\ApiException
941: */
942: public function AddEventsFromFile($UserId, $CalendarId, $File)
943: {
944: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
945: $sUserPublicId = \Aurora\System\Api::getUserPublicIdById($UserId);
946: $mResult = false;
947:
948: $this->_checkUserCalendar($sUserPublicId, $CalendarId);
949:
950: if (empty($CalendarId) || empty($File)) {
951: throw new \Aurora\System\Exceptions\ApiException(\Aurora\System\Notifications::InvalidInputParameter);
952: }
953:
954: $sData = $this->getFilecacheManager()->get($sUserPublicId, $File, '', self::GetName());
955: if (!empty($sData)) {
956: $mResult = $this->getManager()->createEventFromRaw($sUserPublicId, $CalendarId, null, $sData);
957: }
958:
959: return $mResult;
960: }
961:
962: public function EntryCalendarPub()
963: {
964: $sResult = '';
965:
966: $oApiIntegrator = \Aurora\System\Managers\Integrator::getInstance();
967:
968: if ($oApiIntegrator) {
969: \Aurora\Modules\CoreWebclient\Module::Decorator()->SetHtmlOutputHeaders();
970:
971: if (!strpos(strtolower($_SERVER['HTTP_USER_AGENT']), 'firefox')) {
972: @\header('Last-Modified: ' . \gmdate('D, d M Y H:i:s') . ' GMT');
973: }
974:
975: $oSettings = &\Aurora\System\Api::GetSettings();
976: if (($oSettings->CacheCtrl && isset($_COOKIE['aft-cache-ctrl']))) {
977: \Aurora\System\Api::setCookie(
978: 'aft-cache-ctrl',
979: '',
980: \strtotime('-1 hour'),
981: false
982: );
983: \MailSo\Base\Http::NewInstance()->StatusHeader(304);
984: exit();
985: }
986: $oCoreClientModule = \Aurora\System\Api::GetModule('CoreWebclient');
987: if ($oCoreClientModule instanceof \Aurora\System\Module\AbstractModule) {
988: $sResult = file_get_contents($oCoreClientModule->GetPath() . '/templates/Index.html');
989: if (is_string($sResult)) {
990: $sFrameOptions = $oSettings->XFrameOptions;
991: if (0 < \strlen($sFrameOptions)) {
992: @\header('X-Frame-Options: ' . $sFrameOptions);
993: }
994:
995: $sAuthToken = isset($_COOKIE[\Aurora\System\Application::AUTH_TOKEN_KEY]) ? $_COOKIE[\Aurora\System\Application::AUTH_TOKEN_KEY] : '';
996: $sResult = strtr($sResult, array(
997: '{{AppVersion}}' => \Aurora\System\Application::GetVersion(),
998: '{{IntegratorDir}}' => $oApiIntegrator->isRtl() ? 'rtl' : 'ltr',
999: '{{IntegratorLinks}}' => $oApiIntegrator->buildHeadersLink(),
1000: '{{IntegratorBody}}' => $oApiIntegrator->buildBody(
1001: array(
1002: 'public_app' => true,
1003: 'modules_list' => $oApiIntegrator->GetModulesForEntry('CalendarWebclient')
1004: )
1005: )
1006: ));
1007: }
1008: }
1009: }
1010: return $sResult;
1011: }
1012:
1013: /**
1014: *
1015: * @param int $UserId
1016: * @param string $Data
1017: * @param string $FromEmail
1018: * @return boolean
1019: */
1020: public function ProcessICS($UserId, $Data, $FromEmail)
1021: {
1022: \Aurora\System\Api::CheckAccess($UserId);
1023:
1024: return $this->getManager()->processICS($UserId, $Data, $FromEmail);
1025: }
1026:
1027: /**
1028: *
1029: * @param int $UserId
1030: * @param array $UploadData
1031: * @param string $CalendarID
1032: * @return array
1033: * @throws \Aurora\System\Exceptions\ApiException
1034: */
1035: public function UploadCalendar($UserId, $UploadData, $CalendarID)
1036: {
1037: \Aurora\System\Api::CheckAccess($UserId);
1038: $sUserPublicId = \Aurora\System\Api::getUserPublicIdById($UserId);
1039:
1040: $sCalendarId = !empty($CalendarID) ? $CalendarID : '';
1041:
1042: $sError = '';
1043: $aResponse = array(
1044: 'ImportedCount' => 0
1045: );
1046:
1047: if (is_array($UploadData)) {
1048: $bIsIcsExtension = strtolower(pathinfo($UploadData['name'], PATHINFO_EXTENSION)) === 'ics';
1049:
1050: if ($bIsIcsExtension) {
1051: $sSavedName = 'import-post-' . md5($UploadData['name'] . $UploadData['tmp_name']);
1052: if ($this->getFilecacheManager()->moveUploadedFile($sUserPublicId, $sSavedName, $UploadData['tmp_name'], '', self::GetName())) {
1053: $iImportedCount = $this->getManager()->importToCalendarFromIcs(
1054: $sUserPublicId,
1055: $sCalendarId,
1056: $this->getFilecacheManager()->generateFullFilePath($sUserPublicId, $sSavedName, '', self::GetName())
1057: );
1058:
1059: if (false !== $iImportedCount && -1 !== $iImportedCount) {
1060: $aResponse['ImportedCount'] = $iImportedCount;
1061: } else {
1062: $sError = 'unknown';
1063: }
1064:
1065: $this->getFilecacheManager()->clear($sUserPublicId, $sSavedName, '', self::GetName());
1066: } else {
1067: $sError = 'unknown';
1068: }
1069: } else {
1070: throw new \Aurora\System\Exceptions\ApiException(\Aurora\System\Notifications::IncorrectFileExtension);
1071: }
1072: } else {
1073: $sError = 'unknown';
1074: }
1075:
1076: if (0 < strlen($sError)) {
1077: $aResponse['Error'] = $sError;
1078: }
1079:
1080: return $aResponse;
1081: }
1082:
1083: /**
1084: *
1085: * @param int $UserId
1086: * @param string $CalendarId
1087: * @param string $SyncToken
1088: * @param int|null $Limit
1089:
1090: * @return array|bool
1091: */
1092: public function GetChangesForCalendar($UserId, $CalendarId, $SyncToken, $Limit = null)
1093: {
1094: \Aurora\System\Api::CheckAccess($UserId);
1095:
1096: $UserPublicId = \Aurora\System\Api::getUserPublicIdById($UserId);
1097:
1098: $oCalendar = $this->getManager()->getCalendar($UserPublicId, $CalendarId);
1099: if (!$oCalendar) {
1100: throw new Exceptions\Exception(Enums\ErrorCodes::CannotFindCalendar);
1101: }
1102:
1103: $changes = $this->getManager()->getChangesForCalendar($UserPublicId, $CalendarId, $SyncToken, $Limit);
1104: $result = [];
1105: if (is_array($changes)) {
1106: foreach ($changes as $action => &$uris) {
1107: if (is_array($uris) && $action !== 'syncToken') {
1108: foreach ($uris as $key => $uri) {
1109: if (empty(trim($uri))) {
1110: unset($uris[$key]);
1111: } else {
1112: $pinfo = pathinfo($uri);
1113: if (isset($pinfo['filename'])) {
1114: $uris[$key] = $pinfo['filename'];
1115: }
1116: }
1117: }
1118:
1119: $uris = array_values($uris);
1120: }
1121:
1122: $result[ucfirst($action)] = $uris;
1123: }
1124: }
1125:
1126: return $result;
1127: }
1128:
1129: public function onGetBodyStructureParts($aParts, &$aResult)
1130: {
1131: foreach ($aParts as $oPart) {
1132: if ($oPart instanceof \MailSo\Imap\BodyStructure &&
1133: $oPart->ContentType() === 'text/calendar') {
1134: $aResult[] = $oPart;
1135: break;
1136: }
1137: }
1138: }
1139:
1140: public function onExtendMessageData($aData, &$oMessage)
1141: {
1142: $oUser = \Aurora\System\Api::getAuthenticatedUser();
1143: $sUserPublicId = \Aurora\System\Api::getUserPublicIdById($oUser->Id);
1144: $sFromEmail = '';
1145: $oFromCollection = $oMessage->getFrom();
1146: if ($oFromCollection && 0 < $oFromCollection->Count()) {
1147: $oFrom = &$oFromCollection->GetByIndex(0);
1148: if ($oFrom) {
1149: $sFromEmail = trim($oFrom->GetEmail());
1150: }
1151: }
1152: foreach ($aData as $aDataItem) {
1153: if ($aDataItem['Part'] instanceof \MailSo\Imap\BodyStructure && $aDataItem['Part']->ContentType() === 'text/calendar') {
1154: $sData = $aDataItem['Data'];
1155: if (!empty($sData)) {
1156: try {
1157: $mResult = $this->getManager()->processICS($sUserPublicId, $sData, $sFromEmail);
1158: } catch (\Exception $oEx) {
1159: $mResult = false;
1160: }
1161: if (is_array($mResult) && !empty($mResult['Action']) && !empty($mResult['Body'])) {
1162: $sTemptFile = md5($sFromEmail . $sData) . '.ics';
1163: if ($this->getFilecacheManager()->put($sUserPublicId, $sTemptFile, $sData, '', self::GetName())) {
1164: $oIcs = Classes\Ics::createInstance();
1165:
1166: $mResult['Description'] = !empty($mResult['Description']) ? $mResult['Description'] : '';
1167: $mResult['Location'] = !empty($mResult['Location']) ? $mResult['Location'] : '';
1168:
1169: $mResult['Description'] = TextUtils::isHtml($mResult['Description']) ? TextUtils::clearHtml($mResult['Description']) : $mResult['Description'];
1170: $mResult['Location'] = TextUtils::isHtml($mResult['Location']) ? TextUtils::clearHtml($mResult['Location']) : $mResult['Location'];
1171:
1172: $oIcs->Uid = $mResult['UID'];
1173: $oIcs->Sequence = $mResult['Sequence'];
1174: $oIcs->File = $sTemptFile;
1175: $oIcs->Type = 'SAVE';
1176: $oIcs->Attendee = null;
1177: $oIcs->Location = $mResult['Location'];
1178: $oIcs->Description = $mResult['Description'];
1179: $oIcs->Summary = !empty($mResult['Summary']) ? $mResult['Summary'] : '';
1180: $oIcs->When = !empty($mResult['When']) ? $mResult['When'] : '';
1181: $oIcs->CalendarId = !empty($mResult['CalendarId']) ? $mResult['CalendarId'] : '';
1182: $oIcs->AttendeeList = $mResult['AttendeeList'];
1183: $oIcs->Organizer = $mResult['Organizer'];
1184:
1185: $this->broadcastEvent(
1186: 'CreateIcs',
1187: $mResult,
1188: $oIcs
1189: );
1190:
1191: $oMessage->addExtend('ICAL', $oIcs);
1192: } else {
1193: \Aurora\System\Api::Log('Can\'t save temp file "' . $sTemptFile . '"', \Aurora\System\Enums\LogLevel::Error);
1194: }
1195: }
1196: }
1197: }
1198: }
1199: }
1200:
1201: public function onGetMobileSyncInfo($aArgs, &$mResult)
1202: {
1203: /** @var \Aurora\Modules\Dav\Module */
1204: $oDavModule = \Aurora\Modules\Dav\Module::Decorator();
1205: $iUserId = \Aurora\System\Api::getAuthenticatedUserId();
1206: $aCalendars = self::Decorator()->GetCalendars($iUserId);
1207:
1208: if (isset($aCalendars['Calendars']) && is_array($aCalendars['Calendars']) && 0 < count($aCalendars['Calendars'])) {
1209: foreach ($aCalendars['Calendars'] as $oCalendar) {
1210: if ($oCalendar instanceof Classes\Calendar) {
1211: $mResult['Dav']['Calendars'][] = array(
1212: 'Name' => $oCalendar->DisplayName,
1213: 'Url' => rtrim($oDavModule->GetServerUrl() . $oCalendar->Url, "/") . "/"
1214: );
1215: }
1216: }
1217: }
1218: }
1219:
1220: public function onBeforeDeleteUser($aArgs, &$mResult)
1221: {
1222: if (isset($aArgs['UserId'])) {
1223: $this->oUserForDelete = \Aurora\System\Api::getUserById($aArgs['UserId']);
1224: }
1225: }
1226:
1227: public function onAfterDeleteUser($aArgs, &$mResult)
1228: {
1229: $sUserPublicId = $this->oUserForDelete instanceof \Aurora\Modules\Core\Models\User ? $this->oUserForDelete->PublicId : null;
1230: if ($sUserPublicId) {
1231: $this->getManager()->deletePrincipalCalendars($sUserPublicId);
1232: }
1233: }
1234: }
1235: