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\SharedContacts;
8:
9: use Afterlogic\DAV\Constants;
10: use Aurora\Api;
11: use Aurora\Modules\Contacts\Enums\Access;
12: use Aurora\Modules\Contacts\Enums\SortField;
13: use Aurora\Modules\Contacts\Enums\StorageType;
14: use Aurora\Modules\Contacts\Models\AddressBook;
15: use Aurora\Modules\Contacts\Models\Contact;
16: use Aurora\Modules\Contacts\Module as ContactsModule;
17: use Aurora\Modules\Core\Models\Group;
18: use Aurora\Modules\Core\Models\User;
19: use Aurora\System\Enums\UserRole;
20: use Aurora\System\Exceptions\InvalidArgumentException;
21: use Aurora\System\Notifications;
22: use Illuminate\Database\Capsule\Manager as Capsule;
23: use Sabre\DAV\UUIDUtil;
24: use Aurora\Modules\Core\Module as CoreModule;
25:
26: /**
27: * @license https://afterlogic.com/products/common-licensing Afterlogic Software License
28: * @copyright Copyright (c) 2023, Afterlogic Corp.
29: *
30: * @package Modules
31: */
32: class Module extends \Aurora\System\Module\AbstractModule
33: {
34: protected static $iStorageOrder = 10;
35:
36: protected $oBeforeDeleteUser = null;
37:
38: public function init()
39: {
40: $this->subscribeEvent('Contacts::GetStorages', array($this, 'onGetStorages'));
41: $this->subscribeEvent('Contacts::PrepareFiltersFromStorage', array($this, 'prepareFiltersFromStorage'));
42:
43: $this->subscribeEvent('Contacts::UpdateSharedContacts::after', array($this, 'onAfterUpdateSharedContacts'));
44:
45: $this->subscribeEvent('Contacts::CheckAccessToObject::after', array($this, 'onAfterCheckAccessToObject'));
46: $this->subscribeEvent('Contacts::GetContactSuggestions', array($this, 'onGetContactSuggestions'));
47: $this->subscribeEvent('Contacts::GetAddressBooks::after', array($this, 'onAfterGetAddressBooks'), 1000);
48: $this->subscribeEvent('Contacts::PopulateContactModel', array($this, 'onPopulateContactModel'));
49:
50: $this->subscribeEvent('Core::AddUsersToGroup::after', [$this, 'onAfterAddUsersToGroup']);
51: $this->subscribeEvent('Core::RemoveUsersFromGroup::after', [$this, 'onAfterRemoveUsersFromGroup']);
52: $this->subscribeEvent('Core::CreateUser::after', [$this, 'onAfterCreateUser']);
53: $this->subscribeEvent('Core::UpdateUser::after', [$this, 'onAfterUpdateUser']);
54: $this->subscribeEvent('Core::DeleteUser::before', [$this, 'onBeforeDeleteUser']);
55: $this->subscribeEvent('Core::DeleteUser::after', [$this, 'onAfterDeleteUser']);
56: $this->subscribeEvent('Core::DeleteGroup::after', [$this, 'onAfterDeleteGroup']);
57: }
58:
59: public function GetAddressbooks($UserId)
60: {
61: $mResult = [];
62:
63: Api::checkUserRoleIsAtLeast(UserRole::NormalUser);
64: Api::CheckAccess($UserId);
65:
66: $dBPrefix = Api::GetSettings()->DBPrefix;
67: $stmt = Api::GetPDO()->prepare("
68: select ab.*, sab.access, sab.group_id, ca.Id as addressbook_id, cu.Id as UserId from " . $dBPrefix . "adav_shared_addressbooks sab
69: left join " . $dBPrefix . "adav_addressbooks ab on sab.addressbook_id = ab.id
70: left join " . $dBPrefix . "core_users cu on ab.principaluri = CONCAT('principals/', cu.PublicId)
71: left join " . $dBPrefix . "contacts_addressbooks ca on ca.UUID = ab.uri
72: where sab.principaluri = ?
73: ");
74:
75: $principalUri = Constants::PRINCIPALS_PREFIX . Api::getUserPublicIdById($UserId);
76: $stmt->execute([
77: $principalUri
78: ]);
79:
80: $abooks = $stmt->fetchAll(\PDO::FETCH_ASSOC);
81:
82: foreach ($abooks as $abook) {
83: if ($abook['principaluri'] !== $principalUri) {
84: if (isset($abook['addressbook_id'])) {
85: $storage = StorageType::Shared . '-' . $abook['UserId'] . '-' . $abook['addressbook_id'];
86: } else {
87: $storage = StorageType::Shared . '-' . $abook['UserId'] . '-' . StorageType::Personal;
88: }
89:
90: if (count($mResult) > 0) {
91: foreach ($mResult as $key => $val) {
92: if ($val['Id'] === $storage) {
93: if ($val['GroupId'] != 0) { //group sharing
94: if ($abook['access'] !== Access::Read) {
95: if ($val['Access'] > (int) $abook['access'] || (int) $abook['access'] === Access::NoAccess) {
96: $mResult[$key]['Access'] = (int) $abook['access'];
97: }
98: } elseif ($val['Access'] !== Access::Write) {
99: $mResult[$key]['Access'] = (int) $abook['access'];
100: }
101: }
102: continue 2;
103: }
104: }
105: }
106:
107: $prevState = Api::skipCheckUserRole(true);
108: $ctag = ContactsModule::Decorator()->GetCTag($abook['UserId'], $storage);
109: Api::skipCheckUserRole($prevState);
110:
111: $mResult[] = [
112: 'Id' => $storage,
113: 'EntityId' => isset($abook['addressbook_id']) ? (int) $abook['addressbook_id'] : null,
114: 'CTag' => $ctag,
115: 'Display' => true,
116: 'Order' => 1,
117: 'DisplayName' => $abook['displayname'] . ' (' . basename($abook['principaluri']) . ')',
118: 'Shared' => true,
119: 'Access' => (int) $abook['access'],
120: 'Owner' => basename($abook['principaluri']),
121: 'GroupId' => (int) $abook['group_id']
122: ];
123: }
124: }
125:
126: return array_filter($mResult, function ($item) {
127: return ($item['Access'] !== Access::NoAccess);
128: });
129: }
130:
131: public function GetSharesForAddressbook($UserId, $Id)
132: {
133: Api::checkUserRoleIsAtLeast(UserRole::NormalUser);
134: Api::CheckAccess($UserId);
135:
136: $aResult = [];
137:
138: $shares = $this->_getSharesForAddressbook($UserId, $Id);
139: if (count($shares) > 0) {
140: $oUser = Api::getUserById($UserId);
141: $groups = [];
142: foreach ($shares as $share) {
143: if ($share['group_id'] != 0) {
144: if (!in_array($share['group_id'], $groups)) {
145: $oGroup = CoreModule::Decorator()->GetGroup($oUser->IdTenant, (int) $share['group_id']);
146: if ($oGroup) {
147: $groups[] = $share['group_id'];
148: $aResult[] = [
149: 'PublicId' => $oGroup->getName(),
150: 'Access' => (int) $share['access'],
151: 'IsGroup' => true,
152: 'IsAll' => !!$oGroup->IsAll,
153: 'GroupId' => (int) $share['group_id']
154: ];
155: }
156: }
157: } else {
158: $aResult[] = [
159: 'PublicId' => basename($share['principaluri']),
160: 'Access' => (int) $share['access']
161: ];
162: }
163: }
164: }
165:
166: return $aResult;
167: }
168:
169: protected function _getSharesForAddressbook($iUserId, $abookComplexId)
170: {
171: $dBPrefix = Api::GetSettings()->DBPrefix;
172: $stmt = Api::GetPDO()->prepare("
173: select * from (select sab.*, CASE WHEN ca.Id is null THEN ? ELSE CONCAT(?, ca.Id) END as storage
174: from " . $dBPrefix . "adav_shared_addressbooks sab
175: left join " . $dBPrefix . "adav_addressbooks ab on sab.addressbook_id = ab.id
176: left join " . $dBPrefix . "core_users cu on ab.principaluri = CONCAT('principals/', cu.PublicId)
177: left join " . $dBPrefix . "contacts_addressbooks ca on ca.UUID = ab.uri
178: where cu.Id = ?) as sub_select where storage = ?
179: ");
180:
181: $stmt->execute([
182: StorageType::Personal,
183: StorageType::AddressBook . '-',
184: $iUserId,
185: $abookComplexId
186: ]);
187:
188: return $stmt->fetchAll(\PDO::FETCH_ASSOC);
189: }
190:
191: protected function getShareForAddressbook($iUserId, $abookComplexId, $principalUri, $groupId = 0)
192: {
193: $dBPrefix = Api::GetSettings()->DBPrefix;
194: $stmt = Api::GetPDO()->prepare("
195: select * from (select sab.*, CASE WHEN ca.Id is null THEN ? ELSE CONCAT(?, ca.Id) END as storage
196: from " . $dBPrefix . "adav_shared_addressbooks sab
197: left join " . $dBPrefix . "adav_addressbooks ab on sab.addressbook_id = ab.id
198: left join " . $dBPrefix . "core_users cu on ab.principaluri = CONCAT('principals/', cu.PublicId)
199: left join " . $dBPrefix . "contacts_addressbooks ca on ca.UUID = ab.uri
200: where cu.Id = ? and sab.principaluri = ? and sab.group_id = ?) as sub_select where storage = ?
201: ");
202:
203: $stmt->execute([
204: StorageType::Personal,
205: StorageType::AddressBook . '-',
206: $iUserId,
207: $principalUri,
208: $groupId,
209: $abookComplexId
210: ]);
211:
212: return $stmt->fetch(\PDO::FETCH_ASSOC);
213: }
214:
215: protected function deleteShareByPublicIds($userId, $abookComplexId, $publicIds)
216: {
217: $dBPrefix = Api::GetSettings()->DBPrefix;
218:
219: $sharesIds = [];
220: foreach ($publicIds as $publicId) {
221: $publicId = \json_decode($publicId);
222: $share = $this->getShareForAddressbook($userId, $abookComplexId, Constants::PRINCIPALS_PREFIX . $publicId[0], $publicId[1]);
223: if ($share) {
224: $sharesIds[] = $share['id'];
225: }
226: }
227: if (count($sharesIds) > 0) {
228: $stmt = Api::GetPDO()->prepare("delete from " . $dBPrefix . "adav_shared_addressbooks where id in (" . \implode(',', $sharesIds) . ")");
229: $stmt->execute();
230: }
231: }
232:
233: protected function getAddressbookByComplexId($iUserId, $abookComplexId)
234: {
235: $mResult = false;
236:
237: $dBPrefix = Api::GetSettings()->DBPrefix;
238:
239: $abookId = \explode('-', $abookComplexId);
240:
241: if (count($abookId) === 1 && $abookId[0] === StorageType::Personal) {
242: $abookId[] = StorageType::Personal;
243: }
244:
245: if (count($abookId) > 1) {
246: if (count($abookId) < 3) {
247: $abookId[2] = $abookId[1];
248: }
249:
250: $iUserId = $abookId[0] === StorageType::Shared ? $abookId[1] : $iUserId;
251: if ($abookId[2] === StorageType::Personal) {
252: $addressbookUri = Constants::ADDRESSBOOK_DEFAULT_NAME;
253: } else {
254: $abook = AddressBook::where('UserId', $iUserId)->where('Id', $abookId[2])->first();
255: if ($abook) {
256: $addressbookUri = $abook->UUID;
257: }
258: }
259: $userPublicId = Api::getUserPublicIdById($iUserId);
260: if (!empty($addressbookUri) && $userPublicId) {
261: $stmt = Api::GetPDO()->prepare("select * from " . $dBPrefix . "adav_addressbooks where principaluri = ? and uri = ?");
262: $stmt->execute([Constants::PRINCIPALS_PREFIX . $userPublicId, $addressbookUri]);
263: $mResult = $stmt->fetch(\PDO::FETCH_ASSOC);
264: }
265: }
266:
267: return $mResult;
268: }
269:
270: protected function createShare($iUserId, $abookComplexId, $share)
271: {
272: $dBPrefix = Api::GetSettings()->DBPrefix;
273:
274: $book = $this->getAddressbookByComplexId($iUserId, $abookComplexId);
275: if ($book) {
276: $shareePublicId = $share['PublicId'];
277: $access = $share['Access'];
278: $groupId = $share['GroupId'];
279: $stmt = Api::GetPDO()->prepare("insert into " . $dBPrefix . "adav_shared_addressbooks
280: (principaluri, access, addressbook_id, addressbookuri, group_id)
281: values (?, ?, ?, ?, ?)");
282: $stmt->execute([Constants::PRINCIPALS_PREFIX . $shareePublicId, $access, $book['id'], UUIDUtil::getUUID(), $groupId]);
283: }
284: }
285:
286: protected function updateShare($iUserId, $abookComplexId, $share)
287: {
288: $dBPrefix = Api::GetSettings()->DBPrefix;
289: $book = $this->getAddressbookByComplexId($iUserId, $abookComplexId);
290: if ($book) {
291: $shareePublicId = $share['PublicId'];
292: $access = $share['Access'];
293: $groupId = $share['GroupId'];
294: $stmt = Api::GetPDO()->prepare("update " . $dBPrefix . "adav_shared_addressbooks
295: set access = ? where principaluri = ? and addressbook_id = ? and group_id = ?");
296: $stmt->execute([$access, Constants::PRINCIPALS_PREFIX . $shareePublicId, $book['id'], $groupId]);
297: }
298: }
299:
300: public function onGetStorages(&$aStorages)
301: {
302: $aStorages[self::$iStorageOrder] = StorageType::Shared;
303: }
304:
305: public function prepareFiltersFromStorage(&$aArgs, &$mResult)
306: {
307: if (!isset($mResult)) {
308: $mResult = \Aurora\Modules\Contacts\Models\Contact::query();
309: }
310: if (isset($aArgs['Storage']) && ($aArgs['Storage'] === StorageType::Shared || $aArgs['Storage'] === StorageType::All)) {
311: $aArgs['IsValid'] = true;
312:
313: $oUser = \Aurora\System\Api::getAuthenticatedUser();
314: $mResult = $mResult->orWhere(function ($query) use ($oUser, $aArgs) {
315: $query = $query->where('IdTenant', $oUser->IdTenant)
316: ->where('Storage', StorageType::Shared)
317: ->where(
318: function ($query) {
319: $query->where('Auto', false)->orWhereNull('Auto');
320: }
321: );
322:
323: // if (isset($aArgs['SortField']) && $aArgs['SortField'] === SortField::Frequency) {
324: // $query->whereNotNull('DateModified');
325: // }
326: });
327: } else {
328: $storageArray = \explode('-', $aArgs['Storage']);
329: if (count($storageArray) === 3 && $storageArray[0] === StorageType::Shared) {
330: $aArgs['IsValid'] = true;
331: $storage = $storageArray[2];
332:
333: $iAddressBookId = 0;
334: if (isset($storage)) {
335: if ($storage === StorageType::Personal) {
336: $sStorage = StorageType::Personal;
337: } else {
338: $iAddressBookId = (int) $storage;
339: $sStorage = StorageType::AddressBook;
340: }
341:
342: $mResult = $mResult->orWhere(function ($query) use ($storageArray, $sStorage, $iAddressBookId, $aArgs) {
343: $query = $query->where('IdUser', $storageArray[1])
344: ->where('Storage', $sStorage)
345: ->where(
346: function ($query) {
347: $query->where('Auto', false)->orWhereNull('Auto');
348: }
349: );
350:
351: if ($iAddressBookId > 0) {
352: $query = $query->where('AddressBookId', $iAddressBookId);
353: }
354: // if (isset($aArgs['SortField']) && $aArgs['SortField'] === SortField::Frequency) {
355: // $query->whereNotNull('DateModified');
356: // }
357: });
358: }
359: }
360: }
361:
362: if (isset($aArgs['Storage']) && $aArgs['Storage'] === StorageType::All) {
363: $aBooks = $this->GetAddressbooks($aArgs['UserId']);
364:
365: if (is_array($aBooks) && count($aBooks) > 0) {
366: $aArgs['IsValid'] = true;
367:
368: $aWhen = [];
369: foreach ($aBooks as $aBook) {
370: $storageArray = \explode('-', $aBook['Id']);
371: $storage = $storageArray[2];
372:
373: $iAddressBookId = 0;
374: if (isset($storage)) {
375: if ($storage !== StorageType::Personal) {
376: $iAddressBookId = (int) $storage;
377: $storage = StorageType::AddressBook;
378: }
379:
380: $mResult = $mResult->orWhere(function ($query) use ($storageArray, $storage, $iAddressBookId, $aArgs, $aBook, &$aWhen) {
381: $query = $query->where('IdUser', $storageArray[1])
382: ->where('Storage', $storage)
383: ->where(
384: function ($query) {
385: $query->where('Auto', false)->orWhereNull('Auto');
386: }
387: );
388:
389: if ($iAddressBookId > 0) {
390: $query = $query->where('AddressBookId', $iAddressBookId);
391: }
392: // if (isset($aArgs['SortField']) && $aArgs['SortField'] === SortField::Frequency) {
393: // $query->whereNotNull('DateModified');
394: // }
395: if ($iAddressBookId > 0) {
396: $aWhen[] = "WHEN IdUser = ". $storageArray[1] . " AND Storage = '" . $storage . "' AND AddressBookId = " . $iAddressBookId . " THEN '" . $aBook['Id'] . "'";
397: } else {
398: $aWhen[] = "WHEN IdUser = ". $storageArray[1] . " AND Storage = '" . $storage . "' THEN '" . $aBook['Id'] . "'";
399: }
400: });
401: }
402: }
403: $rawSql = Capsule::connection()->raw("*, CASE " . \implode("\r\n", $aWhen) . " ELSE Storage END as Storage");
404: $mResult->addSelect($rawSql);
405: }
406: }
407: }
408:
409: public function onAfterUpdateSharedContacts($aArgs, &$mResult)
410: {
411: $oContacts = \Aurora\Modules\Contacts\Module::Decorator();
412: $aUUIDs = isset($aArgs['UUIDs']) ? $aArgs['UUIDs'] : [];
413:
414: foreach ($aUUIDs as $sUUID) {
415: $oContact = $oContacts->GetContact($sUUID, $aArgs['UserId']);
416: if ($oContact instanceof Contact) {
417: $sOldStorage = $oContact->getStorageWithId();
418: $iUserId = -1;
419:
420: if ($oContact->Storage === StorageType::Shared) {
421: $oContact->Storage = StorageType::Personal;
422: $iUserId = $oContact->IdTenant;
423: $oContact->IdUser = $aArgs['UserId'];
424: } elseif ($oContact->Storage === StorageType::Personal) {
425: $oContact->Storage = StorageType::Shared;
426: $iUserId = $oContact->IdUser;
427: }
428: // update CTag for previous storage
429: \Aurora\Modules\Contacts\Module::getInstance()->getManager()->updateCTag($iUserId, $sOldStorage);
430: $mResult = $oContacts->UpdateContact($aArgs['UserId'], $oContact->toArray());
431: }
432: }
433: }
434:
435: public function onAfterCheckAccessToObject(&$aArgs, &$mResult)
436: {
437: $oUser = $aArgs['User'];
438: $oContact = isset($aArgs['Contact']) ? $aArgs['Contact'] : null;
439: $Access = isset($aArgs['Access']) ? (int) $aArgs['Access'] : null;
440:
441: if ($oContact instanceof \Aurora\Modules\Contacts\Models\Contact) {
442: if ($oContact->IdUser === $oUser->Id) {
443: $mResult = true;
444: return true; // break other subscriptions
445: }
446: if ($oContact->Storage === StorageType::Shared) {
447: if ($oUser->Role !== \Aurora\System\Enums\UserRole::SuperAdmin && $oUser->IdTenant !== $oContact->IdTenant) {
448: $mResult = false;
449: } else {
450: $mResult = true;
451: }
452: } elseif ($oContact->Storage === StorageType::Personal || $oContact->Storage === StorageType::AddressBook) {
453: $dBPrefix = Api::GetSettings()->DBPrefix;
454: $sql = "select ab.*, sab.access, ca.Id as addressbook_id, cu.Id as UserId from " . $dBPrefix . "adav_shared_addressbooks sab
455: left join " . $dBPrefix . "adav_addressbooks ab on sab.addressbook_id = ab.id
456: left join " . $dBPrefix . "core_users cu on ab.principaluri = CONCAT('principals/', cu.PublicId)
457: left join " . $dBPrefix . "contacts_addressbooks ca on ca.UUID = ab.uri
458: where sab.principaluri = ? and cu.Id = ?";
459: if ($oContact->Storage === StorageType::AddressBook) {
460: $sql .= ' and ca.Id = ' . $oContact->AddressBookId;
461: } elseif ($oContact->Storage === StorageType::Personal) {
462: $sql .= ' and ca.Id is null';
463: }
464:
465: $stmt = Api::GetPDO()->prepare($sql);
466:
467: $stmt->execute([
468: Constants::PRINCIPALS_PREFIX . $oUser->PublicId,
469: $oContact->IdUser
470: ]);
471:
472: $abook = $stmt->fetch(\PDO::FETCH_ASSOC);
473: if ($abook) {
474: if ((int) $abook['access'] === Access::NoAccess) {
475: $mResult = false;
476: } elseif (isset($Access)) {
477: if ($Access === (int) $abook['access'] && $Access === Access::Write) {
478: $mResult = true;
479: } else {
480: $mResult = false;
481: }
482: } else {
483: $mResult = true;
484: }
485:
486: return true; // break other subscriptions
487: }
488: }
489: }
490: }
491:
492: public function onGetContactSuggestions(&$aArgs, &$mResult)
493: {
494: if ($aArgs['Storage'] === 'all' || $aArgs['Storage'] === StorageType::Shared) {
495: $mResult[StorageType::Shared] = \Aurora\Modules\Contacts\Module::Decorator()->GetContacts(
496: $aArgs['UserId'],
497: StorageType::Shared,
498: 0,
499: $aArgs['Limit'],
500: $aArgs['SortField'],
501: $aArgs['SortOrder'],
502: $aArgs['Search']
503: );
504: }
505: }
506:
507: public function onAfterGetAddressBooks(&$aArgs, &$mResult)
508: {
509: if (!is_array($mResult)) {
510: $mResult = [];
511: }
512: foreach ($mResult as $key => $abook) {
513: $mResult[$key]['Shares'] = self::Decorator()->GetSharesForAddressbook($aArgs['UserId'], $abook['Id']);
514: }
515: $mResult = array_merge(
516: $mResult,
517: $this->GetAddressbooks($aArgs['UserId'])
518: );
519: }
520:
521: public function onPopulateContactModel(&$oContact, &$mResult)
522: {
523: if ($oContact instanceof Contact) {
524: $aStorageParts = \explode('-', $oContact->Storage);
525: if (is_array($aStorageParts) && count($aStorageParts) === 3 && $aStorageParts[0] === StorageType::Shared) {
526: $abooks = $this->GetAddressbooks($oContact->IdUser);
527: foreach ($abooks as $abook) {
528: if ($abook['Id'] === $oContact->Storage) {
529: if ($aStorageParts[2] === StorageType::Personal) {
530: $oContact->Storage = StorageType::Personal;
531: } else {
532: $oContact->Storage = StorageType::AddressBook;
533: $oContact->AddressBookId = (int) $aStorageParts[2];
534: }
535:
536: $oContact->IdUser = (int) $aStorageParts[1];
537: break;
538: }
539: }
540: }
541: }
542: }
543:
544: public function UpdateAddressBookShare($UserId, $Id, $Shares)
545: {
546: $mResult = true;
547:
548: Api::checkUserRoleIsAtLeast(UserRole::NormalUser);
549: Api::CheckAccess($UserId);
550:
551: if (!isset($Id) || !is_array($Shares)) {
552: throw new InvalidArgumentException("", Notifications::InvalidInputParameter);
553: }
554:
555: try {
556: $oUser = Api::getUserById($UserId);
557: $currentABookShares = $this->_getSharesForAddressbook($UserId, $Id);
558:
559: $newABookShares = [];
560: foreach ($Shares as $share) {
561: if (isset($share['GroupId'])) {
562: $aUsers = CoreModule::Decorator()->GetGroupUsers($oUser->IdTenant, (int) $share['GroupId']);
563: foreach ($aUsers as $aUser) {
564: $newABookShares[] = [
565: 'PublicId' => $aUser['PublicId'],
566: 'Access' => (int) $share['Access'],
567: 'GroupId' => (int) $share['GroupId'],
568: ];
569: }
570: } else {
571: $share['GroupId'] = 0;
572: $newABookShares[] = $share;
573: }
574: }
575:
576: $currentShares = array_map(function ($share) {
577: return \json_encode([
578: basename($share['principaluri']),
579: $share['group_id']
580: ]);
581: }, $currentABookShares);
582:
583: $newShares = array_map(function ($share) {
584: return \json_encode([
585: $share['PublicId'],
586: $share['GroupId']
587: ]);
588: }, $newABookShares);
589:
590: $sharesToDelete = array_diff($currentShares, $newShares);
591: $sharesToCreate = array_diff($newShares, $currentShares);
592: $sharesToUpdate = array_intersect($currentShares, $newShares);
593:
594: if (count($sharesToDelete) > 0) {
595: $this->deleteShareByPublicIds($UserId, $Id, $sharesToDelete);
596: }
597:
598: foreach ($newABookShares as $share) {
599: $sharePublicIdAndGroupId = \json_encode([
600: $share['PublicId'],
601: $share['GroupId']
602: ]);
603: if (in_array($sharePublicIdAndGroupId, $sharesToCreate)) {
604: $this->createShare($UserId, $Id, $share);
605: }
606: if (in_array($sharePublicIdAndGroupId, $sharesToUpdate)) {
607: $this->updateShare($UserId, $Id, $share);
608: }
609: }
610:
611: $mResult = true;
612: } catch (\Exception $oException) {
613: Api::LogException($oException);
614: $mResult = false;
615: }
616:
617: return $mResult;
618: }
619:
620: public function LeaveShare($UserId, $Id)
621: {
622: $mResult = false;
623: Api::checkUserRoleIsAtLeast(UserRole::NormalUser);
624: Api::CheckAccess($UserId);
625:
626: $abook = $this->getAddressbookByComplexId($UserId, $Id);
627:
628: if ($abook) {
629: $principalUri = Constants::PRINCIPALS_PREFIX . Api::getUserPublicIdById($UserId);
630: $dBPrefix = Api::GetSettings()->DBPrefix;
631:
632: $stmt = Api::GetPDO()->prepare("select count(*) from " . $dBPrefix . "adav_shared_addressbooks
633: where principaluri = ? and addressbook_id = ? and group_id = 0");
634: $stmt->execute([$principalUri, $abook['id']]);
635: $cnt = $stmt->fetch();
636:
637: if ((int) $cnt[0] > 0) { //persona sharing
638: $stmt = Api::GetPDO()->prepare("update " . $dBPrefix . "adav_shared_addressbooks
639: set access = ?
640: where principaluri = ? and addressbook_id = ? and group_id = 0");
641: $mResult = $stmt->execute([Access::NoAccess, $principalUri, $abook['id']]);
642: } else {
643: $stmt = Api::GetPDO()->prepare("insert into " . $dBPrefix . "adav_shared_addressbooks
644: (principaluri, access, addressbook_id, addressbookuri, group_id)
645: values (?, ?, ?, ?, ?)");
646: $mResult = $stmt->execute([$principalUri, Access::NoAccess, $abook['id'], UUIDUtil::getUUID(), 0]);
647: }
648: }
649:
650: return $mResult;
651: }
652:
653: public function onAfterDeleteGroup($aArgs, &$mResult)
654: {
655: if ($mResult) {
656: $dBPrefix = Api::GetSettings()->DBPrefix;
657: $stmt = Api::GetPDO()->prepare("delete from " . $dBPrefix . "adav_shared_addressbooks where group_id = ?");
658: $stmt->execute([$aArgs['GroupId']]);
659: }
660: }
661:
662: /**
663: * @ignore
664: * @param array $aArgs Arguments of event.
665: * @param mixed $mResult Is passed by reference.
666: */
667: public function onBeforeDeleteUser($aArgs, &$mResult)
668: {
669: if (isset($aArgs['UserId'])) {
670: $this->oBeforeDeleteUser = Api::getUserById($aArgs['UserId']);
671: }
672: }
673:
674: /**
675: * @ignore
676: * @param array $aArgs Arguments of event.
677: * @param mixed $mResult Is passed by reference.
678: */
679: public function onAfterDeleteUser($aArgs, $mResult)
680: {
681: if ($mResult && $this->oBeforeDeleteUser instanceof User) {
682: $dBPrefix = Api::GetSettings()->DBPrefix;
683: $stmt = Api::GetPDO()->prepare("delete from " . $dBPrefix . "adav_shared_addressbooks where principaluri = ?");
684: $stmt->execute([Constants::PRINCIPALS_PREFIX . $this->oBeforeDeleteUser->PublicId]);
685: }
686: }
687:
688: public function onAfterAddUsersToGroup($aArgs, &$mResult)
689: {
690: if ($mResult) {
691: foreach ($aArgs['UserIds'] as $iUserId) {
692: $userPublicId = Api::getUserPublicIdById($iUserId);
693: $sUserPrincipalUri = Constants::PRINCIPALS_PREFIX . $userPublicId;
694:
695: $dBPrefix = Api::GetSettings()->DBPrefix;
696: $stmt = Api::GetPDO()->prepare("select distinct addressbook_id, access from " . $dBPrefix . "adav_shared_addressbooks where group_id = ?");
697: $stmt->execute([$aArgs['GroupId']]);
698: $shares = $stmt->fetchAll(\PDO::FETCH_ASSOC);
699: foreach ($shares as $share) {
700: if (is_array($share)) {
701: $stmt = Api::GetPDO()->prepare("insert into " . $dBPrefix . "adav_shared_addressbooks
702: (principaluri, access, addressbook_id, addressbookuri, group_id)
703: values (?, ?, ?, ?, ?)");
704: $stmt->execute([$sUserPrincipalUri, $share['access'], $share['addressbook_id'], UUIDUtil::getUUID(), $aArgs['GroupId']]);
705: }
706: }
707: }
708: }
709: }
710:
711: public function onAfterCreateUser($aArgs, &$mResult)
712: {
713: if ($mResult) {
714: $oUser = User::find($mResult);
715: if ($oUser) {
716: $oGroup = CoreModule::getInstance()->GetAllGroup($oUser->IdTenant);
717: if ($oGroup) {
718: $newArgs = [
719: 'GroupId' => $oGroup->Id,
720: 'UserIds' => [$mResult]
721: ];
722: $newResult = true;
723: $this->onAfterAddUsersToGroup($newArgs, $newResult);
724: }
725: }
726: }
727: }
728:
729: public function onAfterUpdateUser($aArgs, &$mResult)
730: {
731: if ($mResult) {
732: $groupIds = $aArgs['GroupIds'];
733: $userId = $aArgs['UserId'];
734:
735: if ($groupIds !== null) {
736: $userPublicId = Api::getUserPublicIdById($userId);
737: $sUserPrincipalUri = Constants::PRINCIPALS_PREFIX . $userPublicId;
738:
739: $dBPrefix = Api::GetSettings()->DBPrefix;
740: $stmt = Api::GetPDO()->prepare("select * from " . $dBPrefix . "adav_shared_addressbooks where group_id <> 0 and principaluri = ?");
741: $stmt->execute([$sUserPrincipalUri]);
742: $shares = $stmt->fetchAll(\PDO::FETCH_ASSOC);
743:
744: $currentGroupsIds = [];
745: if (is_array($shares)) {
746: $currentGroupsIds = array_map(function ($share) {
747: return $share['group_id'];
748: }, $shares);
749: }
750:
751: $groupsIdsToDelete = array_diff($currentGroupsIds, $groupIds);
752: $groupsIdsToCreate = array_diff($groupIds, $currentGroupsIds);
753:
754: if (count($groupsIdsToDelete) > 0) {
755: $stmt = Api::GetPDO()->prepare("delete from " . $dBPrefix . "adav_shared_addressbooks
756: where group_id in (" . \implode(',', $groupsIdsToDelete) . ") and principaluri = ?");
757: $stmt->execute([$sUserPrincipalUri]);
758: }
759:
760: if (count($groupsIdsToCreate) > 0) {
761: $stmt = Api::GetPDO()->prepare("select distinct addressbook_id, access, group_id from " . $dBPrefix . "adav_shared_addressbooks where group_id in (" . \implode(',', $groupsIdsToCreate) . ")");
762: $stmt->execute();
763: $shares = $stmt->fetchAll(\PDO::FETCH_ASSOC);
764: foreach ($shares as $share) {
765: if (is_array($share)) {
766: $stmt = Api::GetPDO()->prepare("insert into " . $dBPrefix . "adav_shared_addressbooks
767: (principaluri, access, addressbook_id, addressbookuri, group_id)
768: values (?, ?, ?, ?, ?)");
769: $stmt->execute([$sUserPrincipalUri, $share['access'], $share['addressbook_id'], UUIDUtil::getUUID(), $share['group_id']]);
770: }
771: }
772: }
773: }
774: }
775: }
776:
777: public function onAfterRemoveUsersFromGroup($aArgs, &$mResult)
778: {
779: if ($mResult) {
780: $principals = [];
781: foreach ($aArgs['UserIds'] as $iUserId) {
782: $oUser = Api::getUserById($iUserId);
783: $principals[] = Constants::PRINCIPALS_PREFIX . $oUser->PublicId;
784: }
785:
786: if (count($principals) > 0) {
787: $dBPrefix = Api::GetSettings()->DBPrefix;
788: $stmt = Api::GetPDO()->prepare("delete from " . $dBPrefix . "adav_shared_addressbooks where principaluri in (" . \implode(',', $principals) . ") and group_id = ?");
789: $stmt->execute([$aArgs['GroupId']]);
790: }
791: }
792: }
793: }
794: