Verzeichnisstruktur phpBB-3.1.0
- Veröffentlicht
- 27.10.2014
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
functions_content.php
0001 <?php
0002 /**
0003 *
0004 * This file is part of the phpBB Forum Software package.
0005 *
0006 * @copyright (c) phpBB Limited <https://www.phpbb.com>
0007 * @license GNU General Public License, version 2 (GPL-2.0)
0008 *
0009 * For full copyright and license information, please see
0010 * the docs/CREDITS.txt file.
0011 *
0012 */
0013
0014 /**
0015 * @ignore
0016 */
0017 if (!defined('IN_PHPBB'))
0018 {
0019 exit;
0020 }
0021
0022 /**
0023 * gen_sort_selects()
0024 * make_jumpbox()
0025 * bump_topic_allowed()
0026 * get_context()
0027 * phpbb_clean_search_string()
0028 * decode_message()
0029 * strip_bbcode()
0030 * generate_text_for_display()
0031 * generate_text_for_storage()
0032 * generate_text_for_edit()
0033 * make_clickable_callback()
0034 * make_clickable()
0035 * censor_text()
0036 * bbcode_nl2br()
0037 * smiley_text()
0038 * parse_attachments()
0039 * extension_allowed()
0040 * truncate_string()
0041 * get_username_string()
0042 * class bitfield
0043 */
0044
0045 /**
0046 * Generate sort selection fields
0047 */
0048 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)
0049 {
0050 global $user;
0051
0052 $sort_dir_text = array('a' => $user->lang['ASCENDING'], 'd' => $user->lang['DESCENDING']);
0053
0054 $sorts = array(
0055 'st' => array(
0056 'key' => 'sort_days',
0057 'default' => $def_st,
0058 'options' => $limit_days,
0059 'output' => &$s_limit_days,
0060 ),
0061
0062 'sk' => array(
0063 'key' => 'sort_key',
0064 'default' => $def_sk,
0065 'options' => $sort_by_text,
0066 'output' => &$s_sort_key,
0067 ),
0068
0069 'sd' => array(
0070 'key' => 'sort_dir',
0071 'default' => $def_sd,
0072 'options' => $sort_dir_text,
0073 'output' => &$s_sort_dir,
0074 ),
0075 );
0076 $u_sort_param = '';
0077
0078 foreach ($sorts as $name => $sort_ary)
0079 {
0080 $key = $sort_ary['key'];
0081 $selected = $$sort_ary['key'];
0082
0083 // Check if the key is selectable. If not, we reset to the default or first key found.
0084 // This ensures the values are always valid. We also set $sort_dir/sort_key/etc. to the
0085 // correct value, else the protection is void. ;)
0086 if (!isset($sort_ary['options'][$selected]))
0087 {
0088 if ($sort_ary['default'] !== false)
0089 {
0090 $selected = $$key = $sort_ary['default'];
0091 }
0092 else
0093 {
0094 @reset($sort_ary['options']);
0095 $selected = $$key = key($sort_ary['options']);
0096 }
0097 }
0098
0099 $sort_ary['output'] = '<select name="' . $name . '" id="' . $name . '">';
0100 foreach ($sort_ary['options'] as $option => $text)
0101 {
0102 $sort_ary['output'] .= '<option value="' . $option . '"' . (($selected == $option) ? ' selected="selected"' : '') . '>' . $text . '</option>';
0103 }
0104 $sort_ary['output'] .= '</select>';
0105
0106 $u_sort_param .= ($selected !== $sort_ary['default']) ? ((strlen($u_sort_param)) ? '&' : '') . "{$name}={$selected}" : '';
0107 }
0108
0109 return;
0110 }
0111
0112 /**
0113 * Generate Jumpbox
0114 */
0115 function make_jumpbox($action, $forum_id = false, $select_all = false, $acl_list = false, $force_display = false)
0116 {
0117 global $config, $auth, $template, $user, $db, $phpbb_path_helper;
0118
0119 // We only return if the jumpbox is not forced to be displayed (in case it is needed for functionality)
0120 if (!$config['load_jumpbox'] && $force_display === false)
0121 {
0122 return;
0123 }
0124
0125 $sql = 'SELECT forum_id, forum_name, parent_id, forum_type, left_id, right_id
0126 FROM ' . FORUMS_TABLE . '
0127 ORDER BY left_id ASC';
0128 $result = $db->sql_query($sql, 600);
0129
0130 $right = $padding = 0;
0131 $padding_store = array('0' => 0);
0132 $display_jumpbox = false;
0133 $iteration = 0;
0134
0135 // Sometimes it could happen that forums will be displayed here not be displayed within the index page
0136 // This is the result of forums not displayed at index, having list permissions and a parent of a forum with no permissions.
0137 // If this happens, the padding could be "broken"
0138
0139 while ($row = $db->sql_fetchrow($result))
0140 {
0141 if ($row['left_id'] < $right)
0142 {
0143 $padding++;
0144 $padding_store[$row['parent_id']] = $padding;
0145 }
0146 else if ($row['left_id'] > $right + 1)
0147 {
0148 // Ok, if the $padding_store for this parent is empty there is something wrong. For now we will skip over it.
0149 // @todo digging deep to find out "how" this can happen.
0150 $padding = (isset($padding_store[$row['parent_id']])) ? $padding_store[$row['parent_id']] : $padding;
0151 }
0152
0153 $right = $row['right_id'];
0154
0155 if ($row['forum_type'] == FORUM_CAT && ($row['left_id'] + 1 == $row['right_id']))
0156 {
0157 // Non-postable forum with no subforums, don't display
0158 continue;
0159 }
0160
0161 if (!$auth->acl_get('f_list', $row['forum_id']))
0162 {
0163 // if the user does not have permissions to list this forum skip
0164 continue;
0165 }
0166
0167 if ($acl_list && !$auth->acl_gets($acl_list, $row['forum_id']))
0168 {
0169 continue;
0170 }
0171
0172 if (!$display_jumpbox)
0173 {
0174 $template->assign_block_vars('jumpbox_forums', array(
0175 'FORUM_ID' => ($select_all) ? 0 : -1,
0176 'FORUM_NAME' => ($select_all) ? $user->lang['ALL_FORUMS'] : $user->lang['SELECT_FORUM'],
0177 'S_FORUM_COUNT' => $iteration,
0178 'LINK' => $phpbb_path_helper->append_url_params($action, array('f' => $forum_id)),
0179 ));
0180
0181 $iteration++;
0182 $display_jumpbox = true;
0183 }
0184
0185 $template->assign_block_vars('jumpbox_forums', array(
0186 'FORUM_ID' => $row['forum_id'],
0187 'FORUM_NAME' => $row['forum_name'],
0188 'SELECTED' => ($row['forum_id'] == $forum_id) ? ' selected="selected"' : '',
0189 'S_FORUM_COUNT' => $iteration,
0190 'S_IS_CAT' => ($row['forum_type'] == FORUM_CAT) ? true : false,
0191 'S_IS_LINK' => ($row['forum_type'] == FORUM_LINK) ? true : false,
0192 'S_IS_POST' => ($row['forum_type'] == FORUM_POST) ? true : false,
0193 'LINK' => $phpbb_path_helper->append_url_params($action, array('f' => $row['forum_id'])),
0194 ));
0195
0196 for ($i = 0; $i < $padding; $i++)
0197 {
0198 $template->assign_block_vars('jumpbox_forums.level', array());
0199 }
0200 $iteration++;
0201 }
0202 $db->sql_freeresult($result);
0203 unset($padding_store);
0204
0205 $url_parts = $phpbb_path_helper->get_url_parts($action);
0206
0207 $template->assign_vars(array(
0208 'S_DISPLAY_JUMPBOX' => $display_jumpbox,
0209 'S_JUMPBOX_ACTION' => $action,
0210 'HIDDEN_FIELDS_FOR_JUMPBOX' => build_hidden_fields($url_parts['params']),
0211 ));
0212
0213 return;
0214 }
0215
0216 /**
0217 * Bump Topic Check - used by posting and viewtopic
0218 */
0219 function bump_topic_allowed($forum_id, $topic_bumped, $last_post_time, $topic_poster, $last_topic_poster)
0220 {
0221 global $config, $auth, $user;
0222
0223 // Check permission and make sure the last post was not already bumped
0224 if (!$auth->acl_get('f_bump', $forum_id) || $topic_bumped)
0225 {
0226 return false;
0227 }
0228
0229 // Check bump time range, is the user really allowed to bump the topic at this time?
0230 $bump_time = ($config['bump_type'] == 'm') ? $config['bump_interval'] * 60 : (($config['bump_type'] == 'h') ? $config['bump_interval'] * 3600 : $config['bump_interval'] * 86400);
0231
0232 // Check bump time
0233 if ($last_post_time + $bump_time > time())
0234 {
0235 return false;
0236 }
0237
0238 // Check bumper, only topic poster and last poster are allowed to bump
0239 if ($topic_poster != $user->data['user_id'] && $last_topic_poster != $user->data['user_id'])
0240 {
0241 return false;
0242 }
0243
0244 // A bump time of 0 will completely disable the bump feature... not intended but might be useful.
0245 return $bump_time;
0246 }
0247
0248 /**
0249 * Generates a text with approx. the specified length which contains the specified words and their context
0250 *
0251 * @param string $text The full text from which context shall be extracted
0252 * @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!)
0253 * @param int $length The desired length of the resulting text, however the result might be shorter or longer than this value
0254 *
0255 * @return string Context of the specified words separated by "..."
0256 */
0257 function get_context($text, $words, $length = 400)
0258 {
0259 // first replace all whitespaces with single spaces
0260 $text = preg_replace('/ +/', ' ', strtr($text, "\t\n\r\x0C ", ' '));
0261
0262 // we need to turn the entities back into their original form, to not cut the message in between them
0263 $entities = array('<', '>', '[', ']', '.', ':', ':');
0264 $characters = array('<', '>', '[', ']', '.', ':', ':');
0265 $text = str_replace($entities, $characters, $text);
0266
0267 $word_indizes = array();
0268 if (sizeof($words))
0269 {
0270 $match = '';
0271 // find the starting indizes of all words
0272 foreach ($words as $word)
0273 {
0274 if ($word)
0275 {
0276 if (preg_match('#(?:[^\w]|^)(' . $word . ')(?:[^\w]|$)#i', $text, $match))
0277 {
0278 if (empty($match[1]))
0279 {
0280 continue;
0281 }
0282
0283 $pos = utf8_strpos($text, $match[1]);
0284 if ($pos !== false)
0285 {
0286 $word_indizes[] = $pos;
0287 }
0288 }
0289 }
0290 }
0291 unset($match);
0292
0293 if (sizeof($word_indizes))
0294 {
0295 $word_indizes = array_unique($word_indizes);
0296 sort($word_indizes);
0297
0298 $wordnum = sizeof($word_indizes);
0299 // number of characters on the right and left side of each word
0300 $sequence_length = (int) ($length / (2 * $wordnum)) - 2;
0301 $final_text = '';
0302 $word = $j = 0;
0303 $final_text_index = -1;
0304
0305 // cycle through every character in the original text
0306 for ($i = $word_indizes[$word], $n = utf8_strlen($text); $i < $n; $i++)
0307 {
0308 // if the current position is the start of one of the words then append $sequence_length characters to the final text
0309 if (isset($word_indizes[$word]) && ($i == $word_indizes[$word]))
0310 {
0311 if ($final_text_index < $i - $sequence_length - 1)
0312 {
0313 $final_text .= '... ' . preg_replace('#^([^ ]*)#', '', utf8_substr($text, $i - $sequence_length, $sequence_length));
0314 }
0315 else
0316 {
0317 // if the final text is already nearer to the current word than $sequence_length we only append the text
0318 // from its current index on and distribute the unused length to all other sequenes
0319 $sequence_length += (int) (($final_text_index - $i + $sequence_length + 1) / (2 * $wordnum));
0320 $final_text .= utf8_substr($text, $final_text_index + 1, $i - $final_text_index - 1);
0321 }
0322 $final_text_index = $i - 1;
0323
0324 // add the following characters to the final text (see below)
0325 $word++;
0326 $j = 1;
0327 }
0328
0329 if ($j > 0)
0330 {
0331 // add the character to the final text and increment the sequence counter
0332 $final_text .= utf8_substr($text, $i, 1);
0333 $final_text_index++;
0334 $j++;
0335
0336 // if this is a whitespace then check whether we are done with this sequence
0337 if (utf8_substr($text, $i, 1) == ' ')
0338 {
0339 // only check whether we have to exit the context generation completely if we haven't already reached the end anyway
0340 if ($i + 4 < $n)
0341 {
0342 if (($j > $sequence_length && $word >= $wordnum) || utf8_strlen($final_text) > $length)
0343 {
0344 $final_text .= ' ...';
0345 break;
0346 }
0347 }
0348 else
0349 {
0350 // make sure the text really reaches the end
0351 $j -= 4;
0352 }
0353
0354 // stop context generation and wait for the next word
0355 if ($j > $sequence_length)
0356 {
0357 $j = 0;
0358 }
0359 }
0360 }
0361 }
0362 return str_replace($characters, $entities, $final_text);
0363 }
0364 }
0365
0366 if (!sizeof($words) || !sizeof($word_indizes))
0367 {
0368 return str_replace($characters, $entities, ((utf8_strlen($text) >= $length + 3) ? utf8_substr($text, 0, $length) . '...' : $text));
0369 }
0370 }
0371
0372 /**
0373 * Cleans a search string by removing single wildcards from it and replacing multiple spaces with a single one.
0374 *
0375 * @param string $search_string The full search string which should be cleaned.
0376 *
0377 * @return string The cleaned search string without any wildcards and multiple spaces.
0378 */
0379 function phpbb_clean_search_string($search_string)
0380 {
0381 // This regular expressions matches every single wildcard.
0382 // That means one after a whitespace or the beginning of the string or one before a whitespace or the end of the string.
0383 $search_string = preg_replace('#(?<=^|\s)\*+(?=\s|$)#', '', $search_string);
0384 $search_string = trim($search_string);
0385 $search_string = preg_replace(array('#\s+#u', '#\*+#u'), array(' ', '*'), $search_string);
0386 return $search_string;
0387 }
0388
0389 /**
0390 * Decode text whereby text is coming from the db and expected to be pre-parsed content
0391 * We are placing this outside of the message parser because we are often in need of it...
0392 */
0393 function decode_message(&$message, $bbcode_uid = '')
0394 {
0395 global $config;
0396
0397 if ($bbcode_uid)
0398 {
0399 $match = array('<br />', "[/*:m:$bbcode_uid]", ":u:$bbcode_uid", ":o:$bbcode_uid", ":$bbcode_uid");
0400 $replace = array("\n", '', '', '', '');
0401 }
0402 else
0403 {
0404 $match = array('<br />');
0405 $replace = array("\n");
0406 }
0407
0408 $message = str_replace($match, $replace, $message);
0409
0410 $match = get_preg_expression('bbcode_htm');
0411 $replace = array('\1', '\1', '\2', '\1', '', '');
0412
0413 $message = preg_replace($match, $replace, $message);
0414 }
0415
0416 /**
0417 * Strips all bbcode from a text and returns the plain content
0418 */
0419 function strip_bbcode(&$text, $uid = '')
0420 {
0421 if (!$uid)
0422 {
0423 $uid = '[0-9a-z]{5,}';
0424 }
0425
0426 $text = preg_replace("#\[\/?[a-z0-9\*\+\-]+(?:=(?:".*"|[^\]]*))?(?::[a-z])?(\:$uid)\]#", ' ', $text);
0427
0428 $match = get_preg_expression('bbcode_htm');
0429 $replace = array('\1', '\1', '\2', '\1', '', '');
0430
0431 $text = preg_replace($match, $replace, $text);
0432 }
0433
0434 /**
0435 * For display of custom parsed text on user-facing pages
0436 * Expects $text to be the value directly from the database (stored value)
0437 */
0438 function generate_text_for_display($text, $uid, $bitfield, $flags, $censor_text = true)
0439 {
0440 static $bbcode;
0441 global $phpbb_dispatcher;
0442
0443 if ($text === '')
0444 {
0445 return '';
0446 }
0447
0448 /**
0449 * Use this event to modify the text before it is parsed
0450 *
0451 * @event core.modify_text_for_display_before
0452 * @var string text The text to parse
0453 * @var string uid The BBCode UID
0454 * @var string bitfield The BBCode Bitfield
0455 * @var int flags The BBCode Flags
0456 * @var bool censor_text Whether or not to apply word censors
0457 * @since 3.1.0-a1
0458 */
0459 $vars = array('text', 'uid', 'bitfield', 'flags', 'censor_text');
0460 extract($phpbb_dispatcher->trigger_event('core.modify_text_for_display_before', compact($vars)));
0461
0462 if ($censor_text)
0463 {
0464 $text = censor_text($text);
0465 }
0466
0467 // Parse bbcode if bbcode uid stored and bbcode enabled
0468 if ($uid && ($flags & OPTION_FLAG_BBCODE))
0469 {
0470 if (!class_exists('bbcode'))
0471 {
0472 global $phpbb_root_path, $phpEx;
0473 include($phpbb_root_path . 'includes/bbcode.' . $phpEx);
0474 }
0475
0476 if (empty($bbcode))
0477 {
0478 $bbcode = new bbcode($bitfield);
0479 }
0480 else
0481 {
0482 $bbcode->bbcode($bitfield);
0483 }
0484
0485 $bbcode->bbcode_second_pass($text, $uid);
0486 }
0487
0488 $text = bbcode_nl2br($text);
0489 $text = smiley_text($text, !($flags & OPTION_FLAG_SMILIES));
0490
0491 /**
0492 * Use this event to modify the text after it is parsed
0493 *
0494 * @event core.modify_text_for_display_after
0495 * @var string text The text to parse
0496 * @var string uid The BBCode UID
0497 * @var string bitfield The BBCode Bitfield
0498 * @var int flags The BBCode Flags
0499 * @since 3.1.0-a1
0500 */
0501 $vars = array('text', 'uid', 'bitfield', 'flags');
0502 extract($phpbb_dispatcher->trigger_event('core.modify_text_for_display_after', compact($vars)));
0503
0504 return $text;
0505 }
0506
0507 /**
0508 * For parsing custom parsed text to be stored within the database.
0509 * This function additionally returns the uid and bitfield that needs to be stored.
0510 * Expects $text to be the value directly from request_var() and in it's non-parsed form
0511 *
0512 * @param string $text The text to be replaced with the parsed one
0513 * @param string $uid The BBCode uid for this parse
0514 * @param string $bitfield The BBCode bitfield for this parse
0515 * @param int $flags The allow_bbcode, allow_urls and allow_smilies compiled into a single integer.
0516 * @param bool $allow_bbcode If BBCode is allowed (i.e. if BBCode is parsed)
0517 * @param bool $allow_urls If urls is allowed
0518 * @param bool $allow_smilies If smilies are allowed
0519 *
0520 * @return array An array of string with the errors that occurred while parsing
0521 */
0522 function generate_text_for_storage(&$text, &$uid, &$bitfield, &$flags, $allow_bbcode = false, $allow_urls = false, $allow_smilies = false)
0523 {
0524 global $phpbb_root_path, $phpEx, $phpbb_dispatcher;
0525
0526 /**
0527 * Use this event to modify the text before it is prepared for storage
0528 *
0529 * @event core.modify_text_for_storage_before
0530 * @var string text The text to parse
0531 * @var string uid The BBCode UID
0532 * @var string bitfield The BBCode Bitfield
0533 * @var int flags The BBCode Flags
0534 * @var bool allow_bbcode Whether or not to parse BBCode
0535 * @var bool allow_urls Whether or not to parse URLs
0536 * @var bool allow_smilies Whether or not to parse Smilies
0537 * @since 3.1.0-a1
0538 */
0539 $vars = array(
0540 'text',
0541 'uid',
0542 'bitfield',
0543 'flags',
0544 'allow_bbcode',
0545 'allow_urls',
0546 'allow_smilies',
0547 );
0548 extract($phpbb_dispatcher->trigger_event('core.modify_text_for_storage_before', compact($vars)));
0549
0550 $uid = $bitfield = '';
0551 $flags = (($allow_bbcode) ? OPTION_FLAG_BBCODE : 0) + (($allow_smilies) ? OPTION_FLAG_SMILIES : 0) + (($allow_urls) ? OPTION_FLAG_LINKS : 0);
0552
0553 if ($text === '')
0554 {
0555 return;
0556 }
0557
0558 if (!class_exists('parse_message'))
0559 {
0560 include($phpbb_root_path . 'includes/message_parser.' . $phpEx);
0561 }
0562
0563 $message_parser = new parse_message($text);
0564 $message_parser->parse($allow_bbcode, $allow_urls, $allow_smilies);
0565
0566 $text = $message_parser->message;
0567 $uid = $message_parser->bbcode_uid;
0568
0569 // If the bbcode_bitfield is empty, there is no need for the uid to be stored.
0570 if (!$message_parser->bbcode_bitfield)
0571 {
0572 $uid = '';
0573 }
0574
0575 $bitfield = $message_parser->bbcode_bitfield;
0576
0577 /**
0578 * Use this event to modify the text after it is prepared for storage
0579 *
0580 * @event core.modify_text_for_storage_after
0581 * @var string text The text to parse
0582 * @var string uid The BBCode UID
0583 * @var string bitfield The BBCode Bitfield
0584 * @var int flags The BBCode Flags
0585 * @since 3.1.0-a1
0586 */
0587 $vars = array('text', 'uid', 'bitfield', 'flags');
0588 extract($phpbb_dispatcher->trigger_event('core.modify_text_for_storage_after', compact($vars)));
0589
0590 return $message_parser->warn_msg;
0591 }
0592
0593 /**
0594 * For decoding custom parsed text for edits as well as extracting the flags
0595 * Expects $text to be the value directly from the database (pre-parsed content)
0596 */
0597 function generate_text_for_edit($text, $uid, $flags)
0598 {
0599 global $phpbb_root_path, $phpEx, $phpbb_dispatcher;
0600
0601 /**
0602 * Use this event to modify the text before it is decoded for editing
0603 *
0604 * @event core.modify_text_for_edit_before
0605 * @var string text The text to parse
0606 * @var string uid The BBCode UID
0607 * @var int flags The BBCode Flags
0608 * @since 3.1.0-a1
0609 */
0610 $vars = array('text', 'uid', 'flags');
0611 extract($phpbb_dispatcher->trigger_event('core.modify_text_for_edit_before', compact($vars)));
0612
0613 decode_message($text, $uid);
0614
0615 /**
0616 * Use this event to modify the text after it is decoded for editing
0617 *
0618 * @event core.modify_text_for_edit_after
0619 * @var string text The text to parse
0620 * @var int flags The BBCode Flags
0621 * @since 3.1.0-a1
0622 */
0623 $vars = array('text', 'flags');
0624 extract($phpbb_dispatcher->trigger_event('core.modify_text_for_edit_after', compact($vars)));
0625
0626 return array(
0627 'allow_bbcode' => ($flags & OPTION_FLAG_BBCODE) ? 1 : 0,
0628 'allow_smilies' => ($flags & OPTION_FLAG_SMILIES) ? 1 : 0,
0629 'allow_urls' => ($flags & OPTION_FLAG_LINKS) ? 1 : 0,
0630 'text' => $text
0631 );
0632 }
0633
0634 /**
0635 * A subroutine of make_clickable used with preg_replace
0636 * It places correct HTML around an url, shortens the displayed text
0637 * and makes sure no entities are inside URLs
0638 */
0639 function make_clickable_callback($type, $whitespace, $url, $relative_url, $class)
0640 {
0641 $orig_url = $url;
0642 $orig_relative = $relative_url;
0643 $append = '';
0644 $url = htmlspecialchars_decode($url);
0645 $relative_url = htmlspecialchars_decode($relative_url);
0646
0647 // make sure no HTML entities were matched
0648 $chars = array('<', '>', '"');
0649 $split = false;
0650
0651 foreach ($chars as $char)
0652 {
0653 $next_split = strpos($url, $char);
0654 if ($next_split !== false)
0655 {
0656 $split = ($split !== false) ? min($split, $next_split) : $next_split;
0657 }
0658 }
0659
0660 if ($split !== false)
0661 {
0662 // an HTML entity was found, so the URL has to end before it
0663 $append = substr($url, $split) . $relative_url;
0664 $url = substr($url, 0, $split);
0665 $relative_url = '';
0666 }
0667 else if ($relative_url)
0668 {
0669 // same for $relative_url
0670 $split = false;
0671 foreach ($chars as $char)
0672 {
0673 $next_split = strpos($relative_url, $char);
0674 if ($next_split !== false)
0675 {
0676 $split = ($split !== false) ? min($split, $next_split) : $next_split;
0677 }
0678 }
0679
0680 if ($split !== false)
0681 {
0682 $append = substr($relative_url, $split);
0683 $relative_url = substr($relative_url, 0, $split);
0684 }
0685 }
0686
0687 // if the last character of the url is a punctuation mark, exclude it from the url
0688 $last_char = ($relative_url) ? $relative_url[strlen($relative_url) - 1] : $url[strlen($url) - 1];
0689
0690 switch ($last_char)
0691 {
0692 case '.':
0693 case '?':
0694 case '!':
0695 case ':':
0696 case ',':
0697 $append = $last_char;
0698 if ($relative_url)
0699 {
0700 $relative_url = substr($relative_url, 0, -1);
0701 }
0702 else
0703 {
0704 $url = substr($url, 0, -1);
0705 }
0706 break;
0707
0708 // set last_char to empty here, so the variable can be used later to
0709 // check whether a character was removed
0710 default:
0711 $last_char = '';
0712 break;
0713 }
0714
0715 $short_url = (strlen($url) > 55) ? substr($url, 0, 39) . ' ... ' . substr($url, -10) : $url;
0716
0717 switch ($type)
0718 {
0719 case MAGIC_URL_LOCAL:
0720 $tag = 'l';
0721 $relative_url = preg_replace('/[&?]sid=[0-9a-f]{32}$/', '', preg_replace('/([&?])sid=[0-9a-f]{32}&/', '$1', $relative_url));
0722 $url = $url . '/' . $relative_url;
0723 $text = $relative_url;
0724
0725 // this url goes to http://domain.tld/path/to/board/ which
0726 // would result in an empty link if treated as local so
0727 // don't touch it and let MAGIC_URL_FULL take care of it.
0728 if (!$relative_url)
0729 {
0730 return $whitespace . $orig_url . '/' . $orig_relative; // slash is taken away by relative url pattern
0731 }
0732 break;
0733
0734 case MAGIC_URL_FULL:
0735 $tag = 'm';
0736 $text = $short_url;
0737 break;
0738
0739 case MAGIC_URL_WWW:
0740 $tag = 'w';
0741 $url = 'http://' . $url;
0742 $text = $short_url;
0743 break;
0744
0745 case MAGIC_URL_EMAIL:
0746 $tag = 'e';
0747 $text = $short_url;
0748 $url = 'mailto:' . $url;
0749 break;
0750 }
0751
0752 $url = htmlspecialchars($url);
0753 $text = htmlspecialchars($text);
0754 $append = htmlspecialchars($append);
0755
0756 $html = "$whitespace<!-- $tag --><a$class href=\"$url\">$text</a><!-- $tag -->$append";
0757
0758 return $html;
0759 }
0760
0761 /**
0762 * make_clickable function
0763 *
0764 * Replace magic urls of form http://xxx.xxx., www.xxx. and xxx@xxx.xxx.
0765 * Cuts down displayed size of link if over 50 chars, turns absolute links
0766 * into relative versions when the server/script path matches the link
0767 */
0768 function make_clickable($text, $server_url = false, $class = 'postlink')
0769 {
0770 if ($server_url === false)
0771 {
0772 $server_url = generate_board_url();
0773 }
0774
0775 static $static_class;
0776 static $magic_url_match_args;
0777
0778 if (!isset($magic_url_match_args[$server_url]) || $static_class != $class)
0779 {
0780 $static_class = $class;
0781 $class = ($static_class) ? ' class="' . $static_class . '"' : '';
0782 $local_class = ($static_class) ? ' class="' . $static_class . '-local"' : '';
0783
0784 if (!is_array($magic_url_match_args))
0785 {
0786 $magic_url_match_args = array();
0787 }
0788
0789 // relative urls for this board
0790 $magic_url_match_args[$server_url][] = array(
0791 '#(^|[\n\t (>.])(' . preg_quote($server_url, '#') . ')/(' . get_preg_expression('relative_url_inline') . ')#i',
0792 MAGIC_URL_LOCAL,
0793 $local_class,
0794 );
0795
0796 // matches a xxxx://aaaaa.bbb.cccc. ...
0797 $magic_url_match_args[$server_url][] = array(
0798 '#(^|[\n\t (>.])(' . get_preg_expression('url_inline') . ')#i',
0799 MAGIC_URL_FULL,
0800 $class,
0801 );
0802
0803 // matches a "www.xxxx.yyyy[/zzzz]" kinda lazy URL thing
0804 $magic_url_match_args[$server_url][] = array(
0805 '#(^|[\n\t (>])(' . get_preg_expression('www_url_inline') . ')#i',
0806 MAGIC_URL_WWW,
0807 $class,
0808 );
0809
0810 // matches an email@domain type address at the start of a line, or after a space or after what might be a BBCode.
0811 $magic_url_match_args[$server_url][] = array(
0812 '/(^|[\n\t (>])(' . get_preg_expression('email') . ')/i',
0813 MAGIC_URL_EMAIL,
0814 '',
0815 );
0816 }
0817
0818 foreach ($magic_url_match_args[$server_url] as $magic_args)
0819 {
0820 if (preg_match($magic_args[0], $text, $matches))
0821 {
0822 $text = preg_replace_callback($magic_args[0], function($matches) use ($magic_args)
0823 {
0824 $relative_url = isset($matches[3]) ? $matches[3] : '';
0825 return make_clickable_callback($magic_args[1], $matches[1], $matches[2], $relative_url, $magic_args[2]);
0826 }, $text);
0827 }
0828 }
0829
0830 return $text;
0831 }
0832
0833 /**
0834 * Censoring
0835 */
0836 function censor_text($text)
0837 {
0838 static $censors;
0839
0840 // Nothing to do?
0841 if ($text === '')
0842 {
0843 return '';
0844 }
0845
0846 // We moved the word censor checks in here because we call this function quite often - and then only need to do the check once
0847 if (!isset($censors) || !is_array($censors))
0848 {
0849 global $config, $user, $auth, $cache;
0850
0851 // We check here if the user is having viewing censors disabled (and also allowed to do so).
0852 if (!$user->optionget('viewcensors') && $config['allow_nocensors'] && $auth->acl_get('u_chgcensors'))
0853 {
0854 $censors = array();
0855 }
0856 else
0857 {
0858 $censors = $cache->obtain_word_list();
0859 }
0860 }
0861
0862 if (sizeof($censors))
0863 {
0864 return preg_replace($censors['match'], $censors['replace'], $text);
0865 }
0866
0867 return $text;
0868 }
0869
0870 /**
0871 * custom version of nl2br which takes custom BBCodes into account
0872 */
0873 function bbcode_nl2br($text)
0874 {
0875 // custom BBCodes might contain carriage returns so they
0876 // are not converted into <br /> so now revert that
0877 $text = str_replace(array("\n", "\r"), array('<br />', "\n"), $text);
0878 return $text;
0879 }
0880
0881 /**
0882 * Smiley processing
0883 */
0884 function smiley_text($text, $force_option = false)
0885 {
0886 global $config, $user, $phpbb_path_helper;
0887
0888 if ($force_option || !$config['allow_smilies'] || !$user->optionget('viewsmilies'))
0889 {
0890 return preg_replace('#<!\-\- s(.*?) \-\-><img src="\{SMILIES_PATH\}\/.*? \/><!\-\- s\1 \-\->#', '\1', $text);
0891 }
0892 else
0893 {
0894 $root_path = (defined('PHPBB_USE_BOARD_URL_PATH') && PHPBB_USE_BOARD_URL_PATH) ? generate_board_url() . '/' : $phpbb_path_helper->get_web_root_path();
0895 return preg_replace('#<!\-\- s(.*?) \-\-><img src="\{SMILIES_PATH\}\/(.*?) \/><!\-\- s\1 \-\->#', '<img class="smilies" src="' . $root_path . $config['smilies_path'] . '/\2 />', $text);
0896 }
0897 }
0898
0899 /**
0900 * General attachment parsing
0901 *
0902 * @param mixed $forum_id The forum id the attachments are displayed in (false if in private message)
0903 * @param string &$message The post/private message
0904 * @param array &$attachments The attachments to parse for (inline) display. The attachments array will hold templated data after parsing.
0905 * @param array &$update_count The attachment counts to be updated - will be filled
0906 * @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.
0907 */
0908 function parse_attachments($forum_id, &$message, &$attachments, &$update_count, $preview = false)
0909 {
0910 if (!sizeof($attachments))
0911 {
0912 return;
0913 }
0914
0915 global $template, $cache, $user, $phpbb_dispatcher;
0916 global $extensions, $config, $phpbb_root_path, $phpEx;
0917
0918 //
0919 $compiled_attachments = array();
0920
0921 if (!isset($template->filename['attachment_tpl']))
0922 {
0923 $template->set_filenames(array(
0924 'attachment_tpl' => 'attachment.html')
0925 );
0926 }
0927
0928 if (empty($extensions) || !is_array($extensions))
0929 {
0930 $extensions = $cache->obtain_attach_extensions($forum_id);
0931 }
0932
0933 // Look for missing attachment information...
0934 $attach_ids = array();
0935 foreach ($attachments as $pos => $attachment)
0936 {
0937 // If is_orphan is set, we need to retrieve the attachments again...
0938 if (!isset($attachment['extension']) && !isset($attachment['physical_filename']))
0939 {
0940 $attach_ids[(int) $attachment['attach_id']] = $pos;
0941 }
0942 }
0943
0944 // Grab attachments (security precaution)
0945 if (sizeof($attach_ids))
0946 {
0947 global $db;
0948
0949 $new_attachment_data = array();
0950
0951 $sql = 'SELECT *
0952 FROM ' . ATTACHMENTS_TABLE . '
0953 WHERE ' . $db->sql_in_set('attach_id', array_keys($attach_ids));
0954 $result = $db->sql_query($sql);
0955
0956 while ($row = $db->sql_fetchrow($result))
0957 {
0958 if (!isset($attach_ids[$row['attach_id']]))
0959 {
0960 continue;
0961 }
0962
0963 // If we preview attachments we will set some retrieved values here
0964 if ($preview)
0965 {
0966 $row['attach_comment'] = $attachments[$attach_ids[$row['attach_id']]]['attach_comment'];
0967 }
0968
0969 $new_attachment_data[$attach_ids[$row['attach_id']]] = $row;
0970 }
0971 $db->sql_freeresult($result);
0972
0973 $attachments = $new_attachment_data;
0974 unset($new_attachment_data);
0975 }
0976
0977 // Sort correctly
0978 if ($config['display_order'])
0979 {
0980 // Ascending sort
0981 krsort($attachments);
0982 }
0983 else
0984 {
0985 // Descending sort
0986 ksort($attachments);
0987 }
0988
0989 foreach ($attachments as $attachment)
0990 {
0991 if (!sizeof($attachment))
0992 {
0993 continue;
0994 }
0995
0996 // We need to reset/empty the _file block var, because this function might be called more than once
0997 $template->destroy_block_vars('_file');
0998
0999 $block_array = array();
1000
1001 // Some basics...
1002 $attachment['extension'] = strtolower(trim($attachment['extension']));
1003 $filename = $phpbb_root_path . $config['upload_path'] . '/' . utf8_basename($attachment['physical_filename']);
1004 $thumbnail_filename = $phpbb_root_path . $config['upload_path'] . '/thumb_' . utf8_basename($attachment['physical_filename']);
1005
1006 $upload_icon = '';
1007
1008 if (isset($extensions[$attachment['extension']]))
1009 {
1010 if ($user->img('icon_topic_attach', '') && !$extensions[$attachment['extension']]['upload_icon'])
1011 {
1012 $upload_icon = $user->img('icon_topic_attach', '');
1013 }
1014 else if ($extensions[$attachment['extension']]['upload_icon'])
1015 {
1016 $upload_icon = '<img src="' . $phpbb_root_path . $config['upload_icons_path'] . '/' . trim($extensions[$attachment['extension']]['upload_icon']) . '" alt="" />';
1017 }
1018 }
1019
1020 $filesize = get_formatted_filesize($attachment['filesize'], false);
1021
1022 $comment = bbcode_nl2br(censor_text($attachment['attach_comment']));
1023
1024 $block_array += array(
1025 'UPLOAD_ICON' => $upload_icon,
1026 'FILESIZE' => $filesize['value'],
1027 'SIZE_LANG' => $filesize['unit'],
1028 'DOWNLOAD_NAME' => utf8_basename($attachment['real_filename']),
1029 'COMMENT' => $comment,
1030 );
1031
1032 $denied = false;
1033
1034 if (!extension_allowed($forum_id, $attachment['extension'], $extensions))
1035 {
1036 $denied = true;
1037
1038 $block_array += array(
1039 'S_DENIED' => true,
1040 'DENIED_MESSAGE' => sprintf($user->lang['EXTENSION_DISABLED_AFTER_POSTING'], $attachment['extension'])
1041 );
1042 }
1043
1044 if (!$denied)
1045 {
1046 $l_downloaded_viewed = $download_link = '';
1047 $display_cat = $extensions[$attachment['extension']]['display_cat'];
1048
1049 if ($display_cat == ATTACHMENT_CATEGORY_IMAGE)
1050 {
1051 if ($attachment['thumbnail'])
1052 {
1053 $display_cat = ATTACHMENT_CATEGORY_THUMB;
1054 }
1055 else
1056 {
1057 if ($config['img_display_inlined'])
1058 {
1059 if ($config['img_link_width'] || $config['img_link_height'])
1060 {
1061 $dimension = @getimagesize($filename);
1062
1063 // If the dimensions could not be determined or the image being 0x0 we display it as a link for safety purposes
1064 if ($dimension === false || empty($dimension[0]) || empty($dimension[1]))
1065 {
1066 $display_cat = ATTACHMENT_CATEGORY_NONE;
1067 }
1068 else
1069 {
1070 $display_cat = ($dimension[0] <= $config['img_link_width'] && $dimension[1] <= $config['img_link_height']) ? ATTACHMENT_CATEGORY_IMAGE : ATTACHMENT_CATEGORY_NONE;
1071 }
1072 }
1073 }
1074 else
1075 {
1076 $display_cat = ATTACHMENT_CATEGORY_NONE;
1077 }
1078 }
1079 }
1080
1081 // Make some descisions based on user options being set.
1082 if (($display_cat == ATTACHMENT_CATEGORY_IMAGE || $display_cat == ATTACHMENT_CATEGORY_THUMB) && !$user->optionget('viewimg'))
1083 {
1084 $display_cat = ATTACHMENT_CATEGORY_NONE;
1085 }
1086
1087 if ($display_cat == ATTACHMENT_CATEGORY_FLASH && !$user->optionget('viewflash'))
1088 {
1089 $display_cat = ATTACHMENT_CATEGORY_NONE;
1090 }
1091
1092 $download_link = append_sid("{$phpbb_root_path}download/file.$phpEx", 'id=' . $attachment['attach_id']);
1093 $l_downloaded_viewed = 'VIEWED_COUNTS';
1094
1095 switch ($display_cat)
1096 {
1097 // Images
1098 case ATTACHMENT_CATEGORY_IMAGE:
1099 $inline_link = append_sid("{$phpbb_root_path}download/file.$phpEx", 'id=' . $attachment['attach_id']);
1100 $download_link .= '&mode=view';
1101
1102 $block_array += array(
1103 'S_IMAGE' => true,
1104 'U_INLINE_LINK' => $inline_link,
1105 );
1106
1107 $update_count[] = $attachment['attach_id'];
1108 break;
1109
1110 // Images, but display Thumbnail
1111 case ATTACHMENT_CATEGORY_THUMB:
1112 $thumbnail_link = append_sid("{$phpbb_root_path}download/file.$phpEx", 'id=' . $attachment['attach_id'] . '&t=1');
1113 $download_link .= '&mode=view';
1114
1115 $block_array += array(
1116 'S_THUMBNAIL' => true,
1117 'THUMB_IMAGE' => $thumbnail_link,
1118 );
1119
1120 $update_count[] = $attachment['attach_id'];
1121 break;
1122
1123 // Windows Media Streams
1124 case ATTACHMENT_CATEGORY_WM:
1125
1126 // Giving the filename directly because within the wm object all variables are in local context making it impossible
1127 // to validate against a valid session (all params can differ)
1128 // $download_link = $filename;
1129
1130 $block_array += array(
1131 'U_FORUM' => generate_board_url(),
1132 'ATTACH_ID' => $attachment['attach_id'],
1133 'S_WM_FILE' => true,
1134 );
1135
1136 // Viewed/Heared File ... update the download count
1137 $update_count[] = $attachment['attach_id'];
1138 break;
1139
1140 // Real Media Streams
1141 case ATTACHMENT_CATEGORY_RM:
1142 case ATTACHMENT_CATEGORY_QUICKTIME:
1143
1144 $block_array += array(
1145 'S_RM_FILE' => ($display_cat == ATTACHMENT_CATEGORY_RM) ? true : false,
1146 'S_QUICKTIME_FILE' => ($display_cat == ATTACHMENT_CATEGORY_QUICKTIME) ? true : false,
1147 'U_FORUM' => generate_board_url(),
1148 'ATTACH_ID' => $attachment['attach_id'],
1149 );
1150
1151 // Viewed/Heared File ... update the download count
1152 $update_count[] = $attachment['attach_id'];
1153 break;
1154
1155 // Macromedia Flash Files
1156 case ATTACHMENT_CATEGORY_FLASH:
1157 list($width, $height) = @getimagesize($filename);
1158
1159 $block_array += array(
1160 'S_FLASH_FILE' => true,
1161 'WIDTH' => $width,
1162 'HEIGHT' => $height,
1163 'U_VIEW_LINK' => $download_link . '&view=1',
1164 );
1165
1166 // Viewed/Heared File ... update the download count
1167 $update_count[] = $attachment['attach_id'];
1168 break;
1169
1170 default:
1171 $l_downloaded_viewed = 'DOWNLOAD_COUNTS';
1172
1173 $block_array += array(
1174 'S_FILE' => true,
1175 );
1176 break;
1177 }
1178
1179 if (!isset($attachment['download_count']))
1180 {
1181 $attachment['download_count'] = 0;
1182 }
1183
1184 $block_array += array(
1185 'U_DOWNLOAD_LINK' => $download_link,
1186 'L_DOWNLOAD_COUNT' => $user->lang($l_downloaded_viewed, (int) $attachment['download_count']),
1187 );
1188 }
1189
1190 /**
1191 * Use this event to modify the attachment template data.
1192 *
1193 * This event is triggered once per attachment.
1194 *
1195 * @event core.parse_attachments_modify_template_data
1196 * @var array attachment Array with attachment data
1197 * @var array block_array Template data of the attachment
1198 * @var int display_cat Attachment category data
1199 * @var string download_link Attachment download link
1200 * @var array extensions Array with attachment extensions data
1201 * @var mixed forum_id The forum id the attachments are displayed in (false if in private message)
1202 * @var bool preview Flag indicating if we are in post preview mode
1203 * @var array update_count Array with attachment ids to update download count
1204 * @since 3.1.0-RC5
1205 */
1206 $vars = array(
1207 'attachment',
1208 'block_array',
1209 'display_cat',
1210 'download_link',
1211 'extensions',
1212 'forum_id',
1213 'preview',
1214 'update_count',
1215 );
1216 extract($phpbb_dispatcher->trigger_event('core.parse_attachments_modify_template_data', compact($vars)));
1217
1218 $template->assign_block_vars('_file', $block_array);
1219
1220 $compiled_attachments[] = $template->assign_display('attachment_tpl');
1221 }
1222
1223 $attachments = $compiled_attachments;
1224 unset($compiled_attachments);
1225
1226 $tpl_size = sizeof($attachments);
1227
1228 $unset_tpl = array();
1229
1230 preg_match_all('#<!\-\- ia([0-9]+) \-\->(.*?)<!\-\- ia\1 \-\->#', $message, $matches, PREG_PATTERN_ORDER);
1231
1232 $replace = array();
1233 foreach ($matches[0] as $num => $capture)
1234 {
1235 // Flip index if we are displaying the reverse way
1236 $index = ($config['display_order']) ? ($tpl_size-($matches[1][$num] + 1)) : $matches[1][$num];
1237
1238 $replace['from'][] = $matches[0][$num];
1239 $replace['to'][] = (isset($attachments[$index])) ? $attachments[$index] : sprintf($user->lang['MISSING_INLINE_ATTACHMENT'], $matches[2][array_search($index, $matches[1])]);
1240
1241 $unset_tpl[] = $index;
1242 }
1243
1244 if (isset($replace['from']))
1245 {
1246 $message = str_replace($replace['from'], $replace['to'], $message);
1247 }
1248
1249 $unset_tpl = array_unique($unset_tpl);
1250
1251 // Needed to let not display the inlined attachments at the end of the post again
1252 foreach ($unset_tpl as $index)
1253 {
1254 unset($attachments[$index]);
1255 }
1256 }
1257
1258 /**
1259 * Check if extension is allowed to be posted.
1260 *
1261 * @param mixed $forum_id The forum id to check or false if private message
1262 * @param string $extension The extension to check, for example zip.
1263 * @param array &$extensions The extension array holding the information from the cache (will be obtained if empty)
1264 *
1265 * @return bool False if the extension is not allowed to be posted, else true.
1266 */
1267 function extension_allowed($forum_id, $extension, &$extensions)
1268 {
1269 if (empty($extensions))
1270 {
1271 global $cache;
1272 $extensions = $cache->obtain_attach_extensions($forum_id);
1273 }
1274
1275 return (!isset($extensions['_allowed_'][$extension])) ? false : true;
1276 }
1277
1278 /**
1279 * Truncates string while retaining special characters if going over the max length
1280 * The default max length is 60 at the moment
1281 * The maximum storage length is there to fit the string within the given length. The string may be further truncated due to html entities.
1282 * For example: string given is 'a "quote"' (length: 9), would be a stored as 'a "quote"' (length: 19)
1283 *
1284 * @param string $string The text to truncate to the given length. String is specialchared.
1285 * @param int $max_length Maximum length of string (multibyte character count as 1 char / Html entity count as 1 char)
1286 * @param int $max_store_length Maximum character length of string (multibyte character count as 1 char / Html entity count as entity chars).
1287 * @param bool $allow_reply Allow Re: in front of string
1288 * NOTE: This parameter can cause undesired behavior (returning strings longer than $max_store_length) and is deprecated.
1289 * @param string $append String to be appended
1290 */
1291 function truncate_string($string, $max_length = 60, $max_store_length = 255, $allow_reply = false, $append = '')
1292 {
1293 $chars = array();
1294
1295 $strip_reply = false;
1296 $stripped = false;
1297 if ($allow_reply && strpos($string, 'Re: ') === 0)
1298 {
1299 $strip_reply = true;
1300 $string = substr($string, 4);
1301 }
1302
1303 $_chars = utf8_str_split(htmlspecialchars_decode($string));
1304 $chars = array_map('utf8_htmlspecialchars', $_chars);
1305
1306 // Now check the length ;)
1307 if (sizeof($chars) > $max_length)
1308 {
1309 // Cut off the last elements from the array
1310 $string = implode('', array_slice($chars, 0, $max_length - utf8_strlen($append)));
1311 $stripped = true;
1312 }
1313
1314 // Due to specialchars, we may not be able to store the string...
1315 if (utf8_strlen($string) > $max_store_length)
1316 {
1317 // let's split again, we do not want half-baked strings where entities are split
1318 $_chars = utf8_str_split(htmlspecialchars_decode($string));
1319 $chars = array_map('utf8_htmlspecialchars', $_chars);
1320
1321 do
1322 {
1323 array_pop($chars);
1324 $string = implode('', $chars);
1325 }
1326 while (!empty($chars) && utf8_strlen($string) > $max_store_length);
1327 }
1328
1329 if ($strip_reply)
1330 {
1331 $string = 'Re: ' . $string;
1332 }
1333
1334 if ($append != '' && $stripped)
1335 {
1336 $string = $string . $append;
1337 }
1338
1339 return $string;
1340 }
1341
1342 /**
1343 * Get username details for placing into templates.
1344 * This function caches all modes on first call, except for no_profile and anonymous user - determined by $user_id.
1345 *
1346 * @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)
1347 * @param int $user_id The users id
1348 * @param string $username The users name
1349 * @param string $username_colour The users colour
1350 * @param string $guest_username optional parameter to specify the guest username. It will be used in favor of the GUEST language variable then.
1351 * @param string $custom_profile_url optional parameter to specify a profile url. The user id get appended to this url as &u={user_id}
1352 *
1353 * @return string A string consisting of what is wanted based on $mode.
1354 */
1355 function get_username_string($mode, $user_id, $username, $username_colour = '', $guest_username = false, $custom_profile_url = false)
1356 {
1357 static $_profile_cache;
1358 global $phpbb_dispatcher;
1359
1360 // We cache some common variables we need within this function
1361 if (empty($_profile_cache))
1362 {
1363 global $phpbb_root_path, $phpEx;
1364
1365 $_profile_cache['base_url'] = append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=viewprofile&u={USER_ID}');
1366 $_profile_cache['tpl_noprofile'] = '<span class="username">{USERNAME}</span>';
1367 $_profile_cache['tpl_noprofile_colour'] = '<span style="color: {USERNAME_COLOUR};" class="username-coloured">{USERNAME}</span>';
1368 $_profile_cache['tpl_profile'] = '<a href="{PROFILE_URL}" class="username">{USERNAME}</a>';
1369 $_profile_cache['tpl_profile_colour'] = '<a href="{PROFILE_URL}" style="color: {USERNAME_COLOUR};" class="username-coloured">{USERNAME}</a>';
1370 }
1371
1372 global $user, $auth;
1373
1374 // This switch makes sure we only run code required for the mode
1375 switch ($mode)
1376 {
1377 case 'full':
1378 case 'no_profile':
1379 case 'colour':
1380
1381 // Build correct username colour
1382 $username_colour = ($username_colour) ? '#' . $username_colour : '';
1383
1384 // Return colour
1385 if ($mode == 'colour')
1386 {
1387 $username_string = $username_colour;
1388 break;
1389 }
1390
1391 // no break;
1392
1393 case 'username':
1394
1395 // Build correct username
1396 if ($guest_username === false)
1397 {
1398 $username = ($username) ? $username : $user->lang['GUEST'];
1399 }
1400 else
1401 {
1402 $username = ($user_id && $user_id != ANONYMOUS) ? $username : ((!empty($guest_username)) ? $guest_username : $user->lang['GUEST']);
1403 }
1404
1405 // Return username
1406 if ($mode == 'username')
1407 {
1408 $username_string = $username;
1409 break;
1410 }
1411
1412 // no break;
1413
1414 case 'profile':
1415
1416 // Build correct profile url - only show if not anonymous and permission to view profile if registered user
1417 // For anonymous the link leads to a login page.
1418 if ($user_id && $user_id != ANONYMOUS && ($user->data['user_id'] == ANONYMOUS || $auth->acl_get('u_viewprofile')))
1419 {
1420 $profile_url = ($custom_profile_url !== false) ? $custom_profile_url . '&u=' . (int) $user_id : str_replace(array('={USER_ID}', '=%7BUSER_ID%7D'), '=' . (int) $user_id, $_profile_cache['base_url']);
1421 }
1422 else
1423 {
1424 $profile_url = '';
1425 }
1426
1427 // Return profile
1428 if ($mode == 'profile')
1429 {
1430 $username_string = $profile_url;
1431 break;
1432 }
1433
1434 // no break;
1435 }
1436
1437 if (!isset($username_string))
1438 {
1439 if (($mode == 'full' && !$profile_url) || $mode == 'no_profile')
1440 {
1441 $username_string = str_replace(array('{USERNAME_COLOUR}', '{USERNAME}'), array($username_colour, $username), (!$username_colour) ? $_profile_cache['tpl_noprofile'] : $_profile_cache['tpl_noprofile_colour']);
1442 }
1443 else
1444 {
1445 $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']);
1446 }
1447 }
1448
1449 /**
1450 * Use this event to change the output of get_username_string()
1451 *
1452 * @event core.modify_username_string
1453 * @var string mode profile|username|colour|full|no_profile
1454 * @var int user_id String or array of additional url
1455 * parameters
1456 * @var string username The user's username
1457 * @var string username_colour The user's colour
1458 * @var string guest_username Optional parameter to specify the
1459 * guest username.
1460 * @var string custom_profile_url Optional parameter to specify a
1461 * profile url.
1462 * @var string username_string The string that has been generated
1463 * @var array _profile_cache Array of original return templates
1464 * @since 3.1.0-a1
1465 */
1466 $vars = array(
1467 'mode',
1468 'user_id',
1469 'username',
1470 'username_colour',
1471 'guest_username',
1472 'custom_profile_url',
1473 'username_string',
1474 '_profile_cache',
1475 );
1476 extract($phpbb_dispatcher->trigger_event('core.modify_username_string', compact($vars)));
1477
1478 return $username_string;
1479 }
1480
1481 /**
1482 * Add an option to the quick-mod tools.
1483 *
1484 * @param string $url The recepting URL for the quickmod actions.
1485 * @param string $option The language key for the value of the option.
1486 * @param string $lang_string The language string to use.
1487 */
1488 function phpbb_add_quickmod_option($url, $option, $lang_string)
1489 {
1490 global $template, $user, $phpbb_path_helper;
1491
1492 $lang_string = $user->lang($lang_string);
1493 $template->assign_block_vars('quickmod', array(
1494 'VALUE' => $option,
1495 'TITLE' => $lang_string,
1496 'LINK' => $phpbb_path_helper->append_url_params($url, array('action' => $option)),
1497 ));
1498 }
1499
1500 /**
1501 * Concatenate an array into a string list.
1502 *
1503 * @param array $items Array of items to concatenate
1504 * @param object $user The phpBB $user object.
1505 *
1506 * @return string String list. Examples: "A"; "A and B"; "A, B, and C"
1507 */
1508 function phpbb_generate_string_list($items, $user)
1509 {
1510 if (empty($items))
1511 {
1512 return '';
1513 }
1514
1515 $count = sizeof($items);
1516 $last_item = array_pop($items);
1517 $lang_key = 'STRING_LIST_MULTI';
1518
1519 if ($count == 1)
1520 {
1521 return $last_item;
1522 }
1523 else if ($count == 2)
1524 {
1525 $lang_key = 'STRING_LIST_SIMPLE';
1526 }
1527 $list = implode($user->lang['COMMA_SEPARATOR'], $items);
1528
1529 return $user->lang($lang_key, $list, $last_item);
1530 }
1531
1532 class bitfield
1533 {
1534 var $data;
1535
1536 function bitfield($bitfield = '')
1537 {
1538 $this->data = base64_decode($bitfield);
1539 }
1540
1541 /**
1542 */
1543 function get($n)
1544 {
1545 // Get the ($n / 8)th char
1546 $byte = $n >> 3;
1547
1548 if (strlen($this->data) >= $byte + 1)
1549 {
1550 $c = $this->data[$byte];
1551
1552 // Lookup the ($n % 8)th bit of the byte
1553 $bit = 7 - ($n & 7);
1554 return (bool) (ord($c) & (1 << $bit));
1555 }
1556 else
1557 {
1558 return false;
1559 }
1560 }
1561
1562 function set($n)
1563 {
1564 $byte = $n >> 3;
1565 $bit = 7 - ($n & 7);
1566
1567 if (strlen($this->data) >= $byte + 1)
1568 {
1569 $this->data[$byte] = $this->data[$byte] | chr(1 << $bit);
1570 }
1571 else
1572 {
1573 $this->data .= str_repeat("\0", $byte - strlen($this->data));
1574 $this->data .= chr(1 << $bit);
1575 }
1576 }
1577
1578 function clear($n)
1579 {
1580 $byte = $n >> 3;
1581
1582 if (strlen($this->data) >= $byte + 1)
1583 {
1584 $bit = 7 - ($n & 7);
1585 $this->data[$byte] = $this->data[$byte] &~ chr(1 << $bit);
1586 }
1587 }
1588
1589 function get_blob()
1590 {
1591 return $this->data;
1592 }
1593
1594 function get_base64()
1595 {
1596 return base64_encode($this->data);
1597 }
1598
1599 function get_bin()
1600 {
1601 $bin = '';
1602 $len = strlen($this->data);
1603
1604 for ($i = 0; $i < $len; ++$i)
1605 {
1606 $bin .= str_pad(decbin(ord($this->data[$i])), 8, '0', STR_PAD_LEFT);
1607 }
1608
1609 return $bin;
1610 }
1611
1612 function get_all_set()
1613 {
1614 return array_keys(array_filter(str_split($this->get_bin())));
1615 }
1616
1617 function merge($bitfield)
1618 {
1619 $this->data = $this->data | $bitfield->get_blob();
1620 }
1621 }
1622