[PATCH] News cleanup & hAtom support

[email protected] ("Hannes Magnusson")
Newsgroups php.webmaster
Message-ID <[email protected]>
Hi all

Attached is a master & phpweb "news system" cleanup patch (gosh, what
was I thinking) which should increase the readability, performance and
scalability heckofalot.
The patch also adds hAtom support for all news items.

If there are no objections I'll commit it probably on Monday.

-Hannes
news.patch.txt (text/plain, 14.7 KB)
Index: master/scripts/pregen_news
===================================================================
RCS file: /repository/php-master-web/scripts/pregen_news,v
retrieving revision 1.2
diff -u -p -r1.2 pregen_news
--- master/scripts/pregen_news	29 Dec 2007 14:44:08 -0000	1.2
+++ master/scripts/pregen_news	29 Feb 2008 17:17:08 -0000
@@ -1,102 +1,151 @@
 <?php /* vim: set noet ts=4 sw=4 ft=php: : */
 
+define("XMLNS_ATOM",  "http://www.w3.org/2005/Atom");
+define("XMLNS_XHTML", "http://www.w3.org/1999/xhtml");
+define("XMLNS_PHP",   "http://php.net/ns/news");
+
+function date_sort($a, $b) {
+	return $a["updated"] == $b["updated"] ? 0 : (strtotime($a["updated"]) > strtotime($b["updated"]) ? -1 : 1);
+}
+
 $XML_OPTIONS =  LIBXML_COMPACT | LIBXML_NOBLANKS | LIBXML_NOCDATA | LIBXML_NSCLEAN | LIBXML_XINCLUDE;
