diff --git a/parsers/BBCodeParser.ini b/parsers/BBCodeParser.ini
new file mode 100644
index 000000000..83bb44920
--- /dev/null
+++ b/parsers/BBCodeParser.ini
@@ -0,0 +1,24 @@
+[SSHTMLBBCodeParser]
+
+; possible values: single|double
+; use single or double quotes for attributes
+quotestyle = "double"
+
+; possible values: all|nothing|strings
+; quote all attribute values, none, or only the strings
+quotewhat = "all"
+
+; the closing tag character
+close = "]"
+
+; the opening tag character
+open = "["
+
+; possible values: true|false
+; use xml style closing tags for single html tags ( or )
+xmlclose = "true"
+
+; possible values: a comma seperated list of filters
+; comma seperated list of filters to use
+filters = "Basic,Extended,EmailLinks,Images,Links,Lists"
+
diff --git a/parsers/BBCodeParser.php b/parsers/BBCodeParser.php
new file mode 100644
index 000000000..f124ba152
--- /dev/null
+++ b/parsers/BBCodeParser.php
@@ -0,0 +1,112 @@
+ "Bold Text",
+ "Example" => "[b]Bold[/b]"
+ )),
+ new ArrayData(array(
+ "Title" => "Italic Text",
+ "Example" => "[i]Italics[/i]"
+ )),
+ new ArrayData(array(
+ "Title" => "Underlined Text",
+ "Example" => "[u]Underlined[/u]"
+ )),
+ new ArrayData(array(
+ "Title" => "Struck-out Text",
+ "Example" => "[s]Struck-out[/s]"
+ )),
+ new ArrayData(array(
+ "Title" => "Colored text",
+ "Example" => "[color=blue]blue text[/color]"
+ )),
+ new ArrayData(array(
+ "Title" => "Alignment",
+ "Example" => "[align=right]right aligned[/align]"
+ )),
+
+ new ArrayData(array(
+ "Title" => "Website link",
+ "Description" => "Link to another website or URL",
+ "Example" => "[url]http://www.website.com/[/url]"
+ )),
+ new ArrayData(array(
+ "Title" => "Website link",
+ "Description" => "Link to another website or URL",
+ "Example" => "[url=http://www.website.com/]Some website[/url]"
+ )),
+ new ArrayData(array(
+ "Title" => "Email link",
+ "Description" => "Create link to an email address",
+ "Example" => "[email]you@yoursite.com[/email]"
+ )),
+ new ArrayData(array(
+ "Title" => "Email link",
+ "Description" => "Create link to an email address",
+ "Example" => "[email=you@yoursite.com]email me[/email]"
+ )),
+
+ new ArrayData(array(
+ "Title" => "Image",
+ "Description" => "Show an image in your post",
+ "Example" => "[img]http://www.website.com/image.jpg[/img]"
+ )),
+
+ new ArrayData(array(
+ "Title" => "Code Block",
+ "Description" => "Unformatted code block",
+ "Example" => "[code]Code block[/code]"
+ )),
+ new ArrayData(array(
+ "Title" => "Unordered list",
+ "Description" => "Unordered list",
+ "Example" => "[ulist][*]unordered item 1[*]unordered item 2[/ulist]"
+ ))
+
+
+ );
+ }
+
+ function useable_tagsHTML(){
+ $useabletags = "
";
+ foreach($this->usable_tags()->toArray() as $tag){
+ $useabletags = $useabletags."- ".$tag->Example."
";
+ }
+ return $useabletags."
";
+ }
+
+ function parse() {
+ $this->content = str_replace(array('&', '<', '>'), array('&', '<', '>'), $this->content);
+ $this->content = SSHTMLBBCodeParser::staticQparse($this->content);
+ $this->content = "".$this->content."
";
+ $this->content = str_replace("\n\n", "", $this->content);
+ $this->content = str_replace("\n", "
", $this->content);
+ return $this->content;
+ }
+
+}
+?>
\ No newline at end of file
diff --git a/parsers/HTML/BBCodeParser/Filter.php b/parsers/HTML/BBCodeParser/Filter.php
new file mode 100644
index 000000000..ca92de584
--- /dev/null
+++ b/parsers/HTML/BBCodeParser/Filter.php
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/parsers/HTML/BBCodeParser/Filter/Basic.php b/parsers/HTML/BBCodeParser/Filter/Basic.php
new file mode 100644
index 000000000..81c58e01a
--- /dev/null
+++ b/parsers/HTML/BBCodeParser/Filter/Basic.php
@@ -0,0 +1,71 @@
+ |
+// +----------------------------------------------------------------------+
+//
+// $Id: Basic.php,v 1.6 2007/07/02 16:54:25 cweiske Exp $
+//
+
+/**
+* @package SSHTMLBBCodeParser
+* @author Stijn de Reede
+*/
+
+
+require_once 'HTML/BBCodeParser/Filter.php';
+
+
+
+
+class SSHTMLBBCodeParser_Filter_Basic extends SSHTMLBBCodeParser_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())
+ );
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/parsers/HTML/BBCodeParser/Filter/EmailLinks.php b/parsers/HTML/BBCodeParser/Filter/EmailLinks.php
new file mode 100644
index 000000000..1c5f9b6cf
--- /dev/null
+++ b/parsers/HTML/BBCodeParser/Filter/EmailLinks.php
@@ -0,0 +1,85 @@
+ |
+// +----------------------------------------------------------------------+
+//
+// $Id: Email.php,v 1.5 2007/07/02 16:54:25 cweiske Exp $
+//
+
+/**
+* @package SSHTMLBBCodeParser
+* @author Stijn de Reede
+*/
+
+
+require_once 'HTML/BBCodeParser/Filter.php';
+
+
+
+
+class SSHTMLBBCodeParser_Filter_EmailLinks extends SSHTMLBBCodeParser_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
+ */
+ function _preparse()
+ {
+ $options = SSHTMLBBCodeParser::getStaticProperty('SSHTMLBBCodeParser','_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);
+ }
+
+
+}
+
+
+?>
diff --git a/parsers/HTML/BBCodeParser/Filter/Extended.php b/parsers/HTML/BBCodeParser/Filter/Extended.php
new file mode 100644
index 000000000..7482c7287
--- /dev/null
+++ b/parsers/HTML/BBCodeParser/Filter/Extended.php
@@ -0,0 +1,97 @@
+ |
+// +----------------------------------------------------------------------+
+//
+// $Id: Extended.php,v 1.3 2007/07/02 16:54:25 cweiske Exp $
+//
+
+/**
+* @package SSHTMLBBCodeParser
+* @author Stijn de Reede
+*/
+
+
+require_once 'HTML/BBCodeParser/Filter.php';
+
+
+
+
+class SSHTMLBBCodeParser_Filter_Extended extends SSHTMLBBCodeParser_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())
+
+ );
+
+
+}
+
+?>
\ No newline at end of file
diff --git a/parsers/HTML/BBCodeParser/Filter/Images.php b/parsers/HTML/BBCodeParser/Filter/Images.php
new file mode 100644
index 000000000..0abc82e9d
--- /dev/null
+++ b/parsers/HTML/BBCodeParser/Filter/Images.php
@@ -0,0 +1,79 @@
+ |
+// +----------------------------------------------------------------------+
+//
+// $Id: Images.php,v 1.8 2007/07/02 17:44:47 cweiske Exp $
+//
+
+/**
+* @package SSHTMLBBCodeParser
+* @author Stijn de Reede
+*/
+require_once 'HTML/BBCodeParser/Filter.php';
+
+class SSHTMLBBCodeParser_Filter_Images extends SSHTMLBBCodeParser_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
+ */
+ function _preparse()
+ {
+ $options = SSHTMLBBCodeParser::getStaticProperty('SSHTMLBBCodeParser','_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);
+ }
+}
\ No newline at end of file
diff --git a/parsers/HTML/BBCodeParser/Filter/Links.php b/parsers/HTML/BBCodeParser/Filter/Links.php
new file mode 100644
index 000000000..c40898f03
--- /dev/null
+++ b/parsers/HTML/BBCodeParser/Filter/Links.php
@@ -0,0 +1,205 @@
+ |
+// +----------------------------------------------------------------------+
+//
+// $Id: Links.php,v 1.12 2007/07/02 18:26:17 cweiske Exp $
+//
+
+/**
+* @package SSHTMLBBCodeParser
+* @author Stijn de Reede
+*/
+require_once 'HTML/BBCodeParser/Filter.php';
+
+/**
+ *
+ */
+class SSHTMLBBCodeParser_Filter_Links extends SSHTMLBBCodeParser_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
+ * @author Seth Price
+ */
+ function _preparse()
+ {
+ $options = SSHTMLBBCodeParser::getStaticProperty('SSHTMLBBCodeParser', '_options');
+ $o = $options['open'];
+ $c = $options['close'];
+ $oe = $options['open_esc'];
+ $ce = $options['close_esc'];
+
+ $schemes = implode('|', $this->_allowedSchemes);
+
+ $pattern = array( "/(?_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
+ * @author Lorenzo Alberton
+ */
+ function smarterPPLinkExpand($matches)
+ {
+ //echo '
';var_dump($matches);echo '
';
+ $options = SSHTMLBBCodeParser::getStaticProperty('SSHTMLBBCodeParser','_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];
+ }
+
+ if(!BBCodeParser::autolinkUrls()){
+ 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
+ */
+ function smarterPPLink($matches)
+ {
+ $options = SSHTMLBBCodeParser::getStaticProperty('SSHTMLBBCodeParser','_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];
+ }
+}
+?>
\ No newline at end of file
diff --git a/parsers/HTML/BBCodeParser/Filter/Lists.php b/parsers/HTML/BBCodeParser/Filter/Lists.php
new file mode 100644
index 000000000..053ddb39c
--- /dev/null
+++ b/parsers/HTML/BBCodeParser/Filter/Lists.php
@@ -0,0 +1,108 @@
+ |
+// +----------------------------------------------------------------------+
+//
+// $Id: Lists.php,v 1.5 2007/07/02 16:54:25 cweiske Exp $
+//
+
+
+/**
+* @package SSHTMLBBCodeParser
+* @author Stijn de Reede
+*/
+
+require_once 'HTML/BBCodeParser/Filter.php';
+
+/**
+ *
+ */
+class SSHTMLBBCodeParser_Filter_Lists extends SSHTMLBBCodeParser_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 , Seth Price
+ */
+ function _preparse()
+ {
+ $options = SSHTMLBBCodeParser::getStaticProperty('SSHTMLBBCodeParser','_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);
+ }
+}
+
+
+?>
\ No newline at end of file
diff --git a/parsers/HTML/HTMLBBCodeParser.php b/parsers/HTML/HTMLBBCodeParser.php
new file mode 100644
index 000000000..80ee3f126
--- /dev/null
+++ b/parsers/HTML/HTMLBBCodeParser.php
@@ -0,0 +1,873 @@
+ |
+// +----------------------------------------------------------------------+
+//
+// $Id: BBCodeParser.php,v 1.17 2007/07/02 18:46:30 cweiske Exp $
+//
+// Modified by SilverStripe www.silverstripe.com
+
+/**
+* @package SSHTMLBBCodeParser
+* @author Stijn de Reede , SilverStripe
+*
+*
+* 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 SSHTMLBBCodeParser();
+* $parser->setText('normal [b]bold[/b] and normal again');
+* $parser->parse();
+* echo $parser->getParsed();
+* or:
+* $parser = new SSHTMLBBCodeParser();
+* echo $parser->qparse('normal [b]bold[/b] and normal again');
+* or:
+* echo SSHTMLBBCodeParser::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('SSHTMLBBCodeParser', '_options');
+* $options = $config['SSHTMLBBCodeParser'];
+* unset($options);
+*
+*
+*/
+
+class SSHTMLBBCodeParser
+{
+ /**
+ * 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
+ */
+ function SSHTMLBBCodeParser($options = array())
+ {
+ // set the already set options
+ $baseoptions = &SSHTMLBBCodeParser::getStaticProperty('SSHTMLBBCodeParser', '_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, 'SSHTMLBBCodeParser_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
+ */
+ function setOption($name, $value)
+ {
+ $this->_options[$name] = $value;
+ }
+
+ /**
+ * Add a new filter
+ *
+ * @param string filter
+ * @author Lorenzo Alberton
+ */
+ function addFilter($filter)
+ {
+
+ $filter = ucfirst($filter);
+ if (!array_key_exists($filter, $this->_filters)) {
+ $class = 'SSHTMLBBCodeParser_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
+ */
+ 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
+ */
+ 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
+ */
+ 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, 'SSHTMLBBCodeParser')) {
+ 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
+ */
+ 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, 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
+ */
+ 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 , Seth Price
+ */
+ 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
+ */
+ 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
+ */
+ 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
+ */
+ 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
+ */
+ 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:", $v);
+ $v = htmlspecialchars($v);
+ $v = str_replace('&', '&', $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
+ */
+ 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
+ */
+ 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
+ */
+ 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
+ */
+ 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
+ */
+ 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
+ */
+ 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
+ */
+ function staticQparse($str)
+ {
+ $p = new SSHTMLBBCodeParser();
+ $str = $p->qparse($str);
+ unset($p);
+ return $str;
+ }
+}
+?>
\ No newline at end of file
diff --git a/parsers/TextParser.php b/parsers/TextParser.php
new file mode 100644
index 000000000..f18ee002c
--- /dev/null
+++ b/parsers/TextParser.php
@@ -0,0 +1,51 @@
+content and parse it however you want. For an example
+ * of the implementation, @see BBCodeParser.
+ *
+ * Your sub-class will be initialized with a string of text, then parse() will be called.
+ * parse() should (after processing) return the formatted string.
+ *
+ * Note: $this->content will have NO conversions applied to it.
+ * You should run Covert::raw2xml or whatever is appropriate before using it.
+ *
+ * Optionally (but recommended), is creating a static usable_tags method,
+ * which will return a DataObjectSet of all the usable tags that can be parsed.
+ * This will (mostly) be used to create helper blocks - telling users what things will be parsed.
+ * Again, @see BBCodeParser for an example of the syntax
+ *
+ * TODO Define a proper syntax for (or refactor) usable_tags that can be extended as needed.
+ */
+abstract class TextParser extends Object {
+ protected $content;
+
+ /**
+ * Creates a new TextParser object.
+ *
+ * @param string $content The contents of the dbfield
+ */
+ function __construct($content = "") {
+ $this->content = $content;
+ }
+
+ /**
+ * Convenience method, shouldn't really be used, but it's here if you want it
+ */
+ function setContent($content = "") {
+ $this->content = $content;
+ }
+
+ /**
+ * Define your own parse method to parse $this->content appropriately.
+ * See the class doc-block for more implementation details.
+ */
+ abstract function parse();
+}
+?>
\ No newline at end of file