grommunio /
grommunio-web
| 1 | <?php |
||
| 2 | /** |
||
| 3 | * suggestEmailAddressModule |
||
| 4 | * |
||
| 5 | * Class is used to store/retrieve suggestion list entries from a mapi property PR_EC_RECIPIENT_HISTORY_JSON |
||
| 6 | * on default store. The format of recipient history that is stored in this property is shown below |
||
| 7 | * { |
||
| 8 | * recipients : [ |
||
| 9 | * 'display_name' : 'foo bar', |
||
| 10 | * 'smtp_address' : '[email protected]', |
||
| 11 | * 'count' : 1, |
||
| 12 | * 'last_used' : 1232313121, |
||
| 13 | * 'object_type' : 6 // MAPI_MAILUSER |
||
| 14 | * ] |
||
| 15 | * } |
||
| 16 | */ |
||
| 17 | |||
| 18 | class suggestEmailAddressModule extends Module |
||
| 19 | { |
||
| 20 | function __construct($id, $data) |
||
| 21 | { |
||
| 22 | parent::__construct($id, $data); |
||
| 23 | } |
||
| 24 | |||
| 25 | function execute() |
||
| 26 | { |
||
| 27 | try { |
||
| 28 | // Retrieve the recipient history |
||
| 29 | $storeProps = mapi_getprops($GLOBALS["mapisession"]->getDefaultMessageStore(), array(PR_EC_RECIPIENT_HISTORY_JSON)); |
||
| 30 | $recipient_history = false; |
||
| 31 | |||
| 32 | if(isset($storeProps[PR_EC_RECIPIENT_HISTORY_JSON]) || propIsError(PR_EC_RECIPIENT_HISTORY_JSON, $storeProps) == MAPI_E_NOT_ENOUGH_MEMORY) { |
||
| 33 | $datastring = streamProperty($GLOBALS["mapisession"]->getDefaultMessageStore(), PR_EC_RECIPIENT_HISTORY_JSON); |
||
| 34 | |||
| 35 | if($datastring !== "") { |
||
| 36 | $recipient_history = json_decode_data($datastring, true); |
||
| 37 | } |
||
| 38 | } |
||
| 39 | |||
| 40 | foreach($this->data as $actionType => $action) |
||
| 41 | { |
||
| 42 | if(isset($actionType)) { |
||
| 43 | switch($actionType) { |
||
| 44 | case 'delete': |
||
| 45 | $this->deleteRecipient($action, $recipient_history); |
||
| 46 | break; |
||
| 47 | case 'list': |
||
| 48 | $data = $this->getRecipientList($action, $recipient_history); |
||
| 49 | |||
| 50 | // Pass data on to be returned to the client |
||
| 51 | $this->addActionData("list", $data); |
||
| 52 | $GLOBALS["bus"]->addData($this->getResponseData()); |
||
| 53 | |||
| 54 | break; |
||
| 55 | } |
||
| 56 | } |
||
| 57 | } |
||
| 58 | } catch (MAPIException $e) { |
||
| 59 | $this->processException($e, $actionType); |
||
| 60 | } |
||
| 61 | } |
||
| 62 | |||
| 63 | |||
| 64 | static function cmpSortResultList($a, $b){ |
||
| 65 | if($a['count'] < $b['count']){ |
||
| 66 | return 1; |
||
| 67 | } elseif($a['count'] > $b['count']){ |
||
| 68 | return -1; |
||
| 69 | } else { |
||
| 70 | $l_iReturnVal = strnatcasecmp($a['display_name'], $b['display_name']); |
||
| 71 | if($l_iReturnVal == 0){ |
||
| 72 | $l_iReturnVal = strnatcasecmp($a['smtp_address'], $b['smtp_address']); |
||
| 73 | } |
||
| 74 | return $l_iReturnVal; |
||
| 75 | } |
||
| 76 | } |
||
| 77 | |||
| 78 | /** |
||
| 79 | * Function is used to delete a recipient entry from already stored recipient history |
||
| 80 | * in mapi property. it searches for deleteRecipients key in the action array which will |
||
| 81 | * contain email addresses of recipients that should be deleted in semicolon separated format. |
||
| 82 | * @param {Array} $action action data in associative array format. |
||
| 83 | * @param {Array} $recipient_history recipient history stored in mapi property. |
||
| 84 | */ |
||
| 85 | 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 | * @param {Array} $action action data in associative array format. |
||
| 119 | * @param {Array} $recipient_history recipient history stored in mapi property. |
||
| 120 | * @returns {Array} data holding recipients that matched the query. |
||
| 121 | */ |
||
| 122 | function getRecipientList($action, $recipient_history) { |
||
| 123 | if(!empty($action["query"]) && !empty($recipient_history) && !empty($recipient_history['recipients'])) { |
||
| 124 | // Setup result array with match levels |
||
| 125 | $l_aResult = Array( |
||
| 126 | 0 => Array(), // Matches on whole string |
||
| 127 | 1 => Array() // Matches on part of string |
||
| 128 | ); |
||
| 129 | |||
| 130 | // Loop through all the recipients |
||
| 131 | |||
| 132 | for($i = 0, $len = count($recipient_history['recipients']); $i < $len; $i++) { |
||
| 133 | // Prepare strings for case sensitive search |
||
| 134 | $l_sName = strtolower($recipient_history['recipients'][$i]['display_name']); |
||
| 135 | $l_sEmail = strtolower($recipient_history['recipients'][$i]['smtp_address']); |
||
| 136 | $l_sSearchString = strtolower($action["query"]); |
||
| 137 | |||
| 138 | // Check for the presence of the search string |
||
| 139 | $l_ibPosName = strpos($l_sName, $l_sSearchString); |
||
| 140 | $l_ibPosEmail = strpos($l_sEmail, $l_sSearchString); |
||
| 141 | |||
| 142 | // Check if the string is present in name or email fields |
||
| 143 | if($l_ibPosName !== false || $l_ibPosEmail !== false){ |
||
| 144 | // Check if the found string matches from the start of the word |
||
| 145 | if($l_ibPosName === 0 || substr($l_sName, ($l_ibPosName-1), 1) == ' ' || $l_ibPosEmail === 0 || substr($l_sEmail, ($l_ibPosEmail-1), 1) == ' '){ |
||
| 146 | array_push($l_aResult[0], Array( |
||
| 147 | 'display_name' => $recipient_history['recipients'][$i]['display_name'], |
||
| 148 | 'smtp_address' => $recipient_history['recipients'][$i]['smtp_address'], |
||
| 149 | 'email_address' => $recipient_history['recipients'][$i]['email_address'], |
||
| 150 | 'address_type' => $recipient_history['recipients'][$i]['address_type'], |
||
| 151 | 'count' => $recipient_history['recipients'][$i]['count'], |
||
| 152 | 'last_used' => $recipient_history['recipients'][$i]['last_used'], |
||
| 153 | 'object_type' => $recipient_history['recipients'][$i]['object_type'], |
||
| 154 | )); |
||
| 155 | // Does not match from start of a word, but start in the middle |
||
| 156 | } else { |
||
| 157 | array_push($l_aResult[1], Array( |
||
| 158 | 'display_name' => $recipient_history['recipients'][$i]['display_name'], |
||
| 159 | 'smtp_address' => $recipient_history['recipients'][$i]['smtp_address'], |
||
| 160 | 'email_address' => $recipient_history['recipients'][$i]['email_address'], |
||
| 161 | 'address_type' => $recipient_history['recipients'][$i]['address_type'], |
||
| 162 | 'count' => $recipient_history['recipients'][$i]['count'], |
||
| 163 | 'last_used' => $recipient_history['recipients'][$i]['last_used'], |
||
| 164 | 'object_type' => $recipient_history['recipients'][$i]['object_type'], |
||
| 165 | )); |
||
| 166 | } |
||
| 167 | } |
||
| 168 | } |
||
| 169 | |||
| 170 | // Prevent the displaying of the exact match of the whole email address when only one item is found. |
||
| 171 | if(count($l_aResult[0]) == 1 && empty($l_aResult[1]) && $l_sSearchString == strtolower($l_aResult[0][0]['smtp_address'])){ |
||
| 172 | $recipientList = Array(); |
||
| 173 | } else { |
||
| 174 | /** |
||
| 175 | * Sort lists |
||
| 176 | * |
||
| 177 | * This block of code sorts the two lists and creates one final list. |
||
| 178 | * The first list holds the matches based on whole words or words |
||
| 179 | * beginning with the search string and the second list contains the |
||
| 180 | * partial matches that start in the middle of the words. |
||
| 181 | * The first list is sorted on count (the number of emails sent to this |
||
| 182 | * email address), name and finally on the email address. This is done |
||
| 183 | * by a natural sort. When this first list already contains the maximum |
||
| 184 | * number of returned items the second list needs no sorting. If it has |
||
| 185 | * less, then the second list is sorted and included in the first list |
||
| 186 | * as well. At the end the final list is sorted on name and email again. |
||
| 187 | * |
||
| 188 | */ |
||
| 189 | $l_iMaxNumListItems = 10; |
||
| 190 | $l_aSortedList = Array(); |
||
| 191 | usort($l_aResult[0], Array($this, 'cmpSortResultList')); |
||
| 192 | for($i = 0, $len = min($l_iMaxNumListItems, count($l_aResult[0])); $i < $len; $i++){ |
||
| 193 | $l_aSortedList[] = $l_aResult[0][$i]; |
||
| 194 | } |
||
| 195 | if(count($l_aSortedList) < $l_iMaxNumListItems){ |
||
| 196 | $l_iMaxNumRemainingListItems = $l_iMaxNumListItems - count($l_aSortedList); |
||
| 197 | usort($l_aResult[1], Array($this, 'cmpSortResultList')); |
||
| 198 | for($i = 0, $len = min($l_iMaxNumRemainingListItems, count($l_aResult[1])); $i < $len; $i++){ |
||
| 199 | $l_aSortedList[] = $l_aResult[1][$i]; |
||
| 200 | } |
||
| 201 | } |
||
| 202 | |||
| 203 | $recipientList = Array(); |
||
| 204 | foreach($l_aSortedList as $index => $recipient) { |
||
| 205 | $recipient['id'] = count($recipientList) + 1; |
||
| 206 | $recipientList[] = $recipient; |
||
| 207 | } |
||
| 208 | } |
||
| 209 | |||
| 210 | $data = Array( |
||
| 211 | 'query' => $action["query"], |
||
| 212 | 'results' => $recipientList |
||
| 213 | ); |
||
| 214 | } else { |
||
| 215 | $data = Array( |
||
| 216 | 'query' => $action["query"], |
||
| 217 | 'results' => Array() |
||
| 218 | ); |
||
| 219 | } |
||
| 220 | |||
| 221 | return $data; |
||
| 222 | } |
||
| 223 | } |
||
| 224 | ?> |
||
|
0 ignored issues
–
show
|
|||
| 225 |
Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.
A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.