Author: chabotc
Date: Sun Jul 13 05:52:44 2008
New Revision: 676312

URL: http://svn.apache.org/viewvc?rev=676312&view=rev
Log:
Code cleanup of the OutputAtomConverter, it's much easier to read now and all 
the Atom output validates with http://validator.w3.org/feed/

Modified:
    
incubator/shindig/trunk/php/src/socialrest/converters/OutputAtomConverter.php

Modified: 
incubator/shindig/trunk/php/src/socialrest/converters/OutputAtomConverter.php
URL: 
http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/socialrest/converters/OutputAtomConverter.php?rev=676312&r1=676311&r2=676312&view=diff
==============================================================================
--- 
incubator/shindig/trunk/php/src/socialrest/converters/OutputAtomConverter.php 
(original)
+++ 
incubator/shindig/trunk/php/src/socialrest/converters/OutputAtomConverter.php 
Sun Jul 13 05:52:44 2008
@@ -27,136 +27,103 @@
        private static $xmlVersion = '1.0';
        private static $charSet = 'UTF-8';
        private static $formatOutput = true;
+       //FIXME osearch fields break the validator ... remove option once i 
know if they should be included or not
+       private static $includeOsearch = false;
        // this maps the REST url to the atom content type
-       private static $entryTypes = array(
-               'people' => 'person', 'appdata' => 'appdata', 'activities' => 
'activity'
-       );
+       private static $entryTypes = array('people' => 'person', 'appdata' => 
'appdata', 'activities' => 'activity');
+       private $doc;
 
        function outputResponse(ResponseItem $responseItem, RestRequestItem 
$requestItem)
        {
-               $doc = new DOMDocument(self::$xmlVersion, self::$charSet);
-               $doc->formatOutput = self::$formatOutput;
+               $doc = $this->createAtomDoc();
+               $requestType = $this->getRequestType($requestItem);
                $data = $responseItem->getResponse();
-               $params = $requestItem->getParameters();
-               // map the Request URL to the content type to use  
-               if (empty(self::$entryTypes[$params[0]])) {
-                       throw new Exception("Unsupported request type");
-               }
-               $requestType = self::$entryTypes[$params[0]];
-               // Check to see if this is a single entry, or a collection, and 
construct either an atom 
-               // feed (collection) or an entry (single)
+               $userId = 
$requestItem->getUser()->getUserId($requestItem->getToken());
+               $guid = 'urn:guid:' . $userId;
+               $authorName = $_SERVER['HTTP_HOST'].':'.$userId;
+               $updatedAtom = date(DATE_ATOM);
                
+               // Check to see if this is a single entry, or a collection, and 
construct either an atom 
+               // feed (collection) or an entry (single)               
                if ($responseItem->getResponse() instanceof RestFulCollection) {
-                       $entry = 
$doc->appendChild($doc->createElementNS(self::$nameSpace, "feed"));
+                       $totalResults = 
$responseItem->getResponse()->getTotalResults();
+                       $itemsPerPage = $requestItem->getCount();
+                       $startIndex = $requestItem->getStartIndex();
                        
-                       // osearch fields and next link
-                       $total = 
$entry->appendChild($doc->createElement('osearch:totalResults'));
-                       
$total->appendChild($doc->createTextNode($responseItem->getResponse()->getTotalResults()));
-                       $startIndex = 
$entry->appendChild($doc->createElement('osearch:startIndex'));
-                       
$startIndex->appendChild($doc->createTextNode($requestItem->getStartIndex()));
-                       $itemsPerPage = 
$entry->appendChild($doc->createElement('osearch:itemsPerPage'));
-                       
$itemsPerPage->appendChild($doc->createTextNode($requestItem->getCount()));
-                       
-                       // fabricate a next link based on our current url if 
this is a pageable collection
-                       if (($requestItem->getStartIndex() + 
$requestItem->getCount()) < $responseItem->getResponse()->getTotalResults()) {
-                               $nextStartIndex = $requestItem->getStartIndex() 
+ $requestItem->getCount();
-                               if (($uri = $_SERVER['REQUEST_URI']) === false) 
{
-                                       throw new Exception("Could not parse 
URI : {$_SERVER['REQUEST_URI']}");
-                               }
-                               $uri = parse_url($uri);
-                               if (isset($uri['query'])) {
-                                       parse_str($uri['query'], $params);
-                               } else {
-                                       $params = array();
-                               }
-                               $params[RestRequestItem::$START_INDEX] = 
$nextStartIndex;
-                               $params[RestRequestItem::$COUNT] = 
$requestItem->getCount();
-                               foreach ($params as $paramKey => $paramVal) {
-                                       $outParams[] = $paramKey . '=' . 
$paramVal;
-                               }
-                               $outParams = '?' . implode('&', $outParams);
-                               $nextUri = 'http://' . $_SERVER['HTTP_HOST'] . 
$uri['path'] . $outParams;
-                               // <link rel="next" 
href="http://api.example.org/..."; />
-                               $link = 
$entry->appendChild($doc->createElement('link'));
-                               $linkRel = 
$link->appendChild($doc->createAttribute('rel'));
-                               
$linkRel->appendChild($doc->createTextNode('next'));
-                               $linkHref = 
$link->appendChild($doc->createAttribute('href'));
-                               
$linkHref->appendChild($doc->createTextNode($nextUri));
-                       }
+                       // The root Feed element
+                       $entry = $this->addNode($doc, 'feed', '', false, 
self::$nameSpace);
+
+                       // Required Atom fields
+                       $this->addNode($entry, 'title', $requestType.' feed for 
id '.$authorName.' ('.$startIndex. ' - '. ($startIndex + $itemsPerPage).' of 
'.$totalResults.')');
+                       $author = $this->addNode($entry, 'author');
+                       $this->addNode($author, 'uri', $guid);
+                       $this->addNode($author, 'name', $authorName);           
        
+                       $this->addNode($entry, 'updated', $updatedAtom);
+                       $this->addNode($entry, 'id', $guid);
+                       $this->addNode($entry, 'link', '', array('rel' => 
'self', 'href' => 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']));
                        
-                       // Atom fields
-                       $title = 
$entry->appendChild($doc->createElement('title'));
-                       $author = 
$entry->appendChild($doc->createElement('author'));
-                       $updated = 
$entry->appendChild($doc->createElement('updated'));
-                       
$updated->appendChild($doc->createTextNode(date(DATE_ATOM)));
-                       $id = $entry->appendChild($doc->createElement('id'));
-                       $id->appendChild($doc->createTextNode('urn:guid:' . 
$requestItem->getToken()->getDomain() . ':' . 
htmlentities($requestItem->getUser()->getUserId($requestItem->getToken(), 
ENT_NOQUOTES, 'UTF-8'))));
+                       // Add osearch & next link to the entry
+                       $this->addPagingFields($entry, $startIndex, 
$itemsPerPage, $totalResults);
                        
                        // Add response entries to feed
                        $responses = $responseItem->getResponse()->getEntry();
                        foreach ($responses as $response) {
-                               $feedEntry = 
$entry->appendChild($doc->createElement("entry"));
-                               $type = 
$feedEntry->appendChild($doc->createElement('content'));
+                               // Attempt to have a real ID field, otherwise 
we fall back on the idSpec id
+                               $idField = is_object($response) && 
isset($response->id) ? $response->id : (is_array($response) && 
isset($response['id']) ? $response['id'] : 
$requestItem->getUser()->getUserId($requestItem->getToken()));
+                               // construct <entry> blocks this record
+                               $feedEntry = $this->addNode($entry, 'entry');
+                               $content = $this->addNode($feedEntry, 
'content', '', array('type' => 'application/xml'));
+                               // Author node
+                               $author = $this->addNode($feedEntry, 'author');
+                               $this->addNode($author, 'uri', $guid);
+                               $this->addNode($author, 'name', $authorName);
+                               // Special hoisting rules for activities
+                               
                                if ($response instanceof Activity) {
-                                       // Special hoisting rules for activities
-                                       $updated = 
$feedEntry->appendChild($doc->createElement('updated'));
-                                       
$updated->appendChild($doc->createTextNode(date(DATE_ATOM, 
$response->postedTime)));
-                                       $id = 
$feedEntry->appendChild($doc->createElement('id'));
-                                       //FIXME these should get proper URL's 
in the ID and link:
+                                       $this->addNode($feedEntry, 'updated', 
date(DATE_ATOM, $response->postedTime));
+                                       $this->addNode($feedEntry, 'id', 
$response->id);
+                                       //FIXME should add a link field but 
don't have URL's available yet:
                                        // <link rel="self" 
type="application/atom+xml" 
href="http://api.example.org/activity/feeds/.../af3778"/>
-                                       
$id->appendChild($doc->createTextNode('urn:guid:activity:' . 
htmlentities($response->id, ENT_NOQUOTES, 'UTF-8')));
-                                       $summary = 
$feedEntry->appendChild($doc->createElement('summary'));
-                                       
$summary->appendChild($doc->createTextNode(htmlentities($response->body, 
ENT_NOQUOTES, 'UTF-8')));
-                                       $title = 
$feedEntry->appendChild($doc->createElement('title'));
-                                       
$title->appendChild($doc->createTextNode(htmlentities($response->title, 
ENT_NOQUOTES, 'UTF-8')));
+                                       $this->addNode($feedEntry, 'title', 
$response->title);
+                                       $this->addNode($feedEntry, 'summary', 
$response->body);
+                                       // Unset them so addData doesn't 
include them again
+                                       unset($response->postedTime);
                                        unset($response->id);
                                        unset($response->title);
                                        unset($response->body);
-                                       unset($response->postedTime);
-                               }                               
-                               $content = $this->addData($doc, $type, 
$requestType, $response, self::$osNameSpace);
-                               $contentType = 
$type->appendChild($doc->createAttribute('type'));
-                               
$contentType->appendChild($doc->createTextNode('application/xml'));
-                               
-                               // Attempt to have a real ID field, otherwise 
we fall back on the idSpec id
-                               $idField = is_object($response) && 
isset($response->id) ? $response->id : (is_array($response) && 
isset($response['id']) ? $response['id'] : 
$requestItem->getUser()->getUserId($requestItem->getToken()));
-                               
-                               // Author node
-                               $author = 
$feedEntry->appendChild($doc->createElement('author'));
-                               $authorUrl = 
$author->appendChild($doc->createElement('uri'));
-                               
$authorUrl->appendChild($doc->createTextNode('urn:guid:' . 
htmlentities($idField, ENT_NOQUOTES, 'UTF-8')));
-                               // Updated node, only if it's not an activity 
(special case)
-                               if ($response instanceof Activity) {
-                                       $title = 
$feedEntry->appendChild($doc->createElement('title'));
-                                       $updated = 
$feedEntry->appendChild($doc->createElement('updated'));
-                                       
$updated->appendChild($doc->createTextNode(date(DATE_ATOM)));
-                                       $id = 
$feedEntry->appendChild($doc->createElement('id'));
-                                       
$id->appendChild($doc->createTextNode('urn:guid:' . htmlentities($idField, 
ENT_NOQUOTES, 'UTF-8')));
+                               } else {
+                                       $this->addNode($feedEntry, 'id', 
'urn:guid:'.$idField);
+                                       $this->addNode($feedEntry, 'title', 
$requestType.' feed entry for id '.$idField);
+                                       $this->addNode($feedEntry, 'updated', 
$updatedAtom);
                                }
+                               
+                               // recursively add responseItem data to the xml 
structure
+                               $this->addData($content, $requestType, 
$response, self::$osNameSpace);
                        }
+                       
                } else {
-                       // Single entry = Atom:Entry
-                       $entry = 
$doc->appendChild($doc->createElementNS(self::$nameSpace, "entry"));
-                       $type = 
$entry->appendChild($doc->createElement('content'));
                        
-                       // addData loops through the responseItem data 
recursively creating a matching XML structure
-                       // and appends the nodes to the $type element
-                       $content = $this->addData($doc, $type, $requestType, 
$data, self::$osNameSpace);
-                       $contentType = 
$type->appendChild($doc->createAttribute('type'));
-                       
$contentType->appendChild($doc->createTextNode('application/xml'));
+                       // Single entry = Atom:Entry    
+                       $entry = 
$doc->appendChild($doc->createElementNS(self::$nameSpace, "entry"));
                        
                        // Atom fields
-                       $title = 
$entry->appendChild($doc->createElement('title'));
-                       $author = 
$entry->appendChild($doc->createElement('author'));
-                       $authorUri = 
$author->appendChild($doc->createElement('uri'));
-                       
$authorUri->appendChild($doc->createTextNode(htmlentities($requestItem->getUser()->getUserId($requestItem->getToken(),
 ENT_NOQUOTES, 'UTF-8'))));
-                       $updated = 
$entry->appendChild($doc->createElement('updated'));
-                       
$updated->appendChild($doc->createTextNode(date(DATE_ATOM)));
-                       $id = $entry->appendChild($doc->createElement('id'));
-                       $id->appendChild($doc->createTextNode('urn:guid:' . 
htmlentities($requestItem->getUser()->getUserId($requestItem->getToken()), 
ENT_NOQUOTES, 'UTF-8')));
+                       $this->addNode($entry, 'title', $requestType.' entry 
for '.$authorName);
+                       $author = $this->addNode($entry, 'author');
+                       $this->addNode($author, 'uri', $guid);
+                       $this->addNode($author, 'name', $authorName);
+                       $this->addNode($entry, 'id', $guid);
+                       $this->addNode($entry, 'updated', $updatedAtom);
+                       $content = $this->addNode($entry, 'content', '', 
array('type' => 'application/xml'));
+                       
+                       // addData loops through the responseItem data 
recursively creating a matching XML structure
+                       $this->addData($content, $requestType, $data, 
self::$osNameSpace);
                }
-               echo $doc->saveXML();
-       
+               $xml = $doc->saveXML();
+               if (self::$includeOsearch && $responseItem->getResponse() 
instanceof RestFulCollection) {
+                       //FIXME dirty hack until i find a way to add multiple 
name spaces using DomXML functions
+                       $xml = str_replace('<feed 
xmlns="http://www.w3.org/2005/Atom";>', '<feed 
xmlns="http://www.w3.org/2005/Atom"; 
xmlos:osearch="http://a9.com/-/spec/opensearch/1.1";>' ,$xml);
+               }
+               echo $xml;
        }
 
        function outputBatch(Array $responses, SecurityToken $token)
@@ -164,12 +131,118 @@
                //TODO once we support spec compliance batching, this needs to 
be added too
        }
 
-       private function addData(DOMDocument $doc, DOMElement $element, $name, 
$data, $nameSpace = false)
+       /**
+        * Easy shortcut for creating & appending XML nodes
+        *
+        * @param DOMElement $node node to append the new child node too
+        * @param string $name name of the new element
+        * @param string $value value of the element, if empty no text node is 
created
+        * @param array $attributes optional array of attributes, false by 
default. If set attributes are added to the node using the key => val pairs
+        * @param string $nameSpace optional namespace to use when creating node
+        * @return DOMElement node
+        */
+       private function addNode($node, $name, $value = '', $attributes = 
false, $nameSpace = false)
+       {
+               if ($nameSpace) {
+                       $childNode = 
$node->appendChild($this->doc->createElementNS($nameSpace, $name));
+               } else {
+                       $childNode = 
$node->appendChild($this->doc->createElement($name));
+               }
+               if (! empty($value) || $value == '0') {
+                       
$childNode->appendChild($this->doc->createTextNode($value));
+               }
+               if ($attributes && is_array($attributes)) {
+                       foreach ($attributes as $attrName => $attrVal) {
+                               $childNodeAttr = 
$childNode->appendChild($this->doc->createAttribute($attrName));
+                               if (! empty($attrVal)) {
+                                       
$childNodeAttr->appendChild($this->doc->createTextNode($attrVal));
+                               }
+                       }
+               }
+               return $childNode;
+       }
+
+       /**
+        * Adds the osearch fields & generates a next link if result set > 
itemsPerPage
+        *
+        * @param DOMElement $entry the entry DOMElement to append the links too
+        * @param int $startIndex
+        * @param int $itemsPerPage
+        * @param int $totalResults
+        */
+       private function addPagingFields($entry, $startIndex, $itemsPerPage, 
$totalResults)
+       {
+               if (self::$includeOsearch) {
+                       $this->addNode($entry, 'osearch:totalResults', 
$totalResults);
+                       $this->addNode($entry, 'osearch:startIndex', 
$startIndex ? $startIndex : '0');
+                       $this->addNode($entry, 'osearch:itemsPerPage', 
$itemsPerPage);
+               }
+               // Create a 'next' link based on our current url if this is a 
pageable collection & there is more to display
+               if (($startIndex + $itemsPerPage) < $totalResults) {
+                       $nextStartIndex = $startIndex + $itemsPerPage;
+                       if (($uri = $_SERVER['REQUEST_URI']) === false) {
+                               throw new Exception("Could not parse URI : 
{$_SERVER['REQUEST_URI']}");
+                       }
+                       $uri = parse_url($uri);
+                       $params = array();
+                       if (isset($uri['query'])) {
+                               parse_str($uri['query'], $params);
+                       }
+                       $params[RestRequestItem::$START_INDEX] = 
$nextStartIndex;
+                       $params[RestRequestItem::$COUNT] = $itemsPerPage;
+                       foreach ($params as $paramKey => $paramVal) {
+                               $outParams[] = $paramKey . '=' . $paramVal;
+                       }
+                       $outParams = '?' . implode('&', $outParams);
+                       $nextUri = 'http://' . $_SERVER['HTTP_HOST'] . 
$uri['path'] . $outParams;
+                       $this->addNode($entry, 'link', '', array('rel' => 
'next', 'href' => $nextUri));
+               }
+       }
+
+       /**
+        * Creates the root document using our xml version & charset
+        *
+        * @return DOMDocument
+        */
+       private function createAtomDoc()
+       {
+               $this->doc = new DOMDocument(self::$xmlVersion, self::$charSet);
+               $this->doc->formatOutput = self::$formatOutput;
+               return $this->doc;
+       }
+
+       /**
+        * Extracts the Atom entity name from the request url
+        *
+        * @param RequestItem $requestItem the request item
+        * @return string the request type
+        */
+       private function getRequestType($requestItem)
+       {
+               // map the Request URL to the content type to use  
+               $params = $requestItem->getParameters();
+               if (! is_array($params) || 
empty(self::$entryTypes[$params[0]])) {
+                       throw new Exception("Unsupported request type");
+               }
+               return self::$entryTypes[$params[0]];
+       }
+
+       /**
+        * Recursive function that maps an data array or object to it's xml 
represantation 
+        *
+        * @param DOMDocument $doc the root document
+        * @param DOMElement $element the element to append the new node(s) to
+        * @param string $name the name of the to be created node
+        * @param array or object $data the data to map to xml
+        * @param string $nameSpace if specified, the node is created using 
this namespace
+        * @return DOMElement returns newly created element
+        */
+       private function addData(DOMElement $element, $name, $data, $nameSpace 
= false)
        {
                if ($nameSpace) {
-                       $newElement = 
$element->appendChild($doc->createElementNS($nameSpace, $name));
+                       $newElement = 
$element->appendChild($this->doc->createElementNS($nameSpace, $name));
                } else {
-                       $newElement = 
$element->appendChild($doc->createElement($name));
+                       $newElement = 
$element->appendChild($this->doc->createElement($name));
                }
                if (is_array($data)) {
                        foreach ($data as $key => $val) {
@@ -178,18 +251,18 @@
                                        if (is_numeric($key)) {
                                                $key = is_object($val) ? 
get_class($val) : $key = $name;
                                        }
-                                       $this->addData($doc, $newElement, $key, 
$val);
+                                       $this->addData($newElement, $key, $val);
                                } else {
-                                       $elm = 
$newElement->appendChild($doc->createElement($key));
-                                       
$elm->appendChild($doc->createTextNode(htmlentities($val, ENT_NOQUOTES, 
'UTF-8')));
+                                       $elm = 
$newElement->appendChild($this->doc->createElement($key));
+                                       
$elm->appendChild($this->doc->createTextNode(htmlentities($val, ENT_NOQUOTES, 
'UTF-8')));
                                }
                        }
                } elseif (is_object($data)) {
                        if ($data instanceof Enum) {
                                // enums are output as : <NAME 
key="$key">$displayValue</NAME> 
-                               $keyEntry = 
$newElement->appendChild($doc->createAttribute('key'));
-                               
$keyEntry->appendChild($doc->createTextNode(htmlentities($data->key, 
ENT_NOQUOTES, 'UTF-8')));
-                               
$newElement->appendChild($doc->createTextNode(htmlentities($data->getDisplayValue(),
 ENT_NOQUOTES, 'UTF-8')));
+                               $keyEntry = 
$newElement->appendChild($this->doc->createAttribute('key'));
+                               
$keyEntry->appendChild($this->doc->createTextNode(htmlentities($data->key, 
ENT_NOQUOTES, 'UTF-8')));
+                               
$newElement->appendChild($this->doc->createTextNode(htmlentities($data->getDisplayValue(),
 ENT_NOQUOTES, 'UTF-8')));
                        
                        } else {
                                $vars = get_object_vars($data);
@@ -199,15 +272,15 @@
                                                if (is_numeric($key)) {
                                                        $key = is_object($val) 
? get_class($val) : $key = $name;
                                                }
-                                               $this->addData($doc, 
$newElement, $key, $val);
+                                               $this->addData($newElement, 
$key, $val);
                                        } else {
-                                               $elm = 
$newElement->appendChild($doc->createElement($key));
-                                               
$elm->appendChild($doc->createTextNode(htmlentities($val, ENT_NOQUOTES, 
'UTF-8')));
+                                               $elm = 
$newElement->appendChild($this->doc->createElement($key));
+                                               
$elm->appendChild($this->doc->createTextNode(htmlentities($val, ENT_NOQUOTES, 
'UTF-8')));
                                        }
                                }
                        }
                } else {
-                       
$newElement->appendChild($doc->createTextNode(htmlentities($data, ENT_NOQUOTES, 
'UTF-8')));
+                       
$newElement->appendChild($this->doc->createTextNode(htmlentities($data, 
ENT_NOQUOTES, 'UTF-8')));
                }
                return $newElement;
        }


Reply via email to