diff --git a/feed.php b/feed.php
new file mode 100644
index 0000000..0d12385
--- /dev/null
+++ b/feed.php
@@ -0,0 +1,126 @@
+ $email, "email" => "");
+ if (preg_match("|([^\<]*)<([^>]*)>|", $email, $m)) {
+ $ret["name"] = trim($m[1]);
+ $ret["email"] = trim($m[2]);
+ }
+ return $ret;
+}
+
+
+function sort_by_date($a, $b)
+{
+ if ($a["time"] < $b["time"]) {
+ return 1;
+ } else if ($a["time"] > $b["time"]) {
+ return -1;
+ }
+ return 0;
+}
+
+function print_changeset($row)
+{
+ $author = parse_email($row["author"]);
+ $gid = md5($author["email"]);
+ $avatar = (USE_SSL ? "https://secure.gravatar.com" : "http://www.gravatar.com") .
+ "/avatar/$gid?r=x&d=mm&s=48";
+ return '
 '
+ . htmlspecialchars($author["name"]) . ' | '
+ . '' . htmlspecialchars($row["hash"]) . ' '
+ . htmlspecialchars($row["time"]). "
\n\n"
+ . htmlspecialchars($row["message"]) . ' | ';
+}
+
+function print_changes($current, $next)
+{
+ global $DB;
+
+ $ret = "";
+
+ $revs = array();
+ if (!($res = $DB->query(kl_str_sql("select revisions from changes where build<=!i and build>!i order by build desc", $current->nr, $next->nr)))) {
+ return $ret;
+ } else {
+ while ($row = $DB->fetchRow($res)) {
+ $revs = array_merge($revs, explode(",", $row["revisions"]));
+ }
+ }
+
+ if ($res = $DB->query("select * from revs where hash in ('" . implode("','", $revs) . "')")) {
+ $ret .= '';
+
+ $changesets = array();
+
+ while ($row = $DB->fetchRow($res)) {
+ $changesets[] = $row;
+ }
+
+ usort($changesets, "sort_by_date");
+
+ foreach ($changesets as $change) {
+ $ret .= print_changeset($change);
+ }
+
+ $ret .= '
';
+ }
+
+ return $ret;
+}
+
+$buildFeed = new FeedWriter(ATOM);
+$buildFeed->setTitle('Singularity Automatic Development Builds');
+$buildFeed->setLink('http://files.streamgrid.net/singularity/');
+$buildFeed->setDescription('Latest automated build of the Singularity Viewer project');
+// $buildFeed->setImage('Testing the RSS writer class',
+// 'http://www.ajaxray.com/projects/rss',
+// 'http://www.rightbrainsolution.com/images/logo.gif');
+
+$chan = "SingularityAlpha";
+
+$pageSize = 20;
+
+$builds = array();
+
+if ($res = $DB->query(kl_str_sql("select * from builds where chan=!s order by nr desc limit !i", $chan, $pageSize + 1))) {
+ while ($row = $DB->fetchRow($res)) {
+
+ $build = new stdClass;
+ $DB->loadFromDbRow($build, $res, $row);
+ $builds[] = $build;
+ }
+}
+
+$nrBuilds = count($builds);
+
+if ($nrBuilds) {
+ for ($i = 0; $i < $pageSize; $i++) {
+ if (!isset($builds[$i])) continue;
+ $newItem = $buildFeed->createNewItem();
+ $newItem->setTitle("Singularity Alpha build " . $builds[$i]->nr);
+ $newItem->setLink("http://files.streamgrid.net/singularity/?build_id=" . $builds[$i]->nr);
+ $newItem->setDate($builds[$i]->modified);
+ if (isset($builds[$i+1])) {
+ $newItem->setDescription(print_changes($builds[$i], $builds[$i + 1]));
+ }
+ $buildFeed->addItem($newItem);
+ }
+
+ $buildFeed->genarateFeed();
+
+}
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/index.php b/index.php
index 3d0459c..68df762 100644
--- a/index.php
+++ b/index.php
@@ -17,9 +17,11 @@ function print_changeset($row)
{
$author = parse_email($row["author"]);
$gid = md5($author["email"]);
+ $avatar = (USE_SSL ? "https://secure.gravatar.com" : "http://www.gravatar.com") .
+ "/avatar/$gid?r=x&d=mm&s=48";
print '
-  ' .
+ |  ' .
htmlspecialchars($author["name"]) . ' |
' . htmlspecialchars($row["hash"]) . ' |
' . htmlspecialchars($row["time"]).
@@ -71,11 +73,11 @@ function print_changes($current, $next)
}
}
-Function print_build($current, $next)
+Function print_build($current, $next, $buildNr)
{
print "
|
- | nr})\">Build " . htmlspecialchars($current->nr). " |
+ nr}\">Build " . htmlspecialchars($current->nr). " |
" . htmlspecialchars($current->modified). " (" . Layout::since(strtotime($current->modified)) . " ago) |
" . htmlspecialchars($current->chan). " |
Windows Installer
@@ -83,7 +85,9 @@ Function print_build($current, $next)
|
";
if ($next) {
print '|
- Show changes >> |
";
}
@@ -98,7 +102,16 @@ $pageSize = 20;
$builds = array();
-if ($res = $DB->query(kl_str_sql("select * from builds where chan=!s order by nr desc limit !i", $chan, $pageSize + 1))) {
+$buildNr = 0;
+$where = "";
+
+if (isset($_GET["build_id"])) {
+ $buildNr = (int)$_GET["build_id"];
+ $pageSize = 1;
+ $where = kl_str_sql(" and nr <= !i ", $buildNr);
+}
+
+if ($res = $DB->query(kl_str_sql("select * from builds where chan=!s $where order by nr desc limit !i", $chan, $pageSize + 1))) {
while ($row = $DB->fetchRow($res)) {
$build = new stdClass;
@@ -115,7 +128,7 @@ if ($nrBuilds) {
for ($i = 0; $i < $pageSize; $i++) {
if (!isset($builds[$i])) continue;
- print_build($builds[$i], $builds[$i + 1]);
+ print_build($builds[$i], $builds[$i + 1], $buildNr);
}
print '';
diff --git a/lib/FeedItem.php b/lib/FeedItem.php
new file mode 100644
index 0000000..a9fe931
--- /dev/null
+++ b/lib/FeedItem.php
@@ -0,0 +1,167 @@
+
+ * @link http://www.ajaxray.com/projects/rss
+ */
+ class FeedItem
+ {
+ private $elements = array(); //Collection of feed elements
+ private $version;
+
+ /**
+ * Constructor
+ *
+ * @param contant (RSS1/RSS2/ATOM) RSS2 is default.
+ */
+ function __construct($version = RSS2)
+ {
+ $this->version = $version;
+ }
+
+ /**
+ * Add an element to elements array
+ *
+ * @access public
+ * @param srting The tag name of an element
+ * @param srting The content of tag
+ * @param array Attributes(if any) in 'attrName' => 'attrValue' format
+ * @return void
+ */
+ public function addElement($elementName, $content, $attributes = null)
+ {
+ $this->elements[$elementName]['name'] = $elementName;
+ $this->elements[$elementName]['content'] = $content;
+ $this->elements[$elementName]['attributes'] = $attributes;
+ }
+
+ /**
+ * Set multiple feed elements from an array.
+ * Elements which have attributes cannot be added by this method
+ *
+ * @access public
+ * @param array array of elements in 'tagName' => 'tagContent' format.
+ * @return void
+ */
+ public function addElementArray($elementArray)
+ {
+ if(! is_array($elementArray)) return;
+ foreach ($elementArray as $elementName => $content)
+ {
+ $this->addElement($elementName, $content);
+ }
+ }
+
+ /**
+ * Return the collection of elements in this feed item
+ *
+ * @access public
+ * @return array
+ */
+ public function getElements()
+ {
+ return $this->elements;
+ }
+
+ // Wrapper functions ------------------------------------------------------
+
+ /**
+ * Set the 'dscription' element of feed item
+ *
+ * @access public
+ * @param string The content of 'description' element
+ * @return void
+ */
+ public function setDescription($description)
+ {
+ $tag = ($this->version == ATOM)? 'summary' : 'description';
+ $this->addElement($tag, $description);
+ }
+
+ /**
+ * @desc Set the 'title' element of feed item
+ * @access public
+ * @param string The content of 'title' element
+ * @return void
+ */
+ public function setTitle($title)
+ {
+ $this->addElement('title', $title);
+ }
+
+ /**
+ * Set the 'date' element of feed item
+ *
+ * @access public
+ * @param string The content of 'date' element
+ * @return void
+ */
+ public function setDate($date)
+ {
+ if(! is_numeric($date))
+ {
+ $date = strtotime($date);
+ }
+
+ if($this->version == ATOM)
+ {
+ $tag = 'updated';
+ $value = date(DATE_ATOM, $date);
+ }
+ elseif($this->version == RSS2)
+ {
+ $tag = 'pubDate';
+ $value = date(DATE_RSS, $date);
+ }
+ else
+ {
+ $tag = 'dc:date';
+ $value = date("Y-m-d", $date);
+ }
+
+ $this->addElement($tag, $value);
+ }
+
+ /**
+ * Set the 'link' element of feed item
+ *
+ * @access public
+ * @param string The content of 'link' element
+ * @return void
+ */
+ public function setLink($link)
+ {
+ if($this->version == RSS2 || $this->version == RSS1)
+ {
+ $this->addElement('link', $link);
+ }
+ else
+ {
+ $this->addElement('link','',array('href'=>$link));
+ $this->addElement('id', FeedWriter::uuid($link,'urn:uuid:'));
+ }
+
+ }
+
+ /**
+ * Set the 'encloser' element of feed item
+ * For RSS 2.0 only
+ *
+ * @access public
+ * @param string The url attribute of encloser tag
+ * @param string The length attribute of encloser tag
+ * @param string The type attribute of encloser tag
+ * @return void
+ */
+ public function setEncloser($url, $length, $type)
+ {
+ $attributes = array('url'=>$url, 'length'=>$length, 'type'=>$type);
+ $this->addElement('enclosure','',$attributes);
+ }
+
+ } // end of class FeedItem
+?>
diff --git a/lib/FeedWriter.php b/lib/FeedWriter.php
new file mode 100644
index 0000000..70d01d0
--- /dev/null
+++ b/lib/FeedWriter.php
@@ -0,0 +1,430 @@
+
+ * @link http://www.ajaxray.com/projects/rss
+ */
+ class FeedWriter
+ {
+ private $channels = array(); // Collection of channel elements
+ private $items = array(); // Collection of items as object of FeedItem class.
+ private $data = array(); // Store some other version wise data
+ private $CDATAEncoding = array(); // The tag names which have to encoded as CDATA
+
+ private $version = null;
+
+ /**
+ * Constructor
+ *
+ * @param constant the version constant (RSS1/RSS2/ATOM).
+ */
+ function __construct($version = RSS2)
+ {
+ $this->version = $version;
+
+ // Setting default value for assential channel elements
+ $this->channels['title'] = $version . ' Feed';
+ $this->channels['link'] = 'http://www.ajaxray.com/blog';
+
+ //Tag names to encode in CDATA
+ $this->CDATAEncoding = array('description', 'content:encoded', 'summary');
+ }
+
+ // Start # public functions ---------------------------------------------
+
+ /**
+ * Set a channel element
+ * @access public
+ * @param srting name of the channel tag
+ * @param string content of the channel tag
+ * @return void
+ */
+ public function setChannelElement($elementName, $content)
+ {
+ $this->channels[$elementName] = $content ;
+ }
+
+ /**
+ * Set multiple channel elements from an array. Array elements
+ * should be 'channelName' => 'channelContent' format.
+ *
+ * @access public
+ * @param array array of channels
+ * @return void
+ */
+ public function setChannelElementsFromArray($elementArray)
+ {
+ if(! is_array($elementArray)) return;
+ foreach ($elementArray as $elementName => $content)
+ {
+ $this->setChannelElement($elementName, $content);
+ }
+ }
+
+ /**
+ * Genarate the actual RSS/ATOM file
+ *
+ * @access public
+ * @return void
+ */
+ public function genarateFeed()
+ {
+ header("Content-type: text/xml");
+
+ $this->printHead();
+ $this->printChannels();
+ $this->printItems();
+ $this->printTale();
+ }
+
+ /**
+ * Create a new FeedItem.
+ *
+ * @access public
+ * @return object instance of FeedItem class
+ */
+ public function createNewItem()
+ {
+ $Item = new FeedItem($this->version);
+ return $Item;
+ }
+
+ /**
+ * Add a FeedItem to the main class
+ *
+ * @access public
+ * @param object instance of FeedItem class
+ * @return void
+ */
+ public function addItem($feedItem)
+ {
+ $this->items[] = $feedItem;
+ }
+
+
+ // Wrapper functions -------------------------------------------------------------------
+
+ /**
+ * Set the 'title' channel element
+ *
+ * @access public
+ * @param srting value of 'title' channel tag
+ * @return void
+ */
+ public function setTitle($title)
+ {
+ $this->setChannelElement('title', $title);
+ }
+
+ /**
+ * Set the 'description' channel element
+ *
+ * @access public
+ * @param srting value of 'description' channel tag
+ * @return void
+ */
+ public function setDescription($desciption)
+ {
+ $this->setChannelElement('description', $desciption);
+ }
+
+ /**
+ * Set the 'link' channel element
+ *
+ * @access public
+ * @param srting value of 'link' channel tag
+ * @return void
+ */
+ public function setLink($link)
+ {
+ $this->setChannelElement('link', $link);
+ }
+
+ /**
+ * Set the 'image' channel element
+ *
+ * @access public
+ * @param srting title of image
+ * @param srting link url of the imahe
+ * @param srting path url of the image
+ * @return void
+ */
+ public function setImage($title, $link, $url)
+ {
+ $this->setChannelElement('image', array('title'=>$title, 'link'=>$link, 'url'=>$url));
+ }
+
+ /**
+ * Set the 'about' channel element. Only for RSS 1.0
+ *
+ * @access public
+ * @param srting value of 'about' channel tag
+ * @return void
+ */
+ public function setChannelAbout($url)
+ {
+ $this->data['ChannelAbout'] = $url;
+ }
+
+ /**
+ * Genarates an UUID
+ * @author Anis uddin Ahmad
+ * @param string an optional prefix
+ * @return string the formated uuid
+ */
+ public function uuid($key = null, $prefix = '')
+ {
+ $key = ($key == null)? uniqid(rand()) : $key;
+ $chars = md5($key);
+ $uuid = substr($chars,0,8) . '-';
+ $uuid .= substr($chars,8,4) . '-';
+ $uuid .= substr($chars,12,4) . '-';
+ $uuid .= substr($chars,16,4) . '-';
+ $uuid .= substr($chars,20,12);
+
+ return $prefix . $uuid;
+ }
+ // End # public functions ----------------------------------------------
+
+ // Start # private functions ----------------------------------------------
+
+ /**
+ * Prints the xml and rss namespace
+ *
+ * @access private
+ * @return void
+ */
+ private function printHead()
+ {
+ $out = '' . "\n";
+
+ if($this->version == RSS2)
+ {
+ $out .= '' . PHP_EOL;
+ }
+ elseif($this->version == RSS1)
+ {
+ $out .= '' . PHP_EOL;;
+ }
+ else if($this->version == ATOM)
+ {
+ $out .= '' . PHP_EOL;;
+ }
+ echo $out;
+ }
+
+ /**
+ * Closes the open tags at the end of file
+ *
+ * @access private
+ * @return void
+ */
+ private function printTale()
+ {
+ if($this->version == RSS2)
+ {
+ echo '' . PHP_EOL . '';
+ }
+ elseif($this->version == RSS1)
+ {
+ echo '';
+ }
+ else if($this->version == ATOM)
+ {
+ echo '';
+ }
+
+ }
+
+ /**
+ * Creates a single node as xml format
+ *
+ * @access private
+ * @param srting name of the tag
+ * @param mixed tag value as string or array of nested tags in 'tagName' => 'tagValue' format
+ * @param array Attributes(if any) in 'attrName' => 'attrValue' format
+ * @return string formatted xml tag
+ */
+ private function makeNode($tagName, $tagContent, $attributes = null)
+ {
+ $nodeText = '';
+ $attrText = '';
+
+ if(is_array($attributes))
+ {
+ foreach ($attributes as $key => $value)
+ {
+ $attrText .= " $key=\"$value\" ";
+ }
+ }
+
+ if(is_array($tagContent) && $this->version == RSS1)
+ {
+ $attrText = ' rdf:parseType="Resource"';
+ }
+
+
+ $attrText .= (in_array($tagName, $this->CDATAEncoding) && $this->version == ATOM)? ' type="html" ' : '';
+ $nodeText .= (in_array($tagName, $this->CDATAEncoding))? "<{$tagName}{$attrText}>";
+
+ if(is_array($tagContent))
+ {
+ foreach ($tagContent as $key => $value)
+ {
+ $nodeText .= $this->makeNode($key, $value);
+ }
+ }
+ else
+ {
+ $nodeText .= (in_array($tagName, $this->CDATAEncoding))? $tagContent : htmlentities($tagContent);
+ }
+
+ $nodeText .= (in_array($tagName, $this->CDATAEncoding))? "]]>$tagName>" : "$tagName>";
+
+ return $nodeText . PHP_EOL;
+ }
+
+ /**
+ * @desc Print channels
+ * @access private
+ * @return void
+ */
+ private function printChannels()
+ {
+ //Start channel tag
+ switch ($this->version)
+ {
+ case RSS2:
+ echo '' . PHP_EOL;
+ break;
+ case RSS1:
+ echo (isset($this->data['ChannelAbout']))? "data['ChannelAbout']}\">" : "channels['link']}\">";
+ break;
+ }
+
+ //Print Items of channel
+ foreach ($this->channels as $key => $value)
+ {
+ if($this->version == ATOM && $key == 'link')
+ {
+ // ATOM prints link element as href attribute
+ echo $this->makeNode($key,'',array('href'=>$value));
+ //Add the id for ATOM
+ echo $this->makeNode('id',$this->uuid($value,'urn:uuid:'));
+ }
+ else
+ {
+ echo $this->makeNode($key, $value);
+ }
+
+ }
+
+ //RSS 1.0 have special tag with channel
+ if($this->version == RSS1)
+ {
+ echo "" . PHP_EOL . "" . PHP_EOL;
+ foreach ($this->items as $item)
+ {
+ $thisItems = $item->getElements();
+ echo "" . PHP_EOL;
+ }
+ echo "" . PHP_EOL . "" . PHP_EOL . "" . PHP_EOL;
+ }
+ }
+
+ /**
+ * Prints formatted feed items
+ *
+ * @access private
+ * @return void
+ */
+ private function printItems()
+ {
+ foreach ($this->items as $item)
+ {
+ $thisItems = $item->getElements();
+
+ //the argument is printed as rdf:about attribute of item in rss 1.0
+ echo $this->startItem($thisItems['link']['content']);
+
+ foreach ($thisItems as $feedItem )
+ {
+ echo $this->makeNode($feedItem['name'], $feedItem['content'], $feedItem['attributes']);
+ }
+ echo $this->endItem();
+ }
+ }
+
+ /**
+ * Make the starting tag of channels
+ *
+ * @access private
+ * @param srting The vale of about tag which is used for only RSS 1.0
+ * @return void
+ */
+ private function startItem($about = false)
+ {
+ if($this->version == RSS2)
+ {
+ echo '- ' . PHP_EOL;
+ }
+ elseif($this->version == RSS1)
+ {
+ if($about)
+ {
+ echo "
- " . PHP_EOL;
+ }
+ else
+ {
+ die('link element is not set .\n It\'s required for RSS 1.0 to be used as about attribute of item');
+ }
+ }
+ else if($this->version == ATOM)
+ {
+ echo "" . PHP_EOL;
+ }
+ }
+
+ /**
+ * Closes feed item tag
+ *
+ * @access private
+ * @return void
+ */
+ private function endItem()
+ {
+ if($this->version == RSS2 || $this->version == RSS1)
+ {
+ echo '
' . PHP_EOL;
+ }
+ else if($this->version == ATOM)
+ {
+ echo "" . PHP_EOL;
+ }
+ }
+
+
+
+ // End # private functions ----------------------------------------------
+
+ } // end of class FeedWriter
+
diff --git a/lib/Layout.php b/lib/Layout.php
index 3028629..b68ef29 100644
--- a/lib/Layout.php
+++ b/lib/Layout.php
@@ -36,6 +36,7 @@ class Layout
+
Singularity Viewer Automated Build System
@@ -67,7 +68,7 @@ class Layout
-
Automated Build System
+
Automated Build System
0 ? true:false;
+ define('USE_SSL', $init_ssl);
$init_url = $init_ssl ? "https://" : "http://";