| Total Complexity | 62 |
| Total Lines | 401 |
| Duplicated Lines | 0 % |
| Changes | 2 | ||
| Bugs | 2 | Features | 0 |
Complex classes like ResolveNamesModule often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use ResolveNamesModule, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 6 | class ResolveNamesModule extends Module { |
||
| 7 | /** |
||
| 8 | * Constructor. |
||
| 9 | * |
||
| 10 | * @param mixed $id |
||
| 11 | * @param mixed $data |
||
| 12 | */ |
||
| 13 | public function __construct($id, $data) { |
||
| 15 | } |
||
| 16 | |||
| 17 | /** |
||
| 18 | * Executes all the actions in the $data variable. |
||
| 19 | */ |
||
| 20 | public function execute() { |
||
| 35 | } |
||
| 36 | } |
||
| 37 | } |
||
| 38 | } |
||
| 39 | |||
| 40 | /** |
||
| 41 | * Function which checks the names, sent by the client. This function is used |
||
| 42 | * when a user wants to sent an email and want to check the names filled in |
||
| 43 | * by the user in the to, cc and bcc field. This function uses the global |
||
| 44 | * user list to check if the names are correct. |
||
| 45 | * |
||
| 46 | * @param array $action the action data, sent by the client |
||
| 47 | */ |
||
| 48 | public function checkNames($action) { |
||
| 49 | if (isset($action['resolverequests'])) { |
||
| 50 | $data = []; |
||
| 51 | $excludeLocalContacts = $action['exclude_local_contacts'] ?? false; |
||
| 52 | $excludeGABGroups = $action['exclude_gab_groups'] ?? false; |
||
| 53 | $resolveRequest = $action['resolverequests']; |
||
| 54 | if (!is_array($resolveRequest)) { |
||
| 55 | $resolveRequest = [$resolveRequest]; |
||
| 56 | } |
||
| 57 | |||
| 58 | // open addressbook |
||
| 59 | // When local contacts need to be excluded we have pass true as the first argument |
||
| 60 | // so we will not have any Contact Providers set on the Addressbook resource. |
||
| 61 | $ab = $GLOBALS['mapisession']->getAddressbook($excludeLocalContacts); |
||
| 62 | |||
| 63 | $ab_dir = mapi_ab_openentry($ab); |
||
| 64 | $resolveResponse = []; |
||
| 65 | |||
| 66 | // check names |
||
| 67 | foreach ($resolveRequest as $query) { |
||
| 68 | if (is_array($query) && isset($query['id'])) { |
||
| 69 | $responseEntry = []; |
||
| 70 | $responseEntry['id'] = $query['id']; |
||
| 71 | |||
| 72 | if (!empty($query['display_name']) || !empty($query['email_address'])) { |
||
| 73 | $responseEntry['result'] = $this->searchAddressBook($ab, $ab_dir, $query, $excludeGABGroups); |
||
| 74 | $resolveResponse[] = $responseEntry; |
||
| 75 | } |
||
| 76 | } |
||
| 77 | } |
||
| 78 | |||
| 79 | $data['resolveresponse'] = $resolveResponse; |
||
| 80 | |||
| 81 | $this->addActionData('checknames', $data); |
||
| 82 | $GLOBALS['bus']->addData($this->getResponseData()); |
||
| 83 | } |
||
| 84 | } |
||
| 85 | |||
| 86 | /** |
||
| 87 | * This function searches the addressbook specified for users and returns an array with data |
||
| 88 | * Please note that the returning array must be UTF8. |
||
| 89 | * |
||
| 90 | * @param resource $ab The addressbook |
||
| 91 | * @param resource $ab_dir The addressbook container |
||
| 92 | * @param string $query The search query, case is ignored |
||
| 93 | * @param bool $excludeGABGroups flag to exclude groups from resolving |
||
| 94 | */ |
||
| 95 | public function searchAddressBook($ab, $ab_dir, $query, $excludeGABGroups) { |
||
| 96 | // Prefer resolving the email_address. This allows the user |
||
| 97 | // to resolve recipients with a display name that matches a EX |
||
| 98 | // user with an alternative (external) email address. |
||
| 99 | $searchstr = empty($query['email_address']) ? $query['display_name'] : $query['email_address']; |
||
| 100 | // If the address_type is 'EX' then we are resolving something which must be found in |
||
| 101 | // the GAB as an exact match. So add the flag EMS_AB_ADDRESS_LOOKUP to ensure we will not |
||
| 102 | // get multiple results when multiple items have a partial match. |
||
| 103 | $flags = $query['address_type'] === 'EX' ? EMS_AB_ADDRESS_LOOKUP : 0; |
||
| 104 | |||
| 105 | try { |
||
| 106 | // First, try an addressbook lookup |
||
| 107 | $rows = mapi_ab_resolvename($ab, [[PR_DISPLAY_NAME => $searchstr]], $flags); |
||
| 108 | $this->searchContactsFolders($ab, $ab_dir, $searchstr, $rows); |
||
| 109 | } |
||
| 110 | catch (MAPIException $e) { |
||
| 111 | if ($e->getCode() == MAPI_E_AMBIGUOUS_RECIP) { |
||
| 112 | $ab_entryid = mapi_ab_getdefaultdir($ab); |
||
| 113 | $ab_dir = mapi_ab_openentry($ab, $ab_entryid); |
||
| 114 | // Ambiguous, show possibilities: |
||
| 115 | $table = mapi_folder_getcontentstable($ab_dir, MAPI_DEFERRED_ERRORS); |
||
| 116 | $restriction = $this->getAmbigiousContactRestriction($searchstr, $excludeGABGroups, PR_ACCOUNT); |
||
| 117 | |||
| 118 | mapi_table_restrict($table, $restriction, TBL_BATCH); |
||
| 119 | mapi_table_sort($table, [PR_DISPLAY_NAME => TABLE_SORT_ASCEND], TBL_BATCH); |
||
| 120 | |||
| 121 | $rows = mapi_table_queryallrows($table, [PR_ACCOUNT, PR_ADDRTYPE, PR_DISPLAY_NAME, PR_ENTRYID, PR_SEARCH_KEY, PR_OBJECT_TYPE, PR_SMTP_ADDRESS, PR_DISPLAY_TYPE_EX, PR_EMAIL_ADDRESS, PR_OBJECT_TYPE, PR_DISPLAY_TYPE]); |
||
| 122 | |||
| 123 | $rows = array_merge($rows, $this->getAmbigiousContactResolveResults($ab, $searchstr, $excludeGABGroups)); |
||
| 124 | } |
||
| 125 | elseif ($e->getCode() == MAPI_E_NOT_FOUND) { |
||
| 126 | $rows = []; |
||
| 127 | if ($query['address_type'] === 'SMTP') { |
||
| 128 | // If we still can't find anything, and we were searching for a SMTP user |
||
| 129 | // we can generate a oneoff entry which contains the information of the user. |
||
| 130 | if (!empty($query['email_address'])) { |
||
| 131 | $rows[] = [ |
||
| 132 | PR_ACCOUNT => $query['email_address'], PR_ADDRTYPE => 'SMTP', PR_EMAIL_ADDRESS => $query['email_address'], |
||
| 133 | PR_DISPLAY_NAME => $query['display_name'], PR_DISPLAY_TYPE_EX => DT_REMOTE_MAILUSER, PR_DISPLAY_TYPE => DT_MAILUSER, |
||
| 134 | PR_SMTP_ADDRESS => $query['email_address'], PR_OBJECT_TYPE => MAPI_MAILUSER, |
||
| 135 | PR_ENTRYID => mapi_createoneoff($query['display_name'], 'SMTP', $query['email_address']), |
||
| 136 | ]; |
||
| 137 | } |
||
| 138 | // Check also the user's contacts folders |
||
| 139 | else { |
||
| 140 | $this->searchContactsFolders($ab, $ab_dir, $searchstr, $rows); |
||
| 141 | } |
||
| 142 | } |
||
| 143 | } |
||
| 144 | else { |
||
| 145 | // all other errors should be propagated to higher level exception handlers |
||
| 146 | throw $e; |
||
| 147 | } |
||
| 148 | } |
||
| 149 | |||
| 150 | $items = []; |
||
| 151 | if ($rows) { |
||
| 152 | foreach ($rows as $user_data) { |
||
| 153 | $item = []; |
||
| 154 | |||
| 155 | if (!isset($user_data[PR_ACCOUNT])) { |
||
| 156 | $abitem = mapi_ab_openentry($ab, $user_data[PR_ENTRYID]); |
||
| 157 | $user_data = mapi_getprops($abitem, [PR_ACCOUNT, PR_ADDRTYPE, PR_DISPLAY_NAME, PR_DISPLAY_TYPE_EX, PR_ENTRYID, PR_SEARCH_KEY, PR_EMAIL_ADDRESS, PR_SMTP_ADDRESS, PR_OBJECT_TYPE, PR_DISPLAY_TYPE]); |
||
| 158 | } |
||
| 159 | |||
| 160 | if ($excludeGABGroups && $user_data[PR_OBJECT_TYPE] === MAPI_DISTLIST) { |
||
| 161 | // exclude groups from result |
||
| 162 | continue; |
||
| 163 | } |
||
| 164 | |||
| 165 | $item = []; |
||
| 166 | $item['object_type'] = isset($user_data[PR_OBJECT_TYPE]) ? $user_data[PR_OBJECT_TYPE] : MAPI_MAILUSER; |
||
| 167 | $item['entryid'] = isset($user_data[PR_ENTRYID]) ? bin2hex($user_data[PR_ENTRYID]) : ''; |
||
| 168 | $item['display_name'] = isset($user_data[PR_DISPLAY_NAME]) ? $user_data[PR_DISPLAY_NAME] : ''; |
||
| 169 | $item['display_type'] = isset($user_data[PR_DISPLAY_TYPE]) ? $user_data[PR_DISPLAY_TYPE] : DT_MAILUSER; |
||
| 170 | |||
| 171 | // Test whether the GUID in the entryid is from the Contact Provider |
||
| 172 | if ($GLOBALS['entryid']->hasContactProviderGUID($item['entryid'])) { |
||
| 173 | // The properties for a Distribution List differs from the other objects |
||
| 174 | if ($item['object_type'] == MAPI_DISTLIST) { |
||
| 175 | $item['address_type'] = 'MAPIPDL'; |
||
| 176 | // The email_address is empty for DistList, using display name for resolving |
||
| 177 | $item['email_address'] = $item['display_name']; |
||
| 178 | $item['smtp_address'] = isset($item['smtp_address']) ? $item['smtp_address'] : ''; |
||
| 179 | } |
||
| 180 | else { |
||
| 181 | $item['address_type'] = 'EX'; |
||
| 182 | if (isset($user_data['address_type']) && $user_data['address_type'] === 'EX') { |
||
| 183 | $item['email_address'] = isset($user_data[PR_EMAIL_ADDRESS]) ? $user_data[PR_EMAIL_ADDRESS] : ''; |
||
| 184 | } |
||
| 185 | else { |
||
| 186 | // Fake being an EX account, since it's actually an SMTP addrtype the email address is in a different property. |
||
| 187 | $item['smtp_address'] = isset($user_data[PR_EMAIL_ADDRESS]) ? $user_data[PR_EMAIL_ADDRESS] : ''; |
||
| 188 | // Keep the old scenario happy. |
||
| 189 | $item['email_address'] = isset($user_data[PR_EMAIL_ADDRESS]) ? $user_data[PR_EMAIL_ADDRESS] : ''; |
||
| 190 | } |
||
| 191 | } |
||
| 192 | // It can be considered a GAB entry |
||
| 193 | } |
||
| 194 | else { |
||
| 195 | $item['user_name'] = isset($user_data[PR_ACCOUNT]) ? $user_data[PR_ACCOUNT] : $item['display_name']; |
||
| 196 | $item['display_type_ex'] = isset($user_data[PR_DISPLAY_TYPE_EX]) ? $user_data[PR_DISPLAY_TYPE_EX] : MAPI_MAILUSER; |
||
| 197 | $item['email_address'] = isset($user_data[PR_EMAIL_ADDRESS]) ? $user_data[PR_EMAIL_ADDRESS] : ''; |
||
| 198 | $item['smtp_address'] = isset($user_data[PR_SMTP_ADDRESS]) ? $user_data[PR_SMTP_ADDRESS] : $item['email_address']; |
||
| 199 | $item['address_type'] = isset($user_data[PR_ADDRTYPE]) ? $user_data[PR_ADDRTYPE] : 'SMTP'; |
||
| 200 | } |
||
| 201 | |||
| 202 | if (isset($user_data[PR_SEARCH_KEY])) { |
||
| 203 | $item['search_key'] = bin2hex($user_data[PR_SEARCH_KEY]); |
||
| 204 | } |
||
| 205 | else { |
||
| 206 | $emailAddress = isset($item['smtp_address']) ? $item['smtp_address'] : $item['email_address']; |
||
| 207 | $item['search_key'] = bin2hex(strtoupper($item['address_type'] . ':' . $emailAddress)) . '00'; |
||
| 208 | } |
||
| 209 | |||
| 210 | array_push($items, $item); |
||
| 211 | } |
||
| 212 | } |
||
| 213 | |||
| 214 | return $items; |
||
| 215 | } |
||
| 216 | |||
| 217 | /** |
||
| 218 | * Used to find multiple entries from the contact folders in the Addressbook when resolving |
||
| 219 | * returned an ambiguous result. It will find the Contact folders in the Addressbook and |
||
| 220 | * apply a restriction to extract the entries. |
||
| 221 | * |
||
| 222 | * @param resource $ab The addressbook |
||
| 223 | * @param string $query The search query, case is ignored |
||
| 224 | * @param bool $excludeGABGroups flag to exclude groups from resolving |
||
| 225 | */ |
||
| 226 | public function getAmbigiousContactResolveResults($ab, $query, $excludeGABGroups) { |
||
| 227 | /* We need to look for the Contact folders at the bottom of the following tree. |
||
| 228 | * |
||
| 229 | * IAddrBook |
||
| 230 | * - Root Container |
||
| 231 | * - HIERARCHY TABLE |
||
| 232 | * - Contacts Folders (Contact Container) |
||
| 233 | * - HIERARCHY TABLE (Contact Container Hierarchy) |
||
| 234 | * - Contact folder 1 |
||
| 235 | * - Contact folder 2 |
||
| 236 | */ |
||
| 237 | |||
| 238 | $rows = []; |
||
| 239 | $contactFolderRestriction = $this->getAmbigiousContactRestriction($query, $excludeGABGroups, PR_EMAIL_ADDRESS); |
||
| 240 | // Open the AB Root Container by not supplying an entryid |
||
| 241 | $abRootContainer = mapi_ab_openentry($ab); |
||
| 242 | |||
| 243 | // Get the 'Contact Folders' |
||
| 244 | $hierarchyTable = mapi_folder_gethierarchytable($abRootContainer, MAPI_DEFERRED_ERRORS); |
||
| 245 | $abHierarchyRows = mapi_table_queryallrows($hierarchyTable, [PR_AB_PROVIDER_ID, PR_ENTRYID]); |
||
| 246 | |||
| 247 | // Look for the 'Contacts Folders' |
||
| 248 | for ($i = 0,$len = count($abHierarchyRows); $i < $len; ++$i) { |
||
| 249 | // Check if the folder matches the Contact Provider GUID |
||
| 250 | if ($abHierarchyRows[$i][PR_AB_PROVIDER_ID] == MUIDZCSAB) { |
||
| 251 | $abContactContainerEntryid = $abHierarchyRows[$i][PR_ENTRYID]; |
||
| 252 | break; |
||
| 253 | } |
||
| 254 | } |
||
| 255 | |||
| 256 | // Next go into the 'Contacts Folders' and look in the hierarchy table for the Contact folders. |
||
| 257 | if ($abContactContainerEntryid) { |
||
|
|
|||
| 258 | // Get the rows from hierarchy table of the 'Contacts Folders' |
||
| 259 | $abContactContainer = mapi_ab_openentry($ab, $abContactContainerEntryid); |
||
| 260 | $abContactContainerHierarchyTable = mapi_folder_gethierarchytable($abContactContainer, MAPI_DEFERRED_ERRORS); |
||
| 261 | $abContactContainerHierarchyRows = mapi_table_queryallrows($abContactContainerHierarchyTable, [PR_DISPLAY_NAME, PR_OBJECT_TYPE, PR_ENTRYID]); |
||
| 262 | |||
| 263 | // Loop through all the contact folders found under the 'Contacts Folders' hierarchy |
||
| 264 | for ($j = 0,$len = count($abContactContainerHierarchyRows); $j < $len; ++$j) { |
||
| 265 | // Open, get contents table, restrict, sort and then merge the result in the list of $rows |
||
| 266 | $abContactFolder = mapi_ab_openentry($ab, $abContactContainerHierarchyRows[$j][PR_ENTRYID]); |
||
| 267 | $abContactFolderTable = mapi_folder_getcontentstable($abContactFolder, MAPI_DEFERRED_ERRORS); |
||
| 268 | |||
| 269 | mapi_table_restrict($abContactFolderTable, $contactFolderRestriction, TBL_BATCH); |
||
| 270 | mapi_table_sort($abContactFolderTable, [PR_DISPLAY_NAME => TABLE_SORT_ASCEND], TBL_BATCH); |
||
| 271 | |||
| 272 | // Go go gadget, merge! |
||
| 273 | $rows = array_merge($rows, mapi_table_queryallrows($abContactFolderTable, [PR_ACCOUNT, PR_DISPLAY_NAME, PR_ENTRYID, PR_OBJECT_TYPE, PR_SMTP_ADDRESS, PR_DISPLAY_TYPE_EX, PR_EMAIL_ADDRESS, PR_OBJECT_TYPE, PR_DISPLAY_TYPE])); |
||
| 274 | } |
||
| 275 | } |
||
| 276 | |||
| 277 | return $rows; |
||
| 278 | } |
||
| 279 | |||
| 280 | /** |
||
| 281 | * Setup the restriction used for resolving in the Contact folders or GAB. |
||
| 282 | * |
||
| 283 | * @param string $query The search query, case is ignored |
||
| 284 | * @param bool $excludeGABGroups flag to exclude groups from resolving |
||
| 285 | * @param int $content the PROPTAG to search in |
||
| 286 | */ |
||
| 287 | public function getAmbigiousContactRestriction($query, $excludeGABGroups, $content) { |
||
| 288 | // only return users from who the displayName or the username starts with $name |
||
| 289 | // TODO: use PR_ANR for this restriction instead of PR_DISPLAY_NAME and $content. |
||
| 290 | $resAnd = [ |
||
| 291 | [RES_OR, |
||
| 292 | [ |
||
| 293 | [RES_CONTENT, |
||
| 294 | [ |
||
| 295 | FUZZYLEVEL => FL_PREFIX | FL_IGNORECASE, |
||
| 296 | ULPROPTAG => PR_DISPLAY_NAME, |
||
| 297 | VALUE => $query, |
||
| 298 | ], |
||
| 299 | ], |
||
| 300 | [RES_CONTENT, |
||
| 301 | [ |
||
| 302 | FUZZYLEVEL => FL_PREFIX | FL_IGNORECASE, |
||
| 303 | ULPROPTAG => $content, |
||
| 304 | VALUE => $query, |
||
| 305 | ], |
||
| 306 | ], |
||
| 307 | ], // RES_OR |
||
| 308 | ], |
||
| 309 | ]; |
||
| 310 | |||
| 311 | // create restrictions based on excludeGABGroups flag |
||
| 312 | if ($excludeGABGroups) { |
||
| 313 | array_push($resAnd, [ |
||
| 314 | RES_PROPERTY, |
||
| 315 | [ |
||
| 316 | RELOP => RELOP_EQ, |
||
| 317 | ULPROPTAG => PR_OBJECT_TYPE, |
||
| 318 | VALUE => MAPI_MAILUSER, |
||
| 319 | ], |
||
| 320 | ]); |
||
| 321 | } |
||
| 322 | else { |
||
| 323 | array_push($resAnd, [RES_OR, |
||
| 324 | [ |
||
| 325 | [RES_PROPERTY, |
||
| 326 | [ |
||
| 327 | RELOP => RELOP_EQ, |
||
| 328 | ULPROPTAG => PR_OBJECT_TYPE, |
||
| 329 | VALUE => MAPI_MAILUSER, |
||
| 330 | ], |
||
| 331 | ], |
||
| 332 | [RES_PROPERTY, |
||
| 333 | [ |
||
| 334 | RELOP => RELOP_EQ, |
||
| 335 | ULPROPTAG => PR_OBJECT_TYPE, |
||
| 336 | VALUE => MAPI_DISTLIST, |
||
| 337 | ], |
||
| 338 | ], |
||
| 339 | ], |
||
| 340 | ]); |
||
| 341 | } |
||
| 342 | |||
| 343 | return [RES_AND, $resAnd]; |
||
| 344 | } |
||
| 345 | |||
| 346 | /** |
||
| 347 | * Function does customization of exception based on module data. |
||
| 348 | * like, here it will generate display message based on actionType |
||
| 349 | * for particular exception. |
||
| 350 | * |
||
| 351 | * @param object $e Exception object |
||
| 352 | * @param string $actionType the action type, sent by the client |
||
| 353 | * @param MAPIobject $store store object of message |
||
| 354 | * @param string $parententryid parent entryid of the message |
||
| 355 | * @param string $entryid entryid of the message |
||
| 356 | * @param array $action the action data, sent by the client |
||
| 357 | */ |
||
| 358 | public function handleException(&$e, $actionType = null, $store = null, $parententryid = null, $entryid = null, $action = null) { |
||
| 359 | if (is_null($e->displayMessage)) { |
||
| 360 | switch ($actionType) { |
||
| 361 | case 'checknames': |
||
| 362 | if ($e->getCode() == MAPI_E_NO_ACCESS) { |
||
| 363 | $e->setDisplayMessage(_('You have insufficient privileges to perform this action.')); |
||
| 364 | } |
||
| 365 | else { |
||
| 366 | $e->setDisplayMessage(_('Could not resolve user.')); |
||
| 367 | } |
||
| 368 | break; |
||
| 369 | } |
||
| 370 | } |
||
| 371 | |||
| 372 | parent::handleException($e, $actionType, $store, $parententryid, $entryid, $action); |
||
| 373 | } |
||
| 374 | |||
| 375 | /** |
||
| 376 | * This function searches the private contact folders for users and returns an array with data. |
||
| 377 | * Please note that the returning array must be UTF8. |
||
| 378 | * |
||
| 379 | * @param resource $ab The addressbook |
||
| 380 | * @param resource $ab_dir The addressbook container |
||
| 381 | * @param string $searchstr The search query, case is ignored |
||
| 382 | * @param array $rows Array of the found contacts |
||
| 383 | */ |
||
| 384 | public function searchContactsFolders($ab, $ab_dir, $searchstr, &$rows) { |
||
| 407 | } |
||
| 408 | } |
||
| 409 | } |
||
| 410 | } |
||
| 411 | } |
||
| 412 |