Test Failed
Push — master ( 647c72...cd42b5 )
by
unknown
10:25
created

modules/class.suggestemailaddressmodule.php (1 issue)

Severity
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
It is not recommended to use PHP's closing tag ?> in files other than templates.

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.

Loading history...
225