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 [2/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...

Added: incubator/shindig/trunk/php/src/common/Zend/Feed/Builder/Header/Itunes.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/Zend/Feed/Builder/Header/Itunes.php?rev=654331&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/common/Zend/Feed/Builder/Header/Itunes.php (added)
+++ incubator/shindig/trunk/php/src/common/Zend/Feed/Builder/Header/Itunes.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: Itunes.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * ITunes rss extension
+ *
+ * Classes used to describe the itunes channel extension
+ *
+ * @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_Itunes extends ArrayObject
+{
+    /**
+     * Constructor
+     *
+     * @param  array $categories Categories columns and in iTunes Music Store Browse
+     * @return void
+     */
+    public function __construct(array $categories)
+    {
+        $this->setCategories($categories);
+    }
+
+    /**
+     * Sets the categories column and in iTunes Music Store Browse
+     * $categories must conform to the following format:
+     * <code>
+     * array(array('main' => 'main category',
+     *             'sub' => 'sub category' // optionnal
+     *            ),
+     *       // up to 3 rows
+     *      )
+     * </code>
+     *
+     * @param  array $categories
+     * @return Zend_Feed_Builder_Header_Itunes
+     * @throws Zend_Feed_Builder_Exception
+     */
+    public function setCategories(array $categories)
+    {
+        $nb = count($categories);
+        if (0 === $nb) {
+            /**
+             * @see Zend_Feed_Builder_Exception
+             */
+            require_once 'Zend/Feed/Builder/Exception.php';
+            throw new Zend_Feed_Builder_Exception("you have to set at least one itunes category");
+        }
+        if ($nb > 3) {
+            /**
+             * @see Zend_Feed_Builder_Exception
+             */
+            require_once 'Zend/Feed/Builder/Exception.php';
+            throw new Zend_Feed_Builder_Exception("you have to set at most three itunes categories");
+        }
+        foreach ($categories as $i => $category) {
+            if (empty($category['main'])) {
+                /**
+                 * @see Zend_Feed_Builder_Exception
+                 */
+                require_once 'Zend/Feed/Builder/Exception.php';
+                throw new Zend_Feed_Builder_Exception("you have to set the main category (category #$i)");
+            }
+        }
+        $this->offsetSet('category', $categories);
+        return $this;
+    }
+
+    /**
+     * Sets the artist value, default to the feed's author value
+     *
+     * @param  string $author
+     * @return Zend_Feed_Builder_Header_Itunes
+     */
+    public function setAuthor($author)
+    {
+        $this->offsetSet('author', $author);
+        return $this;
+    }
+
+    /**
+     * Sets the owner of the postcast
+     *
+     * @param  string $name  default to the feed's author value
+     * @param  string $email default to the feed's email value
+     * @return Zend_Feed_Builder_Header_Itunes
+     * @throws Zend_Feed_Builder_Exception
+     */
+    public function setOwner($name = '', $email = '')
+    {
+        if (!empty($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 itunes owner's email property");
+            }
+        }
+        $this->offsetSet('owner', array('name' => $name, 'email' => $email));
+        return $this;
+    }
+
+    /**
+     * Sets the album/podcast art picture
+     * Default to the feed's image value
+     *
+     * @param  string $image
+     * @return Zend_Feed_Builder_Header_Itunes
+     */
+    public function setImage($image)
+    {
+        $this->offsetSet('image', $image);
+        return $this;
+    }
+
+    /**
+     * Sets the short description of the podcast
+     * Default to the feed's description
+     *
+     * @param  string $subtitle
+     * @return Zend_Feed_Builder_Header_Itunes
+     */
+    public function setSubtitle($subtitle)
+    {
+        $this->offsetSet('subtitle', $subtitle);
+        return $this;
+    }
+
+    /**
+     * Sets the longer description of the podcast
+     * Default to the feed's description
+     *
+     * @param  string $summary
+     * @return Zend_Feed_Builder_Header_Itunes
+     */
+    public function setSummary($summary)
+    {
+        $this->offsetSet('summary', $summary);
+        return $this;
+    }
+
+    /**
+     * Prevent a feed from appearing
+     *
+     * @param  string $block can be 'yes' or 'no'
+     * @return Zend_Feed_Builder_Header_Itunes
+     * @throws Zend_Feed_Builder_Exception
+     */
+    public function setBlock($block)
+    {
+        $block = strtolower($block);
+        if (!in_array($block, array('yes', 'no'))) {
+            /**
+             * @see Zend_Feed_Builder_Exception
+             */
+            require_once 'Zend/Feed/Builder/Exception.php';
+            throw new Zend_Feed_Builder_Exception("you have to set yes or no to the itunes block property");
+        }
+        $this->offsetSet('block', $block);
+        return $this;
+    }
+
+    /**
+     * Configuration of the parental advisory graphic
+     *
+     * @param  string $explicit can be 'yes', 'no' or 'clean'
+     * @return Zend_Feed_Builder_Header_Itunes
+     * @throws Zend_Feed_Builder_Exception
+     */
+    public function setExplicit($explicit)
+    {
+        $explicit = strtolower($explicit);
+        if (!in_array($explicit, array('yes', 'no', 'clean'))) {
+            /**
+             * @see Zend_Feed_Builder_Exception
+             */
+            require_once 'Zend/Feed/Builder/Exception.php';
+            throw new Zend_Feed_Builder_Exception("you have to set yes, no or clean to the itunes explicit property");
+        }
+        $this->offsetSet('explicit', $explicit);
+        return $this;
+    }
+
+    /**
+     * Sets a comma separated list of 12 keywords maximum
+     *
+     * @param  string $keywords
+     * @return Zend_Feed_Builder_Header_Itunes
+     */
+    public function setKeywords($keywords)
+    {
+        $this->offsetSet('keywords', $keywords);
+        return $this;
+    }
+
+    /**
+     * Sets the new feed URL location
+     *
+     * @param  string $url
+     * @return Zend_Feed_Builder_Header_Itunes
+     */
+    public function setNewFeedUrl($url)
+    {
+        $this->offsetSet('new_feed_url', $url);
+        return $this;
+    }
+
+    /**
+     * 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);
+        }
+    }
+
+}
\ No newline at end of file

Added: incubator/shindig/trunk/php/src/common/Zend/Feed/Builder/Interface.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/Zend/Feed/Builder/Interface.php?rev=654331&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/common/Zend/Feed/Builder/Interface.php (added)
+++ incubator/shindig/trunk/php/src/common/Zend/Feed/Builder/Interface.php Wed May  7 16:48:15 2008
@@ -0,0 +1,52 @@
+<?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: Interface.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * Input feed data interface
+ *
+ * Classes implementing this interface can be passe to Zend_Feed::importBuilder
+ * as an input data source for the Zend_Feed construction
+ *
+ * @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
+ */
+interface Zend_Feed_Builder_Interface
+{
+    /**
+     * Returns an instance of Zend_Feed_Builder_Header
+     * describing the header of the feed
+     *
+     * @return Zend_Feed_Builder_Header
+     */
+    public function getHeader();
+
+    /**
+     * 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();
+}

Added: incubator/shindig/trunk/php/src/common/Zend/Feed/Element.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/Zend/Feed/Element.php?rev=654331&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/common/Zend/Feed/Element.php (added)
+++ incubator/shindig/trunk/php/src/common/Zend/Feed/Element.php Wed May  7 16:48:15 2008
@@ -0,0 +1,408 @@
+<?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: Element.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * Wraps a DOMElement allowing for SimpleXML-like access to attributes.
+ *
+ * @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_Element implements ArrayAccess
+{
+
+    /**
+     * @var DOMElement
+     */
+    protected $_element;
+
+    /**
+     * @var Zend_Feed_Element
+     */
+    protected $_parentElement;
+
+    /**
+     * @var boolean
+     */
+    protected $_appended = true;
+
+
+    /**
+     * Zend_Feed_Element constructor.
+     *
+     * @param  DOMElement $element The DOM element we're encapsulating.
+     * @return void
+     */
+    public function __construct($element = null)
+    {
+        $this->_element = $element;
+    }
+
+
+    /**
+     * Get a DOM representation of the element
+     *
+     * Returns the underlying DOM object, which can then be
+     * manipulated with full DOM methods.
+     *
+     * @return DOMDocument
+     */
+    public function getDOM()
+    {
+        return $this->_element;
+    }
+
+
+    /**
+     * Update the object from a DOM element
+     *
+     * Take a DOMElement object, which may be originally from a call
+     * to getDOM() or may be custom created, and use it as the
+     * DOM tree for this Zend_Feed_Element.
+     *
+     * @param  DOMElement $element
+     * @return void
+     */
+    public function setDOM(DOMElement $element)
+    {
+        $this->_element = $this->_element->ownerDocument->importNode($element, true);
+    }
+
+    /**
+     * Set the parent element of this object to another
+     * Zend_Feed_Element.
+     *
+     * @param  Zend_Feed_Element $element
+     * @return void
+     */
+    public function setParent(Zend_Feed_Element $element)
+    {
+        $this->_parentElement = $element;
+        $this->_appended = false;
+    }
+
+
+    /**
+     * Appends this element to its parent if necessary.
+     *
+     * @return void
+     */
+    protected function ensureAppended()
+    {
+        if (!$this->_appended) {
+            $this->_parentElement->getDOM()->appendChild($this->_element);
+            $this->_appended = true;
+            $this->_parentElement->ensureAppended();
+        }
+    }
+
+
+    /**
+     * Get an XML string representation of this element
+     *
+     * Returns a string of this element's XML, including the XML
+     * prologue.
+     *
+     * @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));
+        return $doc->saveXML();
+    }
+
+
+    /**
+     * Get the XML for only this element
+     *
+     * Returns a string of this element's XML without prologue.
+     *
+     * @return string
+     */
+    public function saveXmlFragment()
+    {
+        return $this->_element->ownerDocument->saveXML($this->_element);
+    }
+
+
+    /**
+     * Map variable access onto the underlying entry representation.
+     *
+     * Get-style access returns a Zend_Feed_Element representing the
+     * child element accessed. To get string values, use method syntax
+     * with the __call() overriding.
+     *
+     * @param  string $var The property to access.
+     * @return mixed
+     */
+    public function __get($var)
+    {
+        $nodes = $this->_children($var);
+        $length = count($nodes);
+
+        if ($length == 1) {
+            return new Zend_Feed_Element($nodes[0]);
+        } elseif ($length > 1) {
+            return array_map(create_function('$e', 'return new Zend_Feed_Element($e);'), $nodes);
+        } else {
+            // When creating anonymous nodes for __set chaining, don't
+            // call appendChild() on them. Instead we pass the current
+            // element to them as an extra reference; the child is
+            // then responsible for appending itself when it is
+            // actually set. This way "if ($foo->bar)" doesn't create
+            // a phantom "bar" element in our tree.
+            if (strpos($var, ':') !== false) {
+                list($ns, $elt) = explode(':', $var, 2);
+                $node = $this->_element->ownerDocument->createElementNS(Zend_Feed::lookupNamespace($ns), $elt);
+            } else {
+                $node = $this->_element->ownerDocument->createElement($var);
+            }
+            $node = new self($node);
+            $node->setParent($this);
+            return $node;
+        }
+    }
+
+
+    /**
+     * Map variable sets onto the underlying entry representation.
+     *
+     * @param  string $var The property to change.
+     * @param  string $val The property's new value.
+     * @return void
+     * @throws Zend_Feed_Exception
+     */
+    public function __set($var, $val)
+    {
+        $this->ensureAppended();
+
+        $nodes = $this->_children($var);
+        if (!$nodes) {
+            if (strpos($var, ':') !== false) {
+                list($ns, $elt) = explode(':', $var, 2);
+                $node = $this->_element->ownerDocument->createElementNS(Zend_Feed::lookupNamespace($ns), $var, $val);
+                $this->_element->appendChild($node);
+            } else {
+                $node = $this->_element->ownerDocument->createElement($var, $val);
+                $this->_element->appendChild($node);
+            }
+        } elseif (count($nodes) > 1) {
+            /** 
+             * @see Zend_Feed_Exception
+             */
+            require_once 'Zend/Feed/Exception.php';
+            throw new Zend_Feed_Exception('Cannot set the value of multiple tags simultaneously.');
+        } else {
+            $nodes[0]->nodeValue = $val;
+        }
+    }
+
+
+    /**
+     * Map isset calls onto the underlying entry representation.
+     *
+     * @param  string $var
+     * @return boolean
+     */
+    public function __isset($var)
+    {
+        // Look for access of the form {ns:var}. We don't use
+        // _children() here because we can break out of the loop
+        // immediately once we find something.
+        if (strpos($var, ':') !== false) {
+            list($ns, $elt) = explode(':', $var, 2);
+            foreach ($this->_element->childNodes as $child) {
+                if ($child->localName == $elt && $child->prefix == $ns) {
+                    return true;
+                }
+            }
+        } else {
+            foreach ($this->_element->childNodes as $child) {
+                if ($child->localName == $var) {
+                    return true;
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Get the value of an element with method syntax.
+     *
+     * Map method calls to get the string value of the requested
+     * element. If there are multiple elements that match, this will
+     * return an array of those objects.
+     *
+     * @param  string $var    The element to get the string value of.
+     * @param  mixed  $unused This parameter is not used.
+     * @return mixed The node's value, null, or an array of nodes.
+     */
+    public function __call($var, $unused)
+    {
+        $nodes = $this->_children($var);
+
+        if (!$nodes) {
+            return null;
+        } elseif (count($nodes) > 1) {
+            return $nodes;
+        } else {
+            return $nodes[0]->nodeValue;
+        }
+    }
+
+
+    /**
+     * Remove all children matching $var.
+     *
+     * @param  string $var
+     * @return void
+     */
+    public function __unset($var)
+    {
+        $nodes = $this->_children($var);
+        foreach ($nodes as $node) {
+            $parent = $node->parentNode;
+            $parent->removeChild($node);
+        }
+    }
+
+
+    /**
+     * Returns the nodeValue of this element when this object is used
+     * in a string context.
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return $this->_element->nodeValue;
+    }
+
+
+    /**
+     * Finds children with tagnames matching $var
+     *
+     * Similar to SimpleXML's children() method.
+     *
+     * @param  string $var Tagname to match, can be either namespace:tagName or just tagName.
+     * @return array
+     */
+    protected function _children($var)
+    {
+        $found = array();
+
+        // Look for access of the form {ns:var}.
+        if (strpos($var, ':') !== false) {
+            list($ns, $elt) = explode(':', $var, 2);
+            foreach ($this->_element->childNodes as $child) {
+                if ($child->localName == $elt && $child->prefix == $ns) {
+                    $found[] = $child;
+                }
+            }
+        } else {
+            foreach ($this->_element->childNodes as $child) {
+                if ($child->localName == $var) {
+                    $found[] = $child;
+                }
+            }
+        }
+
+        return $found;
+    }
+
+
+    /**
+     * Required by the ArrayAccess interface.
+     *
+     * @param  string $offset
+     * @return boolean
+     */
+    public function offsetExists($offset)
+    {
+        if (strpos($offset, ':') !== false) {
+            list($ns, $attr) = explode(':', $offset, 2);
+            return $this->_element->hasAttributeNS(Zend_Feed::lookupNamespace($ns), $attr);
+        } else {
+            return $this->_element->hasAttribute($offset);
+        }
+    }
+
+
+    /**
+     * Required by the ArrayAccess interface.
+     *
+     * @param  string $offset
+     * @return string
+     */
+    public function offsetGet($offset)
+    {
+        if (strpos($offset, ':') !== false) {
+            list($ns, $attr) = explode(':', $offset, 2);
+            return $this->_element->getAttributeNS(Zend_Feed::lookupNamespace($ns), $attr);
+        } else {
+            return $this->_element->getAttribute($offset);
+        }
+    }
+
+
+    /**
+     * Required by the ArrayAccess interface.
+     *
+     * @param  string $offset
+     * @param  string $value
+     * @return string
+     */
+    public function offsetSet($offset, $value)
+    {
+        $this->ensureAppended();
+
+        if (strpos($offset, ':') !== false) {
+            list($ns, $attr) = explode(':', $offset, 2);
+            return $this->_element->setAttributeNS(Zend_Feed::lookupNamespace($ns), $attr, $value);
+        } else {
+            return $this->_element->setAttribute($offset, $value);
+        }
+    }
+
+
+    /**
+     * Required by the ArrayAccess interface.
+     *
+     * @param  string $offset
+     * @return boolean
+     */
+    public function offsetUnset($offset)
+    {
+        if (strpos($offset, ':') !== false) {
+            list($ns, $attr) = explode(':', $offset, 2);
+            return $this->_element->removeAttributeNS(Zend_Feed::lookupNamespace($ns), $attr);
+        } else {
+            return $this->_element->removeAttribute($offset);
+        }
+    }
+
+}

Added: incubator/shindig/trunk/php/src/common/Zend/Feed/Entry/Abstract.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/Zend/Feed/Entry/Abstract.php?rev=654331&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/common/Zend/Feed/Entry/Abstract.php (added)
+++ incubator/shindig/trunk/php/src/common/Zend/Feed/Entry/Abstract.php Wed May  7 16:48:15 2008
@@ -0,0 +1,123 @@
+<?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
+ */
+require_once 'Zend/Feed.php';
+
+/**
+ * @see Zend_Feed_Element
+ */
+require_once 'Zend/Feed/Element.php';
+
+
+/**
+ * Zend_Feed_Entry_Abstract represents a single entry in an Atom or RSS
+ * 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
+ */
+abstract class Zend_Feed_Entry_Abstract extends Zend_Feed_Element
+{
+    /**
+     * Root XML element for entries. Subclasses must define this to a
+     * non-null value.
+     *
+     * @var string
+     */
+    protected $_rootElement;
+
+    /**
+     * Root namespace for entries. Subclasses may define this to a
+     * non-null value.
+     *
+     * @var string
+     */
+    protected $_rootNamespace = null;
+
+
+    /**
+     * Zend_Feed_Entry_Abstract constructor
+     *
+     * The Zend_Feed_Entry_Abstract constructor takes the URI of the feed the entry
+     * is part of, and optionally an XML construct (usually a
+     * SimpleXMLElement, but it can be an XML string or a DOMNode as
+     * well) that contains the contents of the entry.
+     *
+     * @param  string $uri
+     * @param  SimpleXMLElement|DOMNode|string  $element
+     * @return void
+     * @throws Zend_Feed_Exception
+     */
+    public function __construct($uri = null, $element = null)
+    {
+        if (!($element instanceof DOMElement)) {
+            if ($element) {
+                // Load the feed as an XML DOMDocument object
+                @ini_set('track_errors', 1);
+                $doc = @DOMDocument::loadXML($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");
+                }
+
+                $element = $doc->getElementsByTagName($this->_rootElement)->item(0);
+                if (!$element) {
+                    /** 
+                     * @see Zend_Feed_Exception
+                     */
+                    require_once 'Zend/Feed/Exception.php';
+                    throw new Zend_Feed_Exception('No root <' . $this->_rootElement . '> element found, cannot parse feed.');
+                }
+            } else {
+                $doc = new DOMDocument('1.0', 'utf-8');
+                if ($this->_rootNamespace !== null) {
+                    $element = $doc->createElementNS(Zend_Feed::lookupNamespace($this->_rootNamespace), $this->_rootElement);
+                } else {
+                    $element = $doc->createElement($this->_rootElement);
+                }
+            }
+        }
+
+        parent::__construct($element);
+    }
+
+}

Added: incubator/shindig/trunk/php/src/common/Zend/Feed/Entry/Atom.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/Zend/Feed/Entry/Atom.php?rev=654331&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/common/Zend/Feed/Entry/Atom.php (added)
+++ incubator/shindig/trunk/php/src/common/Zend/Feed/Entry/Atom.php Wed May  7 16:48:15 2008
@@ -0,0 +1,273 @@
+<?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_Entry_Abstract
+ */
+require_once 'Zend/Feed/Entry/Abstract.php';
+
+
+/**
+ * Concrete class for working with Atom 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
+ */
+class Zend_Feed_Entry_Atom extends Zend_Feed_Entry_Abstract
+{
+    /**
+     * Root XML element for Atom entries.
+     *
+     * @var string
+     */
+    protected $_rootElement = 'entry';
+
+    /**
+     * Root namespace for Atom entries.
+     *
+     * @var string
+     */
+    protected $_rootNamespace = 'atom';
+
+
+    /**
+     * Delete an atom entry.
+     *
+     * Delete tries to delete this entry from its feed. If the entry
+     * does not contain a link rel="edit", we throw an error (either
+     * the entry does not yet exist or this is not an editable
+     * feed). If we have a link rel="edit", we do the empty-body
+     * HTTP DELETE to that URI and check for a response of 2xx.
+     * Usually the response would be 204 No Content, but the Atom
+     * Publishing Protocol permits it to be 200 OK.
+     *
+     * @return void
+     * @throws Zend_Feed_Exception
+     */
+    public function delete()
+    {
+        // Look for link rel="edit" in the entry object.
+        $deleteUri = $this->link('edit');
+        if (!$deleteUri) {
+            /** 
+             * @see Zend_Feed_Exception
+             */
+            require_once 'Zend/Feed/Exception.php';
+            throw new Zend_Feed_Exception('Cannot delete entry; no link rel="edit" is present.');
+        }
+
+        // DELETE
+        $client = Zend_Feed::getHttpClient();
+        do {
+            $client->setUri($deleteUri);
+            if (Zend_Feed::getHttpMethodOverride()) {
+                $client->setHeader('X-HTTP-Method-Override', 'DELETE');
+                $response = $client->request('POST');
+            } else {
+                $response = $client->request('DELETE');
+            }
+            $httpStatus = $response->getStatus();
+            switch ((int) $httpStatus / 100) {
+                // Success
+                case 2:
+                    return true;
+                // Redirect
+                case 3:
+                    $deleteUri = $response->getHeader('Location');
+                    continue;
+                // Error
+                default:
+                    /** 
+                     * @see Zend_Feed_Exception
+                     */
+                    require_once 'Zend/Feed/Exception.php';
+                    throw new Zend_Feed_Exception("Expected response code 2xx, got $httpStatus");
+            }
+        } while (true);
+    }
+
+
+    /**
+     * Save a new or updated Atom entry.
+     *
+     * Save is used to either create new entries or to save changes to
+     * existing ones. If we have a link rel="edit", we are changing
+     * an existing entry. In this case we re-serialize the entry and
+     * PUT it to the edit URI, checking for a 200 OK result.
+     *
+     * For posting new entries, you must specify the $postUri
+     * parameter to save() to tell the object where to post itself.
+     * We use $postUri and POST the serialized entry there, checking
+     * for a 201 Created response. If the insert is successful, we
+     * then parse the response from the POST to get any values that
+     * the server has generated: an id, an updated time, and its new
+     * link rel="edit".
+     *
+     * @param  string $postUri Location to POST for creating new entries.
+     * @return void
+     * @throws Zend_Feed_Exception
+     */
+    public function save($postUri = null)
+    {
+        if ($this->id()) {
+            // If id is set, look for link rel="edit" in the
+            // entry object and PUT.
+            $editUri = $this->link('edit');
+            if (!$editUri) {
+                /** 
+                 * @see Zend_Feed_Exception
+                 */
+                require_once 'Zend/Feed/Exception.php';
+                throw new Zend_Feed_Exception('Cannot edit entry; no link rel="edit" is present.');
+            }
+
+            $client = Zend_Feed::getHttpClient();
+            $client->setUri($editUri);
+            if (Zend_Feed::getHttpMethodOverride()) {
+                $client->setHeaders(array('X-HTTP-Method-Override: PUT',
+                    'Content-Type: application/atom+xml'));
+                $client->setRawData($this->saveXML());
+                $response = $client->request('POST');
+            } else {
+                $client->setHeaders('Content-Type', 'application/atom+xml');
+                $client->setRawData($this->saveXML());
+                $response = $client->request('PUT');
+            }
+            if ($response->getStatus() !== 200) {
+                /** 
+                 * @see Zend_Feed_Exception
+                 */
+                require_once 'Zend/Feed/Exception.php';
+                throw new Zend_Feed_Exception('Expected response code 200, got ' . $response->getStatus());
+            }
+        } else {
+            if ($postUri === null) {
+                /** 
+                 * @see Zend_Feed_Exception
+                 */
+                require_once 'Zend/Feed/Exception.php';
+                throw new Zend_Feed_Exception('PostURI must be specified to save new entries.');
+            }
+            $client = Zend_Feed::getHttpClient();
+            $client->setUri($postUri);
+            $client->setRawData($this->saveXML());
+            $response = $client->request('POST');
+
+            if ($response->getStatus() !== 201) {
+                /** 
+                 * @see Zend_Feed_Exception
+                 */
+                require_once 'Zend/Feed/Exception.php';
+                throw new Zend_Feed_Exception('Expected response code 201, got '
+                                              . $response->getStatus());
+            }
+        }
+
+        // Update internal properties using $client->responseBody;
+        @ini_set('track_errors', 1);
+        $newEntry = @DOMDocument::loadXML($response->getBody());
+        @ini_restore('track_errors');
+
+        if (!$newEntry) {
+            // 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('XML cannot be parsed: ' . $php_errormsg);
+        }
+
+        $newEntry = $newEntry->getElementsByTagName($this->_rootElement)->item(0);
+        if (!$newEntry) {
+            /** 
+             * @see Zend_Feed_Exception
+             */
+            require_once 'Zend/Feed/Exception.php';
+            throw new Zend_Feed_Exception('No root <feed> element found in server response:'
+                                          . "\n\n" . $client->responseBody);
+        }
+
+        if ($this->_element->parentNode) {
+            $oldElement = $this->_element;
+            $this->_element = $oldElement->ownerDocument->importNode($newEntry, true);
+            $oldElement->parentNode->replaceChild($this->_element, $oldElement);
+        } else {
+            $this->_element = $newEntry;
+        }
+    }
+
+
+    /**
+     * 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;
+    }
+
+}

Added: incubator/shindig/trunk/php/src/common/Zend/Feed/Entry/Rss.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/Zend/Feed/Entry/Rss.php?rev=654331&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/common/Zend/Feed/Entry/Rss.php (added)
+++ incubator/shindig/trunk/php/src/common/Zend/Feed/Entry/Rss.php Wed May  7 16:48:15 2008
@@ -0,0 +1,122 @@
+<?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: Rss.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Feed_Entry_Abstract
+ */
+require_once 'Zend/Feed/Entry/Abstract.php';
+
+
+/**
+ * Concrete class for working with RSS items.
+ *
+ * @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_Entry_Rss extends Zend_Feed_Entry_Abstract
+{
+    /**
+     * Root XML element for RSS items.
+     *
+     * @var string
+     */
+    protected $_rootElement = 'item';
+
+    /**
+     * Overwrites parent::_get method to enable read access
+     * to content:encoded element.
+     *
+     * @param  string $var The property to access.
+     * @return mixed
+     */
+    public function __get($var)
+    {
+        switch ($var) {
+            case 'content':
+                $prefix = $this->_element->lookupPrefix('http://purl.org/rss/1.0/modules/content/');
+                return parent::__get("$prefix:encoded");
+            default:
+                return parent::__get($var);
+        }
+    }
+
+    /**
+     * Overwrites parent::_set method to enable write access
+     * to content:encoded element.
+     *
+     * @param  string $var The property to change.
+     * @param  string $val The property's new value.
+     * @return void
+     */
+    public function __set($var, $value)
+    {
+        switch ($var) {
+            case 'content':
+                parent::__set('content:encoded', $value);
+                break;
+            default:
+                parent::__set($var, $value);
+        }
+    }
+
+    /**
+     * Overwrites parent::_isset method to enable access
+     * to content:encoded element.
+     *
+     * @param  string $var
+     * @return boolean
+     */
+    public function __isset($var)
+    {
+        switch ($var) {
+            case 'content':
+                // don't use other callback to prevent invalid returned value
+                return $this->content() !== null;
+            default:
+                return parent::__isset($var);
+        }
+    }
+    
+    /**
+     * Overwrites parent::_call method to enable read access
+     * to content:encoded element.
+     * Please note that method-style write access is not currently supported
+     * by parent method, consequently this method doesn't as well.
+     *
+     * @param  string $var    The element to get the string value of.
+     * @param  mixed  $unused This parameter is not used.
+     * @return mixed The node's value, null, or an array of nodes.
+     */
+    public function __call($var, $unused)
+    {
+        switch ($var) {
+            case 'content':
+                $prefix = $this->_element->lookupPrefix('http://purl.org/rss/1.0/modules/content/');
+                return parent::__call("$prefix:encoded", $unused);
+            default:
+                return parent::__call($var, $unused);
+        }
+    }
+}

Added: incubator/shindig/trunk/php/src/common/Zend/Feed/Exception.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/Zend/Feed/Exception.php?rev=654331&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/common/Zend/Feed/Exception.php (added)
+++ incubator/shindig/trunk/php/src/common/Zend/Feed/Exception.php Wed May  7 16:48:15 2008
@@ -0,0 +1,42 @@
+<?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_Exception
+ */
+require_once 'Zend/Exception.php';
+
+
+/**
+ * Feed exceptions
+ *
+ * Class to represent exceptions that occur during Feed operations.
+ *
+ * @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_Exception extends Zend_Exception
+{}
+

Added: incubator/shindig/trunk/php/src/common/Zend/Feed/Rss.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/Zend/Feed/Rss.php?rev=654331&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/common/Zend/Feed/Rss.php (added)
+++ incubator/shindig/trunk/php/src/common/Zend/Feed/Rss.php Wed May  7 16:48:15 2008
@@ -0,0 +1,504 @@
+<?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: Rss.php 8064 2008-02-16 10:58:39Z thomas $
+ */
+
+
+/**
+ * @see Zend_Feed_Abstract
+ */
+require_once 'Zend/Feed/Abstract.php';
+
+/**
+ * @see Zend_Feed_Entry_Rss
+ */
+require_once 'Zend/Feed/Entry/Rss.php';
+
+
+/**
+ * RSS channel class
+ *
+ * The Zend_Feed_Rss class is a concrete subclass of
+ * Zend_Feed_Abstract meant for representing RSS channels. It does not
+ * add any methods to its parent, just provides a classname to check
+ * against with the instanceof operator, and expects to be handling
+ * RSS-formatted data instead of Atom.
+ *
+ * @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_Rss extends Zend_Feed_Abstract
+{
+    /**
+     * The classname for individual channel elements.
+     *
+     * @var string
+     */
+    protected $_entryClassName = 'Zend_Feed_Entry_Rss';
+
+    /**
+     * The element name for individual channel elements (RSS <item>s).
+     *
+     * @var string
+     */
+    protected $_entryElementName = 'item';
+
+    /**
+     * The default namespace for RSS channels.
+     *
+     * @var string
+     */
+    protected $_defaultNamespace = 'rss';
+
+    /**
+     * 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 channel element and create an alias to it.
+        $this->_element = $this->_element->getElementsByTagName('channel')->item(0);
+        if (!$this->_element) {
+            /** 
+             * @see Zend_Feed_Exception
+             */
+            require_once 'Zend/Feed/Exception.php';
+            throw new Zend_Feed_Exception('No root <channel> element found, cannot parse channel.');
+        }
+
+        // Find the entries and save a pointer to them for speed and
+        // simplicity.
+        $this->_buildEntryCache();
+    }
+
+
+    /**
+     * Make accessing some individual elements of the channel easier.
+     *
+     * Special accessors 'item' and 'items' are provided so that if
+     * you wish to iterate over an RSS channel's items, you can do so
+     * using foreach ($channel->items as $item) or foreach
+     * ($channel->item as $item).
+     *
+     * @param  string $var The property to access.
+     * @return mixed
+     */
+    public function __get($var)
+    {
+        switch ($var) {
+            case 'item':
+                // fall through to the next case
+            case 'items':
+                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)
+    {
+        $channel = $this->_element->createElement('channel');
+
+        $title = $this->_element->createElement('title');
+        $title->appendChild($this->_element->createCDATASection($array->title));
+        $channel->appendChild($title);
+
+        $link = $this->_element->createElement('link', $array->link);
+        $channel->appendChild($link);
+
+        $desc = isset($array->description) ? $array->description : '';
+        $description = $this->_element->createElement('description');
+        $description->appendChild($this->_element->createCDATASection($desc));
+        $channel->appendChild($description);
+
+        $pubdate = isset($array->lastUpdate) ? $array->lastUpdate : time();
+        $pubdate = $this->_element->createElement('pubDate', gmdate('r', $pubdate));
+        $channel->appendChild($pubdate);
+
+        if (isset($array->published)) {
+            $lastBuildDate = $this->_element->createElement('lastBuildDate', gmdate('r', $array->published));
+        }
+
+        $editor = '';
+        if (!empty($array->email)) {
+            $editor .= $array->email;
+        }
+        if (!empty($array->author)) {
+            $editor .= ' (' . $array->author . ')';
+        }
+        if (!empty($editor)) {
+            $author = $this->_element->createElement('managingEditor', ltrim($editor));
+            $channel->appendChild($author);
+        }
+        if (isset($array->webmaster)) {
+            $channel->appendChild($this->_element->createElement('webMaster', $array->webmaster));
+        }
+
+        if (!empty($array->copyright)) {
+            $copyright = $this->_element->createElement('copyright', $array->copyright);
+            $channel->appendChild($copyright);
+        }
+
+        if (!empty($array->image)) {
+            $image = $this->_element->createElement('image');
+            $url = $this->_element->createElement('url', $array->image);
+            $image->appendChild($url);
+            $imagetitle = $this->_element->createElement('title', $array->title);
+            $image->appendChild($imagetitle);
+            $imagelink = $this->_element->createElement('link', $array->link);
+            $image->appendChild($imagelink);
+
+            $channel->appendChild($image);
+        }
+
+        $generator = !empty($array->generator) ? $array->generator : 'Zend_Feed';
+        $generator = $this->_element->createElement('generator', $generator);
+        $channel->appendChild($generator);
+
+        if (!empty($array->language)) {
+            $language = $this->_element->createElement('language', $array->language);
+            $channel->appendChild($language);
+        }
+
+        $doc = $this->_element->createElement('docs', 'http://blogs.law.harvard.edu/tech/rss');
+        $channel->appendChild($doc);
+
+        if (isset($array->cloud)) {
+            $cloud = $this->_element->createElement('cloud');
+            $cloud->setAttribute('domain', $array->cloud['uri']->getHost());
+            $cloud->setAttribute('port', $array->cloud['uri']->getPort());
+            $cloud->setAttribute('path', $array->cloud['uri']->getPath());
+            $cloud->setAttribute('registerProcedure', $array->cloud['procedure']);
+            $cloud->setAttribute('protocol', $array->cloud['protocol']);
+            $channel->appendChild($cloud);
+        }
+
+        if (isset($array->rating)) {
+            $rating = $this->_element->createElement('rating', $array->rating);
+            $channel->appendChild($rating);
+        }
+
+        if (isset($array->textInput)) {
+            $textinput = $this->_element->createElement('textInput');
+            $textinput->appendChild($this->_element->createElement('title', $array->textInput['title']));
+            $textinput->appendChild($this->_element->createElement('description', $array->textInput['description']));
+            $textinput->appendChild($this->_element->createElement('name', $array->textInput['name']));
+            $textinput->appendChild($this->_element->createElement('link', $array->textInput['link']));
+            $channel->appendChild($textinput);
+        }
+
+        if (isset($array->skipHours)) {
+            $skipHours = $this->_element->createElement('skipHours');
+            foreach ($array->skipHours as $hour) {
+                $skipHours->appendChild($this->_element->createElement('hour', $hour));
+            }
+            $channel->appendChild($skipHours);
+        }
+
+        if (isset($array->skipDays)) {
+            $skipDays = $this->_element->createElement('skipDays');
+            foreach ($array->skipDays as $day) {
+                $skipDays->appendChild($this->_element->createElement('day', $day));
+            }
+            $channel->appendChild($skipDays);
+        }
+
+        if (isset($array->itunes)) {
+            $this->_buildiTunes($channel, $array);
+        }
+
+        return $channel;
+    }
+
+    /**
+     * Adds the iTunes extensions to a root node
+     *
+     * @param  DOMElement $root
+     * @param  array $array
+     * @return void
+     */
+    private function _buildiTunes(DOMElement $root, $array)
+    {
+        /* author node */
+        $author = '';
+        if (isset($array->itunes->author)) {
+            $author = $array->itunes->author;
+        } elseif (isset($array->author)) {
+            $author = $array->author;
+        }
+        if (!empty($author)) {
+            $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:author', $author);
+            $root->appendChild($node);
+        }
+
+        /* owner node */
+        $author = '';
+        $email = '';
+        if (isset($array->itunes->owner)) {
+            if (isset($array->itunes->owner['name'])) {
+                $author = $array->itunes->owner['name'];
+            }
+            if (isset($array->itunes->owner['email'])) {
+                $email = $array->itunes->owner['email'];
+            }
+        }
+        if (empty($author) && isset($array->author)) {
+            $author = $array->author;
+        }
+        if (empty($email) && isset($array->email)) {
+            $email = $array->email;
+        }
+        if (!empty($author) || !empty($email)) {
+            $owner = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:owner');
+            if (!empty($author)) {
+                $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:name', $author);
+                $owner->appendChild($node);
+            }
+            if (!empty($email)) {
+                $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:email', $email);
+                $owner->appendChild($node);
+            }
+            $root->appendChild($owner);
+        }
+        $image = '';
+        if (isset($array->itunes->image)) {
+            $image = $array->itunes->image;
+        } elseif (isset($array->image)) {
+            $image = $array->image;
+        }
+        if (!empty($image)) {
+            $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:image');
+            $node->setAttribute('href', $image);
+            $root->appendChild($node);
+        }
+        $subtitle = '';
+        if (isset($array->itunes->subtitle)) {
+            $subtitle = $array->itunes->subtitle;
+        } elseif (isset($array->description)) {
+            $subtitle = $array->description;
+        }
+        if (!empty($subtitle)) {
+            $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:subtitle', $subtitle);
+            $root->appendChild($node);
+        }
+        $summary = '';
+        if (isset($array->itunes->summary)) {
+            $summary = $array->itunes->summary;
+        } elseif (isset($array->description)) {
+            $summary = $array->description;
+        }
+        if (!empty($summary)) {
+            $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:summary', $summary);
+            $root->appendChild($node);
+        }
+        if (isset($array->itunes->block)) {
+            $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:block', $array->itunes->block);
+            $root->appendChild($node);
+        }
+        if (isset($array->itunes->explicit)) {
+            $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:explicit', $array->itunes->explicit);
+            $root->appendChild($node);
+        }
+        if (isset($array->itunes->keywords)) {
+            $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:keywords', $array->itunes->keywords);
+            $root->appendChild($node);
+        }
+        if (isset($array->itunes->new_feed_url)) {
+            $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:new-feed-url', $array->itunes->new_feed_url);
+            $root->appendChild($node);
+        }
+        if (isset($array->itunes->category)) {
+            foreach ($array->itunes->category as $i => $category) {
+                $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:category');
+                $node->setAttribute('text', $category['main']);
+                $root->appendChild($node);
+                $add_end_category = false;
+                if (!empty($category['sub'])) {
+                    $add_end_category = true;
+                    $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:category');
+                    $node->setAttribute('text', $category['sub']);
+                    $root->appendChild($node);
+                }
+                if ($i > 0 || $add_end_category) {
+                    $node = $this->_element->createElementNS('http://www.itunes.com/DTDs/Podcast-1.0.dtd', 'itunes:category');
+                    $root->appendChild($node);
+                }
+            }
+        }
+    }
+
+    /**
+     * Generate the entries of the feed when working in write mode
+     *
+     * The following nodes are constructed for each feed entry
+     * <item>
+     *    <title>entry title</title>
+     *    <link>url to feed entry</link>
+     *    <guid>url to feed entry</guid>
+     *    <description>short text</description>
+     *    <content:encoded>long version, can contain html</content:encoded>
+     * </item>
+     *
+     * @param  DOMElement $root the root node to use
+     * @param  array $array the data to use
+     * @return void
+     */
+    protected function _mapFeedEntries(DOMElement $root, $array)
+    {
+        Zend_Feed::registerNamespace('content', 'http://purl.org/rss/1.0/modules/content/');
+
+        foreach ($array as $dataentry) {
+            $item = $this->_element->createElement('item');
+
+            $title = $this->_element->createElement('title');
+            $title->appendChild($this->_element->createCDATASection($dataentry->title));
+            $item->appendChild($title);
+
+            $link = $this->_element->createElement('link', $dataentry->link);
+            $item->appendChild($link);
+
+            if (isset($dataentry->guid)) {
+                $guid = $this->_element->createElement('guid', $dataentry->guid);
+                $item->appendChild($guid);
+            }
+
+            $description = $this->_element->createElement('description');
+            $description->appendChild($this->_element->createCDATASection($dataentry->description));
+            $item->appendChild($description);
+
+            if (isset($dataentry->content)) {
+                $content = $this->_element->createElement('content:encoded');
+                $content->appendChild($this->_element->createCDATASection($dataentry->content));
+                $item->appendChild($content);
+            }
+
+            $pubdate = isset($dataentry->lastUpdate) ? $dataentry->lastUpdate : time();
+            $pubdate = $this->_element->createElement('pubDate', gmdate('r', $pubdate));
+            $item->appendChild($pubdate);
+
+            if (isset($dataentry->category)) {
+                foreach ($dataentry->category as $category) {
+                    $node = $this->_element->createElement('category', $category['term']);
+                    if (isset($category['scheme'])) {
+                        $node->setAttribute('domain', $category['scheme']);
+                    }
+                    $item->appendChild($node);
+                }
+            }
+
+            if (isset($dataentry->source)) {
+                $source = $this->_element->createElement('source', $dataentry->source['title']);
+                $source->setAttribute('url', $dataentry->source['url']);
+                $item->appendChild($source);
+            }
+
+            if (isset($dataentry->comments)) {
+                $comments = $this->_element->createElement('comments', $dataentry->comments);
+                $item->appendChild($comments);
+            }
+            if (isset($dataentry->commentRss)) {
+                $comments = $this->_element->createElementNS('http://wellformedweb.org/CommentAPI/',
+                                                             'wfw:commentRss',
+                                                             $dataentry->commentRss);
+                $item->appendChild($comments);
+            }
+
+
+            if (isset($dataentry->enclosure)) {
+                foreach ($dataentry->enclosure as $enclosure) {
+                    $node = $this->_element->createElement('enclosure');
+                    $node->setAttribute('url', $enclosure['url']);
+                    if (isset($enclosure['type'])) {
+                        $node->setAttribute('type', $enclosure['type']);
+                    }
+                    if (isset($enclosure['length'])) {
+                        $node->setAttribute('length', $enclosure['length']);
+                    }
+                    $item->appendChild($node);
+                }
+            }
+
+            $root->appendChild($item);
+        }
+    }
+
+    /**
+     * Override Zend_Feed_Element to include <rss> root node
+     *
+     * @return string
+     */
+    public function saveXml()
+    {
+        // Return a complete document including XML prologue.
+        $doc = new DOMDocument($this->_element->ownerDocument->version,
+                               $this->_element->ownerDocument->actualEncoding);
+        $root = $doc->createElement('rss');
+
+        // Use rss version 2.0
+        $root->setAttribute('version', '2.0');
+
+        // Content namespace
+        $root->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:content', 'http://purl.org/rss/1.0/modules/content/');
+        $root->appendChild($doc->importNode($this->_element, true));
+
+        // Append root node
+        $doc->appendChild($root);
+
+        // Format output
+        $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 RSS because headers have already been sent.');
+        }
+
+        header('Content-type: application/rss+xml; charset: ' . $this->_element->ownerDocument->actualEncoding);
+
+        echo $this->saveXml();
+    }
+
+}