| Total Complexity | 85 |
| Total Lines | 766 |
| Duplicated Lines | 0 % |
| Changes | 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 |
||
| 37 | class Profile extends AbstractController |
||
| 38 | { |
||
| 39 | /** @var bool If the save was successful or not */ |
||
| 40 | private $completedSave = false; |
||
| 41 | |||
| 42 | /** @var null If this was a request to save an update */ |
||
| 43 | private $isSaving; |
||
| 44 | |||
| 45 | /** @var null What it says, on completion */ |
||
| 46 | private $_force_redirect; |
||
| 47 | |||
| 48 | /** @var array|bool Holds the output of createMenu for the profile areas */ |
||
| 49 | private $_profile_include_data; |
||
| 50 | |||
| 51 | /** @var string The current area chosen from the menu */ |
||
| 52 | private $_current_area; |
||
| 53 | |||
| 54 | /** @var string The current subsection, if any, of the area chosen */ |
||
| 55 | private $_current_subsection; |
||
| 56 | |||
| 57 | /** @var int Member id for the history being viewed */ |
||
| 58 | private $_memID = 0; |
||
| 59 | |||
| 60 | /** @var Member The \ElkArte\Member object is stored here to avoid some global */ |
||
| 61 | private $_profile; |
||
| 62 | |||
| 63 | /** |
||
| 64 | * Called before all other methods when coming from the dispatcher or |
||
| 65 | * action class. |
||
| 66 | */ |
||
| 67 | public function pre_dispatch() |
||
| 68 | { |
||
| 69 | require_once(SUBSDIR . '/Menu.subs.php'); |
||
| 70 | require_once(SUBSDIR . '/Profile.subs.php'); |
||
| 71 | |||
| 72 | $this->_memID = currentMemberID(); |
||
| 73 | MembersList::load($this->_memID, false, 'profile'); |
||
| 74 | $this->_profile = MembersList::get($this->_memID); |
||
|
|
|||
| 75 | } |
||
| 76 | |||
| 77 | /** |
||
| 78 | * Allow the change or view of profiles. |
||
| 79 | * |
||
| 80 | * - Fires the pre_load event |
||
| 81 | * |
||
| 82 | * @see AbstractController::action_index |
||
| 83 | */ |
||
| 84 | public function action_index() |
||
| 188 | } |
||
| 189 | |||
| 190 | /** |
||
| 191 | * Define all the sections within the profile area! |
||
| 192 | * |
||
| 193 | * We start by defining the permission required - then we take this and turn |
||
| 194 | * it into the relevant context ;) |
||
| 195 | * |
||
| 196 | * Possible fields: |
||
| 197 | * For Section: |
||
| 198 | * - string $title: Section title. |
||
| 199 | * - array $areas: Array of areas within this section. |
||
| 200 | * |
||
| 201 | * For Areas: |
||
| 202 | * - string $label: Text string that will be used to show the area in the menu. |
||
| 203 | * - string $file: Optional text string that may contain a file name that's needed for inclusion in order to display the area properly. |
||
| 204 | * - string $custom_url: Optional href for area. |
||
| 205 | * - string $function: Function to execute for this section. |
||
| 206 | * - bool $enabled: Should area be shown? |
||
| 207 | * - string $sc: Session check validation to do on save - note without this save will get unset - if set. |
||
| 208 | * - bool $hidden: Does this not actually appear on the menu? |
||
| 209 | * - bool $password: Whether to require the user's password in order to save the data in the area. |
||
| 210 | * - array $subsections: Array of subsections, in order of appearance. |
||
| 211 | * - array $permission: Array of permissions to determine who can access this area. Should contain arrays $own and $any. |
||
| 212 | */ |
||
| 213 | private function _define_profile_menu(): void |
||
| 214 | { |
||
| 215 | global $txt, $context, $modSettings; |
||
| 216 | |||
| 217 | $profile_areas = [ |
||
| 218 | 'info' => [ |
||
| 219 | 'title' => $txt['profileInfo'], |
||
| 220 | 'areas' => [ |
||
| 221 | 'summary' => [ |
||
| 222 | 'label' => $txt['summary'], |
||
| 223 | 'controller' => ProfileInfo::class, |
||
| 224 | 'function' => 'action_summary', |
||
| 225 | // From the summary it's possible to activate an account, so we need the token |
||
| 226 | 'token' => 'profile-aa%u', |
||
| 227 | 'token_type' => 'get', |
||
| 228 | 'permission' => [ |
||
| 229 | 'own' => 'profile_view_own', |
||
| 230 | 'any' => 'profile_view_any', |
||
| 231 | ], |
||
| 232 | ], |
||
| 233 | 'statistics' => [ |
||
| 234 | 'label' => $txt['statPanel'], |
||
| 235 | 'controller' => ProfileInfo::class, |
||
| 236 | 'function' => 'action_statPanel', |
||
| 237 | 'permission' => [ |
||
| 238 | 'own' => 'profile_view_own', |
||
| 239 | 'any' => 'profile_view_any', |
||
| 240 | ], |
||
| 241 | ], |
||
| 242 | 'showposts' => [ |
||
| 243 | 'label' => $txt['showPosts'], |
||
| 244 | 'controller' => ProfileInfo::class, |
||
| 245 | 'function' => 'action_showPosts', |
||
| 246 | 'subsections' => [ |
||
| 247 | 'messages' => [$txt['showMessages'], ['profile_view_own', 'profile_view_any']], |
||
| 248 | 'topics' => [$txt['showTopics'], ['profile_view_own', 'profile_view_any']], |
||
| 249 | 'unwatchedtopics' => [$txt['showUnwatched'], ['profile_view_own', 'profile_view_any'], 'enabled' => $modSettings['enable_unwatch'] && $context['user']['is_owner']], |
||
| 250 | 'attach' => [$txt['showAttachments'], ['profile_view_own', 'profile_view_any']], |
||
| 251 | ], |
||
| 252 | 'permission' => [ |
||
| 253 | 'own' => 'profile_view_own', |
||
| 254 | 'any' => 'profile_view_any', |
||
| 255 | ], |
||
| 256 | ], |
||
| 257 | 'showlikes' => [ |
||
| 258 | 'label' => $txt['likes_show'], |
||
| 259 | 'controller' => Likes::class, |
||
| 260 | 'function' => 'action_showProfileLikes', |
||
| 261 | 'enabled' => !empty($modSettings['likes_enabled']) && $context['user']['is_owner'], |
||
| 262 | 'subsections' => [ |
||
| 263 | 'given' => [$txt['likes_given'], ['profile_view_own']], |
||
| 264 | 'received' => [$txt['likes_received'], ['profile_view_own']], |
||
| 265 | ], |
||
| 266 | 'permission' => [ |
||
| 267 | 'own' => 'profile_view_own', |
||
| 268 | 'any' => [], |
||
| 269 | ], |
||
| 270 | ], |
||
| 271 | 'permissions' => [ |
||
| 272 | 'label' => $txt['showPermissions'], |
||
| 273 | 'controller' => ProfileInfo::class, |
||
| 274 | 'function' => 'action_showPermissions', |
||
| 275 | 'permission' => [ |
||
| 276 | 'own' => 'manage_permissions', |
||
| 277 | 'any' => 'manage_permissions', |
||
| 278 | ], |
||
| 279 | ], |
||
| 280 | 'history' => [ |
||
| 281 | 'label' => $txt['history'], |
||
| 282 | 'controller' => ProfileHistory::class, |
||
| 283 | 'function' => 'action_index', |
||
| 284 | 'subsections' => [ |
||
| 285 | 'activity' => [$txt['trackActivity'], 'moderate_forum'], |
||
| 286 | 'ip' => [$txt['trackIP'], 'moderate_forum'], |
||
| 287 | 'edits' => [$txt['trackEdits'], 'moderate_forum', 'enabled' => featureEnabled('ml') && !empty($modSettings['userlog_enabled'])], |
||
| 288 | 'logins' => [$txt['trackLogins'], ['profile_view_own', 'moderate_forum']], |
||
| 289 | ], |
||
| 290 | 'permission' => [ |
||
| 291 | 'own' => 'moderate_forum', |
||
| 292 | 'any' => 'moderate_forum', |
||
| 293 | ], |
||
| 294 | ], |
||
| 295 | 'viewwarning' => [ |
||
| 296 | 'label' => $txt['profile_view_warnings'], |
||
| 297 | 'enabled' => featureEnabled('w') && !empty($modSettings['warning_enable']) && $this->_profile['warning'] && (!empty($modSettings['warning_show']) && ($context['user']['is_owner'] || $modSettings['warning_show'] == 2)), |
||
| 298 | 'controller' => ProfileInfo::class, |
||
| 299 | 'function' => 'action_viewWarning', |
||
| 300 | 'permission' => [ |
||
| 301 | 'own' => 'profile_view_own', |
||
| 302 | 'any' => 'issue_warning', |
||
| 303 | ], |
||
| 304 | ], |
||
| 305 | ], |
||
| 306 | ], |
||
| 307 | 'edit_profile' => [ |
||
| 308 | 'title' => $txt['profileEdit'], |
||
| 309 | 'areas' => [ |
||
| 310 | 'account' => [ |
||
| 311 | 'label' => $txt['account'], |
||
| 312 | 'controller' => ProfileOptions::class, |
||
| 313 | 'function' => 'action_account', |
||
| 314 | 'enabled' => $context['user']['is_admin'] || ((int) $this->_profile['id_group'] !== 1 && !in_array(1, array_map('intval', explode(',', $this->_profile['additional_groups'])), true)), 'sc' => 'post', |
||
| 315 | 'token' => 'profile-ac%u', |
||
| 316 | 'password' => true, |
||
| 317 | 'permission' => [ |
||
| 318 | 'own' => ['profile_identity_any', 'profile_identity_own', 'manage_membergroups'], |
||
| 319 | 'any' => ['profile_identity_any', 'manage_membergroups'], |
||
| 320 | ], |
||
| 321 | ], |
||
| 322 | 'forumprofile' => [ |
||
| 323 | 'label' => $txt['forumprofile'], |
||
| 324 | 'controller' => ProfileOptions::class, |
||
| 325 | 'function' => 'action_forumProfile', |
||
| 326 | 'sc' => 'post', |
||
| 327 | 'token' => 'profile-fp%u', |
||
| 328 | 'permission' => [ |
||
| 329 | 'own' => ['profile_extra_any', 'profile_extra_own', 'profile_title_own', 'profile_title_any'], |
||
| 330 | 'any' => ['profile_extra_any', 'profile_title_any'], |
||
| 331 | ], |
||
| 332 | ], |
||
| 333 | 'theme' => [ |
||
| 334 | 'label' => $txt['theme'], |
||
| 335 | 'controller' => ProfileOptions::class, |
||
| 336 | 'function' => 'action_themepick', |
||
| 337 | 'sc' => 'post', |
||
| 338 | 'token' => 'profile-th%u', |
||
| 339 | 'permission' => [ |
||
| 340 | 'own' => ['profile_extra_any', 'profile_extra_own'], |
||
| 341 | 'any' => ['profile_extra_any'], |
||
| 342 | ], |
||
| 343 | ], |
||
| 344 | 'pick' => [ |
||
| 345 | 'label' => $txt['theme'], |
||
| 346 | 'controller' => ProfileOptions::class, |
||
| 347 | 'function' => 'action_pick', |
||
| 348 | 'hidden' => true, |
||
| 349 | 'sc' => 'post', |
||
| 350 | 'token' => 'profile-th%u', |
||
| 351 | 'permission' => [ |
||
| 352 | 'own' => ['profile_extra_any', 'profile_extra_own'], |
||
| 353 | 'any' => ['profile_extra_any'], |
||
| 354 | ], |
||
| 355 | ], |
||
| 356 | 'notification' => [ |
||
| 357 | 'label' => $txt['notifications'], |
||
| 358 | 'controller' => ProfileOptions::class, |
||
| 359 | 'function' => 'action_notification', |
||
| 360 | 'sc' => 'post', |
||
| 361 | 'token' => 'profile-nt%u', |
||
| 362 | 'subsections' => [ |
||
| 363 | 'settings' => [$txt['notify_settings']], |
||
| 364 | 'boards' => [$txt['notify_boards']], |
||
| 365 | 'topics' => [$txt['notify_topics']], |
||
| 366 | ], |
||
| 367 | 'permission' => [ |
||
| 368 | 'own' => ['profile_extra_any', 'profile_extra_own'], |
||
| 369 | 'any' => ['profile_extra_any'], |
||
| 370 | ], |
||
| 371 | ], |
||
| 372 | // Without profile_extra_own, settings are accessible from the PM section. |
||
| 373 | // @todo at some point decouple it from PMs |
||
| 374 | 'contactprefs' => [ |
||
| 375 | 'label' => $txt['contactprefs'], |
||
| 376 | 'controller' => ProfileOptions::class, |
||
| 377 | 'function' => 'action_pmprefs', |
||
| 378 | 'enabled' => allowedTo(['profile_extra_own', 'profile_extra_any']), |
||
| 379 | 'sc' => 'post', |
||
| 380 | 'token' => 'profile-pm%u', |
||
| 381 | 'permission' => [ |
||
| 382 | 'own' => ['pm_read'], |
||
| 383 | 'any' => ['profile_extra_any'], |
||
| 384 | ], |
||
| 385 | ], |
||
| 386 | 'ignoreboards' => [ |
||
| 387 | 'label' => $txt['ignoreboards'], |
||
| 388 | 'controller' => ProfileOptions::class, |
||
| 389 | 'function' => 'action_ignoreboards', |
||
| 390 | 'enabled' => !empty($modSettings['allow_ignore_boards']), |
||
| 391 | 'sc' => 'post', |
||
| 392 | 'token' => 'profile-ib%u', |
||
| 393 | 'permission' => [ |
||
| 394 | 'own' => ['profile_extra_any', 'profile_extra_own'], |
||
| 395 | 'any' => ['profile_extra_any'], |
||
| 396 | ], |
||
| 397 | ], |
||
| 398 | 'lists' => [ |
||
| 399 | 'label' => $txt['editBuddyIgnoreLists'], |
||
| 400 | 'controller' => ProfileOptions::class, |
||
| 401 | 'function' => 'action_editBuddyIgnoreLists', |
||
| 402 | 'enabled' => !empty($modSettings['enable_buddylist']) && $context['user']['is_owner'], |
||
| 403 | 'sc' => 'post', |
||
| 404 | 'token' => 'profile-bl%u', |
||
| 405 | 'subsections' => [ |
||
| 406 | 'buddies' => [$txt['editBuddies']], |
||
| 407 | 'ignore' => [$txt['editIgnoreList']], |
||
| 408 | ], |
||
| 409 | 'permission' => [ |
||
| 410 | 'own' => ['profile_extra_any', 'profile_extra_own'], |
||
| 411 | 'any' => [], |
||
| 412 | ], |
||
| 413 | ], |
||
| 414 | 'groupmembership' => [ |
||
| 415 | 'label' => $txt['groupmembership'], |
||
| 416 | 'controller' => ProfileOptions::class, |
||
| 417 | 'function' => 'action_groupMembership', |
||
| 418 | 'enabled' => !empty($modSettings['show_group_membership']) && $context['user']['is_owner'], |
||
| 419 | 'sc' => 'request', |
||
| 420 | 'token' => 'profile-gm%u', |
||
| 421 | 'token_type' => 'request', |
||
| 422 | 'permission' => [ |
||
| 423 | 'own' => ['profile_view_own'], |
||
| 424 | 'any' => ['manage_membergroups'], |
||
| 425 | ], |
||
| 426 | ], |
||
| 427 | ], |
||
| 428 | ], |
||
| 429 | 'profile_action' => [ |
||
| 430 | 'title' => $txt['profileAction'], |
||
| 431 | 'areas' => [ |
||
| 432 | 'sendpm' => [ |
||
| 433 | 'label' => $txt['profileSendIm'], |
||
| 434 | 'custom_url' => getUrl('action', ['action' => 'pm', 'sa' => 'send']), |
||
| 435 | 'permission' => [ |
||
| 436 | 'own' => [], |
||
| 437 | 'any' => ['pm_send'], |
||
| 438 | ], |
||
| 439 | ], |
||
| 440 | 'issuewarning' => [ |
||
| 441 | 'label' => $txt['profile_issue_warning'], |
||
| 442 | 'enabled' => featureEnabled('w') && !empty($modSettings['warning_enable']) && (!$context['user']['is_owner'] || $context['user']['is_admin']), |
||
| 443 | 'controller' => ProfileAccount::class, |
||
| 444 | 'function' => 'action_issuewarning', |
||
| 445 | 'token' => 'profile-iw%u', |
||
| 446 | 'permission' => [ |
||
| 447 | 'own' => [], |
||
| 448 | 'any' => ['issue_warning'], |
||
| 449 | ], |
||
| 450 | ], |
||
| 451 | 'banuser' => [ |
||
| 452 | 'label' => $txt['profileBanUser'], |
||
| 453 | 'custom_url' => getUrl('admin', ['action' => 'admin', 'area' => 'ban', 'sa' => 'add']), |
||
| 454 | 'enabled' => (int) $this->_profile['id_group'] !== 1 && !in_array(1, array_map('intval', explode(',', $this->_profile['additional_groups'])), true), |
||
| 455 | 'permission' => [ |
||
| 456 | 'own' => [], |
||
| 457 | 'any' => ['manage_bans'], |
||
| 458 | ], |
||
| 459 | ], |
||
| 460 | 'subscriptions' => [ |
||
| 461 | 'label' => $txt['subscriptions'], |
||
| 462 | 'controller' => ProfileSubscriptions::class, |
||
| 463 | 'function' => 'action_subscriptions', |
||
| 464 | 'enabled' => !empty($modSettings['paid_enabled']) && ((empty($modSettings['paidsubs_test']) || $context['user']['is_admin'])), |
||
| 465 | 'permission' => [ |
||
| 466 | 'own' => ['profile_view_own'], |
||
| 467 | 'any' => ['moderate_forum'], |
||
| 468 | ], |
||
| 469 | ], |
||
| 470 | 'deleteaccount' => [ |
||
| 471 | 'label' => $txt['deleteAccount'], |
||
| 472 | 'controller' => ProfileAccount::class, |
||
| 473 | 'function' => 'action_deleteaccount', |
||
| 474 | 'sc' => 'post', |
||
| 475 | 'token' => 'profile-da%u', |
||
| 476 | 'password' => true, |
||
| 477 | 'permission' => [ |
||
| 478 | 'own' => ['profile_remove_any', 'profile_remove_own'], |
||
| 479 | 'any' => ['profile_remove_any'], |
||
| 480 | ], |
||
| 481 | ], |
||
| 482 | 'activateaccount' => [ |
||
| 483 | 'label' => $txt['account_activate'], |
||
| 484 | 'hidden' => true, |
||
| 485 | 'controller' => ProfileAccount::class, |
||
| 486 | 'function' => 'action_activateaccount', |
||
| 487 | 'sc' => 'get', |
||
| 488 | 'token' => 'profile-aa%u', |
||
| 489 | 'token_type' => 'get', |
||
| 490 | 'permission' => [ |
||
| 491 | 'own' => [], |
||
| 492 | 'any' => ['moderate_forum'], |
||
| 493 | ], |
||
| 494 | ], |
||
| 495 | ], |
||
| 496 | ], |
||
| 497 | ]; |
||
| 498 | |||
| 499 | // Set a few options for the menu. |
||
| 500 | $menuOptions = [ |
||
| 501 | 'disable_url_session_check' => true, |
||
| 502 | 'hook' => 'profile', |
||
| 503 | 'extra_url_parameters' => [ |
||
| 504 | 'u' => $context['id_member'], |
||
| 505 | ], |
||
| 506 | ]; |
||
| 507 | |||
| 508 | // Actually create the menu! |
||
| 509 | $this->_profile_include_data = (new Menu()) |
||
| 510 | ->addMenuData($profile_areas) |
||
| 511 | ->addOptions($menuOptions) |
||
| 512 | ->prepareMenu() |
||
| 513 | ->setContext() |
||
| 514 | ->getIncludeData(); |
||
| 515 | |||
| 516 | unset($profile_areas); |
||
| 517 | |||
| 518 | // Make a note of the Unique ID for this menu. |
||
| 519 | $context['profile_menu_id'] = $context['max_menu_id']; |
||
| 520 | $context['profile_menu_name'] = 'menu_data_' . $context['profile_menu_id']; |
||
| 521 | } |
||
| 522 | |||
| 523 | /** |
||
| 524 | * Does session and token checks for the areas that require those |
||
| 525 | */ |
||
| 526 | private function _check_access(): void |
||
| 527 | { |
||
| 528 | global $context; |
||
| 529 | |||
| 530 | // Check the session, if required, and they are trying to save |
||
| 531 | $this->completedSave = false; |
||
| 532 | if (isset($this->_profile_include_data['sc']) && ($this->isSaving !== null || $context['do_preview'])) |
||
| 533 | { |
||
| 534 | checkSession($this->_profile_include_data['sc']); |
||
| 535 | $this->completedSave = true; |
||
| 536 | } |
||
| 537 | |||
| 538 | // Does this require admin/moderator session validating? |
||
| 539 | if ($this->isSaving !== null && !$context['user']['is_owner']) |
||
| 540 | { |
||
| 541 | validateSession(); |
||
| 542 | } |
||
| 543 | |||
| 544 | // Do we need to perform a token check? |
||
| 545 | if (!empty($this->_profile_include_data['token'])) |
||
| 546 | { |
||
| 547 | $token_name = str_replace('%u', $context['id_member'], $this->_profile_include_data['token']); |
||
| 548 | $token_type = $this->_profile_include_data['tokenType'] ?? 'post'; |
||
| 549 | |||
| 550 | if (!in_array($token_type, ['request', 'post', 'get'])) |
||
| 551 | { |
||
| 552 | $token_type = 'post'; |
||
| 553 | } |
||
| 554 | |||
| 555 | if ($this->isSaving !== null) |
||
| 556 | { |
||
| 557 | validateToken($token_name, $token_type); |
||
| 558 | } |
||
| 559 | |||
| 560 | createToken($token_name, $token_type); |
||
| 561 | $context['token_check'] = $token_name; |
||
| 562 | } |
||
| 563 | } |
||
| 564 | |||
| 565 | /** |
||
| 566 | * Just builds the link tree based on where we are in the profile section |
||
| 567 | * and whose profile is being viewed, etc. |
||
| 568 | */ |
||
| 569 | private function _build_profile_breadcrumbs(): void |
||
| 604 | ]; |
||
| 605 | } |
||
| 606 | |||
| 607 | /** |
||
| 608 | * Save profile updates |
||
| 609 | */ |
||
| 610 | private function _save_updates(): void |
||
| 762 | } |
||
| 763 | } |
||
| 764 | } |
||
| 765 | |||
| 766 | /** |
||
| 767 | * If a password validation before a change is needed, this is the function to do it |
||
| 768 | * |
||
| 769 | * @param bool $check_password if this profile update requires password verification |
||
| 770 | * @throws Exception |
||
| 771 | */ |
||
| 772 | private function _check_password($check_password): void |
||
| 803 | } |
||
| 804 | } |
||
| 807 |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountIdthat can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theidproperty of an instance of theAccountclass. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.