Getting rid of pear folder. Modifying and moving bbcode parsing to parsers.

git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/branches/2.1.0@41607 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
Jeremy Shipman 2007-09-12 22:36:26 +00:00 committed by Sam Minnee
parent 2ff0a30980
commit cdafcbcf77
10 changed files with 1826 additions and 0 deletions

View File

@ -0,0 +1,896 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
// +----------------------------------------------------------------------+
// | PHP Version 4 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2003 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.02 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Author: Stijn de Reede <sjr@gmx.co.uk> |
// +----------------------------------------------------------------------+
//
// $Id: BBCodeParser.php,v 1.17 2007/07/02 18:46:30 cweiske Exp $
//
/**
* @package HTML_BBCodeParser
* @author Stijn de Reede <sjr@gmx.co.uk>
*
*
* This is a parser to replace UBB style tags with their html equivalents. It
* does not simply do some regex calls, but is complete stack based
* parse engine. This ensures that all tags are properly nested, if not,
* extra tags are added to maintain the nesting. This parser should only produce
* xhtml 1.0 compliant code. All tags are validated and so are all their attributes.
* It should be easy to extend this parser with your own tags, see the _definedTags
* format description below.
*
*
* Usage:
* $parser = new HTML_BBCodeParser();
* $parser->setText('normal [b]bold[/b] and normal again');
* $parser->parse();
* echo $parser->getParsed();
* or:
* $parser = new HTML_BBCodeParser();
* echo $parser->qparse('normal [b]bold[/b] and normal again');
* or:
* echo HTML_BBCodeParser::staticQparse('normal [b]bold[/b] and normal again');
*
*
* Setting the options from the ini file:
* $config = parse_ini_file('BBCodeParser.ini', true);
* $options = &PEAR::getStaticProperty('HTML_BBCodeParser', '_options');
* $options = $config['HTML_BBCodeParser'];
* unset($options);
*
*
* The _definedTags variables should be in this format:
* array('tag' // the actual tag used
* => array('htmlopen' => 'open', // the opening tag in html
* 'htmlclose' => 'close', // the closing tag in html,
* can be set to an empty string
* if no closing tag is present
* in html (like <img>)
* 'allowed' => 'allow', // tags that are allowed inside
* this tag. Values can be all
* or none, or either of these
* two, followed by a ^ and then
* followed by a comma seperated
* list of exceptions on this
* 'attributes' => array() // an associative array containing
* the tag attributes and their
* printf() html equivalents, to
* which the first argument is
* the value, and the second is
* the quote. Default would be
* something like this:
* 'attr' => 'attr=%2$s%1$s%2$s'
* ),
* 'etc'
* => (...)
* )
*/
class HTML_BBCodeParser
{
/**
* An array of tags parsed by the engine, should be overwritten by filters
*
* @access private
* @var array
*/
var $_definedTags = array();
/**
* A string containing the input
*
* @access private
* @var string
*/
var $_text = '';
/**
* A string containing the preparsed input
*
* @access private
* @var string
*/
var $_preparsed = '';
/**
* An array tags and texts build from the input text
*
* @access private
* @var array
*/
var $_tagArray = array();
/**
* A string containing the parsed version of the text
*
* @access private
* @var string
*/
var $_parsed = '';
/**
* An array of options, filled by an ini file or through the contructor
*
* @access private
* @var array
*/
var $_options = array(
'quotestyle' => 'double',
'quotewhat' => 'all',
'open' => '[',
'close' => ']',
'xmlclose' => true,
'filters' => 'Basic'
);
/**
* An array of filters used for parsing
*
* @access private
* @var array
*/
var $_filters = array();
/**
* Constructor, initialises the options and filters
*
* Sets the private variable _options with base options defined with
* &PEAR::getStaticProperty(), overwriting them with (if present)
* the argument to this method.
* Then it sets the extra options to properly escape the tag
* characters in preg_replace() etc. The set options are
* then stored back with &PEAR::getStaticProperty(), so that the filter
* classes can use them.
* All the filters in the options are initialised and their defined tags
* are copied into the private variable _definedTags.
*
* @param array options to use, can be left out
* @return none
* @access public
* @author Stijn de Reede <sjr@gmx.co.uk>
*/
function HTML_BBCodeParser($options = array())
{
// set the already set options
$baseoptions = &HTML_BBCodeParser::getStaticProperty('HTML_BBCodeParser', '_options');
if (is_array($baseoptions)) {
foreach ($baseoptions as $k => $v) {
$this->_options[$k] = $v;
}
}
// set the options passed as an argument
foreach ($options as $k => $v ) {
$this->_options[$k] = $v;
}
// add escape open and close chars to the options for preg escaping
$preg_escape = '\^$.[]|()?*+{}';
if ($this->_options['open'] != '' && strpos($preg_escape, $this->_options['open'])) {
$this->_options['open_esc'] = "\\".$this->_options['open'];
} else {
$this->_options['open_esc'] = $this->_options['open'];
}
if ($this->_options['close'] != '' && strpos($preg_escape, $this->_options['close'])) {
$this->_options['close_esc'] = "\\".$this->_options['close'];
} else {
$this->_options['close_esc'] = $this->_options['close'];
}
// set the options back so that child classes can use them */
$baseoptions = $this->_options;
unset($baseoptions);
// return if this is a subclass
if (is_subclass_of($this, 'HTML_BBCodeParser_Filter')) {
return;
}
// extract the definedTags from subclasses */
$this->addFilters($this->_options['filters']);
}
function &getStaticProperty($class, $var)
{
static $properties;
if (!isset($properties[$class])) {
$properties[$class] = array();
}
if (!array_key_exists($var, $properties[$class])) {
$properties[$class][$var] = null;
}
return $properties[$class][$var];
}
/**
* Option setter
*
* @param string option name
* @param mixed option value
* @author Lorenzo Alberton <l.alberton@quipo.it>
*/
function setOption($name, $value)
{
$this->_options[$name] = $value;
}
/**
* Add a new filter
*
* @param string filter
* @author Lorenzo Alberton <l.alberton@quipo.it>
*/
function addFilter($filter)
{
$filter = ucfirst($filter);
if (!array_key_exists($filter, $this->_filters)) {
$class = 'HTML_BBCodeParser_Filter_'.$filter;
@include_once 'HTML/BBCodeParser/Filter/'.$filter.'.php';
if (!class_exists($class)) {
//PEAR::raiseError("Failed to load filter $filter", null, PEAR_ERROR_DIE);
}
$this->_filters[$filter] = new $class;
$this->_definedTags = array_merge(
$this->_definedTags,
$this->_filters[$filter]->_definedTags
);
}
}
/**
* Remove an existing filter
*
* @param string $filter
* @author Lorenzo Alberton <l.alberton@quipo.it>
*/
function removeFilter($filter)
{
$filter = ucfirst(trim($filter));
if (!empty($filter) && array_key_exists($filter, $this->_filters)) {
unset($this->_filters[$filter]);
}
// also remove the related $this->_definedTags for this filter,
// preserving the others
$this->_definedTags = array();
foreach (array_keys($this->_filters) as $filter) {
$this->_definedTags = array_merge(
$this->_definedTags,
$this->_filters[$filter]->_definedTags
);
}
}
/**
* Add new filters
*
* @param mixed (array or string)
* @return boolean true if all ok, false if not.
* @author Lorenzo Alberton <l.alberton@quipo.it>
*/
function addFilters($filters)
{
if (is_string($filters)) {
//comma-separated list
if (strpos($filters, ',') !== false) {
$filters = explode(',', $filters);
} else {
$filters = array($filters);
}
}
if (!is_array($filters)) {
//invalid format
return false;
}
foreach ($filters as $filter) {
if (trim($filter)){
$this->addFilter($filter);
}
}
return true;
}
/**
* Executes statements before the actual array building starts
*
* This method should be overwritten in a filter if you want to do
* something before the parsing process starts. This can be useful to
* allow certain short alternative tags which then can be converted into
* proper tags with preg_replace() calls.
* The main class walks through all the filters and and calls this
* method. The filters should modify their private $_preparsed
* variable, with input from $_text.
*
* @return none
* @access private
* @see $_text
* @author Stijn de Reede <sjr@gmx.co.uk>
*/
function _preparse()
{
// default: assign _text to _preparsed, to be overwritten by filters
$this->_preparsed = $this->_text;
// return if this is a subclass
if (is_subclass_of($this, 'HTML_BBCodeParser')) {
return;
}
// walk through the filters and execute _preparse
foreach ($this->_filters as $filter) {
$filter->setText($this->_preparsed);
$filter->_preparse();
$this->_preparsed = $filter->getPreparsed();
}
}
/**
* Builds the tag array from the input string $_text
*
* An array consisting of tag and text elements is contructed from the
* $_preparsed variable. The method uses _buildTag() to check if a tag is
* valid and to build the actual tag to be added to the tag array.
*
* TODO: - rewrite whole method, as this one is old and probably slow
* - see if a recursive method would be better than an iterative one
*
* @return none
* @access private
* @see _buildTag()
* @see $_text
* @see $_tagArray
* @author Stijn de Reede <sjr@gmx.co.uk>
*/
function _buildTagArray()
{
$this->_tagArray = array();
$str = $this->_preparsed;
$strPos = 0;
$strLength = strlen($str);
while (($strPos < $strLength)) {
$tag = array();
$openPos = strpos($str, $this->_options['open'], $strPos);
if ($openPos === false) {
$openPos = $strLength;
$nextOpenPos = $strLength;
}
if ($openPos + 1 > $strLength) {
$nextOpenPos = $strLength;
} else {
$nextOpenPos = strpos($str, $this->_options['open'], $openPos + 1);
if ($nextOpenPos === false) {
$nextOpenPos = $strLength;
}
}
$closePos = strpos($str, $this->_options['close'], $strPos);
if ($closePos === false) {
$closePos = $strLength + 1;
}
if ($openPos == $strPos) {
if (($nextOpenPos < $closePos)) {
// new open tag before closing tag: treat as text
$newPos = $nextOpenPos;
$tag['text'] = substr($str, $strPos, $nextOpenPos - $strPos);
$tag['type'] = 0;
} else {
// possible valid tag
$newPos = $closePos + 1;
$newTag = $this->_buildTag(substr($str, $strPos, $closePos - $strPos + 1));
if (($newTag !== false)) {
$tag = $newTag;
} else {
// no valid tag after all
$tag['text'] = substr($str, $strPos, $closePos - $strPos + 1);
$tag['type'] = 0;
}
}
} else {
// just text
$newPos = $openPos;
$tag['text'] = substr($str, $strPos, $openPos - $strPos);
$tag['type'] = 0;
}
// join 2 following text elements
if ($tag['type'] === 0 && isset($prev) && $prev['type'] === 0) {
$tag['text'] = $prev['text'].$tag['text'];
array_pop($this->_tagArray);
}
$this->_tagArray[] = $tag;
$prev = $tag;
$strPos = $newPos;
}
}
/**
* Builds a tag from the input string
*
* This method builds a tag array based on the string it got as an
* argument. If the tag is invalid, <false> is returned. The tag
* attributes are extracted from the string and stored in the tag
* array as an associative array.
*
* @param string string to build tag from
* @return array tag in array format
* @access private
* @see _buildTagArray()
* @author Stijn de Reede <sjr@gmx.co.uk>
*/
function _buildTag($str)
{
$tag = array('text' => $str, 'attributes' => array());
if (substr($str, 1, 1) == '/') { // closing tag
$tag['tag'] = strtolower(substr($str, 2, strlen($str) - 3));
if (!in_array($tag['tag'], array_keys($this->_definedTags))) {
return false; // nope, it's not valid
} else {
$tag['type'] = 2;
return $tag;
}
} else { // opening tag
$tag['type'] = 1;
if (strpos($str, ' ') && (strpos($str, '=') === false)) {
return false; // nope, it's not valid
}
// tnx to Onno for the regex
// split the tag with arguments and all
$oe = $this->_options['open_esc'];
$ce = $this->_options['close_esc'];
$tagArray = array();
if (preg_match("!$oe([a-z0-9]+)[^$ce]*$ce!i", $str, $tagArray) == 0) {
return false;
}
$tag['tag'] = strtolower($tagArray[1]);
if (!in_array($tag['tag'], array_keys($this->_definedTags))) {
return false; // nope, it's not valid
}
// tnx to Onno for the regex
// validate the arguments
$attributeArray = array();
$regex = "![\s$oe]([a-z0-9]+)=(\"[^\s$ce]+\"|[^\s$ce]";
if ($tag['tag'] != 'url') {
$regex .= "[^=]";
}
$regex .= "+)(?=[\s$ce])!i";
preg_match_all($regex, $str, $attributeArray, PREG_SET_ORDER);
foreach ($attributeArray as $attribute) {
$attNam = strtolower($attribute[1]);
if (in_array($attNam, array_keys($this->_definedTags[$tag['tag']]['attributes']))) {
if ($attribute[2][0] == '"' && $attribute[2][strlen($attribute[2])-1] == '"') {
$tag['attributes'][$attNam] = substr($attribute[2], 1, -1);
} else {
$tag['attributes'][$attNam] = $attribute[2];
}
}
}
return $tag;
}
}
/**
* Validates the tag array, regarding the allowed tags
*
* While looping through the tag array, two following text tags are
* joined, and it is checked that the tag is allowed inside the
* last opened tag.
* By remembering what tags have been opened it is checked that
* there is correct (xml compliant) nesting.
* In the end all still opened tags are closed.
*
* @return none
* @access private
* @see _isAllowed()
* @see $_tagArray
* @author Stijn de Reede <sjr@gmx.co.uk>, Seth Price <seth@pricepages.org>
*/
function _validateTagArray()
{
$newTagArray = array();
$openTags = array();
foreach ($this->_tagArray as $tag) {
$prevTag = end($newTagArray);
switch ($tag['type']) {
case 0:
if (($child = $this->_childNeeded(end($openTags), 'text')) &&
$child !== false &&
/*
* No idea what to do in this case: A child is needed, but
* no valid one is returned. We'll ignore it here and live
* with it until someone reports a valid bug.
*/
$child !== true )
{
if (trim($tag['text']) == '') {
//just an empty indentation or newline without value?
continue;
}
$newTagArray[] = $child;
$openTags[] = $child['tag'];
}
if ($prevTag['type'] === 0) {
$tag['text'] = $prevTag['text'].$tag['text'];
array_pop($newTagArray);
}
$newTagArray[] = $tag;
break;
case 1:
if (!$this->_isAllowed(end($openTags), $tag['tag']) ||
($parent = $this->_parentNeeded(end($openTags), $tag['tag'])) === true ||
($child = $this->_childNeeded(end($openTags), $tag['tag'])) === true) {
$tag['type'] = 0;
if ($prevTag['type'] === 0) {
$tag['text'] = $prevTag['text'].$tag['text'];
array_pop($newTagArray);
}
} else {
if ($parent) {
/*
* Avoid use of parent if we can help it. If we are
* trying to insert a new parent, but the current tag is
* the same as the previous tag, then assume that the
* previous tag structure is valid, and add this tag as
* a sibling. To add as a sibling, we need to close the
* current tag.
*/
if ($tag['tag'] == end($openTags)){
$newTagArray[] = $this->_buildTag('[/'.$tag['tag'].']');
array_pop($openTags);
} else {
$newTagArray[] = $parent;
$openTags[] = $parent['tag'];
}
}
if ($child) {
$newTagArray[] = $child;
$openTags[] = $child['tag'];
}
$openTags[] = $tag['tag'];
}
$newTagArray[] = $tag;
break;
case 2:
if (($tag['tag'] == end($openTags) || $this->_isAllowed(end($openTags), $tag['tag']))) {
if (in_array($tag['tag'], $openTags)) {
$tmpOpenTags = array();
while (end($openTags) != $tag['tag']) {
$newTagArray[] = $this->_buildTag('[/'.end($openTags).']');
$tmpOpenTags[] = end($openTags);
array_pop($openTags);
}
$newTagArray[] = $tag;
array_pop($openTags);
/* why is this here? it just seems to break things
* (nested lists where closing tags need to be
* generated)
while (end($tmpOpenTags)) {
$tmpTag = $this->_buildTag('['.end($tmpOpenTags).']');
$newTagArray[] = $tmpTag;
$openTags[] = $tmpTag['tag'];
array_pop($tmpOpenTags);
}*/
}
} else {
$tag['type'] = 0;
if ($prevTag['type'] === 0) {
$tag['text'] = $prevTag['text'].$tag['text'];
array_pop($newTagArray);
}
$newTagArray[] = $tag;
}
break;
}
}
while (end($openTags)) {
$newTagArray[] = $this->_buildTag('[/'.end($openTags).']');
array_pop($openTags);
}
$this->_tagArray = $newTagArray;
}
/**
* Checks to see if a parent is needed
*
* Checks to see if the current $in tag has an appropriate parent. If it
* does, then it returns false. If a parent is needed, then it returns the
* first tag in the list to add to the stack.
*
* @param array tag that is on the outside
* @param array tag that is on the inside
* @return boolean false if not needed, tag if needed, true if out
* of our minds
* @access private
* @see _validateTagArray()
* @author Seth Price <seth@pricepages.org>
*/
function _parentNeeded($out, $in)
{
if (!isset($this->_definedTags[$in]['parent']) ||
($this->_definedTags[$in]['parent'] == 'all')
) {
return false;
}
$ar = explode('^', $this->_definedTags[$in]['parent']);
$tags = explode(',', $ar[1]);
if ($ar[0] == 'none'){
if ($out && in_array($out, $tags)) {
return false;
}
//Create a tag from the first one on the list
return $this->_buildTag('['.$tags[0].']');
}
if ($ar[0] == 'all' && $out && !in_array($out, $tags)) {
return false;
}
// Tag is needed, we don't know which one. We could make something up,
// but it would be so random, I think that it would be worthless.
return true;
}
/**
* Checks to see if a child is needed
*
* Checks to see if the current $out tag has an appropriate child. If it
* does, then it returns false. If a child is needed, then it returns the
* first tag in the list to add to the stack.
*
* @param array tag that is on the outside
* @param array tag that is on the inside
* @return boolean false if not needed, tag if needed, true if out
* of our minds
* @access private
* @see _validateTagArray()
* @author Seth Price <seth@pricepages.org>
*/
function _childNeeded($out, $in)
{
if (!isset($this->_definedTags[$out]['child']) ||
($this->_definedTags[$out]['child'] == 'all')
) {
return false;
}
$ar = explode('^', $this->_definedTags[$out]['child']);
$tags = explode(',', $ar[1]);
if ($ar[0] == 'none'){
if ($in && in_array($in, $tags)) {
return false;
}
//Create a tag from the first one on the list
return $this->_buildTag('['.$tags[0].']');
}
if ($ar[0] == 'all' && $in && !in_array($in, $tags)) {
return false;
}
// Tag is needed, we don't know which one. We could make something up,
// but it would be so random, I think that it would be worthless.
return true;
}
/**
* Checks to see if a tag is allowed inside another tag
*
* The allowed tags are extracted from the private _definedTags array.
*
* @param array tag that is on the outside
* @param array tag that is on the inside
* @return boolean return true if the tag is allowed, false
* otherwise
* @access private
* @see _validateTagArray()
* @author Stijn de Reede <sjr@gmx.co.uk>
*/
function _isAllowed($out, $in)
{
if (!$out || ($this->_definedTags[$out]['allowed'] == 'all')) {
return true;
}
if ($this->_definedTags[$out]['allowed'] == 'none') {
return false;
}
$ar = explode('^', $this->_definedTags[$out]['allowed']);
$tags = explode(',', $ar[1]);
if ($ar[0] == 'none' && in_array($in, $tags)) {
return true;
}
if ($ar[0] == 'all' && in_array($in, $tags)) {
return false;
}
return false;
}
/**
* Builds a parsed string based on the tag array
*
* The correct html and attribute values are extracted from the private
* _definedTags array.
*
* @return none
* @access private
* @see $_tagArray
* @see $_parsed
* @author Stijn de Reede <sjr@gmx.co.uk>
*/
function _buildParsedString()
{
$this->_parsed = '';
foreach ($this->_tagArray as $tag) {
switch ($tag['type']) {
// just text
case 0:
$this->_parsed .= $tag['text'];
break;
// opening tag
case 1:
$this->_parsed .= '<'.$this->_definedTags[$tag['tag']]['htmlopen'];
if ($this->_options['quotestyle'] == 'single') $q = "'";
if ($this->_options['quotestyle'] == 'double') $q = '"';
foreach ($tag['attributes'] as $a => $v) {
//prevent XSS attacks. IMHO this is not enough, though...
//@see http://pear.php.net/bugs/bug.php?id=5609
$v = preg_replace('#(script|about|applet|activex|chrome):#is', "\\1&#058;", $v);
$v = htmlspecialchars($v);
$v = str_replace('&amp;amp;', '&amp;', $v);
if (($this->_options['quotewhat'] == 'nothing') ||
(($this->_options['quotewhat'] == 'strings') && is_numeric($v))
) {
$this->_parsed .= ' '.sprintf($this->_definedTags[$tag['tag']]['attributes'][$a], $v, '');
} else {
$this->_parsed .= ' '.sprintf($this->_definedTags[$tag['tag']]['attributes'][$a], $v, $q);
}
}
if ($this->_definedTags[$tag['tag']]['htmlclose'] == '' && $this->_options['xmlclose']) {
$this->_parsed .= ' /';
}
$this->_parsed .= '>';
break;
// closing tag
case 2:
if ($this->_definedTags[$tag['tag']]['htmlclose'] != '') {
$this->_parsed .= '</'.$this->_definedTags[$tag['tag']]['htmlclose'].'>';
}
break;
}
}
}
/**
* Sets text in the object to be parsed
*
* @param string the text to set in the object
* @return none
* @access public
* @see getText()
* @see $_text
* @author Stijn de Reede <sjr@gmx.co.uk>
*/
function setText($str)
{
$this->_text = $str;
}
/**
* Gets the unparsed text from the object
*
* @return string the text set in the object
* @access public
* @see setText()
* @see $_text
* @author Stijn de Reede <sjr@gmx.co.uk>
*/
function getText()
{
return $this->_text;
}
/**
* Gets the preparsed text from the object
*
* @return string the text set in the object
* @access public
* @see _preparse()
* @see $_preparsed
* @author Stijn de Reede <sjr@gmx.co.uk>
*/
function getPreparsed()
{
return $this->_preparsed;
}
/**
* Gets the parsed text from the object
*
* @return string the parsed text set in the object
* @access public
* @see parse()
* @see $_parsed
* @author Stijn de Reede <sjr@gmx.co.uk>
*/
function getParsed()
{
return $this->_parsed;
}
/**
* Parses the text set in the object
*
* @return none
* @access public
* @see _preparse()
* @see _buildTagArray()
* @see _validateTagArray()
* @see _buildParsedString()
* @author Stijn de Reede <sjr@gmx.co.uk>
*/
function parse()
{
$this->_preparse();
$this->_buildTagArray();
$this->_validateTagArray();
$this->_buildParsedString();
}
/**
* Quick method to do setText(), parse() and getParsed at once
*
* @return none
* @access public
* @see parse()
* @see $_text
* @author Stijn de Reede <sjr@gmx.co.uk>
*/
function qparse($str)
{
$this->_text = $str;
$this->parse();
return $this->_parsed;
}
/**
* Quick static method to do setText(), parse() and getParsed at once
*
* @return none
* @access public
* @see parse()
* @see $_text
* @author Stijn de Reede <sjr@gmx.co.uk>
*/
function staticQparse($str)
{
$p = new HTML_BBCodeParser();
$str = $p->qparse($str);
unset($p);
return $str;
}
}
?>

