| Total Complexity | 572 | 
| Total Lines | 3090 | 
| Duplicated Lines | 0 % | 
| Changes | 6 | ||
| Bugs | 0 | Features | 0 | 
Complex classes like Profile 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 Profile, and based on these observations, apply Extract Interface, too.
| 1 | <?php | ||
| 27 | { | ||
| 28 | global $txt, $scripturl, $user_info, $context, $sourcedir, $user_profile, $cur_profile; | ||
| 29 | global $modSettings, $memberContext, $profile_vars, $post_errors, $smcFunc; | ||
| 30 | |||
| 31 | // Don't reload this as we may have processed error strings. | ||
| 32 | if (empty($post_errors)) | ||
| 33 | 		loadLanguage('Profile+Drafts'); | ||
| 34 | 	loadTemplate('Profile'); | ||
| 35 | |||
| 36 | require_once($sourcedir . '/Subs-Menu.php'); | ||
| 37 | |||
| 38 | // Did we get the user by name... | ||
| 39 | if (isset($_REQUEST['user'])) | ||
| 40 | $memberResult = loadMemberData($_REQUEST['user'], true, 'profile'); | ||
| 41 | // ... or by id_member? | ||
| 42 | elseif (!empty($_REQUEST['u'])) | ||
| 43 | $memberResult = loadMemberData((int) $_REQUEST['u'], false, 'profile'); | ||
| 44 | // If it was just ?action=profile, edit your own profile, but only if you're not a guest. | ||
| 45 | else | ||
| 46 | 	{ | ||
| 47 | // Members only... | ||
| 48 | is_not_guest(); | ||
| 49 | $memberResult = loadMemberData($user_info['id'], false, 'profile'); | ||
| 50 | } | ||
| 51 | |||
| 52 | // Check if loadMemberData() has returned a valid result. | ||
| 53 | if (!$memberResult) | ||
| 54 | 		fatal_lang_error('not_a_user', false, 404); | ||
| 55 | |||
| 56 | // If all went well, we have a valid member ID! | ||
| 57 | list ($memID) = $memberResult; | ||
| 58 | $memID = (int) $memID; | ||
| 59 | $context['id_member'] = $memID; | ||
| 60 | $cur_profile = $user_profile[$memID]; | ||
| 61 | |||
| 62 | // Let's have some information about this member ready, too. | ||
| 63 | loadMemberContext($memID); | ||
| 64 | $context['member'] = $memberContext[$memID]; | ||
| 65 | |||
| 66 | // Is this the profile of the user himself or herself? | ||
| 67 | $context['user']['is_owner'] = $memID == $user_info['id']; | ||
| 68 | |||
| 69 | // Group management isn't actually a permission. But we need it to be for this, so we need a phantom permission. | ||
| 70 | // And we care about what the current user can do, not what the user whose profile it is. | ||
| 71 | if ($user_info['mod_cache']['gq'] != '0=1') | ||
| 72 | $user_info['permissions'][] = 'approve_group_requests'; | ||
| 73 | |||
| 74 | // If paid subscriptions are enabled, make sure we actually have at least one subscription available... | ||
| 75 | $context['subs_available'] = false; | ||
| 76 | |||
| 77 | if (!empty($modSettings['paid_enabled'])) | ||
| 78 | 	{ | ||
| 79 | 		$get_active_subs = $smcFunc['db_query']('', ' | ||
| 80 | SELECT COUNT(*) | ||
| 81 | 			FROM {db_prefix}subscriptions | ||
| 82 | 			WHERE active = {int:active}', array( | ||
| 83 | 'active' => 1, | ||
| 84 | ) | ||
| 85 | ); | ||
| 86 | |||
| 87 | list ($num_subs) = $smcFunc['db_fetch_row']($get_active_subs); | ||
| 88 | |||
| 89 | $context['subs_available'] = ($num_subs > 0); | ||
| 90 | |||
| 91 | $smcFunc['db_free_result']($get_active_subs); | ||
| 92 | } | ||
| 93 | |||
| 94 | /* Define all the sections within the profile area! | ||
| 95 | We start by defining the permission required - then SMF takes this and turns it into the relevant context ;) | ||
| 96 | Possible fields: | ||
| 97 | For Section: | ||
| 98 | string $title: Section title. | ||
| 99 | array $areas: Array of areas within this section. | ||
| 100 | |||
| 101 | For Areas: | ||
| 102 | string $label: Text string that will be used to show the area in the menu. | ||
| 103 | string $file: Optional text string that may contain a file name that's needed for inclusion in order to display the area properly. | ||
| 104 | string $custom_url: Optional href for area. | ||
| 105 | string $function: Function to execute for this section. Can be a call to an static method: class::method | ||
| 106 | string $class If your function is a method, set the class field with your class's name and SMF will create a new instance for it. | ||
| 107 | bool $enabled: Should area be shown? | ||
| 108 | string $sc: Session check validation to do on save - note without this save will get unset - if set. | ||
| 109 | bool $hidden: Does this not actually appear on the menu? | ||
| 110 | bool $password: Whether to require the user's password in order to save the data in the area. | ||
| 111 | array $subsections: Array of subsections, in order of appearance. | ||
| 112 | array $permission: Array of permissions to determine who can access this area. Should contain arrays $own and $any. | ||
| 113 | */ | ||
| 114 | $profile_areas = array( | ||
| 115 | 'info' => array( | ||
| 116 | 'title' => $txt['profileInfo'], | ||
| 117 | 'areas' => array( | ||
| 118 | 'summary' => array( | ||
| 119 | 'label' => $txt['summary'], | ||
| 120 | 'file' => 'Profile-View.php', | ||
| 121 | 'function' => 'summary', | ||
| 122 | 'icon' => 'administration', | ||
| 123 | 'permission' => array( | ||
| 124 | 'own' => 'is_not_guest', | ||
| 125 | 'any' => 'profile_view', | ||
| 126 | ), | ||
| 127 | ), | ||
| 128 | 'popup' => array( | ||
| 129 | 'function' => 'profile_popup', | ||
| 130 | 'permission' => array( | ||
| 131 | 'own' => 'is_not_guest', | ||
| 132 | 'any' => array(), | ||
| 133 | ), | ||
| 134 | 'select' => 'summary', | ||
| 135 | ), | ||
| 136 | 'alerts_popup' => array( | ||
| 137 | 'function' => 'alerts_popup', | ||
| 138 | 'permission' => array( | ||
| 139 | 'own' => 'is_not_guest', | ||
| 140 | 'any' => array(), | ||
| 141 | ), | ||
| 142 | 'select' => 'summary', | ||
| 143 | ), | ||
| 144 | 'statistics' => array( | ||
| 145 | 'label' => $txt['statPanel'], | ||
| 146 | 'file' => 'Profile-View.php', | ||
| 147 | 'function' => 'statPanel', | ||
| 148 | 'icon' => 'stats', | ||
| 149 | 'permission' => array( | ||
| 150 | 'own' => 'is_not_guest', | ||
| 151 | 'any' => 'profile_view', | ||
| 152 | ), | ||
| 153 | ), | ||
| 154 | 'showposts' => array( | ||
| 155 | 'label' => $txt['showPosts'], | ||
| 156 | 'file' => 'Profile-View.php', | ||
| 157 | 'function' => 'showPosts', | ||
| 158 | 'icon' => 'posts', | ||
| 159 | 'subsections' => array( | ||
| 160 | 						'messages' => array($txt['showMessages'], array('is_not_guest', 'profile_view')), | ||
| 161 | 						'topics' => array($txt['showTopics'], array('is_not_guest', 'profile_view')), | ||
| 162 | 						'unwatchedtopics' => array($txt['showUnwatched'], array('is_not_guest', 'profile_view'), 'enabled' => $context['user']['is_owner']), | ||
| 163 | 						'attach' => array($txt['showAttachments'], array('is_not_guest', 'profile_view')), | ||
| 164 | ), | ||
| 165 | 'permission' => array( | ||
| 166 | 'own' => 'is_not_guest', | ||
| 167 | 'any' => 'profile_view', | ||
| 168 | ), | ||
| 169 | ), | ||
| 170 | 'showdrafts' => array( | ||
| 171 | 'label' => $txt['drafts_show'], | ||
| 172 | 'file' => 'Drafts.php', | ||
| 173 | 'function' => 'showProfileDrafts', | ||
| 174 | 'icon' => 'drafts', | ||
| 175 | 'enabled' => !empty($modSettings['drafts_post_enabled']) && $context['user']['is_owner'], | ||
| 176 | 'permission' => array( | ||
| 177 | 'own' => 'is_not_guest', | ||
| 178 | 'any' => array(), | ||
| 179 | ), | ||
| 180 | ), | ||
| 181 | 'showalerts' => array( | ||
| 182 | 'label' => $txt['alerts_show'], | ||
| 183 | 'file' => 'Profile-View.php', | ||
| 184 | 'function' => 'showAlerts', | ||
| 185 | 'icon' => 'alerts', | ||
| 186 | 'permission' => array( | ||
| 187 | 'own' => 'is_not_guest', | ||
| 188 | 'any' => array(), | ||
| 189 | ), | ||
| 190 | ), | ||
| 191 | 'permissions' => array( | ||
| 192 | 'label' => $txt['showPermissions'], | ||
| 193 | 'file' => 'Profile-View.php', | ||
| 194 | 'function' => 'showPermissions', | ||
| 195 | 'icon' => 'permissions', | ||
| 196 | 'permission' => array( | ||
| 197 | 'own' => 'manage_permissions', | ||
| 198 | 'any' => 'manage_permissions', | ||
| 199 | ), | ||
| 200 | ), | ||
| 201 | 'tracking' => array( | ||
| 202 | 'label' => $txt['trackUser'], | ||
| 203 | 'file' => 'Profile-View.php', | ||
| 204 | 'function' => 'tracking', | ||
| 205 | 'icon' => 'logs', | ||
| 206 | 'subsections' => array( | ||
| 207 | 'activity' => array($txt['trackActivity'], 'moderate_forum'), | ||
| 208 | 'ip' => array($txt['trackIP'], 'moderate_forum'), | ||
| 209 | 'edits' => array($txt['trackEdits'], 'moderate_forum', 'enabled' => !empty($modSettings['userlog_enabled'])), | ||
| 210 | 'groupreq' => array($txt['trackGroupRequests'], 'approve_group_requests', 'enabled' => !empty($modSettings['show_group_membership'])), | ||
| 211 | 'logins' => array($txt['trackLogins'], 'moderate_forum', 'enabled' => !empty($modSettings['loginHistoryDays'])), | ||
| 212 | ), | ||
| 213 | 'permission' => array( | ||
| 214 | 						'own' => array('moderate_forum', 'approve_group_requests'), | ||
| 215 | 						'any' => array('moderate_forum', 'approve_group_requests'), | ||
| 216 | ), | ||
| 217 | ), | ||
| 218 | 'viewwarning' => array( | ||
| 219 | 'label' => $txt['profile_view_warnings'], | ||
| 220 | 'enabled' => $modSettings['warning_settings'][0] == 1 && $cur_profile['warning'], | ||
| 221 | 'file' => 'Profile-View.php', | ||
| 222 | 'function' => 'viewWarning', | ||
| 223 | 'icon' => 'warning', | ||
| 224 | 'permission' => array( | ||
| 225 | 						'own' => array('view_warning_own', 'view_warning_any', 'issue_warning', 'moderate_forum'), | ||
| 226 | 						'any' => array('view_warning_any', 'issue_warning', 'moderate_forum'), | ||
| 227 | ), | ||
| 228 | ), | ||
| 229 | ), | ||
| 230 | ), | ||
| 231 | 'edit_profile' => array( | ||
| 232 | 'title' => $txt['forumprofile'], | ||
| 233 | 'areas' => array( | ||
| 234 | 'account' => array( | ||
| 235 | 'label' => $txt['account'], | ||
| 236 | 'file' => 'Profile-Modify.php', | ||
| 237 | 'function' => 'account', | ||
| 238 | 'icon' => 'maintain', | ||
| 239 | 					'enabled' => $context['user']['is_admin'] || ($cur_profile['id_group'] != 1 && !in_array(1, explode(',', $cur_profile['additional_groups']))), | ||
| 240 | 'sc' => 'post', | ||
| 241 | 'token' => 'profile-ac%u', | ||
| 242 | 'password' => true, | ||
| 243 | 'permission' => array( | ||
| 244 | 						'own' => array('profile_identity_any', 'profile_identity_own', 'profile_password_any', 'profile_password_own', 'manage_membergroups'), | ||
| 245 | 						'any' => array('profile_identity_any', 'profile_password_any', 'manage_membergroups'), | ||
| 246 | ), | ||
| 247 | ), | ||
| 248 | 'tfasetup' => array( | ||
| 249 | 'label' => $txt['account'], | ||
| 250 | 'file' => 'Profile-Modify.php', | ||
| 251 | 'function' => 'tfasetup', | ||
| 252 | 'token' => 'profile-tfa%u', | ||
| 253 | 'enabled' => !empty($modSettings['tfa_mode']), | ||
| 254 | 'hidden' => true, | ||
| 255 | 'select' => 'account', | ||
| 256 | 'permission' => array( | ||
| 257 | 						'own' => array('profile_password_own'), | ||
| 258 | 						'any' => array('profile_password_any'), | ||
| 259 | ), | ||
| 260 | ), | ||
| 261 | 'tfadisable' => array( | ||
| 262 | 'label' => $txt['account'], | ||
| 263 | 'file' => 'Profile-Modify.php', | ||
| 264 | 'function' => 'tfadisable', | ||
| 265 | 'token' => 'profile-tfa%u', | ||
| 266 | 'sc' => 'post', | ||
| 267 | 'password' => true, | ||
| 268 | 'enabled' => !empty($modSettings['tfa_mode']), | ||
| 269 | 'hidden' => true, | ||
| 270 | 'select' => 'account', | ||
| 271 | 'permission' => array( | ||
| 272 | 						'own' => array('profile_password_own'), | ||
| 273 | 						'any' => array('profile_password_any'), | ||
| 274 | ), | ||
| 275 | ), | ||
| 276 | 'forumprofile' => array( | ||
| 277 | 'label' => $txt['forumprofile'], | ||
| 278 | 'file' => 'Profile-Modify.php', | ||
| 279 | 'function' => 'forumProfile', | ||
| 280 | 'icon' => 'members', | ||
| 281 | 'sc' => 'post', | ||
| 282 | 'token' => 'profile-fp%u', | ||
| 283 | 'permission' => array( | ||
| 284 | 						'own' => array('profile_forum_any', 'profile_forum_own'), | ||
| 285 | 						'any' => array('profile_forum_any'), | ||
| 286 | ), | ||
| 287 | ), | ||
| 288 | 'theme' => array( | ||
| 289 | 'label' => $txt['theme'], | ||
| 290 | 'file' => 'Profile-Modify.php', | ||
| 291 | 'function' => 'theme', | ||
| 292 | 'icon' => 'features', | ||
| 293 | 'sc' => 'post', | ||
| 294 | 'token' => 'profile-th%u', | ||
| 295 | 'permission' => array( | ||
| 296 | 						'own' => array('profile_extra_any', 'profile_extra_own'), | ||
| 297 | 						'any' => array('profile_extra_any'), | ||
| 298 | ), | ||
| 299 | ), | ||
| 300 | 'notification' => array( | ||
| 301 | 'label' => $txt['notification'], | ||
| 302 | 'file' => 'Profile-Modify.php', | ||
| 303 | 'function' => 'notification', | ||
| 304 | 'icon' => 'alerts', | ||
| 305 | 'sc' => 'post', | ||
| 306 | //'token' => 'profile-nt%u', This is not checked here. We do it in the function itself - but if it was checked, this is what it'd be. | ||
| 307 | 'subsections' => array( | ||
| 308 | 						'alerts' => array($txt['alert_prefs'], array('is_not_guest', 'profile_extra_any')), | ||
| 309 | 						'topics' => array($txt['watched_topics'], array('is_not_guest', 'profile_extra_any')), | ||
| 310 | 						'boards' => array($txt['watched_boards'], array('is_not_guest', 'profile_extra_any')), | ||
| 311 | ), | ||
| 312 | 'permission' => array( | ||
| 313 | 						'own' => array('is_not_guest'), | ||
| 314 | 						'any' => array('profile_extra_any'), // If you change this, update it in the functions themselves; we delegate all saving checks there. | ||
| 315 | ), | ||
| 316 | ), | ||
| 317 | 'ignoreboards' => array( | ||
| 318 | 'label' => $txt['ignoreboards'], | ||
| 319 | 'file' => 'Profile-Modify.php', | ||
| 320 | 'function' => 'ignoreboards', | ||
| 321 | 'icon' => 'boards', | ||
| 322 | 'enabled' => !empty($modSettings['allow_ignore_boards']), | ||
| 323 | 'sc' => 'post', | ||
| 324 | 'token' => 'profile-ib%u', | ||
| 325 | 'permission' => array( | ||
| 326 | 						'own' => array('profile_extra_any', 'profile_extra_own'), | ||
| 327 | 						'any' => array('profile_extra_any'), | ||
| 328 | ), | ||
| 329 | ), | ||
| 330 | 'lists' => array( | ||
| 331 | 'label' => $txt['editBuddyIgnoreLists'], | ||
| 332 | 'file' => 'Profile-Modify.php', | ||
| 333 | 'function' => 'editBuddyIgnoreLists', | ||
| 334 | 'icon' => 'frenemy', | ||
| 335 | 'enabled' => !empty($modSettings['enable_buddylist']) && $context['user']['is_owner'], | ||
| 336 | 'sc' => 'post', | ||
| 337 | 'subsections' => array( | ||
| 338 | 'buddies' => array($txt['editBuddies']), | ||
| 339 | 'ignore' => array($txt['editIgnoreList']), | ||
| 340 | ), | ||
| 341 | 'permission' => array( | ||
| 342 | 						'own' => array('profile_extra_any', 'profile_extra_own'), | ||
| 343 | 'any' => array(), | ||
| 344 | ), | ||
| 345 | ), | ||
| 346 | 'groupmembership' => array( | ||
| 347 | 'label' => $txt['groupmembership'], | ||
| 348 | 'file' => 'Profile-Modify.php', | ||
| 349 | 'function' => 'groupMembership', | ||
| 350 | 'icon' => 'people', | ||
| 351 | 'enabled' => !empty($modSettings['show_group_membership']) && $context['user']['is_owner'], | ||
| 352 | 'sc' => 'request', | ||
| 353 | 'token' => 'profile-gm%u', | ||
| 354 | 'token_type' => 'request', | ||
| 355 | 'permission' => array( | ||
| 356 | 						'own' => array('is_not_guest'), | ||
| 357 | 						'any' => array('manage_membergroups'), | ||
| 358 | ), | ||
| 359 | ), | ||
| 360 | ), | ||
| 361 | ), | ||
| 362 | 'profile_action' => array( | ||
| 363 | 'title' => $txt['profileAction'], | ||
| 364 | 'areas' => array( | ||
| 365 | 'sendpm' => array( | ||
| 366 | 'label' => $txt['profileSendIm'], | ||
| 367 | 'custom_url' => $scripturl . '?action=pm;sa=send', | ||
| 368 | 'icon' => 'personal_message', | ||
| 369 | 					'enabled' => allowedTo('profile_view'), | ||
| 370 | 'permission' => array( | ||
| 371 | 'own' => array(), | ||
| 372 | 						'any' => array('pm_send'), | ||
| 373 | ), | ||
| 374 | ), | ||
| 375 | 'report' => array( | ||
| 376 | 'label' => $txt['report_profile'], | ||
| 377 | 'custom_url' => $scripturl . '?action=reporttm;' . $context['session_var'] . '=' . $context['session_id'], | ||
| 378 | 'icon' => 'warning', | ||
| 379 | 					'enabled' => allowedTo('profile_view'), | ||
| 380 | 'permission' => array( | ||
| 381 | 'own' => array(), | ||
| 382 | 						'any' => array('report_user'), | ||
| 383 | ), | ||
| 384 | ), | ||
| 385 | 'issuewarning' => array( | ||
| 386 | 'label' => $txt['profile_issue_warning'], | ||
| 387 | 'enabled' => $modSettings['warning_settings'][0] == 1, | ||
| 388 | 'file' => 'Profile-Actions.php', | ||
| 389 | 'function' => 'issueWarning', | ||
| 390 | 'icon' => 'warning', | ||
| 391 | 'token' => 'profile-iw%u', | ||
| 392 | 'permission' => array( | ||
| 393 | 'own' => array(), | ||
| 394 | 						'any' => array('issue_warning'), | ||
| 395 | ), | ||
| 396 | ), | ||
| 397 | 'banuser' => array( | ||
| 398 | 'label' => $txt['profileBanUser'], | ||
| 399 | 'custom_url' => $scripturl . '?action=admin;area=ban;sa=add', | ||
| 400 | 'icon' => 'ban', | ||
| 401 | 					'enabled' => $cur_profile['id_group'] != 1 && !in_array(1, explode(',', $cur_profile['additional_groups'])), | ||
| 402 | 'permission' => array( | ||
| 403 | 'own' => array(), | ||
| 404 | 						'any' => array('manage_bans'), | ||
| 405 | ), | ||
| 406 | ), | ||
| 407 | 'subscriptions' => array( | ||
| 408 | 'label' => $txt['subscriptions'], | ||
| 409 | 'file' => 'Profile-Actions.php', | ||
| 410 | 'function' => 'subscriptions', | ||
| 411 | 'icon' => 'paid', | ||
| 412 | 'enabled' => !empty($modSettings['paid_enabled']) && $context['subs_available'], | ||
| 413 | 'permission' => array( | ||
| 414 | 						'own' => array('is_not_guest'), | ||
| 415 | 						'any' => array('moderate_forum'), | ||
| 416 | ), | ||
| 417 | ), | ||
| 418 | 'getprofiledata' => array( | ||
| 419 | 'label' => $txt['export_profile_data'], | ||
| 420 | 'file' => 'Profile-Export.php', | ||
| 421 | 'function' => 'export_profile_data', | ||
| 422 | 'icon' => 'packages', | ||
| 423 | // 'token' => 'profile-ex%u', // This is not checked here. We do it in the function itself - but if it was checked, this is what it'd be. | ||
| 424 | 'permission' => array( | ||
| 425 | 						'own' => array('profile_view_own'), | ||
| 426 | 						'any' => array('moderate_forum'), | ||
| 427 | ), | ||
| 428 | ), | ||
| 429 | 'download' => array( | ||
| 430 | 'label' => $txt['export_profile_data'], | ||
| 431 | 'file' => 'Profile-Export.php', | ||
| 432 | 'function' => 'download_export_file', | ||
| 433 | 'icon' => 'packages', | ||
| 434 | 'hidden' => true, | ||
| 435 | 'select' => 'getprofiledata', | ||
| 436 | 'permission' => array( | ||
| 437 | 						'own' => array('profile_view_own'), | ||
| 438 | 						'any' => array('moderate_forum'), | ||
| 439 | ), | ||
| 440 | ), | ||
| 441 | 'dlattach' => array( | ||
| 442 | 'label' => $txt['export_profile_data'], | ||
| 443 | 'file' => 'Profile-Export.php', | ||
| 444 | 'function' => 'export_attachment', | ||
| 445 | 'icon' => 'packages', | ||
| 446 | 'hidden' => true, | ||
| 447 | 'select' => 'getprofiledata', | ||
| 448 | 'permission' => array( | ||
| 449 | 						'own' => array('profile_view_own'), | ||
| 450 | 'any' => array(), | ||
| 451 | ), | ||
| 452 | ), | ||
| 453 | 'deleteaccount' => array( | ||
| 454 | 'label' => $txt['deleteAccount'], | ||
| 455 | 'file' => 'Profile-Actions.php', | ||
| 456 | 'function' => 'deleteAccount', | ||
| 457 | 'icon' => 'members_delete', | ||
| 458 | 'sc' => 'post', | ||
| 459 | 'token' => 'profile-da%u', | ||
| 460 | 'password' => true, | ||
| 461 | 'permission' => array( | ||
| 462 | 						'own' => array('profile_remove_any', 'profile_remove_own'), | ||
| 463 | 						'any' => array('profile_remove_any'), | ||
| 464 | ), | ||
| 465 | ), | ||
| 466 | 'activateaccount' => array( | ||
| 467 | 'file' => 'Profile-Actions.php', | ||
| 468 | 'function' => 'activateAccount', | ||
| 469 | 'icon' => 'regcenter', | ||
| 470 | 'sc' => 'get', | ||
| 471 | 'token' => 'profile-aa%u', | ||
| 472 | 'token_type' => 'get', | ||
| 473 | 'permission' => array( | ||
| 474 | 'own' => array(), | ||
| 475 | 						'any' => array('moderate_forum'), | ||
| 476 | ), | ||
| 477 | ), | ||
| 478 | // A logout link just for the popup menu. | ||
| 479 | 'logout' => array( | ||
| 480 | 'label' => $txt['logout'], | ||
| 481 | 'custom_url' => $scripturl . '?action=logout;%1$s=%2$s', | ||
| 482 | 'icon' => 'logout', | ||
| 483 | 'enabled' => !empty($_REQUEST['area']) && $_REQUEST['area'] === 'popup', | ||
| 484 | 'permission' => array( | ||
| 485 | 						'own' => array('is_not_guest'), | ||
| 486 | 'any' => array(), | ||
| 487 | ), | ||
| 488 | ), | ||
| 489 | ), | ||
| 490 | ), | ||
| 491 | ); | ||
| 492 | |||
| 493 | // Let them modify profile areas easily. | ||
| 494 | 	call_integration_hook('integrate_profile_areas', array(&$profile_areas)); | ||
| 495 | |||
| 496 | // Deprecated since 2.1.4 and will be removed in 3.0.0. Kept for compatibility with early versions of 2.1. | ||
| 497 | // @todo add runtime warnings. | ||
| 498 | 	call_integration_hook('integrate_pre_profile_areas', array(&$profile_areas)); | ||
| 499 | |||
| 500 | // Do some cleaning ready for the menu function. | ||
| 501 | $context['password_areas'] = array(); | ||
| 502 | $current_area = isset($_REQUEST['area']) ? $_REQUEST['area'] : ''; | ||
| 503 | |||
| 504 | foreach ($profile_areas as $section_id => $section) | ||
| 505 | 	{ | ||
| 506 | // Do a bit of spring cleaning so to speak. | ||
| 507 | foreach ($section['areas'] as $area_id => $area) | ||
| 508 | 		{ | ||
| 509 | // If it said no permissions that meant it wasn't valid! | ||
| 510 | if (empty($area['permission'][$context['user']['is_owner'] ? 'own' : 'any'])) | ||
| 511 | $profile_areas[$section_id]['areas'][$area_id]['enabled'] = false; | ||
| 512 | // Otherwise pick the right set. | ||
| 513 | else | ||
| 514 | $profile_areas[$section_id]['areas'][$area_id]['permission'] = $area['permission'][$context['user']['is_owner'] ? 'own' : 'any']; | ||
| 515 | |||
| 516 | // Password required in most cases | ||
| 517 | if (!empty($area['password'])) | ||
| 518 | $context['password_areas'][] = $area_id; | ||
| 519 | } | ||
| 520 | } | ||
| 521 | |||
| 522 | // Is there an updated message to show? | ||
| 523 | if (isset($_GET['updated'])) | ||
| 524 | $context['profile_updated'] = $txt['profile_updated_own']; | ||
| 525 | |||
| 526 | // Set a few options for the menu. | ||
| 527 | $menuOptions = array( | ||
| 528 | 'disable_hook_call' => true, | ||
| 529 | 'disable_url_session_check' => true, | ||
| 530 | 'current_area' => $current_area, | ||
| 531 | 'extra_url_parameters' => array( | ||
| 532 | 'u' => $context['id_member'], | ||
| 533 | ), | ||
| 534 | ); | ||
| 535 | |||
| 536 | // Logging out requires the session id in the url. | ||
| 537 | $profile_areas['profile_action']['areas']['logout']['custom_url'] = sprintf($profile_areas['profile_action']['areas']['logout']['custom_url'], $context['session_var'], $context['session_id']); | ||
| 538 | |||
| 539 | // Actually create the menu! | ||
| 540 | $profile_include_data = createMenu($profile_areas, $menuOptions); | ||
| 541 | |||
| 542 | // No menu means no access. | ||
| 543 | if (!$profile_include_data && (!$user_info['is_guest'] || validateSession())) | ||
| 544 | 		fatal_lang_error('no_access', false); | ||
| 545 | |||
| 546 | // Make a note of the Unique ID for this menu. | ||
| 547 | $context['profile_menu_id'] = $context['max_menu_id']; | ||
| 548 | $context['profile_menu_name'] = 'menu_data_' . $context['profile_menu_id']; | ||
| 549 | |||
| 550 | // Set the selected item - now it's been validated. | ||
| 551 | $current_area = $profile_include_data['current_area']; | ||
| 552 | $current_sa = $profile_include_data['current_subsection']; | ||
| 553 | $context['menu_item_selected'] = $current_area; | ||
| 554 | |||
| 555 | // Before we go any further, let's work on the area we've said is valid. Note this is done here just in case we ever compromise the menu function in error! | ||
| 556 | $context['completed_save'] = false; | ||
| 557 | $context['do_preview'] = isset($_REQUEST['preview_signature']); | ||
| 558 | |||
| 559 | $security_checks = array(); | ||
| 560 | $found_area = false; | ||
| 561 | foreach ($profile_areas as $section_id => $section) | ||
| 562 | 	{ | ||
| 563 | // Do a bit of spring cleaning so to speak. | ||
| 564 | foreach ($section['areas'] as $area_id => $area) | ||
| 565 | 		{ | ||
| 566 | // Is this our area? | ||
| 567 | if ($current_area == $area_id) | ||
| 568 | 			{ | ||
| 569 | // This can't happen - but is a security check. | ||
| 570 | if ((isset($section['enabled']) && $section['enabled'] == false) || (isset($area['enabled']) && $area['enabled'] == false)) | ||
| 571 | 					fatal_lang_error('no_access', false); | ||
| 572 | |||
| 573 | // Are we saving data in a valid area? | ||
| 574 | if (isset($area['sc']) && (isset($_REQUEST['save']) || $context['do_preview'])) | ||
| 575 | 				{ | ||
| 576 | $security_checks['session'] = $area['sc']; | ||
| 577 | $context['completed_save'] = true; | ||
| 578 | } | ||
| 579 | |||
| 580 | // Do we need to perform a token check? | ||
| 581 | if (!empty($area['token'])) | ||
| 582 | 				{ | ||
| 583 | $security_checks[isset($_REQUEST['save']) ? 'validateToken' : 'needsToken'] = $area['token']; | ||
| 584 | 					$token_name = $area['token'] !== true ? str_replace('%u', $context['id_member'], $area['token']) : 'profile-u' . $context['id_member']; | ||
| 585 | |||
| 586 | 					$token_type = isset($area['token_type']) && in_array($area['token_type'], array('request', 'post', 'get')) ? $area['token_type'] : 'post'; | ||
| 587 | } | ||
| 588 | |||
| 589 | // Does this require session validating? | ||
| 590 | if (!empty($area['validate']) || (isset($_REQUEST['save']) && !$context['user']['is_owner'] && ($area_id != 'issuewarning' || empty($modSettings['securityDisable_moderate'])))) | ||
| 591 | $security_checks['validate'] = true; | ||
| 592 | |||
| 593 | // Permissions for good measure. | ||
| 594 | if (!empty($profile_include_data['permission'])) | ||
| 595 | $security_checks['permission'] = $profile_include_data['permission']; | ||
| 596 | |||
| 597 | // Either way got something. | ||
| 598 | $found_area = true; | ||
| 599 | } | ||
| 600 | } | ||
| 601 | } | ||
| 602 | |||
| 603 | // Oh dear, some serious security lapse is going on here... we'll put a stop to that! | ||
| 604 | if (!$found_area) | ||
| 605 | 		fatal_lang_error('no_access', false); | ||
| 606 | |||
| 607 | // Release this now. | ||
| 608 | unset($profile_areas); | ||
| 609 | |||
| 610 | // Now the context is setup have we got any security checks to carry out additional to that above? | ||
| 611 | if (isset($security_checks['session'])) | ||
| 612 | checkSession($security_checks['session']); | ||
| 613 | if (isset($security_checks['validate'])) | ||
| 614 | validateSession(); | ||
| 615 | if (isset($security_checks['validateToken'])) | ||
| 616 | validateToken($token_name, $token_type); | ||
| 617 | if (isset($security_checks['permission'])) | ||
| 618 | isAllowedTo($security_checks['permission']); | ||
| 619 | |||
| 620 | // Create a token if needed. | ||
| 621 | if (isset($security_checks['needsToken']) || isset($security_checks['validateToken'])) | ||
| 622 | 	{ | ||
| 623 | createToken($token_name, $token_type); | ||
| 624 | $context['token_check'] = $token_name; | ||
| 625 | } | ||
| 626 | |||
| 627 | // File to include? | ||
| 628 | if (isset($profile_include_data['file'])) | ||
| 629 | require_once($sourcedir . '/' . $profile_include_data['file']); | ||
| 630 | |||
| 631 | // Build the link tree. | ||
| 632 | $context['linktree'][] = array( | ||
| 633 | 'url' => $scripturl . '?action=profile' . ($memID != $user_info['id'] ? ';u=' . $memID : ''), | ||
| 634 | 'name' => sprintf($txt['profile_of_username'], $context['member']['name']), | ||
| 635 | ); | ||
| 636 | |||
| 637 | if (!empty($profile_include_data['label'])) | ||
| 638 | $context['linktree'][] = array( | ||
| 639 | 'url' => $scripturl . '?action=profile' . ($memID != $user_info['id'] ? ';u=' . $memID : '') . ';area=' . $profile_include_data['current_area'], | ||
| 640 | 'name' => $profile_include_data['label'], | ||
| 641 | ); | ||
| 642 | |||
| 643 | if (!empty($profile_include_data['current_subsection']) && $profile_include_data['subsections'][$profile_include_data['current_subsection']][0] != $profile_include_data['label']) | ||
| 644 | $context['linktree'][] = array( | ||
| 645 | 'url' => $scripturl . '?action=profile' . ($memID != $user_info['id'] ? ';u=' . $memID : '') . ';area=' . $profile_include_data['current_area'] . ';sa=' . $profile_include_data['current_subsection'], | ||
| 646 | 'name' => $profile_include_data['subsections'][$profile_include_data['current_subsection']][0], | ||
| 647 | ); | ||
| 648 | |||
| 649 | // Set the template for this area and add the profile layer. | ||
| 650 | $context['sub_template'] = $profile_include_data['function']; | ||
| 651 | $context['template_layers'][] = 'profile'; | ||
| 652 | |||
| 653 | // All the subactions that require a user password in order to validate. | ||
| 654 | $check_password = $context['user']['is_owner'] && in_array($profile_include_data['current_area'], $context['password_areas']); | ||
| 655 | $context['require_password'] = $check_password; | ||
| 656 | |||
| 657 | 	loadJavaScriptFile('profile.js', array('defer' => false, 'minimize' => true), 'smf_profile'); | ||
| 658 | |||
| 659 | // These will get populated soon! | ||
| 660 | $post_errors = array(); | ||
| 661 | $profile_vars = array(); | ||
| 662 | |||
| 663 | // Right - are we saving - if so let's save the old data first. | ||
| 664 | if ($context['completed_save']) | ||
| 665 | 	{ | ||
| 666 | // Clean up the POST variables. | ||
| 667 | $_POST = htmltrim__recursive($_POST); | ||
| 668 | $_POST = htmlspecialchars__recursive($_POST); | ||
| 669 | |||
| 670 | if ($check_password) | ||
| 671 | 		{ | ||
| 672 | // Check to ensure we're forcing SSL for authentication | ||
| 673 | if (!empty($modSettings['force_ssl']) && empty($maintenance) && !httpsOn()) | ||
| 674 | 				fatal_lang_error('login_ssl_required', false); | ||
| 675 | |||
| 676 | $password = isset($_POST['oldpasswrd']) ? $_POST['oldpasswrd'] : ''; | ||
| 677 | |||
| 678 | // You didn't even enter a password! | ||
| 679 | if (trim($password) == '') | ||
| 680 | $post_errors[] = 'no_password'; | ||
| 681 | |||
| 682 | // Since the password got modified due to all the $_POST cleaning, lets undo it so we can get the correct password | ||
| 683 | $password = un_htmlspecialchars($password); | ||
| 684 | |||
| 685 | // Does the integration want to check passwords? | ||
| 686 | 			$good_password = in_array(true, call_integration_hook('integrate_verify_password', array($cur_profile['member_name'], $password, false)), true); | ||
| 687 | |||
| 688 | // Bad password!!! | ||
| 689 | if (!$good_password && !hash_verify_password($user_profile[$memID]['member_name'], $password, $user_info['passwd'])) | ||
| 690 | $post_errors[] = 'bad_password'; | ||
| 691 | |||
| 692 | // Warn other elements not to jump the gun and do custom changes! | ||
| 693 | 			if (in_array('bad_password', $post_errors)) | ||
| 694 | $context['password_auth_failed'] = true; | ||
| 695 | } | ||
| 696 | |||
| 697 | // Change the IP address in the database. | ||
| 698 | if ($context['user']['is_owner'] && $menuOptions['current_area'] != 'tfasetup') | ||
| 699 | $profile_vars['member_ip'] = $user_info['ip']; | ||
| 700 | |||
| 701 | // Now call the sub-action function... | ||
| 702 | if ($current_area == 'activateaccount') | ||
| 703 | 		{ | ||
| 704 | if (empty($post_errors)) | ||
| 705 | activateAccount($memID); | ||
| 706 | } | ||
| 707 | elseif ($current_area == 'deleteaccount') | ||
| 708 | 		{ | ||
| 709 | if (empty($post_errors)) | ||
| 710 | 			{ | ||
| 711 | deleteAccount2($memID); | ||
| 712 | redirectexit(); | ||
| 713 | } | ||
| 714 | } | ||
| 715 | elseif ($menuOptions['current_area'] == 'tfadisable') | ||
| 716 | 		{ | ||
| 717 | // Already checked the password, token, permissions, and session. | ||
| 718 | $profile_vars += array( | ||
| 719 | 'tfa_secret' => '', | ||
| 720 | 'tfa_backup' => '', | ||
| 721 | ); | ||
| 722 | } | ||
| 723 | elseif ($current_area == 'groupmembership' && empty($post_errors)) | ||
| 724 | 		{ | ||
| 725 | $msg = groupMembership2($profile_vars, $post_errors, $memID); | ||
| 726 | |||
| 727 | // Whatever we've done, we have nothing else to do here... | ||
| 728 | 			redirectexit('action=profile' . ($context['user']['is_owner'] ? '' : ';u=' . $memID) . ';area=groupmembership' . (!empty($msg) ? ';msg=' . $msg : '')); | ||
| 729 | } | ||
| 730 | 		elseif (in_array($current_area, array('account', 'forumprofile', 'theme'))) | ||
| 731 | saveProfileFields(); | ||
| 732 | else | ||
| 733 | 		{ | ||
| 734 | $force_redirect = true; | ||
| 735 | // Ensure we include this. | ||
| 736 | require_once($sourcedir . '/Profile-Modify.php'); | ||
| 737 | saveProfileChanges($profile_vars, $post_errors, $memID); | ||
| 738 | } | ||
| 739 | |||
| 740 | 		call_integration_hook('integrate_profile_save', array(&$profile_vars, &$post_errors, $memID, $cur_profile, $current_area)); | ||
| 741 | |||
| 742 | // There was a problem, let them try to re-enter. | ||
| 743 | if (!empty($post_errors)) | ||
| 744 | 		{ | ||
| 745 | // Load the language file so we can give a nice explanation of the errors. | ||
| 746 | 			loadLanguage('Errors'); | ||
| 747 | $context['post_errors'] = $post_errors; | ||
| 748 | } | ||
| 749 | elseif (!empty($profile_vars)) | ||
| 750 | 		{ | ||
| 751 | // If we've changed the password, notify any integration that may be listening in. | ||
| 752 | if (isset($profile_vars['passwd'])) | ||
| 753 | 				call_integration_hook('integrate_reset_pass', array($cur_profile['member_name'], $cur_profile['member_name'], $_POST['passwrd2'])); | ||
| 754 | |||
| 755 | updateMemberData($memID, $profile_vars); | ||
| 756 | |||
| 757 | // What if this is the newest member? | ||
| 758 | if ($modSettings['latestMember'] == $memID) | ||
| 759 | 				updateStats('member'); | ||
| 760 | elseif (isset($profile_vars['real_name'])) | ||
| 761 | 				updateSettings(array('memberlist_updated' => time())); | ||
| 762 | |||
| 763 | // If the member changed his/her birthdate, update calendar statistics. | ||
| 764 | if (isset($profile_vars['birthdate']) || isset($profile_vars['real_name'])) | ||
| 765 | updateSettings(array( | ||
| 766 | 'calendar_updated' => time(), | ||
| 767 | )); | ||
| 768 | |||
| 769 | // Anything worth logging? | ||
| 770 | if (!empty($context['log_changes']) && !empty($modSettings['modlog_enabled'])) | ||
| 771 | 			{ | ||
| 772 | $log_changes = array(); | ||
| 773 | require_once($sourcedir . '/Logging.php'); | ||
| 774 | foreach ($context['log_changes'] as $k => $v) | ||
| 775 | $log_changes[] = array( | ||
| 776 | 'action' => $k, | ||
| 777 | 'log_type' => 'user', | ||
| 778 | 'extra' => array_merge($v, array( | ||
| 779 | 'applicator' => $user_info['id'], | ||
| 780 | 'member_affected' => $memID, | ||
| 781 | )), | ||
| 782 | ); | ||
| 783 | |||
| 784 | logActions($log_changes); | ||
| 785 | } | ||
| 786 | |||
| 787 | // Have we got any post save functions to execute? | ||
| 788 | if (!empty($context['profile_execute_on_save'])) | ||
| 789 | foreach ($context['profile_execute_on_save'] as $saveFunc) | ||
| 790 | $saveFunc(); | ||
| 791 | |||
| 792 | // Let them know it worked! | ||
| 793 | $context['profile_updated'] = $context['user']['is_owner'] ? $txt['profile_updated_own'] : sprintf($txt['profile_updated_else'], $cur_profile['member_name']); | ||
| 794 | |||
| 795 | // Invalidate any cached data. | ||
| 796 | 			cache_put_data('member_data-profile-' . $memID, null, 0); | ||
| 797 | } | ||
| 798 | } | ||
| 799 | |||
| 800 | // Have some errors for some reason? | ||
| 801 | if (!empty($post_errors)) | ||
| 802 | 	{ | ||
| 803 | // Set all the errors so the template knows what went wrong. | ||
| 804 | foreach ($post_errors as $error_type) | ||
| 805 | $context['modify_error'][$error_type] = true; | ||
| 806 | } | ||
| 807 | // If it's you then we should redirect upon save. | ||
| 808 | elseif (!empty($profile_vars) && $context['user']['is_owner'] && !$context['do_preview']) | ||
| 809 | 		redirectexit('action=profile;area=' . $current_area . (!empty($current_sa) ? ';sa=' . $current_sa : '') . ';updated'); | ||
| 810 | elseif (!empty($force_redirect)) | ||
| 811 | 		redirectexit('action=profile' . ($context['user']['is_owner'] ? '' : ';u=' . $memID) . ';area=' . $current_area); | ||
| 812 | |||
| 813 | // Get the right callable. | ||
| 814 | $call = call_helper($profile_include_data['function'], true); | ||
| 815 | |||
| 816 | // Is it valid? | ||
| 817 | if (!empty($call)) | ||
| 818 | call_user_func($call, $memID); | ||
| 819 | |||
| 820 | // Set the page title if it's not already set... | ||
| 821 | if (!isset($context['page_title'])) | ||
| 822 | $context['page_title'] = $txt['profile'] . (isset($txt[$current_area]) ? ' - ' . $txt[$current_area] : ''); | ||
| 823 | } | ||
| 824 | |||
| 825 | /** | ||
| 826 | * Set up the requirements for the profile popup - the area that is shown as the popup menu for the current user. | ||
| 827 | * | ||
| 828 | * @param int $memID The ID of the member | ||
| 829 | */ | ||
| 830 | function profile_popup($memID) | ||
| 831 | { | ||
| 832 | global $context, $scripturl, $txt, $db_show_debug; | ||
| 833 | |||
| 834 | // We do not want to output debug information here. | ||
| 835 | $db_show_debug = false; | ||
| 836 | |||
| 837 | // We only want to output our little layer here. | ||
| 838 | $context['template_layers'] = array(); | ||
| 839 | |||
| 840 | // This list will pull from the master list wherever possible. Hopefully it should be clear what does what. | ||
| 841 | $profile_items = array( | ||
| 842 | array( | ||
| 843 | 'menu' => 'edit_profile', | ||
| 844 | 'area' => 'account', | ||
| 845 | ), | ||
| 846 | array( | ||
| 847 | 'menu' => 'edit_profile', | ||
| 848 | 'area' => 'forumprofile', | ||
| 849 | 'title' => $txt['popup_forumprofile'], | ||
| 850 | ), | ||
| 851 | array( | ||
| 852 | 'menu' => 'edit_profile', | ||
| 853 | 'area' => 'theme', | ||
| 854 | 'title' => $txt['theme'], | ||
| 855 | ), | ||
| 856 | array( | ||
| 857 | 'menu' => 'edit_profile', | ||
| 858 | 'area' => 'notification', | ||
| 859 | ), | ||
| 860 | array( | ||
| 861 | 'menu' => 'edit_profile', | ||
| 862 | 'area' => 'ignoreboards', | ||
| 863 | ), | ||
| 864 | array( | ||
| 865 | 'menu' => 'edit_profile', | ||
| 866 | 'area' => 'lists', | ||
| 867 | 'url' => $scripturl . '?action=profile;area=lists;sa=ignore', | ||
| 868 | 'title' => $txt['popup_ignore'], | ||
| 869 | ), | ||
| 870 | array( | ||
| 871 | 'menu' => 'info', | ||
| 872 | 'area' => 'showposts', | ||
| 873 | 'title' => $txt['popup_showposts'], | ||
| 874 | ), | ||
| 875 | array( | ||
| 876 | 'menu' => 'info', | ||
| 877 | 'area' => 'showdrafts', | ||
| 878 | 'title' => $txt['popup_showdrafts'], | ||
| 879 | ), | ||
| 880 | array( | ||
| 881 | 'menu' => 'edit_profile', | ||
| 882 | 'area' => 'groupmembership', | ||
| 883 | 'title' => $txt['popup_groupmembership'], | ||
| 884 | ), | ||
| 885 | array( | ||
| 886 | 'menu' => 'profile_action', | ||
| 887 | 'area' => 'subscriptions', | ||
| 888 | 'title' => $txt['popup_subscriptions'], | ||
| 889 | ), | ||
| 890 | array( | ||
| 891 | 'menu' => 'profile_action', | ||
| 892 | 'area' => 'logout', | ||
| 893 | ), | ||
| 894 | ); | ||
| 895 | |||
| 896 | 	call_integration_hook('integrate_profile_popup', array(&$profile_items)); | ||
| 897 | |||
| 898 | // Now check if these items are available | ||
| 899 | $context['profile_items'] = array(); | ||
| 900 | $menu_context = &$context[$context['profile_menu_name']]['sections']; | ||
| 901 | foreach ($profile_items as $item) | ||
| 902 | 	{ | ||
| 903 | if (isset($menu_context[$item['menu']]['areas'][$item['area']])) | ||
| 904 | 		{ | ||
| 905 | $context['profile_items'][] = $item; | ||
| 906 | } | ||
| 907 | } | ||
| 908 | } | ||
| 909 | |||
| 910 | /** | ||
| 911 | * Set up the requirements for the alerts popup - the area that shows all the alerts just quickly for the current user. | ||
| 912 | * | ||
| 913 | * @param int $memID The ID of the member | ||
| 914 | */ | ||
| 915 | function alerts_popup($memID) | ||
| 938 | } | ||
| 939 | } | ||
| 940 | |||
| 941 | /** | ||
| 942 | * Load any custom fields for this area... no area means load all, 'summary' loads all public ones. | ||
| 943 | * | ||
| 944 | * @param int $memID The ID of the member | ||
| 945 | * @param string $area Which area to load fields for | ||
| 946 | */ | ||
| 947 | function loadCustomFields($memID, $area = 'summary') | ||
| 1089 | } | ||
| 1090 | |||
| 1091 | ?> |