Total Complexity | 131 |
Total Lines | 1267 |
Duplicated Lines | 0 % |
Changes | 1 | ||
Bugs | 0 | Features | 0 |
Complex classes like ProfileOptions 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 ProfileOptions, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
36 | class ProfileOptions extends AbstractController |
||
37 | { |
||
38 | /** @var int Member id for the profile being viewed */ |
||
39 | private $_memID = 0; |
||
40 | |||
41 | /** @var Member The \ElkArte\Member object is stored here to avoid some global */ |
||
42 | private $_profile; |
||
43 | |||
44 | /** |
||
45 | * Called before all other methods when coming from the dispatcher or |
||
46 | * action class. |
||
47 | * |
||
48 | * - If you initiate the class outside those methods, call this method. |
||
49 | * or setup the class yourself else a horrible fate awaits you |
||
50 | */ |
||
51 | public function pre_dispatch() |
||
52 | { |
||
53 | $this->_memID = currentMemberID(); |
||
54 | $this->_profile = MembersList::get($this->_memID); |
||
|
|||
55 | } |
||
56 | |||
57 | /** |
||
58 | * Default method, if another action is not called by the menu. |
||
59 | * |
||
60 | * @see AbstractController::action_index() |
||
61 | */ |
||
62 | public function action_index() |
||
63 | { |
||
64 | // action_account() is the first to do |
||
65 | // these subactions are mostly routed to from the profile |
||
66 | // menu though. |
||
67 | } |
||
68 | |||
69 | /** |
||
70 | * Show all the users buddies, as well as a add/delete interface. |
||
71 | * |
||
72 | * @throws Exception |
||
73 | */ |
||
74 | public function action_editBuddyIgnoreLists() |
||
75 | { |
||
76 | global $context, $txt, $modSettings; |
||
77 | |||
78 | // Do a quick check to ensure people aren't getting here illegally! |
||
79 | if (!$context['user']['is_owner'] || empty($modSettings['enable_buddylist'])) |
||
80 | { |
||
81 | throw new Exception('no_access', false); |
||
82 | } |
||
83 | |||
84 | theme()->getTemplates()->load('ProfileOptions'); |
||
85 | |||
86 | // Can we email the user direct? |
||
87 | $context['can_moderate_forum'] = allowedTo('moderate_forum'); |
||
88 | $context['can_send_email'] = allowedTo('send_email_to_members'); |
||
89 | |||
90 | $subActions = [ |
||
91 | 'buddies' => [$this, 'action_editBuddies'], |
||
92 | 'ignore' => [$this, 'action_editIgnoreList'], |
||
93 | ]; |
||
94 | |||
95 | // Set a subaction |
||
96 | $action = new Action('buddy_actions'); |
||
97 | $subAction = $action->initialize($subActions, 'buddies'); |
||
98 | |||
99 | // Create the tabs for the template. |
||
100 | $context[$context['profile_menu_name']]['object']->prepareTabData([ |
||
101 | 'title' => $txt['editBuddyIgnoreLists'], |
||
102 | 'description' => $txt['buddy_ignore_desc'], |
||
103 | 'class' => 'i-user', |
||
104 | ]); |
||
105 | |||
106 | // Pass on to the actual function. |
||
107 | $action->dispatch($subAction); |
||
108 | } |
||
109 | |||
110 | /** |
||
111 | * Show all the users buddies, as well as an add/delete interface. |
||
112 | * |
||
113 | * @uses template_editBuddies() |
||
114 | */ |
||
115 | public function action_editBuddies() |
||
116 | { |
||
117 | global $context; |
||
118 | |||
119 | theme()->getTemplates()->load('ProfileOptions'); |
||
120 | |||
121 | // We want to view what we're doing :P |
||
122 | $context['sub_template'] = 'editBuddies'; |
||
123 | |||
124 | // Use suggest finding the right buddies |
||
125 | loadJavascriptFile('suggest.js', array('defer' => true)); |
||
126 | |||
127 | // For making changes! |
||
128 | $buddiesArray = array_map('intval', explode(',', $this->_profile['buddy_list'])); |
||
129 | $buddiesArray = array_filter($buddiesArray, static fn($value) => $value !== ''); |
||
130 | |||
131 | // Removing a buddy? |
||
132 | $notMyBuddy = $this->_req->getQuery('remove', 'intval'); |
||
133 | if ($notMyBuddy !== null) |
||
134 | { |
||
135 | checkSession('get'); |
||
136 | |||
137 | call_integration_hook('integrate_remove_buddy', [$this->_memID]); |
||
138 | |||
139 | $key = array_search($notMyBuddy, $buddiesArray, true); |
||
140 | if ($key !== false) |
||
141 | { |
||
142 | unset($buddiesArray[$key]); |
||
143 | } |
||
144 | |||
145 | // Make the changes. |
||
146 | $this->_profile['buddy_list'] = implode(',', $buddiesArray); |
||
147 | require_once(SUBSDIR . '/Members.subs.php'); |
||
148 | updateMemberData($this->_memID, ['buddy_list' => $this->_profile['buddy_list']]); |
||
149 | |||
150 | // Redirect off the page because we don't like all this ugly query stuff to stick in the history. |
||
151 | redirectexit('action=profile;area=lists;sa=buddies;u=' . $this->_memID); |
||
152 | } |
||
153 | // Or adding a new one |
||
154 | elseif (isset($this->_req->post->new_buddy)) |
||
155 | { |
||
156 | checkSession(); |
||
157 | |||
158 | // Prepare the string for extraction... |
||
159 | $new_buddy = strtr($this->_req->getPost('new_buddy', 'trim|htmlspecialchars[ENT_QUOTES]'), ['"' => '"']); |
||
160 | if ($new_buddy === '' || in_array($new_buddy, [$this->_profile['member_name'], $this->_profile['real_name']], true)) |
||
161 | { |
||
162 | unset($new_buddy); |
||
163 | } |
||
164 | |||
165 | call_integration_hook('integrate_add_buddies', [$this->_memID, &$new_buddies]); |
||
166 | |||
167 | if (!empty($new_buddy)) |
||
168 | { |
||
169 | // Now find out the id_member of the buddy. |
||
170 | require_once(SUBSDIR . '/ProfileOptions.subs.php'); |
||
171 | $new_buddiesArray = getBuddiesID([$new_buddy]); |
||
172 | $old_buddiesArray = explode(',', $this->_profile['buddy_list']); |
||
173 | |||
174 | // Now update the current users buddy list. |
||
175 | $this->_profile['buddy_list'] = implode(',', array_filter(array_unique(array_merge($new_buddiesArray, $old_buddiesArray)))); |
||
176 | |||
177 | require_once(SUBSDIR . '/Members.subs.php'); |
||
178 | updateMemberData($this->_memID, ['buddy_list' => $this->_profile['buddy_list']]); |
||
179 | } |
||
180 | |||
181 | // Back to the buddy list! |
||
182 | redirectexit('action=profile;area=lists;sa=buddies;u=' . $this->_memID); |
||
183 | } |
||
184 | |||
185 | // Get all the users "buddies"... |
||
186 | $buddies = []; |
||
187 | |||
188 | if (!empty($buddiesArray)) |
||
189 | { |
||
190 | require_once(SUBSDIR . '/Members.subs.php'); |
||
191 | $result = getBasicMemberData($buddiesArray, ['sort' => 'real_name', 'limit' => substr_count($this->_profile['buddy_list'], ',') + 1]); |
||
192 | foreach ($result as $row) |
||
193 | { |
||
194 | $buddies[] = (int) $row['id_member']; |
||
195 | } |
||
196 | } |
||
197 | |||
198 | $context['buddy_count'] = count($buddies); |
||
199 | |||
200 | // Load all the members up. |
||
201 | MembersList::load($buddies, false, 'profile'); |
||
202 | |||
203 | // Set the context for each buddy. |
||
204 | $context['buddies'] = []; |
||
205 | foreach ($buddies as $buddy) |
||
206 | { |
||
207 | $context['buddies'][$buddy] = MembersList::get($buddy); |
||
208 | $context['buddies'][$buddy]->loadContext(); |
||
209 | } |
||
210 | |||
211 | call_integration_hook('integrate_view_buddies', [$this->_memID]); |
||
212 | } |
||
213 | |||
214 | /** |
||
215 | * Allows the user to view their ignore list, |
||
216 | * |
||
217 | * - Provides the option to manage members on it. |
||
218 | */ |
||
219 | public function action_editIgnoreList() |
||
220 | { |
||
221 | global $context; |
||
222 | |||
223 | theme()->getTemplates()->load('ProfileOptions'); |
||
224 | |||
225 | // We want to view what we're doing :P |
||
226 | $context['sub_template'] = 'editIgnoreList'; |
||
227 | loadJavascriptFile('suggest.js', array('defer' => true)); |
||
228 | |||
229 | // For making changes! |
||
230 | $ignoreArray = array_map('intval', explode(',', $this->_profile['pm_ignore_list'])); |
||
231 | $ignoreArray = array_filter($ignoreArray, static fn($value) => $value !== ''); |
||
232 | |||
233 | // Removing a member from the ignore list? |
||
234 | $id_remove = $this->_req->getQuery('remove', 'intval'); |
||
235 | if (isset($id_remove)) |
||
236 | { |
||
237 | checkSession('get'); |
||
238 | |||
239 | // Heh, I'm lazy, do it the easy way... |
||
240 | $key = array_search($id_remove, $ignoreArray, true); |
||
241 | if ($key !== false) |
||
242 | { |
||
243 | unset($ignoreArray[$key]); |
||
244 | } |
||
245 | |||
246 | // Make the changes. |
||
247 | $this->_profile['pm_ignore_list'] = implode(',', $ignoreArray); |
||
248 | require_once(SUBSDIR . '/Members.subs.php'); |
||
249 | updateMemberData($this->_memID, ['pm_ignore_list' => $this->_profile['pm_ignore_list']]); |
||
250 | |||
251 | // Redirect off the page because we don't like all this ugly query stuff |
||
252 | // to stick in the history. |
||
253 | redirectexit('action=profile;area=lists;sa=ignore;u=' . $this->_memID); |
||
254 | } |
||
255 | elseif (isset($this->_req->post->new_ignore)) |
||
256 | { |
||
257 | checkSession(); |
||
258 | |||
259 | // Prepare the string for extraction... |
||
260 | $new_ignore = strtr($this->_req->getPost('new_ignore', 'trim|htmlspecialchars[ENT_QUOTES]'), ['"' => '"']); |
||
261 | if ($new_ignore === '' || in_array($new_ignore, [$this->_profile['member_name'], $this->_profile['real_name']], true)) |
||
262 | { |
||
263 | unset($new_ignore); |
||
264 | } |
||
265 | |||
266 | if (!empty($new_ignore)) |
||
267 | { |
||
268 | // Now find out the id_member for the members in question. |
||
269 | require_once(SUBSDIR . '/ProfileOptions.subs.php'); |
||
270 | $ignoreArray = array_merge($ignoreArray, getBuddiesID([$new_ignore], false)); |
||
271 | |||
272 | // Now update the current users buddy list. |
||
273 | $this->_profile['pm_ignore_list'] = implode(',', $ignoreArray); |
||
274 | require_once(SUBSDIR . '/Members.subs.php'); |
||
275 | updateMemberData($this->_memID, ['pm_ignore_list' => $this->_profile['pm_ignore_list']]); |
||
276 | } |
||
277 | |||
278 | // Back to the list of pitiful people! |
||
279 | redirectexit('action=profile;area=lists;sa=ignore;u=' . $this->_memID); |
||
280 | } |
||
281 | |||
282 | // Initialise the list of members we're ignoring. |
||
283 | $ignored = []; |
||
284 | |||
285 | if (!empty($ignoreArray)) |
||
286 | { |
||
287 | require_once(SUBSDIR . '/Members.subs.php'); |
||
288 | $result = getBasicMemberData($ignoreArray, ['sort' => 'real_name', 'limit' => substr_count($this->_profile['pm_ignore_list'], ',') + 1]); |
||
289 | foreach ($result as $row) |
||
290 | { |
||
291 | $ignored[] = (int) $row['id_member']; |
||
292 | } |
||
293 | } |
||
294 | |||
295 | $context['ignore_count'] = count($ignored); |
||
296 | |||
297 | // Load all the members up. |
||
298 | MembersList::load($ignored, false, 'profile'); |
||
299 | |||
300 | // Set the context for everyone we ignore. |
||
301 | $context['ignore_list'] = []; |
||
302 | foreach ($ignored as $ignore_member) |
||
303 | { |
||
304 | $context['ignore_list'][$ignore_member] = MembersList::get($ignore_member); |
||
305 | $context['ignore_list'][$ignore_member]->loadContext(); |
||
306 | } |
||
307 | } |
||
308 | |||
309 | /** |
||
310 | * Allows the user to see or change their account info. |
||
311 | */ |
||
312 | public function action_account() |
||
313 | { |
||
314 | global $modSettings, $context, $txt; |
||
315 | |||
316 | theme()->getTemplates()->load('ProfileOptions'); |
||
317 | $this->loadThemeOptions(); |
||
318 | |||
319 | if (allowedTo(['profile_identity_own', 'profile_identity_any'])) |
||
320 | { |
||
321 | $profileFields = new ProfileFields(); |
||
322 | $profileFields->loadCustomFields($this->_memID, 'account'); |
||
323 | } |
||
324 | |||
325 | $context['sub_template'] = 'edit_options'; |
||
326 | $context['page_desc'] = $txt['account_info']; |
||
327 | |||
328 | if (!empty($modSettings['enableOTP'])) |
||
329 | { |
||
330 | $fields = self::getFields('account_otp'); |
||
331 | setupProfileContext($fields['fields'], $fields['hook']); |
||
332 | |||
333 | loadJavascriptFile('ext/qrcode.js'); |
||
334 | $context['load_google_authenticator'] = true; |
||
335 | } |
||
336 | else |
||
337 | { |
||
338 | $fields = self::getFields('account'); |
||
339 | } |
||
340 | |||
341 | setupProfileContext($fields['fields'], $fields['hook']); |
||
342 | } |
||
343 | |||
344 | /** |
||
345 | * Load the options for a user. |
||
346 | */ |
||
347 | public function loadThemeOptions() |
||
348 | { |
||
349 | global $context, $cur_profile, $options; |
||
350 | |||
351 | $default_options = $this->_req->getPost('default_options'); |
||
352 | $post_options = $this->_req->getPost('options'); |
||
353 | if (isset($default_options)) |
||
354 | { |
||
355 | $post_options = isset($post_options) ? $post_options + $default_options : $default_options; |
||
356 | } |
||
357 | |||
358 | if ($context['user']['is_owner']) |
||
359 | { |
||
360 | $context['member']['options'] = $options + $this->_profile->options; |
||
361 | |||
362 | if (isset($post_options) && is_array($post_options)) |
||
363 | { |
||
364 | foreach ($post_options as $k => $v) |
||
365 | { |
||
366 | $context['member']['options'][$k] = $v; |
||
367 | } |
||
368 | } |
||
369 | } |
||
370 | else |
||
371 | { |
||
372 | require_once(SUBSDIR . '/Themes.subs.php'); |
||
373 | $context['member']['options'] = loadThemeOptionsInto( |
||
374 | [1, (int) $cur_profile['id_theme']], |
||
375 | [-1, $this->_memID], $context['member']['options'] |
||
376 | ); |
||
377 | |||
378 | if (isset($post_options)) |
||
379 | { |
||
380 | foreach ($post_options as $var => $val) |
||
381 | { |
||
382 | $context['member']['options'][$var] = $val; |
||
383 | } |
||
384 | } |
||
385 | } |
||
386 | } |
||
387 | |||
388 | /** |
||
389 | * Returns the profile fields for a given area |
||
390 | * |
||
391 | * @param string $area |
||
392 | * @return array |
||
393 | */ |
||
394 | public static function getFields($area) |
||
395 | { |
||
396 | global $modSettings; |
||
397 | |||
398 | $fields = [ |
||
399 | 'account' => [ |
||
400 | 'fields' => [ |
||
401 | 'member_name', 'real_name', 'date_registered', 'posts', 'lngfile', 'hr', |
||
402 | 'id_group', 'hr', |
||
403 | 'email_address', 'show_online', 'hr', |
||
404 | 'passwrd1', 'passwrd2', 'hr', |
||
405 | 'secret_question', 'secret_answer', |
||
406 | ], |
||
407 | 'hook' => 'account' |
||
408 | ], |
||
409 | 'account_otp' => [ |
||
410 | 'fields' => [ |
||
411 | 'member_name', 'real_name', 'date_registered', 'posts', 'lngfile', 'hr', |
||
412 | 'id_group', 'hr', |
||
413 | 'email_address', 'show_online', 'hr', |
||
414 | 'passwrd1', 'passwrd2', 'hr', |
||
415 | 'secret_question', 'secret_answer', 'hr', |
||
416 | 'enable_otp', 'otp_secret', 'hr' |
||
417 | ], |
||
418 | 'hook' => 'account' |
||
419 | ], |
||
420 | 'forumprofile' => [ |
||
421 | 'fields' => [ |
||
422 | 'avatar_choice', 'hr', |
||
423 | 'bday1', 'usertitle', 'hr', |
||
424 | 'signature', 'hr', |
||
425 | 'karma_good', 'hr', |
||
426 | 'website_title', 'website_url', |
||
427 | ], |
||
428 | 'hook' => 'forum' |
||
429 | ], |
||
430 | 'theme' => [ |
||
431 | 'fields' => [ |
||
432 | 'id_theme', 'smiley_set', 'hr', |
||
433 | 'time_format', 'time_offset', 'hr', |
||
434 | 'theme_settings', |
||
435 | ], |
||
436 | 'hook' => 'themepick' |
||
437 | ], |
||
438 | 'contactprefs' => [ |
||
439 | 'fields' => [ |
||
440 | 'receive_from', |
||
441 | 'hr', |
||
442 | 'pm_settings', |
||
443 | ], |
||
444 | 'hook' => 'pmprefs' |
||
445 | ], |
||
446 | 'registration' => [ |
||
447 | 'fields' => empty($modSettings['registration_fields']) ? [] : explode(',', $modSettings['registration_fields']), |
||
448 | 'hook' => 'registration' |
||
449 | ] |
||
450 | ]; |
||
451 | |||
452 | return $fields[$area] ?? []; |
||
453 | } |
||
454 | |||
455 | /** |
||
456 | * Allow the user to change the forum options in their profile. |
||
457 | */ |
||
458 | public function action_forumProfile() |
||
459 | { |
||
460 | global $context, $txt; |
||
461 | |||
462 | theme()->getTemplates()->load('ProfileOptions'); |
||
463 | $this->loadThemeOptions(); |
||
464 | |||
465 | if (allowedTo(['profile_extra_own', 'profile_extra_any'])) |
||
466 | { |
||
467 | $profileFields = new ProfileFields(); |
||
468 | $profileFields->loadCustomFields($this->_memID, 'forumprofile'); |
||
469 | } |
||
470 | |||
471 | $context['sub_template'] = 'edit_options'; |
||
472 | $context['page_desc'] = replaceBasicActionUrl($txt['forumProfile_info']); |
||
473 | $context['show_preview_button'] = true; |
||
474 | |||
475 | $fields = self::getFields('forumprofile'); |
||
476 | setupProfileContext($fields['fields'], $fields['hook']); |
||
477 | } |
||
478 | |||
479 | /** |
||
480 | * Allow the edit of *someone else's* personal message settings. |
||
481 | */ |
||
482 | public function action_pmprefs() |
||
483 | { |
||
484 | global $context, $txt; |
||
485 | |||
486 | $this->loadThemeOptions(); |
||
487 | $profileFields = new ProfileFields(); |
||
488 | $profileFields->loadCustomFields($this->_memID, 'pmprefs'); |
||
489 | theme()->getTemplates()->load('ProfileOptions'); |
||
490 | |||
491 | $context['sub_template'] = 'edit_options'; |
||
492 | $context['page_desc'] = $txt['pm_settings_desc']; |
||
493 | |||
494 | // Set up the profile context and call the 'integrate_pmprefs_profile_fields' hook |
||
495 | $fields = self::getFields('contactprefs'); |
||
496 | setupProfileContext($fields['fields'], $fields['hook']); |
||
497 | } |
||
498 | |||
499 | /** |
||
500 | * Allow the user to pick a theme, Set time formats, and set |
||
501 | * overall look and layout options. |
||
502 | */ |
||
503 | public function action_themepick() |
||
504 | { |
||
505 | global $txt, $context; |
||
506 | |||
507 | $this->loadThemeOptions(); |
||
508 | |||
509 | if (allowedTo(['profile_extra_own', 'profile_extra_any'])) |
||
510 | { |
||
511 | $profileFields = new ProfileFields(); |
||
512 | $profileFields->loadCustomFields($this->_memID, 'theme'); |
||
513 | } |
||
514 | |||
515 | theme()->getTemplates()->load('ProfileOptions'); |
||
516 | |||
517 | $context['sub_template'] = 'edit_options'; |
||
518 | $context['page_desc'] = $txt['theme_info']; |
||
519 | |||
520 | // Set up profile look and layout, call 'integrate_themepick_profile_fields' hook |
||
521 | $fields = self::getFields('theme'); |
||
522 | setupProfileContext($fields['fields'], $fields['hook']); |
||
523 | } |
||
524 | |||
525 | /** |
||
526 | * Choose a theme from a list of those that are available |
||
527 | * |
||
528 | * What it does: |
||
529 | * |
||
530 | * - Uses the Themes template. (pick sub template.) |
||
531 | * - Accessed with ?action=admin;area=theme;sa=pick. |
||
532 | * - Allows previewing of the theme and variants |
||
533 | */ |
||
534 | public function action_pick() |
||
614 | } |
||
615 | |||
616 | /** |
||
617 | * Display the notification settings for the user and allow changes. |
||
618 | */ |
||
619 | public function action_notification() |
||
620 | { |
||
621 | global $txt, $context; |
||
622 | |||
623 | theme()->getTemplates()->load('ProfileOptions'); |
||
624 | |||
625 | require_once(SUBSDIR . '/Profile.subs.php'); |
||
646 | } |
||
647 | |||
648 | /** |
||
649 | * Generate the users existing notification options and allow for updates |
||
650 | */ |
||
651 | public function action_editNotificationSettings() |
||
652 | { |
||
653 | global $context; |
||
654 | |||
655 | // Show the list of notification types and how they can subscribe to them |
||
656 | $context['mention_types'] = getMemberNotificationsProfile($this->_memID); |
||
657 | |||
658 | // What options are set? |
||
659 | $context['member']['notify_announcements'] = $this->_profile['notify_announcements']; |
||
660 | $context['member']['notify_send_body'] = $this->_profile['notify_send_body']; |
||
661 | $context['member']['notify_types'] = $this->_profile['notify_types']; |
||
662 | $context['member']['notify_regularity'] = $this->_profile['notify_regularity']; |
||
663 | $context['member']['notify_from'] = $this->_profile['notify_from']; |
||
664 | |||
665 | $this->loadThemeOptions(); |
||
666 | } |
||
667 | |||
668 | /** |
||
669 | * Generate the users existing board notification list. |
||
670 | * Loads data into $context to be displayed wth template_board_notification_list |
||
671 | */ |
||
672 | public function action_editNotificationBoards() |
||
673 | { |
||
674 | global $txt, $scripturl, $context; |
||
675 | |||
676 | require_once(SUBSDIR . '/Boards.subs.php'); |
||
677 | |||
678 | $context['mention_types'] = getMemberNotificationsProfile($this->_memID); |
||
679 | unset($context['sub_template']); |
||
680 | |||
681 | // Fine, start with the board list. |
||
682 | $listOptions = [ |
||
683 | 'id' => 'board_notification_list', |
||
684 | 'width' => '100%', |
||
685 | 'no_items_label' => $txt['notifications_boards_none'] . '<br /><br />' . $txt['notifications_boards_howto'], |
||
686 | 'no_items_align' => 'left', |
||
687 | 'base_href' => $scripturl . '?action=profile;u=' . $this->_memID . ';area=notification', |
||
688 | 'default_sort_col' => 'board_name', |
||
689 | 'get_items' => [ |
||
690 | 'function' => fn($start, $items_per_page, $sort, $memID) => $this->list_getBoardNotifications($start, $items_per_page, $sort, $memID), |
||
691 | 'params' => [ |
||
692 | $this->_memID, |
||
693 | ], |
||
694 | ], |
||
695 | 'columns' => [ |
||
696 | 'board_name' => [ |
||
697 | 'header' => [ |
||
698 | 'value' => $txt['notifications_boards'], |
||
699 | 'class' => 'lefttext', |
||
700 | ], |
||
701 | 'data' => [ |
||
702 | 'function' => static function ($board) { |
||
703 | global $txt; |
||
704 | |||
705 | $link = $board['link']; |
||
706 | if ($board['new']) |
||
707 | { |
||
708 | $link .= ' <a href="' . $board['href'] . '" class="new_posts">' . $txt['new'] . '</a>'; |
||
709 | } |
||
710 | |||
711 | return $link; |
||
712 | }, |
||
713 | ], |
||
714 | 'sort' => [ |
||
715 | 'default' => 'name', |
||
716 | 'reverse' => 'name DESC', |
||
717 | ], |
||
718 | ], |
||
719 | 'delete' => [ |
||
720 | 'header' => [ |
||
721 | 'value' => '<input type="checkbox" onclick="invertAll(this, this.form);" />', |
||
722 | 'class' => 'centertext', |
||
723 | 'style' => 'width:4%;', |
||
724 | ], |
||
725 | 'data' => [ |
||
726 | 'sprintf' => [ |
||
727 | 'format' => '<input type="checkbox" name="notify_boards[]" value="%1$d" %2$s />', |
||
728 | 'params' => [ |
||
729 | 'id' => false, |
||
730 | 'checked' => false, |
||
731 | ], |
||
732 | ], |
||
733 | 'class' => 'centertext', |
||
734 | ], |
||
735 | ], |
||
736 | ], |
||
737 | 'form' => [ |
||
738 | 'href' => $scripturl . '?action=profile;area=notification;sa=boards', |
||
739 | 'include_sort' => true, |
||
740 | 'include_start' => true, |
||
741 | 'hidden_fields' => [ |
||
742 | 'u' => $this->_memID, |
||
743 | 'sa' => $context['menu_item_selected'], |
||
744 | $context['session_var'] => $context['session_id'], |
||
745 | ], |
||
746 | 'token' => $context['token_check'], |
||
747 | ], |
||
748 | 'additional_rows' => [ |
||
749 | [ |
||
750 | 'class' => 'submitbutton', |
||
751 | 'position' => 'bottom_of_list', |
||
752 | 'value' => ' |
||
753 | <input type="submit" name="edit_notify_boards" value="' . $txt['notifications_boards_update'] . '" /> |
||
754 | <input type="hidden" name="save" value="save" />', |
||
755 | ], |
||
756 | [ |
||
757 | 'position' => 'after_title', |
||
758 | 'value' => getBoardNotificationsCount($this->_memID) == 0 ? $txt['notifications_boards_none'] . '<br />' . $txt['notifications_boards_howto'] : $txt['notifications_boards_current'], |
||
759 | ], |
||
760 | ], |
||
761 | ]; |
||
762 | |||
763 | // Create the board notification list. |
||
764 | createList($listOptions); |
||
765 | } |
||
766 | |||
767 | /** |
||
768 | * Generate the users existing topic notification list. |
||
769 | * Loads data into $context to be displayed wth template_topic_notification_list |
||
770 | */ |
||
771 | public function action_editNotificationTopics() |
||
772 | { |
||
773 | global $txt, $scripturl, $context, $modSettings; |
||
774 | |||
775 | require_once(SUBSDIR . '/Topic.subs.php'); |
||
776 | |||
777 | $context['mention_types'] = getMemberNotificationsProfile($this->_memID); |
||
778 | unset($context['sub_template']); |
||
779 | |||
780 | // Now do the topic notifications. |
||
781 | $listOptions = [ |
||
782 | 'id' => 'topic_notification_list', |
||
783 | 'width' => '100%', |
||
784 | 'items_per_page' => $modSettings['defaultMaxMessages'], |
||
785 | 'no_items_label' => $txt['notifications_topics_none'] . '<br /><br />' . $txt['notifications_topics_howto'], |
||
786 | 'no_items_align' => 'left', |
||
787 | 'base_href' => $scripturl . '?action=profile;u=' . $this->_memID . ';area=notification', |
||
788 | 'default_sort_col' => 'last_post', |
||
789 | 'get_items' => [ |
||
790 | 'function' => fn($start, $items_per_page, $sort, $memID) => $this->list_getTopicNotifications($start, $items_per_page, $sort, $memID), |
||
791 | 'params' => [ |
||
792 | $this->_memID, |
||
793 | ], |
||
794 | ], |
||
795 | 'get_count' => [ |
||
796 | 'function' => fn($memID) => $this->list_getTopicNotificationCount($memID), |
||
797 | 'params' => [ |
||
798 | $this->_memID, |
||
799 | ], |
||
800 | ], |
||
801 | 'columns' => [ |
||
802 | 'subject' => [ |
||
803 | 'header' => [ |
||
804 | 'value' => $txt['notifications_topics'], |
||
805 | 'class' => 'lefttext', |
||
806 | ], |
||
807 | 'data' => [ |
||
808 | 'function' => static function ($topic) { |
||
809 | global $txt; |
||
810 | |||
811 | $link = $topic['link']; |
||
812 | if ($topic['new']) |
||
813 | { |
||
814 | $link .= ' <a href="' . $topic['new_href'] . '" class="new_posts">' . $txt['new'] . '</a>'; |
||
815 | } |
||
816 | |||
817 | return $link . ('<br /><span class="smalltext"><em>' . $txt['in'] . ' ' . $topic['board_link'] . '</em></span>'); |
||
818 | }, |
||
819 | ], |
||
820 | 'sort' => [ |
||
821 | 'default' => 'ms.subject', |
||
822 | 'reverse' => 'ms.subject DESC', |
||
823 | ], |
||
824 | ], |
||
825 | 'started_by' => [ |
||
826 | 'header' => [ |
||
827 | 'value' => $txt['started_by'], |
||
828 | 'class' => 'lefttext', |
||
829 | ], |
||
830 | 'data' => [ |
||
831 | 'db' => 'poster_link', |
||
832 | ], |
||
833 | 'sort' => [ |
||
834 | 'default' => 'real_name_col', |
||
835 | 'reverse' => 'real_name_col DESC', |
||
836 | ], |
||
837 | ], |
||
838 | 'last_post' => [ |
||
839 | 'header' => [ |
||
840 | 'value' => $txt['last_post'], |
||
841 | 'class' => 'lefttext', |
||
842 | ], |
||
843 | 'data' => [ |
||
844 | 'sprintf' => [ |
||
845 | 'format' => '<span class="smalltext">%1$s<br />' . $txt['by'] . ' %2$s</span>', |
||
846 | 'params' => [ |
||
847 | 'updated' => false, |
||
848 | 'poster_updated_link' => false, |
||
849 | ], |
||
850 | ], |
||
851 | ], |
||
852 | 'sort' => [ |
||
853 | 'default' => 'ml.id_msg DESC', |
||
854 | 'reverse' => 'ml.id_msg', |
||
855 | ], |
||
856 | ], |
||
857 | 'delete' => [ |
||
858 | 'header' => [ |
||
859 | 'value' => '<input type="checkbox" onclick="invertAll(this, this.form);" />', |
||
860 | 'class' => 'centertext', |
||
861 | 'style' => 'width:4%;', |
||
862 | ], |
||
863 | 'data' => [ |
||
864 | 'sprintf' => [ |
||
865 | 'format' => '<input type="checkbox" name="notify_topics[]" value="%1$d" />', |
||
866 | 'params' => [ |
||
867 | 'id' => false, |
||
868 | ], |
||
869 | ], |
||
870 | 'class' => 'centertext', |
||
871 | ], |
||
872 | ], |
||
873 | ], |
||
874 | 'form' => [ |
||
875 | 'href' => $scripturl . '?action=profile;area=notification', |
||
876 | 'include_sort' => true, |
||
877 | 'include_start' => true, |
||
878 | 'hidden_fields' => [ |
||
879 | 'u' => $this->_memID, |
||
880 | 'sa' => $context['menu_item_selected'], |
||
881 | $context['session_var'] => $context['session_id'], |
||
882 | ], |
||
883 | 'token' => $context['token_check'], |
||
884 | ], |
||
885 | 'additional_rows' => [ |
||
886 | [ |
||
887 | 'class' => 'submitbutton', |
||
888 | 'position' => 'bottom_of_list', |
||
889 | 'value' => ' |
||
890 | <input type="submit" name="edit_notify_topics" value="' . $txt['notifications_update'] . '" /> |
||
891 | <input type="hidden" name="save" value="save" />', |
||
892 | ], |
||
893 | ], |
||
894 | ]; |
||
895 | |||
896 | // Create the topic notification list. |
||
897 | createList($listOptions); |
||
898 | } |
||
899 | |||
900 | /** |
||
901 | * Callback for createList() in action_notification() |
||
902 | * |
||
903 | * @param int $start The item to start with (for pagination purposes) |
||
904 | * @param int $items_per_page The number of items to show per page |
||
905 | * @param string $sort A string indicating how to sort the results |
||
906 | * @param int $memID id_member |
||
907 | * |
||
908 | * @return array array of board notifications |
||
909 | * @uses template_ignoreboards() |
||
910 | */ |
||
911 | public function list_getBoardNotifications($start, $items_per_page, $sort, $memID) |
||
912 | { |
||
913 | // Return boards you see and their notification status for the list |
||
914 | return boardNotifications($sort, $memID); |
||
915 | } |
||
916 | |||
917 | /** |
||
918 | * Callback for createList() in action_notification() |
||
919 | * |
||
920 | * @param int $start The item to start with (for pagination purposes) |
||
921 | * @param int $items_per_page The number of items to show per page |
||
922 | * @param string $sort A string indicating how to sort the results |
||
923 | * @param int $memID id_member |
||
924 | * |
||
925 | * @return array array of topic notifications |
||
926 | */ |
||
927 | public function list_getTopicNotifications($start, $items_per_page, $sort, $memID) |
||
928 | { |
||
929 | // Topic notifications, for the list |
||
930 | return topicNotifications($start, $items_per_page, $sort, $memID); |
||
931 | } |
||
932 | |||
933 | /** |
||
934 | * Callback for createList() in action_notification() |
||
935 | * |
||
936 | * - Retrieve topic notifications count. |
||
937 | * |
||
938 | * @param int $memID id_member the id of the member who's notifications we are loading |
||
939 | * @return int |
||
940 | */ |
||
941 | public function list_getTopicNotificationCount($memID) |
||
945 | } |
||
946 | |||
947 | /** |
||
948 | * Allows the user to see the list of their ignored boards. |
||
949 | * (and un-ignore them) |
||
950 | */ |
||
951 | public function action_ignoreboards() |
||
975 | } |
||
976 | |||
977 | /** |
||
978 | * Function to allow the user to choose group membership etc... |
||
979 | */ |
||
980 | public function action_groupMembership() |
||
981 | { |
||
982 | global $txt, $context; |
||
983 | |||
984 | theme()->getTemplates()->load('ProfileOptions'); |
||
985 | $context['sub_template'] = 'groupMembership'; |
||
986 | |||
987 | $curMember = $this->_profile; |
||
988 | $context['primary_group'] = (int) $curMember['id_group']; |
||
989 | $msgName = $this->_req->getQuery('msg', 'trim'); |
||
990 | |||
991 | // Can they manage groups? |
||
992 | $context['can_manage_membergroups'] = allowedTo('manage_membergroups'); |
||
993 | $context['can_manage_protected'] = allowedTo('admin_forum'); |
||
994 | $context['can_edit_primary'] = $context['can_manage_protected']; |
||
995 | $context['update_message'] = isset($msgName, $txt['group_membership_msg_' . $msgName]) ? $txt['group_membership_msg_' . $msgName] : ''; |
||
996 | |||
997 | // Get all the groups this user is a member of. |
||
998 | $groups = array_map('intval', explode(',', $curMember['additional_groups'])); |
||
999 | $groups[] = (int) $curMember['id_group']; |
||
1000 | |||
1001 | // Ensure the query doesn't croak! |
||
1002 | if (empty($groups)) |
||
1003 | { |
||
1004 | $groups = [0]; |
||
1005 | } |
||
1006 | |||
1007 | // Just to be sure... |
||
1008 | $groups = array_map('intval', $groups); |
||
1009 | |||
1010 | // Get all the membergroups they can join. |
||
1011 | require_once(SUBSDIR . '/ProfileOptions.subs.php'); |
||
1012 | $context['groups'] = loadMembergroupsJoin($groups, $this->_memID); |
||
1013 | |||
1014 | // Add registered members on the end. |
||
1015 | $context['groups']['member'][0] = [ |
||
1016 | 'id' => 0, |
||
1017 | 'name' => $txt['regular_members'], |
||
1018 | 'desc' => $txt['regular_members_desc'], |
||
1019 | 'type' => 0, |
||
1020 | 'is_primary' => $context['primary_group'] === 0, |
||
1021 | 'can_be_primary' => true, |
||
1022 | 'can_leave' => 0, |
||
1023 | ]; |
||
1024 | |||
1025 | // No changing primary one unless you have enough groups! |
||
1026 | if (count($context['groups']['member']) < 2) |
||
1027 | { |
||
1028 | $context['can_edit_primary'] = false; |
||
1029 | } |
||
1030 | |||
1031 | // In the special case that someone is requesting membership of a group, setup some special context vars. |
||
1032 | $groupRequest = $this->_req->getQuery('request', 'intval'); |
||
1033 | if (!isset($groupRequest, $context['groups']['available'][$groupRequest])) |
||
1034 | { |
||
1035 | return; |
||
1036 | } |
||
1037 | |||
1038 | if ($context['groups']['available'][$groupRequest]['type'] !== 2) |
||
1039 | { |
||
1040 | return; |
||
1041 | } |
||
1042 | |||
1043 | $context['group_request'] = $context['groups']['available'][$groupRequest]; |
||
1044 | } |
||
1045 | |||
1046 | /** |
||
1047 | * This function actually makes all the group changes |
||
1048 | * |
||
1049 | * @return string |
||
1050 | * @throws Exception no_access |
||
1051 | */ |
||
1052 | public function action_groupMembership2() |
||
1303 | } |
||
1304 | } |
||
1305 |
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.