View File

@ -0,0 +1,10 @@
<?php
require_once 'HTML/BBCodeParser.php';
/**
* Dummy class that filters need to extend from.
*/
class HTML_BBCodeParser_Filter extends HTML_BBCodeParser
{
}
?>

View File

@ -0,0 +1,71 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
// +----------------------------------------------------------------------+
// | PHP Version 4 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2003 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.02 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Author: Stijn de Reede <sjr@gmx.co.uk> |
// +----------------------------------------------------------------------+
//
// $Id: Basic.php,v 1.6 2007/07/02 16:54:25 cweiske Exp $
//
/**
* @package HTML_BBCodeParser
* @author Stijn de Reede <sjr@gmx.co.uk>
*/
require_once 'HTML/BBCodeParser/Filter.php';
class HTML_BBCodeParser_Filter_Basic extends HTML_BBCodeParser_Filter
{
/**
* An array of tags parsed by the engine
*
* @access private
* @var array
*/
var $_definedTags = array( 'b' => array( 'htmlopen' => 'strong',
'htmlclose' => 'strong',
'allowed' => 'all',
'attributes'=> array()),
'i' => array( 'htmlopen' => 'em',
'htmlclose' => 'em',
'allowed' => 'all',
'attributes'=> array()),
'u' => array( 'htmlopen' => 'span style="text-decoration:underline;"',
'htmlclose' => 'span',
'allowed' => 'all',
'attributes'=> array()),
's' => array( 'htmlopen' => 'del',
'htmlclose' => 'del',
'allowed' => 'all',
'attributes'=> array()),
'sub' => array( 'htmlopen' => 'sub',
'htmlclose' => 'sub',
'allowed' => 'all',
'attributes'=> array()),
'sup' => array( 'htmlopen' => 'sup',
'htmlclose' => 'sup',
'allowed' => 'all',
'attributes'=> array())
);
}
?>

