Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 833
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);
21
22/** @var \phpbb\controller\helper $controller_helper */
23$controller_helper = $phpbb_container->get('controller.helper');
24
25// Start session management
26$user->session_begin();
27$auth->acl($user->data);
28$user->setup('search');
29
30// Define initial vars
31$mode            = $request->variable('mode', '');
32$search_id        = $request->variable('search_id', '');
33$start            = max($request->variable('start', 0), 0);
34$post_id        = $request->variable('p', 0);
35$topic_id        = $request->variable('t', 0);
36$view            = $request->variable('view', '');
37
38$submit            = $request->variable('submit', false);
39$keywords        = $request->variable('keywords', '', true);
40$add_keywords    = $request->variable('add_keywords', '', true);
41$author            = $request->variable('author', '', true);
42$author_id        = $request->variable('author_id', 0);
43$show_results    = ($topic_id) ? 'posts' : $request->variable('sr', 'posts');
44$show_results    = ($show_results == 'posts') ? 'posts' : 'topics';
45$search_terms    = $request->variable('terms', 'all');
46$search_fields    = $request->variable('sf', 'all');
47$search_child    = $request->variable('sc', true);
48
49$sort_days        = $request->variable('st', 0);
50$sort_key        = $request->variable('sk', 't');
51$sort_dir        = $request->variable('sd', 'd');
52
53$return_chars    = $request->variable('ch', $topic_id ? 0 : (int) $config['default_search_return_chars']);
54$search_forum    = $request->variable('fid', array(0));
55
56// We put login boxes for the case if search_id is newposts, egosearch or unreadposts
57// because a guest should be able to log in even if guests search is not permitted
58
59switch ($search_id)
60{
61    // Egosearch is an author search
62    case 'egosearch':
63        $author_id = $user->data['user_id'];
64        if ($user->data['user_id'] == ANONYMOUS)
65        {
66            login_box('', $user->lang['LOGIN_EXPLAIN_EGOSEARCH']);
67        }
68    break;
69
70    // Search for unread posts needs to be allowed and user to be logged in if topics tracking for guests is disabled
71    case 'unreadposts':
72        if (!$config['load_unreads_search'])
73        {
74            $template->assign_var('S_NO_SEARCH', true);
75            trigger_error('NO_SEARCH_UNREADS');
76        }
77        else if (!$config['load_anon_lastread'] && !$user->data['is_registered'])
78        {
79            login_box('', $user->lang['LOGIN_EXPLAIN_UNREADSEARCH']);
80        }
81    break;
82
83    // The "new posts" search uses user_lastvisit which is user based, so it should require user to log in.
84    case 'newposts':
85        if ($user->data['user_id'] == ANONYMOUS)
86        {
87            login_box('', $user->lang['LOGIN_EXPLAIN_NEWPOSTS']);
88        }
89    break;
90
91    default:
92        // There's nothing to do here for now ;)
93    break;
94}
95
96$search_auth_check_override = false;
97/**
98* This event allows you to override search auth checks
99*
100* @event core.search_auth_check_override
101* @var    bool    search_auth_check_override    Whether or not the search auth check overridden
102* @since 3.3.14-RC1
103*/
104$vars = [
105    'search_auth_check_override',
106];
107extract($phpbb_dispatcher->trigger_event('core.search_auth_check_override', compact($vars)));
108
109// Is user able to search? Has search been disabled?
110if (!$search_auth_check_override && (!$auth->acl_get('u_search') || !$auth->acl_getf_global('f_search') || !$config['load_search']))
111{
112    $template->assign_var('S_NO_SEARCH', true);
113    trigger_error('NO_SEARCH');
114}
115
116// Check search load limit
117if ($user->load && $config['limit_search_load'] && ($user->load > doubleval($config['limit_search_load'])))
118{
119    $template->assign_var('S_NO_SEARCH', true);
120    trigger_error('NO_SEARCH_LOAD');
121}
122
123// It is applicable if the configuration setting is non-zero, and the user cannot
124// ignore the flood setting, and the search is a keyword search.
125$interval = ($user->data['user_id'] == ANONYMOUS) ? $config['search_anonymous_interval'] : $config['search_interval'];
126if ($interval && !in_array($search_id, array('unreadposts', 'unanswered', 'active_topics', 'egosearch')) && !$auth->acl_get('u_ignoreflood'))
127{
128    if ($user->data['user_last_search'] > time() - $interval)
129    {
130        $template->assign_var('S_NO_SEARCH', true);
131        trigger_error($user->lang('NO_SEARCH_TIME', (int) ($user->data['user_last_search'] + $interval - time())));
132    }
133}
134
135// Define some vars
136$limit_days        = array(0 => $user->lang['ALL_RESULTS'], 1 => $user->lang['1_DAY'], 7 => $user->lang['7_DAYS'], 14 => $user->lang['2_WEEKS'], 30 => $user->lang['1_MONTH'], 90 => $user->lang['3_MONTHS'], 180 => $user->lang['6_MONTHS'], 365 => $user->lang['1_YEAR']);
137$sort_by_text    = array('a' => $user->lang['SORT_AUTHOR'], 't' => $user->lang['SORT_TIME'], 'f' => $user->lang['SORT_FORUM'], 'i' => $user->lang['SORT_TOPIC_TITLE'], 's' => $user->lang['SORT_POST_SUBJECT']);
138
139$s_limit_days = $s_sort_key = $s_sort_dir = $u_sort_param = '';
140gen_sort_selects($limit_days, $sort_by_text, $sort_days, $sort_key, $sort_dir, $s_limit_days, $s_sort_key, $s_sort_dir, $u_sort_param);
141
142/* @var $phpbb_content_visibility \phpbb\content_visibility */
143$phpbb_content_visibility = $phpbb_container->get('content.visibility');
144
145/* @var $pagination \phpbb\pagination */
146$pagination = $phpbb_container->get('pagination');
147
148$template->assign_block_vars('navlinks', array(
149    'BREADCRUMB_NAME'    => $user->lang('SEARCH'),
150    'U_BREADCRUMB'        => append_sid("{$phpbb_root_path}search.$phpEx"),
151));
152
153/**
154* This event allows you to alter the above parameters, such as keywords and submit
155*
156* @event core.search_modify_submit_parameters
157* @var    string    keywords    The search keywords
158* @var    string    author        Specifies the author match, when ANONYMOUS is also a search-match
159* @var    int        author_id    ID of the author to search by
160* @var    string    search_id    Predefined search type name
161* @var    bool    submit        Whether or not the form has been submitted
162* @since 3.1.10-RC1
163*/
164$vars = array(
165    'keywords',
166    'author',
167    'author_id',
168    'search_id',
169    'submit',
170);
171extract($phpbb_dispatcher->trigger_event('core.search_modify_submit_parameters', compact($vars)));
172
173if ($keywords || $author || $author_id || $search_id || $submit)
174{
175    // clear arrays
176    $id_ary = array();
177
178    // If we are looking for authors get their ids
179    $author_id_ary = array();
180    $sql_author_match = '';
181    if ($author_id)
182    {
183        $author_id_ary[] = $author_id;
184    }
185    else if ($author)
186    {
187        if ((strpos($author, '*') !== false) && (utf8_strlen(str_replace(array('*', '%'), '', $author)) < $config['min_search_author_chars']))
188        {
189            trigger_error($user->lang('TOO_FEW_AUTHOR_CHARS', (int) $config['min_search_author_chars']));
190        }
191
192        $sql_where = (strpos($author, '*') !== false) ? ' username_clean ' . $db->sql_like_expression(str_replace('*', $db->get_any_char(), utf8_clean_string($author))) : " username_clean = '" . $db->sql_escape(utf8_clean_string($author)) . "'";
193
194        $sql = 'SELECT user_id
195            FROM ' . USERS_TABLE . "
196            WHERE $sql_where
197                AND user_type <> " . USER_IGNORE;
198        $result = $db->sql_query_limit($sql, 100);
199
200        while ($row = $db->sql_fetchrow($result))
201        {
202            $author_id_ary[] = (int) $row['user_id'];
203        }
204        $db->sql_freeresult($result);
205
206        $sql_where = (strpos($author, '*') !== false) ? ' post_username ' . $db->sql_like_expression(str_replace('*', $db->get_any_char(), utf8_clean_string($author))) : " post_username = '" . $db->sql_escape(utf8_clean_string($author)) . "'";
207
208        $sql = 'SELECT 1 as guest_post
209            FROM ' . POSTS_TABLE . "
210            WHERE $sql_where
211                AND poster_id = " . ANONYMOUS;
212        $result = $db->sql_query_limit($sql, 1);
213        $found_guest_post = $db->sql_fetchfield('guest_post');
214        $db->sql_freeresult($result);
215
216        if ($found_guest_post)
217        {
218            $author_id_ary[] = ANONYMOUS;
219            $sql_author_match = (strpos($author, '*') !== false) ? ' ' . $db->sql_like_expression(str_replace('*', $db->get_any_char(), utf8_clean_string($author))) : " = '" . $db->sql_escape(utf8_clean_string($author)) . "'";
220        }
221
222        if (!count($author_id_ary))
223        {
224            trigger_error('NO_SEARCH_RESULTS');
225        }
226    }
227
228    // if we search in an existing search result just add the additional keywords. But we need to use "all search terms"-mode
229    // so we can keep the old keywords in their old mode, but add the new ones as required words
230    if ($add_keywords)
231    {
232        if ($search_terms == 'all')
233        {
234            $keywords .= ' ' . $add_keywords;
235        }
236        else
237        {
238            $search_terms = 'all';
239            $keywords = implode(' |', explode(' ', preg_replace('#\s+#u', ' ', $keywords))) . ' ' .$add_keywords;
240        }
241    }
242
243    // Which forums should not be searched? Author searches are also carried out in unindexed forums
244    if (empty($keywords) && count($author_id_ary))
245    {
246        $ex_fid_ary = array_keys($auth->acl_getf('!f_read', true));
247    }
248    else
249    {
250        $ex_fid_ary = array_unique(array_merge(array_keys($auth->acl_getf('!f_read', true)), array_keys($auth->acl_getf('!f_search', true))));
251    }
252
253    // Consider if there are any forums where can read forum = no, can read topics = yes
254    // In these cases, the user should see the topic title in the search results but not the link to the topic (or any posts) because they don't have the permissions
255    if ($request->variable('sr', '') == 'topics' && $search_fields == 'titleonly')
256    {
257        // The user could get here from a quick search through the viewforum page, or by doing a main search displayed by topics and searching only the topic titles.
258        // Allow the 'can read topics = yes' forums back in to the search by removing from $ex_fid_ary any of the 'can read topics' forums
259        $ex_fid_ary = array_diff($ex_fid_ary, array_keys($auth->acl_getf('f_list_topics', true)));
260    }
261
262    $not_in_fid = (count($ex_fid_ary)) ? 'WHERE ' . $db->sql_in_set('f.forum_id', $ex_fid_ary, true) . " OR (f.forum_password <> '' AND fa.user_id <> " . (int) $user->data['user_id'] . ')' : "";
263
264    $sql = 'SELECT f.forum_id, f.forum_name, f.parent_id, f.forum_type, f.right_id, f.forum_password, f.forum_flags, fa.user_id
265        FROM ' . FORUMS_TABLE . ' f
266        LEFT JOIN ' . FORUMS_ACCESS_TABLE . " fa ON (fa.forum_id = f.forum_id
267            AND fa.session_id = '" . $db->sql_escape($user->session_id) . "')
268        $not_in_fid
269        ORDER BY f.left_id";
270    $result = $db->sql_query($sql);
271
272    $right_id = 0;
273    $reset_search_forum = true;
274    while ($row = $db->sql_fetchrow($result))
275    {
276        if ($row['forum_password'] && $row['user_id'] != $user->data['user_id'])
277        {
278            $ex_fid_ary[] = (int) $row['forum_id'];
279            continue;
280        }
281
282        // Exclude forums from active topics
283        if (!($row['forum_flags'] & FORUM_FLAG_ACTIVE_TOPICS) && ($search_id == 'active_topics'))
284        {
285            $ex_fid_ary[] = (int) $row['forum_id'];
286            continue;
287        }
288
289        if (count($search_forum))
290        {
291            if ($search_child)
292            {
293                if (in_array($row['forum_id'], $search_forum) && $row['right_id'] > $right_id)
294                {
295                    $right_id = (int) $row['right_id'];
296                }
297                else if ($row['right_id'] < $right_id)
298                {
299                    continue;
300                }
301            }
302
303            if (!in_array($row['forum_id'], $search_forum))
304            {
305                $ex_fid_ary[] = (int) $row['forum_id'];
306                $reset_search_forum = false;
307            }
308        }
309    }
310    $db->sql_freeresult($result);
311
312    // find out in which forums the user is allowed to view posts
313    $m_approve_posts_fid_sql = $phpbb_content_visibility->get_global_visibility_sql('post', $ex_fid_ary, 'p.');
314    $m_approve_topics_fid_sql = $phpbb_content_visibility->get_global_visibility_sql('topic', $ex_fid_ary, 't.');
315
316    if ($reset_search_forum)
317    {
318        $search_forum = array();
319    }
320
321    // Select which method we'll use to obtain the post_id or topic_id information
322    try
323    {
324        $search_backend_factory = $phpbb_container->get('search.backend_factory');
325        $search = $search_backend_factory->get_active();
326    }
327    catch (\phpbb\search\exception\no_search_backend_found_exception $e)
328    {
329        trigger_error('NO_SUCH_SEARCH_MODULE');
330    }
331
332    // let the search module split up the keywords
333    if ($keywords)
334    {
335        $correct_query = $search->split_keywords($keywords, $search_terms);
336        $common_words = $search->get_common_words();
337        if (!$correct_query || (!$search->get_search_query() && !count($author_id_ary) && !$search_id))
338        {
339            $ignored = (count($common_words)) ? sprintf($user->lang['IGNORED_TERMS_EXPLAIN'], implode(' ', $common_words)) . '<br />' : '';
340            $word_length = $search->get_word_length();
341            if ($word_length)
342            {
343                trigger_error($ignored . $user->lang('NO_KEYWORDS', $user->lang('CHARACTERS', (int) $word_length['min']), $user->lang('CHARACTERS', (int) $word_length['max'])));
344            }
345            else
346            {
347                trigger_error($ignored);
348            }
349        }
350    }
351
352    if (!$keywords && count($author_id_ary))
353    {
354        // if it is an author search we want to show topics by default
355        $show_results = ($topic_id) ? 'posts' : $request->variable('sr', ($search_id == 'egosearch') ? 'topics' : 'posts');
356        $show_results = ($show_results == 'posts') ? 'posts' : 'topics';
357    }
358
359    // define some variables needed for retrieving post_id/topic_id information
360    $sort_by_sql = [
361        'a' => 'u.username_clean',
362        't' => (($show_results == 'posts') ? 'p.post_time' : 't.topic_last_post_time'),
363        'f' => 'f.forum_id',
364        'i' => 't.topic_title',
365        's' => (($show_results == 'posts') ? 'p.post_subject' : 't.topic_title')
366    ];
367
368    /**
369    * Event to modify the SQL parameters before pre-made searches
370    *
371    * @event core.search_modify_param_before
372    * @var    string    keywords        String of the specified keywords
373    * @var    array    sort_by_sql        Array of SQL sorting instructions
374    * @var    array    ex_fid_ary        Array of excluded forum ids
375    * @var    array    author_id_ary    Array of exclusive author ids
376    * @var    string    search_id        The id of the search request
377    * @var    array    id_ary            Array of post or topic ids for search result
378    * @var    string    show_results    'posts' or 'topics' type of ids
379    * @since 3.1.3-RC1
380    * @changed 3.1.10-RC1 Added id_ary, show_results
381    */
382    $vars = array(
383        'keywords',
384        'sort_by_sql',
385        'ex_fid_ary',
386        'author_id_ary',
387        'search_id',
388        'id_ary',
389        'show_results',
390    );
391    extract($phpbb_dispatcher->trigger_event('core.search_modify_param_before', compact($vars)));
392
393    // pre-made searches
394    $sql = $field = $l_search_title = '';
395    if ($search_id)
396    {
397        switch ($search_id)
398        {
399            // Oh holy Bob, bring us some activity...
400            case 'active_topics':
401                $l_search_title = $user->lang['SEARCH_ACTIVE_TOPICS'];
402                $show_results = 'topics';
403                $sort_key = 't';
404                $sort_dir = 'd';
405                $sort_days = $request->variable('st', 7);
406                $sort_by_sql['t'] = 't.topic_last_post_time';
407
408                gen_sort_selects($limit_days, $sort_by_text, $sort_days, $sort_key, $sort_dir, $s_limit_days, $s_sort_key, $s_sort_dir, $u_sort_param);
409                $s_sort_key = $s_sort_dir = '';
410
411                $last_post_time_sql = ($sort_days) ? ' AND t.topic_last_post_time > ' . (time() - ($sort_days * 24 * 3600)) : '';
412
413                $sql = 'SELECT t.topic_last_post_time, t.topic_id
414                    FROM ' . TOPICS_TABLE . " t
415                    WHERE t.topic_moved_id = 0
416                        $last_post_time_sql
417                        AND " . $m_approve_topics_fid_sql . '
418                        ' . ((count($ex_fid_ary)) ? ' AND ' . $db->sql_in_set('t.forum_id', $ex_fid_ary, true) : '') . '
419                    ORDER BY t.topic_last_post_time DESC';
420                $field = 'topic_id';
421            break;
422
423            case 'unanswered':
424                $l_search_title = $user->lang['SEARCH_UNANSWERED'];
425                $show_results = $request->variable('sr', 'topics');
426                $show_results = ($show_results == 'posts') ? 'posts' : 'topics';
427                $sort_by_sql['t'] = ($show_results == 'posts') ? 'p.post_time' : 't.topic_last_post_time';
428                $sort_by_sql['s'] = ($show_results == 'posts') ? 'p.post_subject' : 't.topic_title';
429                $sql_sort = 'ORDER BY ' . $sort_by_sql[$sort_key] . (($sort_dir == 'a') ? ' ASC' : ' DESC');
430
431                $sort_join = ($sort_key == 'f') ? FORUMS_TABLE . ' f, ' : '';
432                $sql_sort = ($sort_key == 'f') ? ' AND f.forum_id = t.forum_id ' . $sql_sort : $sql_sort;
433
434                if ($sort_days)
435                {
436                    $last_post_time = 'AND ' . ($show_results == 'posts' ? 'p.post_time' : 't.topic_last_post_time') . ' > ' . (time() - ($sort_days * 24 * 3600));
437                }
438                else
439                {
440                    $last_post_time = '';
441                }
442
443                if ($sort_key == 'a')
444                {
445                    $sort_join = USERS_TABLE . ' u, ';
446                    $sql_sort = ' AND u.user_id = ' . ($show_results == 'posts' ? 'p.poster_id ' : 't.topic_last_poster_id ') . $sql_sort;
447                }
448                if ($show_results == 'posts')
449                {
450                    $sql = "SELECT p.post_id
451                        FROM $sort_join" . POSTS_TABLE . ' p, ' . TOPICS_TABLE . " t
452                        WHERE t.topic_posts_approved = 1
453                            AND p.topic_id = t.topic_id
454                            $last_post_time
455                            AND $m_approve_posts_fid_sql
456                            " . ((count($ex_fid_ary)) ? ' AND ' . $db->sql_in_set('p.forum_id', $ex_fid_ary, true) : '') . "
457                            $sql_sort";
458                    $field = 'post_id';
459                }
460                else
461                {
462                    $sql = 'SELECT DISTINCT ' . $sort_by_sql[$sort_key] . ", t.topic_id
463                        FROM $sort_join" . TOPICS_TABLE . " t
464                        WHERE t.topic_posts_approved = 1
465                            AND t.topic_moved_id = 0
466                            $last_post_time
467                            AND $m_approve_topics_fid_sql
468                            " . ((count($ex_fid_ary)) ? ' AND ' . $db->sql_in_set('t.forum_id', $ex_fid_ary, true) : '') . "
469                        $sql_sort";
470                    $field = 'topic_id';
471                }
472            break;
473
474            case 'unreadposts':
475                $l_search_title = $user->lang['SEARCH_UNREAD'];
476                // force sorting
477                $show_results = 'topics';
478                $sort_key = 't';
479                $sort_by_sql['t'] = 't.topic_last_post_time';
480                $sql_sort = 'ORDER BY ' . $sort_by_sql[$sort_key] . (($sort_dir == 'a') ? ' ASC' : ' DESC');
481
482                $sql_where = 'AND t.topic_moved_id = 0
483                    AND ' . $m_approve_topics_fid_sql . '
484                    ' . ((count($ex_fid_ary)) ? 'AND ' . $db->sql_in_set('t.forum_id', $ex_fid_ary, true) : '');
485
486                gen_sort_selects($limit_days, $sort_by_text, $sort_days, $sort_key, $sort_dir, $s_limit_days, $s_sort_key, $s_sort_dir, $u_sort_param);
487                $s_sort_key = $s_sort_dir = $u_sort_param = $s_limit_days = '';
488
489                $template->assign_var('U_MARK_ALL_READ', ($user->data['is_registered'] || $config['load_anon_lastread']) ? $controller_helper->route('phpbb_index_controller', ['hash' => generate_link_hash('global'), 'mark' => 'forums', 'mark_time' => time()]) : '');
490            break;
491
492            case 'newposts':
493                $l_search_title = $user->lang['SEARCH_NEW'];
494                // force sorting
495                $show_results = ($request->variable('sr', 'topics') == 'posts') ? 'posts' : 'topics';
496                $sort_key = 't';
497                $sort_dir = 'd';
498                $sort_by_sql['t'] = ($show_results == 'posts') ? 'p.post_time' : 't.topic_last_post_time';
499                $sql_sort = 'ORDER BY ' . $sort_by_sql[$sort_key] . (($sort_dir == 'a') ? ' ASC' : ' DESC');
500
501                gen_sort_selects($limit_days, $sort_by_text, $sort_days, $sort_key, $sort_dir, $s_limit_days, $s_sort_key, $s_sort_dir, $u_sort_param);
502                $s_sort_key = $s_sort_dir = $u_sort_param = $s_limit_days = '';
503
504                if ($show_results == 'posts')
505                {
506                    $sql = 'SELECT p.post_id
507                        FROM ' . POSTS_TABLE . ' p
508                        WHERE p.post_time > ' . $user->data['user_lastvisit'] . '
509                            AND ' . $m_approve_posts_fid_sql . '
510                            ' . ((count($ex_fid_ary)) ? ' AND ' . $db->sql_in_set('p.forum_id', $ex_fid_ary, true) : '') . "
511                        $sql_sort";
512                    $field = 'post_id';
513                }
514                else
515                {
516                    $sql = 'SELECT t.topic_id
517                        FROM ' . TOPICS_TABLE . ' t
518                        WHERE t.topic_last_post_time > ' . $user->data['user_lastvisit'] . '
519                            AND t.topic_moved_id = 0
520                            AND ' . $m_approve_topics_fid_sql . '
521                            ' . ((count($ex_fid_ary)) ? 'AND ' . $db->sql_in_set('t.forum_id', $ex_fid_ary, true) : '') . "
522                        $sql_sort";
523/*
524        [Fix] queued replies missing from "view new posts" (Bug #42705 - Patch by Paul)
525        - Creates temporary table, query is far from optimized
526
527                    $sql = 'SELECT t.topic_id
528                        FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . ' p
529                        WHERE p.post_time > ' . $user->data['user_lastvisit'] . '
530                            AND t.topic_id = p.topic_id
531                            AND t.topic_moved_id = 0
532                            AND ' . $m_approve_topics_fid_sql . "
533                        GROUP BY t.topic_id
534                        $sql_sort";
535*/
536                    $field = 'topic_id';
537                }
538            break;
539
540            case 'egosearch':
541                $l_search_title = $user->lang['SEARCH_SELF'];
542            break;
543        }
544
545        $template->assign_block_vars('navlinks', array(
546            'BREADCRUMB_NAME'    => $l_search_title,
547            'U_BREADCRUMB'        => append_sid("{$phpbb_root_path}search.$phpEx", "search_id=$search_id"),
548        ));
549    }
550
551    /**
552    * Event to modify data after pre-made searches
553    *
554    * @event core.search_modify_param_after
555    * @var    string    l_search_title    The title of the search page
556    * @var    string    search_id        Predefined search type name
557    * @var    string    show_results    Display topics or posts
558    * @var    string    sql                SQL query corresponding to the pre-made search id
559    * @since 3.1.7-RC1
560    */
561    $vars = array(
562        'l_search_title',
563        'search_id',
564        'show_results',
565        'sql',
566    );
567    extract($phpbb_dispatcher->trigger_event('core.search_modify_param_after', compact($vars)));
568
569    // show_results should not change after this
570    $per_page = ($show_results == 'posts') ? (int) $config['posts_per_page'] : (int) $config['topics_per_page'];
571    $total_match_count = 0;
572
573    // Set limit for the $total_match_count to reduce server load
574    $total_matches_limit = 1000;
575    $found_more_search_matches = false;
576
577    if ($search_id)
578    {
579        if ($sql)
580        {
581            // Only return up to $total_matches_limit+1 ids (the last one will be removed later)
582            $result = $db->sql_query_limit($sql, $total_matches_limit + 1);
583
584            while ($row = $db->sql_fetchrow($result))
585            {
586                $id_ary[] = (int) $row[$field];
587            }
588            $db->sql_freeresult($result);
589        }
590        else if ($search_id == 'unreadposts')
591        {
592            // Only return up to $total_matches_limit+1 ids (the last one will be removed later)
593            $id_ary = array_keys(get_unread_topics($user->data['user_id'], $sql_where, $sql_sort, $total_matches_limit + 1));
594        }
595        else
596        {
597            $search_id = '';
598        }
599
600        $total_match_count = count($id_ary);
601        if ($total_match_count)
602        {
603            // Limit the number to $total_matches_limit for pre-made searches
604            if ($total_match_count > $total_matches_limit)
605            {
606                $found_more_search_matches = true;
607                $total_match_count = $total_matches_limit;
608            }
609
610            // Make sure $start is set to the last page if it exceeds the amount
611            $start = $pagination->validate_start($start, $per_page, $total_match_count);
612
613            $id_ary = array_slice($id_ary, $start, $per_page);
614        }
615        else
616        {
617            // Set $start to 0 if no matches were found
618            $start = 0;
619        }
620    }
621
622    // make sure that some arrays are always in the same order
623    sort($ex_fid_ary);
624    sort($author_id_ary);
625
626    if ($search->get_search_query())
627    {
628        $total_match_count = $search->keyword_search($show_results, $search_fields, $search_terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_posts_fid_sql, $topic_id, $author_id_ary, $sql_author_match, $id_ary, $start, $per_page);
629    }
630    else if (count($author_id_ary))
631    {
632        $firstpost_only = ($search_fields === 'firstpost' || $search_fields == 'titleonly') ? true : false;
633        $total_match_count = $search->author_search($show_results, $firstpost_only, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_posts_fid_sql, $topic_id, $author_id_ary, $sql_author_match, $id_ary, $start, $per_page);
634    }
635
636    /**
637    * Event to search otherwise than by keywords or author
638    *
639    * @event core.search_backend_search_after
640    * @var    string        show_results                'posts' or 'topics' type of ids
641    * @var    string        search_fields                The data fields to search in
642    * @var    string        search_terms                Is either 'all' (use query as entered, words without prefix should default to "have to be in field") or 'any' (ignore search query parts and just return all posts that contain any of the specified words)
643    * @var    array        sort_by_sql                    Array of SQL sorting instructions
644    * @var    string        sort_key                    The sort key
645    * @var    string        sort_dir                    The sort direction
646    * @var    int            sort_days                    Limit the age of results
647    * @var    array        ex_fid_ary                    Array of excluded forum ids
648    * @var    string        m_approve_posts_fid_sql        Specifies which types of posts the user can view in which forums
649    * @var    int            topic_id                    is set to 0 or a topic id, if it is not 0 then only posts in this topic should be searched
650    * @var    array        author_id_ary                Array of exclusive author ids
651    * @var    string        sql_author_match            Specifies the author match, when ANONYMOUS is also a search-match
652    * @var    array        id_ary                        Array of post or topic ids for search result
653    * @var    int            start                        The starting id of the results
654    * @var    int            per_page                    Number of ids each page is supposed to contain
655    * @var    int            total_match_count            The total number of search matches
656    * @since 3.1.10-RC1
657    */
658    $vars = array(
659        'show_results',
660        'search_fields',
661        'search_terms',
662        'sort_by_sql',
663        'sort_key',
664        'sort_dir',
665        'sort_days',
666        'ex_fid_ary',
667        'm_approve_posts_fid_sql',
668        'topic_id',
669        'author_id_ary',
670        'sql_author_match',
671        'id_ary',
672        'start',
673        'per_page',
674        'total_match_count',
675    );
676    extract($phpbb_dispatcher->trigger_event('core.search_backend_search_after', compact($vars)));
677
678    $sql_where = '';
679
680    if (count($id_ary))
681    {
682        $sql_where .= $db->sql_in_set(($show_results == 'posts') ? 'p.post_id' : 't.topic_id', $id_ary);
683        $sql_where .= (count($ex_fid_ary)) ? ' AND (' . $db->sql_in_set('f.forum_id', $ex_fid_ary, true) . ' OR f.forum_id IS NULL)' : '';
684        $sql_where .= ' AND ' . (($show_results == 'posts') ? $m_approve_posts_fid_sql : $m_approve_topics_fid_sql);
685    }
686
687    if ($show_results == 'posts')
688    {
689        include($phpbb_root_path . 'includes/functions_posting.' . $phpEx);
690    }
691    else
692    {
693        include($phpbb_root_path . 'includes/functions_display.' . $phpEx);
694    }
695
696    $user->add_lang('viewtopic');
697
698    // Grab icons
699    $icons = $cache->obtain_icons();
700
701    // define some vars for urls
702    // A single wildcard will make the search results look ugly
703    $hilit = phpbb_clean_search_string(str_replace(array('+', '-', '|', '(', ')', '&quot;'), ' ', $keywords));
704    $hilit = str_replace(' ', '|', $hilit);
705
706    $u_hilit = urlencode(html_entity_decode(str_replace('|', ' ', $hilit), ENT_COMPAT));
707    $u_show_results = 'sr=' . $show_results;
708    $u_search_forum = implode('&amp;fid%5B%5D=', $search_forum);
709
710    $u_search = append_sid("{$phpbb_root_path}search.$phpEx", (($u_sort_param) ? $u_sort_param . '&amp;' : '') . $u_show_results);
711    $u_search .= ($search_id) ? '&amp;search_id=' . $search_id : '';
712    $u_search .= ($u_hilit) ? '&amp;keywords=' . urlencode(html_entity_decode($keywords, ENT_COMPAT)) : '';
713    $u_search .= ($search_terms != 'all') ? '&amp;terms=' . $search_terms : '';
714    $u_search .= ($topic_id) ? '&amp;t=' . $topic_id : '';
715    $u_search .= ($author) ? '&amp;author=' . urlencode(html_entity_decode($author, ENT_COMPAT)) : '';
716    $u_search .= ($author_id) ? '&amp;author_id=' . $author_id : '';
717    $u_search .= ($u_search_forum) ? '&amp;fid%5B%5D=' . $u_search_forum : '';
718    $u_search .= (!$search_child) ? '&amp;sc=0' : '';
719    $u_search .= ($search_fields != 'all') ? '&amp;sf=' . $search_fields : '';
720    $u_search .= $return_chars !== (int) $config['default_search_return_chars'] ? '&amp;ch=' . $return_chars : '';
721
722    /**
723    * Event to add or modify search URL parameters
724    *
725    * @event core.search_modify_url_parameters
726    * @var    string    u_search        Search URL parameters string
727    * @var    string    search_id        Predefined search type name
728    * @var    string    show_results    String indicating the show results mode
729    * @var    string    sql_where        The SQL WHERE string used by search to get topic data
730    * @var    int        total_match_count    The total number of search matches
731    * @var    array    ex_fid_ary        Array of excluded forum ids
732    * @since 3.1.7-RC1
733    * @changed 3.1.10-RC1 Added show_results, sql_where, total_match_count
734    * @changed 3.1.11-RC1 Added ex_fid_ary
735    */
736    $vars = array(
737        'u_search',
738        'search_id',
739        'show_results',
740        'sql_where',
741        'total_match_count',
742        'ex_fid_ary',
743    );
744    extract($phpbb_dispatcher->trigger_event('core.search_modify_url_parameters', compact($vars)));
745
746    if ($sql_where)
747    {
748        $zebra = [];
749
750        if ($show_results == 'posts')
751        {
752            // @todo Joining this query to the one below?
753            $sql = 'SELECT zebra_id, friend, foe
754                FROM ' . ZEBRA_TABLE . '
755                WHERE user_id = ' . $user->data['user_id'];
756            $result = $db->sql_query($sql);
757
758            while ($row = $db->sql_fetchrow($result))
759            {
760                $zebra[($row['friend']) ? 'friend' : 'foe'][] = $row['zebra_id'];
761            }
762            $db->sql_freeresult($result);
763
764            $sql_array = array(
765                'SELECT'    => 'p.*, f.forum_id, f.forum_name, t.*, u.username, u.username_clean, u.user_sig, u.user_sig_bbcode_uid, u.user_colour',
766                'FROM'        => array(
767                    POSTS_TABLE        => 'p',
768                ),
769                'LEFT_JOIN' => array(
770                    array(
771                        'FROM'    => array(TOPICS_TABLE => 't'),
772                        'ON'    => 'p.topic_id = t.topic_id',
773                    ),
774                    array(
775                        'FROM'    => array(FORUMS_TABLE => 'f'),
776                        'ON'    => 'p.forum_id = f.forum_id',
777                    ),
778                    array(
779                        'FROM'    => array(USERS_TABLE => 'u'),
780                        'ON'    => 'p.poster_id = u.user_id',
781                    ),
782                ),
783                'WHERE'    => $sql_where,
784                'ORDER_BY' => $sort_by_sql[$sort_key] . ' ' . (($sort_dir == 'd') ? 'DESC' : 'ASC'),
785            );
786
787            /**
788            * Event to modify the SQL query before the posts data is retrieved
789            *
790            * @event core.search_get_posts_data
791            * @var    array    sql_array        The SQL array
792            * @var    array    zebra            Array of zebra data for the current user
793            * @var    int        total_match_count    The total number of search matches
794            * @var    string    keywords        String of the specified keywords
795            * @var    array    sort_by_sql        Array of SQL sorting instructions
796            * @var    string    s_sort_dir        The sort direction
797            * @var    string    s_sort_key        The sort key
798            * @var    string    s_limit_days    Limit the age of results
799            * @var    array    ex_fid_ary        Array of excluded forum ids
800            * @var    array    author_id_ary    Array of exclusive author ids
801            * @var    string    search_fields    The data fields to search in
802            * @var    int        search_id        The id of the search request
803            * @var    int        start            The starting id of the results
804            * @since 3.1.0-b3
805            */
806            $vars = array(
807                'sql_array',
808                'zebra',
809                'total_match_count',
810                'keywords',
811                'sort_by_sql',
812                's_sort_dir',
813                's_sort_key',
814                's_limit_days',
815                'ex_fid_ary',
816                'author_id_ary',
817                'search_fields',
818                'search_id',
819                'start',
820            );
821            extract($phpbb_dispatcher->trigger_event('core.search_get_posts_data', compact($vars)));
822
823            $sql = $db->sql_build_query('SELECT', $sql_array);
824        }
825        else
826        {
827            $sql_from = TOPICS_TABLE . ' t
828                LEFT JOIN ' . FORUMS_TABLE . ' f ON (f.forum_id = t.forum_id)
829                ' . (($sort_key == 'a') ? ' LEFT JOIN ' . USERS_TABLE . ' u ON (u.user_id = t.topic_poster) ' : '');
830            $sql_select = 't.*, f.forum_id, f.forum_name';
831
832            if ($user->data['is_registered'])
833            {
834                if ($config['load_db_track'] && $author_id !== $user->data['user_id'])
835                {
836                    $sql_from .= ' LEFT JOIN ' . TOPICS_POSTED_TABLE . ' tp ON (tp.user_id = ' . $user->data['user_id'] . '
837                        AND t.topic_id = tp.topic_id)';
838                    $sql_select .= ', tp.topic_posted';
839                }
840
841                if ($config['load_db_lastread'])
842                {
843                    $sql_from .= ' LEFT JOIN ' . TOPICS_TRACK_TABLE . ' tt ON (tt.user_id = ' . $user->data['user_id'] . '
844                            AND t.topic_id = tt.topic_id)
845                        LEFT JOIN ' . FORUMS_TRACK_TABLE . ' ft ON (ft.user_id = ' . $user->data['user_id'] . '
846                            AND ft.forum_id = f.forum_id)';
847                    $sql_select .= ', tt.mark_time, ft.mark_time as f_mark_time';
848                }
849            }
850
851            if ($config['load_anon_lastread'] || ($user->data['is_registered'] && !$config['load_db_lastread']))
852            {
853                $tracking_topics = $request->variable($config['cookie_name'] . '_track', '', true, \phpbb\request\request_interface::COOKIE);
854                $tracking_topics = ($tracking_topics) ? tracking_unserialize($tracking_topics) : array();
855            }
856
857            $sql_order_by = $sort_by_sql[$sort_key] . ' ' . (($sort_dir == 'd') ? 'DESC' : 'ASC');
858
859            /**
860            * Event to modify the SQL query before the topic data is retrieved
861            *
862            * @event core.search_get_topic_data
863            * @var    string    sql_select        The SQL SELECT string used by search to get topic data
864            * @var    string    sql_from        The SQL FROM string used by search to get topic data
865            * @var    string    sql_where        The SQL WHERE string used by search to get topic data
866            * @var    int        total_match_count    The total number of search matches
867            * @var    array    sort_by_sql        Array of SQL sorting instructions
868            * @var    string    sort_dir        The sorting direction
869            * @var    string    sort_key        The sorting key
870            * @var    string    sql_order_by    The SQL ORDER BY string used by search to get topic data
871            * @since 3.1.0-a1
872            * @changed 3.1.0-RC5 Added total_match_count
873            * @changed 3.1.7-RC1 Added sort_by_sql, sort_dir, sort_key, sql_order_by
874            */
875            $vars = array(
876                'sql_select',
877                'sql_from',
878                'sql_where',
879                'total_match_count',
880                'sort_by_sql',
881                'sort_dir',
882                'sort_key',
883                'sql_order_by',
884            );
885            extract($phpbb_dispatcher->trigger_event('core.search_get_topic_data', compact($vars)));
886
887            $sql = "SELECT $sql_select
888                FROM $sql_from
889                WHERE $sql_where
890                ORDER BY $sql_order_by";
891        }
892        $result = $db->sql_query($sql);
893        $result_topic_id = 0;
894
895        $rowset = $attachments = $topic_tracking_info = array();
896
897        if ($show_results == 'topics')
898        {
899            $forums = $rowset = $shadow_topic_list = array();
900            while ($row = $db->sql_fetchrow($result))
901            {
902                $row['forum_id'] = (int) $row['forum_id'];
903                $row['topic_id'] = (int) $row['topic_id'];
904
905                if ($row['topic_status'] == ITEM_MOVED)
906                {
907                    $shadow_topic_list[$row['topic_moved_id']] = $row['topic_id'];
908                }
909
910                $rowset[$row['topic_id']] = $row;
911
912                if (!isset($forums[$row['forum_id']]) && $user->data['is_registered'] && $config['load_db_lastread'])
913                {
914                    $forums[$row['forum_id']]['mark_time'] = $row['f_mark_time'];
915                }
916                $forums[$row['forum_id']]['topic_list'][] = $row['topic_id'];
917                $forums[$row['forum_id']]['rowset'][$row['topic_id']] = &$rowset[$row['topic_id']];
918            }
919            $db->sql_freeresult($result);
920
921            // If we have some shadow topics, update the rowset to reflect their topic information
922            if (count($shadow_topic_list))
923            {
924                $sql = 'SELECT *
925                    FROM ' . TOPICS_TABLE . '
926                    WHERE ' . $db->sql_in_set('topic_id', array_keys($shadow_topic_list));
927                $result = $db->sql_query($sql);
928
929                while ($row = $db->sql_fetchrow($result))
930                {
931                    $orig_topic_id = $shadow_topic_list[$row['topic_id']];
932
933                    // We want to retain some values
934                    $row = array_merge($row, array(
935                        'topic_moved_id'    => $rowset[$orig_topic_id]['topic_moved_id'],
936                        'topic_status'        => $rowset[$orig_topic_id]['topic_status'],
937                        'forum_name'        => $rowset[$orig_topic_id]['forum_name'])
938                    );
939
940                    $rowset[$orig_topic_id] = $row;
941                }
942                $db->sql_freeresult($result);
943            }
944            unset($shadow_topic_list);
945
946            foreach ($forums as $forum_id => $forum)
947            {
948                if ($user->data['is_registered'] && $config['load_db_lastread'])
949                {
950                    $topic_tracking_info[$forum_id] = get_topic_tracking($forum_id, $forum['topic_list'], $forum['rowset'], array($forum_id => $forum['mark_time']));
951                }
952                else if ($config['load_anon_lastread'] || $user->data['is_registered'])
953                {
954                    $topic_tracking_info[$forum_id] = get_complete_topic_tracking($forum_id, $forum['topic_list']);
955
956                    if (!$user->data['is_registered'])
957                    {
958                        $user->data['user_lastmark'] = (isset($tracking_topics['l'])) ? (int) (base_convert($tracking_topics['l'], 36, 10) + $config['board_startdate']) : 0;
959                    }
960                }
961            }
962            unset($forums);
963        }
964        else
965        {
966            $text_only_message = '';
967            $attach_list = array();
968
969            while ($row = $db->sql_fetchrow($result))
970            {
971                /**
972                * Modify the row of a post result before the post_text is trimmed
973                *
974                * @event core.search_modify_post_row
975                * @var    string    hilit                    String to highlight
976                * @var    array    row                        Array with the post data
977                * @var    string    u_hilit                    Highlight string to be injected into URL
978                * @var    string    view                    Search results view mode
979                * @var    array    zebra                    Array with zebra data for the current user
980                * @since 3.2.2-RC1
981                */
982                $vars = array(
983                    'hilit',
984                    'row',
985                    'u_hilit',
986                    'view',
987                    'zebra',
988                );
989                extract($phpbb_dispatcher->trigger_event('core.search_modify_post_row', compact($vars)));
990
991                // We pre-process some variables here for later usage
992                $row['post_text'] = censor_text($row['post_text']);
993
994                $text_only_message = $row['post_text'];
995                // make list items visible as such
996                if ($row['bbcode_uid'])
997                {
998                    $text_only_message = str_replace('[*:' . $row['bbcode_uid'] . ']', '&sdot;&nbsp;', $text_only_message);
999                    // no BBCode in text only message
1000                    strip_bbcode($text_only_message, $row['bbcode_uid']);
1001                }
1002
1003                if ($return_chars === 0 || utf8_strlen($text_only_message) < ($return_chars + 3))
1004                {
1005                    $row['display_text_only'] = false;
1006
1007                    // Does this post have an attachment? If so, add it to the list
1008                    if ($row['post_attachment'] && $config['allow_attachments'])
1009                    {
1010                        $attach_list[$row['forum_id']][] = $row['post_id'];
1011                    }
1012                }
1013                else
1014                {
1015                    $row['post_text'] = $text_only_message;
1016                    $row['display_text_only'] = true;
1017                }
1018
1019                $rowset[] = $row;
1020            }
1021            $db->sql_freeresult($result);
1022
1023            unset($text_only_message);
1024
1025            // Pull attachment data
1026            if (count($attach_list))
1027            {
1028                $use_attach_list = $attach_list;
1029                $attach_list = array();
1030
1031                foreach ($use_attach_list as $forum_id => $_list)
1032                {
1033                    if ($auth->acl_get('u_download') && $auth->acl_get('f_download', $forum_id))
1034                    {
1035                        $attach_list = array_merge($attach_list, $_list);
1036                    }
1037                }
1038            }
1039
1040            if (count($attach_list))
1041            {
1042                $sql = 'SELECT *
1043                    FROM ' . ATTACHMENTS_TABLE . '
1044                    WHERE ' . $db->sql_in_set('post_msg_id', $attach_list) . '
1045                        AND in_message = 0
1046                    ORDER BY filetime DESC, post_msg_id ASC';
1047                $result = $db->sql_query($sql);
1048
1049                while ($row = $db->sql_fetchrow($result))
1050                {
1051                    $attachments[$row['post_msg_id']][] = $row;
1052                }
1053                $db->sql_freeresult($result);
1054            }
1055        }
1056
1057        if ($hilit)
1058        {
1059            // Remove bad highlights
1060            $hilit_array = array_filter(explode('|', $hilit), 'strlen');
1061            foreach ($hilit_array as $key => $value)
1062            {
1063                $hilit_array[$key] = phpbb_clean_search_string($value);
1064                $hilit_array[$key] = str_replace('\*', '\w*?', preg_quote($hilit_array[$key], '#'));
1065                $hilit_array[$key] = preg_replace('#(^|\s)\\\\w\*\?(\s|$)#', '$1\w+?$2', $hilit_array[$key]);
1066            }
1067            $hilit = implode('|', $hilit_array);
1068        }
1069
1070        /**
1071        * Modify the rowset data
1072        *
1073        * @event core.search_modify_rowset
1074        * @var    array    attachments                Array with posts attachments data
1075        * @var    string    hilit                    String to highlight
1076        * @var    array    rowset                    Array with the search results data
1077        * @var    string    show_results            String indicating the show results mode
1078        * @var    array    topic_tracking_info        Array with the topics tracking data
1079        * @var    string    u_hilit                    Highlight string to be injected into URL
1080        * @var    string    view                    Search results view mode
1081        * @var    array    zebra                    Array with zebra data for the current user
1082        * @since 3.1.0-b4
1083        * @changed 3.1.0-b5 Added var show_results
1084        */
1085        $vars = array(
1086            'attachments',
1087            'hilit',
1088            'rowset',
1089            'show_results',
1090            'topic_tracking_info',
1091            'u_hilit',
1092            'view',
1093            'zebra',
1094        );
1095        extract($phpbb_dispatcher->trigger_event('core.search_modify_rowset', compact($vars)));
1096
1097        foreach ($rowset as $row)
1098        {
1099            $forum_id = $row['forum_id'];
1100            $result_topic_id = $row['topic_id'];
1101            $topic_title = censor_text($row['topic_title']);
1102            $replies = $phpbb_content_visibility->get_count('topic_posts', $row, $forum_id) - 1;
1103
1104            $view_topic_url_params = "t=$result_topic_id" . (($u_hilit) ? "&amp;hilit=$u_hilit" : '');
1105            $view_topic_url = append_sid("{$phpbb_root_path}viewtopic.$phpEx", $view_topic_url_params);
1106
1107            $folder_img = $folder_alt = $u_mcp_queue = '';
1108            $topic_type = $posts_unapproved = 0;
1109            $unread_topic = $topic_unapproved = $topic_deleted = false;
1110
1111            if ($show_results == 'topics')
1112            {
1113                if ($config['load_db_track'] && $author_id === $user->data['user_id'])
1114                {
1115                    $row['topic_posted'] = 1;
1116                }
1117
1118                topic_status($row, $replies, (isset($topic_tracking_info[$forum_id][$row['topic_id']]) && $row['topic_last_post_time'] > $topic_tracking_info[$forum_id][$row['topic_id']]) ? true : false, $folder_img, $folder_alt, $topic_type);
1119
1120                $unread_topic = (isset($topic_tracking_info[$forum_id][$row['topic_id']]) && $row['topic_last_post_time'] > $topic_tracking_info[$forum_id][$row['topic_id']]) ? true : false;
1121
1122                $topic_unapproved = (($row['topic_visibility'] == ITEM_UNAPPROVED || $row['topic_visibility'] == ITEM_REAPPROVE) && $auth->acl_get('m_approve', $forum_id)) ? true : false;
1123                $posts_unapproved = ($row['topic_visibility'] == ITEM_APPROVED && $row['topic_posts_unapproved'] && $auth->acl_get('m_approve', $forum_id)) ? true : false;
1124                $topic_deleted = $row['topic_visibility'] == ITEM_DELETED;
1125                $u_mcp_queue = ($topic_unapproved || $posts_unapproved) ? append_sid("{$phpbb_root_path}mcp.$phpEx", 'i=queue&amp;mode=' . (($topic_unapproved) ? 'approve_details' : 'unapproved_posts') . "&amp;t=$result_topic_id") : '';
1126                $u_mcp_queue = (!$u_mcp_queue && $topic_deleted) ? append_sid("{$phpbb_root_path}mcp.$phpEx", "i=queue&amp;mode=deleted_topics&amp;t=$result_topic_id") : $u_mcp_queue;
1127
1128                $row['topic_title'] = preg_replace('#(?!<.*)(?<!\w)(' . $hilit . ')(?!\w|[^<>]*(?:</s(?:cript|tyle))?>)#isu', '<span class="posthilit">$1</span>', $row['topic_title']);
1129
1130                $tpl_ary = array(
1131                    'TOPIC_AUTHOR'                => get_username_string('username', $row['topic_poster'], $row['topic_first_poster_name'], $row['topic_first_poster_colour']),
1132                    'TOPIC_AUTHOR_COLOUR'        => get_username_string('colour', $row['topic_poster'], $row['topic_first_poster_name'], $row['topic_first_poster_colour']),
1133                    'TOPIC_AUTHOR_FULL'            => get_username_string('full', $row['topic_poster'], $row['topic_first_poster_name'], $row['topic_first_poster_colour']),
1134                    'FIRST_POST_TIME'            => $user->format_date($row['topic_time']),
1135                    'FIRST_POST_TIME_RFC3339'    => gmdate(DATE_RFC3339, $row['topic_time']),
1136                    'LAST_POST_SUBJECT'            => $row['topic_last_post_subject'],
1137                    'LAST_POST_TIME'            => $user->format_date($row['topic_last_post_time']),
1138                    'LAST_POST_TIME_RFC3339'    => gmdate(DATE_RFC3339, $row['topic_last_post_time']),
1139                    'LAST_VIEW_TIME'            => $user->format_date($row['topic_last_view_time']),
1140                    'LAST_VIEW_TIME_RFC3339'    => gmdate(DATE_RFC3339, $row['topic_last_view_time']),
1141                    'LAST_POST_AUTHOR'            => get_username_string('username', $row['topic_last_poster_id'], $row['topic_last_poster_name'], $row['topic_last_poster_colour']),
1142                    'LAST_POST_AUTHOR_COLOUR'    => get_username_string('colour', $row['topic_last_poster_id'], $row['topic_last_poster_name'], $row['topic_last_poster_colour']),
1143                    'LAST_POST_AUTHOR_FULL'        => get_username_string('full', $row['topic_last_poster_id'], $row['topic_last_poster_name'], $row['topic_last_poster_colour']),
1144
1145                    'TOPIC_TYPE'        => $topic_type,
1146
1147                    'TOPIC_IMG_STYLE'        => $folder_img,
1148                    'TOPIC_FOLDER_IMG'        => $user->img($folder_img, $folder_alt),
1149                    'TOPIC_FOLDER_IMG_ALT'    => $user->lang[$folder_alt],
1150                    'S_POST_ANNOUNCE'        => $row['topic_type'] == POST_ANNOUNCE,
1151                    'S_POST_GLOBAL'            => $row['topic_type'] == POST_GLOBAL,
1152                    'S_POST_STICKY'            => $row['topic_type'] == POST_STICKY,
1153                    'S_TOPIC_LOCKED'        => $row['topic_status'] == ITEM_LOCKED,
1154                    'S_TOPIC_MOVED'            => $row['topic_status'] == ITEM_MOVED,
1155
1156                    'TOPIC_ICON_IMG'        => (!empty($icons[$row['icon_id']])) ? $icons[$row['icon_id']]['img'] : '',
1157                    'TOPIC_ICON_IMG_WIDTH'    => (!empty($icons[$row['icon_id']])) ? $icons[$row['icon_id']]['width'] : '',
1158                    'TOPIC_ICON_IMG_HEIGHT'    => (!empty($icons[$row['icon_id']])) ? $icons[$row['icon_id']]['height'] : '',
1159                    'ATTACH_ICON_IMG'        => ($auth->acl_get('u_download') && $auth->acl_get('f_download', $forum_id) && $row['topic_attachment']) ? $user->img('icon_topic_attach', $user->lang['TOTAL_ATTACHMENTS']) : '',
1160                    'UNAPPROVED_IMG'        => ($topic_unapproved || $posts_unapproved) ? $user->img('icon_topic_unapproved', ($topic_unapproved) ? 'TOPIC_UNAPPROVED' : 'POSTS_UNAPPROVED') : '',
1161
1162                    'S_TOPIC_TYPE'            => $row['topic_type'],
1163                    'S_USER_POSTED'            => !empty($row['topic_posted']),
1164                    'S_UNREAD_TOPIC'        => $unread_topic,
1165
1166                    'S_TOPIC_REPORTED'        => !empty($row['topic_reported']) && $auth->acl_get('m_report', $forum_id),
1167                    'S_TOPIC_UNAPPROVED'    => $topic_unapproved,
1168                    'S_POSTS_UNAPPROVED'    => $posts_unapproved,
1169                    'S_TOPIC_DELETED'        => $topic_deleted,
1170                    'S_HAS_POLL'            => (bool) $row['poll_start'],
1171
1172                    'U_LAST_POST'            => $auth->acl_get('f_read', $forum_id) ? append_sid("{$phpbb_root_path}viewtopic.$phpEx", 'p=' . $row['topic_last_post_id']) . '#p' . $row['topic_last_post_id'] : false,
1173                    'U_LAST_POST_AUTHOR'    => get_username_string('profile', $row['topic_last_poster_id'], $row['topic_last_poster_name'], $row['topic_last_poster_colour']),
1174                    'U_TOPIC_AUTHOR'        => get_username_string('profile', $row['topic_poster'], $row['topic_first_poster_name'], $row['topic_first_poster_colour']),
1175                    'U_NEWEST_POST'            => $auth->acl_get('f_read', $forum_id) ? append_sid("{$phpbb_root_path}viewtopic.$phpEx", $view_topic_url_params . '&amp;view=unread') . '#unread' : false,
1176                    'U_MCP_REPORT'            => append_sid("{$phpbb_root_path}mcp.$phpEx", 'i=reports&amp;mode=reports&amp;t=' . $result_topic_id),
1177                    'U_MCP_QUEUE'            => $u_mcp_queue,
1178                );
1179            }
1180            else
1181            {
1182                if ((isset($zebra['foe']) && in_array($row['poster_id'], $zebra['foe'])) && (!$view || $view != 'show' || $post_id != $row['post_id']))
1183                {
1184                    $template->assign_block_vars('searchresults', array(
1185                        'S_IGNORE_POST' => true,
1186
1187                        'L_IGNORE_POST' => sprintf($user->lang['POST_BY_FOE'], $row['username'], "<a href=\"$u_search&amp;start=$start&amp;p=" . $row['post_id'] . '&amp;view=show#p' . $row['post_id'] . '">', '</a>'))
1188                    );
1189
1190                    continue;
1191                }
1192
1193                // Replace naughty words such as farty pants
1194                $row['post_subject'] = censor_text($row['post_subject']);
1195
1196                if ($row['display_text_only'])
1197                {
1198                    // now find context for the searched words
1199                    $row['post_text'] = get_context($row['post_text'], array_filter(explode('|', $hilit), 'strlen'), $return_chars);
1200                    $row['post_text'] = bbcode_nl2br($row['post_text']);
1201                }
1202                else
1203                {
1204                    $parse_flags = ($row['bbcode_bitfield'] ? OPTION_FLAG_BBCODE : 0) | OPTION_FLAG_SMILIES;
1205                    $row['post_text'] = generate_text_for_display($row['post_text'], $row['bbcode_uid'], $row['bbcode_bitfield'], $parse_flags, false);
1206
1207                    if (!empty($attachments[$row['post_id']]))
1208                    {
1209                        parse_attachments($forum_id, $row['post_text'], $attachments[$row['post_id']], $update_count);
1210
1211                        // we only display inline attachments
1212                        unset($attachments[$row['post_id']]);
1213                    }
1214                }
1215
1216                if ($hilit)
1217                {
1218                    // post highlighting
1219                    $row['post_text'] = preg_replace('#(?!<.*)(?<!\w)(' . $hilit . ')(?!\w|[^<>]*(?:</s(?:cript|tyle))?>)#isu', '<span class="posthilit">$1</span>', $row['post_text']);
1220                    $row['post_subject'] = preg_replace('#(?!<.*)(?<!\w)(' . $hilit . ')(?!\w|[^<>]*(?:</s(?:cript|tyle))?>)#isu', '<span class="posthilit">$1</span>', $row['post_subject']);
1221                }
1222
1223                $tpl_ary = array(
1224                    'POST_AUTHOR_FULL'        => get_username_string('full', $row['poster_id'], $row['username'], $row['user_colour'], $row['post_username']),
1225                    'POST_AUTHOR_COLOUR'    => get_username_string('colour', $row['poster_id'], $row['username'], $row['user_colour'], $row['post_username']),
1226                    'POST_AUTHOR'            => get_username_string('username', $row['poster_id'], $row['username'], $row['user_colour'], $row['post_username']),
1227                    'U_POST_AUTHOR'            => get_username_string('profile', $row['poster_id'], $row['username'], $row['user_colour'], $row['post_username']),
1228
1229                    'POST_SUBJECT'        => $row['post_subject'],
1230                    'POST_DATE'            => (!empty($row['post_time'])) ? $user->format_date($row['post_time']) : '',
1231                    'MESSAGE'            => $row['post_text']
1232                );
1233            }
1234
1235            $tpl_ary = array_merge($tpl_ary, array(
1236                'FORUM_ID'            => $forum_id,
1237                'TOPIC_ID'            => $result_topic_id,
1238                'POST_ID'            => ($show_results == 'posts') ? $row['post_id'] : false,
1239
1240                'FORUM_TITLE'        => $row['forum_name'],
1241                'TOPIC_TITLE'        => $topic_title,
1242                'TOPIC_REPLIES'        => $replies,
1243                'TOPIC_VIEWS'        => $row['topic_views'],
1244
1245                'U_VIEW_TOPIC'        => $auth->acl_get('f_read', $forum_id) ? $view_topic_url : false,
1246                'U_VIEW_FORUM'        => append_sid("{$phpbb_root_path}viewforum.$phpEx", 'f=' . $forum_id),
1247                'U_VIEW_POST'        => (!empty($row['post_id'])) ? append_sid("{$phpbb_root_path}viewtopic.$phpEx", 'p=' . $row['post_id'] . (($u_hilit) ? '&amp;hilit=' . $u_hilit : '')) . '#p' . $row['post_id'] : '',
1248            ));
1249
1250            /**
1251            * Modify the topic data before it is assigned to the template
1252            *
1253            * @event core.search_modify_tpl_ary
1254            * @var    array    row                Array with topic data
1255            * @var    array    tpl_ary            Template block array with topic data
1256            * @var    string    show_results    Display topics or posts
1257            * @var    string    topic_title        Cleaned topic title
1258            * @var    int        replies            The number of topic replies
1259            * @var    string    view_topic_url    The URL to the topic
1260            * @var    string    folder_img        The folder image of the topic
1261            * @var    string    folder_alt        The alt attribute of the topic folder img
1262            * @var    int        topic_type        The topic type
1263            * @var    bool    unread_topic    Whether the topic has unread posts
1264            * @var    bool    topic_unapproved    Whether the topic is unapproved
1265            * @var    int        posts_unapproved    The number of unapproved posts
1266            * @var    bool    topic_deleted    Whether the topic has been deleted
1267            * @var    string    u_mcp_queue        The URL to the corresponding MCP queue page
1268            * @var    array    zebra            The zebra data of the current user
1269            * @var    array    attachments        All the attachments of the search results
1270            * @since 3.1.0-a1
1271            * @changed 3.1.0-b3 Added vars show_results, topic_title, replies,
1272            *        view_topic_url, folder_img, folder_alt, topic_type, unread_topic,
1273            *        topic_unapproved, posts_unapproved, topic_deleted, u_mcp_queue,
1274            *        zebra, attachments
1275            */
1276            $vars = array(
1277                'row',
1278                'tpl_ary',
1279                'show_results',
1280                'topic_title',
1281                'replies',
1282                'view_topic_url',
1283                'folder_img',
1284                'folder_alt',
1285                'topic_type',
1286                'unread_topic',
1287                'topic_unapproved',
1288                'posts_unapproved',
1289                'topic_deleted',
1290                'u_mcp_queue',
1291                'zebra',
1292                'attachments',
1293            );
1294            extract($phpbb_dispatcher->trigger_event('core.search_modify_tpl_ary', compact($vars)));
1295
1296            $template->assign_block_vars('searchresults', $tpl_ary);
1297
1298            if ($show_results == 'topics')
1299            {
1300                $pagination->generate_template_pagination($view_topic_url, 'searchresults.pagination', 'start', $replies + 1, $config['posts_per_page'], 1, true, true);
1301            }
1302        }
1303
1304        if ($topic_id && ($topic_id == $result_topic_id))
1305        {
1306            $template->assign_vars(array(
1307                'SEARCH_TOPIC'        => $topic_title,
1308                'L_RETURN_TO_TOPIC'    => $user->lang('RETURN_TO', $topic_title),
1309                'U_SEARCH_TOPIC'    => $view_topic_url
1310            ));
1311        }
1312    }
1313    unset($rowset);
1314
1315    // Output header
1316    if ($found_more_search_matches)
1317    {
1318        $l_search_matches = $user->lang('FOUND_MORE_SEARCH_MATCHES', (int) $total_match_count);
1319    }
1320    else
1321    {
1322        $l_search_matches = $user->lang('FOUND_SEARCH_MATCHES', (int) $total_match_count);
1323    }
1324
1325    // Check if search backend supports phrase search or not
1326    $phrase_search_disabled = '';
1327    if (strpos(html_entity_decode($keywords), '"') !== false)
1328    {
1329        $phrase_search_disabled = $search->supports_phrase_search() ? false : true;
1330    }
1331
1332    $pagination->generate_template_pagination($u_search, 'pagination', 'start', $total_match_count, $per_page, $start);
1333
1334    $template->assign_vars(array(
1335        'SEARCH_TITLE'        => $l_search_title,
1336        'SEARCH_MATCHES'    => $l_search_matches,
1337        'SEARCH_WORDS'        => $keywords,
1338        'SEARCHED_QUERY'    => $search->get_search_query(),
1339        'IGNORED_WORDS'        => (!empty($common_words)) ? implode(' ', $common_words) : '',
1340
1341        'PHRASE_SEARCH_DISABLED'        => $phrase_search_disabled,
1342
1343        'TOTAL_MATCHES'        => $total_match_count,
1344        'SEARCH_IN_RESULTS'    => ($search_id) ? false : true,
1345
1346        'S_SELECT_SORT_DIR'        => $s_sort_dir,
1347        'S_SELECT_SORT_KEY'        => $s_sort_key,
1348        'S_SELECT_SORT_DAYS'    => $s_limit_days,
1349        'S_SEARCH_ACTION'        => $u_search,
1350        'S_SHOW_TOPICS'            => ($show_results == 'posts') ? false : true,
1351
1352        'GOTO_PAGE_IMG'        => $user->img('icon_post_target', 'GOTO_PAGE'),
1353        'NEWEST_POST_IMG'    => $user->img('icon_topic_newest', 'VIEW_NEWEST_POST'),
1354        'REPORTED_IMG'        => $user->img('icon_topic_reported', 'TOPIC_REPORTED'),
1355        'UNAPPROVED_IMG'    => $user->img('icon_topic_unapproved', 'TOPIC_UNAPPROVED'),
1356        'DELETED_IMG'        => $user->img('icon_topic_deleted', 'TOPIC_DELETED'),
1357        'POLL_IMG'            => $user->img('icon_topic_poll', 'TOPIC_POLL'),
1358        'LAST_POST_IMG'        => $user->img('icon_topic_latest', 'VIEW_LATEST_POST'),
1359
1360        'U_SEARCH_WORDS'    => $u_search,
1361    ));
1362
1363    /**
1364    * Modify the title and/or load data for the search results page
1365    *
1366    * @event core.search_results_modify_search_title
1367    * @var    int        author_id            ID of the author to search by
1368    * @var    string    l_search_title        The title of the search page
1369    * @var    string    search_id            Predefined search type name
1370    * @var    string    show_results        Search results output mode - topics or posts
1371    * @var    int        start                The starting id of the results
1372    * @var    int        total_match_count    The count of search results
1373    * @var    string    keywords            The search keywords
1374    * @since 3.1.0-RC4
1375    * @changed 3.1.6-RC1 Added total_match_count and keywords
1376    */
1377    $vars = array(
1378        'author_id',
1379        'l_search_title',
1380        'search_id',
1381        'show_results',
1382        'start',
1383        'total_match_count',
1384        'keywords',
1385    );
1386    extract($phpbb_dispatcher->trigger_event('core.search_results_modify_search_title', compact($vars)));
1387
1388    page_header(($l_search_title) ? $l_search_title : $user->lang['SEARCH']);
1389
1390    $template->set_filenames(array(
1391        'body' => 'search_results.html')
1392    );
1393    make_jumpbox(append_sid("{$phpbb_root_path}viewforum.$phpEx"));
1394
1395    page_footer();
1396}
1397
1398// Search forum
1399$rowset = array();
1400$s_forums = '';
1401$sql = 'SELECT f.forum_id, f.forum_name, f.parent_id, f.forum_type, f.left_id, f.right_id, f.forum_password, f.enable_indexing, fa.user_id
1402    FROM ' . FORUMS_TABLE . ' f
1403    LEFT JOIN ' . FORUMS_ACCESS_TABLE . " fa ON (fa.forum_id = f.forum_id
1404        AND fa.session_id = '" . $db->sql_escape($user->session_id) . "')
1405    ORDER BY f.left_id ASC";
1406$result = $db->sql_query($sql);
1407
1408while ($row = $db->sql_fetchrow($result))
1409{
1410    $rowset[(int) $row['forum_id']] = $row;
1411}
1412$db->sql_freeresult($result);
1413
1414$right = $cat_right = $padding_inc = 0;
1415$padding = $forum_list = $holding = '';
1416$pad_store = array('0' => '');
1417
1418/**
1419* Modify the forum select list for advanced search page
1420*
1421* @event core.search_modify_forum_select_list
1422* @var    array    rowset    Array with the forums list data
1423* @since 3.1.10-RC1
1424*/
1425$vars = array('rowset');
1426extract($phpbb_dispatcher->trigger_event('core.search_modify_forum_select_list', compact($vars)));
1427
1428foreach ($rowset as $row)
1429{
1430    if ($row['forum_type'] == FORUM_CAT && ($row['left_id'] + 1 == $row['right_id']))
1431    {
1432        // Non-postable forum with no subforums, don't display
1433        continue;
1434    }
1435
1436    if ($row['forum_type'] == FORUM_POST && ($row['left_id'] + 1 == $row['right_id']) && !$row['enable_indexing'])
1437    {
1438        // Postable forum with no subforums and indexing disabled, don't display
1439        continue;
1440    }
1441
1442    if ($row['forum_type'] == FORUM_LINK || ($row['forum_password'] && !$row['user_id']))
1443    {
1444        // if this forum is a link or password protected (user has not entered the password yet) then skip to the next branch
1445        continue;
1446    }
1447
1448    if ($row['left_id'] < $right)
1449    {
1450        $padding .= '&nbsp; &nbsp;';
1451        $pad_store[$row['parent_id']] = $padding;
1452    }
1453    else if ($row['left_id'] > $right + 1)
1454    {
1455        if (isset($pad_store[$row['parent_id']]))
1456        {
1457            $padding = $pad_store[$row['parent_id']];
1458        }
1459        else
1460        {
1461            continue;
1462        }
1463    }
1464
1465    $right = $row['right_id'];
1466
1467    if ($auth->acl_gets('!f_search', '!f_list', $row['forum_id']))
1468    {
1469        // if the user does not have permissions to search or see this forum skip only this forum/category
1470        continue;
1471    }
1472
1473    $selected = (in_array($row['forum_id'], $search_forum)) ? ' selected="selected"' : '';
1474
1475    if ($row['left_id'] > $cat_right)
1476    {
1477        // make sure we don't forget anything
1478        $s_forums .= $holding;
1479        $holding = '';
1480    }
1481
1482    if ($row['right_id'] - $row['left_id'] > 1)
1483    {
1484        $cat_right = max($cat_right, $row['right_id']);
1485
1486        $holding .= '<option value="' . $row['forum_id'] . '"' . $selected . '>' . $padding . $row['forum_name'] . '</option>';
1487    }
1488    else
1489    {
1490        $s_forums .= $holding . '<option value="' . $row['forum_id'] . '"' . $selected . '>' . $padding . $row['forum_name'] . '</option>';
1491        $holding = '';
1492    }
1493}
1494
1495if ($holding)
1496{
1497    $s_forums .= $holding;
1498}
1499
1500unset($pad_store);
1501unset($rowset);
1502
1503if (!$s_forums)
1504{
1505    trigger_error('NO_SEARCH');
1506}
1507
1508/**
1509 * Build options for a select list for the number of characters returned.
1510 *
1511 * If the admin defined amount is not within the predefined range,
1512 * and the admin did not set it to unlimited (0), we add that option aswell.
1513 *
1514 * @deprecated 3.3.1-RC1    Templates should use an numeric input, in favor of a select.
1515 */
1516$s_characters = '<option value="0">' . $language->lang('ALL_AVAILABLE') . '</option>';
1517$i_characters = array_merge([25, 50], range(100, 1000, 100));
1518
1519if ($config['default_search_return_chars'] && !in_array((int) $config['default_search_return_chars'], $i_characters))
1520{
1521    $i_characters[] = (int) $config['default_search_return_chars'];
1522    sort($i_characters);
1523}
1524
1525foreach ($i_characters as $i)
1526{
1527    $selected = $i === (int) $config['default_search_return_chars'] ? ' selected="selected"' : '';
1528    $s_characters .= sprintf('<option value="%1$s"%2$s>%1$s</option>', $i, $selected);
1529}
1530
1531$s_hidden_fields = array('t' => $topic_id);
1532
1533if ($_SID)
1534{
1535    $s_hidden_fields['sid'] = $_SID;
1536}
1537
1538if (!empty($_EXTRA_URL))
1539{
1540    foreach ($_EXTRA_URL as $url_param)
1541    {
1542        $url_param = explode('=', $url_param, 2);
1543        $s_hidden_fields[$url_param[0]] = $url_param[1];
1544    }
1545}
1546
1547$template->assign_vars(array(
1548    'DEFAULT_RETURN_CHARS'    => (int) $config['default_search_return_chars'],
1549    'S_SEARCH_ACTION'        => append_sid("{$phpbb_root_path}search.$phpEx", false, true, 0), // We force no ?sid= appending by using 0
1550    'S_HIDDEN_FIELDS'        => build_hidden_fields($s_hidden_fields),
1551    'S_CHARACTER_OPTIONS'    => $s_characters,
1552    'S_FORUM_OPTIONS'        => $s_forums,
1553    'S_SELECT_SORT_DIR'        => $s_sort_dir,
1554    'S_SELECT_SORT_KEY'        => $s_sort_key,
1555    'S_SELECT_SORT_DAYS'    => $s_limit_days,
1556    'S_IN_SEARCH'            => true,
1557));
1558
1559// only show recent searches to search administrators
1560if ($auth->acl_get('a_search'))
1561{
1562    // Handle large objects differently for Oracle and MSSQL
1563    switch ($db->get_sql_layer())
1564    {
1565        case 'oracle':
1566            $sql = 'SELECT search_time, search_keywords
1567                FROM ' . SEARCH_RESULTS_TABLE . '
1568                WHERE dbms_lob.getlength(search_keywords) > 0
1569                ORDER BY search_time DESC';
1570        break;
1571
1572        case 'mssql_odbc':
1573        case 'mssqlnative':
1574            $sql = 'SELECT search_time, search_keywords
1575                FROM ' . SEARCH_RESULTS_TABLE . '
1576                WHERE DATALENGTH(search_keywords) > 0
1577                ORDER BY search_time DESC';
1578        break;
1579
1580        default:
1581            $sql = 'SELECT search_time, search_keywords
1582                FROM ' . SEARCH_RESULTS_TABLE . '
1583                WHERE search_keywords <> \'\'
1584                ORDER BY search_time DESC';
1585        break;
1586    }
1587    $result = $db->sql_query_limit($sql, 5);
1588
1589    while ($row = $db->sql_fetchrow($result))
1590    {
1591        $keywords = $row['search_keywords'];
1592
1593        $template->assign_block_vars('recentsearch', array(
1594            'KEYWORDS'    => $keywords,
1595            'TIME'        => $user->format_date($row['search_time']),
1596
1597            'U_KEYWORDS'    => append_sid("{$phpbb_root_path}search.$phpEx", 'keywords=' . urlencode(html_entity_decode($keywords, ENT_COMPAT)))
1598        ));
1599    }
1600    $db->sql_freeresult($result);
1601}
1602
1603// Output the basic page
1604page_header($user->lang['SEARCH']);
1605
1606$template->set_filenames(array(
1607    'body' => 'search_body.html')
1608);
1609make_jumpbox(append_sid("{$phpbb_root_path}viewforum.$phpEx"));
1610
1611page_footer();