You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@shindig.apache.org by ch...@apache.org on 2008/05/08 01:48:22 UTC

svn commit: r654331 [1/6] - in /incubator/shindig/trunk/php: ./ src/common/Zend/ src/common/Zend/Feed/ src/common/Zend/Feed/Builder/ src/common/Zend/Feed/Builder/Header/ src/common/Zend/Feed/Entry/ src/common/Zend/Http/ src/common/Zend/Http/Client/ src...

Author: chabotc
Date: Wed May  7 16:48:15 2008
New Revision: 654331

URL: http://svn.apache.org/viewvc?rev=654331&view=rev
Log:
Added ContentType.FEED support

Added:
    incubator/shindig/trunk/php/src/common/Zend/
    incubator/shindig/trunk/php/src/common/Zend/Exception.php
    incubator/shindig/trunk/php/src/common/Zend/Feed/
    incubator/shindig/trunk/php/src/common/Zend/Feed.php
    incubator/shindig/trunk/php/src/common/Zend/Feed/Abstract.php
    incubator/shindig/trunk/php/src/common/Zend/Feed/Atom.php
    incubator/shindig/trunk/php/src/common/Zend/Feed/Builder/
    incubator/shindig/trunk/php/src/common/Zend/Feed/Builder.php
    incubator/shindig/trunk/php/src/common/Zend/Feed/Builder/Entry.php
    incubator/shindig/trunk/php/src/common/Zend/Feed/Builder/Exception.php
    incubator/shindig/trunk/php/src/common/Zend/Feed/Builder/Header/
    incubator/shindig/trunk/php/src/common/Zend/Feed/Builder/Header.php
    incubator/shindig/trunk/php/src/common/Zend/Feed/Builder/Header/Itunes.php
    incubator/shindig/trunk/php/src/common/Zend/Feed/Builder/Interface.php
    incubator/shindig/trunk/php/src/common/Zend/Feed/Element.php
    incubator/shindig/trunk/php/src/common/Zend/Feed/Entry/
    incubator/shindig/trunk/php/src/common/Zend/Feed/Entry/Abstract.php
    incubator/shindig/trunk/php/src/common/Zend/Feed/Entry/Atom.php
    incubator/shindig/trunk/php/src/common/Zend/Feed/Entry/Rss.php
    incubator/shindig/trunk/php/src/common/Zend/Feed/Exception.php
    incubator/shindig/trunk/php/src/common/Zend/Feed/Rss.php
    incubator/shindig/trunk/php/src/common/Zend/Http/
    incubator/shindig/trunk/php/src/common/Zend/Http/Client/
    incubator/shindig/trunk/php/src/common/Zend/Http/Client.php
    incubator/shindig/trunk/php/src/common/Zend/Http/Client/Adapter/
    incubator/shindig/trunk/php/src/common/Zend/Http/Client/Adapter/Exception.php
    incubator/shindig/trunk/php/src/common/Zend/Http/Client/Adapter/Interface.php
    incubator/shindig/trunk/php/src/common/Zend/Http/Client/Adapter/Proxy.php
    incubator/shindig/trunk/php/src/common/Zend/Http/Client/Adapter/Socket.php
    incubator/shindig/trunk/php/src/common/Zend/Http/Client/Adapter/Test.php
    incubator/shindig/trunk/php/src/common/Zend/Http/Client/Exception.php
    incubator/shindig/trunk/php/src/common/Zend/Http/Cookie.php
    incubator/shindig/trunk/php/src/common/Zend/Http/CookieJar.php
    incubator/shindig/trunk/php/src/common/Zend/Http/Exception.php
    incubator/shindig/trunk/php/src/common/Zend/Http/Response.php
    incubator/shindig/trunk/php/src/common/Zend/Loader/
    incubator/shindig/trunk/php/src/common/Zend/Loader.php
    incubator/shindig/trunk/php/src/common/Zend/Loader/Exception.php
    incubator/shindig/trunk/php/src/common/Zend/Loader/PluginLoader/
    incubator/shindig/trunk/php/src/common/Zend/Loader/PluginLoader.php
    incubator/shindig/trunk/php/src/common/Zend/Loader/PluginLoader/Exception.php
    incubator/shindig/trunk/php/src/common/Zend/Loader/PluginLoader/Interface.php
    incubator/shindig/trunk/php/src/common/Zend/Registry.php
    incubator/shindig/trunk/php/src/common/Zend/Uri/
    incubator/shindig/trunk/php/src/common/Zend/Uri.php
    incubator/shindig/trunk/php/src/common/Zend/Uri/Exception.php
    incubator/shindig/trunk/php/src/common/Zend/Uri/Http.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/
    incubator/shindig/trunk/php/src/common/Zend/Validate.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/Abstract.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/Alnum.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/Alpha.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/Barcode/
    incubator/shindig/trunk/php/src/common/Zend/Validate/Barcode.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/Barcode/Ean13.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/Barcode/UpcA.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/Between.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/Ccnum.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/Date.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/Digits.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/EmailAddress.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/Exception.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/Float.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/GreaterThan.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/Hex.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/Hostname/
    incubator/shindig/trunk/php/src/common/Zend/Validate/Hostname.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/Hostname/At.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/Hostname/Ch.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/Hostname/De.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/Hostname/Fi.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/Hostname/Hu.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/Hostname/Interface.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/Hostname/Li.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/Hostname/No.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/Hostname/Se.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/Identical.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/InArray.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/Int.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/Interface.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/Ip.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/LessThan.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/NotEmpty.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/Regex.php
    incubator/shindig/trunk/php/src/common/Zend/Validate/StringLength.php
Modified:
    incubator/shindig/trunk/php/index.php
    incubator/shindig/trunk/php/src/gadgets/ProxyHandler.php
    incubator/shindig/trunk/php/src/gadgets/http/ProxyServlet.php

Modified: incubator/shindig/trunk/php/index.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/index.php?rev=654331&r1=654330&r2=654331&view=diff
==============================================================================
--- incubator/shindig/trunk/php/index.php (original)
+++ incubator/shindig/trunk/php/index.php Wed May  7 16:48:15 2008
@@ -38,41 +38,21 @@
 
 function __autoload($className)
 {
-	//FIXME this started out like a good idea when we just knew gadgets
-	// and it was just 1 directory, now its pretty nuts and probably needs
-	// replacing since it's getting dog slow this way
-	
+	$locations = array('src/common', 'src/gadgets', 'src/gadgets/samplecontainer', 'src/gadgets/http', 'src/socialdata', 'src/socialdata/opensocial', 'src/socialdata/opensocial/model', 'src/socialdata/http');
 	// Check for the presense of this class in our all our directories.
 	$fileName = $className.'.php';
-	if (file_exists("src/common/$fileName")) {
-		    require "src/common/$fileName";
-		    
-	} elseif (file_exists("src/gadgets/$fileName")) {
-		          require "src/gadgets/$fileName";
-		          
-	} elseif (file_exists("src/gadgets/samplecontainer/$fileName")) {
-		          require "src/gadgets/samplecontainer/$fileName";
-		          
-	} elseif (file_exists("src/gadgets/http/$fileName")) {
-		          require "src/gadgets/http/$fileName";
-		          
-	} elseif (file_exists("src/socialdata/$fileName")) {
-		          require "src/socialdata/$fileName";
-		          
-	} elseif (file_exists("src/socialdata/samplecontainer/$fileName")) {
-		          require "src/socialdata/samplecontainer/$fileName";
-		           
-	} elseif (file_exists("src/socialdata/opensocial/$fileName")) {
-		          require "src/socialdata/opensocial/$fileName";
-		          
-	} elseif (file_exists("src/socialdata/opensocial/model/$fileName")) {
-		          require "src/socialdata/opensocial/model/$fileName";
-		          
-	} elseif (file_exists("src/socialdata/http/$fileName")) {
-		          require "src/socialdata/http/$fileName";
+	foreach ($locations as $path) {
+		if (file_exists("{$path}/$fileName")) {
+			require "{$path}/$fileName";
+			break;
+		}
 	}
 }
 
+// prefix our Zend framework dir to the include path so it picks it up before any other 
+// system libs to avoid conflicts
+ini_set('include_path', realpath(dirname(__FILE__))."/src/common:".ini_get('include_path'));
+
 $servletMap = array(
 	Config::get('web_prefix') . '/gadgets/files'    => 'FilesServlet',
 	Config::get('web_prefix') . '/gadgets/js'       => 'JsServlet',
@@ -90,7 +70,7 @@
 		break;
 	}
 }