View File

@ -0,0 +1,85 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
// +----------------------------------------------------------------------+
// | PHP Version 4 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2003 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.02 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Author: Stijn de Reede <sjr@gmx.co.uk> |
// +----------------------------------------------------------------------+
//
// $Id: Email.php,v 1.5 2007/07/02 16:54:25 cweiske Exp $
//
/**
* @package HTML_BBCodeParser
* @author Stijn de Reede <sjr@gmx.co.uk>
*/
require_once 'HTML/BBCodeParser/Filter.php';
class HTML_BBCodeParser_Filter_Email extends HTML_BBCodeParser_Filter
{
/**
* An array of tags parsed by the engine
*
* @access private
* @var array
*/
var $_definedTags = array( 'email' => array( 'htmlopen' => 'a',
'htmlclose' => 'a',
'allowed' => 'none^img',
'attributes'=> array('email' =>'href=%2$smailto:%1$s%2$s')
)
);
/**
* Executes statements before the actual array building starts
*
* This method should be overwritten in a filter if you want to do
* something before the parsing process starts. This can be useful to
* allow certain short alternative tags which then can be converted into
* proper tags with preg_replace() calls.
* The main class walks through all the filters and and calls this
* method if it exists. The filters should modify their private $_text
* variable.
*
* @return none
* @access private
* @see $_text
* @author Stijn de Reede <sjr@gmx.co.uk>
*/
function _preparse()
{
$options = HTML_BBCodeParser::getStaticProperty('HTML_BBCodeParser','_options');
$o = $options['open'];
$c = $options['close'];
$oe = $options['open_esc'];
$ce = $options['close_esc'];
$pattern = array( "!(^|\s)([-a-z0-9_.]+@[-a-z0-9.]+\.[a-z]{2,4})!i",
"!".$oe."email(".$ce."|\s.*".$ce.")(.*)".$oe."/email".$ce."!Ui");
$replace = array( "\\1".$o."email=\\2".$c."\\2".$o."/email".$c,
$o."email=\\2\\1\\2".$o."/email".$c);
$this->_preparsed = preg_replace($pattern, $replace, $this->_text);
}
}
?>

