From 0ebcf1e2781ed12942888735d85926851fa019df Mon Sep 17 00:00:00 2001 From: rurban Date: Mon, 7 Jun 2010 08:12:29 +0000 Subject: [PATCH] =?utf8?q?-=20Atom=20Parser=20and=20Feed=20Plugin=20=20=20?= =?utf8?q?with=20unit=20tests=20by=20S=C3=A9bastien=20Le=20Callonnec=20=20?= =?utf8?q?=20patches=20ID=203012033=20-=20HtmlElement5.php=20split=20up=20?= =?utf8?q?for=20php-5.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit git-svn-id: svn://svn.code.sf.net/p/phpwiki/code/trunk@7466 96ab9672-09ca-45d6-a79d-3d69d39ca109 --- lib/AtomParser.php | 257 +++++++++++ lib/HtmlElement.php | 79 ++-- lib/HtmlElement5.php | 593 +++++++++++++++++++++++++ lib/IniConfig.php | 18 +- lib/XmlParser.php | 6 +- lib/plugin/AtomFeed.php | 85 ++++ tests/unit/lib/AtomParserTest.php | 220 +++++++++ tests/unit/lib/plugin/AtomFeedTest.php | 80 ++++ tests/unit/lib/plugin/atom-example.xml | 94 ++++ tests/unit/test.php | 1 + 10 files changed, 1394 insertions(+), 39 deletions(-) create mode 100644 lib/AtomParser.php create mode 100644 lib/HtmlElement5.php create mode 100644 lib/plugin/AtomFeed.php create mode 100644 tests/unit/lib/AtomParserTest.php create mode 100644 tests/unit/lib/plugin/AtomFeedTest.php create mode 100644 tests/unit/lib/plugin/atom-example.xml diff --git a/lib/AtomParser.php b/lib/AtomParser.php new file mode 100644 index 000000000..87dba6ce0 --- /dev/null +++ b/lib/AtomParser.php @@ -0,0 +1,257 @@ +inside_entry = true; + } elseif ($this->inside_content) { + $this->content .= $this->serialize_tag(strtolower($name), $attrs); + } elseif ($name == "CONTENT") { + $this->inside_content = true; + } + } + + function tag_close($parser, $name, $attrs='') { + if ($name == "AUTHOR") { + $an_author = $this->trim_data(array( + "name" => $this->name, + "email" => $this->email, + "uri" => $this->uri + )); + if ($this->inside_entry) { + $this->authors[] = $an_author; + } else { + $this->feed_authors[] = $an_author; + } + $this->name = ''; + $this->email = ''; + $this->uri = ''; + } elseif ($name == "FEED") { + $this->feed[] = $this->trim_data(array( + "id" => $this->feed_id, + "title" => $this->feed_title, + "links" => $this->feed_links, + "subtitle" => $this->feed_subtitle, + "updated" => $this->feed_updated, + "generator" => $this->generator, + "icon" => $this->icon, + "rights" => $this->rights, + "logo" => $this->logo, + "authors" => $this->feed_authors, + "contributors" => $this->feed_contributors + )); + $this->feed_title = ''; + $this->feed_id = ''; + $this->feed_links = array(); + $this->feed_subtitle = ''; + $this->feed_updated = ''; + $this->feed_authors = array(); + $this->feed_contributors = array(); + $this->generator = ''; + $this->icon = ''; + $this->rights = ''; + $this->logo = ''; + } elseif ($name == "ENTRY") { + $this->entries[] = $this->trim_data(array( + "id" => $this->id, + "title" => $this->title, + "updated" => $this->updated, + "links" => $this->links, + "published" => $this->published, + "content" => $this->content, + "summary" => $this->summary, + "authors" => $this->authors, + "contributors" => $this->contributors + )); + $this->id = ''; + $this->title = ''; + $this->updated = ''; + $this->links = ''; + $this->published = ''; + $this->content = ''; + $this->authors = array(); + $this->contributors = array(); + $this->inside_entry = false; + } elseif ($name == "CONTENT") { + $this->inside_content = false; + } elseif ($name == "CONTRIBUTOR") { + $a_contributor = $this->trim_data(array( + "name" => $this->name, + "email" => $this->email + )); + if ($this->inside_entry) { + $this->contributors[] = $a_contributor; + } else { + $this->feed_contributors[] = $a_contributor; + } + $this->name = ''; + $this->email = ''; + } elseif ($this->inside_content) { + $this->content .= ""; + } + } + + function cdata($parser, $data) { + global $current_tag, $current_attrs; + + if ($this->inside_content) { + $this->content .= $data; + } else { + switch ($current_tag) { + case "ID": + if ($this->inside_entry) + $this->id .= $data; + else + $this->feed_id .= $data; + break; + case "LINK": + $a_link = array(); + foreach ($current_attrs as $k => $v) { + $a_link[strtolower($k)] = $v; + } + if ($this->inside_entry) { + $this->links[] = $a_link; + } else { + $this->feed_links[] = $a_link; + } + break; + case "NAME": + $this->name .= $data; + break; + case "EMAIL": + $this->email .= $data; + break; + case "TITLE" : + if ($this->inside_entry) + $this->title .= $data; + else + $this->feed_title .= $data; + break; + case "UPDATED": + if ($this->inside_entry) + $this->updated .= $data; + else + $this->feed_updated .= $data; + break; + case "SUBTITLE": + $this->feed_subtitle .= $data; + break; + case "PUBLISHED": + $this->published .= $data; + break; + case "SUMMARY": + $this->summary .= $data; + break; + case "URI": + $this->uri .= $data; + break; + case "GENERATOR": + $this->generator .= $data; + break; + case "ICON": + $this->icon .= $data; + break; + case "LOGO": + $this->logo .= $data; + break; + case "RIGHTS": + $this->rights .= $data; + break; + } + } + } + + private function trim_data($array) { + return array_map(array("self", "trim_element"), $array); + } + + private function trim_element($element) { + if (is_array($element)) { + return $this->trim_data($element); + } elseif (is_string($element)) { + return trim($element); + } + } + + private function serialize_tag($tag_name, $attributes) { + $tag = "<" . $tag_name; + foreach ($attributes as $k => $v) { + $tag .= " " . strtolower($k). "=\"$v\""; + } + $tag .= ">"; + return $tag; + } +} +?> diff --git a/lib/HtmlElement.php b/lib/HtmlElement.php index 0a5cc3cdd..cd26d74c7 100644 --- a/lib/HtmlElement.php +++ b/lib/HtmlElement.php @@ -87,12 +87,17 @@ class HtmlElement extends XmlElement if (!empty($this->_attr['title'])) { if (preg_match("/\[(alt-)?(.)\]$/", $this->_attr['title'], $m)) { - $this->_attr['title'] = preg_replace("/\[(alt-)?(.)\]$/", "[".$WikiTheme->tooltipAccessKeyPrefix()."-\\2]", $this->_attr['title']); + $this->_attr['title'] = preg_replace + ("/\[(alt-)?(.)\]$/", + "[".$WikiTheme->tooltipAccessKeyPrefix()."-\\2]", + $this->_attr['title']); } else { - $this->_attr['title'] .= " [".$WikiTheme->tooltipAccessKeyPrefix()."-$key]"; + $this->_attr['title'] .= + " [".$WikiTheme->tooltipAccessKeyPrefix()."-$key]"; } } else { - $this->_attr['title'] = "[".$WikiTheme->tooltipAccessKeyPrefix()."-$key]"; + $this->_attr['title'] = + "[".$WikiTheme->tooltipAccessKeyPrefix()."-$key]"; } } @@ -142,38 +147,42 @@ class HTML extends HtmlElement { // // Shell script to generate the following static methods: - // - // #!/bin/sh - // function mkfuncs () { - // for tag in "$@" - // do - // echo " function $tag (/*...*/) {" - // echo " \$el = new HtmlElement('$tag');" - // echo " return \$el->_init2(func_get_args());" - // echo " }" - // done - // } - // d=' - // /****************************************/' - // mkfuncs link meta style script noscript - // echo "$d" - // mkfuncs a img br span - // echo "$d" - // mkfuncs h1 h2 h3 h4 h5 h6 - // echo "$d" - // mkfuncs hr div p pre blockquote - // echo "$d" - // mkfuncs em strong small - // echo "$d" - // mkfuncs tt u sup sub - // echo "$d" - // mkfuncs ul ol dl li dt dd - // echo "$d" - // mkfuncs table caption thead tbody tfoot tr td th colgroup col - // echo "$d" - // mkfuncs form input option select textarea - // echo "$d" - // mkfuncs area map frame frameset iframe nobody +/* + +#!/bin/sh +mkfuncs () { + for tag in "$@" + do + echo " public static function $tag (/*...*/) {" + echo " \$el = new HtmlElement('$tag');" + echo " return \$el->_init2(func_get_args());" + echo " }" + done +} +d=' + /****************************************/' +mkfuncs link meta style script noscript +echo "$d" +mkfuncs a img br span +echo "$d" +mkfuncs h1 h2 h3 h4 h5 h6 +echo "$d" +mkfuncs hr div p pre blockquote +echo "$d" +mkfuncs em strong small +echo "$d" +mkfuncs tt u sup sub +echo "$d" +mkfuncs ul ol dl li dt dd +echo "$d" +mkfuncs table caption thead tbody tfoot tr td th colgroup col +echo "$d" +mkfuncs form input option select textarea label fieldset legend +echo "$d" +mkfuncs area map frame frameset iframe nobody object embed param +echo "$d" +mkfuncs video +*/ function link (/*...*/) { $el = new HtmlElement('link'); diff --git a/lib/HtmlElement5.php b/lib/HtmlElement5.php new file mode 100644 index 000000000..898491224 --- /dev/null +++ b/lib/HtmlElement5.php @@ -0,0 +1,593 @@ + 'HTML::div(...)')) + */ +if (!class_exists("XmlElement")) + require_once(dirname(__FILE__)."/XmlElement.php"); +if (class_exists("HtmlElement")) + return; + +/** + * An XML element. + */ +//apd_set_session_trace(35); + +class HtmlElement extends XmlElement +{ + function __construct ($tagname /* , $attr_or_content , ...*/) { + $this->_init(func_get_args()); + $this->_properties = HTML::getTagProperties($tagname); + } + + function _init ($args) { + if (!is_array($args)) + $args = func_get_args(); + + assert(count($args) >= 1); + assert(is_string($args[0])); + $this->_tag = array_shift($args); + + if ($args && is_array($args[0])) + $this->_attr = array_shift($args); + else { + $this->_attr = array(); + if ($args && $args[0] === false) + array_shift($args); + } + $this->setContent($args); + $this->_properties = HTML::getTagProperties($this->_tag); + } + + /** + * @access protected + * This is used by the static factory methods is class HTML. + */ + function _init2 ($args) { + if ($args) { + if (is_array($args[0])) + $this->_attr = array_shift($args); + elseif ($args[0] === false) + array_shift($args); + } + + if (count($args) == 1 && is_array($args[0])) + $args = $args[0]; + $this->_content = $args; + return $this; + } + + /** Add a "tooltip" to an element. + * + * @param $tooltip_text string The tooltip text. + */ + function addTooltip ($tooltip_text, $accesskey = null) { + $this->setAttr('title', $tooltip_text); + if ($accesskey) $this->setAccesskey($accesskey); + + // FIXME: this should be initialized from title by an onLoad() function. + // (though, that may not be possible.) + $qtooltip = str_replace("'", "\\'", $tooltip_text); + $this->setAttr('onmouseover', + sprintf('window.status="%s"; return true;', + addslashes($tooltip_text))); + $this->setAttr('onmouseout', "window.status='';return true;"); + } + + function setAccesskey ($key) { + global $WikiTheme; + if (strlen($key) != 1) return; + $this->setAttr("accesskey", $key); + + if (!empty($this->_attr['title'])) { + if (preg_match("/\[(alt-)?(.)\]$/", $this->_attr['title'], $m)) + { + $this->_attr['title'] = preg_replace + ("/\[(alt-)?(.)\]$/", + "[".$WikiTheme->tooltipAccessKeyPrefix()."-\\2]", + $this->_attr['title']); + } else { + $this->_attr['title'] .= + " [".$WikiTheme->tooltipAccessKeyPrefix()."-$key]"; + } + } else { + $this->_attr['title'] = + "[".$WikiTheme->tooltipAccessKeyPrefix()."-$key]"; + } + } + + function emptyTag () { + if (($this->_properties & HTMLTAG_EMPTY) == 0) + return $this->startTag() . "_tag>"; + + return substr($this->startTag(), 0, -1) . " />"; + } + + function hasInlineContent () { + return ($this->_properties & HTMLTAG_ACCEPTS_INLINE) != 0; + } + + function isInlineElement () { + return ($this->_properties & HTMLTAG_INLINE) != 0; + } +}; + +function HTML (/* $content, ... */) { + return new XmlContent(func_get_args()); +} + +class HTML extends HtmlElement { + public static function raw ($html_text) { + return new RawXml($html_text); + } + + function getTagProperties($tag) { + $props = &$GLOBALS['HTML_TagProperties']; + return isset($props[$tag]) ? $props[$tag] : 0; + } + + function _setTagProperty($prop_flag, $tags) { + $props = &$GLOBALS['HTML_TagProperties']; + if (is_string($tags)) + $tags = preg_split('/\s+/', $tags); + foreach ($tags as $tag) { + $tag = trim($tag); + if ($tag) + if (isset($props[$tag])) + $props[$tag] |= $prop_flag; + else + $props[$tag] = $prop_flag; + } + } + + // + // Shell script to generate the following static methods: +/* + +#!/bin/sh +mkfuncs () { + for tag in "$@" + do + echo " public static function $tag (/*...*/) {" + echo " \$el = new HtmlElement('$tag');" + echo " return \$el->_init2(func_get_args());" + echo " }" + done +} +d=' + /****************************************/' +mkfuncs link meta style script noscript +echo "$d" +mkfuncs a img br span +echo "$d" +mkfuncs h1 h2 h3 h4 h5 h6 +echo "$d" +mkfuncs hr div p pre blockquote +echo "$d" +mkfuncs em strong small +echo "$d" +mkfuncs tt u sup sub +echo "$d" +mkfuncs ul ol dl li dt dd +echo "$d" +mkfuncs table caption thead tbody tfoot tr td th colgroup col +echo "$d" +mkfuncs form input option select textarea label fieldset legend +echo "$d" +mkfuncs area map frame frameset iframe nobody object embed param +echo "$d" +mkfuncs video +*/ + + public static function link (/*...*/) { + $el = new HtmlElement('link'); + return $el->_init2(func_get_args()); + } + public static function meta (/*...*/) { + $el = new HtmlElement('meta'); + return $el->_init2(func_get_args()); + } + public static function style (/*...*/) { + $el = new HtmlElement('style'); + return $el->_init2(func_get_args()); + } + public static function script (/*...*/) { + $el = new HtmlElement('script'); + return $el->_init2(func_get_args()); + } + public static function noscript (/*...*/) { + $el = new HtmlElement('noscript'); + return $el->_init2(func_get_args()); + } + + /****************************************/ + public static function a (/*...*/) { + $el = new HtmlElement('a'); + return $el->_init2(func_get_args()); + } + public static function img (/*...*/) { + $el = new HtmlElement('img'); + return $el->_init2(func_get_args()); + } + public static function br (/*...*/) { + $el = new HtmlElement('br'); + return $el->_init2(func_get_args()); + } + public static function span (/*...*/) { + $el = new HtmlElement('span'); + return $el->_init2(func_get_args()); + } + + /****************************************/ + public static function h1 (/*...*/) { + $el = new HtmlElement('h1'); + return $el->_init2(func_get_args()); + } + public static function h2 (/*...*/) { + $el = new HtmlElement('h2'); + return $el->_init2(func_get_args()); + } + public static function h3 (/*...*/) { + $el = new HtmlElement('h3'); + return $el->_init2(func_get_args()); + } + public static function h4 (/*...*/) { + $el = new HtmlElement('h4'); + return $el->_init2(func_get_args()); + } + public static function h5 (/*...*/) { + $el = new HtmlElement('h5'); + return $el->_init2(func_get_args()); + } + public static function h6 (/*...*/) { + $el = new HtmlElement('h6'); + return $el->_init2(func_get_args()); + } + + /****************************************/ + public static function hr (/*...*/) { + $el = new HtmlElement('hr'); + return $el->_init2(func_get_args()); + } + public static function div (/*...*/) { + $el = new HtmlElement('div'); + return $el->_init2(func_get_args()); + } + public static function p (/*...*/) { + $el = new HtmlElement('p'); + return $el->_init2(func_get_args()); + } + public static function pre (/*...*/) { + $el = new HtmlElement('pre'); + return $el->_init2(func_get_args()); + } + public static function blockquote (/*...*/) { + $el = new HtmlElement('blockquote'); + return $el->_init2(func_get_args()); + } + + /****************************************/ + public static function em (/*...*/) { + $el = new HtmlElement('em'); + return $el->_init2(func_get_args()); + } + public static function strong (/*...*/) { + $el = new HtmlElement('strong'); + return $el->_init2(func_get_args()); + } + public static function small (/*...*/) { + $el = new HtmlElement('small'); + return $el->_init2(func_get_args()); + } + + /****************************************/ + public static function tt (/*...*/) { + $el = new HtmlElement('tt'); + return $el->_init2(func_get_args()); + } + public static function u (/*...*/) { + $el = new HtmlElement('u'); + return $el->_init2(func_get_args()); + } + public static function sup (/*...*/) { + $el = new HtmlElement('sup'); + return $el->_init2(func_get_args()); + } + public static function sub (/*...*/) { + $el = new HtmlElement('sub'); + return $el->_init2(func_get_args()); + } + + /****************************************/ + public static function ul (/*...*/) { + $el = new HtmlElement('ul'); + return $el->_init2(func_get_args()); + } + public static function ol (/*...*/) { + $el = new HtmlElement('ol'); + return $el->_init2(func_get_args()); + } + public static function dl (/*...*/) { + $el = new HtmlElement('dl'); + return $el->_init2(func_get_args()); + } + public static function li (/*...*/) { + $el = new HtmlElement('li'); + return $el->_init2(func_get_args()); + } + public static function dt (/*...*/) { + $el = new HtmlElement('dt'); + return $el->_init2(func_get_args()); + } + public static function dd (/*...*/) { + $el = new HtmlElement('dd'); + return $el->_init2(func_get_args()); + } + + /****************************************/ + public static function table (/*...*/) { + $el = new HtmlElement('table'); + return $el->_init2(func_get_args()); + } + public static function caption (/*...*/) { + $el = new HtmlElement('caption'); + return $el->_init2(func_get_args()); + } + public static function thead (/*...*/) { + $el = new HtmlElement('thead'); + return $el->_init2(func_get_args()); + } + public static function tbody (/*...*/) { + $el = new HtmlElement('tbody'); + return $el->_init2(func_get_args()); + } + public static function tfoot (/*...*/) { + $el = new HtmlElement('tfoot'); + return $el->_init2(func_get_args()); + } + public static function tr (/*...*/) { + $el = new HtmlElement('tr'); + return $el->_init2(func_get_args()); + } + public static function td (/*...*/) { + $el = new HtmlElement('td'); + return $el->_init2(func_get_args()); + } + public static function th (/*...*/) { + $el = new HtmlElement('th'); + return $el->_init2(func_get_args()); + } + public static function colgroup (/*...*/) { + $el = new HtmlElement('colgroup'); + return $el->_init2(func_get_args()); + } + public static function col (/*...*/) { + $el = new HtmlElement('col'); + return $el->_init2(func_get_args()); + } + + /****************************************/ + public static function form (/*...*/) { + $el = new HtmlElement('form'); + return $el->_init2(func_get_args()); + } + public static function input (/*...*/) { + $el = new HtmlElement('input'); + return $el->_init2(func_get_args()); + } + public static function button (/*...*/) { + $el = new HtmlElement('button'); + return $el->_init2(func_get_args()); + } + public static function option (/*...*/) { + $el = new HtmlElement('option'); + return $el->_init2(func_get_args()); + } + public static function select (/*...*/) { + $el = new HtmlElement('select'); + return $el->_init2(func_get_args()); + } + public static function textarea (/*...*/) { + $el = new HtmlElement('textarea'); + return $el->_init2(func_get_args()); + } + public static function label (/*...*/) { + $el = new HtmlElement('label'); + return $el->_init2(func_get_args()); + } + + /****************************************/ + public static function area (/*...*/) { + $el = new HtmlElement('area'); + return $el->_init2(func_get_args()); + } + public static function map (/*...*/) { + $el = new HtmlElement('map'); + return $el->_init2(func_get_args()); + } + public static function frame (/*...*/) { + $el = new HtmlElement('frame'); + return $el->_init2(func_get_args()); + } + public static function frameset (/*...*/) { + $el = new HtmlElement('frameset'); + return $el->_init2(func_get_args()); + } + public static function iframe (/*...*/) { + $el = new HtmlElement('iframe'); + return $el->_init2(func_get_args()); + } + public static function nobody (/*...*/) { + $el = new HtmlElement('nobody'); + return $el->_init2(func_get_args()); + } + public static function object (/*...*/) { + $el = new HtmlElement('object'); + return $el->_init2(func_get_args()); + } + public static function embed (/*...*/) { + $el = new HtmlElement('embed'); + return $el->_init2(func_get_args()); + } + public static function param (/*...*/) { + $el = new HtmlElement('param'); + return $el->_init2(func_get_args()); + } + public static function fieldset (/*...*/) { + $el = new HtmlElement('fieldset'); + return $el->_init2(func_get_args()); + } + public static function legend (/*...*/) { + $el = new HtmlElement('legend'); + return $el->_init2(func_get_args()); + } + + /****************************************/ + public static function video (/*...*/) { + $el = new HtmlElement('video'); + return $el->_init2(func_get_args()); + } +} + +define('HTMLTAG_EMPTY', 1); +define('HTMLTAG_INLINE', 2); +define('HTMLTAG_ACCEPTS_INLINE', 4); + + +HTML::_setTagProperty(HTMLTAG_EMPTY, + 'area base basefont br col frame hr img input isindex link meta param'); +HTML::_setTagProperty(HTMLTAG_ACCEPTS_INLINE, + // %inline elements: + 'b big i small tt ' // %fontstyle + . 's strike u ' // (deprecated) + . 'abbr acronym cite code dfn em kbd samp strong var ' //%phrase + . 'a img object embed br script map q sub sup span bdo '//%special + . 'button input label option select textarea label ' //%formctl + + // %block elements which contain inline content + . 'address h1 h2 h3 h4 h5 h6 p pre ' + // %block elements which contain either block or inline content + . 'div fieldset frameset' + + // other with inline content + . 'caption dt label legend video ' + // other with either inline or block + . 'dd del ins li td th colgroup'); + +HTML::_setTagProperty(HTMLTAG_INLINE, + // %inline elements: + 'b big i small tt ' // %fontstyle + . 's strike u ' // (deprecated) + . 'abbr acronym cite code dfn em kbd samp strong var ' //%phrase + . 'a img object br script map q sub sup span bdo '//%special + . 'button input label option select textarea ' //%formctl + . 'nobody iframe' + ); + +/** + * Generate hidden form input fields. + * + * @param $query_args hash A hash mapping names to values for the hidden inputs. + * Values in the hash can themselves be hashes. The will result in hidden inputs + * which will reconstruct the nested structure in the resulting query args as + * processed by PHP. + * + * Example: + * + * $args = array('x' => '2', + * 'y' => array('a' => 'aval', 'b' => 'bval')); + * $inputs = HiddenInputs($args); + * + * Will result in: + * + * + * + * + * + * @return object An XmlContent object containing the inputs. + */ +function HiddenInputs ($query_args, $pfx = false, $exclude = array()) { + $inputs = HTML(); + + foreach ($query_args as $key => $val) { + if (in_array($key, $exclude)) continue; + $name = $pfx ? $pfx . "[$key]" : $key; + if (is_array($val)) + $inputs->pushContent(HiddenInputs($val, $name)); + else + $inputs->pushContent(HTML::input(array('type' => 'hidden', + 'name' => $name, + 'value' => $val))); + } + return $inputs; +} + + +/** Generate a