+
 function pregen_atom($feed, $feedDest, $newsDest) {
-	$dom = new DOMDocument("1.0", "UTF-8");
-	$dom->formatOutput = true;
-	$dom->preserveWhiteSpace = false;
-	$options = $GLOBALS["XML_OPTIONS"];
-
-	$y = date("Y");
-	if (file_exists($feed .$y. ".xml")) {
-		$dom->load($feed .$y. ".xml", $options);
-	} elseif (file_exists($feed .(--$y). ".xml")) {
-		$dom->load($feed .$y. ".xml");
-	} else {
-		trigger_error("Can't find news feed", E_USER_WARNING);
-		return;
+	$entries = format_atom_feed($feed, $y = date("Y"));
+	uasort($entries, "date_sort");
+
+	$write = '<?php $NEWS_ENTRIES = ' . var_export($entries, 1) . ';';
+	file_put_contents($newsDest, $write);
+
+	do {
+		$filename = $feed . $y .".xml";
+	} while (!file_exists($filename));
+	copy($filename, $feedDest);
+}
+
+function format_atom_feed($feed, $year) {
+	static $frontpage = 0;
+	static $conf = 0;
+
+	// Lets only go back one year
+	if ($year < (date("Y")-1)) {
+		$frontpage = $conf = 100;
+		return array();
 	}
 
+	$filename = $feed . $year .".xml";
 
-	$entrys = $dom->getElementsByTagNameNS("http://www.w3.org/2005/Atom", "entry");
-	if(!$entrys || $entrys->length < 10) {
-		$fragment = $dom->createDocumentFragment();
-		$tmp = new DOMDocument("1.0", "UTF-8");
-		$tmp->formatOutput = true;
-		$tmp->preserveWhiteSpace = false;
-
-		$tmp->load($feed .($y-1). ".xml", $options);
-		$e = $tmp->getElementsByTagNameNS("http://www.w3.org/2005/Atom", "entry");
-		foreach($e as $n) {
-			$n->setAttribute("xmlns", "http://www.w3.org/2005/Atom");
-			$n->setAttribute("xmlns:php", "http://php.net/ns/news");
-			$fragment->appendXML($tmp->saveXML($n));
+	// January 1st...
+	if (!file_exists($filename)) {
+		return format_atom_feed($feed, --$year);
+	}
+
+	$r = new XMLReader;
+	$r->open($filename, "UTF-8");
+
+	$entries = array();
+	while($r->read()) {
+		if ($r->nodeType === XMLReader::ELEMENT && $r->name === "entry") {
+			$entries[] = $current = format_atom_entry($r);
+
+			// Count the frontpage & conference entries
+			foreach($current["category"] as $dog) {
+				if ($dog["term"] == "frontpage") {
+					++$frontpage;
+					break;
+				} else {
+					++$conf;
+				}
+			}
 		}
-		$child = $dom->documentElement->appendChild($fragment);
-		$child->removeAttributeNS("http://www.w3.org/1999/xhtml", "default");
 	}
 
-	$dom->save($feedDest, $options);
-	//$dom->normalizeDocument(); # For whatever reason this doesn't seem to work :(
-	$dom->load($feedDest);
-	$NEWS = array();
-
-	foreach($entrys as $entry) {
-		$fragment = "";
-		$c = array();
-		$contentNode 	  = $entry->getElementsByTagName("content")->item(0)->firstChild;
-
-		foreach($entry->getElementsByTagName("link") as $link) {
-			if($link->getAttribute("rel") == "alternate") {
-				$self     = $link->getAttribute("href");
-				$fragment = parse_url("http://php.net" . $self, PHP_URL_FRAGMENT);
+	// Make sure we have enough entries to display (shortly after new-year..)
+	if (min($frontpage, $conf) < 5) {
+		$entries = array_merge($entries, format_atom_feed($feed, --$year));
+	}
+
+	return $entries;
+}
+
+// {{{ Parse the entry into array(element => value)
+function format_atom_entry($r) {
+	$retval = array();
+
+	while($r->read()) {
+		if ($r->nodeType !== XMLReader::ELEMENT) {
+			if ($r->nodeType === XMLReader::END_ELEMENT && $r->name === "entry") {
+				return $retval;
+			}
+			continue;
+		}
+
+		$name = $r->localName;
+		if ($r->namespaceURI === XMLNS_ATOM) {
+			switch($name) {
+			case "title":
+			case "id":
+			case "published":
+			case "updated":
+				$retval[$name] = $r->readString();
+				$retval[$name] = $r->readString();
+				break;
+
+			case "link":
+			case "category":
+				$retval[$name][] = format_attributes($r);
+				break;
+
+			case "content":
+				if ($r->hasAttributes) {
+					switch($r->getAttribute("type")) {
+					case "html":
+					case "xhtml":
+						$retval[$name] = $r->readInnerXML();
+						break;
+
+					case "text":
+						$retval[$name] = $r->readString();
+						break;
+					}
+				} else {
+					$retval[$name] = $r->readString();
+				}
 				break;
 			}
-	 	}
+		} elseif ($r->namespaceURI === XMLNS_PHP) {
+			switch($name) {
+			case "newsImage":
+				$retval[$name] = format_attributes($r);
+				$retval[$name]["content"] = $r->readString();
+				break;
 
-		$dateNode         = $entry->getElementsByTagName("published")->item(0)->firstChild;
-		$date             = date("d-M-Y", strtotime($dom->saveXML($dateNode, $options)));
-		$dateSpanNode     = $dom->createElement("span", "[$date]");
-		$dateSpanNode->setAttribute("class", "newsdate");
-
-		$contentNode->insertBefore($dateSpanNode, $contentNode->firstChild);
-		$contentNode->removeAttributeNS("http://www.w3.org/1999/xhtml", "");
-
-		$content          = $dom->saveXML($contentNode, $options);
-		$title            = $dom->saveXML($entry->getElementsByTagName("title")->item(0)->firstChild, $options);
-		$image            = $entry->getElementsByTagNameNS("http://php.net/ns/news", "newsImage");
-		if($image->length > 0) {
-			$image = array(
-				"link"    => $image->item(0)->getAttribute("link"),
-				"title"   => $image->item(0)->getAttribute("title"),
-				"image"   => $image->item(0)->nodeValue,
-			);
-		} else {
-			$image = false;
+			case "finalTeaserDate":
+				$retval[$name] = $r->readString();
+				break;
+			}
 		}
+	}
 
-		$newsEntry        = array(
-			"fragment"    => $fragment,
-			"title"       => $title,
-			"content"     => $content,
-			"image"       => $image,
-			"date"        => $date,
-		);
-		$categories       = $entry->getElementsByTagName("category");
-		for ($i=0; $i<$categories->length; $i++) {
-			$c[] = $categories->item($i)->getAttribute("term");
-		}
-		if (in_array("cfp", $c) || in_array("conferences", $c)) {
-			$NEWS["conferences"][] = $newsEntry;
-		}
-		if (in_array("frontpage", $c)) {
-			$NEWS["frontpage"][] = $newsEntry;
-		}
+	return $retval;
+} // }}}
+
+// {{{ Return all attrs for current element as an array(attr-name => attr-value)
+function format_attributes($r) {
+	$retval = array();
+
+	if (!$r->hasAttributes) {
+		return $retval;
 	}
 
-	$write = '<?php $NEWS_ENTRIES = ' . var_export($NEWS, 1) . ';';
-	file_put_contents($newsDest, $write);
-}
+	$r->moveToFirstAttribute();
+	do {
+		$retval[$r->localName] = $r->value;
+	} while($r->moveToNextAttribute());
+	$r->moveToElement();
+
+	return $retval;
+} // }}}
 
 function legacy_rss($atom, $newsDest, $confDest) {
 	$sxe = new SimpleXMLElement($atom, $GLOBALS["XML_OPTIONS"], true);
Index: web/index.php
===================================================================
RCS file: /repository/phpweb/index.php,v
retrieving revision 1.905
diff -u -p -r1.905 index.php
--- web/index.php	25 Feb 2008 13:05:43 -0000	1.905
+++ web/index.php	29 Feb 2008 17:17:09 -0000
@@ -245,7 +245,7 @@ if (is_array($CONF_TEASER) && count($CON
 /* Where the h*ll did all the news go?
  * See archives/2007.xml
  */
-print_news($NEWS_ENTRIES["frontpage"]);
+print_news($NEWS_ENTRIES, "frontpage");
 ?>
 
 <p class="center"><a href="/archive/index.php">News Archive</a></p>
Index: web/archive/2008.xml
===================================================================
RCS file: /repository/phpweb/archive/2008.xml,v
retrieving revision 1.11
diff -u -p -r1.11 2008.xml
--- web/archive/2008.xml	28 Feb 2008 23:02:42 -0000	1.11
+++ web/archive/2008.xml	29 Feb 2008 17:17:09 -0000
@@ -21,7 +21,7 @@
     <php:finalTeaserDate>2008-05-24</php:finalTeaserDate>
     <php:newsImage link="http://tek.phparch.com/" title="php|tek 2008: Chicago">phptek_2008.png</php:newsImage>
     <content type="xhtml">
-      <div xmlns="http://www.w3.org/1999/xhtml" class="description">
+      <div xmlns="http://www.w3.org/1999/xhtml">
 <a href="http://tek.phparch.com/" class="url">php|tek 2008: Chicago</a>
 <p>
 The publishers of <a href="http://www.phparch.com/">php|architect Magazine</a> are proud to announce the php|tek 2008 conference in Chicago, Illinois, USA.
@@ -46,7 +46,7 @@ For the past two years, php|architect's 
     <php:finalTeaserDate>2008-03-16</php:finalTeaserDate>
     <php:newsImage link="http://conf.phpquebec.com/en/conf2008/" title="PHP Québec conference 2008">conference_php_quebec.gif</php:newsImage>
     <content type="xhtml">
-      <div xmlns="http://www.w3.org/1999/xhtml" class="description">
+      <div xmlns="http://www.w3.org/1999/xhtml">
 <a href="http://conf.phpquebec.com/">2008 PHP Quebec Conference &amp; Job Fair</a>
 <p>
 The PHP Quebec team is pleased to present the sixth edition of the <a 
@@ -78,7 +78,7 @@ visit the website: <a href="http://conf.
     <php:newsImage xmlns="http://php.net/ns/news" link="http://www.phpconference.co.uk/" title="PHP London Conference 08">phplondon2008.png</php:newsImage>
     <link href="http://www.phpconference.co.uk/" rel="via" type="text/html"/>
     <content type="xhtml">
-      <div xmlns="http://www.w3.org/1999/xhtml" class="description">
+      <div xmlns="http://www.w3.org/1999/xhtml">
 		  <p><abbr class="dtstart" title="2008-02-29">February 29th</abbr> (Leap Year Day). phplondon.org  announce their third
             annual <a href="http://www.phpconference.co.uk" alt="phplondon.org community conference" class="url">community conference</a>
             to be held at Inmarsat, Old Street, London.</p>
Index: web/archive/2008.php
===================================================================
RCS file: /repository/phpweb/archive/2008.php,v
retrieving revision 1.1
diff -u -p -r1.1 2008.php
--- web/archive/2008.php	29 Dec 2007 15:12:04 -0000	1.1
+++ web/archive/2008.php	29 Feb 2008 17:17:09 -0000
@@ -16,13 +16,8 @@ site_header("News Archive - 2008");
 <hr />
 
 <?php
-function date_sort($a, $b) {
-	return $a["date"] == $b["date"] ? 0 : (strtotime($a["date"]) > strtotime($b["date"]) ? -1 : 1);
-}
 
-$news = array_merge($NEWS_ENTRIES["frontpage"], $NEWS_ENTRIES["conferences"]);
-uasort($news, "date_sort");
-print_news($news, 2008);
+print_news($NEWS_ENTRIES, array("conferences", "cfp", "frontpage"), 50);
 /* %s/<a href="\(.*\)"><img src="\/images\/news\/\(.*\)" alt="\(.*\)" width.*><\/a>/<?php news_image("\1", "\2", "\3"); ?>/g */
 site_footer();
 
Index: web/conferences/index.php
===================================================================
RCS file: /repository/phpweb/conferences/index.php,v
retrieving revision 1.23
diff -u -p -r1.23 index.php
--- web/conferences/index.php	25 Feb 2008 15:49:21 -0000	1.23
+++ web/conferences/index.php	29 Feb 2008 17:17:09 -0000
@@ -22,7 +22,7 @@ unset($RSIDEBAR_DATA);
 
 site_header("PHP Conferences around the world", array("layout_workaround" => $layout_workaround, 'headtags' => '<link rel="alternate" type="application/atom+xml" title="PHP: Conference announcements" href="' . $MYSITE . 'feed.atom" />'));
 
-print_news($NEWS_ENTRIES["conferences"], false, true);
+print_news($NEWS_ENTRIES, array("conferences", "cfp"), 15);
 
 site_footer(
     array("atom" => "/feed.atom") // Add a link to the feed
Index: web/include/layout.inc
===================================================================
RCS file: /repository/phpweb/include/layout.inc,v
retrieving revision 1.277
diff -u -p -r1.277 layout.inc
--- web/include/layout.inc	25 Feb 2008 15:49:21 -0000	1.277
+++ web/include/layout.inc	29 Feb 2008 17:17:09 -0000
@@ -623,24 +623,60 @@ function news_archive_sidebar()
 }
 
 // Print news
-function print_news($news, $year = false, $event = false) {
+function print_news($news, $dog, $max = 5) {
+    $count = 0;
     foreach($news as $item) {
-        if ($year && $year != date("Y", strtotime($item["date"]))) {
+        $vevent = "";
+        $ok = false;
+
+        // Only print entries in the provided s/dog/cat/ egory
+        // If its a conference, use the hCalendar container
+        foreach($item["category"] as $category) {
+            if (in_array($category["term"], (array)$dog)) {
+                $ok = true;
+                ++$count;
+            }
+            if ($category["term"] === "conferences") {
+                $vevent = " vevent";
+            }
+        }
+        if ($count > $max) {
+            break;
+        }
+        if ($ok === false) {
             continue;
         }
+
         $image = "";
-        if($item["image"]) {
-            $image = news_image($item["image"]["link"], $item["image"]["image"], $item["image"]["title"]);
+        if(isset($item["newsImage"])) {
+            $image = news_image($item["newsImage"]["link"], $item["newsImage"]["content"], $item["newsImage"]["title"]);
         }
-        $vevent = "";
-        if ($event) {
-            $vevent = " vevent";
+
+        //$id = parse_url($item["id"], PHP_URL_FRAGMENT); 5.1.2
+        $id = parse_url($item["id"]);
+        $id = $id["fragment"];
+
+        // Find the permlink
+        foreach($item["link"] as $link) {
+            if ($link["rel"] === "via") {
+                $permlink = $link["href"];
+                break;
+            }
+        }
+        if (!isset($permlink)) {
+            $permlink = "#" .$id;
         }
+
+        $newsdate = date("d-M-Y", strtotime($item["updated"]));
+
         echo <<< EOT
-<div class="newsItem$vevent">
-    $image
-    <a name="{$item["fragment"]}" id="{$item["fragment"]}"><h1 class="summary">{$item["title"]}</h1></a>
-    {$item["content"]}
+<div class="newsItem hentry$vevent">
+    <div class="newsImage">$image</div>
+    <h1 class="summary entry-title"><a name="{$id}" id="{$id}" href="{$permlink}" rel="bookmark" class="bookmark">{$item["title"]}</a></h1>
+    [<abbr class="updated newsdate" title="{$item["updated"]}">{$newsdate}</abbr>]
+    <div class="entry-content description">
+        {$item["content"]}
+    </div>
 </div>
 
 <hr />
@@ -649,3 +685,4 @@ EOT;
     }
 }
 /* vim: set et ts=4 sw=4 ft=php: : */
+
Index: web/styles/site.css
===================================================================
RCS file: /repository/phpweb/styles/site.css,v
retrieving revision 1.63
diff -u -p -r1.63 site.css
--- web/styles/site.css	5 Feb 2008 08:12:26 -0000	1.63
+++ web/styles/site.css	29 Feb 2008 17:17:09 -0000
@@ -160,10 +160,6 @@ hr {
 	color: #6666cc;
 	background-color: transparent;
 }
-div.newsItem .newsdate {
-	float:left;
-	padding-right: 10px;
-}
 
 div.indent {
 	margin-left: 50px;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.