View File

@ -0,0 +1,97 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
// +----------------------------------------------------------------------+
// | PHP Version 4 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2003 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.02 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Author: Stijn de Reede <sjr@gmx.co.uk> |
// +----------------------------------------------------------------------+
//
// $Id: Extended.php,v 1.3 2007/07/02 16:54:25 cweiske Exp $
//
/**
* @package HTML_BBCodeParser
* @author Stijn de Reede <sjr@gmx.co.uk>
*/
require_once 'HTML/BBCodeParser/Filter.php';
class HTML_BBCodeParser_Filter_Extended extends HTML_BBCodeParser_Filter
{
/**
* An array of tags parsed by the engine
*
* @access private
* @var array
*/
var $_definedTags = array(
'color' => array( 'htmlopen' => 'span',
'htmlclose' => 'span',
'allowed' => 'all',
'attributes'=> array('color' =>'style=%2$scolor:%1$s%2$s')),
'size' => array( 'htmlopen' => 'span',
'htmlclose' => 'span',
'allowed' => 'all',
'attributes'=> array('size' =>'style=%2$sfont-size:%1$spt%2$s')),
'font' => array( 'htmlopen' => 'span',
'htmlclose' => 'span',
'allowed' => 'all',
'attributes'=> array('font' =>'style=%2$sfont-family:%1$s%2$s')),
'align' => array( 'htmlopen' => 'div',
'htmlclose' => 'div',
'allowed' => 'all',
'attributes'=> array('align' =>'style=%2$stext-align:%1$s%2$s')),
'quote' => array('htmlopen' => 'q',
'htmlclose' => 'q',
'allowed' => 'all',
'attributes'=> array('quote' =>'cite=%2$s%1$s%2$s')),
'code' => array('htmlopen' => 'pre',
'htmlclose' => 'pre',
'allowed' => 'all',
'attributes'=> array()),
'h1' => array('htmlopen' => 'h1',
'htmlclose' => 'h1',
'allowed' => 'all',
'attributes'=> array()),
'h2' => array('htmlopen' => 'h2',
'htmlclose' => 'h2',
'allowed' => 'all',
'attributes'=> array()),
'h3' => array('htmlopen' => 'h3',
'htmlclose' => 'h3',
'allowed' => 'all',
'attributes'=> array()),
'h4' => array('htmlopen' => 'h4',
'htmlclose' => 'h4',
'allowed' => 'all',
'attributes'=> array()),
'h5' => array('htmlopen' => 'h5',
'htmlclose' => 'h5',
'allowed' => 'all',
'attributes'=> array()),
'h6' => array('htmlopen' => 'h6',
'htmlclose' => 'h6',
'allowed' => 'all',
'attributes'=> array())
);
}
?>

