diff --git a/parsers/BBCodeParser.php b/parsers/BBCodeParser.php new file mode 100644 index 000000000..cf8b3452a --- /dev/null +++ b/parsers/BBCodeParser.php @@ -0,0 +1,204 @@ + "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" => "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" => "HTML Code Block", + "Description" => "HTML-formatted code block", + "Example" => "[html]HTML code block[/html]" + )), + new ArrayData(array( + "Title" => "HTML Code Block", + "Description" => "HTML-formatted code block", + "Example" => "[code html]HTML code block[/code]" + )), + new ArrayData(array( + "Title" => "PHP Code Block", + "Description" => "PHP-formatted code block", + "Example" => "[php]PHP code block[/php]" + )), + new ArrayData(array( + "Title" => "PHP Code Block", + "Description" => "PHP-formatted code block", + "Example" => "[code php]PHP code block[/code]" + )) + + ); + } + + function useable_tagsHTML(){ + $useabletags = ""; + } + + /** + * Meat of the class - finds tags to parse and offloads the parsing to member methods. + * + * parse() will look at various [square-bracketed] tags and replace them. + * Complicated replacements are handled via private member methods. + * Simple stuff is done inline inside the preg_replace to keep overhead low. + */ + function parse() { + $this->content = str_replace(array('&', '<', '>'), array('&', '<', '>'), $this->content); + + // Parse [code X] and base [code] tags + $this->content = preg_replace("#\[code (.+?)\](.+?)\[/code\]#ies", "\$this->parseCode('\\2', '\\1')", $this->content); + $this->content = preg_replace("#\[code\](.+?)\[/code\]#ies", "\$this->parseCode('\\1')", $this->content); + + // Parse [html] and [php] tags (Shorthand [code html] and [code php] respectively) + $this->content = preg_replace("#\[html\](.+?)\[/html\]#ies", "\$this->parseCode('\\1', 'html')", $this->content); + $this->content = preg_replace("#\[php\](.+?)\[/php\]#ies", "\$this->parseCode('\\1', 'php')", $this->content); + + // Simple HTML tags (bold, italic, underline, strike-through) - No need to call member methods for these + $this->content = preg_replace("#\[b\](.+?)\[/b\]#is", "\\1", $this->content); + $this->content = preg_replace("#\[i\](.+?)\[/i\]#is", "\\1", $this->content); + $this->content = preg_replace("#\[u\](.+?)\[/u\]#is", "\\1", $this->content); + $this->content = preg_replace("#\[s\](.+?)\[/s\]#is", "\\1", $this->content); + + // Email tags ([email]name@domain.tld[/email] and [email=name@domain.tld]email me![/email]) + $this->content = preg_replace("#\[email\](\S+?)\[/email\]#i", "\\1", $this->content); + $this->content = preg_replace("#\[email\s*=\s*([\.\w\-]+\@[\.\w\-]+\.[\w\-]+)\s*\](.*?)\[\/email\]#i", "\\2", $this->content); + + // URL tags ([url]someurl.tld[/url] and [url=someurl.tld]visit someurl.tld![/url]) + $this->content = preg_replace("#\[url\](\S+?)\[/url\]#ie", "\$this->parseURL('\\1')", $this->content); + $this->content = preg_replace("#\[url\s*=\s*(\S+?)\s*\](.*?)\[\/url\]#ie", "\$this->parseURL('\\1', '\\2')", $this->content); + $this->content = preg_replace("#\[url\s*=\s*\"\;\s*(\S+?)\s*\"\;\s*\](.*?)\[\/url\]#ie", "\$this->parseURL('\\1', '\\2')", $this->content); + + // Img tags ([img]link/to/some/image.ext[/img]) + $this->content = preg_replace("#\[img\](.+?)\[/img\]#ie", "\$this->parseImg('\\1')", $this->content); + + $this->content = str_replace("\n", "
", $this->content); + return $this->content; + } + + /** + * Parses a [code] tag + */ + private function parseCode($text, $type="text") { + + // Wrap $text if required + $text = wordwrap($text, 80); + + // Add opening tags if required for PHP + // Assumes that if there is no opening tag, that there is also no closing tag + if($type == 'php') { + if(strpos($text, '<?') === false) { + $text = "<?php\n$text\n?>"; + } + } + + switch($type) { + case "php": + case "html": + return "

".strtoupper($type).":

".highlight_string(Convert::js2raw(Convert::xml2raw($text)), true)."
"; + break; + case "text": + default: + return "

".strtoupper($type).":

".wordwrap($text)."
"; + break; + } + } + + /** + * Parses a [url] tag + */ + private function parseURL($link, $text = "") { + // If text isn't defined, make it the same as the link (for the [url][/url] tag) + if(!$text) $text = $link; + + // Remove ability to influence Javascript + $link = preg_replace("/javascript:/i", "java script", $link); + + // Ensure the URL starts with protocol:// + // If we don't, assume http:// + if (!preg_match("#^(\S+?)://#", $link)) { + $link = "http://$link"; + } + + // Rewrite *really* long URLs to beginning...end, but only where the URL is the same as the title + // This will make the title 43 characters long if the link is >100 characters + if(strlen($link) > 100 && $link == $text) { + $text = substr($link, 0, 20)."...".substr($link, -20); + } + + $text = wordwrap($text, 78, " ", true); + return "$text"; + } + + /** + * Parses a [img] tag + * + * TODO Set a maximum-images-per-post flag and check it + */ + private function parseImg($url) { + // If we don't let them put this image tag in, just show the unchanged [img] tag so it's sorta obvious that it didn't work + $noChange = "[img]".$url."[/img]"; + + // Disallow 'dynamic' images (dynamic being images with '?' in the url :P) + // TODO Make this more robust, possibly allow it to be turned on/off via the CMS + if(preg_match("/[?&;]/", $url) || preg_match("/javascript/i", $url)) return $noChange; + + // Replace spaces with %20 and return the image tag + return "\"\""; + } +} +?> \ 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