1: <?php
2: /**
3: * This code is licensed under AGPLv3 license or Afterlogic Software License
4: * if commercial version of the product was purchased.
5: * For full statements of the licenses see LICENSE-AFTERLOGIC and LICENSE-AGPL3 files.
6: */
7:
8: namespace Aurora\Modules\Contacts\Models;
9:
10: use Aurora\System\Classes\Model;
11: use Aurora\Modules\Contacts\Classes\VCard\Helper;
12: use Aurora\Modules\Contacts\Enums\StorageType;
13: use Aurora\Modules\Contacts\Models\Group;
14: use Aurora\Modules\Core\Models\User;
15: use Aurora\System\EventEmitter;
16:
17: class Contact extends Model
18: {
19: public $GroupsContacts = array();
20:
21: public $ExtendedInformation = array();
22:
23: protected $foreignModel = User::class;
24: protected $foreignModelIdColumn = 'IdUser'; // Column that refers to an external table
25:
26: protected $fillable = [
27: 'Id',
28: 'UUID',
29: 'IdUser',
30: 'IdTenant',
31: 'Storage',
32: 'AddressBookId',
33: 'FullName',
34: 'UseFriendlyName',
35: 'PrimaryEmail',
36: 'PrimaryPhone',
37: 'PrimaryAddress',
38: 'ViewEmail',
39:
40: 'Title',
41: 'FirstName',
42: 'LastName',
43: 'NickName',
44: 'Skype',
45: 'Facebook',
46:
47: 'PersonalEmail',
48: 'PersonalAddress',
49: 'PersonalCity',
50: 'PersonalState',
51: 'PersonalZip',
52: 'PersonalCountry',
53: 'PersonalWeb',
54: 'PersonalFax',
55: 'PersonalPhone',
56: 'PersonalMobile',
57:
58: 'BusinessEmail',
59: 'BusinessCompany',
60: 'BusinessAddress',
61: 'BusinessCity',
62: 'BusinessState',
63: 'BusinessZip',
64: 'BusinessCountry',
65: 'BusinessJobTitle',
66: 'BusinessDepartment',
67: 'BusinessOffice',
68: 'BusinessPhone',
69: 'BusinessFax',
70: 'BusinessWeb',
71:
72: 'OtherEmail',
73: 'Notes',
74:
75: 'BirthDay',
76: 'BirthMonth',
77: 'BirthYear',
78:
79: 'ETag',
80: 'Auto',
81: 'Frequency',
82: 'DateModified',
83: 'Properties'
84: ];
85:
86: protected $casts = [
87: 'Properties' => 'array',
88: 'Auto' => 'boolean',
89: 'UseFriendlyName' => 'boolean'
90: ];
91:
92: protected $appends = [
93: 'AgeScore'
94: ];
95:
96: public function getAgeScoreAttribute()
97: {
98: return 0;
99: }
100:
101: public function getNotesAttribute()
102: {
103: if (is_null($this->attributes['Notes'])) {
104: $this->attributes['Notes'] = '';
105: }
106:
107: return $this->attributes['Notes'];
108: }
109:
110: /**
111: * Adds groups to contact. Groups are specified by names.
112: * @param array $aGroupNames List of group names.
113: */
114: protected function addGroupsFromNames($aGroupNames)
115: {
116: $aNonExistingGroups = [];
117: if (is_array($aGroupNames) && count($aGroupNames) > 0) {
118: $oContactsDecorator = \Aurora\Modules\Contacts\Module::Decorator();
119: $oApiContactsManager = $oContactsDecorator ? $oContactsDecorator->GetApiContactsManager() : null;
120: if ($oApiContactsManager) {
121: foreach ($aGroupNames as $sGroupName) {
122: $oGroups = $oApiContactsManager->getGroups($this->IdUser, Group::firstWhere('Name', $sGroupName));
123: if ($oGroups && count($oGroups) > 0) {
124: $this->Groups()->sync(
125: $oGroups->map(function ($oGroup) {
126: return $oGroup->Id;
127: })->toArray(),
128: false
129: );
130: }
131:
132: // Group shouldn't be created here.
133: // Very often after this populating contact will never be created.
134: // It can be used only for suggestion to create.
135: elseif (!empty($sGroupName)) {
136: $oGroup = new Group();
137: $oGroup->IdUser = $this->IdUser;
138: $oGroup->Name = $sGroupName;
139: $aNonExistingGroups[] = $oGroup;
140: }
141: }
142: }
143: }
144:
145: return $aNonExistingGroups;
146: }
147:
148: /**
149: * Add group to contact.
150: * @param string $sGroupUUID Group UUID.
151: */
152: public function addGroups($aGroupUUIDs, $aGroupNames, $bCreateNonExistingGroups = false)
153: {
154: if (isset($aGroupUUIDs) && is_array($aGroupUUIDs)) {
155: $this->Groups()->sync(Group::whereIn('UUID', $aGroupUUIDs)
156: ->get()->map(
157: function ($oGroup) {
158: return $oGroup->Id;
159: }
160: )->toArray());
161: }
162: $aNonExistingGroups = [];
163: if (isset($aGroupNames)) {
164: $aNonExistingGroups = $this->addGroupsFromNames($aGroupNames);
165: }
166:
167: if ($bCreateNonExistingGroups && is_array($aNonExistingGroups) && count($aNonExistingGroups) > 0) {
168: $oContactsDecorator = \Aurora\Modules\Contacts\Module::Decorator();
169: $oApiContactsManager = $oContactsDecorator ? $oContactsDecorator->GetApiContactsManager() : null;
170: if ($oApiContactsManager) {
171: $oContactsDecorator = \Aurora\Modules\Contacts\Module::Decorator();
172: $oApiContactsManager = $oContactsDecorator ? $oContactsDecorator->GetApiContactsManager() : null;
173: if ($oApiContactsManager) {
174: $aGroupIds = [];
175: foreach ($aNonExistingGroups as $oGroup) {
176: $oApiContactsManager->createGroup($oGroup);
177: $aGroupIds[] = $oGroup->Id;
178: }
179: if (count($aGroupIds) > 0) {
180: $this->Groups()->sync($aGroupIds, false);
181: }
182: }
183: }
184: }
185: }
186:
187: /**
188: * Returns value of email that is specified as primary.
189: * @return string
190: */
191: protected function getViewEmail()
192: {
193: switch ((int) $this->PrimaryEmail) {
194: default:
195: case \Aurora\Modules\Contacts\Enums\PrimaryEmail::Personal:
196: return (string) $this->PersonalEmail;
197: case \Aurora\Modules\Contacts\Enums\PrimaryEmail::Business:
198: return (string) $this->BusinessEmail;
199: case \Aurora\Modules\Contacts\Enums\PrimaryEmail::Other:
200: return (string) $this->OtherEmail;
201: }
202: }
203:
204: /**
205: * Sets ViewEmail field.
206: */
207: public function SetViewEmail()
208: {
209: $this->ViewEmail = $this->getViewEmail();
210: }
211:
212: /**
213: * Inits contacts from Vcard string.
214: * @param int $iUserId User identifier.
215: * @param string $sData Vcard string.
216: * @param string $sUid Contact UUID.
217: */
218: public function InitFromVCardStr($iUserId, $sData, $sUid = '')
219: {
220: $oUser = \Aurora\Modules\Core\Module::Decorator()->GetUserUnchecked($iUserId);
221: if ($oUser instanceof \Aurora\Modules\Core\Models\User) {
222: $this->IdUser = $oUser->Id;
223: $this->IdTenant = $oUser->IdTenant;
224: }
225:
226: if (!empty($sUid)) {
227: $this->UUID = $sUid;
228: }
229:
230: $this->populate(
231: Helper::GetContactDataFromVcard(
232: \Sabre\VObject\Reader::read(
233: $sData,
234: \Sabre\VObject\Reader::OPTION_IGNORE_INVALID_LINES
235: )
236: )
237: );
238: }
239:
240: /**
241: * Populate contact with specified data.
242: * @param array $aContact List of contact data.
243: */
244: public function populate($aContact, $bCreateNonExistingGroups = false)
245: {
246: $aNonExistingGroups = [];
247: $aStorageParts = \explode('-', $aContact['Storage']);
248: if (isset($aStorageParts[0]) && $aStorageParts[0] === StorageType::AddressBook) {
249: $aContact['AddressBookId'] = (int) $aStorageParts[1];
250: $aContact['Storage'] = StorageType::AddressBook;
251: }
252: parent::populate($aContact);
253:
254: if (!empty($aContact['UUID'])) {
255: $this->UUID = $aContact['UUID'];
256: } elseif (empty($this->UUID)) {
257: $this->UUID = \Sabre\DAV\UUIDUtil::getUUID();
258: }
259: $this->SetViewEmail();
260:
261: EventEmitter::getInstance()->emit('Contacts', 'PopulateContactModel', $this);
262: }
263:
264:
265: /**
266: * Returns array with contact data.
267: * @return array
268: */
269: public function toResponseArray()
270: {
271: // $this->calculateETag();
272:
273: $aRes = parent::toResponseArray();
274: if (is_null($aRes['Notes'])) {
275: $aRes['Notes'] = '';
276: }
277: $aRes['GroupUUIDs'] = $this->Groups->map(function ($oGroup) {
278: return $oGroup->UUID;
279: });
280:
281: foreach ($this->ExtendedInformation as $sKey => $mValue) {
282: $aRes[$sKey] = $mValue;
283: }
284:
285: $aArgs = ['Contact' => $this];
286: \Aurora\System\Api::GetModule('Core')->broadcastEvent(
287: 'Contacts::Contact::ToResponseArray',
288: $aArgs,
289: $aRes
290: );
291:
292: return $aRes;
293: }
294:
295: public function calculateETag()
296: {
297: $this->ETag = \md5(\json_encode($this));
298: }
299:
300: public function Groups()
301: {
302: return $this->belongsToMany(Group::class, 'contacts_group_contact', 'ContactId', 'GroupId');
303: }
304:
305: public function getStorageWithId()
306: {
307: if ($this->Storage === StorageType::AddressBook) {
308: return $this->Storage . $this->AddressBookId;
309: } else {
310: return $this->Storage;
311: }
312: }
313: }
314: