[ Index ]

PHP Cross Reference of phpBB-3.2.11-deutsch

title

Body

[close]

/includes/ -> functions_content.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  if (!defined('IN_PHPBB'))
  18  {
  19      exit;
  20  }
  21  
  22  /**
  23  * gen_sort_selects()
  24  * make_jumpbox()
  25  * bump_topic_allowed()
  26  * get_context()
  27  * phpbb_clean_search_string()
  28  * decode_message()
  29  * strip_bbcode()
  30  * generate_text_for_display()
  31  * generate_text_for_storage()
  32  * generate_text_for_edit()
  33  * make_clickable_callback()
  34  * make_clickable()
  35  * censor_text()
  36  * bbcode_nl2br()
  37  * smiley_text()
  38  * parse_attachments()
  39  * extension_allowed()
  40  * truncate_string()
  41  * get_username_string()
  42  * class bitfield
  43  */
  44  
  45  /**
  46  * Generate sort selection fields
  47  */
  48  function 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, $def_st = false, $def_sk = false, $def_sd = false)
  49  {
  50      global $user, $phpbb_dispatcher;
  51  
  52      $sort_dir_text = array('a' => $user->lang['ASCENDING'], 'd' => $user->lang['DESCENDING']);
  53  
  54      $sorts = array(
  55          'st'    => array(
  56              'key'        => 'sort_days',
  57              'default'    => $def_st,
  58              'options'    => $limit_days,
  59              'output'    => &$s_limit_days,
  60          ),
  61  
  62          'sk'    => array(
  63              'key'        => 'sort_key',
  64              'default'    => $def_sk,
  65              'options'    => $sort_by_text,
  66              'output'    => &$s_sort_key,
  67          ),
  68  
  69          'sd'    => array(
  70              'key'        => 'sort_dir',
  71              'default'    => $def_sd,
  72              'options'    => $sort_dir_text,
  73              'output'    => &$s_sort_dir,
  74          ),
  75      );
  76      $u_sort_param  = '';
  77  
  78      foreach ($sorts as $name => $sort_ary)
  79      {
  80          $key = $sort_ary['key'];
  81          $selected = ${$sort_ary['key']};
  82  
  83          // Check if the key is selectable. If not, we reset to the default or first key found.
  84          // This ensures the values are always valid. We also set $sort_dir/sort_key/etc. to the
  85          // correct value, else the protection is void. ;)
  86          if (!isset($sort_ary['options'][$selected]))
  87          {
  88              if ($sort_ary['default'] !== false)
  89              {
  90                  $selected = ${$key} = $sort_ary['default'];
  91              }
  92              else
  93              {
  94                  @reset($sort_ary['options']);
  95                  $selected = ${$key} = key($sort_ary['options']);
  96              }
  97          }
  98  
  99          $sort_ary['output'] = '<select name="' . $name . '" id="' . $name . '">';
 100          foreach ($sort_ary['options'] as $option => $text)
 101          {
 102              $sort_ary['output'] .= '<option value="' . $option . '"' . (($selected == $option) ? ' selected="selected"' : '') . '>' . $text . '</option>';
 103          }
 104          $sort_ary['output'] .= '</select>';
 105  
 106          $u_sort_param .= ($selected !== $sort_ary['default']) ? ((strlen($u_sort_param)) ? '&amp;' : '') . "{$name}={$selected}" : '';
 107      }
 108  
 109      /**
 110       * Run code before generated sort selects are returned
 111       *
 112       * @event core.gen_sort_selects_after
 113       * @var    int      limit_days     Days limit
 114       * @var    array    sort_by_text   Sort by text options
 115       * @var    int      sort_days      Sort by days flag
 116       * @var    string   sort_key       Sort key
 117       * @var    string   sort_dir       Sort dir
 118       * @var    string   s_limit_days   String of days limit
 119       * @var    string   s_sort_key     String of sort key
 120       * @var    string   s_sort_dir     String of sort dir
 121       * @var    string   u_sort_param   Sort URL params
 122       * @var    bool     def_st         Default sort days
 123       * @var    bool     def_sk         Default sort key
 124       * @var    bool     def_sd         Default sort dir
 125       * @var    array    sorts          Sorts
 126       * @since 3.1.9-RC1
 127       */
 128      $vars = array(
 129          'limit_days',
 130          'sort_by_text',
 131          'sort_days',
 132          'sort_key',
 133          'sort_dir',
 134          's_limit_days',
 135          's_sort_key',
 136          's_sort_dir',
 137          'u_sort_param',
 138          'def_st',
 139          'def_sk',
 140          'def_sd',
 141          'sorts',
 142      );
 143      extract($phpbb_dispatcher->trigger_event('core.gen_sort_selects_after', compact($vars)));
 144  
 145      return;
 146  }
 147  
 148  /**
 149  * Generate Jumpbox
 150  */
 151  function make_jumpbox($action, $forum_id = false, $select_all = false, $acl_list = false, $force_display = false)
 152  {
 153      global $config, $auth, $template, $user, $db, $phpbb_path_helper, $phpbb_dispatcher;
 154  
 155      // We only return if the jumpbox is not forced to be displayed (in case it is needed for functionality)
 156      if (!$config['load_jumpbox'] && $force_display === false)
 157      {
 158          return;
 159      }
 160  
 161      $sql = 'SELECT forum_id, forum_name, parent_id, forum_type, left_id, right_id
 162          FROM ' . FORUMS_TABLE . '
 163          ORDER BY left_id ASC';
 164      $result = $db->sql_query($sql, 600);
 165  
 166      $rowset = array();
 167      while ($row = $db->sql_fetchrow($result))
 168      {
 169          $rowset[(int) $row['forum_id']] = $row;
 170      }
 171      $db->sql_freeresult($result);
 172  
 173      $right = $padding = 0;
 174      $padding_store = array('0' => 0);
 175      $display_jumpbox = false;
 176      $iteration = 0;
 177  
 178      /**
 179      * Modify the jumpbox forum list data
 180      *
 181      * @event core.make_jumpbox_modify_forum_list
 182      * @var    array    rowset    Array with the forums list data
 183      * @since 3.1.10-RC1
 184      */
 185      $vars = array('rowset');
 186      extract($phpbb_dispatcher->trigger_event('core.make_jumpbox_modify_forum_list', compact($vars)));
 187  
 188      // Sometimes it could happen that forums will be displayed here not be displayed within the index page
 189      // This is the result of forums not displayed at index, having list permissions and a parent of a forum with no permissions.
 190      // If this happens, the padding could be "broken"
 191  
 192      foreach ($rowset as $row)
 193      {
 194          if ($row['left_id'] < $right)
 195          {
 196              $padding++;
 197              $padding_store[$row['parent_id']] = $padding;
 198          }
 199          else if ($row['left_id'] > $right + 1)
 200          {
 201              // Ok, if the $padding_store for this parent is empty there is something wrong. For now we will skip over it.
 202              // @todo digging deep to find out "how" this can happen.
 203              $padding = (isset($padding_store[$row['parent_id']])) ? $padding_store[$row['parent_id']] : $padding;
 204          }
 205  
 206          $right = $row['right_id'];
 207  
 208          if ($row['forum_type'] == FORUM_CAT && ($row['left_id'] + 1 == $row['right_id']))
 209          {
 210              // Non-postable forum with no subforums, don't display
 211              continue;
 212          }
 213  
 214          if (!$auth->acl_get('f_list', $row['forum_id']))
 215          {
 216              // if the user does not have permissions to list this forum skip
 217              continue;
 218          }
 219  
 220          if ($acl_list && !$auth->acl_gets($acl_list, $row['forum_id']))
 221          {
 222              continue;
 223          }
 224  
 225          $tpl_ary = array();
 226          if (!$display_jumpbox)
 227          {
 228              $tpl_ary[] = array(
 229                  'FORUM_ID'        => ($select_all) ? 0 : -1,
 230                  'FORUM_NAME'    => ($select_all) ? $user->lang['ALL_FORUMS'] : $user->lang['SELECT_FORUM'],
 231                  'S_FORUM_COUNT'    => $iteration,
 232                  'LINK'            => $phpbb_path_helper->append_url_params($action, array('f' => $forum_id)),
 233              );
 234  
 235              $iteration++;
 236              $display_jumpbox = true;
 237          }
 238  
 239          $tpl_ary[] = array(
 240              'FORUM_ID'        => $row['forum_id'],
 241              'FORUM_NAME'    => $row['forum_name'],
 242              'SELECTED'        => ($row['forum_id'] == $forum_id) ? ' selected="selected"' : '',
 243              'S_FORUM_COUNT'    => $iteration,
 244              'S_IS_CAT'        => ($row['forum_type'] == FORUM_CAT) ? true : false,
 245              'S_IS_LINK'        => ($row['forum_type'] == FORUM_LINK) ? true : false,
 246              'S_IS_POST'        => ($row['forum_type'] == FORUM_POST) ? true : false,
 247              'LINK'            => $phpbb_path_helper->append_url_params($action, array('f' => $row['forum_id'])),
 248          );
 249  
 250          /**
 251           * Modify the jumpbox before it is assigned to the template
 252           *
 253           * @event core.make_jumpbox_modify_tpl_ary
 254           * @var    array    row                The data of the forum
 255           * @var    array    tpl_ary            Template data of the forum
 256           * @since 3.1.10-RC1
 257           */
 258          $vars = array(
 259              'row',
 260              'tpl_ary',
 261          );
 262          extract($phpbb_dispatcher->trigger_event('core.make_jumpbox_modify_tpl_ary', compact($vars)));
 263  
 264          $template->assign_block_vars_array('jumpbox_forums', $tpl_ary);
 265  
 266          unset($tpl_ary);
 267  
 268          for ($i = 0; $i < $padding; $i++)
 269          {
 270              $template->assign_block_vars('jumpbox_forums.level', array());
 271          }
 272          $iteration++;
 273      }
 274      unset($padding_store, $rowset);
 275  
 276      $url_parts = $phpbb_path_helper->get_url_parts($action);
 277  
 278      $template->assign_vars(array(
 279          'S_DISPLAY_JUMPBOX'            => $display_jumpbox,
 280          'S_JUMPBOX_ACTION'            => $action,
 281          'HIDDEN_FIELDS_FOR_JUMPBOX'    => build_hidden_fields($url_parts['params']),
 282      ));
 283  
 284      return;
 285  }
 286  
 287  /**
 288  * Bump Topic Check - used by posting and viewtopic
 289  */
 290  function bump_topic_allowed($forum_id, $topic_bumped, $last_post_time, $topic_poster, $last_topic_poster)
 291  {
 292      global $config, $auth, $user;
 293  
 294      // Check permission and make sure the last post was not already bumped
 295      if (!$auth->acl_get('f_bump', $forum_id) || $topic_bumped)
 296      {
 297          return false;
 298      }
 299  
 300      // Check bump time range, is the user really allowed to bump the topic at this time?
 301      $bump_time = ($config['bump_type'] == 'm') ? $config['bump_interval'] * 60 : (($config['bump_type'] == 'h') ? $config['bump_interval'] * 3600 : $config['bump_interval'] * 86400);
 302  
 303      // Check bump time
 304      if ($last_post_time + $bump_time > time())
 305      {
 306          return false;
 307      }
 308  
 309      // Check bumper, only topic poster and last poster are allowed to bump
 310      if ($topic_poster != $user->data['user_id'] && $last_topic_poster != $user->data['user_id'])
 311      {
 312          return false;
 313      }
 314  
 315      // A bump time of 0 will completely disable the bump feature... not intended but might be useful.
 316      return $bump_time;
 317  }
 318  
 319  /**
 320  * Generates a text with approx. the specified length which contains the specified words and their context
 321  *
 322  * @param    string    $text    The full text from which context shall be extracted
 323  * @param    string    $words    An array of words which should be contained in the result, has to be a valid part of a PCRE pattern (escape with preg_quote!)
 324  * @param    int        $length    The desired length of the resulting text, however the result might be shorter or longer than this value
 325  *
 326  * @return    string            Context of the specified words separated by "..."
 327  */
 328  function get_context($text, $words, $length = 400)
 329  {
 330      // first replace all whitespaces with single spaces
 331      $text = preg_replace('/ +/', ' ', strtr($text, "\t\n\r\x0C ", '     '));
 332  
 333      // we need to turn the entities back into their original form, to not cut the message in between them
 334      $entities = array('&lt;', '&gt;', '&#91;', '&#93;', '&#46;', '&#58;', '&#058;');
 335      $characters = array('<', '>', '[', ']', '.', ':', ':');
 336      $text = str_replace($entities, $characters, $text);
 337  
 338      $word_indizes = array();
 339      if (count($words))
 340      {
 341          $match = '';
 342          // find the starting indizes of all words
 343          foreach ($words as $word)
 344          {
 345              if ($word)
 346              {
 347                  if (preg_match('#(?:[^\w]|^)(' . $word . ')(?:[^\w]|$)#i', $text, $match))
 348                  {
 349                      if (empty($match[1]))
 350                      {
 351                          continue;
 352                      }
 353  
 354                      $pos = utf8_strpos($text, $match[1]);
 355                      if ($pos !== false)
 356                      {
 357                          $word_indizes[] = $pos;
 358                      }
 359                  }
 360              }
 361          }
 362          unset($match);
 363  
 364          if (count($word_indizes))
 365          {
 366              $word_indizes = array_unique($word_indizes);
 367              sort($word_indizes);
 368  
 369              $wordnum = count($word_indizes);
 370              // number of characters on the right and left side of each word
 371              $sequence_length = (int) ($length / (2 * $wordnum)) - 2;
 372              $final_text = '';
 373              $word = $j = 0;
 374              $final_text_index = -1;
 375  
 376              // cycle through every character in the original text
 377              for ($i = $word_indizes[$word], $n = utf8_strlen($text); $i < $n; $i++)
 378              {
 379                  // if the current position is the start of one of the words then append $sequence_length characters to the final text
 380                  if (isset($word_indizes[$word]) && ($i == $word_indizes[$word]))
 381                  {
 382                      if ($final_text_index < $i - $sequence_length - 1)
 383                      {
 384                          $final_text .= '... ' . preg_replace('#^([^ ]*)#', '', utf8_substr($text, $i - $sequence_length, $sequence_length));
 385                      }
 386                      else
 387                      {
 388                          // if the final text is already nearer to the current word than $sequence_length we only append the text
 389                          // from its current index on and distribute the unused length to all other sequenes
 390                          $sequence_length += (int) (($final_text_index - $i + $sequence_length + 1) / (2 * $wordnum));
 391                          $final_text .= utf8_substr($text, $final_text_index + 1, $i - $final_text_index - 1);
 392                      }
 393                      $final_text_index = $i - 1;
 394  
 395                      // add the following characters to the final text (see below)
 396                      $word++;
 397                      $j = 1;
 398                  }
 399  
 400                  if ($j > 0)
 401                  {
 402                      // add the character to the final text and increment the sequence counter
 403                      $final_text .= utf8_substr($text, $i, 1);
 404                      $final_text_index++;
 405                      $j++;
 406  
 407                      // if this is a whitespace then check whether we are done with this sequence
 408                      if (utf8_substr($text, $i, 1) == ' ')
 409                      {
 410                          // only check whether we have to exit the context generation completely if we haven't already reached the end anyway
 411                          if ($i + 4 < $n)
 412                          {
 413                              if (($j > $sequence_length && $word >= $wordnum) || utf8_strlen($final_text) > $length)
 414                              {
 415                                  $final_text .= ' ...';
 416                                  break;
 417                              }
 418                          }
 419                          else
 420                          {
 421                              // make sure the text really reaches the end
 422                              $j -= 4;
 423                          }
 424  
 425                          // stop context generation and wait for the next word
 426                          if ($j > $sequence_length)
 427                          {
 428                              $j = 0;
 429                          }
 430                      }
 431                  }
 432              }
 433              return str_replace($characters, $entities, $final_text);
 434          }
 435      }
 436  
 437      if (!count($words) || !count($word_indizes))
 438      {
 439          return str_replace($characters, $entities, ((utf8_strlen($text) >= $length + 3) ? utf8_substr($text, 0, $length) . '...' : $text));
 440      }
 441  }
 442  
 443  /**
 444  * Cleans a search string by removing single wildcards from it and replacing multiple spaces with a single one.
 445  *
 446  * @param string $search_string The full search string which should be cleaned.
 447  *
 448  * @return string The cleaned search string without any wildcards and multiple spaces.
 449  */
 450  function phpbb_clean_search_string($search_string)
 451  {
 452      // This regular expressions matches every single wildcard.
 453      // That means one after a whitespace or the beginning of the string or one before a whitespace or the end of the string.
 454      $search_string = preg_replace('#(?<=^|\s)\*+(?=\s|$)#', '', $search_string);
 455      $search_string = trim($search_string);
 456      $search_string = preg_replace(array('#\s+#u', '#\*+#u'), array(' ', '*'), $search_string);
 457      return $search_string;
 458  }
 459  
 460  /**
 461  * Decode text whereby text is coming from the db and expected to be pre-parsed content
 462  * We are placing this outside of the message parser because we are often in need of it...
 463  *
 464  * NOTE: special chars are kept encoded
 465  *
 466  * @param string &$message Original message, passed by reference
 467  * @param string $bbcode_uid BBCode UID
 468  * @return null
 469  */
 470  function decode_message(&$message, $bbcode_uid = '')
 471  {
 472      global $phpbb_container, $phpbb_dispatcher;
 473  
 474      /**
 475       * Use this event to modify the message before it is decoded
 476       *
 477       * @event core.decode_message_before
 478       * @var string    message_text    The message content
 479       * @var string    bbcode_uid        The message BBCode UID
 480       * @since 3.1.9-RC1
 481       */
 482      $message_text = $message;
 483      $vars = array('message_text', 'bbcode_uid');
 484      extract($phpbb_dispatcher->trigger_event('core.decode_message_before', compact($vars)));
 485      $message = $message_text;
 486  
 487      if (preg_match('#^<[rt][ >]#', $message))
 488      {
 489          $message = htmlspecialchars($phpbb_container->get('text_formatter.utils')->unparse($message), ENT_COMPAT);
 490      }
 491      else
 492      {
 493          if ($bbcode_uid)
 494          {
 495              $match = array('<br />', "[/*:m:$bbcode_uid]", ":u:$bbcode_uid", ":o:$bbcode_uid", ":$bbcode_uid");
 496              $replace = array("\n", '', '', '', '');
 497          }
 498          else
 499          {
 500              $match = array('<br />');
 501              $replace = array("\n");
 502          }
 503  
 504          $message = str_replace($match, $replace, $message);
 505  
 506          $match = get_preg_expression('bbcode_htm');
 507          $replace = array('\1', '\1', '\2', '\2', '\1', '', '');
 508  
 509          $message = preg_replace($match, $replace, $message);
 510      }
 511  
 512      /**
 513      * Use this event to modify the message after it is decoded
 514      *
 515      * @event core.decode_message_after
 516      * @var string    message_text    The message content
 517      * @var string    bbcode_uid        The message BBCode UID
 518      * @since 3.1.9-RC1
 519      */
 520      $message_text = $message;
 521      $vars = array('message_text', 'bbcode_uid');
 522      extract($phpbb_dispatcher->trigger_event('core.decode_message_after', compact($vars)));
 523      $message = $message_text;
 524  }
 525  
 526  /**
 527  * Strips all bbcode from a text in place
 528  */
 529  function strip_bbcode(&$text, $uid = '')
 530  {
 531      global $phpbb_container;
 532  
 533      if (preg_match('#^<[rt][ >]#', $text))
 534      {
 535          $text = $phpbb_container->get('text_formatter.utils')->clean_formatting($text);
 536      }
 537      else
 538      {
 539          if (!$uid)
 540          {
 541              $uid = '[0-9a-z]{5,}';
 542          }
 543  
 544          $text = preg_replace("#\[\/?[a-z0-9\*\+\-]+(?:=(?:&quot;.*&quot;|[^\]]*))?(?::[a-z])?(\:$uid)\]#", ' ', $text);
 545  
 546          $match = get_preg_expression('bbcode_htm');
 547          $replace = array('\1', '\1', '\2', '\1', '', '');
 548  
 549          $text = preg_replace($match, $replace, $text);
 550      }
 551  }
 552  
 553  /**
 554  * For display of custom parsed text on user-facing pages
 555  * Expects $text to be the value directly from the database (stored value)
 556  */
 557  function generate_text_for_display($text, $uid, $bitfield, $flags, $censor_text = true)
 558  {
 559      static $bbcode;
 560      global $auth, $config, $user;
 561      global $phpbb_dispatcher, $phpbb_container;
 562  
 563      if ($text === '')
 564      {
 565          return '';
 566      }
 567  
 568      /**
 569      * Use this event to modify the text before it is parsed
 570      *
 571      * @event core.modify_text_for_display_before
 572      * @var string    text            The text to parse
 573      * @var string    uid                The BBCode UID
 574      * @var string    bitfield        The BBCode Bitfield
 575      * @var int        flags            The BBCode Flags
 576      * @var bool        censor_text        Whether or not to apply word censors
 577      * @since 3.1.0-a1
 578      */
 579      $vars = array('text', 'uid', 'bitfield', 'flags', 'censor_text');
 580      extract($phpbb_dispatcher->trigger_event('core.modify_text_for_display_before', compact($vars)));
 581  
 582      if (preg_match('#^<[rt][ >]#', $text))
 583      {
 584          $renderer = $phpbb_container->get('text_formatter.renderer');
 585  
 586          // Temporarily switch off viewcensors if applicable
 587          $old_censor = $renderer->get_viewcensors();
 588  
 589          // Check here if the user is having viewing censors disabled (and also allowed to do so).
 590          if (!$user->optionget('viewcensors') && $config['allow_nocensors'] && $auth->acl_get('u_chgcensors'))
 591          {
 592              $censor_text = false;
 593          }
 594  
 595          if ($old_censor !== $censor_text)
 596          {
 597              $renderer->set_viewcensors($censor_text);
 598          }
 599  
 600          $text = $renderer->render($text);
 601  
 602          // Restore the previous value
 603          if ($old_censor !== $censor_text)
 604          {
 605              $renderer->set_viewcensors($old_censor);
 606          }
 607      }
 608      else
 609      {
 610          if ($censor_text)
 611          {
 612              $text = censor_text($text);
 613          }
 614  
 615          // Parse bbcode if bbcode uid stored and bbcode enabled
 616          if ($uid && ($flags & OPTION_FLAG_BBCODE))
 617          {
 618              if (!class_exists('bbcode'))
 619              {
 620                  global $phpbb_root_path, $phpEx;
 621                  include($phpbb_root_path . 'includes/bbcode.' . $phpEx);
 622              }
 623  
 624              if (empty($bbcode))
 625              {
 626                  $bbcode = new bbcode($bitfield);
 627              }
 628              else
 629              {
 630                  $bbcode->bbcode_set_bitfield($bitfield);
 631              }
 632  
 633              $bbcode->bbcode_second_pass($text, $uid);
 634          }
 635  
 636          $text = bbcode_nl2br($text);
 637          $text = smiley_text($text, !($flags & OPTION_FLAG_SMILIES));
 638      }
 639  
 640      /**
 641      * Use this event to modify the text after it is parsed
 642      *
 643      * @event core.modify_text_for_display_after
 644      * @var string    text        The text to parse
 645      * @var string    uid            The BBCode UID
 646      * @var string    bitfield    The BBCode Bitfield
 647      * @var int        flags        The BBCode Flags
 648      * @since 3.1.0-a1
 649      */
 650      $vars = array('text', 'uid', 'bitfield', 'flags');
 651      extract($phpbb_dispatcher->trigger_event('core.modify_text_for_display_after', compact($vars)));
 652  
 653      return $text;
 654  }
 655  
 656  /**
 657  * For parsing custom parsed text to be stored within the database.
 658  * This function additionally returns the uid and bitfield that needs to be stored.
 659  * Expects $text to be the value directly from $request->variable() and in it's non-parsed form
 660  *
 661  * @param string $text The text to be replaced with the parsed one
 662  * @param string $uid The BBCode uid for this parse
 663  * @param string $bitfield The BBCode bitfield for this parse
 664  * @param int $flags The allow_bbcode, allow_urls and allow_smilies compiled into a single integer.
 665  * @param bool $allow_bbcode If BBCode is allowed (i.e. if BBCode is parsed)
 666  * @param bool $allow_urls If urls is allowed
 667  * @param bool $allow_smilies If smilies are allowed
 668  * @param bool $allow_img_bbcode
 669  * @param bool $allow_flash_bbcode
 670  * @param bool $allow_quote_bbcode
 671  * @param bool $allow_url_bbcode
 672  * @param string $mode Mode to parse text as, e.g. post or sig
 673  *
 674  * @return array    An array of string with the errors that occurred while parsing
 675  */
 676  function generate_text_for_storage(&$text, &$uid, &$bitfield, &$flags, $allow_bbcode = false, $allow_urls = false, $allow_smilies = false, $allow_img_bbcode = true, $allow_flash_bbcode = true, $allow_quote_bbcode = true, $allow_url_bbcode = true, $mode = 'post')
 677  {
 678      global $phpbb_root_path, $phpEx, $phpbb_dispatcher;
 679  
 680      /**
 681      * Use this event to modify the text before it is prepared for storage
 682      *
 683      * @event core.modify_text_for_storage_before
 684      * @var string    text            The text to parse
 685      * @var string    uid                The BBCode UID
 686      * @var string    bitfield        The BBCode Bitfield
 687      * @var int        flags            The BBCode Flags
 688      * @var bool        allow_bbcode    Whether or not to parse BBCode
 689      * @var bool        allow_urls        Whether or not to parse URLs
 690      * @var bool        allow_smilies    Whether or not to parse Smilies
 691      * @var bool        allow_img_bbcode    Whether or not to parse the [img] BBCode
 692      * @var bool        allow_flash_bbcode    Whether or not to parse the [flash] BBCode
 693      * @var bool        allow_quote_bbcode    Whether or not to parse the [quote] BBCode
 694      * @var bool        allow_url_bbcode    Whether or not to parse the [url] BBCode
 695      * @var string    mode                Mode to parse text as, e.g. post or sig
 696      * @since 3.1.0-a1
 697      * @changed 3.2.0-a1 Added mode
 698      */
 699      $vars = array(
 700          'text',
 701          'uid',
 702          'bitfield',
 703          'flags',
 704          'allow_bbcode',
 705          'allow_urls',
 706          'allow_smilies',
 707          'allow_img_bbcode',
 708          'allow_flash_bbcode',
 709          'allow_quote_bbcode',
 710          'allow_url_bbcode',
 711          'mode',
 712      );
 713      extract($phpbb_dispatcher->trigger_event('core.modify_text_for_storage_before', compact($vars)));
 714  
 715      $uid = $bitfield = '';
 716      $flags = (($allow_bbcode) ? OPTION_FLAG_BBCODE : 0) + (($allow_smilies) ? OPTION_FLAG_SMILIES : 0) + (($allow_urls) ? OPTION_FLAG_LINKS : 0);
 717  
 718      if (!class_exists('parse_message'))
 719      {
 720          include($phpbb_root_path . 'includes/message_parser.' . $phpEx);
 721      }
 722  
 723      $message_parser = new parse_message($text);
 724      $message_parser->parse($allow_bbcode, $allow_urls, $allow_smilies, $allow_img_bbcode, $allow_flash_bbcode, $allow_quote_bbcode, $allow_url_bbcode, true, $mode);
 725  
 726      $text = $message_parser->message;
 727      $uid = $message_parser->bbcode_uid;
 728  
 729      // If the bbcode_bitfield is empty, there is no need for the uid to be stored.
 730      if (!$message_parser->bbcode_bitfield)
 731      {
 732          $uid = '';
 733      }
 734  
 735      $bitfield = $message_parser->bbcode_bitfield;
 736  
 737      /**
 738      * Use this event to modify the text after it is prepared for storage
 739      *
 740      * @event core.modify_text_for_storage_after
 741      * @var string    text            The text to parse
 742      * @var string    uid                The BBCode UID
 743      * @var string    bitfield        The BBCode Bitfield
 744      * @var int        flags            The BBCode Flags
 745      * @var string    message_parser    The message_parser object
 746      * @since 3.1.0-a1
 747      * @changed 3.1.11-RC1            Added message_parser to vars
 748      */
 749      $vars = array('text', 'uid', 'bitfield', 'flags', 'message_parser');
 750      extract($phpbb_dispatcher->trigger_event('core.modify_text_for_storage_after', compact($vars)));
 751  
 752      return $message_parser->warn_msg;
 753  }
 754  
 755  /**
 756  * For decoding custom parsed text for edits as well as extracting the flags
 757  * Expects $text to be the value directly from the database (pre-parsed content)
 758  */
 759  function generate_text_for_edit($text, $uid, $flags)
 760  {
 761      global $phpbb_dispatcher;
 762  
 763      /**
 764      * Use this event to modify the text before it is decoded for editing
 765      *
 766      * @event core.modify_text_for_edit_before
 767      * @var string    text            The text to parse
 768      * @var string    uid                The BBCode UID
 769      * @var int        flags            The BBCode Flags
 770      * @since 3.1.0-a1
 771      */
 772      $vars = array('text', 'uid', 'flags');
 773      extract($phpbb_dispatcher->trigger_event('core.modify_text_for_edit_before', compact($vars)));
 774  
 775      decode_message($text, $uid);
 776  
 777      /**
 778      * Use this event to modify the text after it is decoded for editing
 779      *
 780      * @event core.modify_text_for_edit_after
 781      * @var string    text            The text to parse
 782      * @var int        flags            The BBCode Flags
 783      * @since 3.1.0-a1
 784      */
 785      $vars = array('text', 'flags');
 786      extract($phpbb_dispatcher->trigger_event('core.modify_text_for_edit_after', compact($vars)));
 787  
 788      return array(
 789          'allow_bbcode'    => ($flags & OPTION_FLAG_BBCODE) ? 1 : 0,
 790          'allow_smilies'    => ($flags & OPTION_FLAG_SMILIES) ? 1 : 0,
 791          'allow_urls'    => ($flags & OPTION_FLAG_LINKS) ? 1 : 0,
 792          'text'            => $text
 793      );
 794  }
 795  
 796  /**
 797  * A subroutine of make_clickable used with preg_replace
 798  * It places correct HTML around an url, shortens the displayed text
 799  * and makes sure no entities are inside URLs
 800  */
 801  function make_clickable_callback($type, $whitespace, $url, $relative_url, $class)
 802  {
 803      $orig_url        = $url;
 804      $orig_relative    = $relative_url;
 805      $append            = '';
 806      $url            = htmlspecialchars_decode($url);
 807      $relative_url    = htmlspecialchars_decode($relative_url);
 808  
 809      // make sure no HTML entities were matched
 810      $chars = array('<', '>', '"');
 811      $split = false;
 812  
 813      foreach ($chars as $char)
 814      {
 815          $next_split = strpos($url, $char);
 816          if ($next_split !== false)
 817          {
 818              $split = ($split !== false) ? min($split, $next_split) : $next_split;
 819          }
 820      }
 821  
 822      if ($split !== false)
 823      {
 824          // an HTML entity was found, so the URL has to end before it
 825          $append            = substr($url, $split) . $relative_url;
 826          $url            = substr($url, 0, $split);
 827          $relative_url    = '';
 828      }
 829      else if ($relative_url)
 830      {
 831          // same for $relative_url
 832          $split = false;
 833          foreach ($chars as $char)
 834          {
 835              $next_split = strpos($relative_url, $char);
 836              if ($next_split !== false)
 837              {
 838                  $split = ($split !== false) ? min($split, $next_split) : $next_split;
 839              }
 840          }
 841  
 842          if ($split !== false)
 843          {
 844              $append            = substr($relative_url, $split);
 845              $relative_url    = substr($relative_url, 0, $split);
 846          }
 847      }
 848  
 849      // if the last character of the url is a punctuation mark, exclude it from the url
 850      $last_char = ($relative_url) ? $relative_url[strlen($relative_url) - 1] : $url[strlen($url) - 1];
 851  
 852      switch ($last_char)
 853      {
 854          case '.':
 855          case '?':
 856          case '!':
 857          case ':':
 858          case ',':
 859              $append = $last_char;
 860              if ($relative_url)
 861              {
 862                  $relative_url = substr($relative_url, 0, -1);
 863              }
 864              else
 865              {
 866                  $url = substr($url, 0, -1);
 867              }
 868          break;
 869  
 870          // set last_char to empty here, so the variable can be used later to
 871          // check whether a character was removed
 872          default:
 873              $last_char = '';
 874          break;
 875      }
 876  
 877      $short_url = (utf8_strlen($url) > 55) ? utf8_substr($url, 0, 39) . ' ... ' . utf8_substr($url, -10) : $url;
 878  
 879      switch ($type)
 880      {
 881          case MAGIC_URL_LOCAL:
 882              $tag            = 'l';
 883              $relative_url    = preg_replace('/[&?]sid=[0-9a-f]{32}$/', '', preg_replace('/([&?])sid=[0-9a-f]{32}&/', '$1', $relative_url));
 884              $url            = $url . '/' . $relative_url;
 885              $text            = $relative_url;
 886  
 887              // this url goes to http://domain.tld/path/to/board/ which
 888              // would result in an empty link if treated as local so
 889              // don't touch it and let MAGIC_URL_FULL take care of it.
 890              if (!$relative_url)
 891              {
 892                  return $whitespace . $orig_url . '/' . $orig_relative; // slash is taken away by relative url pattern
 893              }
 894          break;
 895  
 896          case MAGIC_URL_FULL:
 897              $tag    = 'm';
 898              $text    = $short_url;
 899          break;
 900  
 901          case MAGIC_URL_WWW:
 902              $tag    = 'w';
 903              $url    = 'http://' . $url;
 904              $text    = $short_url;
 905          break;
 906  
 907          case MAGIC_URL_EMAIL:
 908              $tag    = 'e';
 909              $text    = $short_url;
 910              $url    = 'mailto:' . $url;
 911          break;
 912      }
 913  
 914      $url    = htmlspecialchars($url);
 915      $text    = htmlspecialchars($text);
 916      $append    = htmlspecialchars($append);
 917  
 918      $html    = "$whitespace<!-- $tag --><a$class href=\"$url\">$text</a><!-- $tag -->$append";
 919  
 920      return $html;
 921  }
 922  
 923  /**
 924  * make_clickable function
 925  *
 926  * Replace magic urls of form http://xxx.xxx., www.xxx. and xxx@xxx.xxx.
 927  * Cuts down displayed size of link if over 50 chars, turns absolute links
 928  * into relative versions when the server/script path matches the link
 929  */
 930  function make_clickable($text, $server_url = false, $class = 'postlink')
 931  {
 932      if ($server_url === false)
 933      {
 934          $server_url = generate_board_url();
 935      }
 936  
 937      static $static_class;
 938      static $magic_url_match_args;
 939  
 940      if (!isset($magic_url_match_args[$server_url]) || $static_class != $class)
 941      {
 942          $static_class = $class;
 943          $class = ($static_class) ? ' class="' . $static_class . '"' : '';
 944          $local_class = ($static_class) ? ' class="' . $static_class . '-local"' : '';
 945  
 946          if (!is_array($magic_url_match_args))
 947          {
 948              $magic_url_match_args = array();
 949          }
 950  
 951          // relative urls for this board
 952          $magic_url_match_args[$server_url][] = array(
 953              '#(^|[\n\t (>.])(' . preg_quote($server_url, '#') . ')/(' . get_preg_expression('relative_url_inline') . ')#iu',
 954              MAGIC_URL_LOCAL,
 955              $local_class,
 956          );
 957  
 958          // matches a xxxx://aaaaa.bbb.cccc. ...
 959          $magic_url_match_args[$server_url][] = array(
 960              '#(^|[\n\t (>.])(' . get_preg_expression('url_inline') . ')#iu',
 961              MAGIC_URL_FULL,
 962              $class,
 963          );
 964  
 965          // matches a "www.xxxx.yyyy[/zzzz]" kinda lazy URL thing
 966          $magic_url_match_args[$server_url][] = array(
 967              '#(^|[\n\t (>])(' . get_preg_expression('www_url_inline') . ')#iu',
 968              MAGIC_URL_WWW,
 969              $class,
 970          );
 971  
 972          // matches an email@domain type address at the start of a line, or after a space or after what might be a BBCode.
 973          $magic_url_match_args[$server_url][] = array(
 974              '/(^|[\n\t (>])(' . get_preg_expression('email') . ')/iu',
 975              MAGIC_URL_EMAIL,
 976              '',
 977          );
 978      }
 979  
 980      foreach ($magic_url_match_args[$server_url] as $magic_args)
 981      {
 982          if (preg_match($magic_args[0], $text, $matches))
 983          {
 984              $text = preg_replace_callback($magic_args[0], function($matches) use ($magic_args)
 985              {
 986                  $relative_url = isset($matches[3]) ? $matches[3] : '';
 987                  return make_clickable_callback($magic_args[1], $matches[1], $matches[2], $relative_url, $magic_args[2]);
 988              }, $text);
 989          }
 990      }
 991  
 992      return $text;
 993  }
 994  
 995  /**
 996  * Censoring
 997  */
 998  function censor_text($text)
 999  {
1000      static $censors;
1001  
1002      // Nothing to do?
1003      if ($text === '')
1004      {
1005          return '';
1006      }
1007  
1008      // We moved the word censor checks in here because we call this function quite often - and then only need to do the check once
1009      if (!isset($censors) || !is_array($censors))
1010      {
1011          global $config, $user, $auth, $cache;
1012  
1013          // We check here if the user is having viewing censors disabled (and also allowed to do so).
1014          if (!$user->optionget('viewcensors') && $config['allow_nocensors'] && $auth->acl_get('u_chgcensors'))
1015          {
1016              $censors = array();
1017          }
1018          else
1019          {
1020              $censors = $cache->obtain_word_list();
1021          }
1022      }
1023  
1024      if (count($censors))
1025      {
1026          return preg_replace($censors['match'], $censors['replace'], $text);
1027      }
1028  
1029      return $text;
1030  }
1031  
1032  /**
1033  * custom version of nl2br which takes custom BBCodes into account
1034  */
1035  function bbcode_nl2br($text)
1036  {
1037      // custom BBCodes might contain carriage returns so they
1038      // are not converted into <br /> so now revert that
1039      $text = str_replace(array("\n", "\r"), array('<br />', "\n"), $text);
1040      return $text;
1041  }
1042  
1043  /**
1044  * Smiley processing
1045  */
1046  function smiley_text($text, $force_option = false)
1047  {
1048      global $config, $user, $phpbb_path_helper, $phpbb_dispatcher;
1049  
1050      if ($force_option || !$config['allow_smilies'] || !$user->optionget('viewsmilies'))
1051      {
1052          return preg_replace('#<!\-\- s(.*?) \-\-><img src="\{SMILIES_PATH\}\/.*? \/><!\-\- s\1 \-\->#', '\1', $text);
1053      }
1054      else
1055      {
1056          $root_path = (defined('PHPBB_USE_BOARD_URL_PATH') && PHPBB_USE_BOARD_URL_PATH) ? generate_board_url() . '/' : $phpbb_path_helper->get_web_root_path();
1057  
1058          /**
1059          * Event to override the root_path for smilies
1060          *
1061          * @event core.smiley_text_root_path
1062          * @var string root_path root_path for smilies
1063          * @since 3.1.11-RC1
1064          */
1065          $vars = array('root_path');
1066          extract($phpbb_dispatcher->trigger_event('core.smiley_text_root_path', compact($vars)));
1067          return preg_replace('#<!\-\- s(.*?) \-\-><img src="\{SMILIES_PATH\}\/(.*?) \/><!\-\- s\1 \-\->#', '<img class="smilies" src="' . $root_path . $config['smilies_path'] . '/\2 />', $text);
1068      }
1069  }
1070  
1071  /**
1072  * General attachment parsing
1073  *
1074  * @param mixed $forum_id The forum id the attachments are displayed in (false if in private message)
1075  * @param string &$message The post/private message
1076  * @param array &$attachments The attachments to parse for (inline) display. The attachments array will hold templated data after parsing.
1077  * @param array &$update_count_ary The attachment counts to be updated - will be filled
1078  * @param bool $preview If set to true the attachments are parsed for preview. Within preview mode the comments are fetched from the given $attachments array and not fetched from the database.
1079  */
1080  function parse_attachments($forum_id, &$message, &$attachments, &$update_count_ary, $preview = false)
1081  {
1082      if (!count($attachments))
1083      {
1084          return;
1085      }
1086  
1087      global $template, $cache, $user, $phpbb_dispatcher;
1088      global $extensions, $config, $phpbb_root_path, $phpEx;
1089  
1090      //
1091      $compiled_attachments = array();
1092  
1093      if (!isset($template->filename['attachment_tpl']))
1094      {
1095          $template->set_filenames(array(
1096              'attachment_tpl'    => 'attachment.html')
1097          );
1098      }
1099  
1100      if (empty($extensions) || !is_array($extensions))
1101      {
1102          $extensions = $cache->obtain_attach_extensions($forum_id);
1103      }
1104  
1105      // Look for missing attachment information...
1106      $attach_ids = array();
1107      foreach ($attachments as $pos => $attachment)
1108      {
1109          // If is_orphan is set, we need to retrieve the attachments again...
1110          if (!isset($attachment['extension']) && !isset($attachment['physical_filename']))
1111          {
1112              $attach_ids[(int) $attachment['attach_id']] = $pos;
1113          }
1114      }
1115  
1116      // Grab attachments (security precaution)
1117      if (count($attach_ids))
1118      {
1119          global $db;
1120  
1121          $new_attachment_data = array();
1122  
1123          $sql = 'SELECT *
1124              FROM ' . ATTACHMENTS_TABLE . '
1125              WHERE ' . $db->sql_in_set('attach_id', array_keys($attach_ids));
1126          $result = $db->sql_query($sql);
1127  
1128          while ($row = $db->sql_fetchrow($result))
1129          {
1130              if (!isset($attach_ids[$row['attach_id']]))
1131              {
1132                  continue;
1133              }
1134  
1135              // If we preview attachments we will set some retrieved values here
1136              if ($preview)
1137              {
1138                  $row['attach_comment'] = $attachments[$attach_ids[$row['attach_id']]]['attach_comment'];
1139              }
1140  
1141              $new_attachment_data[$attach_ids[$row['attach_id']]] = $row;
1142          }
1143          $db->sql_freeresult($result);
1144  
1145          $attachments = $new_attachment_data;
1146          unset($new_attachment_data);
1147      }
1148  
1149      // Make sure attachments are properly ordered
1150      ksort($attachments);
1151  
1152      foreach ($attachments as $attachment)
1153      {
1154          if (!count($attachment))
1155          {
1156              continue;
1157          }
1158  
1159          // We need to reset/empty the _file block var, because this function might be called more than once
1160          $template->destroy_block_vars('_file');
1161  
1162          $block_array = array();
1163  
1164          // Some basics...
1165          $attachment['extension'] = strtolower(trim($attachment['extension']));
1166          $filename = $phpbb_root_path . $config['upload_path'] . '/' . utf8_basename($attachment['physical_filename']);
1167  
1168          $upload_icon = '';
1169          $download_link = '';
1170          $display_cat = false;
1171  
1172          if (isset($extensions[$attachment['extension']]))
1173          {
1174              if ($user->img('icon_topic_attach', '') && !$extensions[$attachment['extension']]['upload_icon'])
1175              {
1176                  $upload_icon = $user->img('icon_topic_attach', '');
1177              }
1178              else if ($extensions[$attachment['extension']]['upload_icon'])
1179              {
1180                  $upload_icon = '<img src="' . $phpbb_root_path . $config['upload_icons_path'] . '/' . trim($extensions[$attachment['extension']]['upload_icon']) . '" alt="" />';
1181              }
1182          }
1183  
1184          $filesize = get_formatted_filesize($attachment['filesize'], false);
1185  
1186          $comment = bbcode_nl2br(censor_text($attachment['attach_comment']));
1187  
1188          $block_array += array(
1189              'UPLOAD_ICON'        => $upload_icon,
1190              'FILESIZE'            => $filesize['value'],
1191              'SIZE_LANG'            => $filesize['unit'],
1192              'DOWNLOAD_NAME'        => utf8_basename($attachment['real_filename']),
1193              'COMMENT'            => $comment,
1194          );
1195  
1196          $denied = false;
1197  
1198          if (!extension_allowed($forum_id, $attachment['extension'], $extensions))
1199          {
1200              $denied = true;
1201  
1202              $block_array += array(
1203                  'S_DENIED'            => true,
1204                  'DENIED_MESSAGE'    => sprintf($user->lang['EXTENSION_DISABLED_AFTER_POSTING'], $attachment['extension'])
1205              );
1206          }
1207  
1208          if (!$denied)
1209          {
1210              $display_cat = $extensions[$attachment['extension']]['display_cat'];
1211  
1212              if ($display_cat == ATTACHMENT_CATEGORY_IMAGE)
1213              {
1214                  if ($attachment['thumbnail'])
1215                  {
1216                      $display_cat = ATTACHMENT_CATEGORY_THUMB;
1217                  }
1218                  else
1219                  {
1220                      if ($config['img_display_inlined'])
1221                      {
1222                          if ($config['img_link_width'] || $config['img_link_height'])
1223                          {
1224                              $dimension = @getimagesize($filename);
1225  
1226                              // If the dimensions could not be determined or the image being 0x0 we display it as a link for safety purposes
1227                              if ($dimension === false || empty($dimension[0]) || empty($dimension[1]))
1228                              {
1229                                  $display_cat = ATTACHMENT_CATEGORY_NONE;
1230                              }
1231                              else
1232                              {
1233                                  $display_cat = ($dimension[0] <= $config['img_link_width'] && $dimension[1] <= $config['img_link_height']) ? ATTACHMENT_CATEGORY_IMAGE : ATTACHMENT_CATEGORY_NONE;
1234                              }
1235                          }
1236                      }
1237                      else
1238                      {
1239                          $display_cat = ATTACHMENT_CATEGORY_NONE;
1240                      }
1241                  }
1242              }
1243  
1244              // Make some descisions based on user options being set.
1245              if (($display_cat == ATTACHMENT_CATEGORY_IMAGE || $display_cat == ATTACHMENT_CATEGORY_THUMB) && !$user->optionget('viewimg'))
1246              {
1247                  $display_cat = ATTACHMENT_CATEGORY_NONE;
1248              }
1249  
1250              if ($display_cat == ATTACHMENT_CATEGORY_FLASH && !$user->optionget('viewflash'))
1251              {
1252                  $display_cat = ATTACHMENT_CATEGORY_NONE;
1253              }
1254  
1255              $download_link = append_sid("{$phpbb_root_path}download/file.$phpEx", 'id=' . $attachment['attach_id']);
1256              $l_downloaded_viewed = 'VIEWED_COUNTS';
1257  
1258              switch ($display_cat)
1259              {
1260                  // Images
1261                  case ATTACHMENT_CATEGORY_IMAGE:
1262                      $inline_link = append_sid("{$phpbb_root_path}download/file.$phpEx", 'id=' . $attachment['attach_id']);
1263                      $download_link .= '&amp;mode=view';
1264  
1265                      $block_array += array(
1266                          'S_IMAGE'        => true,
1267                          'U_INLINE_LINK'        => $inline_link,
1268                      );
1269  
1270                      $update_count_ary[] = $attachment['attach_id'];
1271                  break;
1272  
1273                  // Images, but display Thumbnail
1274                  case ATTACHMENT_CATEGORY_THUMB:
1275                      $thumbnail_link = append_sid("{$phpbb_root_path}download/file.$phpEx", 'id=' . $attachment['attach_id'] . '&amp;t=1');
1276                      $download_link .= '&amp;mode=view';
1277  
1278                      $block_array += array(
1279                          'S_THUMBNAIL'        => true,
1280                          'THUMB_IMAGE'        => $thumbnail_link,
1281                      );
1282  
1283                      $update_count_ary[] = $attachment['attach_id'];
1284                  break;
1285  
1286                  // Macromedia Flash Files
1287                  case ATTACHMENT_CATEGORY_FLASH:
1288                      list($width, $height) = @getimagesize($filename);
1289  
1290                      $block_array += array(
1291                          'S_FLASH_FILE'    => true,
1292                          'WIDTH'            => $width,
1293                          'HEIGHT'        => $height,
1294                          'U_VIEW_LINK'    => $download_link . '&amp;view=1',
1295                      );
1296  
1297                      // Viewed/Heared File ... update the download count
1298                      $update_count_ary[] = $attachment['attach_id'];
1299                  break;
1300  
1301                  default:
1302                      $l_downloaded_viewed = 'DOWNLOAD_COUNTS';
1303  
1304                      $block_array += array(
1305                          'S_FILE'        => true,
1306                      );
1307                  break;
1308              }
1309  
1310              if (!isset($attachment['download_count']))
1311              {
1312                  $attachment['download_count'] = 0;
1313              }
1314  
1315              $block_array += array(
1316                  'U_DOWNLOAD_LINK'        => $download_link,
1317                  'L_DOWNLOAD_COUNT'        => $user->lang($l_downloaded_viewed, (int) $attachment['download_count']),
1318              );
1319          }
1320  
1321          $update_count = $update_count_ary;
1322          /**
1323          * Use this event to modify the attachment template data.
1324          *
1325          * This event is triggered once per attachment.
1326          *
1327          * @event core.parse_attachments_modify_template_data
1328          * @var array    attachment        Array with attachment data
1329          * @var array    block_array        Template data of the attachment
1330          * @var int        display_cat        Attachment category data
1331          * @var string    download_link    Attachment download link
1332          * @var array    extensions        Array with attachment extensions data
1333          * @var mixed     forum_id         The forum id the attachments are displayed in (false if in private message)
1334          * @var bool        preview            Flag indicating if we are in post preview mode
1335          * @var array    update_count    Array with attachment ids to update download count
1336          * @since 3.1.0-RC5
1337          */
1338          $vars = array(
1339              'attachment',
1340              'block_array',
1341              'display_cat',
1342              'download_link',
1343              'extensions',
1344              'forum_id',
1345              'preview',
1346              'update_count',
1347          );
1348          extract($phpbb_dispatcher->trigger_event('core.parse_attachments_modify_template_data', compact($vars)));
1349          $update_count_ary = $update_count;
1350          unset($update_count, $display_cat, $download_link);
1351  
1352          $template->assign_block_vars('_file', $block_array);
1353  
1354          $compiled_attachments[] = $template->assign_display('attachment_tpl');
1355      }
1356  
1357      $attachments = $compiled_attachments;
1358      unset($compiled_attachments);
1359  
1360      $unset_tpl = array();
1361  
1362      preg_match_all('#<!\-\- ia([0-9]+) \-\->(.*?)<!\-\- ia\1 \-\->#', $message, $matches, PREG_PATTERN_ORDER);
1363  
1364      $replace = array();
1365      foreach ($matches[0] as $num => $capture)
1366      {
1367          $index = $matches[1][$num];
1368  
1369          $replace['from'][] = $matches[0][$num];
1370          $replace['to'][] = (isset($attachments[$index])) ? $attachments[$index] : sprintf($user->lang['MISSING_INLINE_ATTACHMENT'], $matches[2][array_search($index, $matches[1])]);
1371  
1372          $unset_tpl[] = $index;
1373      }
1374  
1375      if (isset($replace['from']))
1376      {
1377          $message = str_replace($replace['from'], $replace['to'], $message);
1378      }
1379  
1380      $unset_tpl = array_unique($unset_tpl);
1381  
1382      // Sort correctly
1383      if ($config['display_order'])
1384      {
1385          // Ascending sort
1386          krsort($attachments);
1387      }
1388      else
1389      {
1390          // Descending sort
1391          ksort($attachments);
1392      }
1393  
1394      // Needed to let not display the inlined attachments at the end of the post again
1395      foreach ($unset_tpl as $index)
1396      {
1397          unset($attachments[$index]);
1398      }
1399  }
1400  
1401  /**
1402  * Check if extension is allowed to be posted.
1403  *
1404  * @param mixed $forum_id The forum id to check or false if private message
1405  * @param string $extension The extension to check, for example zip.
1406  * @param array &$extensions The extension array holding the information from the cache (will be obtained if empty)
1407  *
1408  * @return bool False if the extension is not allowed to be posted, else true.
1409  */
1410  function extension_allowed($forum_id, $extension, &$extensions)
1411  {
1412      if (empty($extensions))
1413      {
1414          global $cache;
1415          $extensions = $cache->obtain_attach_extensions($forum_id);
1416      }
1417  
1418      return (!isset($extensions['_allowed_'][$extension])) ? false : true;
1419  }
1420  
1421  /**
1422  * Truncates string while retaining special characters if going over the max length
1423  * The default max length is 60 at the moment
1424  * The maximum storage length is there to fit the string within the given length. The string may be further truncated due to html entities.
1425  * For example: string given is 'a "quote"' (length: 9), would be a stored as 'a &quot;quote&quot;' (length: 19)
1426  *
1427  * @param string $string The text to truncate to the given length. String is specialchared.
1428  * @param int $max_length Maximum length of string (multibyte character count as 1 char / Html entity count as 1 char)
1429  * @param int $max_store_length Maximum character length of string (multibyte character count as 1 char / Html entity count as entity chars).
1430  * @param bool $allow_reply Allow Re: in front of string
1431  *     NOTE: This parameter can cause undesired behavior (returning strings longer than $max_store_length) and is deprecated.
1432  * @param string $append String to be appended
1433  */
1434  function truncate_string($string, $max_length = 60, $max_store_length = 255, $allow_reply = false, $append = '')
1435  {
1436      $strip_reply = false;
1437      $stripped = false;
1438      if ($allow_reply && strpos($string, 'Re: ') === 0)
1439      {
1440          $strip_reply = true;
1441          $string = substr($string, 4);
1442      }
1443  
1444      $_chars = utf8_str_split(htmlspecialchars_decode($string));
1445      $chars = array_map('utf8_htmlspecialchars', $_chars);
1446  
1447      // Now check the length ;)
1448      if (count($chars) > $max_length)
1449      {
1450          // Cut off the last elements from the array
1451          $string = implode('', array_slice($chars, 0, $max_length - utf8_strlen($append)));
1452          $stripped = true;
1453      }
1454  
1455      // Due to specialchars, we may not be able to store the string...
1456      if (utf8_strlen($string) > $max_store_length)
1457      {
1458          // let's split again, we do not want half-baked strings where entities are split
1459          $_chars = utf8_str_split(htmlspecialchars_decode($string));
1460          $chars = array_map('utf8_htmlspecialchars', $_chars);
1461  
1462          do
1463          {
1464              array_pop($chars);
1465              $string = implode('', $chars);
1466          }
1467          while (!empty($chars) && utf8_strlen($string) > $max_store_length);
1468      }
1469  
1470      if ($strip_reply)
1471      {
1472          $string = 'Re: ' . $string;
1473      }
1474  
1475      if ($append != '' && $stripped)
1476      {
1477          $string = $string . $append;
1478      }
1479  
1480      return $string;
1481  }
1482  
1483  /**
1484  * Get username details for placing into templates.
1485  * This function caches all modes on first call, except for no_profile and anonymous user - determined by $user_id.
1486  *
1487  * @html Username spans and links
1488  *
1489  * @param string $mode Can be profile (for getting an url to the profile), username (for obtaining the username), colour (for obtaining the user colour), full (for obtaining a html string representing a coloured link to the users profile) or no_profile (the same as full but forcing no profile link)
1490  * @param int $user_id The users id
1491  * @param string $username The users name
1492  * @param string $username_colour The users colour
1493  * @param string $guest_username optional parameter to specify the guest username. It will be used in favor of the GUEST language variable then.
1494  * @param string $custom_profile_url optional parameter to specify a profile url. The user id get appended to this url as &amp;u={user_id}
1495  *
1496  * @return string A string consisting of what is wanted based on $mode.
1497  */
1498  function get_username_string($mode, $user_id, $username, $username_colour = '', $guest_username = false, $custom_profile_url = false)
1499  {
1500      static $_profile_cache;
1501      global $phpbb_dispatcher;
1502  
1503      // We cache some common variables we need within this function
1504      if (empty($_profile_cache))
1505      {
1506          global $phpbb_root_path, $phpEx;
1507  
1508          /** @html Username spans and links for usage in the template */
1509          $_profile_cache['base_url'] = append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=viewprofile&amp;u={USER_ID}');
1510          $_profile_cache['tpl_noprofile'] = '<span class="username">{USERNAME}</span>';
1511          $_profile_cache['tpl_noprofile_colour'] = '<span style="color: {USERNAME_COLOUR};" class="username-coloured">{USERNAME}</span>';
1512          $_profile_cache['tpl_profile'] = '<a href="{PROFILE_URL}" class="username">{USERNAME}</a>';
1513          $_profile_cache['tpl_profile_colour'] = '<a href="{PROFILE_URL}" style="color: {USERNAME_COLOUR};" class="username-coloured">{USERNAME}</a>';
1514      }
1515  
1516      global $user, $auth;
1517  
1518      // This switch makes sure we only run code required for the mode
1519      switch ($mode)
1520      {
1521          case 'full':
1522          case 'no_profile':
1523          case 'colour':
1524  
1525              // Build correct username colour
1526              $username_colour = ($username_colour) ? '#' . $username_colour : '';
1527  
1528              // Return colour
1529              if ($mode == 'colour')
1530              {
1531                  $username_string = $username_colour;
1532                  break;
1533              }
1534  
1535          // no break;
1536  
1537          case 'username':
1538  
1539              // Build correct username
1540              if ($guest_username === false)
1541              {
1542                  $username = ($username) ? $username : $user->lang['GUEST'];
1543              }
1544              else
1545              {
1546                  $username = ($user_id && $user_id != ANONYMOUS) ? $username : ((!empty($guest_username)) ? $guest_username : $user->lang['GUEST']);
1547              }
1548  
1549              // Return username
1550              if ($mode == 'username')
1551              {
1552                  $username_string = $username;
1553                  break;
1554              }
1555  
1556          // no break;
1557  
1558          case 'profile':
1559  
1560              // Build correct profile url - only show if not anonymous and permission to view profile if registered user
1561              // For anonymous the link leads to a login page.
1562              if ($user_id && $user_id != ANONYMOUS && ($user->data['user_id'] == ANONYMOUS || $auth->acl_get('u_viewprofile')))
1563              {
1564                  $profile_url = ($custom_profile_url !== false) ? $custom_profile_url . '&amp;u=' . (int) $user_id : str_replace(array('={USER_ID}', '=%7BUSER_ID%7D'), '=' . (int) $user_id, $_profile_cache['base_url']);
1565              }
1566              else
1567              {
1568                  $profile_url = '';
1569              }
1570  
1571              // Return profile
1572              if ($mode == 'profile')
1573              {
1574                  $username_string = $profile_url;
1575                  break;
1576              }
1577  
1578          // no break;
1579      }
1580  
1581      if (!isset($username_string))
1582      {
1583          if (($mode == 'full' && !$profile_url) || $mode == 'no_profile')
1584          {
1585              $username_string = str_replace(array('{USERNAME_COLOUR}', '{USERNAME}'), array($username_colour, $username), (!$username_colour) ? $_profile_cache['tpl_noprofile'] : $_profile_cache['tpl_noprofile_colour']);
1586          }
1587          else
1588          {
1589              $username_string = str_replace(array('{PROFILE_URL}', '{USERNAME_COLOUR}', '{USERNAME}'), array($profile_url, $username_colour, $username), (!$username_colour) ? $_profile_cache['tpl_profile'] : $_profile_cache['tpl_profile_colour']);
1590          }
1591      }
1592  
1593      /**
1594      * Use this event to change the output of get_username_string()
1595      *
1596      * @event core.modify_username_string
1597      * @var string mode                profile|username|colour|full|no_profile
1598      * @var int user_id                String or array of additional url
1599      *                                parameters
1600      * @var string username            The user's username
1601      * @var string username_colour    The user's colour
1602      * @var string guest_username    Optional parameter to specify the
1603      *                                guest username.
1604      * @var string custom_profile_url Optional parameter to specify a
1605      *                                profile url.
1606      * @var string username_string    The string that has been generated
1607      * @var array _profile_cache        Array of original return templates
1608      * @since 3.1.0-a1
1609      */
1610      $vars = array(
1611          'mode',
1612          'user_id',
1613          'username',
1614          'username_colour',
1615          'guest_username',
1616          'custom_profile_url',
1617          'username_string',
1618          '_profile_cache',
1619      );
1620      extract($phpbb_dispatcher->trigger_event('core.modify_username_string', compact($vars)));
1621  
1622      return $username_string;
1623  }
1624  
1625  /**
1626   * Add an option to the quick-mod tools.
1627   *
1628   * @param string $url The recepting URL for the quickmod actions.
1629   * @param string $option The language key for the value of the option.
1630   * @param string $lang_string The language string to use.
1631   */
1632  function phpbb_add_quickmod_option($url, $option, $lang_string)
1633  {
1634      global $template, $user, $phpbb_path_helper;
1635  
1636      $lang_string = $user->lang($lang_string);
1637      $template->assign_block_vars('quickmod', array(
1638          'VALUE'        => $option,
1639          'TITLE'        => $lang_string,
1640          'LINK'        => $phpbb_path_helper->append_url_params($url, array('action' => $option)),
1641      ));
1642  }
1643  
1644  /**
1645  * Concatenate an array into a string list.
1646  *
1647  * @param array $items Array of items to concatenate
1648  * @param object $user The phpBB $user object.
1649  *
1650  * @return string String list. Examples: "A"; "A and B"; "A, B, and C"
1651  */
1652  function phpbb_generate_string_list($items, $user)
1653  {
1654      if (empty($items))
1655      {
1656          return '';
1657      }
1658  
1659      $count = count($items);
1660      $last_item = array_pop($items);
1661      $lang_key = 'STRING_LIST_MULTI';
1662  
1663      if ($count == 1)
1664      {
1665          return $last_item;
1666      }
1667      else if ($count == 2)
1668      {
1669          $lang_key = 'STRING_LIST_SIMPLE';
1670      }
1671      $list = implode($user->lang['COMMA_SEPARATOR'], $items);
1672  
1673      return $user->lang($lang_key, $list, $last_item);
1674  }
1675  
1676  class bitfield
1677  {
1678      var $data;
1679  
1680  	function __construct($bitfield = '')
1681      {
1682          $this->data = base64_decode($bitfield);
1683      }
1684  
1685      /**
1686      */
1687  	function get($n)
1688      {
1689          // Get the ($n / 8)th char
1690          $byte = $n >> 3;
1691  
1692          if (strlen($this->data) >= $byte + 1)
1693          {
1694              $c = $this->data[$byte];
1695  
1696              // Lookup the ($n % 8)th bit of the byte
1697              $bit = 7 - ($n & 7);
1698              return (bool) (ord($c) & (1 << $bit));
1699          }
1700          else
1701          {
1702              return false;
1703          }
1704      }
1705  
1706  	function set($n)
1707      {
1708          $byte = $n >> 3;
1709          $bit = 7 - ($n & 7);
1710  
1711          if (strlen($this->data) >= $byte + 1)
1712          {
1713              $this->data[$byte] = $this->data[$byte] | chr(1 << $bit);
1714          }
1715          else
1716          {
1717              $this->data .= str_repeat("\0", $byte - strlen($this->data));
1718              $this->data .= chr(1 << $bit);
1719          }
1720      }
1721  
1722  	function clear($n)
1723      {
1724          $byte = $n >> 3;
1725  
1726          if (strlen($this->data) >= $byte + 1)
1727          {
1728              $bit = 7 - ($n & 7);
1729              $this->data[$byte] = $this->data[$byte] &~ chr(1 << $bit);
1730          }
1731      }
1732  
1733  	function get_blob()
1734      {
1735          return $this->data;
1736      }
1737  
1738  	function get_base64()
1739      {
1740          return base64_encode($this->data);
1741      }
1742  
1743  	function get_bin()
1744      {
1745          $bin = '';
1746          $len = strlen($this->data);
1747  
1748          for ($i = 0; $i < $len; ++$i)
1749          {
1750              $bin .= str_pad(decbin(ord($this->data[$i])), 8, '0', STR_PAD_LEFT);
1751          }
1752  
1753          return $bin;
1754      }
1755  
1756  	function get_all_set()
1757      {
1758          return array_keys(array_filter(str_split($this->get_bin())));
1759      }
1760  
1761  	function merge($bitfield)
1762      {
1763          $this->data = $this->data | $bitfield->get_blob();
1764      }
1765  }
1766  
1767  /**
1768   * Formats the quote according to the given BBCode status setting
1769   *
1770   * @param phpbb\language\language                $language Language class
1771   * @param parse_message                         $message_parser Message parser class
1772   * @param phpbb\textformatter\utils_interface    $text_formatter_utils Text formatter utilities
1773   * @param bool                                     $bbcode_status The status of the BBCode setting
1774   * @param array                                 $quote_attributes The attributes of the quoted post
1775   * @param string                                 $message_link Link of the original quoted post
1776   */
1777  function phpbb_format_quote($language, $message_parser, $text_formatter_utils, $bbcode_status, $quote_attributes, $message_link = '')
1778  {
1779      if ($bbcode_status)
1780      {
1781          $quote_text = $text_formatter_utils->generate_quote(
1782              censor_text($message_parser->message),
1783              $quote_attributes
1784          );
1785  
1786          $message_parser->message = $quote_text . "\n\n";
1787      }
1788      else
1789      {
1790          $offset = 0;
1791          $quote_string = "&gt; ";
1792          $message = censor_text(trim($message_parser->message));
1793          // see if we are nesting. It's easily tricked but should work for one level of nesting
1794          if (strpos($message, "&gt;") !== false)
1795          {
1796              $offset = 10;
1797          }
1798          $message = utf8_wordwrap($message, 75 + $offset, "\n");
1799  
1800          $message = $quote_string . $message;
1801          $message = str_replace("\n", "\n" . $quote_string, $message);
1802  
1803          $message_parser->message = $quote_attributes['author'] . " " . $language->lang('WROTE') . ":\n" . $message . "\n";
1804      }
1805  
1806      if ($message_link)
1807      {
1808          $message_parser->message = $message_link . $message_parser->message;
1809      }
1810  }


Generated: Wed Nov 11 20:33:01 2020 Cross-referenced by PHPXref 0.7.1