1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* suggestEmailAddressModule. |
5
|
|
|
* |
6
|
|
|
* Class is used to store/retrieve suggestion list entries from a mapi property PR_EC_RECIPIENT_HISTORY_JSON |
7
|
|
|
* on default store. The format of recipient history that is stored in this property is shown below |
8
|
|
|
* { |
9
|
|
|
* recipients : [ |
10
|
|
|
* 'display_name' : 'foo bar', |
11
|
|
|
* 'smtp_address' : '[email protected]', |
12
|
|
|
* 'count' : 1, |
13
|
|
|
* 'last_used' : 1232313121, |
14
|
|
|
* 'object_type' : 6 // MAPI_MAILUSER |
15
|
|
|
* ] |
16
|
|
|
* } |
17
|
|
|
*/ |
18
|
|
|
class suggestEmailAddressModule extends Module { |
19
|
|
|
public function __construct($id, $data) { |
20
|
|
|
parent::__construct($id, $data); |
21
|
|
|
} |
22
|
|
|
|
23
|
|
|
#[Override] |
24
|
|
|
public function execute() { |
25
|
|
|
try { |
26
|
|
|
// Retrieve the recipient history |
27
|
|
|
$storeProps = mapi_getprops($GLOBALS["mapisession"]->getDefaultMessageStore(), [PR_EC_RECIPIENT_HISTORY_JSON]); |
28
|
|
|
$recipient_history = false; |
29
|
|
|
|
30
|
|
|
if (isset($storeProps[PR_EC_RECIPIENT_HISTORY_JSON]) || propIsError(PR_EC_RECIPIENT_HISTORY_JSON, $storeProps) == MAPI_E_NOT_ENOUGH_MEMORY) { |
31
|
|
|
$datastring = streamProperty($GLOBALS["mapisession"]->getDefaultMessageStore(), PR_EC_RECIPIENT_HISTORY_JSON); |
32
|
|
|
|
33
|
|
|
if ($datastring !== "") { |
34
|
|
|
$recipient_history = json_decode_data($datastring, true); |
35
|
|
|
} |
36
|
|
|
} |
37
|
|
|
|
38
|
|
|
foreach ($this->data as $actionType => $action) { |
39
|
|
|
if (isset($actionType)) { |
40
|
|
|
switch ($actionType) { |
41
|
|
|
case 'delete': |
42
|
|
|
$this->deleteRecipient($action, $recipient_history); |
|
|
|
|
43
|
|
|
break; |
44
|
|
|
|
45
|
|
|
case 'list': |
46
|
|
|
$data = $this->getRecipientList($action, $recipient_history); |
|
|
|
|
47
|
|
|
|
48
|
|
|
// Pass data on to be returned to the client |
49
|
|
|
$this->addActionData("list", $data); |
50
|
|
|
$GLOBALS["bus"]->addData($this->getResponseData()); |
51
|
|
|
|
52
|
|
|
break; |
53
|
|
|
} |
54
|
|
|
} |
55
|
|
|
} |
56
|
|
|
} |
57
|
|
|
catch (MAPIException $e) { |
58
|
|
|
$this->processException($e, $actionType); |
59
|
|
|
} |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
public static function cmpSortResultList($a, $b) { |
63
|
|
|
if ($a['count'] < $b['count']) { |
64
|
|
|
return 1; |
65
|
|
|
} |
66
|
|
|
if ($a['count'] > $b['count']) { |
67
|
|
|
return -1; |
68
|
|
|
} |
69
|
|
|
$l_iReturnVal = strnatcasecmp((string) $a['display_name'], (string) $b['display_name']); |
70
|
|
|
if ($l_iReturnVal == 0) { |
71
|
|
|
$l_iReturnVal = strnatcasecmp((string) $a['smtp_address'], (string) $b['smtp_address']); |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
return $l_iReturnVal; |
75
|
|
|
} |
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* Function is used to delete a recipient entry from already stored recipient history |
79
|
|
|
* in mapi property. it searches for deleteRecipients key in the action array which will |
80
|
|
|
* contain email addresses of recipients that should be deleted in semicolon separated format. |
81
|
|
|
* |
82
|
|
|
* @param array $action action data in associative array format |
83
|
|
|
* @param array $recipient_history recipient history stored in mapi property |
84
|
|
|
*/ |
85
|
|
|
public function deleteRecipient($action, $recipient_history) { |
86
|
|
|
if (isset($action) && !empty($recipient_history) && !empty($recipient_history['recipients'])) { |
87
|
|
|
/* |
88
|
|
|
* A foreach is used instead of a normal for-loop to |
89
|
|
|
* prevent the loop from finishing before the end of |
90
|
|
|
* the array, because of the unsetting of elements |
91
|
|
|
* in that array. |
92
|
|
|
*/ |
93
|
|
|
foreach ($recipient_history['recipients'] as $index => $recipient) { |
94
|
|
|
if ($action['email_address'] == $recipient['email_address'] || $action['smtp_address'] == $recipient['smtp_address']) { |
95
|
|
|
unset($recipient_history['recipients'][$index]); |
96
|
|
|
} |
97
|
|
|
} |
98
|
|
|
// Re-indexing recipients' array to adjust index of deleted recipients |
99
|
|
|
$recipient_history['recipients'] = array_values($recipient_history['recipients']); |
100
|
|
|
|
101
|
|
|
// Write new recipient history to property |
102
|
|
|
$l_sNewRecipientHistoryJSON = json_encode($recipient_history); |
103
|
|
|
|
104
|
|
|
$stream = mapi_openproperty($GLOBALS["mapisession"]->getDefaultMessageStore(), PR_EC_RECIPIENT_HISTORY_JSON, IID_IStream, 0, MAPI_CREATE | MAPI_MODIFY); |
105
|
|
|
mapi_stream_setsize($stream, strlen($l_sNewRecipientHistoryJSON)); |
106
|
|
|
mapi_stream_write($stream, $l_sNewRecipientHistoryJSON); |
107
|
|
|
mapi_stream_commit($stream); |
108
|
|
|
mapi_savechanges($GLOBALS["mapisession"]->getDefaultMessageStore()); |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
// send success message to client |
112
|
|
|
$this->sendFeedback(true); |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
/** |
116
|
|
|
* Function is used to get recipient history from mapi property based |
117
|
|
|
* on the query specified by the client in action array. |
118
|
|
|
* |
119
|
|
|
* @param array $action action data in associative array format |
120
|
|
|
* @param array $recipient_history recipient history stored in mapi property |
121
|
|
|
* |
122
|
|
|
* @returns {Array} data holding recipients that matched the query. |
123
|
|
|
*/ |
124
|
|
|
public function getRecipientList($action, $recipient_history) { |
125
|
|
|
if (!empty($action["query"]) && !empty($recipient_history) && !empty($recipient_history['recipients'])) { |
126
|
|
|
// Setup result array with match levels |
127
|
|
|
$l_aResult = [ |
128
|
|
|
0 => [], |
129
|
|
|
1 => [], |
130
|
|
|
]; |
131
|
|
|
|
132
|
|
|
// Loop through all the recipients |
133
|
|
|
|
134
|
|
|
for ($i = 0, $len = count($recipient_history['recipients']); $i < $len; ++$i) { |
135
|
|
|
// Prepare strings for case sensitive search |
136
|
|
|
$l_sName = strtolower((string) $recipient_history['recipients'][$i]['display_name']); |
137
|
|
|
$l_sEmail = strtolower((string) $recipient_history['recipients'][$i]['smtp_address']); |
138
|
|
|
$l_sSearchString = strtolower((string) $action["query"]); |
139
|
|
|
|
140
|
|
|
// Check for the presence of the search string |
141
|
|
|
$l_ibPosName = strpos($l_sName, $l_sSearchString); |
142
|
|
|
$l_ibPosEmail = strpos($l_sEmail, $l_sSearchString); |
143
|
|
|
|
144
|
|
|
// Check if the string is present in name or email fields |
145
|
|
|
if ($l_ibPosName !== false || $l_ibPosEmail !== false) { |
146
|
|
|
// Check if the found string matches from the start of the word |
147
|
|
|
if ($l_ibPosName === 0 || substr($l_sName, $l_ibPosName - 1, 1) == ' ' || $l_ibPosEmail === 0 || substr($l_sEmail, $l_ibPosEmail - 1, 1) == ' ') { |
148
|
|
|
array_push($l_aResult[0], [ |
149
|
|
|
'display_name' => $recipient_history['recipients'][$i]['display_name'], |
150
|
|
|
'smtp_address' => $recipient_history['recipients'][$i]['smtp_address'], |
151
|
|
|
'email_address' => $recipient_history['recipients'][$i]['email_address'], |
152
|
|
|
'address_type' => $recipient_history['recipients'][$i]['address_type'], |
153
|
|
|
'count' => $recipient_history['recipients'][$i]['count'], |
154
|
|
|
'last_used' => $recipient_history['recipients'][$i]['last_used'], |
155
|
|
|
'object_type' => $recipient_history['recipients'][$i]['object_type'], |
156
|
|
|
]); |
157
|
|
|
// Does not match from start of a word, but start in the middle |
158
|
|
|
} |
159
|
|
|
else { |
160
|
|
|
array_push($l_aResult[1], [ |
161
|
|
|
'display_name' => $recipient_history['recipients'][$i]['display_name'], |
162
|
|
|
'smtp_address' => $recipient_history['recipients'][$i]['smtp_address'], |
163
|
|
|
'email_address' => $recipient_history['recipients'][$i]['email_address'], |
164
|
|
|
'address_type' => $recipient_history['recipients'][$i]['address_type'], |
165
|
|
|
'count' => $recipient_history['recipients'][$i]['count'], |
166
|
|
|
'last_used' => $recipient_history['recipients'][$i]['last_used'], |
167
|
|
|
'object_type' => $recipient_history['recipients'][$i]['object_type'], |
168
|
|
|
]); |
169
|
|
|
} |
170
|
|
|
} |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
// Prevent the displaying of the exact match of the whole email address when only one item is found. |
174
|
|
|
if (count($l_aResult[0]) == 1 && empty($l_aResult[1]) && $l_sSearchString == strtolower((string) $l_aResult[0][0]['smtp_address'])) { |
|
|
|
|
175
|
|
|
$recipientList = []; |
176
|
|
|
} |
177
|
|
|
else { |
178
|
|
|
/** |
179
|
|
|
* Sort lists. |
180
|
|
|
* |
181
|
|
|
* This block of code sorts the two lists and creates one final list. |
182
|
|
|
* The first list holds the matches based on whole words or words |
183
|
|
|
* beginning with the search string and the second list contains the |
184
|
|
|
* partial matches that start in the middle of the words. |
185
|
|
|
* The first list is sorted on count (the number of emails sent to this |
186
|
|
|
* email address), name and finally on the email address. This is done |
187
|
|
|
* by a natural sort. When this first list already contains the maximum |
188
|
|
|
* number of returned items the second list needs no sorting. If it has |
189
|
|
|
* less, then the second list is sorted and included in the first list |
190
|
|
|
* as well. At the end the final list is sorted on name and email again. |
191
|
|
|
*/ |
192
|
|
|
$l_iMaxNumListItems = 10; |
193
|
|
|
$l_aSortedList = []; |
194
|
|
|
usort($l_aResult[0], [self::class, 'cmpSortResultList']); |
195
|
|
|
for ($i = 0, $len = min($l_iMaxNumListItems, count($l_aResult[0])); $i < $len; ++$i) { |
196
|
|
|
$l_aSortedList[] = $l_aResult[0][$i]; |
197
|
|
|
} |
198
|
|
|
if (count($l_aSortedList) < $l_iMaxNumListItems) { |
199
|
|
|
$l_iMaxNumRemainingListItems = $l_iMaxNumListItems - count($l_aSortedList); |
200
|
|
|
usort($l_aResult[1], [self::class, 'cmpSortResultList']); |
201
|
|
|
for ($i = 0, $len = min($l_iMaxNumRemainingListItems, count($l_aResult[1])); $i < $len; ++$i) { |
202
|
|
|
$l_aSortedList[] = $l_aResult[1][$i]; |
203
|
|
|
} |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
$recipientList = []; |
207
|
|
|
foreach ($l_aSortedList as $index => $recipient) { |
208
|
|
|
$recipient['id'] = count($recipientList) + 1; |
209
|
|
|
$recipientList[] = $recipient; |
210
|
|
|
} |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
$data = [ |
214
|
|
|
'query' => $action["query"], |
215
|
|
|
'results' => $recipientList, |
216
|
|
|
]; |
217
|
|
|
} |
218
|
|
|
else { |
219
|
|
|
$data = [ |
220
|
|
|
'query' => $action["query"], |
221
|
|
|
'results' => [], |
222
|
|
|
]; |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
return $data; |
226
|
|
|
} |
227
|
|
|
} |
228
|
|
|
|