View File

@ -0,0 +1,79 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
// +----------------------------------------------------------------------+
// | PHP Version 4 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2003 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.02 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Author: Stijn de Reede <sjr@gmx.co.uk> |
// +----------------------------------------------------------------------+
//
// $Id: Images.php,v 1.8 2007/07/02 17:44:47 cweiske Exp $
//
/**
* @package HTML_BBCodeParser
* @author Stijn de Reede <sjr@gmx.co.uk>
*/
require_once 'HTML/BBCodeParser/Filter.php';
class HTML_BBCodeParser_Filter_Images extends HTML_BBCodeParser_Filter
{
/**
* An array of tags parsed by the engine
*
* @access private
* @var array
*/
var $_definedTags = array(
'img' => array(
'htmlopen' => 'img',
'htmlclose' => '',
'allowed' => 'none',
'attributes'=> array(
'img' => 'src=%2$s%1$s%2$s',
'w' => 'width=%2$s%1$d%2$s',
'h' => 'height=%2$s%1$d%2$s',
'alt' => 'alt=%2$s%1$s%2$s',
)
)
);
/**
* Executes statements before the actual array building starts
*
* This method should be overwritten in a filter if you want to do
* something before the parsing process starts. This can be useful to
* allow certain short alternative tags which then can be converted into
* proper tags with preg_replace() calls.
* The main class walks through all the filters and and calls this
* method if it exists. The filters should modify their private $_text
* variable.
*
* @return none
* @access private
* @see $_text
* @author Stijn de Reede <sjr@gmx.co.uk>
*/
function _preparse()
{
$options = HTML_BBCodeParser::getStaticProperty('HTML_BBCodeParser','_options');
$o = $options['open'];
$c = $options['close'];
$oe = $options['open_esc'];
$ce = $options['close_esc'];
$this->_preparsed = preg_replace(
"!".$oe."img(\s?.*)".$ce."(.*)".$oe."/img".$ce."!Ui",
$o."img=\"\$2\"\$1".$c.$o."/img".$c,
$this->_text);
}
}

View File

@ -0,0 +1,200 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
// +----------------------------------------------------------------------+
// | PHP Version 4 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2003 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.02 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Author: Stijn de Reede <sjr@gmx.co.uk> |
// +----------------------------------------------------------------------+
//
// $Id: Links.php,v 1.12 2007/07/02 18:26:17 cweiske Exp $
//
/**
* @package HTML_BBCodeParser
* @author Stijn de Reede <sjr@gmx.co.uk>
*/
require_once 'HTML/BBCodeParser/Filter.php';
/**
*
*/
class HTML_BBCodeParser_Filter_Links extends HTML_BBCodeParser_Filter
{
/**
* List of allowed schemes
*
* @access private
* @var array
*/
var $_allowedSchemes = array('http', 'https', 'ftp');
/**
* Default scheme
*
* @access private
* @var string
*/
var $_defaultScheme = 'http';
/**
* An array of tags parsed by the engine
*
* @access private
* @var array
*/
var $_definedTags = array(
'url' => array(
'htmlopen' => 'a',
'htmlclose' => 'a',
'allowed' => 'none^img',
'attributes'=> array('url' => 'href=%2$s%1$s%2$s')
)
);
/**
* Executes statements before the actual array building starts
*
* This method should be overwritten in a filter if you want to do
* something before the parsing process starts. This can be useful to
* allow certain short alternative tags which then can be converted into
* proper tags with preg_replace() calls.
* The main class walks through all the filters and and calls this
* method if it exists. The filters should modify their private $_text
* variable.
*
* @return none
* @access private
* @see $_text
* @author Stijn de Reede <sjr@gmx.co.uk>
* @author Seth Price <seth@pricepages.org>
*/
function _preparse()
{
$options = HTML_BBCodeParser::getStaticProperty('HTML_BBCodeParser', '_options');
$o = $options['open'];
$c = $options['close'];
$oe = $options['open_esc'];
$ce = $options['close_esc'];
$schemes = implode('|', $this->_allowedSchemes);
$pattern = array( "/(?<![\"'=".$ce."\/])(".$oe."[^".$ce."]*".$ce.")?(((".$schemes."):\/\/|www)[@-a-z0-9.]+\.[a-z]{2,4}[^\s()\[\]]*)/i",
"!".$oe."url(".$ce."|\s.*".$ce.")(.*)".$oe."/url".$ce."!iU",
"!".$oe."url=((([a-z]*:(//)?)|www)[@-a-z0-9.]+)([^\s\[\]]*)".$ce."(.*)".$oe."/url".$ce."!i");
$pp = preg_replace_callback($pattern[0], array($this, 'smarterPPLinkExpand'), $this->_text);
$pp = preg_replace($pattern[1], $o."url=\$2\$1\$2".$o."/url".$c, $pp);
$this->_preparsed = preg_replace_callback($pattern[2], array($this, 'smarterPPLink'), $pp);
}
/**
* Intelligently expand a URL into a link
*
* @return string
* @access private
* @author Seth Price <seth@pricepages.org>
* @author Lorenzo Alberton <l.alberton@quipo.it>
*/
function smarterPPLinkExpand($matches)
{
//echo '<hr><pre>';var_dump($matches);echo '</pre><hr>';
$options = HTML_BBCodeParser::getStaticProperty('HTML_BBCodeParser','_options');
$o = $options['open'];
$c = $options['close'];
//If we have an intro tag that is [url], then skip this match
if ($matches[1] == $o.'url'.$c) {
return $matches[0];
}
$punctuation = '.,;:'; // Links can't end with these chars
$trailing = '';
// Knock off ending punctuation
$last = substr($matches[2], -1);
while (strpos($punctuation, $last) !== false) {
// Last character is punctuation - remove it from the url
$trailing = $last.$trailing;
$matches[2] = substr($matches[2], 0, -1);
$last = substr($matches[2], -1);
}
$off = strpos($matches[2], ':');
//Is a ":" (therefore a scheme) defined?
if ($off === false) {
/*
* Create a link with the default scheme of http. Notice that the
* text that is viewable to the user is unchanged, but the link
* itself contains the "http://".
*/
return $matches[1].$o.'url='.$this->_defaultScheme.'://'.$matches[2].$c.$matches[2].$o.'/url'.$c.$trailing;
}
$scheme = substr($matches[2], 0, $off);
/*
* If protocol is in the approved list than allow it. Note that this
* check isn't really needed, but the created link will just be deleted
* later in smarterPPLink() if we create it now and it isn't on the
* scheme list.
*/
if (in_array($scheme, $this->_allowedSchemes)) {
return $matches[1].$o.'url'.$c.$matches[2].$o.'/url'.$c.$trailing;
}
return $matches[0];
}
/**
* Finish preparsing URL to clean it up
*
* @return string
* @access private
* @author Seth Price <seth@pricepages.org>
*/
function smarterPPLink($matches)
{
$options = HTML_BBCodeParser::getStaticProperty('HTML_BBCodeParser','_options');
$o = $options['open'];
$c = $options['close'];
$urlServ = $matches[1];
$path = $matches[5];
$off = strpos($urlServ, ':');
if ($off === false) {
//Default to http
$urlServ = $this->_defaultScheme.'://'.$urlServ;
$off = strpos($urlServ, ':');
}
//Add trailing slash if missing (to create a valid URL)
if (!$path) {
$path = '/';
}
$protocol = substr($urlServ, 0, $off);
if (in_array($protocol, $this->_allowedSchemes)) {
//If protocol is in the approved list than allow it
return $o.'url='.$urlServ.$path.$c.$matches[6].$o.'/url'.$c;
}
//Else remove url tag
return $matches[6];
}
}
?>

