Total Complexity | 79 |
Total Lines | 663 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like ProfileAccount 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 ProfileAccount, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
32 | class ProfileAccount extends AbstractController |
||
33 | { |
||
34 | /** @var int Member id for the account being worked on */ |
||
35 | private $_memID = 0; |
||
36 | |||
37 | /** @var Member The \ElkArte\Member object is stored here to avoid some global */ |
||
38 | private $_profile; |
||
39 | |||
40 | /** @var array Holds any errors that were generated when issuing a warning */ |
||
41 | private $_issueErrors = []; |
||
42 | |||
43 | /** |
||
44 | * Called before all other methods when coming from the dispatcher or |
||
45 | * action class. |
||
46 | */ |
||
47 | public function pre_dispatch() |
||
48 | { |
||
49 | $this->_memID = currentMemberID(); |
||
50 | $this->_profile = MembersList::get($this->_memID); |
||
|
|||
51 | } |
||
52 | |||
53 | /** |
||
54 | * Entry point for this class. |
||
55 | * |
||
56 | * @see AbstractController::action_index() |
||
57 | */ |
||
58 | public function action_index() |
||
59 | { |
||
60 | // figure out what action to do... if we're called directly |
||
61 | // actions in this class are called from the Profile menu, though. |
||
62 | } |
||
63 | |||
64 | /** |
||
65 | * Issue/manage a user's warning status. |
||
66 | * |
||
67 | * @throws Exception |
||
68 | * @uses Profile template |
||
69 | * @uses template_issueWarning sub template in ProfileAccount |
||
70 | */ |
||
71 | public function action_issuewarning() |
||
72 | { |
||
73 | global $txt, $scripturl, $modSettings, $mbname, $context, $cur_profile; |
||
74 | |||
75 | // Make sure the sub-template is set... |
||
76 | theme()->getTemplates()->load('ProfileAccount'); |
||
77 | $context['sub_template'] = 'issueWarning'; |
||
78 | |||
79 | // We need this because of template_load_warning_variables |
||
80 | theme()->getTemplates()->load('Profile'); |
||
81 | loadJavascriptFile('profile.js'); |
||
82 | |||
83 | // jQuery-UI FTW! |
||
84 | $modSettings['jquery_include_ui'] = true; |
||
85 | loadCSSFile('jquery.ui.slider.css'); |
||
86 | loadCSSFile('jquery.ui.theme.min.css'); |
||
87 | |||
88 | // Get all the actual settings. |
||
89 | [$modSettings['warning_enable'], $modSettings['user_limit']] = explode(',', $modSettings['warning_settings']); |
||
90 | |||
91 | // Doesn't hurt to be overly cautious. |
||
92 | if (empty($modSettings['warning_enable']) |
||
93 | || ($context['user']['is_owner'] && !$cur_profile['warning']) |
||
94 | || !allowedTo('issue_warning')) |
||
95 | { |
||
96 | throw new Exception('no_access', false); |
||
97 | } |
||
98 | |||
99 | // Get the base (errors related) stuff done. |
||
100 | Txt::load('Errors'); |
||
101 | $context['custom_error_title'] = $txt['profile_warning_errors_occurred']; |
||
102 | |||
103 | // Make sure things which are disabled stay disabled. |
||
104 | $modSettings['warning_watch'] = empty($modSettings['warning_watch']) ? 110 : $modSettings['warning_watch']; |
||
105 | $modSettings['warning_moderate'] = !empty($modSettings['warning_moderate']) && !empty($modSettings['postmod_active']) ? $modSettings['warning_moderate'] : 110; |
||
106 | $modSettings['warning_mute'] = empty($modSettings['warning_mute']) ? 110 : $modSettings['warning_mute']; |
||
107 | |||
108 | $context['warning_limit'] = allowedTo('admin_forum') ? 0 : $modSettings['user_limit']; |
||
109 | $context['member']['warning'] = $cur_profile['warning']; |
||
110 | $context['member']['name'] = $cur_profile['real_name']; |
||
111 | |||
112 | // What are the limits we can apply? |
||
113 | $context['min_allowed'] = 0; |
||
114 | $context['max_allowed'] = 100; |
||
115 | if ($context['warning_limit'] > 0) |
||
116 | { |
||
117 | require_once(SUBSDIR . '/Moderation.subs.php'); |
||
118 | $current_applied = warningDailyLimit($this->_memID); |
||
119 | |||
120 | $context['min_allowed'] = max(0, $cur_profile['warning'] - $current_applied - $context['warning_limit']); |
||
121 | $context['max_allowed'] = min(100, $cur_profile['warning'] - $current_applied + $context['warning_limit']); |
||
122 | } |
||
123 | |||
124 | // Defaults. |
||
125 | $context['warning_data'] = [ |
||
126 | 'reason' => '', |
||
127 | 'notify' => '', |
||
128 | 'notify_subject' => '', |
||
129 | 'notify_body' => '', |
||
130 | ]; |
||
131 | |||
132 | // Are we saving? |
||
133 | $this->_save_warning(); |
||
134 | |||
135 | // Perhaps taking a look first? Good idea that one. |
||
136 | $this->_preview_warning(); |
||
137 | |||
138 | // If we have errors, lets set them for the template |
||
139 | if (!empty($this->_issueErrors)) |
||
140 | { |
||
141 | // Fill in the suite of errors. |
||
142 | $context['post_errors'] = []; |
||
143 | foreach ($this->_issueErrors as $error) |
||
144 | { |
||
145 | $context['post_errors'][] = $txt[$error]; |
||
146 | } |
||
147 | } |
||
148 | |||
149 | $context['page_title'] = $txt['profile_issue_warning']; |
||
150 | |||
151 | // Let's use a generic list to get all the current warnings |
||
152 | require_once(SUBSDIR . '/Profile.subs.php'); |
||
153 | |||
154 | // Work our the various levels. |
||
155 | $context['level_effects'] = [ |
||
156 | 0 => $txt['profile_warning_effect_none'], |
||
157 | ]; |
||
158 | |||
159 | foreach (['watch', 'moderate', 'mute'] as $status) |
||
160 | { |
||
161 | if ($modSettings['warning_' . $status] != 110) |
||
162 | { |
||
163 | $context['level_effects'][$modSettings['warning_' . $status]] = $txt['profile_warning_effect_' . $status]; |
||
164 | } |
||
165 | } |
||
166 | |||
167 | $context['current_level'] = 0; |
||
168 | |||
169 | foreach ($context['level_effects'] as $limit => $dummy) |
||
170 | { |
||
171 | if ($context['member']['warning'] >= $limit) |
||
172 | { |
||
173 | $context['current_level'] = $limit; |
||
174 | } |
||
175 | } |
||
176 | |||
177 | // Build a listing to view the previous warnings for this user |
||
178 | $this->_create_issued_warnings_list(); |
||
179 | |||
180 | $warning_for_message = $this->_req->getQuery('msg', 'intval', false); |
||
181 | $warned_message_subject = ''; |
||
182 | |||
183 | // Are they warning because of a message? |
||
184 | if (!empty($warning_for_message) && $warning_for_message > 0) |
||
185 | { |
||
186 | require_once(SUBSDIR . '/Messages.subs.php'); |
||
187 | $message = basicMessageInfo($warning_for_message); |
||
188 | |||
189 | if (!empty($message)) |
||
190 | { |
||
191 | $warned_message_subject = $message['subject']; |
||
192 | } |
||
193 | } |
||
194 | |||
195 | require_once(SUBSDIR . '/Maillist.subs.php'); |
||
196 | |||
197 | // Any custom templates? |
||
198 | $context['notification_templates'] = []; |
||
199 | $notification_templates = maillist_templates('warntpl'); |
||
200 | |||
201 | foreach ($notification_templates as $row) |
||
202 | { |
||
203 | // If we're not warning for a message skip any that are. |
||
204 | if ($warning_for_message === false && strpos($row['body'], '{MESSAGE}') !== false) |
||
205 | { |
||
206 | continue; |
||
207 | } |
||
208 | |||
209 | $context['notification_templates'][] = [ |
||
210 | 'title' => $row['title'], |
||
211 | 'body' => $row['body'], |
||
212 | ]; |
||
213 | } |
||
214 | |||
215 | // Setup the "default" templates. |
||
216 | foreach (['spamming', 'offence', 'insulting'] as $type) |
||
217 | { |
||
218 | $context['notification_templates'][] = [ |
||
219 | 'title' => $txt['profile_warning_notify_title_' . $type], |
||
220 | 'body' => sprintf($txt['profile_warning_notify_template_outline' . (empty($warning_for_message) ? '' : '_post')], $txt['profile_warning_notify_for_' . $type]), |
||
221 | ]; |
||
222 | } |
||
223 | |||
224 | // Replace all the common variables in the templates. |
||
225 | foreach ($context['notification_templates'] as $k => $name) |
||
226 | { |
||
227 | $context['notification_templates'][$k]['body'] = strtr($name['body'], |
||
228 | [ |
||
229 | '{MEMBER}' => un_htmlspecialchars($context['member']['name']), |
||
230 | '{MESSAGE}' => '[url=' . getUrl('action', ['msg' => $warning_for_message]) . ']' . un_htmlspecialchars($warned_message_subject) . '[/url]', |
||
231 | '{SCRIPTURL}' => $scripturl, |
||
232 | '{FORUMNAME}' => $mbname, |
||
233 | '{REGARDS}' => replaceBasicActionUrl($txt['regards_team']) |
||
234 | ] |
||
235 | ); |
||
236 | } |
||
237 | } |
||
238 | |||
239 | /** |
||
240 | * Does the actual issuing of a warning to a member |
||
241 | * |
||
242 | * What it does: |
||
243 | * |
||
244 | * - Validates the inputs |
||
245 | * - Sends the warning PM if required, to the member |
||
246 | * - Logs the action |
||
247 | * - Updates the user data with the new warning level |
||
248 | */ |
||
249 | private function _save_warning() |
||
250 | { |
||
251 | global $txt, $context, $cur_profile; |
||
252 | |||
253 | if (isset($this->_req->post->save)) |
||
254 | { |
||
255 | // Security is good here. |
||
256 | checkSession(); |
||
257 | |||
258 | // There must be a reason, and use of flowery words is allowed. |
||
259 | $warn_reason = $this->_req->getPost('warn_reason', 'trim|\\ElkArte\\Helper\\Util::htmlspecialchars', ''); |
||
260 | if ($warn_reason === '' && !$context['user']['is_owner']) |
||
261 | { |
||
262 | $this->_issueErrors[] = 'warning_no_reason'; |
||
263 | } |
||
264 | |||
265 | // If the value hasn't changed it's either no JS or a real no change (Which this will pass) |
||
266 | if ($warn_reason === 'SAME') |
||
267 | { |
||
268 | $this->_req->post->warning_level = $this->_req->post->warning_level_nojs; |
||
269 | } |
||
270 | |||
271 | // Set and contain the level and level changes |
||
272 | $warning_level = (int) $this->_req->post->warning_level; |
||
273 | $warning_level = max(0, min(100, $warning_level)); |
||
274 | |||
275 | if ($warning_level < $context['min_allowed']) |
||
276 | { |
||
277 | $warning_level = $context['min_allowed']; |
||
278 | } |
||
279 | elseif ($warning_level > $context['max_allowed']) |
||
280 | { |
||
281 | $warning_level = $context['max_allowed']; |
||
282 | } |
||
283 | |||
284 | // We need this to log moderation notices |
||
285 | require_once(SUBSDIR . '/Moderation.subs.php'); |
||
286 | |||
287 | // Do we actually have to issue them with a PM? |
||
288 | $id_notice = $this->_issue_warning_pm(); |
||
289 | |||
290 | // What have we changed? |
||
291 | $level_change = $warning_level - $cur_profile['warning']; |
||
292 | |||
293 | // No errors? Proceed! Only log if you're not the owner. |
||
294 | if (empty($this->_issueErrors)) |
||
295 | { |
||
296 | // Log what we've done! |
||
297 | if (!$context['user']['is_owner']) |
||
298 | { |
||
299 | logWarning($this->_memID, $cur_profile['real_name'], $id_notice, $level_change, $warn_reason); |
||
300 | } |
||
301 | |||
302 | // Make the change. |
||
303 | require_once(SUBSDIR . '/Members.subs.php'); |
||
304 | updateMemberData($this->_memID, ['warning' => $warning_level]); |
||
305 | |||
306 | // Leave a lovely message. |
||
307 | $context['profile_updated'] = $context['user']['is_owner'] ? $txt['profile_updated_own'] : $txt['profile_warning_success']; |
||
308 | } |
||
309 | else |
||
310 | { |
||
311 | // Try to remember some bits. |
||
312 | $context['warning_data'] = [ |
||
313 | 'reason' => $warn_reason, |
||
314 | 'notify' => !empty($this->_req->post->warn_notify), |
||
315 | 'notify_subject' => $this->_req->getPost('warn_sub', 'trim', ''), |
||
316 | 'notify_body' => $this->_req->getPost('warn_body', 'trim', ''), |
||
317 | ]; |
||
318 | } |
||
319 | |||
320 | // Show the new improved warning level. |
||
321 | $context['member']['warning'] = $warning_level; |
||
322 | } |
||
323 | } |
||
324 | |||
325 | /** |
||
326 | * Issue a pm to the member getting the warning |
||
327 | * |
||
328 | * @return int |
||
329 | * @throws Exception |
||
330 | */ |
||
331 | private function _issue_warning_pm() |
||
332 | { |
||
333 | global $context, $modSettings; |
||
334 | |||
335 | $id_notice = 0; |
||
336 | if (!empty($this->_req->post->warn_notify) && empty($this->_issueErrors)) |
||
337 | { |
||
338 | $warn_sub = $this->_req->getPost('warn_sub', 'trim', ''); |
||
339 | $warn_body = $this->_req->getPost('warn_body', 'trim', ''); |
||
340 | |||
341 | if (empty($warn_sub) || empty($warn_body)) |
||
342 | { |
||
343 | $this->_issueErrors[] = 'warning_notify_blank'; |
||
344 | } |
||
345 | // Send the PM? |
||
346 | else |
||
347 | { |
||
348 | require_once(SUBSDIR . '/PersonalMessage.subs.php'); |
||
349 | $from = [ |
||
350 | 'id' => 0, |
||
351 | 'name' => $context['forum_name'], |
||
352 | 'username' => $context['forum_name'], |
||
353 | ]; |
||
354 | |||
355 | // No sense in sending a PbE |
||
356 | $modSettings['maillist_enabled'] = 0; |
||
357 | sendpm(['to' => [$this->_memID], 'bcc' => []], $warn_sub, $warn_body, false, $from); |
||
358 | |||
359 | // Log the notice. |
||
360 | $id_notice = logWarningNotice($warn_sub, $warn_body); |
||
361 | } |
||
362 | } |
||
363 | |||
364 | return $id_notice; |
||
365 | } |
||
366 | |||
367 | /** |
||
368 | * Prepares a warning preview |
||
369 | */ |
||
370 | private function _preview_warning() |
||
371 | { |
||
372 | global $context; |
||
373 | |||
374 | if (isset($this->_req->post->preview)) |
||
375 | { |
||
376 | $warning_body = empty($this->_req->post->warn_body) ? '' : trim(censor($this->_req->post->warn_body)); |
||
377 | |||
378 | if (empty($this->_req->post->warn_sub) || empty($this->_req->post->warn_body)) |
||
379 | { |
||
380 | $this->_issueErrors[] = 'warning_notify_blank'; |
||
381 | } |
||
382 | |||
383 | if (!empty($this->_req->post->warn_body)) |
||
384 | { |
||
385 | require_once(SUBSDIR . '/Post.subs.php'); |
||
386 | |||
387 | $bbc_parser = ParserWrapper::instance(); |
||
388 | preparsecode($warning_body); |
||
389 | $warning_body = $bbc_parser->parseNotice($warning_body); |
||
390 | } |
||
391 | |||
392 | // Try to remember some bits. |
||
393 | $context['preview_subject'] = $this->_req->getPost('warn_sub', 'trim|\\ElkArte\\Helper\\Util::htmlspecialchars', ''); |
||
394 | $context['warning_data'] = [ |
||
395 | 'reason' => $this->_req->post->warn_reason, |
||
396 | 'notify' => !empty($this->_req->post->warn_notify), |
||
397 | 'notify_subject' => $this->_req->getPost('warn_sub', 'trim', ''), |
||
398 | 'notify_body' => $this->_req->getPost('warn_body', 'trim', ''), |
||
399 | 'body_preview' => $warning_body, |
||
400 | ]; |
||
401 | } |
||
402 | } |
||
403 | |||
404 | /** |
||
405 | * Creates the listing of issued warnings |
||
406 | */ |
||
407 | private function _create_issued_warnings_list() |
||
408 | { |
||
409 | global $txt, $modSettings; |
||
410 | |||
411 | $listOptions = [ |
||
412 | 'id' => 'issued_warnings', |
||
413 | 'title' => $txt['profile_viewwarning_previous_warnings'], |
||
414 | 'items_per_page' => $modSettings['defaultMaxMessages'], |
||
415 | 'no_items_label' => $txt['profile_viewwarning_no_warnings'], |
||
416 | 'base_href' => getUrl('action', ['action' => 'profile', 'area' => 'issuewarning', 'sa' => 'user', 'u' => $this->_memID]), |
||
417 | 'default_sort_col' => 'log_time', |
||
418 | 'get_items' => [ |
||
419 | 'function' => fn($start, $items_per_page, $sort) => $this->list_getUserWarnings($start, $items_per_page, $sort), |
||
420 | ], |
||
421 | 'get_count' => [ |
||
422 | 'function' => fn() => $this->list_getUserWarningCount(), |
||
423 | ], |
||
424 | 'columns' => [ |
||
425 | 'issued_by' => [ |
||
426 | 'header' => [ |
||
427 | 'value' => $txt['profile_warning_previous_issued'], |
||
428 | 'class' => 'grid20', |
||
429 | ], |
||
430 | 'data' => [ |
||
431 | 'function' => static fn($warning) => $warning['issuer']['link'], |
||
432 | ], |
||
433 | 'sort' => [ |
||
434 | 'default' => 'lc.member_name DESC', |
||
435 | 'reverse' => 'lc.member_name', |
||
436 | ], |
||
437 | ], |
||
438 | 'log_time' => [ |
||
439 | 'header' => [ |
||
440 | 'value' => $txt['profile_warning_previous_time'], |
||
441 | 'class' => 'grid30', |
||
442 | ], |
||
443 | 'data' => [ |
||
444 | 'db' => 'time', |
||
445 | ], |
||
446 | 'sort' => [ |
||
447 | 'default' => 'lc.log_time DESC', |
||
448 | 'reverse' => 'lc.log_time', |
||
449 | ], |
||
450 | ], |
||
451 | 'reason' => [ |
||
452 | 'header' => [ |
||
453 | 'value' => $txt['profile_warning_previous_reason'], |
||
454 | ], |
||
455 | 'data' => [ |
||
456 | 'function' => static function ($warning) { |
||
457 | global $txt; |
||
458 | |||
459 | $ret = ' |
||
460 | <div class="floatleft"> |
||
461 | ' . $warning['reason'] . ' |
||
462 | </div>'; |
||
463 | |||
464 | // If a notice was sent, provide a way to view it |
||
465 | if (!empty($warning['id_notice'])) |
||
466 | { |
||
467 | $ret .= ' |
||
468 | <div class="floatright"> |
||
469 | <a href="' . getUrl('action', ['action' => 'moderate', 'area' => 'notice', 'nid' => $warning['id_notice']]) . '" onclick="window.open(this.href, \'\', \'scrollbars=yes,resizable=yes,width=400,height=250\');return false;" target="_blank" class="new_win" title="' . $txt['profile_warning_previous_notice'] . '"><i class="icon icon-small i-search"></i></a> |
||
470 | </div>'; |
||
471 | } |
||
472 | |||
473 | return $ret; |
||
474 | }, |
||
475 | ], |
||
476 | ], |
||
477 | 'level' => [ |
||
478 | 'header' => [ |
||
479 | 'value' => $txt['profile_warning_previous_level'], |
||
480 | 'class' => 'grid8', |
||
481 | ], |
||
482 | 'data' => [ |
||
483 | 'db' => 'counter', |
||
484 | ], |
||
485 | 'sort' => [ |
||
486 | 'default' => 'lc.counter DESC', |
||
487 | 'reverse' => 'lc.counter', |
||
488 | ], |
||
489 | ], |
||
490 | ], |
||
491 | ]; |
||
492 | |||
493 | // Create the list for viewing. |
||
494 | createList($listOptions); |
||
495 | } |
||
496 | |||
497 | /** |
||
498 | * Callback for createList(). Called by action_hooks |
||
499 | * |
||
500 | * @param int $start The item to start with (for pagination purposes) |
||
501 | * @param int $items_per_page The number of items to show per page |
||
502 | * @param string $sort A string indicating how to sort the results |
||
503 | * |
||
504 | * @return array |
||
505 | */ |
||
506 | public function list_getUserWarnings($start, $items_per_page, $sort) |
||
509 | } |
||
510 | |||
511 | /** |
||
512 | * Simply returns the total count of warnings |
||
513 | * Callback for createList(). |
||
514 | * |
||
515 | * @return int |
||
516 | */ |
||
517 | public function list_getUserWarningCount() |
||
520 | } |
||
521 | |||
522 | /** |
||
523 | * Present a screen to make sure the user wants to be deleted. |
||
524 | */ |
||
525 | public function action_deleteaccount() |
||
526 | { |
||
527 | global $txt, $context, $modSettings, $cur_profile; |
||
528 | |||
529 | if (!$context['user']['is_owner']) |
||
530 | { |
||
531 | isAllowedTo('profile_remove_any'); |
||
532 | } |
||
533 | elseif (!allowedTo('profile_remove_any')) |
||
534 | { |
||
535 | isAllowedTo('profile_remove_own'); |
||
536 | } |
||
537 | |||
538 | // Permissions for removing stuff... |
||
539 | $context['can_delete_posts'] = !$context['user']['is_owner'] && allowedTo('moderate_forum'); |
||
540 | |||
541 | // Can they do this, or will they need approval? |
||
542 | $context['needs_approval'] = $context['user']['is_owner'] && !empty($modSettings['approveAccountDeletion']) && !allowedTo('moderate_forum'); |
||
543 | $context['page_title'] = $txt['deleteAccount'] . ': ' . $cur_profile['real_name']; |
||
544 | |||
545 | // make sure the sub-template is set... |
||
546 | theme()->getTemplates()->load('ProfileAccount'); |
||
547 | $context['sub_template'] = 'deleteAccount'; |
||
548 | } |
||
549 | |||
550 | /** |
||
551 | * Actually delete an account. |
||
552 | */ |
||
553 | public function action_deleteaccount2() |
||
649 | } |
||
650 | } |
||
651 | |||
652 | /** |
||
653 | * Activate an account. |
||
654 | * |
||
655 | * - This function is called from the profile account actions area. |
||
656 | */ |
||
657 | public function action_activateaccount() |
||
695 | } |
||
696 | } |
||
697 |
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
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. 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.