[ Index ]

PHP Cross Reference of phpBB-3.1.12-deutsch

title

Body

[close]

/ -> search.php (source)

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


Generated: Thu Jan 11 00:25:41 2018 Cross-referenced by PHPXref 0.7.1