[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * 4 * @package phpBB3 5 * @version $Id$ 6 * @copyright (c) 2005 phpBB Group 7 * @license http://opensource.org/licenses/gpl-license.php GNU Public License 8 * 9 */ 10 11 /** 12 * @ignore 13 */ 14 if (!defined('IN_PHPBB')) 15 { 16 exit; 17 } 18 19 /** 20 * gen_sort_selects() 21 * make_jumpbox() 22 * bump_topic_allowed() 23 * get_context() 24 * decode_message() 25 * strip_bbcode() 26 * generate_text_for_display() 27 * generate_text_for_storage() 28 * generate_text_for_edit() 29 * make_clickable_callback() 30 * make_clickable() 31 * censor_text() 32 * bbcode_nl2br() 33 * smiley_text() 34 * parse_attachments() 35 * extension_allowed() 36 * truncate_string() 37 * get_username_string() 38 * class bitfield 39 */ 40 41 /** 42 * Generate sort selection fields 43 */ 44 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) 45 { 46 global $user; 47 48 $sort_dir_text = array('a' => $user->lang['ASCENDING'], 'd' => $user->lang['DESCENDING']); 49 50 $sorts = array( 51 'st' => array( 52 'key' => 'sort_days', 53 'default' => $def_st, 54 'options' => $limit_days, 55 'output' => &$s_limit_days, 56 ), 57 58 'sk' => array( 59 'key' => 'sort_key', 60 'default' => $def_sk, 61 'options' => $sort_by_text, 62 'output' => &$s_sort_key, 63 ), 64 65 'sd' => array( 66 'key' => 'sort_dir', 67 'default' => $def_sd, 68 'options' => $sort_dir_text, 69 'output' => &$s_sort_dir, 70 ), 71 ); 72 $u_sort_param = ''; 73 74 foreach ($sorts as $name => $sort_ary) 75 { 76 $key = $sort_ary['key']; 77 $selected = $$sort_ary['key']; 78 79 // Check if the key is selectable. If not, we reset to the default or first key found. 80 // This ensures the values are always valid. We also set $sort_dir/sort_key/etc. to the 81 // correct value, else the protection is void. ;) 82 if (!isset($sort_ary['options'][$selected])) 83 { 84 if ($sort_ary['default'] !== false) 85 { 86 $selected = $$key = $sort_ary['default']; 87 } 88 else 89 { 90 @reset($sort_ary['options']); 91 $selected = $$key = key($sort_ary['options']); 92 } 93 } 94 95 $sort_ary['output'] = '<select name="' . $name . '" id="' . $name . '">'; 96 foreach ($sort_ary['options'] as $option => $text) 97 { 98 $sort_ary['output'] .= '<option value="' . $option . '"' . (($selected == $option) ? ' selected="selected"' : '') . '>' . $text . '</option>'; 99 } 100 $sort_ary['output'] .= '</select>'; 101 102 $u_sort_param .= ($selected !== $sort_ary['default']) ? ((strlen($u_sort_param)) ? '&' : '') . "{$name}={$selected}" : ''; 103 } 104 105 return; 106 } 107 108 /** 109 * Generate Jumpbox 110 */ 111 function make_jumpbox($action, $forum_id = false, $select_all = false, $acl_list = false, $force_display = false) 112 { 113 global $config, $auth, $template, $user, $db; 114 115 // We only return if the jumpbox is not forced to be displayed (in case it is needed for functionality) 116 if (!$config['load_jumpbox'] && $force_display === false) 117 { 118 return; 119 } 120 121 $sql = 'SELECT forum_id, forum_name, parent_id, forum_type, left_id, right_id 122 FROM ' . FORUMS_TABLE . ' 123 ORDER BY left_id ASC'; 124 $result = $db->sql_query($sql, 600); 125 126 $right = $padding = 0; 127 $padding_store = array('0' => 0); 128 $display_jumpbox = false; 129 $iteration = 0; 130 131 // Sometimes it could happen that forums will be displayed here not be displayed within the index page 132 // This is the result of forums not displayed at index, having list permissions and a parent of a forum with no permissions. 133 // If this happens, the padding could be "broken" 134 135 while ($row = $db->sql_fetchrow($result)) 136 { 137 if ($row['left_id'] < $right) 138 { 139 $padding++; 140 $padding_store[$row['parent_id']] = $padding; 141 } 142 else if ($row['left_id'] > $right + 1) 143 { 144 // Ok, if the $padding_store for this parent is empty there is something wrong. For now we will skip over it. 145 // @todo digging deep to find out "how" this can happen. 146 $padding = (isset($padding_store[$row['parent_id']])) ? $padding_store[$row['parent_id']] : $padding; 147 } 148 149 $right = $row['right_id']; 150 151 if ($row['forum_type'] == FORUM_CAT && ($row['left_id'] + 1 == $row['right_id'])) 152 { 153 // Non-postable forum with no subforums, don't display 154 continue; 155 } 156 157 if (!$auth->acl_get('f_list', $row['forum_id'])) 158 { 159 // if the user does not have permissions to list this forum skip 160 continue; 161 } 162 163 if ($acl_list && !$auth->acl_gets($acl_list, $row['forum_id'])) 164 { 165 continue; 166 } 167 168 if (!$display_jumpbox) 169 { 170 $template->assign_block_vars('jumpbox_forums', array( 171 'FORUM_ID' => ($select_all) ? 0 : -1, 172 'FORUM_NAME' => ($select_all) ? $user->lang['ALL_FORUMS'] : $user->lang['SELECT_FORUM'], 173 'S_FORUM_COUNT' => $iteration) 174 ); 175 176 $iteration++; 177 $display_jumpbox = true; 178 } 179 180 $template->assign_block_vars('jumpbox_forums', array( 181 'FORUM_ID' => $row['forum_id'], 182 'FORUM_NAME' => $row['forum_name'], 183 'SELECTED' => ($row['forum_id'] == $forum_id) ? ' selected="selected"' : '', 184 'S_FORUM_COUNT' => $iteration, 185 'S_IS_CAT' => ($row['forum_type'] == FORUM_CAT) ? true : false, 186 'S_IS_LINK' => ($row['forum_type'] == FORUM_LINK) ? true : false, 187 'S_IS_POST' => ($row['forum_type'] == FORUM_POST) ? true : false) 188 ); 189 190 for ($i = 0; $i < $padding; $i++) 191 { 192 $template->assign_block_vars('jumpbox_forums.level', array()); 193 } 194 $iteration++; 195 } 196 $db->sql_freeresult($result); 197 unset($padding_store); 198 199 $template->assign_vars(array( 200 'S_DISPLAY_JUMPBOX' => $display_jumpbox, 201 'S_JUMPBOX_ACTION' => $action) 202 ); 203 204 return; 205 } 206 207 /** 208 * Bump Topic Check - used by posting and viewtopic 209 */ 210 function bump_topic_allowed($forum_id, $topic_bumped, $last_post_time, $topic_poster, $last_topic_poster) 211 { 212 global $config, $auth, $user; 213 214 // Check permission and make sure the last post was not already bumped 215 if (!$auth->acl_get('f_bump', $forum_id) || $topic_bumped) 216 { 217 return false; 218 } 219 220 // Check bump time range, is the user really allowed to bump the topic at this time? 221 $bump_time = ($config['bump_type'] == 'm') ? $config['bump_interval'] * 60 : (($config['bump_type'] == 'h') ? $config['bump_interval'] * 3600 : $config['bump_interval'] * 86400); 222 223 // Check bump time 224 if ($last_post_time + $bump_time > time()) 225 { 226 return false; 227 } 228 229 // Check bumper, only topic poster and last poster are allowed to bump 230 if ($topic_poster != $user->data['user_id'] && $last_topic_poster != $user->data['user_id']) 231 { 232 return false; 233 } 234 235 // A bump time of 0 will completely disable the bump feature... not intended but might be useful. 236 return $bump_time; 237 } 238 239 /** 240 * Generates a text with approx. the specified length which contains the specified words and their context 241 * 242 * @param string $text The full text from which context shall be extracted 243 * @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!) 244 * @param int $length The desired length of the resulting text, however the result might be shorter or longer than this value 245 * 246 * @return string Context of the specified words separated by "..." 247 */ 248 function get_context($text, $words, $length = 400) 249 { 250 // first replace all whitespaces with single spaces 251 $text = preg_replace('/ +/', ' ', strtr($text, "\t\n\r\x0C ", ' ')); 252 253 // we need to turn the entities back into their original form, to not cut the message in between them 254 $entities = array('<', '>', '[', ']', '.', ':', ':'); 255 $characters = array('<', '>', '[', ']', '.', ':', ':'); 256 $text = str_replace($entities, $characters, $text); 257 258 $word_indizes = array(); 259 if (sizeof($words)) 260 { 261 $match = ''; 262 // find the starting indizes of all words 263 foreach ($words as $word) 264 { 265 if ($word) 266 { 267 if (preg_match('#(?:[^\w]|^)(' . $word . ')(?:[^\w]|$)#i', $text, $match)) 268 { 269 if (empty($match[1])) 270 { 271 continue; 272 } 273 274 $pos = utf8_strpos($text, $match[1]); 275 if ($pos !== false) 276 { 277 $word_indizes[] = $pos; 278 } 279 } 280 } 281 } 282 unset($match); 283 284 if (sizeof($word_indizes)) 285 { 286 $word_indizes = array_unique($word_indizes); 287 sort($word_indizes); 288 289 $wordnum = sizeof($word_indizes); 290 // number of characters on the right and left side of each word 291 $sequence_length = (int) ($length / (2 * $wordnum)) - 2; 292 $final_text = ''; 293 $word = $j = 0; 294 $final_text_index = -1; 295 296 // cycle through every character in the original text 297 for ($i = $word_indizes[$word], $n = utf8_strlen($text); $i < $n; $i++) 298 { 299 // if the current position is the start of one of the words then append $sequence_length characters to the final text 300 if (isset($word_indizes[$word]) && ($i == $word_indizes[$word])) 301 { 302 if ($final_text_index < $i - $sequence_length - 1) 303 { 304 $final_text .= '... ' . preg_replace('#^([^ ]*)#', '', utf8_substr($text, $i - $sequence_length, $sequence_length)); 305 } 306 else 307 { 308 // if the final text is already nearer to the current word than $sequence_length we only append the text 309 // from its current index on and distribute the unused length to all other sequenes 310 $sequence_length += (int) (($final_text_index - $i + $sequence_length + 1) / (2 * $wordnum)); 311 $final_text .= utf8_substr($text, $final_text_index + 1, $i - $final_text_index - 1); 312 } 313 $final_text_index = $i - 1; 314 315 // add the following characters to the final text (see below) 316 $word++; 317 $j = 1; 318 } 319 320 if ($j > 0) 321 { 322 // add the character to the final text and increment the sequence counter 323 $final_text .= utf8_substr($text, $i, 1); 324 $final_text_index++; 325 $j++; 326 327 // if this is a whitespace then check whether we are done with this sequence 328 if (utf8_substr($text, $i, 1) == ' ') 329 { 330 // only check whether we have to exit the context generation completely if we haven't already reached the end anyway 331 if ($i + 4 < $n) 332 { 333 if (($j > $sequence_length && $word >= $wordnum) || utf8_strlen($final_text) > $length) 334 { 335 $final_text .= ' ...'; 336 break; 337 } 338 } 339 else 340 { 341 // make sure the text really reaches the end 342 $j -= 4; 343 } 344 345 // stop context generation and wait for the next word 346 if ($j > $sequence_length) 347 { 348 $j = 0; 349 } 350 } 351 } 352 } 353 return str_replace($characters, $entities, $final_text); 354 } 355 } 356 357 if (!sizeof($words) || !sizeof($word_indizes)) 358 { 359 return str_replace($characters, $entities, ((utf8_strlen($text) >= $length + 3) ? utf8_substr($text, 0, $length) . '...' : $text)); 360 } 361 } 362 363 /** 364 * Decode text whereby text is coming from the db and expected to be pre-parsed content 365 * We are placing this outside of the message parser because we are often in need of it... 366 */ 367 function decode_message(&$message, $bbcode_uid = '') 368 { 369 global $config; 370 371 if ($bbcode_uid) 372 { 373 $match = array('<br />', "[/*:m:$bbcode_uid]", ":u:$bbcode_uid", ":o:$bbcode_uid", ":$bbcode_uid"); 374 $replace = array("\n", '', '', '', ''); 375 } 376 else 377 { 378 $match = array('<br />'); 379 $replace = array("\n"); 380 } 381 382 $message = str_replace($match, $replace, $message); 383 384 $match = get_preg_expression('bbcode_htm'); 385 $replace = array('\1', '\1', '\2', '\1', '', ''); 386 387 $message = preg_replace($match, $replace, $message); 388 } 389 390 /** 391 * Strips all bbcode from a text and returns the plain content 392 */ 393 function strip_bbcode(&$text, $uid = '') 394 { 395 if (!$uid) 396 { 397 $uid = '[0-9a-z]{5,}'; 398 } 399 400 $text = preg_replace("#\[\/?[a-z0-9\*\+\-]+(?:=(?:".*"|[^\]]*))?(?::[a-z])?(\:$uid)\]#", ' ', $text); 401 402 $match = get_preg_expression('bbcode_htm'); 403 $replace = array('\1', '\1', '\2', '\1', '', ''); 404 405 $text = preg_replace($match, $replace, $text); 406 } 407 408 /** 409 * For display of custom parsed text on user-facing pages 410 * Expects $text to be the value directly from the database (stored value) 411 */ 412 function generate_text_for_display($text, $uid, $bitfield, $flags) 413 { 414 static $bbcode; 415 416 if (!$text) 417 { 418 return ''; 419 } 420 421 $text = censor_text($text); 422 423 // Parse bbcode if bbcode uid stored and bbcode enabled 424 if ($uid && ($flags & OPTION_FLAG_BBCODE)) 425 { 426 if (!class_exists('bbcode')) 427 { 428 global $phpbb_root_path, $phpEx; 429 include($phpbb_root_path . 'includes/bbcode.' . $phpEx); 430 } 431 432 if (empty($bbcode)) 433 { 434 $bbcode = new bbcode($bitfield); 435 } 436 else 437 { 438 $bbcode->bbcode($bitfield); 439 } 440 441 $bbcode->bbcode_second_pass($text, $uid); 442 } 443 444 $text = bbcode_nl2br($text); 445 $text = smiley_text($text, !($flags & OPTION_FLAG_SMILIES)); 446 447 return $text; 448 } 449 450 /** 451 * For parsing custom parsed text to be stored within the database. 452 * This function additionally returns the uid and bitfield that needs to be stored. 453 * Expects $text to be the value directly from request_var() and in it's non-parsed form 454 */ 455 function generate_text_for_storage(&$text, &$uid, &$bitfield, &$flags, $allow_bbcode = false, $allow_urls = false, $allow_smilies = false) 456 { 457 global $phpbb_root_path, $phpEx; 458 459 $uid = $bitfield = ''; 460 $flags = (($allow_bbcode) ? OPTION_FLAG_BBCODE : 0) + (($allow_smilies) ? OPTION_FLAG_SMILIES : 0) + (($allow_urls) ? OPTION_FLAG_LINKS : 0); 461 462 if (!$text) 463 { 464 return; 465 } 466 467 if (!class_exists('parse_message')) 468 { 469 include($phpbb_root_path . 'includes/message_parser.' . $phpEx); 470 } 471 472 $message_parser = new parse_message($text); 473 $message_parser->parse($allow_bbcode, $allow_urls, $allow_smilies); 474 475 $text = $message_parser->message; 476 $uid = $message_parser->bbcode_uid; 477 478 // If the bbcode_bitfield is empty, there is no need for the uid to be stored. 479 if (!$message_parser->bbcode_bitfield) 480 { 481 $uid = ''; 482 } 483 484 $bitfield = $message_parser->bbcode_bitfield; 485 486 return; 487 } 488 489 /** 490 * For decoding custom parsed text for edits as well as extracting the flags 491 * Expects $text to be the value directly from the database (pre-parsed content) 492 */ 493 function generate_text_for_edit($text, $uid, $flags) 494 { 495 global $phpbb_root_path, $phpEx; 496 497 decode_message($text, $uid); 498 499 return array( 500 'allow_bbcode' => ($flags & OPTION_FLAG_BBCODE) ? 1 : 0, 501 'allow_smilies' => ($flags & OPTION_FLAG_SMILIES) ? 1 : 0, 502 'allow_urls' => ($flags & OPTION_FLAG_LINKS) ? 1 : 0, 503 'text' => $text 504 ); 505 } 506 507 /** 508 * A subroutine of make_clickable used with preg_replace 509 * It places correct HTML around an url, shortens the displayed text 510 * and makes sure no entities are inside URLs 511 */ 512 function make_clickable_callback($type, $whitespace, $url, $relative_url, $class) 513 { 514 $orig_url = $url; 515 $orig_relative = $relative_url; 516 $append = ''; 517 $url = htmlspecialchars_decode($url); 518 $relative_url = htmlspecialchars_decode($relative_url); 519 520 // make sure no HTML entities were matched 521 $chars = array('<', '>', '"'); 522 $split = false; 523 524 foreach ($chars as $char) 525 { 526 $next_split = strpos($url, $char); 527 if ($next_split !== false) 528 { 529 $split = ($split !== false) ? min($split, $next_split) : $next_split; 530 } 531 } 532 533 if ($split !== false) 534 { 535 // an HTML entity was found, so the URL has to end before it 536 $append = substr($url, $split) . $relative_url; 537 $url = substr($url, 0, $split); 538 $relative_url = ''; 539 } 540 else if ($relative_url) 541 { 542 // same for $relative_url 543 $split = false; 544 foreach ($chars as $char) 545 { 546 $next_split = strpos($relative_url, $char); 547 if ($next_split !== false) 548 { 549 $split = ($split !== false) ? min($split, $next_split) : $next_split; 550 } 551 } 552 553 if ($split !== false) 554 { 555 $append = substr($relative_url, $split); 556 $relative_url = substr($relative_url, 0, $split); 557 } 558 } 559 560 // if the last character of the url is a punctuation mark, exclude it from the url 561 $last_char = ($relative_url) ? $relative_url[strlen($relative_url) - 1] : $url[strlen($url) - 1]; 562 563 switch ($last_char) 564 { 565 case '.': 566 case '?': 567 case '!': 568 case ':': 569 case ',': 570 $append = $last_char; 571 if ($relative_url) 572 { 573 $relative_url = substr($relative_url, 0, -1); 574 } 575 else 576 { 577 $url = substr($url, 0, -1); 578 } 579 break; 580 581 // set last_char to empty here, so the variable can be used later to 582 // check whether a character was removed 583 default: 584 $last_char = ''; 585 break; 586 } 587 588 $short_url = (strlen($url) > 55) ? substr($url, 0, 39) . ' ... ' . substr($url, -10) : $url; 589 590 switch ($type) 591 { 592 case MAGIC_URL_LOCAL: 593 $tag = 'l'; 594 $relative_url = preg_replace('/[&?]sid=[0-9a-f]{32}$/', '', preg_replace('/([&?])sid=[0-9a-f]{32}&/', '$1', $relative_url)); 595 $url = $url . '/' . $relative_url; 596 $text = $relative_url; 597 598 // this url goes to http://domain.tld/path/to/board/ which 599 // would result in an empty link if treated as local so 600 // don't touch it and let MAGIC_URL_FULL take care of it. 601 if (!$relative_url) 602 { 603 return $whitespace . $orig_url . '/' . $orig_relative; // slash is taken away by relative url pattern 604 } 605 break; 606 607 case MAGIC_URL_FULL: 608 $tag = 'm'; 609 $text = $short_url; 610 break; 611 612 case MAGIC_URL_WWW: 613 $tag = 'w'; 614 $url = 'http://' . $url; 615 $text = $short_url; 616 break; 617 618 case MAGIC_URL_EMAIL: 619 $tag = 'e'; 620 $text = $short_url; 621 $url = 'mailto:' . $url; 622 break; 623 } 624 625 $url = htmlspecialchars($url); 626 $text = htmlspecialchars($text); 627 $append = htmlspecialchars($append); 628 629 $html = "$whitespace<!-- $tag --><a$class href=\"$url\">$text</a><!-- $tag -->$append"; 630 631 return $html; 632 } 633 634 /** 635 * make_clickable function 636 * 637 * Replace magic urls of form http://xxx.xxx., www.xxx. and xxx@xxx.xxx. 638 * Cuts down displayed size of link if over 50 chars, turns absolute links 639 * into relative versions when the server/script path matches the link 640 */ 641 function make_clickable($text, $server_url = false, $class = 'postlink') 642 { 643 if ($server_url === false) 644 { 645 $server_url = generate_board_url(); 646 } 647 648 static $magic_url_match; 649 static $magic_url_replace; 650 static $static_class; 651 652 if (!is_array($magic_url_match) || $static_class != $class) 653 { 654 $static_class = $class; 655 $class = ($static_class) ? ' class="' . $static_class . '"' : ''; 656 $local_class = ($static_class) ? ' class="' . $static_class . '-local"' : ''; 657 658 $magic_url_match = $magic_url_replace = array(); 659 // Be sure to not let the matches cross over. ;) 660 661 // relative urls for this board 662 $magic_url_match[] = '#(^|[\n\t (>.])(' . preg_quote($server_url, '#') . ')/(' . get_preg_expression('relative_url_inline') . ')#ie'; 663 $magic_url_replace[] = "make_clickable_callback(MAGIC_URL_LOCAL, '\$1', '\$2', '\$3', '$local_class')"; 664 665 // matches a xxxx://aaaaa.bbb.cccc. ... 666 $magic_url_match[] = '#(^|[\n\t (>.])(' . get_preg_expression('url_inline') . ')#ie'; 667 $magic_url_replace[] = "make_clickable_callback(MAGIC_URL_FULL, '\$1', '\$2', '', '$class')"; 668 669 // matches a "www.xxxx.yyyy[/zzzz]" kinda lazy URL thing 670 $magic_url_match[] = '#(^|[\n\t (>])(' . get_preg_expression('www_url_inline') . ')#ie'; 671 $magic_url_replace[] = "make_clickable_callback(MAGIC_URL_WWW, '\$1', '\$2', '', '$class')"; 672 673 // matches an email@domain type address at the start of a line, or after a space or after what might be a BBCode. 674 $magic_url_match[] = '/(^|[\n\t (>])(' . get_preg_expression('email') . ')/ie'; 675 $magic_url_replace[] = "make_clickable_callback(MAGIC_URL_EMAIL, '\$1', '\$2', '', '')"; 676 } 677 678 return preg_replace($magic_url_match, $magic_url_replace, $text); 679 } 680 681 /** 682 * Censoring 683 */ 684 function censor_text($text) 685 { 686 static $censors; 687 688 // Nothing to do? 689 if ($text === '') 690 { 691 return ''; 692 } 693 694 // We moved the word censor checks in here because we call this function quite often - and then only need to do the check once 695 if (!isset($censors) || !is_array($censors)) 696 { 697 global $config, $user, $auth, $cache; 698 699 // We check here if the user is having viewing censors disabled (and also allowed to do so). 700 if (!$user->optionget('viewcensors') && $config['allow_nocensors'] && $auth->acl_get('u_chgcensors')) 701 { 702 $censors = array(); 703 } 704 else 705 { 706 $censors = $cache->obtain_word_list(); 707 } 708 } 709 710 if (sizeof($censors)) 711 { 712 return preg_replace($censors['match'], $censors['replace'], $text); 713 } 714 715 return $text; 716 } 717 718 /** 719 * custom version of nl2br which takes custom BBCodes into account 720 */ 721 function bbcode_nl2br($text) 722 { 723 // custom BBCodes might contain carriage returns so they 724 // are not converted into <br /> so now revert that 725 $text = str_replace(array("\n", "\r"), array('<br />', "\n"), $text); 726 return $text; 727 } 728 729 /** 730 * Smiley processing 731 */ 732 function smiley_text($text, $force_option = false) 733 { 734 global $config, $user, $phpbb_root_path; 735 736 if ($force_option || !$config['allow_smilies'] || !$user->optionget('viewsmilies')) 737 { 738 return preg_replace('#<!\-\- s(.*?) \-\-><img src="\{SMILIES_PATH\}\/.*? \/><!\-\- s\1 \-\->#', '\1', $text); 739 } 740 else 741 { 742 $root_path = (defined('PHPBB_USE_BOARD_URL_PATH') && PHPBB_USE_BOARD_URL_PATH) ? generate_board_url() . '/' : $phpbb_root_path; 743 return preg_replace('#<!\-\- s(.*?) \-\-><img src="\{SMILIES_PATH\}\/(.*?) \/><!\-\- s\1 \-\->#', '<img src="' . $root_path . $config['smilies_path'] . '/\2 />', $text); 744 } 745 } 746 747 /** 748 * General attachment parsing 749 * 750 * @param mixed $forum_id The forum id the attachments are displayed in (false if in private message) 751 * @param string &$message The post/private message 752 * @param array &$attachments The attachments to parse for (inline) display. The attachments array will hold templated data after parsing. 753 * @param array &$update_count The attachment counts to be updated - will be filled 754 * @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. 755 */ 756 function parse_attachments($forum_id, &$message, &$attachments, &$update_count, $preview = false) 757 { 758 if (!sizeof($attachments)) 759 { 760 return; 761 } 762 763 global $template, $cache, $user; 764 global $extensions, $config, $phpbb_root_path, $phpEx; 765 766 // 767 $compiled_attachments = array(); 768 769 if (!isset($template->filename['attachment_tpl'])) 770 { 771 $template->set_filenames(array( 772 'attachment_tpl' => 'attachment.html') 773 ); 774 } 775 776 if (empty($extensions) || !is_array($extensions)) 777 { 778 $extensions = $cache->obtain_attach_extensions($forum_id); 779 } 780 781 // Look for missing attachment information... 782 $attach_ids = array(); 783 foreach ($attachments as $pos => $attachment) 784 { 785 // If is_orphan is set, we need to retrieve the attachments again... 786 if (!isset($attachment['extension']) && !isset($attachment['physical_filename'])) 787 { 788 $attach_ids[(int) $attachment['attach_id']] = $pos; 789 } 790 } 791 792 // Grab attachments (security precaution) 793 if (sizeof($attach_ids)) 794 { 795 global $db; 796 797 $new_attachment_data = array(); 798 799 $sql = 'SELECT * 800 FROM ' . ATTACHMENTS_TABLE . ' 801 WHERE ' . $db->sql_in_set('attach_id', array_keys($attach_ids)); 802 $result = $db->sql_query($sql); 803 804 while ($row = $db->sql_fetchrow($result)) 805 { 806 if (!isset($attach_ids[$row['attach_id']])) 807 { 808 continue; 809 } 810 811 // If we preview attachments we will set some retrieved values here 812 if ($preview) 813 { 814 $row['attach_comment'] = $attachments[$attach_ids[$row['attach_id']]]['attach_comment']; 815 } 816 817 $new_attachment_data[$attach_ids[$row['attach_id']]] = $row; 818 } 819 $db->sql_freeresult($result); 820 821 $attachments = $new_attachment_data; 822 unset($new_attachment_data); 823 } 824 825 // Sort correctly 826 if ($config['display_order']) 827 { 828 // Ascending sort 829 krsort($attachments); 830 } 831 else 832 { 833 // Descending sort 834 ksort($attachments); 835 } 836 837 foreach ($attachments as $attachment) 838 { 839 if (!sizeof($attachment)) 840 { 841 continue; 842 } 843 844 // We need to reset/empty the _file block var, because this function might be called more than once 845 $template->destroy_block_vars('_file'); 846 847 $block_array = array(); 848 849 // Some basics... 850 $attachment['extension'] = strtolower(trim($attachment['extension'])); 851 $filename = $phpbb_root_path . $config['upload_path'] . '/' . utf8_basename($attachment['physical_filename']); 852 $thumbnail_filename = $phpbb_root_path . $config['upload_path'] . '/thumb_' . utf8_basename($attachment['physical_filename']); 853 854 $upload_icon = ''; 855 856 if (isset($extensions[$attachment['extension']])) 857 { 858 if ($user->img('icon_topic_attach', '') && !$extensions[$attachment['extension']]['upload_icon']) 859 { 860 $upload_icon = $user->img('icon_topic_attach', ''); 861 } 862 else if ($extensions[$attachment['extension']]['upload_icon']) 863 { 864 $upload_icon = '<img src="' . $phpbb_root_path . $config['upload_icons_path'] . '/' . trim($extensions[$attachment['extension']]['upload_icon']) . '" alt="" />'; 865 } 866 } 867 868 $filesize = get_formatted_filesize($attachment['filesize'], false); 869 870 $comment = bbcode_nl2br(censor_text($attachment['attach_comment'])); 871 872 $block_array += array( 873 'UPLOAD_ICON' => $upload_icon, 874 'FILESIZE' => $filesize['value'], 875 'SIZE_LANG' => $filesize['unit'], 876 'DOWNLOAD_NAME' => utf8_basename($attachment['real_filename']), 877 'COMMENT' => $comment, 878 ); 879 880 $denied = false; 881 882 if (!extension_allowed($forum_id, $attachment['extension'], $extensions)) 883 { 884 $denied = true; 885 886 $block_array += array( 887 'S_DENIED' => true, 888 'DENIED_MESSAGE' => sprintf($user->lang['EXTENSION_DISABLED_AFTER_POSTING'], $attachment['extension']) 889 ); 890 } 891 892 if (!$denied) 893 { 894 $l_downloaded_viewed = $download_link = ''; 895 $display_cat = $extensions[$attachment['extension']]['display_cat']; 896 897 if ($display_cat == ATTACHMENT_CATEGORY_IMAGE) 898 { 899 if ($attachment['thumbnail']) 900 { 901 $display_cat = ATTACHMENT_CATEGORY_THUMB; 902 } 903 else 904 { 905 if ($config['img_display_inlined']) 906 { 907 if ($config['img_link_width'] || $config['img_link_height']) 908 { 909 $dimension = @getimagesize($filename); 910 911 // If the dimensions could not be determined or the image being 0x0 we display it as a link for safety purposes 912 if ($dimension === false || empty($dimension[0]) || empty($dimension[1])) 913 { 914 $display_cat = ATTACHMENT_CATEGORY_NONE; 915 } 916 else 917 { 918 $display_cat = ($dimension[0] <= $config['img_link_width'] && $dimension[1] <= $config['img_link_height']) ? ATTACHMENT_CATEGORY_IMAGE : ATTACHMENT_CATEGORY_NONE; 919 } 920 } 921 } 922 else 923 { 924 $display_cat = ATTACHMENT_CATEGORY_NONE; 925 } 926 } 927 } 928 929 // Make some descisions based on user options being set. 930 if (($display_cat == ATTACHMENT_CATEGORY_IMAGE || $display_cat == ATTACHMENT_CATEGORY_THUMB) && !$user->optionget('viewimg')) 931 { 932 $display_cat = ATTACHMENT_CATEGORY_NONE; 933 } 934 935 if ($display_cat == ATTACHMENT_CATEGORY_FLASH && !$user->optionget('viewflash')) 936 { 937 $display_cat = ATTACHMENT_CATEGORY_NONE; 938 } 939 940 $download_link = append_sid("{$phpbb_root_path}download/file.$phpEx", 'id=' . $attachment['attach_id']); 941 942 switch ($display_cat) 943 { 944 // Images 945 case ATTACHMENT_CATEGORY_IMAGE: 946 $l_downloaded_viewed = 'VIEWED_COUNT'; 947 $inline_link = append_sid("{$phpbb_root_path}download/file.$phpEx", 'id=' . $attachment['attach_id']); 948 $download_link .= '&mode=view'; 949 950 $block_array += array( 951 'S_IMAGE' => true, 952 'U_INLINE_LINK' => $inline_link, 953 ); 954 955 $update_count[] = $attachment['attach_id']; 956 break; 957 958 // Images, but display Thumbnail 959 case ATTACHMENT_CATEGORY_THUMB: 960 $l_downloaded_viewed = 'VIEWED_COUNT'; 961 $thumbnail_link = append_sid("{$phpbb_root_path}download/file.$phpEx", 'id=' . $attachment['attach_id'] . '&t=1'); 962 $download_link .= '&mode=view'; 963 964 $block_array += array( 965 'S_THUMBNAIL' => true, 966 'THUMB_IMAGE' => $thumbnail_link, 967 ); 968 969 $update_count[] = $attachment['attach_id']; 970 break; 971 972 // Windows Media Streams 973 case ATTACHMENT_CATEGORY_WM: 974 $l_downloaded_viewed = 'VIEWED_COUNT'; 975 976 // Giving the filename directly because within the wm object all variables are in local context making it impossible 977 // to validate against a valid session (all params can differ) 978 // $download_link = $filename; 979 980 $block_array += array( 981 'U_FORUM' => generate_board_url(), 982 'ATTACH_ID' => $attachment['attach_id'], 983 'S_WM_FILE' => true, 984 ); 985 986 // Viewed/Heared File ... update the download count 987 $update_count[] = $attachment['attach_id']; 988 break; 989 990 // Real Media Streams 991 case ATTACHMENT_CATEGORY_RM: 992 case ATTACHMENT_CATEGORY_QUICKTIME: 993 $l_downloaded_viewed = 'VIEWED_COUNT'; 994 995 $block_array += array( 996 'S_RM_FILE' => ($display_cat == ATTACHMENT_CATEGORY_RM) ? true : false, 997 'S_QUICKTIME_FILE' => ($display_cat == ATTACHMENT_CATEGORY_QUICKTIME) ? true : false, 998 'U_FORUM' => generate_board_url(), 999 'ATTACH_ID' => $attachment['attach_id'], 1000 ); 1001 1002 // Viewed/Heared File ... update the download count 1003 $update_count[] = $attachment['attach_id']; 1004 break; 1005 1006 // Macromedia Flash Files 1007 case ATTACHMENT_CATEGORY_FLASH: 1008 list($width, $height) = @getimagesize($filename); 1009 1010 $l_downloaded_viewed = 'VIEWED_COUNT'; 1011 1012 $block_array += array( 1013 'S_FLASH_FILE' => true, 1014 'WIDTH' => $width, 1015 'HEIGHT' => $height, 1016 'U_VIEW_LINK' => $download_link . '&view=1', 1017 ); 1018 1019 // Viewed/Heared File ... update the download count 1020 $update_count[] = $attachment['attach_id']; 1021 break; 1022 1023 default: 1024 $l_downloaded_viewed = 'DOWNLOAD_COUNT'; 1025 1026 $block_array += array( 1027 'S_FILE' => true, 1028 ); 1029 break; 1030 } 1031 1032 $l_download_count = (!isset($attachment['download_count']) || $attachment['download_count'] == 0) ? $user->lang[$l_downloaded_viewed . '_NONE'] : (($attachment['download_count'] == 1) ? sprintf($user->lang[$l_downloaded_viewed], $attachment['download_count']) : sprintf($user->lang[$l_downloaded_viewed . 'S'], $attachment['download_count'])); 1033 1034 $block_array += array( 1035 'U_DOWNLOAD_LINK' => $download_link, 1036 'L_DOWNLOAD_COUNT' => $l_download_count 1037 ); 1038 } 1039 1040 $template->assign_block_vars('_file', $block_array); 1041 1042 $compiled_attachments[] = $template->assign_display('attachment_tpl'); 1043 } 1044 1045 $attachments = $compiled_attachments; 1046 unset($compiled_attachments); 1047 1048 $tpl_size = sizeof($attachments); 1049 1050 $unset_tpl = array(); 1051 1052 preg_match_all('#<!\-\- ia([0-9]+) \-\->(.*?)<!\-\- ia\1 \-\->#', $message, $matches, PREG_PATTERN_ORDER); 1053 1054 $replace = array(); 1055 foreach ($matches[0] as $num => $capture) 1056 { 1057 // Flip index if we are displaying the reverse way 1058 $index = ($config['display_order']) ? ($tpl_size-($matches[1][$num] + 1)) : $matches[1][$num]; 1059 1060 $replace['from'][] = $matches[0][$num]; 1061 $replace['to'][] = (isset($attachments[$index])) ? $attachments[$index] : sprintf($user->lang['MISSING_INLINE_ATTACHMENT'], $matches[2][array_search($index, $matches[1])]); 1062 1063 $unset_tpl[] = $index; 1064 } 1065 1066 if (isset($replace['from'])) 1067 { 1068 $message = str_replace($replace['from'], $replace['to'], $message); 1069 } 1070 1071 $unset_tpl = array_unique($unset_tpl); 1072 1073 // Needed to let not display the inlined attachments at the end of the post again 1074 foreach ($unset_tpl as $index) 1075 { 1076 unset($attachments[$index]); 1077 } 1078 } 1079 1080 /** 1081 * Check if extension is allowed to be posted. 1082 * 1083 * @param mixed $forum_id The forum id to check or false if private message 1084 * @param string $extension The extension to check, for example zip. 1085 * @param array &$extensions The extension array holding the information from the cache (will be obtained if empty) 1086 * 1087 * @return bool False if the extension is not allowed to be posted, else true. 1088 */ 1089 function extension_allowed($forum_id, $extension, &$extensions) 1090 { 1091 if (empty($extensions)) 1092 { 1093 global $cache; 1094 $extensions = $cache->obtain_attach_extensions($forum_id); 1095 } 1096 1097 return (!isset($extensions['_allowed_'][$extension])) ? false : true; 1098 } 1099 1100 /** 1101 * Truncates string while retaining special characters if going over the max length 1102 * The default max length is 60 at the moment 1103 * The maximum storage length is there to fit the string within the given length. The string may be further truncated due to html entities. 1104 * For example: string given is 'a "quote"' (length: 9), would be a stored as 'a "quote"' (length: 19) 1105 * 1106 * @param string $string The text to truncate to the given length. String is specialchared. 1107 * @param int $max_length Maximum length of string (multibyte character count as 1 char / Html entity count as 1 char) 1108 * @param int $max_store_length Maximum character length of string (multibyte character count as 1 char / Html entity count as entity chars). 1109 * @param bool $allow_reply Allow Re: in front of string 1110 * NOTE: This parameter can cause undesired behavior (returning strings longer than $max_store_length) and is deprecated. 1111 * @param string $append String to be appended 1112 */ 1113 function truncate_string($string, $max_length = 60, $max_store_length = 255, $allow_reply = false, $append = '') 1114 { 1115 $chars = array(); 1116 1117 $strip_reply = false; 1118 $stripped = false; 1119 if ($allow_reply && strpos($string, 'Re: ') === 0) 1120 { 1121 $strip_reply = true; 1122 $string = substr($string, 4); 1123 } 1124 1125 $_chars = utf8_str_split(htmlspecialchars_decode($string)); 1126 $chars = array_map('utf8_htmlspecialchars', $_chars); 1127 1128 // Now check the length ;) 1129 if (sizeof($chars) > $max_length) 1130 { 1131 // Cut off the last elements from the array 1132 $string = implode('', array_slice($chars, 0, $max_length - utf8_strlen($append))); 1133 $stripped = true; 1134 } 1135 1136 // Due to specialchars, we may not be able to store the string... 1137 if (utf8_strlen($string) > $max_store_length) 1138 { 1139 // let's split again, we do not want half-baked strings where entities are split 1140 $_chars = utf8_str_split(htmlspecialchars_decode($string)); 1141 $chars = array_map('utf8_htmlspecialchars', $_chars); 1142 1143 do 1144 { 1145 array_pop($chars); 1146 $string = implode('', $chars); 1147 } 1148 while (!empty($chars) && utf8_strlen($string) > $max_store_length); 1149 } 1150 1151 if ($strip_reply) 1152 { 1153 $string = 'Re: ' . $string; 1154 } 1155 1156 if ($append != '' && $stripped) 1157 { 1158 $string = $string . $append; 1159 } 1160 1161 return $string; 1162 } 1163 1164 /** 1165 * Get username details for placing into templates. 1166 * This function caches all modes on first call, except for no_profile and anonymous user - determined by $user_id. 1167 * 1168 * @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) 1169 * @param int $user_id The users id 1170 * @param string $username The users name 1171 * @param string $username_colour The users colour 1172 * @param string $guest_username optional parameter to specify the guest username. It will be used in favor of the GUEST language variable then. 1173 * @param string $custom_profile_url optional parameter to specify a profile url. The user id get appended to this url as &u={user_id} 1174 * 1175 * @return string A string consisting of what is wanted based on $mode. 1176 * @author BartVB, Acyd Burn 1177 */ 1178 function get_username_string($mode, $user_id, $username, $username_colour = '', $guest_username = false, $custom_profile_url = false) 1179 { 1180 static $_profile_cache; 1181 1182 // We cache some common variables we need within this function 1183 if (empty($_profile_cache)) 1184 { 1185 global $phpbb_root_path, $phpEx; 1186 1187 $_profile_cache['base_url'] = append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=viewprofile&u={USER_ID}'); 1188 $_profile_cache['tpl_noprofile'] = '{USERNAME}'; 1189 $_profile_cache['tpl_noprofile_colour'] = '<span style="color: {USERNAME_COLOUR};" class="username-coloured">{USERNAME}</span>'; 1190 $_profile_cache['tpl_profile'] = '<a href="{PROFILE_URL}">{USERNAME}</a>'; 1191 $_profile_cache['tpl_profile_colour'] = '<a href="{PROFILE_URL}" style="color: {USERNAME_COLOUR};" class="username-coloured">{USERNAME}</a>'; 1192 } 1193 1194 global $user, $auth; 1195 1196 // This switch makes sure we only run code required for the mode 1197 switch ($mode) 1198 { 1199 case 'full': 1200 case 'no_profile': 1201 case 'colour': 1202 1203 // Build correct username colour 1204 $username_colour = ($username_colour) ? '#' . $username_colour : ''; 1205 1206 // Return colour 1207 if ($mode == 'colour') 1208 { 1209 return $username_colour; 1210 } 1211 1212 // no break; 1213 1214 case 'username': 1215 1216 // Build correct username 1217 if ($guest_username === false) 1218 { 1219 $username = ($username) ? $username : $user->lang['GUEST']; 1220 } 1221 else 1222 { 1223 $username = ($user_id && $user_id != ANONYMOUS) ? $username : ((!empty($guest_username)) ? $guest_username : $user->lang['GUEST']); 1224 } 1225 1226 // Return username 1227 if ($mode == 'username') 1228 { 1229 return $username; 1230 } 1231 1232 // no break; 1233 1234 case 'profile': 1235 1236 // Build correct profile url - only show if not anonymous and permission to view profile if registered user 1237 // For anonymous the link leads to a login page. 1238 if ($user_id && $user_id != ANONYMOUS && ($user->data['user_id'] == ANONYMOUS || $auth->acl_get('u_viewprofile'))) 1239 { 1240 $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']); 1241 } 1242 else 1243 { 1244 $profile_url = ''; 1245 } 1246 1247 // Return profile 1248 if ($mode == 'profile') 1249 { 1250 return $profile_url; 1251 } 1252 1253 // no break; 1254 } 1255 1256 if (($mode == 'full' && !$profile_url) || $mode == 'no_profile') 1257 { 1258 return str_replace(array('{USERNAME_COLOUR}', '{USERNAME}'), array($username_colour, $username), (!$username_colour) ? $_profile_cache['tpl_noprofile'] : $_profile_cache['tpl_noprofile_colour']); 1259 } 1260 1261 return 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']); 1262 } 1263 1264 /** 1265 * @package phpBB3 1266 */ 1267 class bitfield 1268 { 1269 var $data; 1270 1271 function bitfield($bitfield = '') 1272 { 1273 $this->data = base64_decode($bitfield); 1274 } 1275 1276 /** 1277 */ 1278 function get($n) 1279 { 1280 // Get the ($n / 8)th char 1281 $byte = $n >> 3; 1282 1283 if (strlen($this->data) >= $byte + 1) 1284 { 1285 $c = $this->data[$byte]; 1286 1287 // Lookup the ($n % 8)th bit of the byte 1288 $bit = 7 - ($n & 7); 1289 return (bool) (ord($c) & (1 << $bit)); 1290 } 1291 else 1292 { 1293 return false; 1294 } 1295 } 1296 1297 function set($n) 1298 { 1299 $byte = $n >> 3; 1300 $bit = 7 - ($n & 7); 1301 1302 if (strlen($this->data) >= $byte + 1) 1303 { 1304 $this->data[$byte] = $this->data[$byte] | chr(1 << $bit); 1305 } 1306 else 1307 { 1308 $this->data .= str_repeat("\0", $byte - strlen($this->data)); 1309 $this->data .= chr(1 << $bit); 1310 } 1311 } 1312 1313 function clear($n) 1314 { 1315 $byte = $n >> 3; 1316 1317 if (strlen($this->data) >= $byte + 1) 1318 { 1319 $bit = 7 - ($n & 7); 1320 $this->data[$byte] = $this->data[$byte] &~ chr(1 << $bit); 1321 } 1322 } 1323 1324 function get_blob() 1325 { 1326 return $this->data; 1327 } 1328 1329 function get_base64() 1330 { 1331 return base64_encode($this->data); 1332 } 1333 1334 function get_bin() 1335 { 1336 $bin = ''; 1337 $len = strlen($this->data); 1338 1339 for ($i = 0; $i < $len; ++$i) 1340 { 1341 $bin .= str_pad(decbin(ord($this->data[$i])), 8, '0', STR_PAD_LEFT); 1342 } 1343 1344 return $bin; 1345 } 1346 1347 function get_all_set() 1348 { 1349 return array_keys(array_filter(str_split($this->get_bin()))); 1350 } 1351 1352 function merge($bitfield) 1353 { 1354 $this->data = $this->data | $bitfield->get_blob(); 1355 } 1356 } 1357 1358 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Oct 2 15:03:47 2013 | Cross-referenced by PHPXref 0.7.1 |