Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 797
n/a
0 / 0
CRAP
n/a
0 / 0
1<?php
2/**
3*
4* This file is part of the phpBB Forum Software package.
5*
6* @copyright (c) phpBB Limited <https://www.phpbb.com>
7* @license GNU General Public License, version 2 (GPL-2.0)
8*
9* For full copyright and license information, please see
10* the docs/CREDITS.txt file.
11*
12*/
13
14/**
15* @ignore
16*/
17define('IN_PHPBB', true);
18$phpbb_root_path = (defined('PHPBB_ROOT_PATH')) ? PHPBB_ROOT_PATH : './';
19$phpEx = substr(strrchr(__FILE__, '.'), 1);
20include($phpbb_root_path . 'common.' . $phpEx);
21include($phpbb_root_path . 'includes/functions_display.' . $phpEx);
22
23$mode = $request->variable('mode', '');
24
25if ($mode === 'contactadmin')
26{
27    define('SKIP_CHECK_BAN', true);
28    define('SKIP_CHECK_DISABLED', true);
29}
30
31// Start session management
32$user->session_begin();
33$auth->acl($user->data);
34$user->setup(array('memberlist', 'groups'));
35
36// Setting a variable to let the style designer know where he is...
37$template->assign_var('S_IN_MEMBERLIST', true);
38
39// Grab data
40$action        = $request->variable('action', '');
41$user_id    = $request->variable('u', ANONYMOUS);
42$username    = $request->variable('un', '', true);
43$group_id    = $request->variable('g', 0);
44$topic_id    = $request->variable('t', 0);
45
46/** @var \phpbb\controller\helper $controller_helper */
47$controller_helper = $phpbb_container->get('controller.helper');
48
49// Redirect when old mode is used
50if ($mode == 'leaders')
51{
52    send_status_line(301, 'Moved Permanently');
53    redirect($controller_helper->route('phpbb_members_team', [], false));
54}
55
56// Check our mode...
57if (!in_array($mode, array('', 'group', 'viewprofile', 'email', 'contact', 'contactadmin', 'searchuser', 'team', 'livesearch')))
58{
59    trigger_error('NO_MODE');
60}
61
62switch ($mode)
63{
64    case 'email':
65    case 'contactadmin':
66    break;
67
68    case 'team':
69        send_status_line(301, 'Moved Permanently');
70        redirect($controller_helper->route('phpbb_members_team', [], false));
71    break;
72
73    case 'livesearch':
74        if (!$config['allow_live_searches'])
75        {
76            trigger_error('LIVE_SEARCHES_NOT_ALLOWED');
77        }
78        // No break
79
80    default:
81        // Can this user view profiles/memberlist?
82        if (!$auth->acl_gets('u_viewprofile', 'a_user', 'a_useradd', 'a_userdel'))
83        {
84            if ($user->data['user_id'] != ANONYMOUS)
85            {
86                send_status_line(403, 'Forbidden');
87                trigger_error('NO_VIEW_USERS');
88            }
89
90            login_box('', ((isset($user->lang['LOGIN_EXPLAIN_' . strtoupper($mode)])) ? $user->lang['LOGIN_EXPLAIN_' . strtoupper($mode)] : $user->lang['LOGIN_EXPLAIN_MEMBERLIST']));
91        }
92}
93
94/** @var \phpbb\group\helper $group_helper */
95$group_helper = $phpbb_container->get('group_helper');
96
97$start    = $request->variable('start', 0);
98$submit = (isset($_POST['submit'])) ? true : false;
99
100$default_key = 'c';
101$sort_key = $request->variable('sk', $default_key);
102$sort_dir = $request->variable('sd', 'a');
103
104$user_types = array(USER_NORMAL, USER_FOUNDER);
105if ($auth->acl_get('a_user'))
106{
107    $user_types[] = USER_INACTIVE;
108}
109
110// What do you want to do today? ... oops, I think that line is taken ...
111switch ($mode)
112{
113    case 'viewprofile':
114        // Display a profile
115        if ($user_id == ANONYMOUS && !$username)
116        {
117            trigger_error('NO_USER');
118        }
119
120        // Get user...
121        $sql_array = array(
122            'SELECT'    => 'u.*',
123            'FROM'        => array(
124                USERS_TABLE        => 'u'
125            ),
126            'WHERE'        => (($username) ? "u.username_clean = '" . $db->sql_escape(utf8_clean_string($username)) . "'" : "u.user_id = $user_id"),
127        );
128
129        /**
130         * Modify user data SQL before member profile row is created
131         *
132         * @event core.memberlist_modify_viewprofile_sql
133         * @var int        user_id                The user ID
134         * @var string    username            The username
135         * @var array    sql_array            Array containing the main query
136         * @since 3.2.6-RC1
137         */
138        $vars = array(
139            'user_id',
140            'username',
141            'sql_array',
142        );
143        extract($phpbb_dispatcher->trigger_event('core.memberlist_modify_viewprofile_sql', compact($vars)));
144
145        $sql = $db->sql_build_query('SELECT', $sql_array);
146        $result = $db->sql_query($sql);
147        $member = $db->sql_fetchrow($result);
148        $db->sql_freeresult($result);
149
150        if (!$member)
151        {
152            trigger_error('NO_USER');
153        }
154
155        // a_user admins and founder are able to view inactive users and bots to be able to manage them more easily
156        // Normal users are able to see at least users having only changed their profile settings but not yet reactivated.
157        if (!$auth->acl_get('a_user') && $user->data['user_type'] != USER_FOUNDER)
158        {
159            if ($member['user_type'] == USER_IGNORE)
160            {
161                trigger_error('NO_USER');
162            }
163            else if ($member['user_type'] == USER_INACTIVE && $member['user_inactive_reason'] != INACTIVE_PROFILE)
164            {
165                trigger_error('NO_USER');
166            }
167        }
168
169        $user_id = (int) $member['user_id'];
170
171        // Get group memberships
172        // Also get visiting user's groups to determine hidden group memberships if necessary.
173        $auth_hidden_groups = ($user_id === (int) $user->data['user_id'] || $auth->acl_gets('a_group', 'a_groupadd', 'a_groupdel')) ? true : false;
174        $sql_uid_ary = ($auth_hidden_groups) ? array($user_id) : array($user_id, (int) $user->data['user_id']);
175
176        // Do the SQL thang
177        $sql_ary = [
178            'SELECT'    => 'g.group_id, g.group_name, g.group_type, ug.user_id',
179
180            'FROM'        => [
181                GROUPS_TABLE => 'g',
182            ],
183
184            'LEFT_JOIN' => [
185                [
186                    'FROM' => [USER_GROUP_TABLE => 'ug'],
187                    'ON'   => 'g.group_id = ug.group_id',
188                ],
189            ],
190
191            'WHERE'        => $db->sql_in_set('ug.user_id', $sql_uid_ary) . '
192                AND ug.user_pending = 0',
193        ];
194
195        /**
196        * Modify the query used to get the group data
197        *
198        * @event core.modify_memberlist_viewprofile_group_sql
199        * @var array    sql_ary            Array containing the query
200        * @since 3.2.6-RC1
201        */
202        $vars = array(
203            'sql_ary',
204        );
205        extract($phpbb_dispatcher->trigger_event('core.modify_memberlist_viewprofile_group_sql', compact($vars)));
206
207        $result = $db->sql_query($db->sql_build_query('SELECT', $sql_ary));
208
209        // Divide data into profile data and current user data
210        $profile_groups = $user_groups = array();
211        while ($row = $db->sql_fetchrow($result))
212        {
213            $row['user_id'] = (int) $row['user_id'];
214            $row['group_id'] = (int) $row['group_id'];
215
216            if ($row['user_id'] == $user_id)
217            {
218                $profile_groups[] = $row;
219            }
220            else
221            {
222                $user_groups[$row['group_id']] = $row['group_id'];
223            }
224        }
225        $db->sql_freeresult($result);
226
227        // Filter out hidden groups and sort groups by name
228        $group_data = $group_sort = array();
229        foreach ($profile_groups as $row)
230        {
231            if (!$auth_hidden_groups && $row['group_type'] == GROUP_HIDDEN && !isset($user_groups[$row['group_id']]))
232            {
233                // Skip over hidden groups the user cannot see
234                continue;
235            }
236
237            $row['group_name'] = $group_helper->get_name($row['group_name']);
238
239            $group_sort[$row['group_id']] = utf8_clean_string($row['group_name']);
240            $group_data[$row['group_id']] = $row;
241        }
242        unset($profile_groups);
243        unset($user_groups);
244        asort($group_sort);
245
246        /**
247        * Modify group data before options is created and data is unset
248        *
249        * @event core.modify_memberlist_viewprofile_group_data
250        * @var array    group_data            Array containing the group data
251        * @var array    group_sort            Array containing the sorted group data
252        * @since 3.2.6-RC1
253        */
254        $vars = array(
255            'group_data',
256            'group_sort',
257        );
258        extract($phpbb_dispatcher->trigger_event('core.modify_memberlist_viewprofile_group_data', compact($vars)));
259
260        $group_options = '';
261        foreach ($group_sort as $group_id => $null)
262        {
263            $row = $group_data[$group_id];
264
265            $group_options .= '<option value="' . $row['group_id'] . '"' . (($row['group_id'] == $member['group_id']) ? ' selected="selected"' : '') . '>' . $row['group_name'] . '</option>';
266        }
267        unset($group_data);
268        unset($group_sort);
269
270        // What colour is the zebra
271        $sql = 'SELECT friend, foe
272            FROM ' . ZEBRA_TABLE . "
273            WHERE zebra_id = $user_id
274                AND user_id = {$user->data['user_id']}";
275        $result = $db->sql_query($sql);
276        $row = $db->sql_fetchrow($result);
277
278        $foe = $row ? (bool) $row['foe'] : false;
279        $friend = $row ? (bool) $row['friend'] : false;
280
281        $db->sql_freeresult($result);
282
283        if ($config['load_onlinetrack'])
284        {
285            $sql = 'SELECT MAX(session_time) AS session_time, MIN(session_viewonline) AS session_viewonline
286                FROM ' . SESSIONS_TABLE . "
287                WHERE session_user_id = $user_id";
288            $result = $db->sql_query($sql);
289            $row = $db->sql_fetchrow($result);
290            $db->sql_freeresult($result);
291
292            $member['session_time'] = (isset($row['session_time'])) ? $row['session_time'] : 0;
293            $member['session_viewonline'] = (isset($row['session_viewonline'])) ? $row['session_viewonline'] : 0;
294            unset($row);
295        }
296
297        if ($config['load_user_activity'])
298        {
299            display_user_activity($member);
300        }
301
302        // Do the relevant calculations
303        $memberdays = max(1, round((time() - $member['user_regdate']) / 86400));
304        $posts_per_day = $member['user_posts'] / $memberdays;
305        $percentage = ($config['num_posts']) ? min(100, ($member['user_posts'] / $config['num_posts']) * 100) : 0;
306
307
308        if ($member['user_sig'])
309        {
310            $parse_flags = ($member['user_sig_bbcode_bitfield'] ? OPTION_FLAG_BBCODE : 0) | OPTION_FLAG_SMILIES;
311            $member['user_sig'] = generate_text_for_display($member['user_sig'], $member['user_sig_bbcode_uid'], $member['user_sig_bbcode_bitfield'], $parse_flags, true);
312        }
313
314        // We need to check if the modules 'zebra' ('friends' & 'foes' mode),  'notes' ('user_notes' mode) and  'warn' ('warn_user' mode) are accessible to decide if we can display appropriate links
315        $zebra_enabled = $friends_enabled = $foes_enabled = $user_notes_enabled = $warn_user_enabled = false;
316
317        // Only check if the user is logged in
318        if ($user->data['is_registered'])
319        {
320            if (!class_exists('p_master'))
321            {
322                include($phpbb_root_path . 'includes/functions_module.' . $phpEx);
323            }
324            $module = new p_master();
325
326            $module->list_modules('ucp');
327            $module->list_modules('mcp');
328
329            $user_notes_enabled = ($module->loaded('mcp_notes', 'user_notes')) ? true : false;
330            $warn_user_enabled = ($module->loaded('mcp_warn', 'warn_user')) ? true : false;
331            $zebra_enabled = ($module->loaded('ucp_zebra')) ? true : false;
332            $friends_enabled = ($module->loaded('ucp_zebra', 'friends')) ? true : false;
333            $foes_enabled = ($module->loaded('ucp_zebra', 'foes')) ? true : false;
334
335            unset($module);
336        }
337
338        // Custom Profile Fields
339        $profile_fields = array();
340        if ($config['load_cpf_viewprofile'])
341        {
342            /* @var $cp \phpbb\profilefields\manager */
343            $cp = $phpbb_container->get('profilefields.manager');
344            $profile_fields = $cp->grab_profile_fields_data($user_id);
345            $profile_fields = (isset($profile_fields[$user_id])) ? $cp->generate_profile_fields_template_data($profile_fields[$user_id]) : array();
346        }
347
348        /**
349        * Modify user data before we display the profile
350        *
351        * @event core.memberlist_view_profile
352        * @var    array    member                    Array with user's data
353        * @var    bool    user_notes_enabled        Is the mcp user notes module enabled?
354        * @var    bool    warn_user_enabled        Is the mcp warnings module enabled?
355        * @var    bool    zebra_enabled            Is the ucp zebra module enabled?
356        * @var    bool    friends_enabled            Is the ucp friends module enabled?
357        * @var    bool    foes_enabled            Is the ucp foes module enabled?
358        * @var    bool    friend                    Is the user friend?
359        * @var    bool    foe                        Is the user foe?
360        * @var    array    profile_fields            Array with user's profile field data
361        * @since 3.1.0-a1
362        * @changed 3.1.0-b2 Added friend and foe status
363        * @changed 3.1.0-b3 Added profile fields data
364        */
365        $vars = array(
366            'member',
367            'user_notes_enabled',
368            'warn_user_enabled',
369            'zebra_enabled',
370            'friends_enabled',
371            'foes_enabled',
372            'friend',
373            'foe',
374            'profile_fields',
375        );
376        extract($phpbb_dispatcher->trigger_event('core.memberlist_view_profile', compact($vars)));
377
378        $template->assign_vars(phpbb_show_profile($member, $user_notes_enabled, $warn_user_enabled));
379
380        // If the user has m_approve permission or a_user permission, then list then display unapproved posts
381        if ($auth->acl_getf_global('m_approve') || $auth->acl_get('a_user'))
382        {
383            $sql = 'SELECT COUNT(post_id) as posts_in_queue
384                FROM ' . POSTS_TABLE . '
385                WHERE poster_id = ' . $user_id . '
386                    AND ' . $db->sql_in_set('post_visibility', array(ITEM_UNAPPROVED, ITEM_REAPPROVE));
387            $result = $db->sql_query($sql);
388            $member['posts_in_queue'] = (int) $db->sql_fetchfield('posts_in_queue');
389            $db->sql_freeresult($result);
390        }
391        else
392        {
393            $member['posts_in_queue'] = 0;
394        }
395
396        // Define the main array of vars to assign to memberlist_view.html
397        $template_ary = array(
398            'L_POSTS_IN_QUEUE'            => $user->lang('NUM_POSTS_IN_QUEUE', $member['posts_in_queue']),
399
400            'POSTS_DAY'                    => $user->lang('POST_DAY', $posts_per_day),
401            'POSTS_PCT'                    => $user->lang('POST_PCT', $percentage),
402
403            'SIGNATURE'                    => $member['user_sig'],
404            'POSTS_IN_QUEUE'            => $member['posts_in_queue'],
405
406            'PM_IMG'                    => $user->img('icon_contact_pm', $user->lang['SEND_PRIVATE_MESSAGE']),
407            'L_SEND_EMAIL_USER'            => $user->lang('SEND_EMAIL_USER', $member['username']),
408            'EMAIL_IMG'                    => $user->img('icon_contact_email', $user->lang['EMAIL']),
409            'SEARCH_IMG'                => $user->img('icon_user_search', $user->lang['SEARCH']),
410
411            'S_PROFILE_ACTION'            => append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=group'),
412            'S_GROUP_OPTIONS'            => $group_options,
413            'S_CUSTOM_FIELDS'            => (isset($profile_fields['row']) && count($profile_fields['row'])) ? true : false,
414
415            'U_USER_ADMIN'                => ($auth->acl_get('a_user')) ? append_sid("{$phpbb_admin_path}index.$phpEx", 'i=users&amp;mode=overview&amp;u=' . $user_id, true, $user->session_id) : '',
416            'U_USER_BAN'                => ($auth->acl_get('m_ban') && $user_id != $user->data['user_id']) ? append_sid("{$phpbb_root_path}mcp.$phpEx", 'i=ban&amp;mode=user&amp;u=' . $user_id) : '',
417            'U_MCP_QUEUE'                => ($auth->acl_getf_global('m_approve')) ? append_sid("{$phpbb_root_path}mcp.$phpEx") : '',
418
419            'U_SWITCH_PERMISSIONS'        => ($auth->acl_get('a_switchperm') && $user->data['user_id'] != $user_id) ? append_sid("{$phpbb_root_path}ucp.$phpEx", "mode=switch_perm&amp;u={$user_id}&amp;hash=" . generate_link_hash('switchperm')) : '',
420            'U_EDIT_SELF'                => ($user_id == $user->data['user_id'] && $auth->acl_get('u_chgprofileinfo')) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=ucp_profile&amp;mode=profile_info') : '',
421
422            'S_USER_NOTES'                => ($user_notes_enabled) ? true : false,
423            'S_WARN_USER'                => ($warn_user_enabled) ? true : false,
424            'S_ZEBRA'                    => ($user->data['user_id'] != $user_id && $user->data['is_registered'] && $zebra_enabled) ? true : false,
425            'U_ADD_FRIEND'                => (!$friend && !$foe && $friends_enabled) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=zebra&amp;add=' . urlencode(html_entity_decode($member['username'], ENT_COMPAT))) : '',
426            'U_ADD_FOE'                    => (!$friend && !$foe && $foes_enabled) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=zebra&amp;mode=foes&amp;add=' . urlencode(html_entity_decode($member['username'], ENT_COMPAT))) : '',
427            'U_REMOVE_FRIEND'            => ($friend && $friends_enabled) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=zebra&amp;remove=1&amp;usernames[]=' . $user_id) : '',
428            'U_REMOVE_FOE'                => ($foe && $foes_enabled) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=zebra&amp;remove=1&amp;mode=foes&amp;usernames[]=' . $user_id) : '',
429
430            'U_CANONICAL'                => generate_board_url() . '/' . append_sid("memberlist.$phpEx", 'mode=viewprofile&amp;u=' . $user_id, true, ''),
431            'USER_ID'                    => $user_id,
432        );
433
434        /**
435        * Modify user's template vars before we display the profile
436        *
437        * @event core.memberlist_modify_view_profile_template_vars
438        * @var    array    template_ary            Array with user's template vars
439        * @var    int        user_id                    The user ID
440        * @var    bool    user_notes_enabled        Is the mcp user notes module enabled?
441        * @var    bool    warn_user_enabled        Is the mcp warnings module enabled?
442        * @var    bool    friends_enabled            Is the ucp friends module enabled?
443        * @var    bool    foes_enabled            Is the ucp foes module enabled?
444        * @var    bool    friend                    Is the user friend?
445        * @var    bool    foe                        Is the user foe?
446        * @since 3.2.6-RC1
447        * @changed 3.3.15-RC1 Added vars user_id, user_notes_enabled, warn_user_enabled, friend, friends_enabled, foe, foes_enabled
448        */
449        $vars = array(
450            'template_ary',
451            'user_id',
452            'user_notes_enabled',
453            'warn_user_enabled',
454            'friend',
455            'friends_enabled',
456            'foe',
457            'foes_enabled',
458        );
459        extract($phpbb_dispatcher->trigger_event('core.memberlist_modify_view_profile_template_vars', compact($vars)));
460
461        // Assign vars to memberlist_view.html
462        $template->assign_vars($template_ary);
463
464        if (!empty($profile_fields['row']))
465        {
466            $template->assign_vars($profile_fields['row']);
467        }
468
469        if (!empty($profile_fields['blockrow']))
470        {
471            foreach ($profile_fields['blockrow'] as $field_data)
472            {
473                $template->assign_block_vars('custom_fields', $field_data);
474            }
475        }
476
477        // Inactive reason/account?
478        if ($member['user_type'] == USER_INACTIVE)
479        {
480            $user->add_lang('acp/common');
481
482            $inactive_reason = $user->lang['INACTIVE_REASON_UNKNOWN'];
483
484            switch ($member['user_inactive_reason'])
485            {
486                case INACTIVE_REGISTER:
487                    $inactive_reason = $user->lang['INACTIVE_REASON_REGISTER'];
488                break;
489
490                case INACTIVE_PROFILE:
491                    $inactive_reason = $user->lang['INACTIVE_REASON_PROFILE'];
492                break;
493
494                case INACTIVE_MANUAL:
495                    $inactive_reason = $user->lang['INACTIVE_REASON_MANUAL'];
496                break;
497
498                case INACTIVE_REMIND:
499                    $inactive_reason = $user->lang['INACTIVE_REASON_REMIND'];
500                break;
501            }
502
503            $template->assign_vars(array(
504                'S_USER_INACTIVE'        => true,
505                'USER_INACTIVE_REASON'    => $inactive_reason)
506            );
507        }
508
509        // Now generate page title
510        $page_title = sprintf($user->lang['VIEWING_PROFILE'], $member['username']);
511        $template_html = 'memberlist_view.html';
512
513        $template->assign_block_vars('navlinks', array(
514            'BREADCRUMB_NAME'    => $user->lang('MEMBERLIST'),
515            'U_BREADCRUMB'        => append_sid("{$phpbb_root_path}memberlist.$phpEx"),
516        ));
517        $template->assign_block_vars('navlinks', array(
518            'BREADCRUMB_NAME'    => $member['username'],
519            'U_BREADCRUMB'        => append_sid("{$phpbb_root_path}memberlist.$phpEx", "mode=viewprofile&u=$user_id"),
520        ));
521
522    break;
523
524    case 'contactadmin':
525    case 'email':
526        $messenger = $phpbb_container->get('messenger.method_collection');
527
528        $user_id    = $request->variable('u', 0);
529        $topic_id    = $request->variable('t', 0);
530
531        if ($user_id)
532        {
533            $form_name = 'user';
534        }
535        else if ($topic_id)
536        {
537            $form_name = 'topic';
538        }
539        else if ($mode === 'contactadmin')
540        {
541            $form_name = 'admin';
542        }
543        else
544        {
545            trigger_error('NO_EMAIL');
546        }
547
548        /** @var $form \phpbb\message\form */
549        $form = $phpbb_container->get('message.form.' . $form_name);
550
551        $form->bind($request);
552        $error = $form->check_allow();
553        if ($error)
554        {
555            trigger_error($error);
556        }
557
558        if ($request->is_set_post('submit'))
559        {
560            $form->submit($messenger);
561        }
562
563        $page_title = $form->get_page_title();
564        $template_html = $form->get_template_file();
565        $form->render($template);
566
567        if ($user_id)
568        {
569            $navlink_name = $user->lang('SEND_EMAIL');
570            $navlink_url = append_sid("{$phpbb_root_path}memberlist.$phpEx", "mode=email&u=$user_id");
571        }
572        else if ($topic_id)
573        {
574            // Generate the navlinks based on the selected topic
575            $navlinks_sql_array = [
576                'SELECT'    => 'f.parent_id, f.forum_parents, f.left_id, f.right_id, f.forum_type, f.forum_name,
577                    f.forum_id, f.forum_desc, f.forum_desc_uid, f.forum_desc_bitfield, f.forum_desc_options,
578                    f.forum_options, t.topic_title',
579                'FROM'      => [
580                    FORUMS_TABLE  => 'f',
581                    TOPICS_TABLE  => 't',
582                ],
583                'WHERE'     => 't.forum_id = f.forum_id AND t.topic_id = ' . (int) $topic_id,
584            ];
585
586            $sql = $db->sql_build_query('SELECT', $navlinks_sql_array);
587            $result = $db->sql_query($sql);
588            $topic_data = $db->sql_fetchrow($result);
589            $db->sql_freeresult($result);
590
591            generate_forum_nav($topic_data);
592            $template->assign_block_vars('navlinks', array(
593                'BREADCRUMB_NAME'    => $topic_data['topic_title'],
594                'U_BREADCRUMB'        => append_sid("{$phpbb_root_path}viewtopic.$phpEx", "t=$topic_id"),
595            ));
596
597            $navlink_name = $user->lang('EMAIL_TOPIC');
598            $navlink_url = append_sid("{$phpbb_root_path}memberlist.$phpEx", "mode=email&t=$topic_id");
599        }
600        else if ($mode === 'contactadmin')
601        {
602            $navlink_name = $user->lang('CONTACT_ADMIN');
603            $navlink_url = append_sid("{$phpbb_root_path}memberlist.$phpEx", "mode=contactadmin");
604        }
605
606        $template->assign_block_vars('navlinks', array(
607            'BREADCRUMB_NAME'    => $navlink_name,
608            'U_BREADCRUMB'        => $navlink_url,
609        ));
610
611    break;
612
613    case 'livesearch':
614
615        $username_chars = $request->variable('username', '', true);
616
617        $sql = 'SELECT username, user_id, user_colour
618            FROM ' . USERS_TABLE . '
619            WHERE ' . $db->sql_in_set('user_type', $user_types) . '
620                AND username_clean ' . $db->sql_like_expression(utf8_clean_string($username_chars) . $db->get_any_char());
621        $result = $db->sql_query_limit($sql, 10);
622
623        $user_list = [];
624
625        while ($row = $db->sql_fetchrow($result))
626        {
627            $user_list[] = [
628                'user_id'        => (int) $row['user_id'],
629                'result'        => html_entity_decode($row['username']),
630                'username_full'    => get_username_string('full', $row['user_id'], $row['username'], $row['user_colour']),
631                'display'        => get_username_string('no_profile', $row['user_id'], $row['username'], $row['user_colour']),
632            ];
633        }
634        $db->sql_freeresult($result);
635
636        $json_response = new \phpbb\json_response();
637
638        $json_response->send([
639            'keyword' => $username_chars,
640            'results' => $user_list,
641        ]);
642
643    break;
644
645    case 'group':
646    default:
647        // The basic memberlist
648        $page_title = $user->lang['MEMBERLIST'];
649        $template_html = 'memberlist_body.html';
650
651        $template->assign_block_vars('navlinks', array(
652            'BREADCRUMB_NAME'    => $page_title,
653            'U_BREADCRUMB'        => append_sid("{$phpbb_root_path}memberlist.$phpEx"),
654        ));
655
656        /* @var $pagination \phpbb\pagination */
657        $pagination = $phpbb_container->get('pagination');
658
659        // Sorting
660        $sort_key_text = array('a' => $user->lang['SORT_USERNAME'], 'c' => $user->lang['SORT_JOINED'], 'd' => $user->lang['SORT_POST_COUNT']);
661        $sort_key_sql = array('a' => 'u.username_clean', 'c' => 'u.user_regdate', 'd' => 'u.user_posts');
662
663        if ($auth->acl_get('a_user'))
664        {
665            $sort_key_text['e'] = $user->lang['SORT_EMAIL'];
666            $sort_key_sql['e'] = 'u.user_email';
667        }
668
669        if ($auth->acl_get('u_viewonline'))
670        {
671            $sort_key_text['l'] = $user->lang['SORT_LAST_ACTIVE'];
672            $sort_key_sql['l'] = 'u.user_last_active';
673        }
674
675        $sort_key_text['m'] = $user->lang['SORT_RANK'];
676        $sort_key_sql['m'] = 'u.user_rank';
677
678        $sort_dir_text = array('a' => $user->lang['ASCENDING'], 'd' => $user->lang['DESCENDING']);
679
680        $s_sort_key = '';
681        foreach ($sort_key_text as $key => $value)
682        {
683            $selected = ($sort_key == $key) ? ' selected="selected"' : '';
684            $s_sort_key .= '<option value="' . $key . '"' . $selected . '>' . $value . '</option>';
685        }
686
687        $s_sort_dir = '';
688        foreach ($sort_dir_text as $key => $value)
689        {
690            $selected = ($sort_dir == $key) ? ' selected="selected"' : '';
691            $s_sort_dir .= '<option value="' . $key . '"' . $selected . '>' . $value . '</option>';
692        }
693
694        // Additional sorting options for user search ... if search is enabled, if not
695        // then only admins can make use of this (for ACP functionality)
696        $sql_select = $sql_where_data = $sql_from = $sql_where = $order_by = '';
697
698
699        $form            = $request->variable('form', '');
700        $field            = $request->variable('field', '');
701        $select_single     = $request->variable('select_single', false);
702
703        // Search URL parameters, if any of these are in the URL we do a search
704        $search_params = array('username', 'email', 'search_group_id', 'joined_select', 'active_select', 'count_select', 'joined', 'active', 'count', 'ip');
705
706        // We validate form and field here, only id/class allowed
707        $form = (!preg_match('/^[a-z0-9_-]+$/i', $form)) ? '' : $form;
708        $field = (!preg_match('/^[a-z0-9_-]+$/i', $field)) ? '' : $field;
709        if ((($mode == '' || $mode == 'searchuser') || count(array_intersect($request->variable_names(\phpbb\request\request_interface::GET), $search_params)) > 0) && ($config['load_search'] || $auth->acl_get('a_')))
710        {
711            $username    = $request->variable('username', '', true);
712            $email        = strtolower($request->variable('email', ''));
713            $search_group_id    = $request->variable('search_group_id', 0);
714
715            // when using these, make sure that we actually have values defined in $find_key_match
716            $joined_select    = $request->variable('joined_select', 'lt');
717            $active_select    = $request->variable('active_select', 'lt');
718            $count_select    = $request->variable('count_select', 'eq');
719
720            $joined            = explode('-', $request->variable('joined', ''));
721            $active            = explode('-', $request->variable('active', ''));
722            $count            = ($request->variable('count', '') !== '') ? $request->variable('count', 0) : '';
723            $ipdomain        = $request->variable('ip', '');
724
725            $find_key_match = array('lt' => '<', 'gt' => '>', 'eq' => '=');
726
727            $find_count = array('lt' => $user->lang['LESS_THAN'], 'eq' => $user->lang['EQUAL_TO'], 'gt' => $user->lang['MORE_THAN']);
728            $s_find_count = '';
729            foreach ($find_count as $key => $value)
730            {
731                $selected = ($count_select == $key) ? ' selected="selected"' : '';
732                $s_find_count .= '<option value="' . $key . '"' . $selected . '>' . $value . '</option>';
733            }
734
735            $find_time = array('lt' => $user->lang['BEFORE'], 'gt' => $user->lang['AFTER']);
736            $s_find_join_time = '';
737            foreach ($find_time as $key => $value)
738            {
739                $selected = ($joined_select == $key) ? ' selected="selected"' : '';
740                $s_find_join_time .= '<option value="' . $key . '"' . $selected . '>' . $value . '</option>';
741            }
742
743            $s_find_active_time = '';
744            foreach ($find_time as $key => $value)
745            {
746                $selected = ($active_select == $key) ? ' selected="selected"' : '';
747                $s_find_active_time .= '<option value="' . $key . '"' . $selected . '>' . $value . '</option>';
748            }
749
750            $sql_where .= ($username) ? ' AND u.username_clean ' . $db->sql_like_expression(str_replace('*', $db->get_any_char(), utf8_clean_string($username))) : '';
751            $sql_where .= ($auth->acl_get('a_user') && $email) ? ' AND u.user_email ' . $db->sql_like_expression(str_replace('*', $db->get_any_char(), $email)) . ' ' : '';
752            $sql_where .= (is_numeric($count) && isset($find_key_match[$count_select])) ? ' AND u.user_posts ' . $find_key_match[$count_select] . ' ' . (int) $count . ' ' : '';
753
754            if (isset($find_key_match[$joined_select]) && count($joined) == 3)
755            {
756                $joined_time = gmmktime(0, 0, 0, (int) $joined[1], (int) $joined[2], (int) $joined[0]);
757
758                if ($joined_time !== false)
759                {
760                    $sql_where .= " AND u.user_regdate " . $find_key_match[$joined_select] . ' ' . $joined_time;
761                }
762            }
763
764            if (isset($find_key_match[$active_select]) && count($active) == 3 && $auth->acl_get('u_viewonline'))
765            {
766                $active_time = gmmktime(0, 0, 0, (int) $active[1], (int) $active[2], (int) $active[0]);
767
768                if ($active_time !== false)
769                {
770                    if ($active_select === 'lt' && (int) $active[0] == 0 && (int) $active[1] == 0 && (int) $active[2] == 0)
771                    {
772                        $sql_where .= ' AND u.user_last_active = 0';
773                    }
774                    else if ($active_select === 'gt')
775                    {
776                        $sql_where .= ' AND u.user_last_active ' . $find_key_match[$active_select] . ' ' . $active_time;
777                    }
778                    else
779                    {
780                        $sql_where .= ' AND (u.user_last_active > 0 AND u.user_last_active < ' . $active_time . ')';
781                    }
782                }
783            }
784
785            $sql_where .= ($search_group_id) ? " AND u.user_id = ug.user_id AND ug.group_id = $search_group_id AND ug.user_pending = 0 " : '';
786
787            if ($search_group_id)
788            {
789                $sql_from = ', ' . USER_GROUP_TABLE . ' ug ';
790            }
791
792            if ($ipdomain && $auth->acl_getf_global('m_info'))
793            {
794                if (strspn($ipdomain, 'abcdefghijklmnopqrstuvwxyz'))
795                {
796                    $hostnames = gethostbynamel($ipdomain);
797
798                    if ($hostnames !== false)
799                    {
800                        $ips = "'" . implode('\', \'', array_map(array($db, 'sql_escape'), preg_replace('#([0-9]{1,3}\.[0-9]{1,3}[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})#', "\\1", gethostbynamel($ipdomain)))) . "'";
801                    }
802                    else
803                    {
804                        $ips = false;
805                    }
806                }
807                else
808                {
809                    $ips = "'" . str_replace('*', '%', $db->sql_escape($ipdomain)) . "'";
810                }
811
812                if ($ips === false)
813                {
814                    // A minor fudge but it does the job :D
815                    $sql_where .= " AND u.user_id = 0";
816                }
817                else
818                {
819                    $ip_forums = array_keys($auth->acl_getf('m_info', true));
820
821                    $sql = 'SELECT DISTINCT poster_id
822                        FROM ' . POSTS_TABLE . '
823                        WHERE poster_ip ' . ((strpos($ips, '%') !== false) ? 'LIKE' : 'IN') . " ($ips)
824                            AND " . $db->sql_in_set('forum_id', $ip_forums);
825
826                    /**
827                    * Modify sql query for members search by ip address / hostname
828                    *
829                    * @event core.memberlist_modify_ip_search_sql_query
830                    * @var    string    ipdomain    The host name
831                    * @var    string    ips            IP address list for the given host name
832                    * @var    string    sql            The SQL query for searching members by IP address
833                    * @since 3.1.7-RC1
834                    */
835                    $vars = array(
836                        'ipdomain',
837                        'ips',
838                        'sql',
839                    );
840                    extract($phpbb_dispatcher->trigger_event('core.memberlist_modify_ip_search_sql_query', compact($vars)));
841
842                    $result = $db->sql_query($sql);
843
844                    if ($row = $db->sql_fetchrow($result))
845                    {
846                        $ip_sql = array();
847                        do
848                        {
849                            $ip_sql[] = $row['poster_id'];
850                        }
851                        while ($row = $db->sql_fetchrow($result));
852
853                        $sql_where .= ' AND ' . $db->sql_in_set('u.user_id', $ip_sql);
854                    }
855                    else
856                    {
857                        // A minor fudge but it does the job :D
858                        $sql_where .= " AND u.user_id = 0";
859                    }
860                    unset($ip_forums);
861
862                    $db->sql_freeresult($result);
863                }
864            }
865        }
866
867        $first_char = $request->variable('first_char', '');
868
869        if ($first_char == 'other')
870        {
871            for ($i = 97; $i < 123; $i++)
872            {
873                $sql_where .= ' AND u.username_clean NOT ' . $db->sql_like_expression(chr($i) . $db->get_any_char());
874            }
875        }
876        else if ($first_char)
877        {
878            $sql_where .= ' AND u.username_clean ' . $db->sql_like_expression(substr($first_char, 0, 1) . $db->get_any_char());
879        }
880
881        // Are we looking at a usergroup? If so, fetch additional info
882        // and further restrict the user info query
883        if ($mode == 'group')
884        {
885            // We JOIN here to save a query for determining membership for hidden groups. ;)
886            $sql = 'SELECT g.*, ug.user_id, ug.group_leader
887                FROM ' . GROUPS_TABLE . ' g
888                LEFT JOIN ' . USER_GROUP_TABLE . ' ug ON (ug.user_pending = 0 AND ug.user_id = ' . $user->data['user_id'] . " AND ug.group_id = $group_id)
889                WHERE g.group_id = $group_id";
890            $result = $db->sql_query($sql);
891            $group_row = $db->sql_fetchrow($result);
892            $db->sql_freeresult($result);
893
894            if (!$group_row)
895            {
896                trigger_error('NO_GROUP');
897            }
898
899            switch ($group_row['group_type'])
900            {
901                case GROUP_OPEN:
902                    $group_row['l_group_type'] = 'OPEN';
903                break;
904
905                case GROUP_CLOSED:
906                    $group_row['l_group_type'] = 'CLOSED';
907                break;
908
909                case GROUP_HIDDEN:
910                    $group_row['l_group_type'] = 'HIDDEN';
911
912                    // Check for membership or special permissions
913                    if (!$auth->acl_gets('a_group', 'a_groupadd', 'a_groupdel') && $group_row['user_id'] != $user->data['user_id'])
914                    {
915                        trigger_error('NO_GROUP');
916                    }
917                break;
918
919                case GROUP_SPECIAL:
920                    $group_row['l_group_type'] = 'SPECIAL';
921                break;
922
923                case GROUP_FREE:
924                    $group_row['l_group_type'] = 'FREE';
925                break;
926            }
927
928            // ... same for group rank
929            $group_rank_data = array(
930                'title'        => null,
931                'img'        => null,
932                'img_src'    => null,
933            );
934            if ($group_row['group_rank'])
935            {
936                $group_rank_data = $group_helper->get_rank($group_row);
937
938                if ($group_rank_data['img'])
939                {
940                    $group_rank_data['img'] .= '<br />';
941                }
942            }
943            // include modules for manage groups link display or not
944            // need to ensure the module is active
945            $can_manage_group = false;
946            if ($user->data['is_registered'] && $group_row['group_leader'])
947            {
948                if (!class_exists('p_master'))
949                {
950                    include($phpbb_root_path . 'includes/functions_module.' . $phpEx);
951                }
952                $module = new p_master;
953                $module->list_modules('ucp');
954
955                if ($module->is_active('ucp_groups', 'manage'))
956                {
957                    $can_manage_group = true;
958                }
959                unset($module);
960            }
961
962            $template->assign_block_vars('navlinks', array(
963                'BREADCRUMB_NAME'    => $group_helper->get_name($group_row['group_name']),
964                'U_BREADCRUMB'        => append_sid("{$phpbb_root_path}memberlist.$phpEx", "mode=group&amp;g=$group_id"),
965            ));
966
967            /** @var \phpbb\avatar\helper $avatar_helper */
968            $avatar_helper = $phpbb_container->get('avatar.helper');
969
970            $group_avatar = $avatar_helper->get_group_avatar($group_row);
971            $template->assign_vars($avatar_helper->get_template_vars($group_avatar));
972
973            $template->assign_vars(array(
974                'GROUP_DESC'    => generate_text_for_display($group_row['group_desc'], $group_row['group_desc_uid'], $group_row['group_desc_bitfield'], $group_row['group_desc_options']),
975                'GROUP_NAME'    => $group_helper->get_name($group_row['group_name']),
976                'GROUP_COLOR'    => $group_row['group_colour'],
977                'GROUP_TYPE'    => $user->lang['GROUP_IS_' . $group_row['l_group_type']],
978                'GROUP_RANK'    => $group_rank_data['title'],
979
980                'RANK_IMG'        => $group_rank_data['img'],
981                'RANK_IMG_SRC'    => $group_rank_data['img_src'],
982
983                'U_PM'            => ($auth->acl_get('u_sendpm') && $auth->acl_get('u_masspm_group') && $group_row['group_receive_pm'] && $config['allow_privmsg'] && $config['allow_mass_pm']) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;mode=compose&amp;g=' . $group_id) : '',
984                'U_MANAGE'        => ($can_manage_group) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=ucp_groups&amp;mode=manage') : false,)
985            );
986
987            $sql_select = ', ug.group_leader';
988            $sql_from = ', ' . USER_GROUP_TABLE . ' ug ';
989            $order_by = 'ug.group_leader DESC, ';
990
991            $sql_where .= " AND ug.user_pending = 0 AND u.user_id = ug.user_id AND ug.group_id = $group_id";
992            $sql_where_data = " AND u.user_id = ug.user_id AND ug.group_id = $group_id";
993        }
994
995        // Sorting and order
996        if (!isset($sort_key_sql[$sort_key]))
997        {
998            $sort_key = $default_key;
999        }
1000
1001        $order_by .= $sort_key_sql[$sort_key] . ' ' . (($sort_dir == 'a') ? 'ASC' : 'DESC');
1002
1003        // For sorting by non-unique columns (rank, posts) add unique sort key to avoid duplicated rows in results
1004        if ($sort_key == 'm' || $sort_key == 'd')
1005        {
1006            $order_by .= ', u.user_id ASC';
1007        }
1008
1009        /**
1010        * Modify sql query data for members search
1011        *
1012        * @event core.memberlist_modify_sql_query_data
1013        * @var    string    order_by        SQL ORDER BY clause condition
1014        * @var    string    sort_dir        The sorting direction
1015        * @var    string    sort_key        The sorting key
1016        * @var    array    sort_key_sql    Arraty with the sorting conditions data
1017        * @var    string    sql_from        SQL FROM clause condition
1018        * @var    string    sql_select        SQL SELECT fields list
1019        * @var    string    sql_where        SQL WHERE clause condition
1020        * @var    string    sql_where_data    SQL WHERE clause additional conditions data
1021        * @since 3.1.7-RC1
1022        */
1023        $vars = array(
1024            'order_by',
1025            'sort_dir',
1026            'sort_key',
1027            'sort_key_sql',
1028            'sql_from',
1029            'sql_select',
1030            'sql_where',
1031            'sql_where_data',
1032        );
1033        extract($phpbb_dispatcher->trigger_event('core.memberlist_modify_sql_query_data', compact($vars)));
1034
1035        // Count the users ...
1036        $sql = 'SELECT COUNT(u.user_id) AS total_users
1037            FROM ' . USERS_TABLE . " u$sql_from
1038            WHERE " . $db->sql_in_set('u.user_type', $user_types) . "
1039            $sql_where";
1040        $result = $db->sql_query($sql);
1041        $total_users = (int) $db->sql_fetchfield('total_users');
1042        $db->sql_freeresult($result);
1043
1044        // Build a relevant pagination_url
1045        $params = $sort_params = array();
1046
1047        // We do not use $request->variable() here directly to save some calls (not all variables are set)
1048        $check_params = array(
1049            'g'                => array('g', 0),
1050            'sk'            => array('sk', $default_key),
1051            'sd'            => array('sd', 'a'),
1052            'form'            => array('form', ''),
1053            'field'            => array('field', ''),
1054            'select_single'    => array('select_single', $select_single),
1055            'username'        => array('username', '', true),
1056            'email'            => array('email', ''),
1057            'search_group_id'    => array('search_group_id', 0),
1058            'joined_select'    => array('joined_select', 'lt'),
1059            'active_select'    => array('active_select', 'lt'),
1060            'count_select'    => array('count_select', 'eq'),
1061            'joined'        => array('joined', ''),
1062            'active'        => array('active', ''),
1063            'count'            => ($request->variable('count', '') !== '') ? array('count', 0) : array('count', ''),
1064            'ip'            => array('ip', ''),
1065            'first_char'    => array('first_char', ''),
1066        );
1067
1068        $u_first_char_params = array();
1069        foreach ($check_params as $key => $call)
1070        {
1071            if (!isset($_REQUEST[$key]))
1072            {
1073                continue;
1074            }
1075
1076            $param = call_user_func_array(array($request, 'variable'), $call);
1077            // Encode strings, convert everything else to int in order to prevent empty parameters.
1078            $param = urlencode($key) . '=' . ((is_string($param)) ? urlencode($param) : (int) $param);
1079            $params[] = $param;
1080
1081            if ($key != 'first_char')
1082            {
1083                $u_first_char_params[] = $param;
1084            }
1085            if ($key != 'sk' && $key != 'sd')
1086            {
1087                $sort_params[] = $param;
1088            }
1089        }
1090
1091        $u_hide_find_member = append_sid("{$phpbb_root_path}memberlist.$phpEx", "start=$start" . (!empty($params) ? '&amp;' . implode('&amp;', $params) : ''));
1092
1093        if ($mode)
1094        {
1095            $params[] = "mode=$mode";
1096            $u_first_char_params[] = "mode=$mode";
1097        }
1098        $sort_params[] = "mode=$mode";
1099
1100        $u_first_char_params = implode('&amp;', $u_first_char_params);
1101        $u_first_char_params .= ($u_first_char_params) ? '&amp;' : '';
1102
1103        $first_characters = array();
1104        $first_characters[''] = $user->lang['ALL'];
1105        for ($i = 97; $i < 123; $i++)
1106        {
1107            $first_characters[chr($i)] = chr($i - 32);
1108        }
1109        $first_characters['other'] = $user->lang['OTHER'];
1110
1111        $first_char_block_vars = [];
1112
1113        foreach ($first_characters as $char => $desc)
1114        {
1115            $first_char_block_vars[] = [
1116                'DESC'            => $desc,
1117                'VALUE'            => $char,
1118                'S_SELECTED'    => ($first_char == $char) ? true : false,
1119                'U_SORT'        => append_sid("{$phpbb_root_path}memberlist.$phpEx", $u_first_char_params . 'first_char=' . $char) . '#memberlist',
1120            ];
1121        }
1122
1123        /**
1124         * Modify memberlist sort and pagination parameters
1125         *
1126         * @event core.memberlist_modify_sort_pagination_params
1127         * @var array    sort_params                Array with URL parameters for sorting
1128         * @var array    params                    Array with URL parameters for pagination
1129         * @var array    first_characters        Array that maps each letter in a-z, 'other' and the empty string to their display representation
1130         * @var string    u_first_char_params        Concatenated URL parameters for first character search links
1131         * @var array    first_char_block_vars    Template block variables for each first character
1132         * @var int        total_users                Total number of users found in this search
1133         * @since 3.2.6-RC1
1134         */
1135        $vars = [
1136            'sort_params',
1137            'params',
1138            'first_characters',
1139            'u_first_char_params',
1140            'first_char_block_vars',
1141            'total_users',
1142        ];
1143        extract($phpbb_dispatcher->trigger_event('core.memberlist_modify_sort_pagination_params', compact($vars)));
1144
1145        $template->assign_block_vars_array('first_char', $first_char_block_vars);
1146
1147        $pagination_url = append_sid("{$phpbb_root_path}memberlist.$phpEx", implode('&amp;', $params));
1148        $sort_url = append_sid("{$phpbb_root_path}memberlist.$phpEx", implode('&amp;', $sort_params));
1149
1150        unset($search_params, $sort_params);
1151
1152        // Some search user specific data
1153        if (($mode == '' || $mode == 'searchuser') && ($config['load_search'] || $auth->acl_get('a_')))
1154        {
1155            $group_selected = $request->variable('search_group_id', 0);
1156            $s_group_select = '<option value="0"' . ((!$group_selected) ? ' selected="selected"' : '') . '>&nbsp;</option>';
1157            $group_ids = array();
1158
1159            /**
1160            * @todo add this to a separate function (function is responsible for returning the groups the user is able to see based on the users group membership)
1161            */
1162
1163            if ($auth->acl_gets('a_group', 'a_groupadd', 'a_groupdel'))
1164            {
1165                $sql = 'SELECT group_id, group_name, group_type
1166                    FROM ' . GROUPS_TABLE;
1167
1168                if (!$config['coppa_enable'])
1169                {
1170                    $sql .= " WHERE group_name <> 'REGISTERED_COPPA'";
1171                }
1172
1173                $sql .= ' ORDER BY group_name ASC';
1174            }
1175            else
1176            {
1177                $sql = 'SELECT g.group_id, g.group_name, g.group_type
1178                    FROM ' . GROUPS_TABLE . ' g
1179                    LEFT JOIN ' . USER_GROUP_TABLE . ' ug
1180                        ON (
1181                            g.group_id = ug.group_id
1182                            AND ug.user_id = ' . $user->data['user_id'] . '
1183                            AND ug.user_pending = 0
1184                        )
1185                    WHERE (g.group_type <> ' . GROUP_HIDDEN . ' OR ug.user_id = ' . $user->data['user_id'] . ')';
1186
1187                if (!$config['coppa_enable'])
1188                {
1189                    $sql .= " AND g.group_name <> 'REGISTERED_COPPA'";
1190                }
1191
1192                $sql .= ' ORDER BY g.group_name ASC';
1193            }
1194            $result = $db->sql_query($sql);
1195
1196            while ($row = $db->sql_fetchrow($result))
1197            {
1198                $group_ids[] = $row['group_id'];
1199                $s_group_select .= '<option value="' . $row['group_id'] . '"' . (($group_selected == $row['group_id']) ? ' selected="selected"' : '') . '>' . $group_helper->get_name($row['group_name']) . '</option>';
1200            }
1201            $db->sql_freeresult($result);
1202
1203            if ($group_selected !== 0 && !in_array($group_selected, $group_ids))
1204            {
1205                trigger_error('NO_GROUP');
1206            }
1207
1208            $template->assign_vars(array(
1209                'USERNAME'    => $username,
1210                'EMAIL'        => $email,
1211                'JOINED'    => implode('-', $joined),
1212                'ACTIVE'    => implode('-', $active),
1213                'COUNT'        => $count,
1214                'IP'        => $ipdomain,
1215
1216                'S_IP_SEARCH_ALLOWED'    => ($auth->acl_getf_global('m_info')) ? true : false,
1217                'S_EMAIL_SEARCH_ALLOWED'=> ($auth->acl_get('a_user')) ? true : false,
1218                'S_IN_SEARCH_POPUP'        => ($form && $field) ? true : false,
1219                'S_SEARCH_USER'            => ($mode == 'searchuser' || ($mode == '' && $submit)),
1220                'S_FORM_NAME'            => $form,
1221                'S_FIELD_NAME'            => $field,
1222                'S_SELECT_SINGLE'        => $select_single,
1223                'S_COUNT_OPTIONS'        => $s_find_count,
1224                'S_SORT_OPTIONS'        => $s_sort_key,
1225                'S_JOINED_TIME_OPTIONS'    => $s_find_join_time,
1226                'S_ACTIVE_TIME_OPTIONS'    => $s_find_active_time,
1227                'S_GROUP_SELECT'        => $s_group_select,
1228                'S_USER_SEARCH_ACTION'    => append_sid("{$phpbb_root_path}memberlist.$phpEx", "mode=searchuser&amp;form=$form&amp;field=$field"))
1229            );
1230        }
1231
1232        $start = $pagination->validate_start($start, $config['topics_per_page'], $total_users);
1233
1234        // Get us some users :D
1235        $sql = "SELECT u.user_id
1236            FROM " . USERS_TABLE . " u
1237                $sql_from
1238            WHERE " . $db->sql_in_set('u.user_type', $user_types) . "
1239                $sql_where
1240            ORDER BY $order_by";
1241        $result = $db->sql_query_limit($sql, $config['topics_per_page'], $start);
1242
1243        $user_list = array();
1244        while ($row = $db->sql_fetchrow($result))
1245        {
1246            $user_list[] = (int) $row['user_id'];
1247        }
1248        $db->sql_freeresult($result);
1249
1250        // Load custom profile fields
1251        if ($config['load_cpf_memberlist'])
1252        {
1253            /* @var $cp \phpbb\profilefields\manager */
1254            $cp = $phpbb_container->get('profilefields.manager');
1255
1256            $cp_row = $cp->generate_profile_fields_template_headlines('field_show_on_ml');
1257            foreach ($cp_row as $profile_field)
1258            {
1259                $template->assign_block_vars('custom_fields', $profile_field);
1260            }
1261        }
1262
1263        $leaders_set = false;
1264        // So, did we get any users?
1265        if (count($user_list))
1266        {
1267            // Session time?! Session time...
1268            $sql = 'SELECT session_user_id, MAX(session_time) AS session_time, MIN(session_viewonline) AS session_viewonline
1269                FROM ' . SESSIONS_TABLE . '
1270                WHERE session_time >= ' . (time() - $config['session_length']) . '
1271                    AND ' . $db->sql_in_set('session_user_id', $user_list) . '
1272                GROUP BY session_user_id';
1273            $result = $db->sql_query($sql);
1274
1275            $session_ary = [];
1276            while ($row = $db->sql_fetchrow($result))
1277            {
1278                $session_ary[$row['session_user_id']] = [
1279                    'session_time' => $row['session_time'],
1280                    'session_viewonline' => $row['session_viewonline'],
1281                ];
1282            }
1283            $db->sql_freeresult($result);
1284
1285            // Do the SQL thang
1286            if ($mode == 'group')
1287            {
1288                $sql_from_ary = explode(',', $sql_from);
1289                $extra_tables = [];
1290                foreach ($sql_from_ary as $entry)
1291                {
1292                    $table_data = explode(' ', trim($entry));
1293
1294                    if (empty($table_data[0]) || empty($table_data[1]))
1295                    {
1296                        continue;
1297                    }
1298
1299                    $extra_tables[$table_data[0]] = $table_data[1];
1300                }
1301
1302                $sql_array = array(
1303                    'SELECT'    => 'u.*' . $sql_select,
1304                    'FROM'        => array_merge([USERS_TABLE => 'u'], $extra_tables),
1305                    'WHERE'        => $db->sql_in_set('u.user_id', $user_list) . $sql_where_data . '',
1306                );
1307            }
1308            else
1309            {
1310                $sql_array = array(
1311                    'SELECT'    => 'u.*',
1312                    'FROM'        => array(
1313                        USERS_TABLE        => 'u'
1314                    ),
1315                    'WHERE'        => $db->sql_in_set('u.user_id', $user_list),
1316                );
1317            }
1318
1319            /**
1320             * Modify user data SQL before member row is created
1321             *
1322             * @event core.memberlist_modify_memberrow_sql
1323             * @var string    mode                Memberlist mode
1324             * @var string    sql_select            Additional select statement
1325             * @var string    sql_from            Additional from statement
1326             * @var array    sql_array            Array containing the main query
1327             * @var array    user_list            Array containing list of users
1328             * @since 3.2.6-RC1
1329             */
1330            $vars = array(
1331                'mode',
1332                'sql_select',
1333                'sql_from',
1334                'sql_array',
1335                'user_list',
1336            );
1337            extract($phpbb_dispatcher->trigger_event('core.memberlist_modify_memberrow_sql', compact($vars)));
1338
1339            $sql = $db->sql_build_query('SELECT', $sql_array);
1340            $result = $db->sql_query($sql);
1341
1342            $id_cache = array();
1343            while ($row = $db->sql_fetchrow($result))
1344            {
1345                $row['session_time'] = $session_ary[$row['user_id']]['session_time'] ?? 0;
1346                $row['session_viewonline'] = $session_ary[$row['user_id']]['session_viewonline'] ?? 0;
1347                $row['last_visit'] = $row['user_last_active'] ?: $row['session_time'];
1348
1349                $id_cache[$row['user_id']] = $row;
1350            }
1351
1352            $db->sql_freeresult($result);
1353
1354            // Load custom profile fields if required
1355            if ($config['load_cpf_memberlist'])
1356            {
1357                // Grab all profile fields from users in id cache for later use - similar to the poster cache
1358                $profile_fields_cache = $cp->grab_profile_fields_data($user_list);
1359
1360                // Filter the fields we don't want to show
1361                foreach ($profile_fields_cache as $user_id => $user_profile_fields)
1362                {
1363                    foreach ($user_profile_fields as $field_ident => $profile_field)
1364                    {
1365                        if (!$profile_field['data']['field_show_on_ml'])
1366                        {
1367                            unset($profile_fields_cache[$user_id][$field_ident]);
1368                        }
1369                    }
1370                }
1371            }
1372
1373            // If we sort by last active date we need to adjust the id cache due to user_lastvisit not being the last active date...
1374            if ($sort_key == 'l')
1375            {
1376//                uasort($id_cache, create_function('$first, $second', "return (\$first['last_visit'] == \$second['last_visit']) ? 0 : ((\$first['last_visit'] < \$second['last_visit']) ? $lesser_than : ($lesser_than * -1));"));
1377                usort($user_list,  'phpbb_sort_last_active');
1378            }
1379
1380            // do we need to display contact fields as such
1381            $use_contact_fields = true;
1382
1383            /**
1384             * Modify list of users before member row is created
1385             *
1386             * @event core.memberlist_memberrow_before
1387             * @var array    user_list            Array containing list of users
1388             * @var bool    use_contact_fields    Should we display contact fields as such?
1389             * @since 3.1.7-RC1
1390             */
1391            $vars = array('user_list', 'use_contact_fields');
1392            extract($phpbb_dispatcher->trigger_event('core.memberlist_memberrow_before', compact($vars)));
1393
1394            for ($i = 0, $end = count($user_list); $i < $end; ++$i)
1395            {
1396                $user_id = $user_list[$i];
1397                $row = $id_cache[$user_id];
1398                $is_leader = (isset($row['group_leader']) && $row['group_leader']) ? true : false;
1399                $leaders_set = ($leaders_set || $is_leader);
1400
1401                $cp_row = array();
1402                if ($config['load_cpf_memberlist'])
1403                {
1404                    $cp_row = (isset($profile_fields_cache[$user_id])) ? $cp->generate_profile_fields_template_data($profile_fields_cache[$user_id], $use_contact_fields) : array();
1405                }
1406
1407                $memberrow = array_merge(phpbb_show_profile($row, false, false, false), array(
1408                    'ROW_NUMBER'        => $i + ($start + 1),
1409
1410                    'S_CUSTOM_PROFILE'    => (isset($cp_row['row']) && count($cp_row['row'])) ? true : false,
1411                    'S_GROUP_LEADER'    => $is_leader,
1412                    'S_INACTIVE'        => $row['user_type'] == USER_INACTIVE,
1413
1414                    'U_VIEW_PROFILE'    => get_username_string('profile', $user_id, $row['username']),
1415                ));
1416
1417                if (isset($cp_row['row']) && count($cp_row['row']))
1418                {
1419                    $memberrow = array_merge($memberrow, $cp_row['row']);
1420                }
1421
1422                /**
1423                 * Modify the memberrow data before template variables are assigned.
1424                 *
1425                 * @event core.memberlist_modify_memberrow
1426                 * @var int        user_id        The current user ID.
1427                 * @var array    row            Array of raw user data.
1428                 * @var array    memberrow    Array of member template variables.
1429                 * @since 3.3.16-RC1
1430                 */
1431                $vars = ['user_id', 'row', 'memberrow'];
1432                extract($phpbb_dispatcher->trigger_event('core.memberlist_modify_memberrow', compact($vars)));
1433
1434                $template->assign_block_vars('memberrow', $memberrow);
1435
1436                if (isset($cp_row['blockrow']) && count($cp_row['blockrow']))
1437                {
1438                    foreach ($cp_row['blockrow'] as $field_data)
1439                    {
1440                        $template->assign_block_vars('memberrow.custom_fields', $field_data);
1441                    }
1442                }
1443
1444                unset($id_cache[$user_id]);
1445            }
1446        }
1447
1448        $pagination->generate_template_pagination($pagination_url, 'pagination', 'start', $total_users, $config['topics_per_page'], $start);
1449
1450        // Generate page
1451        $template_vars = array(
1452            'TOTAL_USERS'    => $user->lang('LIST_USERS', (int) $total_users),
1453
1454            'PROFILE_IMG'    => $user->img('icon_user_profile', $user->lang['PROFILE']),
1455            'PM_IMG'        => $user->img('icon_contact_pm', $user->lang['SEND_PRIVATE_MESSAGE']),
1456            'EMAIL_IMG'        => $user->img('icon_contact_email', $user->lang['EMAIL']),
1457            'SEARCH_IMG'    => $user->img('icon_user_search', $user->lang['SEARCH']),
1458
1459            'U_FIND_MEMBER'            => ($config['load_search'] || $auth->acl_get('a_')) ? append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=searchuser' . (($start) ? "&amp;start=$start" : '') . (!empty($params) ? '&amp;' . implode('&amp;', $params) : '')) : '',
1460            'U_HIDE_FIND_MEMBER'    => ($mode == 'searchuser' || ($mode == '' && $submit)) ? $u_hide_find_member : '',
1461            'U_LIVE_SEARCH'            => ($config['allow_live_searches']) ? append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=livesearch') : false,
1462            'U_SORT_USERNAME'        => $sort_url . '&amp;sk=a&amp;sd=' . (($sort_key == 'a' && $sort_dir == 'a') ? 'd' : 'a'),
1463            'U_SORT_JOINED'            => $sort_url . '&amp;sk=c&amp;sd=' . (($sort_key == 'c' && $sort_dir == 'd') ? 'a' : 'd'),
1464            'U_SORT_POSTS'            => $sort_url . '&amp;sk=d&amp;sd=' . (($sort_key == 'd' && $sort_dir == 'd') ? 'a' : 'd'),
1465            'U_SORT_EMAIL'            => $sort_url . '&amp;sk=e&amp;sd=' . (($sort_key == 'e' && $sort_dir == 'd') ? 'a' : 'd'),
1466            'U_SORT_ACTIVE'            => ($auth->acl_get('u_viewonline')) ? $sort_url . '&amp;sk=l&amp;sd=' . (($sort_key == 'l' && $sort_dir == 'd') ? 'a' : 'd') : '',
1467            'U_SORT_RANK'            => $sort_url . '&amp;sk=m&amp;sd=' . (($sort_key == 'm' && $sort_dir == 'd') ? 'a' : 'd'),
1468            'U_LIST_CHAR'            => $sort_url . '&amp;sk=a&amp;sd=' . (($sort_key == 'l' && $sort_dir == 'd') ? 'a' : 'd'),
1469
1470            'S_SHOW_GROUP'        => ($mode == 'group') ? true : false,
1471            'S_VIEWONLINE'        => $auth->acl_get('u_viewonline'),
1472            'S_LEADERS_SET'        => $leaders_set,
1473            'S_MODE_SELECT'        => $s_sort_key,
1474            'S_ORDER_SELECT'    => $s_sort_dir,
1475            'S_MODE_ACTION'        => $pagination_url,
1476        );
1477
1478        /**
1479         * Modify memberlist page template vars
1480         *
1481         * @event core.memberlist_modify_template_vars
1482         * @var array    params                Array containing URL parameters
1483         * @var string    sort_url            Sorting URL base
1484         * @var array    template_vars        Array containing template vars
1485         * @since 3.2.2-RC1
1486         */
1487        $vars = array('params', 'sort_url', 'template_vars');
1488        extract($phpbb_dispatcher->trigger_event('core.memberlist_modify_template_vars', compact($vars)));
1489
1490        $template->assign_vars($template_vars);
1491}
1492
1493// Output the page
1494page_header($page_title);
1495
1496$template->set_filenames(array(
1497    'body' => $template_html)
1498);
1499make_jumpbox(append_sid("{$phpbb_root_path}viewforum.$phpEx"));
1500
1501page_footer();