-if ($servlet) {	
+if ($servlet) {
 	$class = new $class();
 	if ($_SERVER['REQUEST_METHOD'] == 'POST') {
 		$class->doPost();

Added: incubator/shindig/trunk/php/src/common/Zend/Exception.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/Zend/Exception.php?rev=654331&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/common/Zend/Exception.php (added)
+++ incubator/shindig/trunk/php/src/common/Zend/Exception.php Wed May  7 16:48:15 2008
@@ -0,0 +1,30 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend
+ * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+
+/**
+ * @category   Zend
+ * @package    Zend
+ * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Exception extends Exception
+{}
+

Added: incubator/shindig/trunk/php/src/common/Zend/Feed.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/Zend/Feed.php?rev=654331&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/common/Zend/Feed.php (added)
+++ incubator/shindig/trunk/php/src/common/Zend/Feed.php Wed May  7 16:48:15 2008
@@ -0,0 +1,403 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Feed
+ * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Feed.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * Feed utility class
+ *
+ * Base Zend_Feed class, containing constants and the Zend_Http_Client instance
+ * accessor.
+ *
+ * @category   Zend
+ * @package    Zend_Feed
+ * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Feed
+{
+
+    /**
+     * HTTP client object to use for retrieving feeds
+     *
+     * @var Zend_Http_Client
+     */
+    protected static $_httpClient = null;
+
+    /**
+     * Override HTTP PUT and DELETE request methods?
+     *
+     * @var boolean
+     */
+    protected static $_httpMethodOverride = false;
+
+    /**
+     * @var array
+     */
+    protected static $_namespaces = array(
+        'opensearch' => 'http://a9.com/-/spec/opensearchrss/1.0/',
+        'atom'       => 'http://www.w3.org/2005/Atom',
+        'rss'        => 'http://blogs.law.harvard.edu/tech/rss',
+    );
+
+
+    /**
+     * Set the HTTP client instance
+     *
+     * Sets the HTTP client object to use for retrieving the feeds.
+     *
+     * @param  Zend_Http_Client $httpClient
+     * @return void
+     */
+    public static function setHttpClient(Zend_Http_Client $httpClient)
+    {
+        self::$_httpClient = $httpClient;
+    }
+
+
+    /**
+     * Gets the HTTP client object. If none is set, a new Zend_Http_Client will be used.
+     *
+     * @return Zend_Http_Client_Abstract
+     */
+    public static function getHttpClient()
+    {
+        if (!self::$_httpClient instanceof Zend_Http_Client) {
+            /**
+             * @see Zend_Http_Client
+             */
+            require_once 'Zend/Http/Client.php';
+            self::$_httpClient = new Zend_Http_Client();
+        }
+
+        return self::$_httpClient;
+    }
+
+
+    /**
+     * Toggle using POST instead of PUT and DELETE HTTP methods
+     *
+     * Some feed implementations do not accept PUT and DELETE HTTP
+     * methods, or they can't be used because of proxies or other
+     * measures. This allows turning on using POST where PUT and
+     * DELETE would normally be used; in addition, an
+     * X-Method-Override header will be sent with a value of PUT or
+     * DELETE as appropriate.
+     *
+     * @param  boolean $override Whether to override PUT and DELETE.
+     * @return void
+     */
+    public static function setHttpMethodOverride($override = true)
+    {
+        self::$_httpMethodOverride = $override;
+    }
+
+
+    /**
+     * Get the HTTP override state
+     *
+     * @return boolean
+     */
+    public static function getHttpMethodOverride()
+    {
+        return self::$_httpMethodOverride;
+    }
+
+
+    /**
+     * Get the full version of a namespace prefix
+     *
+     * Looks up a prefix (atom:, etc.) in the list of registered
+     * namespaces and returns the full namespace URI if
+     * available. Returns the prefix, unmodified, if it's not
+     * registered.
+     *
+     * @return string
+     */
+    public static function lookupNamespace($prefix)
+    {
+        return isset(self::$_namespaces[$prefix]) ?
+            self::$_namespaces[$prefix] :
+            $prefix;
+    }
+
+
+    /**
+     * Add a namespace and prefix to the registered list
+     *
+     * Takes a prefix and a full namespace URI and adds them to the
+     * list of registered namespaces for use by
+     * Zend_Feed::lookupNamespace().
+     *
+     * @param  string $prefix The namespace prefix
+     * @param  string $namespaceURI The full namespace URI
+     * @return void
+     */
+    public static function registerNamespace($prefix, $namespaceURI)
+    {
+        self::$_namespaces[$prefix] = $namespaceURI;
+    }
+
+
+    /**
+     * Imports a feed located at $uri.
+     *
+     * @param  string $uri
+     * @throws Zend_Feed_Exception
+     * @return Zend_Feed_Abstract
+     */
+    public static function import($uri)
+    {
+        $client = self::getHttpClient();
+        $client->setUri($uri);
+        $response = $client->request('GET');
+        if ($response->getStatus() !== 200) {
+            /**
+             * @see Zend_Feed_Exception
+             */
+            require_once 'Zend/Feed/Exception.php';
+            throw new Zend_Feed_Exception('Feed failed to load, got response code ' . $response->getStatus());
+        }
+        $feed = $response->getBody();
+        return self::importString($feed);
+    }
+
+
+    /**
+     * Imports a feed represented by $string.
+     *
+     * @param  string $string
+     * @throws Zend_Feed_Exception
+     * @return Zend_Feed_Abstract
+     */
+    public static function importString($string)
+    {
+        // Load the feed as an XML DOMDocument object
+        @ini_set('track_errors', 1);
+        $doc = @DOMDocument::loadXML($string);
+        @ini_restore('track_errors');
+
+        if (!$doc) {
+            // prevent the class to generate an undefined variable notice (ZF-2590)
+            if (!isset($php_errormsg)) {
+                if (function_exists('xdebug_is_enabled')) {
+                    $php_errormsg = '(error message not available, when XDebug is running)';
+                } else {
+                    $php_errormsg = '(error message not available)';
+                }
+            }
+
+            /**
+             * @see Zend_Feed_Exception
+             */
+            require_once 'Zend/Feed/Exception.php';
+            throw new Zend_Feed_Exception("DOMDocument cannot parse XML: $php_errormsg");
+        }
+
+        // Try to find the base feed element or a single <entry> of an Atom feed
+        if ($doc->getElementsByTagName('feed')->item(0) ||
+            $doc->getElementsByTagName('entry')->item(0)) {
+            /**
+             * @see Zend_Feed_Atom
+             */
+            require_once 'Zend/Feed/Atom.php';
+            // return a newly created Zend_Feed_Atom object
+            return new Zend_Feed_Atom(null, $string);
+        }
+
+        // Try to find the base feed element of an RSS feed
+        if ($doc->getElementsByTagName('channel')->item(0)) {
+            /**
+             * @see Zend_Feed_Rss
+             */
+            require_once 'Zend/Feed/Rss.php';
+            // return a newly created Zend_Feed_Rss object
+            return new Zend_Feed_Rss(null, $string);
+        }
+
+        // $string does not appear to be a valid feed of the supported types
+        /**
+         * @see Zend_Feed_Exception
+         */
+        require_once 'Zend/Feed/Exception.php';
+        throw new Zend_Feed_Exception('Invalid or unsupported feed format');
+    }
+
+
+    /**
+     * Imports a feed from a file located at $filename.
+     *
+     * @param  string $filename
+     * @throws Zend_Feed_Exception
+     * @return Zend_Feed_Abstract
+     */
+    public static function importFile($filename)
+    {
+        @ini_set('track_errors', 1);
+        $feed = @file_get_contents($filename);
+        @ini_restore('track_errors');
+        if ($feed === false) {
+            /**
+             * @see Zend_Feed_Exception
+             */
+            require_once 'Zend/Feed/Exception.php';
+            throw new Zend_Feed_Exception("File could not be loaded: $php_errormsg");
+        }
+        return self::importString($feed);
+    }
+
+
+    /**
+     * Attempts to find feeds at $uri referenced by <link ... /> tags. Returns an
+     * array of the feeds referenced at $uri.
+     *
+     * @todo Allow findFeeds() to follow one, but only one, code 302.
+     *
+     * @param  string $uri
+     * @throws Zend_Feed_Exception
+     * @return array
+     */
+    public static function findFeeds($uri)
+    {
+        // Get the HTTP response from $uri and save the contents
+        $client = self::getHttpClient();
+        $client->setUri($uri);
+        $response = $client->request();
+        if ($response->getStatus() !== 200) {
+            /**
+             * @see Zend_Feed_Exception
+             */
+            require_once 'Zend/Feed/Exception.php';
+            throw new Zend_Feed_Exception("Failed to access $uri, got response code " . $response->getStatus());
+        }
+        $contents = $response->getBody();
+
+        // Parse the contents for appropriate <link ... /> tags
+        @ini_set('track_errors', 1);
+        $pattern = '~(<link[^>]+)/?>~i';
+        $result = @preg_match_all($pattern, $contents, $matches);
+        @ini_restore('track_errors');
+        if ($result === false) {
+            /**
+             * @see Zend_Feed_Exception
+             */
+            require_once 'Zend/Feed/Exception.php';
+            throw new Zend_Feed_Exception("Internal error: $php_errormsg");
+        }
+
+        // Try to fetch a feed for each link tag that appears to refer to a feed
+        $feeds = array();
+        if (isset($matches[1]) && count($matches[1]) > 0) {
+            foreach ($matches[1] as $link) {
+                // force string to be an utf-8 one
+                if (!mb_check_encoding($link, 'UTF-8')) {
+                    $link = mb_convert_encoding($link, 'UTF-8');
+                }
+                $xml = @simplexml_load_string(rtrim($link, ' /') . ' />');
+                if ($xml === false) {
+                    continue;
+                }
+                $attributes = $xml->attributes();
+                if (!isset($attributes['rel']) || !@preg_match('~^(?:alternate|service\.feed)~i', $attributes['rel'])) {
+                    continue;
+                }
+                if (!isset($attributes['type']) ||
+                        !@preg_match('~^application/(?:atom|rss|rdf)\+xml~', $attributes['type'])) {
+                    continue;
+                }
+                if (!isset($attributes['href'])) {
+                    continue;
+                }
+                try {
+                    // checks if we need to canonize the given uri
+                    try {
+                        $uri = Zend_Uri::factory((string) $attributes['href']);
+                    } catch (Zend_Uri_Exception $e) {
+                        // canonize the uri
+                        $path = (string) $attributes['href'];
+                        $query = $fragment = '';
+                        if (substr($path, 0, 1) != '/') {
+                            // add the current root path to this one
+                            $path = rtrim($client->getUri()->getPath(), '/') . '/' . $path;
+                        }
+                        if (strpos($path, '?') !== false) {
+                            list($path, $query) = explode('?', $path, 2);
+                        }
+                        if (strpos($query, '#') !== false) {
+                            list($query, $fragment) = explode('#', $query, 2);
+                        }
+                        $uri = Zend_Uri::factory($client->getUri(true));
+                        $uri->setPath($path);
+                        $uri->setQuery($query);
+                        $uri->setFragment($fragment);
+                    }
+
+                    $feed = self::import($uri);
+                } catch (Exception $e) {
+                    continue;
+                }
+                $feeds[] = $feed;
+            }
+        }
+
+        // Return the fetched feeds
+        return $feeds;
+    }
+
+    /**
+     * Construct a new Zend_Feed_Abstract object from a custom array
+     *
+     * @param  array  $data
+     * @param  string $format (rss|atom) the requested output format
+     * @return Zend_Feed_Abstract
+     */
+    public static function importArray(array $data, $format = 'atom')
+    {
+        $obj = 'Zend_Feed_' . ucfirst(strtolower($format));
+        /**
+         * @see Zend_Loader
+         */
+        require_once 'Zend/Loader.php';
+        Zend_Loader::loadClass($obj);
+        Zend_Loader::loadClass('Zend_Feed_Builder');
+
+        return new $obj(null, null, new Zend_Feed_Builder($data));
+    }
+
+    /**
+     * Construct a new Zend_Feed_Abstract object from a Zend_Feed_Builder_Interface data source
+     *
+     * @param  Zend_Feed_Builder_Interface $builder this object will be used to extract the data of the feed
+     * @param  string                      $format (rss|atom) the requested output format
+     * @return Zend_Feed_Abstract
+     */
+    public static function importBuilder(Zend_Feed_Builder_Interface $builder, $format = 'atom')
+    {
+        $obj = 'Zend_Feed_' . ucfirst(strtolower($format));
+        /**
+         * @see Zend_Loader
+         */
+        require_once 'Zend/Loader.php';
+        Zend_Loader::loadClass($obj);
+
+        return new $obj(null, null, $builder);
+    }
+}

Added: incubator/shindig/trunk/php/src/common/Zend/Feed/Abstract.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/Zend/Feed/Abstract.php?rev=654331&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/common/Zend/Feed/Abstract.php (added)
+++ incubator/shindig/trunk/php/src/common/Zend/Feed/Abstract.php Wed May  7 16:48:15 2008
@@ -0,0 +1,258 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Feed
+ * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Abstract.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Feed_Element
+ */
+require_once 'Zend/Feed/Element.php';
+
+
+/**
+ * The Zend_Feed_Abstract class is an abstract class representing feeds.
+ *
+ * Zend_Feed_Abstract implements two core PHP 5 interfaces: ArrayAccess and
+ * Iterator. In both cases the collection being treated as an array is
+ * considered to be the entry collection, such that iterating over the
+ * feed takes you through each of the feed.s entries.
+ *
+ * @category   Zend
+ * @package    Zend_Feed
+ * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+abstract class Zend_Feed_Abstract extends Zend_Feed_Element implements Iterator
+{
+    /**
+     * Current index on the collection of feed entries for the
+     * Iterator implementation.
+     *
+     * @var integer
+     */
+    protected $_entryIndex = 0;
+
+    /**
+     * Cache of feed entries.
+     *
+     * @var array
+     */
+    protected $_entries;
+
+    /**
+     * Feed constructor
+     *
+     * The Zend_Feed_Abstract constructor takes the URI of a feed or a
+     * feed represented as a string and loads it as XML.
+     *
+     * @param  string $uri The full URI of the feed to load, or NULL if not retrieved via HTTP or as an array.
+     * @param  string $string The feed as a string, or NULL if retrieved via HTTP or as an array.
+     * @param  Zend_Feed_Builder_Interface $builder The feed as a builder instance or NULL if retrieved as a string or via HTTP.
+     * @return void
+     * @throws Zend_Feed_Exception If loading the feed failed.
+     */
+    public function __construct($uri = null, $string = null, Zend_Feed_Builder_Interface $builder = null)
+    {
+        if ($uri !== null) {
+            // Retrieve the feed via HTTP
+            $client = Zend_Feed::getHttpClient();
+            $client->setUri($uri);
+            $response = $client->request('GET');
+            if ($response->getStatus() !== 200) {
+                /** 
+                 * @see Zend_Feed_Exception
+                 */
+                require_once 'Zend/Feed/Exception.php';
+            	throw new Zend_Feed_Exception('Feed failed to load, got response code ' . $response->getStatus());
+            }
+            $this->_element = $response->getBody();
+            $this->__wakeup();
+        } elseif ($string !== null) {
+            // Retrieve the feed from $string
+            $this->_element = $string;
+            $this->__wakeup();
+        } else {
+            // Generate the feed from the array
+            $header = $builder->getHeader();
+            $this->_element = new DOMDocument('1.0', $header['charset']);
+            $root = $this->_mapFeedHeaders($header);
+            $this->_mapFeedEntries($root, $builder->getEntries());
+            $this->_element = $root;
+            $this->_buildEntryCache();
+        }
+    }
+
+
+    /**
+     * Load the feed as an XML DOMDocument object
+     *
+     * @return void
+     * @throws Zend_Feed_Exception
+     */
+    public function __wakeup()
+    {
+        @ini_set('track_errors', 1);
+        $doc = @DOMDocument::loadXML($this->_element);
+        @ini_restore('track_errors');
+
+        if (!$doc) {
+            // prevent the class to generate an undefined variable notice (ZF-2590)
+            if (!isset($php_errormsg)) {
+                if (function_exists('xdebug_is_enabled')) {
+                    $php_errormsg = '(error message not available, when XDebug is running)';
+                } else {
+                    $php_errormsg = '(error message not available)';
+                }
+            }
+            
+            /** 
+             * @see Zend_Feed_Exception
+             */
+            require_once 'Zend/Feed/Exception.php';
+            throw new Zend_Feed_Exception("DOMDocument cannot parse XML: $php_errormsg");
+        }
+
+        $this->_element = $doc;
+    }
+
+
+    /**
+     * Prepare for serialiation
+     *
+     * @return array
+     */
+    public function __sleep()
+    {
+        $this->_element = $this->saveXML();
+
+        return array('_element');
+    }
+
+
+    /**
+     * Cache the individual feed elements so they don't need to be
+     * searched for on every operation.
+     *
+     * @return void
+     */
+    protected function _buildEntryCache()
+    {
+        $this->_entries = array();
+        foreach ($this->_element->childNodes as $child) {
+            if ($child->localName == $this->_entryElementName) {
+                $this->_entries[] = $child;
+            }
+        }
+    }
+
+
+    /**
+     * Get the number of entries in this feed object.
+     *
+     * @return integer Entry count.
+     */
+    public function count()
+    {
+        return count($this->_entries);
+    }
+
+
+    /**
+     * Required by the Iterator interface.
+     *
+     * @return void
+     */
+    public function rewind()
+    {
+        $this->_entryIndex = 0;
+    }
+
+
+    /**
+     * Required by the Iterator interface.
+     *
+     * @return mixed The current row, or null if no rows.
+     */
+    public function current()
+    {
+        return new $this->_entryClassName(
+            null,
+            $this->_entries[$this->_entryIndex]);
+    }
+
+
+    /**
+     * Required by the Iterator interface.
+     *
+     * @return mixed The current row number (starts at 0), or NULL if no rows
+     */
+    public function key()
+    {
+        return $this->_entryIndex;
+    }
+
+
+    /**
+     * Required by the Iterator interface.
+     *
+     * @return mixed The next row, or null if no more rows.
+     */
+    public function next()
+    {
+        ++$this->_entryIndex;
+    }
+
+
+    /**
+     * Required by the Iterator interface.
+     *
+     * @return boolean Whether the iteration is valid
+     */
+    public function valid()
+    {
+        return 0 <= $this->_entryIndex && $this->_entryIndex < $this->count();
+    }
+
+    /**
+     * Generate the header of the feed when working in write mode
+     *
+     * @param  array $array the data to use
+     * @return DOMElement root node
+     */
+    abstract protected function _mapFeedHeaders($array);
+
+    /**
+     * Generate the entries of the feed when working in write mode
+     *
+     * @param  DOMElement $root the root node to use
+     * @param  array $array the data to use
+     * @return DOMElement root node
+     */
+    abstract protected function _mapFeedEntries(DOMElement $root, $array);
+
+    /**
+     * Send feed to a http client with the correct header
+     *
+     * @throws Zend_Feed_Exception if headers have already been sent
+     * @return void
+     */
+    abstract public function send();
+}

Added: incubator/shindig/trunk/php/src/common/Zend/Feed/Atom.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/Zend/Feed/Atom.php?rev=654331&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/common/Zend/Feed/Atom.php (added)
+++ incubator/shindig/trunk/php/src/common/Zend/Feed/Atom.php Wed May  7 16:48:15 2008
@@ -0,0 +1,390 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Feed
+ * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Atom.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Feed_Abstract
+ */
+require_once 'Zend/Feed/Abstract.php';
+
+/**
+ * @see Zend_Feed_Entry_Atom
+ */
+require_once 'Zend/Feed/Entry/Atom.php';
+
+
+/**
+ * Atom feed class
+ *
+ * The Zend_Feed_Atom class is a concrete subclass of the general
+ * Zend_Feed_Abstract class, tailored for representing an Atom
+ * feed. It shares all of the same methods with its abstract
+ * parent. The distinction is made in the format of data that
+ * Zend_Feed_Atom expects, and as a further pointer for users as to
+ * what kind of feed object they have been passed.
+ *
+ * @category   Zend
+ * @package    Zend_Feed
+ * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Feed_Atom extends Zend_Feed_Abstract
+{
+
+    /**
+     * The classname for individual feed elements.
+     *
+     * @var string
+     */
+    protected $_entryClassName = 'Zend_Feed_Entry_Atom';
+
+    /**
+     * The element name for individual feed elements (Atom <entry>
+     * elements).
+     *
+     * @var string
+     */
+    protected $_entryElementName = 'entry';
+
+    /**
+     * The default namespace for Atom feeds.
+     *
+     * @var string
+     */
+    protected $_defaultNamespace = 'atom';
+
+
+    /**
+     * Override Zend_Feed_Abstract to set up the $_element and $_entries aliases.
+     *
+     * @return void
+     * @throws Zend_Feed_Exception
+     */
+    public function __wakeup()
+    {
+        parent::__wakeup();
+
+        // Find the base feed element and create an alias to it.
+        $element = $this->_element->getElementsByTagName('feed')->item(0);
+        if (!$element) {
+            // Try to find a single <entry> instead.
+            $element = $this->_element->getElementsByTagName($this->_entryElementName)->item(0);
+            if (!$element) {
+                /** 
+                 * @see Zend_Feed_Exception
+                 */
+                require_once 'Zend/Feed/Exception.php';
+                throw new Zend_Feed_Exception('No root <feed> or <' . $this->_entryElementName
+                                              . '> element found, cannot parse feed.');
+            }
+
+            $doc = new DOMDocument($this->_element->version,
+                                   $this->_element->actualEncoding);
+            $feed = $doc->appendChild($doc->createElement('feed'));
+            $feed->appendChild($doc->importNode($element, true));
+            $element = $feed;
+        }
+
+        $this->_element = $element;
+
+        // Find the entries and save a pointer to them for speed and
+        // simplicity.
+        $this->_buildEntryCache();
+    }
+
+
+    /**
+     * Easy access to <link> tags keyed by "rel" attributes.
+     *
+     * If $elt->link() is called with no arguments, we will attempt to
+     * return the value of the <link> tag(s) like all other
+     * method-syntax attribute access. If an argument is passed to
+     * link(), however, then we will return the "href" value of the
+     * first <link> tag that has a "rel" attribute matching $rel:
+     *
+     * $elt->link(): returns the value of the link tag.
+     * $elt->link('self'): returns the href from the first <link rel="self"> in the entry.
+     *
+     * @param  string $rel The "rel" attribute to look for.
+     * @return mixed
+     */
+    public function link($rel = null)
+    {
+        if ($rel === null) {
+            return parent::__call('link', null);
+        }
+
+        // index link tags by their "rel" attribute.
+        $links = parent::__get('link');
+        if (!is_array($links)) {
+            if ($links instanceof Zend_Feed_Element) {
+                $links = array($links);
+            } else {
+                return $links;
+            }
+        }
+
+        foreach ($links as $link) {
+            if (empty($link['rel'])) {
+                continue;
+            }
+            if ($rel == $link['rel']) {
+                return $link['href'];
+            }
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Make accessing some individual elements of the feed easier.
+     *
+     * Special accessors 'entry' and 'entries' are provided so that if
+     * you wish to iterate over an Atom feed's entries, you can do so
+     * using foreach ($feed->entries as $entry) or foreach
+     * ($feed->entry as $entry).
+     *
+     * @param  string $var The property to access.
+     * @return mixed
+     */
+    public function __get($var)
+    {
+        switch ($var) {
+            case 'entry':
+                // fall through to the next case
+            case 'entries':
+                return $this;
+
+            default:
+                return parent::__get($var);
+        }
+    }
+
+    /**
+     * Generate the header of the feed when working in write mode
+     *
+     * @param  array $array the data to use
+     * @return DOMElement root node
+     */
+    protected function _mapFeedHeaders($array)
+    {
+        $feed = $this->_element->createElement('feed');
+        $feed->setAttribute('xmlns', 'http://www.w3.org/2005/Atom');
+
+        $id = $this->_element->createElement('id', $array->link);
+        $feed->appendChild($id);
+
+        $title = $this->_element->createElement('title');
+        $title->appendChild($this->_element->createCDATASection($array->title));
+        $feed->appendChild($title);
+
+        if (isset($array->author)) {
+            $author = $this->_element->createElement('author');
+            $name = $this->_element->createElement('name', $array->author);
+            $author->appendChild($name);
+            if (isset($array->email)) {
+                $email = $this->_element->createElement('email', $array->email);
+                $author->appendChild($email);
+            }
+            $feed->appendChild($author);
+        }
+
+        $updated = isset($array->lastUpdate) ? $array->lastUpdate : time();
+        $updated = $this->_element->createElement('updated', date(DATE_ATOM, $updated));
+        $feed->appendChild($updated);
+
+        if (isset($array->published)) {
+            $published = $this->_element->createElement('published', date(DATE_ATOM, $array->published));
+            $feed->appendChild($published);
+        }
+
+        $link = $this->_element->createElement('link');
+        $link->setAttribute('rel', 'self');
+        $link->setAttribute('href', $array->link);
+        if (isset($array->language)) {
+            $link->setAttribute('hreflang', $array->language);
+        }
+        $feed->appendChild($link);
+
+        if (isset($array->description)) {
+            $subtitle = $this->_element->createElement('subtitle');
+            $subtitle->appendChild($this->_element->createCDATASection($array->description));
+            $feed->appendChild($subtitle);
+        }
+
+        if (isset($array->copyright)) {
+            $copyright = $this->_element->createElement('rights', $array->copyright);
+            $feed->appendChild($copyright);
+        }
+
+        if (isset($array->image)) {
+            $image = $this->_element->createElement('logo', $array->image);
+            $feed->appendChild($image);
+        }
+
+        $generator = !empty($array->generator) ? $array->generator : 'Zend_Feed';
+        $generator = $this->_element->createElement('generator', $generator);
+        $feed->appendChild($generator);
+
+        return $feed;
+    }
+
+    /**
+     * Generate the entries of the feed when working in write mode
+     *
+     * The following nodes are constructed for each feed entry
+     * <entry>
+     *    <id>url to feed entry</id>
+     *    <title>entry title</title>
+     *    <updated>last update</updated>
+     *    <link rel="alternate" href="url to feed entry" />
+     *    <summary>short text</summary>
+     *    <content>long version, can contain html</content>
+     * </entry>
+     *
+     * @param  array      $array the data to use
+     * @param  DOMElement $root  the root node to use
+     * @return void
+     */
+    protected function _mapFeedEntries(DOMElement $root, $array)
+    {
+        foreach ($array as $dataentry) {
+            $entry = $this->_element->createElement('entry');
+
+            $id = $this->_element->createElement('id', isset($dataentry->guid) ? $dataentry->guid : $dataentry->link);
+            $entry->appendChild($id);
+
+            $title = $this->_element->createElement('title');
+            $title->appendChild($this->_element->createCDATASection($dataentry->title));
+            $entry->appendChild($title);
+
+            $updated = isset($dataentry->lastUpdate) ? $dataentry->lastUpdate : time();
+            $updated = $this->_element->createElement('updated', date(DATE_ATOM, $updated));
+            $entry->appendChild($updated);
+
+            $link = $this->_element->createElement('link');
+            $link->setAttribute('rel', 'alternate');
+            $link->setAttribute('href', $dataentry->link);
+            $entry->appendChild($link);
+
+            $summary = $this->_element->createElement('summary');
+            $summary->appendChild($this->_element->createCDATASection($dataentry->description));
+            $entry->appendChild($summary);
+
+            if (isset($dataentry->content)) {
+                $content = $this->_element->createElement('content');
+                $content->setAttribute('type', 'html');
+                $content->appendChild($this->_element->createCDATASection($dataentry->content));
+                $entry->appendChild($content);
+            }
+
+            if (isset($dataentry->category)) {
+                foreach ($dataentry->category as $category) {
+                    $node = $this->_element->createElement('category');
+                    $node->setAttribute('term', $category['term']);
+                    if (isset($category['scheme'])) {
+                        $node->setAttribute('scheme', $category['scheme']);
+                    }
+                    $entry->appendChild($node);
+                }
+            }
+
+            if (isset($dataentry->source)) {
+                $source = $this->_element->createElement('source');
+                $title = $this->_element->createElement('title', $dataentry->source['title']);
+                $source->appendChild($title);
+                $link = $this->_element->createElement('link', $dataentry->source['title']);
+                $link->setAttribute('rel', 'alternate');
+                $link->setAttribute('href', $dataentry->source['url']);
+                $source->appendChild($link);
+            }
+
+            if (isset($dataentry->enclosure)) {
+                foreach ($dataentry->enclosure as $enclosure) {
+                    $node = $this->_element->createElement('link');
+                    $node->setAttribute('rel', 'enclosure');
+                    $node->setAttribute('href', $enclosure['url']);
+                    if (isset($enclosure['type'])) {
+                        $node->setAttribute('type', $enclosure['type']);
+                    }
+                    if (isset($enclosure['length'])) {
+                        $node->setAttribute('length', $enclosure['length']);
+                    }
+                    $entry->appendChild($node);
+                }
+            }
+
+            if (isset($dataentry->comments)) {
+                $comments = $this->_element->createElementNS('http://wellformedweb.org/CommentAPI/',
+                                                             'wfw:comment',
+                                                             $dataentry->comments);
+                $entry->appendChild($comments);
+            }
+            if (isset($dataentry->commentRss)) {
+                $comments = $this->_element->createElementNS('http://wellformedweb.org/CommentAPI/',
+                                                             'wfw:commentRss',
+                                                             $dataentry->commentRss);
+                $entry->appendChild($comments);
+            }
+
+            $root->appendChild($entry);
+        }
+    }
+
+    /**
+     * Override Zend_Feed_Element to allow formated feeds
+     *
+     * @return string
+     */
+    public function saveXml()
+    {
+        // Return a complete document including XML prologue.
+        $doc = new DOMDocument($this->_element->ownerDocument->version,
+                               $this->_element->ownerDocument->actualEncoding);
+        $doc->appendChild($doc->importNode($this->_element, true));
+        $doc->formatOutput = true;
+
+        return $doc->saveXML();
+    }
+
+    /**
+     * Send feed to a http client with the correct header
+     *
+     * @return void
+     * @throws Zend_Feed_Exception if headers have already been sent
+     */
+    public function send()
+    {
+        if (headers_sent()) {
+            /** 
+             * @see Zend_Feed_Exception
+             */
+            require_once 'Zend/Feed/Exception.php';
+            throw new Zend_Feed_Exception('Cannot send ATOM because headers have already been sent.');
+        }
+
+        header('Content-type: application/atom+xml; charset: ' . $this->_element->ownerDocument->actualEncoding);
+
+        echo $this->saveXML();
+    }
+}

Added: incubator/shindig/trunk/php/src/common/Zend/Feed/Builder.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/Zend/Feed/Builder.php?rev=654331&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/common/Zend/Feed/Builder.php (added)
+++ incubator/shindig/trunk/php/src/common/Zend/Feed/Builder.php Wed May  7 16:48:15 2008
@@ -0,0 +1,395 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Feed
+ * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Builder.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Feed_Builder_Interface
+ */
+require_once 'Zend/Feed/Builder/Interface.php';
+
+/**
+ * @see Zend_Feed_Builder_Header
+ */
+require_once 'Zend/Feed/Builder/Header.php';
+
+/**
+ * @see Zend_Feed_Builder_Entry
+ */
+require_once 'Zend/Feed/Builder/Entry.php';
+
+
+/**
+ * A simple implementation of Zend_Feed_Builder_Interface.
+ *
+ * Users are encouraged to make their own classes to implement Zend_Feed_Builder_Interface
+ *
+ * @category   Zend
+ * @package    Zend_Feed
+ * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Feed_Builder implements Zend_Feed_Builder_Interface
+{
+    /**
+     * The data of the feed
+     *
+     * @var $_data array
+     */
+    private $_data;
+
+    /**
+     * Header of the feed
+     *
+     * @var $_header Zend_Feed_Builder_Header
+     */
+    private $_header;
+
+    /**
+     * List of the entries of the feed
+     *
+     * @var $_entries array
+     */
+    private $_entries = array();
+
+    /**
+     * Constructor. The $data array must conform to the following format:
+     * <code>
+     *  array(
+     *  'title'       => 'title of the feed', //required
+     *  'link'        => 'canonical url to the feed', //required
+     *  'lastUpdate'  => 'timestamp of the update date', // optional
+     *  'published'   => 'timestamp of the publication date', //optional
+     *  'charset'     => 'charset', // required
+     *  'description' => 'short description of the feed', //optional
+     *  'author'      => 'author/publisher of the feed', //optional
+     *  'email'       => 'email of the author', //optional
+     *  'webmaster'   => 'email address for person responsible for technical issues' // optional, ignored if atom is used
+     *  'copyright'   => 'copyright notice', //optional
+     *  'image'       => 'url to image', //optional
+     *  'generator'   => 'generator', // optional
+     *  'language'    => 'language the feed is written in', // optional
+     *  'ttl'         => 'how long in minutes a feed can be cached before refreshing', // optional, ignored if atom is used
+     *  'rating'      => 'The PICS rating for the channel.', // optional, ignored if atom is used
+     *  'cloud'       => array(
+     *                    'domain'            => 'domain of the cloud, e.g. rpc.sys.com' // required
+     *                    'port'              => 'port to connect to' // optional, default to 80
+     *                    'path'              => 'path of the cloud, e.g. /RPC2 //required
+     *                    'registerProcedure' => 'procedure to call, e.g. myCloud.rssPleaseNotify' // required
+     *                    'protocol'          => 'protocol to use, e.g. soap or xml-rpc' // required
+     *                    ), a cloud to be notified of updates // optional, ignored if atom is used
+     *  'textInput'   => array(
+     *                    'title'       => 'the label of the Submit button in the text input area' // required,
+     *                    'description' => 'explains the text input area' // required
+     *                    'name'        => 'the name of the text object in the text input area' // required
+     *                    'link'        => 'the URL of the CGI script that processes text input requests' // required
+     *                    ) // a text input box that can be displayed with the feed // optional, ignored if atom is used
+     *  'skipHours'   => array(
+     *                    'hour in 24 format', // e.g 13 (1pm)
+     *                    // up to 24 rows whose value is a number between 0 and 23
+     *                    ) // Hint telling aggregators which hours they can skip // optional, ignored if atom is used
+     *  'skipDays '   => array(
+     *                    'a day to skip', // e.g Monday
+     *                    // up to 7 rows whose value is a Monday, Tuesday, Wednesday, Thursday, Friday, Saturday or Sunday
+     *                    ) // Hint telling aggregators which days they can skip // optional, ignored if atom is used
+     *  'itunes'      => array(
+     *                    'author'       => 'Artist column' // optional, default to the main author value
+     *                    'owner'        => array(
+     *                                        'name' => 'name of the owner' // optional, default to main author value
+     *                                        'email' => 'email of the owner' // optional, default to main email value
+     *                                        ) // Owner of the podcast // optional
+     *                    'image'        => 'album/podcast art' // optional, default to the main image value
+     *                    'subtitle'     => 'short description' // optional, default to the main description value
+     *                    'summary'      => 'longer description' // optional, default to the main description value
+     *                    'block'        => 'Prevent an episode from appearing (yes|no)' // optional
+     *                    'category'     => array(
+     *                                      array('main' => 'main category', // required
+     *                                            'sub'  => 'sub category' // optional
+     *                                        ),
+     *                                        // up to 3 rows
+     *                                        ) // 'Category column and in iTunes Music Store Browse' // required
+     *                    'explicit'     => 'parental advisory graphic (yes|no|clean)' // optional
+     *                    'keywords'     => 'a comma separated list of 12 keywords maximum' // optional
+     *                    'new-feed-url' => 'used to inform iTunes of new feed URL location' // optional
+     *                    ) // Itunes extension data // optional, ignored if atom is used
+     *  'entries'     => array(
+     *                   array(
+     *                    'title'        => 'title of the feed entry', //required
+     *                    'link'         => 'url to a feed entry', //required
+     *                    'description'  => 'short version of a feed entry', // only text, no html, required
+     *                    'guid'         => 'id of the article, if not given link value will used', //optional
+     *                    'content'      => 'long version', // can contain html, optional
+     *                    'lastUpdate'   => 'timestamp of the publication date', // optional
+     *                    'comments'     => 'comments page of the feed entry', // optional
+     *                    'commentRss'   => 'the feed url of the associated comments', // optional
+     *                    'source'       => array(
+     *                                        'title' => 'title of the original source' // required,
+     *                                        'url' => 'url of the original source' // required
+     *                                           ) // original source of the feed entry // optional
+     *                    'category'     => array(
+     *                                      array(
+     *                                        'term' => 'first category label' // required,
+     *                                        'scheme' => 'url that identifies a categorization scheme' // optional
+     *                                            ),
+     *                                      array(
+     *                                         //data for the second category and so on
+     *                                           )
+     *                                        ) // list of the attached categories // optional
+     *                    'enclosure'    => array(
+     *                                      array(
+     *                                        'url' => 'url of the linked enclosure' // required
+     *                                        'type' => 'mime type of the enclosure' // optional
+     *                                        'length' => 'length of the linked content in octets' // optional
+     *                                           ),
+     *                                      array(
+     *                                         //data for the second enclosure and so on
+     *                                           )
+     *                                        ) // list of the enclosures of the feed entry // optional
+     *                   ),
+     *                   array(
+     *                   //data for the second entry and so on
+     *                   )
+     *                 )
+     * );
+     * </code>
+     *
+     * @param  array $data
+     * @return void
+     */
+    public function __construct(array $data)
+    {
+        $this->_data = $data;
+        $this->_createHeader($data);
+        if (isset($data['entries'])) {
+            $this->_createEntries($data['entries']);
+        }
+    }
+
+    /**
+     * Returns an instance of Zend_Feed_Builder_Header
+     * describing the header of the feed
+     *
+     * @return Zend_Feed_Builder_Header
+     */
+    public function getHeader()
+    {
+        return $this->_header;
+    }
+
+    /**
+     * Returns an array of Zend_Feed_Builder_Entry instances
+     * describing the entries of the feed
+     *
+     * @return array of Zend_Feed_Builder_Entry
+     */
+    public function getEntries()
+    {
+        return $this->_entries;
+    }
+
+    /**
+     * Create the Zend_Feed_Builder_Header instance
+     *
+     * @param  array $data
+     * @throws Zend_Feed_Builder_Exception
+     * @return void
+     */
+    private function _createHeader(array $data)
+    {
+        $mandatories = array('title', 'link', 'charset');
+        foreach ($mandatories as $mandatory) {
+            if (!isset($data[$mandatory])) {
+                /**
+                 * @see Zend_Feed_Builder_Exception
+                 */
+                require_once 'Zend/Feed/Builder/Exception.php';
+                throw new Zend_Feed_Builder_Exception("$mandatory key is missing");
+            }
+        }
+        $this->_header = new Zend_Feed_Builder_Header($data['title'], $data['link'], $data['charset']);
+        if (isset($data['lastUpdate'])) {
+            $this->_header->setLastUpdate($data['lastUpdate']);
+        }
+        if (isset($data['published'])) {
+            $this->_header->setPublishedDate($data['published']);
+        }
+        if (isset($data['description'])) {
+            $this->_header->setDescription($data['description']);
+        }
+        if (isset($data['author'])) {
+            $this->_header->setAuthor($data['author']);
+        }
+        if (isset($data['email'])) {
+            $this->_header->setEmail($data['email']);
+        }
+        if (isset($data['webmaster'])) {
+            $this->_header->setWebmaster($data['webmaster']);
+        }
+        if (isset($data['copyright'])) {
+            $this->_header->setCopyright($data['copyright']);
+        }
+        if (isset($data['image'])) {
+            $this->_header->setImage($data['image']);
+        }
+        if (isset($data['generator'])) {
+            $this->_header->setGenerator($data['generator']);
+        }
+        if (isset($data['language'])) {
+            $this->_header->setLanguage($data['language']);
+        }
+        if (isset($data['ttl'])) {
+            $this->_header->setTtl($data['ttl']);
+        }
+        if (isset($data['rating'])) {
+            $this->_header->setRating($data['rating']);
+        }
+        if (isset($data['cloud'])) {
+            $mandatories = array('domain', 'path', 'registerProcedure', 'protocol');
+            foreach ($mandatories as $mandatory) {
+                if (!isset($data['cloud'][$mandatory])) {
+                    /**
+                     * @see Zend_Feed_Builder_Exception
+                     */
+                    require_once 'Zend/Feed/Builder/Exception.php';
+                    throw new Zend_Feed_Builder_Exception("you have to define $mandatory property of your cloud");
+                }
+            }
+            $uri_str = 'http://' . $data['cloud']['domain'] . $data['cloud']['path'];
+            $this->_header->setCloud($uri_str, $data['cloud']['registerProcedure'], $data['cloud']['protocol']);
+        }
+        if (isset($data['textInput'])) {
+            $mandatories = array('title', 'description', 'name', 'link');
+            foreach ($mandatories as $mandatory) {
+                if (!isset($data['textInput'][$mandatory])) {
+                    /**
+                     * @see Zend_Feed_Builder_Exception
+                     */
+                    require_once 'Zend/Feed/Builder/Exception.php';
+                    throw new Zend_Feed_Builder_Exception("you have to define $mandatory property of your textInput");
+                }
+            }
+            $this->_header->setTextInput($data['textInput']['title'],
+                                         $data['textInput']['description'],
+                                         $data['textInput']['name'],
+                                         $data['textInput']['link']);
+        }
+        if (isset($data['skipHours'])) {
+            $this->_header->setSkipHours($data['skipHours']);
+        }
+        if (isset($data['skipDays'])) {
+            $this->_header->setSkipDays($data['skipDays']);
+        }
+        if (isset($data['itunes'])) {
+            $itunes = new Zend_Feed_Builder_Header_Itunes($data['itunes']['category']);
+            if (isset($data['itunes']['author'])) {
+                $itunes->setAuthor($data['itunes']['author']);
+            }
+            if (isset($data['itunes']['owner'])) {
+                $name = isset($data['itunes']['owner']['name']) ? $data['itunes']['owner']['name'] : '';
+                $email = isset($data['itunes']['owner']['email']) ? $data['itunes']['owner']['email'] : '';
+                $itunes->setOwner($name, $email);
+            }
+            if (isset($data['itunes']['image'])) {
+                $itunes->setImage($data['itunes']['image']);
+            }
+            if (isset($data['itunes']['subtitle'])) {
+                $itunes->setSubtitle($data['itunes']['subtitle']);
+            }
+            if (isset($data['itunes']['summary'])) {
+                $itunes->setSummary($data['itunes']['summary']);
+            }
+            if (isset($data['itunes']['block'])) {
+                $itunes->setBlock($data['itunes']['block']);
+            }
+            if (isset($data['itunes']['explicit'])) {
+                $itunes->setExplicit($data['itunes']['explicit']);
+            }
+            if (isset($data['itunes']['keywords'])) {
+                $itunes->setKeywords($data['itunes']['keywords']);
+            }
+            if (isset($data['itunes']['new-feed-url'])) {
+                $itunes->setNewFeedUrl($data['itunes']['new-feed-url']);
+            }
+
+            $this->_header->setITunes($itunes);
+        }
+    }
+
+    /**
+     * Create the array of article entries
+     *
+     * @param  array $data
+     * @throws Zend_Feed_Builder_Exception
+     * @return void
+     */
+    private function _createEntries(array $data)
+    {
+        foreach ($data as $row) {
+            $mandatories = array('title', 'link', 'description');
+            foreach ($mandatories as $mandatory) {
+                if (!isset($row[$mandatory])) {
+                    /**
+                     * @see Zend_Feed_Builder_Exception
+                     */
+                    require_once 'Zend/Feed/Builder/Exception.php';
+                    throw new Zend_Feed_Builder_Exception("$mandatory key is missing");
+                }
+            }
+            $entry = new Zend_Feed_Builder_Entry($row['title'], $row['link'], $row['description']);
+            if (isset($row['guid'])) {
+                $entry->setId($row['guid']);
+            }
+            if (isset($row['content'])) {
+                $entry->setContent($row['content']);
+            }
+            if (isset($row['lastUpdate'])) {
+                $entry->setLastUpdate($row['lastUpdate']);
+            }
+            if (isset($row['comments'])) {
+                $entry->setCommentsUrl($row['comments']);
+            }
+            if (isset($row['commentRss'])) {
+                $entry->setCommentsRssUrl($row['commentRss']);
+            }
+            if (isset($row['source'])) {
+                $mandatories = array('title', 'url');
+                foreach ($mandatories as $mandatory) {
+                    if (!isset($row['source'][$mandatory])) {
+                        /**
+                         * @see Zend_Feed_Builder_Exception
+                         */
+                        require_once 'Zend/Feed/Builder/Exception.php';
+                        throw new Zend_Feed_Builder_Exception("$mandatory key of source property is missing");
+                    }
+                }
+                $entry->setSource($row['source']['title'], $row['source']['url']);
+            }
+            if (isset($row['category'])) {
+                $entry->setCategories($row['category']);
+            }
+            if (isset($row['enclosure'])) {
+                $entry->setEnclosures($row['enclosure']);
+            }
+
+            $this->_entries[] = $entry;
+        }
+    }
+}
\ No newline at end of file

Added: incubator/shindig/trunk/php/src/common/Zend/Feed/Builder/Entry.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/Zend/Feed/Builder/Entry.php?rev=654331&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/common/Zend/Feed/Builder/Entry.php (added)
+++ incubator/shindig/trunk/php/src/common/Zend/Feed/Builder/Entry.php Wed May  7 16:48:15 2008
@@ -0,0 +1,285 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Feed
+ * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Entry.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * An entry of a custom build feed
+ *
+ * Classes implementing the Zend_Feed_Builder_Interface interface
+ * uses this class to describe an entry of a feed
+ *
+ * @category   Zend
+ * @package    Zend_Feed
+ * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Feed_Builder_Entry extends ArrayObject
+{
+    /**
+     * Create a new builder entry
+     *
+     * @param  string $title
+     * @param  string $link
+     * @param  string $description short version of the entry, no html
+     * @return void
+     */
+    public function __construct($title, $link, $description)
+    {
+        $this->offsetSet('title', $title);
+        $this->offsetSet('link', $link);
+        $this->offsetSet('description', $description);
+        $this->setLastUpdate(time());
+    }
+
+    /**
+     * Read only properties accessor
+     *
+     * @param  string $name property to read
+     * @return mixed
+     */
+    public function __get($name)
+    {
+        if (!$this->offsetExists($name)) {
+            return NULL;
+        }
+
+        return $this->offsetGet($name);
+    }
+
+    /**
+     * Write properties accessor
+     *
+     * @param  string $name name of the property to set
+     * @param  mixed $value value to set
+     * @return void
+     */
+    public function __set($name, $value)
+    {
+        $this->offsetSet($name, $value);
+    }
+
+    /**
+     * Isset accessor
+     *
+     * @param  string $key
+     * @return boolean
+     */
+    public function __isset($key)
+    {
+        return $this->offsetExists($key);
+    }
+
+    /**
+     * Unset accessor
+     *
+     * @param  string $key
+     * @return void
+     */
+    public function __unset($key)
+    {
+        if ($this->offsetExists($key)) {
+            $this->offsetUnset($key);
+        }
+    }
+
+    /**
+     * Sets the id/guid of the entry
+     *
+     * @param  string $id
+     * @return Zend_Feed_Builder_Entry
+     */
+    public function setId($id)
+    {
+        $this->offsetSet('guid', $id);
+        return $this;
+    }
+
+    /**
+     * Sets the full html content of the entry
+     *
+     * @param  string $content
+     * @return Zend_Feed_Builder_Entry
+     */
+    public function setContent($content)
+    {
+        $this->offsetSet('content', $content);
+        return $this;
+    }
+
+    /**
+     * Timestamp of the update date
+     *
+     * @param  int $lastUpdate
+     * @return Zend_Feed_Builder_Entry
+     */
+    public function setLastUpdate($lastUpdate)
+    {
+        $this->offsetSet('lastUpdate', $lastUpdate);
+        return $this;
+    }
+
+    /**
+     * Sets the url of the commented page associated to the entry
+     *
+     * @param  string $comments
+     * @return Zend_Feed_Builder_Entry
+     */
+    public function setCommentsUrl($comments)
+    {
+        $this->offsetSet('comments', $comments);
+        return $this;
+    }
+
+    /**
+     * Sets the url of the comments feed link
+     *
+     * @param  string $commentRss
+     * @return Zend_Feed_Builder_Entry
+     */
+    public function setCommentsRssUrl($commentRss)
+    {
+        $this->offsetSet('commentRss', $commentRss);
+        return $this;
+    }
+
+    /**
+     * Defines a reference to the original source
+     *
+     * @param  string $title
+     * @param  string $url
+     * @return Zend_Feed_Builder_Entry
+     */
+    public function setSource($title, $url)
+    {
+        $this->offsetSet('source', array('title' => $title,
+                                         'url' => $url));
+        return $this;
+    }
+
+    /**
+     * Sets the categories of the entry
+     * Format of the array:
+     * <code>
+     * array(
+     *   array(
+     *         'term' => 'first category label',
+     *         'scheme' => 'url that identifies a categorization scheme' // optional
+     *        ),
+     *   // second category and so one
+     * )
+     * </code>
+     *
+     * @param  array $categories
+     * @return Zend_Feed_Builder_Entry
+     */
+    public function setCategories(array $categories)
+    {
+        foreach ($categories as $category) {
+            $this->addCategory($category);
+        }
+        return $this;
+    }
+
+    /**
+     * Add a category to the entry
+     *
+     * @param  array $category see Zend_Feed_Builder_Entry::setCategories() for format
+     * @return Zend_Feed_Builder_Entry
+     * @throws Zend_Feed_Builder_Exception
+     */
+    public function addCategory(array $category)
+    {
+        if (empty($category['term'])) {
+            /**
+             * @see Zend_Feed_Builder_Exception
+             */
+            require_once 'Zend/Feed/Builder/Exception.php';
+            throw new Zend_Feed_Builder_Exception("you have to define the name of the category");
+        }
+
+        if (!$this->offsetExists('category')) {
+            $categories = array($category);
+        } else {
+            $categories = $this->offsetGet('category');
+            $categories[] = $category;
+        }
+        $this->offsetSet('category', $categories);
+        return $this;
+    }
+
+    /**
+     * Sets the enclosures of the entry
+     * Format of the array:
+     * <code>
+     * array(
+     *   array(
+     *         'url' => 'url of the linked enclosure',
+     *         'type' => 'mime type of the enclosure' // optional
+     *         'length' => 'length of the linked content in octets' // optional
+     *        ),
+     *   // second enclosure and so one
+     * )
+     * </code>
+     *
+     * @param  array $enclosures
+     * @return Zend_Feed_Builder_Entry
+     * @throws Zend_Feed_Builder_Exception
+     */
+    public function setEnclosures(array $enclosures)
+    {
+        foreach ($enclosures as $enclosure) {
+            if (empty($enclosure['url'])) {
+                /**
+                 * @see Zend_Feed_Builder_Exception
+                 */
+                require_once 'Zend/Feed/Builder/Exception.php';
+                throw new Zend_Feed_Builder_Exception("you have to supply an url for your enclosure");
+            }
+            $type = isset($enclosure['type']) ? $enclosure['type'] : '';
+            $length = isset($enclosure['length']) ? $enclosure['length'] : '';
+            $this->addEnclosure($enclosure['url'], $type, $length);
+        }
+        return $this;
+    }
+
+    /**
+     * Add an enclosure to the entry
+     *
+     * @param  string $url
+     * @param  string $type
+     * @param  string $length
+     * @return Zend_Feed_Builder_Entry
+     */
+    public function addEnclosure($url, $type = '', $length = '')
+    {
+        if (!$this->offsetExists('enclosure')) {
+            $enclosure = array();
+        } else {
+            $enclosure = $this->offsetGet('enclosure');
+        }
+        $enclosure[] = array('url' => $url,
+                             'type' => $type,
+                             'length' => $length);
+        $this->offsetSet('enclosure', $enclosure);
+        return $this;
+    }
+}

Added: incubator/shindig/trunk/php/src/common/Zend/Feed/Builder/Exception.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/Zend/Feed/Builder/Exception.php?rev=654331&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/common/Zend/Feed/Builder/Exception.php (added)
+++ incubator/shindig/trunk/php/src/common/Zend/Feed/Builder/Exception.php Wed May  7 16:48:15 2008
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Feed
+ * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Exception.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Feed_Exception
+ */
+require_once 'Zend/Feed/Exception.php';
+
+
+/**
+ * Zend_Feed_Builder exception class
+ *
+ * @category   Zend
+ * @package    Zend_Feed
+ * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Feed_Builder_Exception extends Zend_Feed_Exception
+{
+}

Added: incubator/shindig/trunk/php/src/common/Zend/Feed/Builder/Header.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/Zend/Feed/Builder/Header.php?rev=654331&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/common/Zend/Feed/Builder/Header.php (added)
+++ incubator/shindig/trunk/php/src/common/Zend/Feed/Builder/Header.php Wed May  7 16:48:15 2008
@@ -0,0 +1,420 @@
+<?php
+
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Feed
+ * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Header.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Loader
+ */
+require_once 'Zend/Loader.php';
+
+/**
+ * @see Zend_Feed_Builder_Header_Itunes
+ */
+require_once 'Zend/Feed/Builder/Header/Itunes.php';
+
+/**
+ * @see Zend_Uri
+ */
+require_once 'Zend/Uri.php';
+
+
+/**
+ * Header of a custom build feed
+ *
+ * Classes implementing the Zend_Feed_Builder_Interface interface
+ * uses this class to describe the header of a feed
+ *
+ * @category   Zend
+ * @package    Zend_Feed
+ * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Feed_Builder_Header extends ArrayObject
+{
+    /**
+     * Constructor
+     *
+     * @param  string $title title of the feed
+     * @param  string $link canonical url of the feed
+     * @param  string $charset charset of the textual data
+     * @return void
+     */
+    public function __construct($title, $link, $charset = 'utf-8')
+    {
+        $this->offsetSet('title', $title);
+        $this->offsetSet('link', $link);
+        $this->offsetSet('charset', $charset);
+        $this->setLastUpdate(time())
+             ->setGenerator('Zend_Feed');
+    }
+
+    /**
+     * Read only properties accessor
+     *
+     * @param  string $name property to read
+     * @return mixed
+     */
+    public function __get($name)
+    {
+        if (!$this->offsetExists($name)) {
+            return NULL;
+        }
+
+        return $this->offsetGet($name);
+    }
+
+    /**
+     * Write properties accessor
+     *
+     * @param string $name  name of the property to set
+     * @param mixed  $value value to set
+     * @return void
+     */
+    public function __set($name, $value)
+    {
+        $this->offsetSet($name, $value);
+    }
+
+    /**
+     * Isset accessor
+     *
+     * @param  string $key
+     * @return boolean
+     */
+    public function __isset($key)
+    {
+        return $this->offsetExists($key);
+    }
+
+    /**
+     * Unset accessor
+     *
+     * @param  string $key
+     * @return void
+     */
+    public function __unset($key)
+    {
+        if ($this->offsetExists($key)) {
+            $this->offsetUnset($key);
+        }
+    }
+
+    /**
+     * Timestamp of the update date
+     *
+     * @param  int $lastUpdate
+     * @return Zend_Feed_Builder_Header
+     */
+    public function setLastUpdate($lastUpdate)
+    {
+        $this->offsetSet('lastUpdate', $lastUpdate);
+        return $this;
+    }
+
+    /**
+     * Timestamp of the publication date
+     *
+     * @param  int $published
+     * @return Zend_Feed_Builder_Header
+     */
+    public function setPublishedDate($published)
+    {
+        $this->offsetSet('published', $published);
+        return $this;
+    }
+
+    /**
+     * Short description of the feed
+     *
+     * @param  string $description
+     * @return Zend_Feed_Builder_Header
+     */
+    public function setDescription($description)
+    {
+        $this->offsetSet('description', $description);
+        return $this;
+    }
+
+    /**
+     * Sets the author of the feed
+     *
+     * @param  string $author
+     * @return Zend_Feed_Builder_Header
+     */
+    public function setAuthor($author)
+    {
+        $this->offsetSet('author', $author);
+        return $this;
+    }
+
+    /**
+     * Sets the author's email
+     *
+     * @param  string $email
+     * @return Zend_Feed_Builder_Header
+     * @throws Zend_Feed_Builder_Exception
+     */
+    public function setEmail($email)
+    {
+        Zend_Loader::loadClass('Zend_Validate_EmailAddress');
+        $validate = new Zend_Validate_EmailAddress();
+        if (!$validate->isValid($email)) {
+            /**
+             * @see Zend_Feed_Builder_Exception
+             */
+            require_once 'Zend/Feed/Builder/Exception.php';
+            throw new Zend_Feed_Builder_Exception("you have to set a valid email address into the email property");
+        }
+        $this->offsetSet('email', $email);
+        return $this;
+    }
+
+    /**
+     * Sets the copyright notice
+     *
+     * @param  string $copyright
+     * @return Zend_Feed_Builder_Header
+     */
+    public function setCopyright($copyright)
+    {
+        $this->offsetSet('copyright', $copyright);
+        return $this;
+    }
+
+    /**
+     * Sets the image of the feed
+     *
+     * @param  string $image
+     * @return Zend_Feed_Builder_Header
+     */
+    public function setImage($image)
+    {
+        $this->offsetSet('image', $image);
+        return $this;
+    }
+
+    /**
+     * Sets the generator of the feed
+     *
+     * @param  string $generator
+     * @return Zend_Feed_Builder_Header
+     */
+    public function setGenerator($generator)
+    {
+        $this->offsetSet('generator', $generator);
+        return $this;
+    }
+
+    /**
+     * Sets the language of the feed
+     *
+     * @param  string $language
+     * @return Zend_Feed_Builder_Header
+     */
+    public function setLanguage($language)
+    {
+        $this->offsetSet('language', $language);
+        return $this;
+    }
+
+    /**
+     * Email address for person responsible for technical issues
+     * Ignored if atom is used
+     *
+     * @param  string $webmaster
+     * @return Zend_Feed_Builder_Header
+     * @throws Zend_Feed_Builder_Exception
+     */
+    public function setWebmaster($webmaster)
+    {
+        Zend_Loader::loadClass('Zend_Validate_EmailAddress');
+        $validate = new Zend_Validate_EmailAddress();
+        if (!$validate->isValid($webmaster)) {
+            /**
+             * @see Zend_Feed_Builder_Exception
+             */
+            require_once 'Zend/Feed/Builder/Exception.php';
+            throw new Zend_Feed_Builder_Exception("you have to set a valid email address into the webmaster property");
+        }
+        $this->offsetSet('webmaster', $webmaster);
+        return $this;
+    }
+
+    /**
+     * How long in minutes a feed can be cached before refreshing
+     * Ignored if atom is used
+     *
+     * @param  int $ttl
+     * @return Zend_Feed_Builder_Header
+     * @throws Zend_Feed_Builder_Exception
+     */
+    public function setTtl($ttl)
+    {
+        Zend_Loader::loadClass('Zend_Validate_Int');
+        $validate = new Zend_Validate_Int();
+        if (!$validate->isValid($ttl)) {
+            /**
+             * @see Zend_Feed_Builder_Exception
+             */
+            require_once 'Zend/Feed/Builder/Exception.php';
+            throw new Zend_Feed_Builder_Exception("you have to set an integer value to the ttl property");
+        }
+        $this->offsetSet('ttl', $ttl);
+        return $this;
+    }
+
+    /**
+     * PICS rating for the feed
+     * Ignored if atom is used
+     *
+     * @param  string $rating
+     * @return Zend_Feed_Builder_Header
+     */
+    public function setRating($rating)
+    {
+        $this->offsetSet('rating', $rating);
+        return $this;
+    }
+
+    /**
+     * Cloud to be notified of updates of the feed
+     * Ignored if atom is used
+     *
+     * @param  string|Zend_Uri_Http $uri
+     * @param  string               $procedure procedure to call, e.g. myCloud.rssPleaseNotify
+     * @param  string               $protocol  protocol to use, e.g. soap or xml-rpc
+     * @return Zend_Feed_Builder_Header
+     * @throws Zend_Feed_Builder_Exception
+     */
+    public function setCloud($uri, $procedure, $protocol)
+    {
+        if (is_string($uri) && Zend_Uri_Http::check($uri)) {
+            $uri = Zend_Uri::factory($uri);
+        }
+        if (!$uri instanceof Zend_Uri_Http) {
+            /**
+             * @see Zend_Feed_Builder_Exception
+             */
+            require_once 'Zend/Feed/Builder/Exception.php';
+            throw new Zend_Feed_Builder_Exception('Passed parameter is not a valid HTTP URI');
+        }
+        if (!$uri->getPort()) {
+            $uri->setPort(80);
+        }
+        $this->offsetSet('cloud', array('uri' => $uri,
+                                        'procedure' => $procedure,
+                                        'protocol' => $protocol));
+        return $this;
+    }
+
+    /**
+     * A text input box that can be displayed with the feed
+     * Ignored if atom is used
+     *
+     * @param  string $title       the label of the Submit button in the text input area
+     * @param  string $description explains the text input area
+     * @param  string $name        the name of the text object in the text input area
+     * @param  string $link        the URL of the CGI script that processes text input requests
+     * @return Zend_Feed_Builder_Header
+     */
+    public function setTextInput($title, $description, $name, $link)
+    {
+        $this->offsetSet('textInput', array('title' => $title,
+                                            'description' => $description,
+                                            'name' => $name,
+                                            'link' => $link));
+        return $this;
+    }
+
+    /**
+     * Hint telling aggregators which hours they can skip
+     * Ignored if atom is used
+     *
+     * @param  array $hours list of hours in 24 format
+     * @return Zend_Feed_Builder_Header
+     * @throws Zend_Feed_Builder_Exception
+     */
+    public function setSkipHours(array $hours)
+    {
+        if (count($hours) > 24) {
+            /**
+             * @see Zend_Feed_Builder_Exception
+             */
+            require_once 'Zend/Feed/Builder/Exception.php';
+            throw new Zend_Feed_Builder_Exception("you can not have more than 24 rows in the skipHours property");
+        }
+        foreach ($hours as $hour) {
+            if ($hour < 0 || $hour > 23) {
+                /**
+                 * @see Zend_Feed_Builder_Exception
+                 */
+                require_once 'Zend/Feed/Builder/Exception.php';
+                throw new Zend_Feed_Builder_Exception("$hour has te be between 0 and 23");
+            }
+        }
+        $this->offsetSet('skipHours', $hours);
+        return $this;
+    }
+
+    /**
+     * Hint telling aggregators which days they can skip
+     * Ignored if atom is used
+     *
+     * @param  array $days list of days to skip, e.g. Monday
+     * @return Zend_Feed_Builder_Header
+     * @throws Zend_Feed_Builder_Exception
+     */
+    public function setSkipDays(array $days)
+    {
+        if (count($days) > 7) {
+            /**
+             * @see Zend_Feed_Builder_Exception
+             */
+            require_once 'Zend/Feed/Builder/Exception.php';
+            throw new Zend_Feed_Builder_Exception("you can not have more than 7 days in the skipDays property");
+        }
+        $valid = array('monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday');
+        foreach ($days as $day) {
+            if (!in_array(strtolower($day), $valid)) {
+                /**
+                 * @see Zend_Feed_Builder_Exception
+                 */
+                require_once 'Zend/Feed/Builder/Exception.php';
+                throw new Zend_Feed_Builder_Exception("$day is not a valid day");
+            }
+        }
+        $this->offsetSet('skipDays', $days);
+        return $this;
+    }
+
+    /**
+     * Sets the iTunes rss extension
+     *
+     * @param  Zend_Feed_Builder_Header_Itunes $itunes
+     * @return Zend_Feed_Builder_Header
+     */
+    public function setITunes(Zend_Feed_Builder_Header_Itunes $itunes)
+    {
+        $this->offsetSet('itunes', $itunes);
+        return $this;
+    }
+}