View File

@ -0,0 +1,108 @@
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
// +----------------------------------------------------------------------+
// | PHP Version 4 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2003 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.02 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Author: Stijn de Reede <sjr@gmx.co.uk> |
// +----------------------------------------------------------------------+
//
// $Id: Lists.php,v 1.5 2007/07/02 16:54:25 cweiske Exp $
//
/**
* @package HTML_BBCodeParser
* @author Stijn de Reede <sjr@gmx.co.uk>
*/
require_once 'HTML/BBCodeParser/Filter.php';
/**
*
*/
class HTML_BBCodeParser_Filter_Lists extends HTML_BBCodeParser_Filter
{
/**
* An array of tags parsed by the engine
*
* @access private
* @var array
*/
var $_definedTags = array( 'list' => array( 'htmlopen' => 'ol',
'htmlclose' => 'ol',
'allowed' => 'all',
'child' => 'none^li',
'attributes'=> array('list' => 'style=%2$slist-style-type:%1$s;%2$s')
),
'ulist' => array( 'htmlopen' => 'ul',
'htmlclose' => 'ul',
'allowed' => 'all',
'child' => 'none^li',
'attributes'=> array('list' => 'style=%2$slist-style-type:%1$s;%2$s')
),
'li' => array( 'htmlopen' => 'li',
'htmlclose' => 'li',
'allowed' => 'all',
'parent' => 'none^ulist,list',
'attributes'=> array()
)
);
/**
* Executes statements before the actual array building starts
*
* This method should be overwritten in a filter if you want to do
* something before the parsing process starts. This can be useful to
* allow certain short alternative tags which then can be converted into
* proper tags with preg_replace() calls.
* The main class walks through all the filters and and calls this
* method if it exists. The filters should modify their private $_text
* variable.
*
* @return none
* @access private
* @see $_text
* @author Stijn de Reede <sjr@gmx.co.uk>, Seth Price <seth@pricepages.org>
*/
function _preparse()
{
$options = HTML_BBCodeParser::getStaticProperty('HTML_BBCodeParser','_options');
$o = $options['open'];
$c = $options['close'];
$oe = $options['open_esc'];
$ce = $options['close_esc'];
$pattern = array( "!".$oe."\*".$ce."!",
"!".$oe."(u?)list=(?-i:A)(\s*[^".$ce."]*)".$ce."!i",
"!".$oe."(u?)list=(?-i:a)(\s*[^".$ce."]*)".$ce."!i",
"!".$oe."(u?)list=(?-i:I)(\s*[^".$ce."]*)".$ce."!i",
"!".$oe."(u?)list=(?-i:i)(\s*[^".$ce."]*)".$ce."!i",
"!".$oe."(u?)list=(?-i:1)(\s*[^".$ce."]*)".$ce."!i",
"!".$oe."(u?)list([^".$ce."]*)".$ce."!i");
$replace = array( $o."li".$c,
$o."\$1list=upper-alpha\$2".$c,
$o."\$1list=lower-alpha\$2".$c,
$o."\$1list=upper-roman\$2".$c,
$o."\$1list=lower-roman\$2".$c,
$o."\$1list=decimal\$2".$c,
$o."\$1list\$2".$c );
$this->_preparsed = preg_replace($pattern, $replace, $this->_text);
}
}
?>

View File

@ -0,0 +1,36 @@
<?php
if (!defined('PHPUnit_MAIN_METHOD')) {
define('PHPUnit_MAIN_METHOD', 'AllTests::main');
chdir(dirname(__FILE__) . '/../');
}
if (!defined('PHPUnit_INSIDE_OWN_TESTSUITE')) {
define('PHPUnit_INSIDE_OWN_TESTSUITE', TRUE);
}
require_once 'PHPUnit/Framework/TestSuite.php';
require_once 'PHPUnit/TextUI/TestRunner.php';
require_once 'HTML_BBCodeParserTest.php';
class AllTests
{
public static function main()
{
PHPUnit_TextUI_TestRunner::run(self::suite());
}
public static function suite()
{
$suite = new PHPUnit_Framework_TestSuite('HTML_BBCodeParserTest');
return $suite;
}
}
if (PHPUnit_MAIN_METHOD == 'AllTests::main') {
AllTests::main();
}
?>

View File

