999 lines
32 KiB
PHP
999 lines
32 KiB
PHP
|
<?php
|
||
|
|
||
|
/* Copyright (C) 2009 Winch Gate Property Limited
|
||
|
*
|
||
|
* This file is part of ryzom_api.
|
||
|
* ryzom_api is free software: you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||
|
* the Free Software Foundation, either version 3 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* ryzom_api is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU Lesser General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU Lesser General Public License
|
||
|
* along with ryzom_api. If not, see <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
// setup bbCode formatter
|
||
|
|
||
|
bbCode::$ig = RYZOM_IG;
|
||
|
|
||
|
/**
|
||
|
* Image proxy
|
||
|
*/
|
||
|
if(!defined('IMG_PROXY')){
|
||
|
$url = 'http://'.$_SERVER['HTTP_HOST'].'/app_forum/tools/imageproxy.php';
|
||
|
define('IMG_PROXY', $url);
|
||
|
}
|
||
|
if (!function_exists('proxy_image_url')) {
|
||
|
function proxy_image_url($href, $attrs=''){
|
||
|
return IMG_PROXY.'?'.($attrs != '' ? $attrs.'&' : '').'url='.urlencode($href);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
abstract class bbCodeParser {
|
||
|
|
||
|
/**
|
||
|
* @var bool
|
||
|
*/
|
||
|
private $_ig;
|
||
|
|
||
|
/**
|
||
|
* @var array
|
||
|
*/
|
||
|
private $tags_ignore;
|
||
|
private $tags_block_open;
|
||
|
private $tags_block_close;
|
||
|
private $tags_ignore_depth;
|
||
|
|
||
|
/**
|
||
|
* @var array
|
||
|
*/
|
||
|
private $open_tags;
|
||
|
|
||
|
/**
|
||
|
* @var string
|
||
|
*/
|
||
|
private $last_closed_tag;
|
||
|
|
||
|
/**
|
||
|
* @var int
|
||
|
*/
|
||
|
private $current_tag;
|
||
|
|
||
|
/**
|
||
|
* @var array
|
||
|
*/
|
||
|
private $state;
|
||
|
|
||
|
/**
|
||
|
* @param bool $ig if true, use ingame markup
|
||
|
*/
|
||
|
function __construct($ig) {
|
||
|
$this->_ig = $ig;
|
||
|
|
||
|
// ignore bbcode between these tags
|
||
|
$this->tags_ignore = array(
|
||
|
'noparse', 'code',
|
||
|
'url', 'img', 'mail', 'page', 'forum', 'topic', 'post', 'wiki', 'time', 'date'
|
||
|
);
|
||
|
|
||
|
// these create block level html code, so '\n' or ' ' or '\t' around them needs to be cleared
|
||
|
$this->tags_block_open = array('h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'quote', 'list', 'p');
|
||
|
$this->tags_block_close = $this->tags_block_open;
|
||
|
if ($this->_ig) {
|
||
|
// ingame <p> is not block level when closing, so dont strip there
|
||
|
$key = array_search('p', $this->tags_block_close, true);
|
||
|
unset($this->tags_block_close[$key]);
|
||
|
}
|
||
|
|
||
|
$this->state = array();
|
||
|
|
||
|
// reset internals
|
||
|
$this->reset();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Format bbcode tag
|
||
|
*
|
||
|
* @param string $tag tag name
|
||
|
* @param string $open open markup
|
||
|
* @param string $close close markup
|
||
|
* @param string $attr tag attributes
|
||
|
* @param string $text text between tags
|
||
|
*/
|
||
|
abstract function format($tag, $open, $close, $attr, $text);
|
||
|
|
||
|
/**
|
||
|
* Wrapper to call Child->format(...)
|
||
|
*
|
||
|
* @param array $tag assoc array with tag info
|
||
|
* @return string
|
||
|
*/
|
||
|
function handle_tag($tag) {
|
||
|
return $this->format($tag['tag'], $tag['open'], $tag['close'], $tag['attr'], $tag['text']);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Reset internals
|
||
|
*/
|
||
|
function reset() {
|
||
|
$this->current_tag = 0;
|
||
|
$this->tags_ignore_depth = 0;
|
||
|
|
||
|
// 0'th position is used as result
|
||
|
$this->open_tags = array(
|
||
|
0 => array('tag' => '', 'open' => '', 'close' => '', 'attr' => '', 'text' => '')
|
||
|
);
|
||
|
|
||
|
$this->last_closed_tag = false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Save working state
|
||
|
*/
|
||
|
private function _state_save() {
|
||
|
$this->state[] = array($this->current_tag, $this->tags_ignore_depth, $this->open_tags, $this->last_closed_tag);
|
||
|
$this->reset();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Restore working state
|
||
|
*/
|
||
|
private function _state_restore() {
|
||
|
if (!empty($this->state)) {
|
||
|
list($this->current_tag, $this->tags_ignore_depth, $this->open_tags, $this->last_closed_tag) = array_pop($this->state);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Main worker. Parse $text for bbCode tags
|
||
|
*
|
||
|
* NOTE: Text must already be safe for HTML, ie. treated with htmlspecialchars()
|
||
|
*
|
||
|
* @param string $text
|
||
|
* @return string formatted string
|
||
|
*/
|
||
|
function bbcode($text) {
|
||
|
$text = str_replace("\r\n", "\n", $text);
|
||
|
|
||
|
$split = preg_split('/(\[[a-zA-Z0-9_\/]*?(?:[= ].*?)?\])/', $text, null, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
|
||
|
|
||
|
foreach ($split as $chunk) {
|
||
|
if (substr($chunk, 0, 1) == '[' && substr($chunk, -1, 1) == ']') {
|
||
|
if (substr($chunk, 0, 2) == '[/') {
|
||
|
$this->close($chunk);
|
||
|
} else {
|
||
|
$this->open($chunk);
|
||
|
}
|
||
|
} else {
|
||
|
$this->text($chunk);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $this->result();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Push tag with args to stack
|
||
|
* Do not strip whitespace because tag might be invalid
|
||
|
*
|
||
|
* @param string $chunk full tag string, eg. [tag=attr]
|
||
|
*/
|
||
|
function open($chunk) {
|
||
|
list($tag, $attr) = $this->split_params($chunk);
|
||
|
|
||
|
// test for [noparse]
|
||
|
if ($this->tags_ignore_depth > 0) {
|
||
|
$this->text($chunk);
|
||
|
} else {
|
||
|
$this->current_tag++;
|
||
|
// remember tag, attributes and complete string that was used in markup
|
||
|
$this->open_tags[$this->current_tag] = array('tag' => $tag, 'attr' => $attr, 'open' => $chunk, 'close' => '', 'text' => '');
|
||
|
}
|
||
|
|
||
|
if (in_array($tag, $this->tags_ignore)) {
|
||
|
$this->tags_ignore_depth++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Close tag and call tag handler to format output
|
||
|
*
|
||
|
* @param $chunk full tag string, eg. [/tag]
|
||
|
*/
|
||
|
function close($chunk) {
|
||
|
// extract tag name from [/name]
|
||
|
$tag = strtolower(substr($chunk, 2, -1));
|
||
|
|
||
|
if ($this->tags_ignore_depth > 0 && in_array($tag, $this->tags_ignore)) {
|
||
|
$this->tags_ignore_depth--;
|
||
|
}
|
||
|
|
||
|
// stack underrun
|
||
|
if ($this->current_tag < 0) {
|
||
|
$this->text($chunk);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// ignore block
|
||
|
if ($this->tags_ignore_depth > 0) {
|
||
|
$this->text($chunk);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// tag mismatch
|
||
|
if ($this->open_tags[$this->current_tag]['tag'] !== $tag) {
|
||
|
// try to find first open tag for this
|
||
|
$key = false;
|
||
|
for ($i = $this->current_tag - 1; $i > 0; $i--) {
|
||
|
if (isset($this->open_tags[$i]['tag']) && $this->open_tags[$i]['tag'] === $tag) {
|
||
|
$key = $i;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if ($key === false) {
|
||
|
$this->text($chunk);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// tag is open so we need to 'rewind' a bit
|
||
|
for ($i = $this->current_tag; $i > $key; $i--) {
|
||
|
$tmp_tag = $this->pop_stack();
|
||
|
$this->text($tmp_tag['open'] . $tmp_tag['text']);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// close tag
|
||
|
$open = $this->pop_stack();
|
||
|
|
||
|
// handle bbcode
|
||
|
$open['close'] = $chunk;
|
||
|
|
||
|
$block_level = false;
|
||
|
if (in_array($tag, $this->tags_block_open)) {
|
||
|
$block_level = true;
|
||
|
// for block level element, trim whitespace from inside tag
|
||
|
// [tag]<ws>...text...<ws>[/tag]
|
||
|
$open['text'] = $this->trim_ws($open['text']);
|
||
|
}
|
||
|
$result = $this->handle_tag($open);
|
||
|
|
||
|
// strip whitespace from text before tag 'text...<ws>[tag]'
|
||
|
if ($block_level) {
|
||
|
$ts = $this->rtrim_ws($this->open_tags[$this->current_tag]['text']);
|
||
|
$this->open_tags[$this->current_tag]['text'] = $ts;
|
||
|
}
|
||
|
|
||
|
$this->text($result);
|
||
|
|
||
|
$this->last_closed_tag = $open['tag'];
|
||
|
}
|
||
|
|
||
|
function text($text) {
|
||
|
// strip whitespace after closing '[/tag]<ws>...text'
|
||
|
if (in_array($this->last_closed_tag, $this->tags_block_close)) {
|
||
|
$text = $this->ltrim_ws($text);
|
||
|
}
|
||
|
$this->open_tags[$this->current_tag]['text'] .= $text;
|
||
|
|
||
|
$this->last_closed_tag = false;
|
||
|
}
|
||
|
|
||
|
function result() {
|
||
|
// close tags that are still open
|
||
|
while ($this->current_tag > 0) {
|
||
|
$open = $this->pop_stack();
|
||
|
|
||
|
if ($this->tags_ignore_depth > 0) {
|
||
|
$this->tags_ignore_depth--;
|
||
|
// need to reparse text that's after ignore tag
|
||
|
$this->_state_save();
|
||
|
$text = $open['open'] . $this->bbcode($open['text']);
|
||
|
$this->_state_restore();
|
||
|
} else {
|
||
|
// tag was not closed proprely, include start tag with result
|
||
|
$text = $open['open'] . $open['text'];
|
||
|
}
|
||
|
|
||
|
$this->text($text);
|
||
|
};
|
||
|
|
||
|
return $this->open_tags[0]['text'];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Pop tag and text from stack and return them
|
||
|
*
|
||
|
* @return array [0] = tag, [1] = text
|
||
|
*/
|
||
|
function pop_stack() {
|
||
|
// remove from stack
|
||
|
$open = $this->open_tags[$this->current_tag];
|
||
|
unset($this->open_tags[$this->current_tag]);
|
||
|
$this->current_tag--;
|
||
|
|
||
|
return $open;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Trim from end of string
|
||
|
* 'text...\s{0,}\n{1}\s{0,}'
|
||
|
*
|
||
|
* @param string $ts
|
||
|
* @return string
|
||
|
*/
|
||
|
function rtrim_ws($ts){
|
||
|
// we want to get rid of all spaces/tabs, but only single \n, so rtrim($ts, " \t\n\r") would not work
|
||
|
$ts = rtrim($ts, " \t");
|
||
|
if (substr($ts, -1, 1) === "\n") {
|
||
|
$ts = substr($ts, 0, -1);
|
||
|
$ts = rtrim($ts, " \t");
|
||
|
}
|
||
|
return $ts;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Trim from start of string
|
||
|
* '\s{0,}\n{1}...text'
|
||
|
*
|
||
|
* @param string $ts
|
||
|
* @return string
|
||
|
*/
|
||
|
function ltrim_ws($ts){
|
||
|
// we want to get rid of all spaces/tabs, but only single \n, so ltrim($ts, " \t\n\r") would not work
|
||
|
$ts = ltrim($ts, " \t");
|
||
|
if (substr($ts, 0, 1) === "\n") {
|
||
|
$ts = substr($ts, 1);
|
||
|
}
|
||
|
return $ts;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Trim from both sides
|
||
|
* '\s{0,}\n{1}...text...\s{0,}\n{1}\s{0,}
|
||
|
*
|
||
|
* @param string $ts
|
||
|
* @return string
|
||
|
*/
|
||
|
function trim_ws($ts){
|
||
|
$ts = $this->ltrim_ws($ts);
|
||
|
$ts = $this->rtrim_ws($ts);
|
||
|
return $ts;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Extract tag parameters from [tag=params] or [tag key1=val1 key2=val2]
|
||
|
*
|
||
|
* @param type $tag
|
||
|
* @return type
|
||
|
*/
|
||
|
function split_params($chunk) {
|
||
|
if (substr($chunk, 0, 1) == '[') {
|
||
|
$b = '\[';
|
||
|
$e = '\]';
|
||
|
} else {
|
||
|
$b = '';
|
||
|
$e = '';
|
||
|
}
|
||
|
// [1] [2] [3]
|
||
|
if (preg_match('/^' . $b . '([\*a-zA-Z0-9]*?)' . '(=| )' . '(.*?)' . $e . '$/', $chunk, $match)) {
|
||
|
$tagName = strtolower($match[1]);
|
||
|
if ($match[2] == '=') {
|
||
|
// = means single parameter
|
||
|
$tagParam = $match[3];
|
||
|
} else {
|
||
|
// <space> means multiple parameters
|
||
|
$tagParam = array();
|
||
|
$args = preg_split('/[ ]/', $match[3], null, PREG_SPLIT_NO_EMPTY);
|
||
|
foreach ($args as $arg) {
|
||
|
$pairs = explode('=', $arg);
|
||
|
// preg_replace will remove possible quotes around value
|
||
|
if (isset($pairs[1])) {
|
||
|
$tagParam[strtolower($pairs[0])] = preg_replace('@("|\'|)(.*?)\\1@', '$2', $pairs[1]);
|
||
|
} else {
|
||
|
$tagParam[] = preg_replace('@("|\'|)(.*?)\\1@', '$2', $pairs[0]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
if (substr($chunk, 0, 1) == '[' && substr($chunk, -1, 1) == ']') {
|
||
|
$chunk = substr($chunk, 1, -1);
|
||
|
}
|
||
|
$tagName = strtolower($chunk);
|
||
|
$tagParam = '';
|
||
|
}
|
||
|
return array($tagName, $tagParam);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
class bbCode extends bbCodeParser {
|
||
|
static $legacy_sync = 1348956841;
|
||
|
static $legacy_shard = array(
|
||
|
'ani' => 2363920179,
|
||
|
'lea' => 2437578274,
|
||
|
'ari' => 2358620001,
|
||
|
);
|
||
|
|
||
|
static $ig = false;
|
||
|
static $timezone = 'UTC';
|
||
|
static $clock12h = false;
|
||
|
static $shardid = false;
|
||
|
static $lang = 'en';
|
||
|
static $disabledTags = array();
|
||
|
//
|
||
|
const COLOR_P = '#d0d0d0'; // normal text
|
||
|
//
|
||
|
const COLOR_BBCODE_TAG = '#444444';
|
||
|
|
||
|
static function bbDisabled($tag) {
|
||
|
return in_array(strtolower($tag), self::$disabledTags);
|
||
|
}
|
||
|
|
||
|
static function getFontSize($value) {
|
||
|
$size = 16;
|
||
|
switch (strtolower($value)) {
|
||
|
case '1': case 'xx-small': $size = 9; break;
|
||
|
case '2': case 'x-small' : $size = 10; break;
|
||
|
case '3': case 'small' : $size = 13; break;
|
||
|
case '4': case 'medium' : $size = 16; break;
|
||
|
case '5': case 'large' : $size = 18; break;
|
||
|
case '6': case 'x-large' : $size = 24; break;
|
||
|
case '7': case 'xx-large': $size = 32; break;
|
||
|
//case '8': case 'smaller' : break;
|
||
|
//case '9': case 'larger' : break;
|
||
|
}
|
||
|
return $size;
|
||
|
}
|
||
|
|
||
|
static function bb_noparse($code) {
|
||
|
return preg_replace(array('/\[/', '/\]/'), array('[', ']'), $code);
|
||
|
}
|
||
|
|
||
|
static function bb_code($code) {
|
||
|
return '<pre>' . self::bb_noparse($code) . '</pre>';
|
||
|
}
|
||
|
|
||
|
static function bb_list($list) {
|
||
|
$result = '';
|
||
|
$list = str_replace("\n[", '[', $list);
|
||
|
$result = '<ul>' . preg_replace('/\s*\[\*\]\s*/is', "</li><li>", $list) . '</li></ul>';
|
||
|
return preg_replace('#<ul>\s*</li>#is', '<ul>', $result);
|
||
|
}
|
||
|
|
||
|
static function bb_quote($author, $text) {
|
||
|
if (self::$ig) {
|
||
|
// prevents [color] tag to take over color
|
||
|
$author = self::bb_color(self::COLOR_P, $author);
|
||
|
$text = self::bb_color(self::COLOR_P, $text);
|
||
|
// left/right border, top/bottom border height
|
||
|
$l = '<td width="1" bgcolor="#888888" height="1"></td>';
|
||
|
$r = '<td width="1" bgcolor="#888888" height="1"></td>';
|
||
|
return // 98% gives bit padding on the right
|
||
|
'<table width="98%" cellpadding="0" cellspacing="0" border="0">' .
|
||
|
'<tr><td width="1" height="5"></td><td></td><td></td></tr>' . // top padding - no border
|
||
|
'<tr>' . $l . '<td bgcolor="#888888"></td>' . $r . '</tr>' . // top border
|
||
|
'<tr>' . $l . '<td bgcolor="#000000" l_margin="5" height="3"></td>' . $r . '</tr>' . // author top padding
|
||
|
'<tr>' . $l . '<td bgcolor="#000000" l_margin="5">' . $author . '</td>' . $r . '</tr>' . // author
|
||
|
'<tr>' . $l . '<td bgcolor="#000000" l_margin="5" height="2"></td>' . $r . '</tr>' . // author bottom padding
|
||
|
'<tr>' . $l . '<td bgcolor="#555555" l_margin="10" height="3"></td>' . $r . '</tr>' . // quote top padding
|
||
|
'<tr>' . $l . '<td bgcolor="#555555" l_margin="10">' . $text . '</td>' . $r . '</tr>' . // quote
|
||
|
'<tr>' . $l . '<td bgcolor="#555555" l_margin="10" height="2"></td>' . $r . '</tr>' . // quote bottom padding
|
||
|
'<tr>' . $l . '<td bgcolor="#888888"></td>' . $r . '</tr>' . // bottom border
|
||
|
'<tr><td width="1" height="8"></td><td></td><td></td></tr>' . // bottom padding - no border
|
||
|
'</table>';
|
||
|
} else {
|
||
|
return '' .
|
||
|
'<div class="post-quote">' .
|
||
|
'<cite>' . $author . '</cite>' .
|
||
|
'<blockquote>' . $text . '</blockquote>' .
|
||
|
'</div>';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static function bb_h($nr, $color, $text) {
|
||
|
$tag = 'h' . $nr;
|
||
|
|
||
|
if (self::$ig) {
|
||
|
if ($color != '') {
|
||
|
$text = '<font color="' . $color . '">' . $text . '</font>';
|
||
|
}
|
||
|
return '<' . $tag . '>' . $text . '</' . $tag . '>';
|
||
|
} else {
|
||
|
if ($color != '') {
|
||
|
$style = ' style="color: ' . $color . ';"';
|
||
|
} else {
|
||
|
$style = '';
|
||
|
}
|
||
|
return '<' . $tag . $style . '>' . $text . '</' . $tag . '>';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static function bb_url($href, $text) {
|
||
|
// "http://..../" remove " if present
|
||
|
if (substr($href, 0, 6) == '"') {
|
||
|
if (substr($href, -6) == '"') {
|
||
|
$href = substr($href, 6, -6);
|
||
|
} else {
|
||
|
$href = substr($href, 6);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($href == '')
|
||
|
$href = $text;
|
||
|
if ($text == '') {
|
||
|
$text = $href;
|
||
|
$text = wordwrap($text, 65, ' ', true);
|
||
|
}
|
||
|
|
||
|
$disable = self::bbDisabled('url');
|
||
|
// if not disabled and in ryzom and is proper url (<scheme>://<host>/)
|
||
|
if (!$disable && self::$ig) {
|
||
|
$url = @parse_url(strtolower($href));
|
||
|
$disable = true;
|
||
|
if (!empty($url['scheme']) && !empty($url['host'])) {
|
||
|
if (in_array($url['scheme'], array('http', 'https'))) {
|
||
|
if (in_array($url['host'], array('app.ryzom.com'))) {
|
||
|
if (empty($url['query']) || stripos($url['query'], 'redirect') === false) {
|
||
|
// http://atys.ryzom.com/
|
||
|
// and does not contain redirect
|
||
|
// - allow url in game browser
|
||
|
$disable = false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} // !empty
|
||
|
}// isRYZOM
|
||
|
|
||
|
if ($disable) {
|
||
|
// empty href will give proper link color without 'underline' - perfect for 'disabled' links
|
||
|
if ($href == '') {
|
||
|
$text = '<a href="">' . $text . '</a>';
|
||
|
} else {
|
||
|
$href = wordwrap($href, 65, ' ', true);
|
||
|
$text = wordwrap($text, 65, ' ', true);
|
||
|
$text = '<a href="">' . $text . '</a> ' . self::bb_color(self::COLOR_BBCODE_TAG, '(' . $href . ')');
|
||
|
}
|
||
|
return $text;
|
||
|
}
|
||
|
|
||
|
// make sure http:// (or ftp:// or mailto:// etc is present), if not, add it
|
||
|
if (!preg_match('#://#', $href)) {
|
||
|
$href = 'http://' . $href;
|
||
|
}
|
||
|
|
||
|
return sprintf('<a href="%s"' . (self::$ig ? '' : ' target="_blank"') . '>%s</a>', $href, $text);
|
||
|
}
|
||
|
|
||
|
static function bb_img($attr, $href) {
|
||
|
if (self::bbDisabled('img')) {
|
||
|
return self::bb_noparse('[img]' . $href . '[/img]');
|
||
|
}
|
||
|
// $href is treated with htmlspecialchars() so any & in url is &
|
||
|
$href = str_replace('&', '&', $href);
|
||
|
|
||
|
// images from current server directly
|
||
|
if ($attr=='src' || strstr($href, $_SERVER['HTTP_HOST']) !== false){
|
||
|
return '<img src="' . $href . '" />';
|
||
|
}
|
||
|
$url = proxy_image_url($href);
|
||
|
return '<a href="' . $url . '&no_proxy=1"><img src="' . $url . '" /></a>';
|
||
|
}
|
||
|
|
||
|
static function bb_banner($lang, $ckey) {
|
||
|
// $lang and $ckey should already be escaped for HTML, so urlencode() in here would double escape them
|
||
|
// - channel it thru image proxy. proxy does caching better and uses '304 Not Modified' status
|
||
|
$src = 'http://atys.ryzom.com/api/banner.php?ckey=' . $ckey . '&langid=' . $lang . '&size=500';
|
||
|
return self::bb_img('', $src);
|
||
|
}
|
||
|
|
||
|
static function bb_mail($user) {
|
||
|
$url = 'http://' . $_SERVER['HTTP_HOST'] . '/app_mail/?page=compose/to/' . urlencode($user);
|
||
|
return '<a href="' . $url . '">' . $user . '</a>';
|
||
|
}
|
||
|
|
||
|
static function bb_profile($ptype, $pname) {
|
||
|
// types from app_profile
|
||
|
$types = array('user', 'player', 'npc', 'fauna', 'entity', 'source');
|
||
|
$ptype = array_search($ptype, $types, true);
|
||
|
// if type not found, then fall back to player
|
||
|
if ($ptype === false)
|
||
|
$ptype = 1;
|
||
|
|
||
|
$url = 'http://' . $_SERVER['HTTP_HOST'] . '/app_profile/?ptype=' . intval($ptype) . '&pname=' . urlencode($pname);
|
||
|
return '<a href="' . $url . '">' . $pname . '</a>';
|
||
|
}
|
||
|
|
||
|
static function bb_color($color, $str) {
|
||
|
if ($color == '') {
|
||
|
return $str;
|
||
|
}
|
||
|
|
||
|
if (self::$ig) {
|
||
|
return '<font color="' . $color . '">' . $str . '</font>';
|
||
|
} else {
|
||
|
return '<span style="color: ' . $color . ';">' . $str . '</span>';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static function bb_size($size, $str) {
|
||
|
$size = self::getFontSize($size);
|
||
|
|
||
|
if (self::$ig) {
|
||
|
return '<font size="' . $size . 'px">' . $str . '</font>';
|
||
|
} else {
|
||
|
return '<span style="font-size: ' . $size . 'px;">' . $str . '</span>';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static function bb_pre($str) {
|
||
|
return '<pre>' . $str . '</pre>';
|
||
|
}
|
||
|
|
||
|
static function bb_p($str) {
|
||
|
return '<p>' . $str . '</p>';
|
||
|
}
|
||
|
|
||
|
// Added by ulukyn. WebIg compatibility.
|
||
|
static function bb_center($str) {
|
||
|
if (self::$ig) {
|
||
|
return '<table width="100%" cellpadding="0" cellspacing="0"><tr><td align="center" valign="middle">' . $str . '</td></tr></table>';
|
||
|
} else {
|
||
|
return '<div style="text-align: center;">' . $str . '</div>';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Table format : (added by ulukyn)
|
||
|
* A1| A2|A3
|
||
|
* B1| B2 |B3
|
||
|
* C1|C2 |C3
|
||
|
*/
|
||
|
static function bb_table($attr, $content) {
|
||
|
$width = isset($attr['width'])?$attr['width']:'100%';
|
||
|
$border = isset($attr['border'])?$attr['border']:'0';
|
||
|
$bgcolor = isset($attr['bgcolor'])?' bgcolor="'.$attr['bgcolor'].'" ':'';
|
||
|
$ret = '<table width="'.$width.'" border="'.$border.'" cellpadding="0" cellspacing="0" '.$bgcolor.' >';
|
||
|
$lines = explode("\n", $content);
|
||
|
foreach ($lines as $line) {
|
||
|
if ($line) {
|
||
|
$ret .= '<tr>';
|
||
|
$cols = explode('|', $line);
|
||
|
foreach ($cols as $text) {
|
||
|
if (!$text)
|
||
|
continue;
|
||
|
$params = array('valign' => 'middle');
|
||
|
if ($text[0] == '#') {
|
||
|
$paramsdef = explode(' ', $text);
|
||
|
$paramlist = substr(array_shift($paramsdef), 1);
|
||
|
$paramlist = explode(',', $paramlist);
|
||
|
foreach ($paramlist as $p) {
|
||
|
list($name, $value) = explode('=', $p);
|
||
|
$params[ _h(str_replace('"', '', $name))] = _h(str_replace('"', '', $value));
|
||
|
}
|
||
|
if ($paramsdef)
|
||
|
$text = implode(' ', $paramsdef);
|
||
|
}
|
||
|
$param_html = '';
|
||
|
foreach ($params as $name => $value)
|
||
|
$param_html .= $name.'="'.$value.'" ';
|
||
|
|
||
|
if ($text && $text[0] == ' ' && $text[strlen($text)-1] == ' ')
|
||
|
$align = 'center';
|
||
|
else if ($text && $text[0] == ' ')
|
||
|
$align = 'right';
|
||
|
else
|
||
|
$align = 'left';
|
||
|
|
||
|
$ret .= '<td '.$param_html.' align="'.$align.'">'.$text.'</td>';
|
||
|
}
|
||
|
$ret .= '</tr>';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$ret .= '</table>';
|
||
|
return $ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
static function bb_page_link($page, $txt) {
|
||
|
if ($page == '') {
|
||
|
$page = $txt;
|
||
|
}
|
||
|
$tmp = explode('/', $page);
|
||
|
foreach ($tmp as $k => $v) {
|
||
|
$tmp[$k] = urlencode($v);
|
||
|
}
|
||
|
$url = 'http://' . $_SERVER['HTTP_HOST'] . '/app_forum/?page=' . join('/', $tmp);
|
||
|
return '<a href="' . $url . '">' . $txt . '</a>';
|
||
|
}
|
||
|
|
||
|
static function bb_forum_link($page, $id, $txt) {
|
||
|
$page = $page . '/view/' . $id;
|
||
|
if ($id == '') {
|
||
|
$page.= $txt;
|
||
|
}
|
||
|
return self::bb_page_link($page, $txt);
|
||
|
}
|
||
|
|
||
|
// Added by Ulukyn
|
||
|
static function bb_wiki_link($page, $txt) {
|
||
|
$need_new_txt = false;
|
||
|
if ($page == '') {
|
||
|
$page = $txt;
|
||
|
$need_new_txt = true;
|
||
|
}
|
||
|
|
||
|
if (substr($page, 0, 22) == 'http://atys.ryzom.com/')
|
||
|
$url = 'http://atys.ryzom.com/start/app_wiki.php?page=' . substr($page, 21);
|
||
|
else {
|
||
|
$tmp = explode('/', $page);
|
||
|
if (count($tmp) != 2) {
|
||
|
return 'Syntax: [wiki]/[page], ex: en/Chronicles';
|
||
|
} else {
|
||
|
$wiki = $tmp[0];
|
||
|
$page = $tmp[1];
|
||
|
}
|
||
|
if (self::$ig) {
|
||
|
$url = 'http://atys.ryzom.com/start/app_wiki.php?page=/projects/pub' . $wiki . '/wiki/' . $page;
|
||
|
}
|
||
|
else
|
||
|
$url = 'http://atys.ryzom.com/projects/pub' . $wiki . '/wiki/' . $page;
|
||
|
if ($need_new_txt)
|
||
|
$txt = 'WIKI [' . $page . ']';
|
||
|
}
|
||
|
return '<a href="' . $url . '"' . (self::$ig ? '' : ' target="_blank"') . '>' . $txt . '</a>';
|
||
|
}
|
||
|
|
||
|
static function bb_biu($tag, $txt) {
|
||
|
$tag = strtolower($tag);
|
||
|
if (self::$ig) {
|
||
|
switch ($tag) {
|
||
|
// FIXME: darken/lighter or tint current color
|
||
|
case 'b': $txt = self::bb_color('white', $txt);
|
||
|
break;
|
||
|
case 'i': $txt = self::bb_color('#ffffd0', $txt);
|
||
|
break;
|
||
|
case 'u': $txt = '<a href="ah:">' . self::bb_color(self::COLOR_P, $txt) . '</a>';
|
||
|
break;
|
||
|
default : $txt = self::bb_color(self::COLOR_BBCODE_TAG, $txt);
|
||
|
break; // fallback
|
||
|
}
|
||
|
return $txt;
|
||
|
}
|
||
|
|
||
|
switch ($tag) {
|
||
|
case 'b': $tag = 'strong';
|
||
|
break;
|
||
|
case 'i': $tag = 'em';
|
||
|
break;
|
||
|
case 'u': $tag = 'u';
|
||
|
break;
|
||
|
default: $tag = 'span'; // fallback
|
||
|
}
|
||
|
return '<' . $tag . '>' . $txt . '</' . $tag . '>';
|
||
|
}
|
||
|
|
||
|
static function bb_date($attr, $txt) {
|
||
|
$time = strtotime($txt);
|
||
|
|
||
|
$shardid = isset($attr['shard']) ? $attr['shard'] : self::$shardid;
|
||
|
if ($time === false || $shardid === false)
|
||
|
return 'ERR:[' . $txt . ']';
|
||
|
|
||
|
if (isset(self::$legacy_shard[$shardid])) {
|
||
|
$tick = self::$legacy_shard[$shardid];
|
||
|
if (self::$legacy_sync > $time) {
|
||
|
// only modify game cycle when asked time is before sync
|
||
|
$tick = ($time - self::$legacy_sync) * 10 + $tick;
|
||
|
}
|
||
|
} else {
|
||
|
$tick = ryzom_time_tick($shardid);
|
||
|
// tick is for NOW, adjust it to match time given
|
||
|
$now = time();
|
||
|
$tick = ($time - $now) * 10 + $tick;
|
||
|
}
|
||
|
|
||
|
$rytime = ryzom_time_array($tick, $shardid);
|
||
|
$txt = ryzom_time_txt($rytime, self::$lang);
|
||
|
|
||
|
return $txt;
|
||
|
}
|
||
|
|
||
|
static function bb_lang($attr, $txt) {
|
||
|
if (_user()->lang == $attr)
|
||
|
return $txt;
|
||
|
else
|
||
|
return '';
|
||
|
}
|
||
|
|
||
|
static function bb_time($options, $txt) {
|
||
|
$time = strtotime($txt);
|
||
|
|
||
|
if ($time == 0) {
|
||
|
return $txt;
|
||
|
}
|
||
|
|
||
|
$timezone = self::$timezone;
|
||
|
|
||
|
$show_time = '';
|
||
|
$show_date = '';
|
||
|
$show_timer = '';
|
||
|
|
||
|
if (is_array($options)) {
|
||
|
foreach ($options as $key => $val) {
|
||
|
switch ($key) {
|
||
|
case 'timezone':
|
||
|
// fix some timezones for php
|
||
|
switch ($val) {
|
||
|
case 'pst': // fall thru
|
||
|
case 'pdt': $val = 'US/Pacific';
|
||
|
break;
|
||
|
}
|
||
|
$timezone = $val;
|
||
|
break;
|
||
|
case 'date' :
|
||
|
$show_date = $val == 'off' ? false : $val;
|
||
|
break;
|
||
|
case 'time' :
|
||
|
$show_time = $val == 'off' ? false : $val;
|
||
|
break;
|
||
|
case 'timer':
|
||
|
$show_timer = $val == 'off' ? false : $val;
|
||
|
break;
|
||
|
}//switch
|
||
|
}//foreach
|
||
|
}
|
||
|
|
||
|
$ret = array();
|
||
|
|
||
|
$old_timezone = date_default_timezone_get();
|
||
|
@date_default_timezone_set($timezone);
|
||
|
if ($show_date !== false) {
|
||
|
$date = ryzom_absolute_time($time);
|
||
|
//ryzom_absolute_time does not have year, so we need to add it
|
||
|
$current_y = date('Y', time());
|
||
|
$y = date('Y', $time);
|
||
|
if ($y != $current_y) {
|
||
|
$date.= ' ' . $y;
|
||
|
}
|
||
|
$ret[] = self::bb_color($show_date, $date);
|
||
|
}
|
||
|
if ($show_time !== false) {
|
||
|
$fmtTime = self::$clock12h ? 'g:i:s a T' : 'H:i:s T';
|
||
|
$ret[] = self::bb_color($show_time, date($fmtTime, $time));
|
||
|
}
|
||
|
date_default_timezone_set($old_timezone);
|
||
|
|
||
|
if ($show_timer !== false) {
|
||
|
if ($show_time === false && $show_date === false) {
|
||
|
$f = '%s';
|
||
|
} else {
|
||
|
$f = '(%s)';
|
||
|
}
|
||
|
$ret[] = self::bb_color($show_timer, sprintf($f, ryzom_relative_time($time)));
|
||
|
}
|
||
|
|
||
|
return join(' ', $ret);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* This function is called by bbCodeParser class
|
||
|
*
|
||
|
* @see bbCodeParser::format
|
||
|
*/
|
||
|
public function format($tag, $open, $close, $attr, $text) {
|
||
|
// silly functions all have different parameters
|
||
|
switch ($tag) {
|
||
|
case 'noparse' :
|
||
|
$result = self::bb_noparse($text);
|
||
|
break;
|
||
|
case 'code' :
|
||
|
$result = self::bb_code($text);
|
||
|
break;
|
||
|
case 'quote' :
|
||
|
$result = self::bb_quote($attr, $text);
|
||
|
break;
|
||
|
case 'h1' : // fall thru
|
||
|
case 'h2' : // fall thru
|
||
|
case 'h3' : // fall thru
|
||
|
case 'h4' : // fall thru
|
||
|
case 'h5' : // fall thru
|
||
|
case 'h6' :
|
||
|
$nr = (int) substr($tag, -1);
|
||
|
$color = isset($attr['color']) ? $attr['color'] : '';
|
||
|
$result = self::bb_h($nr, $color, $text);
|
||
|
break;
|
||
|
case 'color' :
|
||
|
$result = self::bb_color($attr, $text);
|
||
|
break;
|
||
|
case 'size' :
|
||
|
$result = self::bb_size($attr, $text);
|
||
|
break;
|
||
|
case 'list' :
|
||
|
$result = self::bb_list($text);
|
||
|
break;
|
||
|
case 'img' :
|
||
|
$result = self::bb_img($attr, $text);
|
||
|
break;
|
||
|
case 'banner' :
|
||
|
$result = self::bb_banner($attr, $text);
|
||
|
break;
|
||
|
case 'pre' :
|
||
|
$result = self::bb_pre($text);
|
||
|
break;
|
||
|
case 'p' :
|
||
|
$result = self::bb_p($text);
|
||
|
break;
|
||
|
case 'table' :
|
||
|
$result = self::bb_table($attr, $text);
|
||
|
break;
|
||
|
case 'center' :
|
||
|
$result = self::bb_center($text);
|
||
|
break;
|
||
|
case 'url' :
|
||
|
$result = self::bb_url($attr, $text);
|
||
|
break;
|
||
|
case 'mail' :
|
||
|
$result = self::bb_mail($text);
|
||
|
break;
|
||
|
case 'profile' :
|
||
|
$result = self::bb_profile($attr, $text);
|
||
|
break;
|
||
|
case 'page' :
|
||
|
$result = self::bb_page_link($attr, $text);
|
||
|
break;
|
||
|
case 'forum' : // fall thru
|
||
|
case 'topic' : // fall thru
|
||
|
case 'post' :
|
||
|
$result = self::bb_forum_link($tag, $attr, $text);
|
||
|
break;
|
||
|
case 'wiki' :
|
||
|
$result = self::bb_wiki_link($attr, $text);
|
||
|
break;
|
||
|
case 'b' : // fall thru
|
||
|
case 'i' : // fall thru
|
||
|
case 'u' :
|
||
|
$result = self::bb_biu($tag, $text);
|
||
|
break;
|
||
|
case 'time' :
|
||
|
$result = self::bb_time($attr, $text);
|
||
|
break;
|
||
|
case 'date' :
|
||
|
$result = self::bb_date($attr, $text);
|
||
|
break;
|
||
|
case 'lang' :
|
||
|
$result = self::bb_lang($attr, $text);
|
||
|
break;
|
||
|
default :
|
||
|
$result = $open . $text . $close;
|
||
|
break;
|
||
|
}
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Replaces some BBcode with HTML code
|
||
|
*
|
||
|
* NOTE: $text should be already escaped for HTML
|
||
|
*
|
||
|
* @param string $text html escaped input text
|
||
|
* @param array $disabledTags
|
||
|
*/
|
||
|
static function parse($text, $disabledTags = array()) {
|
||
|
static $parser = null;
|
||
|
if ($parser === null) {
|
||
|
$parser = new self(self::$ig);
|
||
|
}
|
||
|
$parser->reset();
|
||
|
|
||
|
self::$disabledTags = $disabledTags;
|
||
|
return $parser->bbcode($text);
|
||
|
}
|
||
|
|
||
|
}
|