@ -0,0 +1,244 @@
<?php
require_once 'PHPUnit.php';
require_once 'HTML/BBCodeParser.php';
class HTML_BBCodeParserTest extends PHPUnit_Framework_TestCase
{
function testFilters()
{
$bbc = new HTML_BBCodeParser(array('filters' => ''));
$bbc->addFilter('Basic');
$this->basicBBCode($bbc, 'qparse');
$bbc->removeFilter('Basic');
$this->assertEquals('[b]txt[/b]', $bbc->qparse('[b]txt[/b]'), 'Basic filters have been removed.');
$bbc->addFilters('Basic,Email');
$this->basicBBCode($bbc, 'qparse');
$this->emailBBCode($bbc, 'qparse');
}
function testQparse()
{
$bbc = new HTML_BBCodeParser(array('filters' => 'Basic,Email,Extended,Images,Links,Lists'));
$this->basicBBCode($bbc, 'qparse');
$this->listBBCode($bbc, 'qparse');
$this->linkBBCode($bbc, 'qparse');
$this->extBBCode($bbc, 'qparse');
$this->imgBBCode($bbc, 'qparse');
$this->emailBBCode($bbc, 'qparse');
}
function emailBBCode($bbc, $funcNam)
{
$this->assertEquals('<a href="mailto:guest@anonymous.org">guest@anonymous.org</a>', $bbc->$funcNam('guest@anonymous.org'));
$this->assertEquals('<a href="mailto:guest@anonymous.org">mail me</a>', $bbc->$funcNam('[email=guest@anonymous.org]mail me[/email]'));
$this->assertEquals('<a href="mailto:guest@anonymous.org">guest@anonymous.org</a>', $bbc->$funcNam('[email]guest@anonymous.org[/email]'));
}
function imgBBCode($bbc, $funcNam)
{
$this->assertEquals('<img src="/images/Enthalpy Wheel.png" width="100" height="99" alt="Enthalpy Wheel" />', $bbc->$funcNam('[img w=100 h=99 alt=Enthalpy Wheel]/images/Enthalpy Wheel.png[/img]'));
$this->assertEquals('<img src="img.jpg" />', $bbc->$funcNam('[img]img.jpg[/img]'));
$this->assertEquals('<img src="http://www.server.org/image.jpg" width="100" height="200" />', $bbc->$funcNam('[img w=100 h=200]http://www.server.org/image.jpg[/img]'));
}
function basicBBCode($bbc, $funcNam)
{
$this->assertEquals('<strong>txt</strong>', $bbc->$funcNam('[b]txt[/b]'));
$this->assertEquals('<strong>txt</strong>', $bbc->$funcNam('[b]txt'));
$this->assertEquals('<em>txt</em>', $bbc->$funcNam('[i]txt[/i]'));
$this->assertEquals('<em>txt</em>', $bbc->$funcNam('[i]txt[/I]'));
$this->assertEquals('<em>txt</em>', $bbc->$funcNam('[I]txt[/i]'));
$this->assertEquals('<em>txt</em>', $bbc->$funcNam('[I]txt[/I]'));
$this->assertEquals('<del>txt</del>', $bbc->$funcNam('[s]txt[/s]'));
$this->assertEquals('<span style="text-decoration:underline;">txt</span>', $bbc->$funcNam('[u]txt[/u]'));
$this->assertEquals('<sub>txt</sub>', $bbc->$funcNam('[sub]txt[/sub]'));
$this->assertEquals('<sup>txt</sup>', $bbc->$funcNam('[sup]txt[/sup]'));
$this->assertEquals('<sup><sub>txt</sub></sup>', $bbc->$funcNam('[sup][sub]txt[/sup][/sub]'));
$this->assertEquals('<em><strong>txt</strong></em>', $bbc->$funcNam('[i][b]txt[/i][/b]'));
}
function listBBCode($bbc, $funcNam)
{
$this->assertEquals('<ul><li>txt</li></ul>', $bbc->$funcNam('[*]txt'));
$this->assertEquals("<ul><li>txt\n</li></ul>", $bbc->$funcNam("[ulist][*]txt\n[/ulist]"));
$this->assertEquals('<ul><li>txt</li></ul>', $bbc->$funcNam('[ulist]txt[/ulist]'));
$this->assertEquals('<ul><li><ul><li><ul><li>txt</li></ul></li></ul></li></ul>', $bbc->$funcNam('[ulist][ulist][ulist]txt'));
$this->assertEquals('<ul><li>[xxx]txt[/xxx]</li></ul>', $bbc->$funcNam('[ulist][xxx]txt[/xxx][/ulist]'));
$this->assertEquals('<ul><li>txt</li></ul>', $bbc->$funcNam('[ulist][li]txt[/li][/ulist]'));
$this->assertEquals('<ul><li>txt</li><li>txt</li></ul>', $bbc->$funcNam('[ulist][li]txt[li]txt[/ulist]'));
$this->assertEquals('<ul><li>txt</li></ul>', $bbc->$funcNam('[ulist][*]txt[/ulist]'));
$this->assertEquals('<ul><li><ol><li>txt</li></ol></li></ul>', $bbc->$funcNam('[ulist][*][list][*]txt[/ulist]'));
$this->assertEquals('<ol><li>txt</li></ol>', $bbc->$funcNam('[list][li]txt[/li][/list]'));
$this->assertEquals('<ul><li><ol><li>txt</li></ol></li></ul>', $bbc->$funcNam('[li][list][li]txt[/li][/list]'));
$this->assertEquals('<ul><li>txt<ul><li>txt</li></ul></li></ul>', $bbc->$funcNam('[*]txt[ulist]txt[/ulist]'));
$this->assertEquals('<ul><li><ul><li><ul><li><ul><li>txt</li></ul></li></ul></li></ul></li></ul>', $bbc->$funcNam('[li][ulist][ulist][ulist]txt'));
$this->assertEquals(
'<ol style="list-style-type:upper-alpha;"><li>ordered item 1, nested list:<ol style="list-style-type:upper-roman;"><li>nested item 1</li><li>nested item 2</li></ol></li><li>ordered item 2</li></ol>',
$bbc->$funcNam('[list=A s=3][li]ordered item 1, nested list:[list=I][li]nested item 1[/li][li]nested item 2[/li][/list][/li][li]ordered item 2[/li][/list]'));
$this->assertEquals(
'<ol style="list-style-type:upper-alpha;"><li>ordered item 1 type A</li><li>ordered item 12 type A</li></ol>',
$bbc->$funcNam('[list=A][li]ordered item 1 type A[/li][li=12]ordered item 12 type A[/li][/list]'));
$this->assertEquals(
'<ol style="list-style-type:lower-alpha;"><li>ordered item 5 type a</li><li>ordered item 6 type a</li></ol>',
$bbc->$funcNam('[list=a s=5][li]ordered item 5 type a[/li][*]ordered item 6 type a[/list]'));
$this->assertEquals(
'<ol style="list-style-type:upper-roman;"><li>ordered item 1 type I</li></ol>',
$bbc->$funcNam('[list=I][*]ordered item 1 type I[/list]'));
$this->assertEquals(
'<ol style="list-style-type:lower-roman;"><li>ordered item 1 type i</li><li>ordered item 4 type i</li></ol>',
$bbc->$funcNam('[list=i][*]ordered item 1 type i[li=4]ordered item 4 type i[/li][/list]'));
$this->assertEquals(
'<ol style="list-style-type:decimal;"><li>ordered item 1</li><li>ordered item 2</li></ol>',
$bbc->$funcNam('[list=1][*]ordered item 1[*]ordered item 2[/list]'));
//Bug #512: [list] in a [list] breaks the first [list]
$this->assertEquals(
'<ol><li> Subject 1<ol><li> First</li><li> Second</li></ol></li><li> Subject 2</li></ol>',
$bbc->$funcNam('[list][*] Subject 1[list][*] First[*] Second[/list][*] Subject 2[/list]')
);
//Bug #1201: [list] output adding extra <li></li>
$this->assertEquals(
'<ol><li>txt</li></ol>',
$bbc->$funcNam('[list][*]txt[/list]')
);
//Bug#6335 Empty item displayed
$this->assertEquals(
'<ol style="list-style-type:decimal;"><li> Item one</li><li> Item two</li><li> Item three</li></ol>',
$bbc->$funcNam('[list=1][*] Item one[*] Item two[*] Item three[/list]'));
}
function linkBBCode($bbc, $funcNam)
{
$this->assertEquals(
'<a href="http://www.test.com/">http://www.test.com/</a>',
$bbc->$funcNam('http://www.test.com/'));
$this->assertEquals(
'<a href="http://www.test.com/">www.test.com</a>',
$bbc->$funcNam('[url]www.test.com[/url]'));
$this->assertEquals(
'<a href="http://www.test.com/testurl">http://www.test.com/testurl</a>',
$bbc->$funcNam('[url]http://www.test.com/testurl[/url]'));
$this->assertEquals(
'<a href="http://www.test.com/">testurl</a>',
$bbc->$funcNam('[url=www.test.com/]testurl[/url]'));
$this->assertEquals(
'<a href="http://www.server.org">server</a>',
$bbc->$funcNam('[url=http://www.server.org t=new]server[/url]'));
$this->assertEquals(
'txt <a href="http://www.test.com/">www.test.com</a> txt',
$bbc->$funcNam('txt www.test.com txt'));
$this->assertEquals(
'txt (<a href="http://www.test.com/">www.test.com</a>) txt',
$bbc->$funcNam('txt (www.test.com) txt'));
$this->assertEquals(
'txt <a href="http://www.test.com/test.php?a=1,2">www.test.com/test.php?a=1,2</a>, txt',
$bbc->$funcNam('txt www.test.com/test.php?a=1,2, txt'));
$this->assertEquals(
'txt <a href="http://www.test.com/">www.test.com</a>, txt',
$bbc->$funcNam('txt www.test.com, txt'));
$this->assertEquals(
'txt <a href="http://www.test.com/">http://www.test.com</a>: txt',
$bbc->$funcNam('txt http://www.test.com: txt'));
$this->assertEquals(
'txt <a href="http://www.test.com/">www.test.com</a>; txt',
$bbc->$funcNam('txt www.test.com; txt'));
//Bug #1755: tags around an url -> mess
$this->assertEquals(
'txt <em><a href="http://www.test.com/">www.test.com</a></em> txt',
$bbc->$funcNam('txt [i]www.test.com[/i] txt'));
//Bug #1512: URL Tags Allow Javascript injection
$this->assertEquals(
'Click here',
$bbc->$funcNam('[url=javascript:location.replace("bad_link");]Click here[/url]'));
$this->assertEquals(
'<a href="http://domain.com/index.php?i=1&amp;j=2">linked text</a>',
$bbc->$funcNam('[url=http://domain.com/index.php?i=1&j=2]linked text[/URL]'));
$this->assertEquals(
'<a href="http://domain.com/index.php?i=1&amp;j=2">linked text</a>',
$bbc->$funcNam('[url=http://domain.com/index.php?i=1&amp;j=2]linked text[/URL]'));
//Bug #5609: BBCodeParser allows XSS
$this->assertEquals(
'<a href="javascript&amp;#058;//%0ASh=alert(%22CouCou%22);window.close();">Alert box with "CouCou"</a>',
$bbc->$funcNam('[url=javascript://%0ASh=alert(%22CouCou%22);window.close();]Alert box with "CouCou"[/url]')
);
/*
//Request #4936: Nested URLs in quotes not handled
$this->assertEquals(
'<q>Quoted text</q>', //?!?!?
$bbc->$funcNam('[quote="[url=http://somewhere.com]URL-Title[/url]"]Quoted text[/quote]')
);
*/
}
function extBBCode($bbc, $funcNam)
{
$this->assertEquals('<h2>txt</h2>', $bbc->$funcNam('[h2]txt[/h2]'));
$this->assertEquals('<span style="color:blue">blue text</span>', $bbc->$funcNam('[color=blue]blue text[/color]'));
$this->assertEquals('<span style="font-size:18pt">the size of this text is 18pt</span>', $bbc->$funcNam('[size=18]the size of this text is 18pt[/size]'));
$this->assertEquals('<span style="font-family:arial">different font type</span>', $bbc->$funcNam('[font=arial]different font type[/font]'));
$this->assertEquals('<div style="text-align:right">yes, you\'re right, this isn\'t on the left</div>', $bbc->$funcNam('[align=right]yes, you\'re right, this isn\'t on the left[/align]'));
$this->assertEquals('he said: <q cite="http://www.server.org/quote.html">i\'m tony montana</q>', $bbc->$funcNam('he said: [quote=http://www.server.org/quote.html]i\'m tony montana[/quote]'));
$this->assertEquals('<code>x + y = 6;</code>', $bbc->$funcNam('[code]x + y = 6;[/code]'));
//Bug #1258: Extra tags rendered with faulty BBCode
$this->assertEquals(
'<span style="font-family:Verdana"><span style="color:red">my name NeverMind!</span></span>',
$bbc->$funcNam('[font=Verdana][color=red]my name NeverMind![/font][/color]')
);
//Bug #1979: Whitespaces in attribute are breaking it
$this->assertEquals(
'<span style="font-family:Comic Sans MS">txt</span>',
$bbc->$funcNam('[font=Comic Sans MS]txt[/font]')
);
//Bug #4844: Arbitrary HTML injection
$this->assertEquals(
'<div style="text-align:foo&quot;&gt;&lt;script&gt;alert(\'JavaScript_Enabled\');&lt;/script&gt;"></div>',
$bbc->$funcNam('[align=foo"><script>alert(\'JavaScript_Enabled\');</script>][/align]')
);
}
/**
* An empty <li> had been included for the first space
*/
function testBug11400()
{
$bbc = new HTML_BBCodeParser(array('filters' => ''));
$bbc->addFilter('Lists');
//this works
$this->assertEquals('<ul><li>one</li><li>two</li></ul>',
$bbc->qparse("[ulist][*]one[*]two[/ulist]")
);
//this not
$this->assertEquals('<ul><li>one</li><li>two</li></ul>',
$bbc->qparse("[ulist] [*]one[*]two[/ulist]")
);
//this not
$this->assertEquals('<ol><li>one</li><li>two</li></ol>',
$bbc->qparse("[list] [*]one[*]two[/list]")
);
}
/**
* img tags didn't like = in url
*/
function testBug11370()
{
$bbc = new HTML_BBCodeParser(array('filters' => ''));
$bbc->addFilter('Images');
$this->assertEquals('<img src="admin.php?fs=image" />',
$bbc->qparse("[img]admin.php?fs=image[/img]")
);
}
}
//Run tests if run from the command line
if (realpath($_SERVER['PHP_SELF']) == __FILE__){
$suite = new PHPUnit_TestSuite('BBCodeParser_TestCase');
$result = PHPUnit::run($suite);
echo $result->toString();
}
?>