Cool!  (Also +spec list)

o Perhaps we could iterate on the Shindig intepretation of adding friend
relationships?
o requestShareApp, last time I asked, required UI approval from the owner of
the gadget (the user); is that still the case?  Not sure how to do that in a
protocol... though it's sort of parallel to sending a message of course.

John Panzer (http://abstractioneer.org)

On Sat, Jul 19, 2008 at 4:43 PM, Chris Chabot <[EMAIL PROTECTED]> wrote:

> Atom format input is now supported for creating activities and setting app
> data, send a Content-Type: application/atom+xml header to tell shindig to
> expect atom xml :) In other words it now supports all the write actions that
> the gadgets have access too.
>
> 'Extra' actions (like adding friend relationships by doing a put to
> /people/...) isn't supported yet, and to be honest i would first like to see
> an overview of the expected actions that we should add before i start on
> that, because i don't like guessing :)
>
> Also I'm not clear yet on how requestShareApp should work through the
> RESTful API, so i'm waiting for input on that before i can put that in
> place. (cc to John Panzer, is that information available?)
>
> Also PUT /messages/{guid}/outbox style message creation has been added,
> though the sample container implementation just returns a NOT_SUPPORTED
> error, but the structure is in place (and has been tested) so containers
> that choose to support this now can.
>
> With that the RESTful API support is pretty much 'done' on the PHP side..
> (It could use some extra testing, but it works with my test scripts that
> tries all possible actions that i could come up with).
>
> www.partuza.nl / modules.partuza.nl is running the latest code, so if you
> feel like kicking the tires, feel free :)
>
>        -- Chris
>
> On Jul 19, 2008, at 1:04 AM, Chris Chabot wrote:
>
>  This has been a bit of a headache to develop (see spec list) but it's
>> starting to take some shape. It's also quite difficult to test for me, since
>> nothing in PHP really supports multipart http posts... So i had to hand
>> craft both a test client, the input parsing in shindig, and the multi part
>> output construct too.
>>
>> In other words, it works with the input that my test client creates, but i
>> have no way to absolutely verify it is all completely valid http protocol
>> compliant stuff ... ah the joys of developing in the dark with blindfolds on
>> :)
>>
>> The batch proxy is advertised through XRDS simple:
>> http://www.chabotc.com/xrds-test.php?url=http://www.partuza.nl
>>
>> When i shoot a hand crafted multipart payload into it it works & you can
>> specify how you want your (presumably http multipart compliant) output on
>> the main url (to batchProxy) with ?format=atom or ?format=json (json is
>> assumed by default). You could even poke at it at
>> http://modules.partuza.nl/social/rest/batchProxy .. just make sure to
>> grab a valid token from a gadget iframe first to use in your requests and
>> your golden :) The only missing bits are OAuth support, and Atom format
>> input support, they will hopefully follow shortly.
>>
>> However my main question ... does anyone have any useful tools for
>> actually testing and verifying multipart http posts & the multipart response
>> ? :)
>>
>>        -- Chris
>>
>> Begin forwarded message:
>>
>>  From: [EMAIL PROTECTED]
>>> Date: July 19, 2008 12:47:41 AM GMT+02:00
>>> To: [EMAIL PROTECTED]
>>> Subject: svn commit: r678068 - in
>>> /incubator/shindig/trunk/php/src/social-api: converters/ dataservice/ http/
>>> samplecontainer/
>>> Reply-To: [email protected]
>>>
>>> Author: chabotc
>>> Date: Fri Jul 18 15:47:41 2008
>>> New Revision: 678068
>>>
>>> URL: http://svn.apache.org/viewvc?rev=678068&view=rev
>>> Log:
>>> Initial support for the batch proxy request type.
>>>
>>> The url is /social/rest/batchProxy and it uses the http multipart
>>> format as described in the RESTful API specification.
>>>
>>> It only supports ONE output format for a set of requests (which
>>> is determined by the main url ?format=foo param, and defaults to
>>> json). Input however will support mixing json and atom.
>>>
>>> Atom input is still missing (has a debug dump right now) but support
>>> for that will follow quickly.
>>>
>>> OAuth and Atom input is still missing, but once their done PHP
>>> Shindig will have full RESTful spec support, we're getting there!
>>>
>>>
>>> Modified:
>>>
>>>  
>>> incubator/shindig/trunk/php/src/social-api/converters/OutputAtomConverter.php
>>>
>>>  incubator/shindig/trunk/php/src/social-api/converters/OutputConverter.php
>>>
>>>  
>>> incubator/shindig/trunk/php/src/social-api/converters/OutputJsonConverter.php
>>>  incubator/shindig/trunk/php/src/social-api/dataservice/PeopleHandler.php
>>>
>>>  incubator/shindig/trunk/php/src/social-api/dataservice/RestRequestItem.php
>>>  incubator/shindig/trunk/php/src/social-api/http/RestServlet.php
>>>
>>>  
>>> incubator/shindig/trunk/php/src/social-api/samplecontainer/BasicAppDataService.php
>>>
>>>  
>>> incubator/shindig/trunk/php/src/social-api/samplecontainer/BasicPeopleService.php
>>>
>>> Modified:
>>> incubator/shindig/trunk/php/src/social-api/converters/OutputAtomConverter.php
>>> URL:
>>> http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/social-api/converters/OutputAtomConverter.php?rev=678068&r1=678067&r2=678068&view=diff
>>>
>>> ==============================================================================
>>> ---
>>> incubator/shindig/trunk/php/src/social-api/converters/OutputAtomConverter.php
>>> (original)
>>> +++
>>> incubator/shindig/trunk/php/src/social-api/converters/OutputAtomConverter.php
>>> Fri Jul 18 15:47:41 2008
>>> @@ -1,4 +1,5 @@
>>> <?php
>>> +
>>> /*
>>> * Licensed to the Apache Software Foundation (ASF) under one
>>> * or more contributor license agreements. See the NOTICE file
>>> @@ -19,7 +20,7 @@
>>>
>>> /**
>>> * Format = atom output converter, for format definition see:
>>> - *
>>> http://www.opensocial.org/Technical-Resources/opensocial-specification----implementation-version-08/restful-api-specification
>>> + *
>>> http://sites.google.com/a/opensocial.org/opensocial/Technical-Resources/opensocial-spec-v08/restful-api-specification
>>> */
>>> class OutputAtomConverter extends OutputConverter {
>>>        private static $nameSpace = 'http://www.w3.org/2005/Atom';
>>> @@ -40,7 +41,7 @@
>>>                $data = $responseItem->getResponse();
>>>                $userId =
>>> $requestItem->getUser()->getUserId($requestItem->getToken());
>>>                $guid = 'urn:guid:' . $userId;
>>> -               $authorName = $_SERVER['HTTP_HOST'].':'.$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
>>> @@ -52,20 +53,18 @@
>>>
>>>                        // The root Feed element
>>>                        $entry = $this->addNode($doc, 'feed', '', false,
>>> self::$nameSpace);
>>> -
>>> +
>>>                        // Required Atom fields
>>>                        $endPos = ($startIndex + $itemsPerPage) >
>>> $totalResults ? $totalResults : ($startIndex + $itemsPerPage);
>>> -                       $this->addNode($entry, 'title', $requestType.'
>>> feed for id '.$authorName.' ('.$startIndex. ' - '. ($endPos - 1).' of
>>> '.$totalResults.')');
>>> +                       $this->addNode($entry, 'title', $requestType . '
>>> feed for id ' . $authorName . ' (' . $startIndex . ' - ' . ($endPos - 1) . '
>>> of ' . $totalResults . ')');
>>>                        $author = $this->addNode($entry, 'author');
>>>                        $this->addNode($author, 'uri', $guid);
>>> -                       $this->addNode($author, 'name', $authorName);
>>>
>>> +                       $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']));
>>> -
>>> +                       $this->addNode($entry, 'link', '', array('rel' =>
>>> 'self', 'href' => 'http://' . $_SERVER['HTTP_HOST'] .
>>> $_SERVER['REQUEST_URI']));
>>>                        // 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) {
>>> @@ -79,11 +78,10 @@
>>>                                $this->addNode($author, 'uri', $guid);
>>>                                $this->addNode($author, 'name',
>>> $authorName);
>>>                                // Special hoisting rules for activities
>>> -
>>>                                if ($response instanceof Activity) {
>>>                                        $this->addNode($feedEntry,
>>> 'category', '', array('term' => 'status'));
>>>                                        $this->addNode($feedEntry,
>>> 'updated', date(DATE_ATOM, $response->postedTime));
>>> -                                       $this->addNode($feedEntry, 'id',
>>> 'urn:guid:'.$response->id);
>>> +                                       $this->addNode($feedEntry, 'id',
>>> 'urn:guid:' . $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"/>
>>>                                        $this->addNode($feedEntry,
>>> 'title', strip_tags($response->title));
>>> @@ -94,43 +92,49 @@
>>>                                        unset($response->title);
>>>                                        unset($response->body);
>>>                                } else {
>>> -                                       $this->addNode($feedEntry, 'id',
>>> 'urn:guid:'.$idField);
>>> -                                       $this->addNode($feedEntry,
>>> 'title', $requestType.' feed entry for id '.$idField);
>>> +                                       $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"));
>>> -
>>>                        // Atom fields
>>> -                       $this->addNode($entry, 'title', $requestType.'
>>> entry for '.$authorName);
>>> +                       $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);
>>>                }
>>>                $xml = $doc->saveXML();
>>> -               if (self::$includeOsearch && $responseItem->getResponse()
>>> instanceof RestFulCollection) {
>>> +               if ($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);
>>> +                       $xml = str_replace('<feed xmlns="
>>> http://www.w3.org/2005/Atom";>', '<feed xmlns="
>>> http://www.w3.org/2005/Atom"; xmlns:osearch="
>>> http://a9.com/-/spec/opensearch/1.1";>', $xml);
>>>                }
>>>                echo $xml;
>>>        }
>>>
>>>        function outputBatch(Array $responses, SecurityToken $token)
>>>        {
>>> -               //TODO once we support spec compliance batching, this
>>> needs to be added too
>>> +               $this->boundryHeaders();
>>> +               foreach ($responses as $response) {
>>> +                       $request = $response['request'];
>>> +                       $response = $response['response'];
>>> +                       // output buffering supports multiple levels of
>>> it.. it's a nice feature to abuse :)
>>> +                       ob_start();
>>> +                       $this->outputResponse($response, $request);
>>> +                       $part = ob_get_contents();
>>> +                       ob_end_clean();
>>> +                       $this->outputPart($part, $response->getError());
>>> +               }
>>>        }
>>>
>>>        /**
>>> @@ -174,11 +178,9 @@
>>>         */
>>>        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);
>>> -               }
>>> +               $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) -
>>> 1;
>>> @@ -255,6 +257,9 @@
>>>                                        }
>>>                                        $this->addData($newElement, $key,
>>> $val);
>>>                                } else {
>>> +                                       if (is_numeric($key)) {
>>> +                                               $key = is_object($val) ?
>>> get_class($val) : $key = $name;
>>> +                                       }
>>>
>>>                                        $elm =
>>> $newElement->appendChild($this->doc->createElement($key));
>>>
>>>  $elm->appendChild($this->doc->createTextNode($val));
>>>                                }
>>>
>>> Modified:
>>> incubator/shindig/trunk/php/src/social-api/converters/OutputConverter.php
>>> URL:
>>> http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/social-api/converters/OutputConverter.php?rev=678068&r1=678067&r2=678068&view=diff
>>>
>>> ==============================================================================
>>> ---
>>> incubator/shindig/trunk/php/src/social-api/converters/OutputConverter.php
>>> (original)
>>> +++
>>> incubator/shindig/trunk/php/src/social-api/converters/OutputConverter.php
>>> Fri Jul 18 15:47:41 2008
>>> @@ -22,6 +22,50 @@
>>> *
>>> */
>>> abstract class OutputConverter {
>>> +       private $boundry;
>>> +
>>>        abstract function outputResponse(ResponseItem $responseItem,
>>> RestRequestItem $requestItem);
>>>        abstract function outputBatch(Array $responses, SecurityToken
>>> $token);
>>> +
>>> +       /**
>>> +        * Output the multipart/mixed headers and returns the boundry
>>> token used
>>> +        *
>>> +        */
>>> +       public function boundryHeaders()
>>> +       {
>>> +               $this->boundry = '--batch-'.md5(rand(0,32000));
>>> +               header("HTTP/1.1 200 OK", true);
>>> +               header("Content-Type: multipart/mixed;
>>> boundary=$this->boundry", true);
>>> +       }
>>> +
>>> +       public function outputPart($part, $code)
>>> +       {
>>> +               $boundryHeader = "{$this->boundry}\n".
>>> +                               "Content-Type:
>>> application/http;version=1.1\n".
>>> +                               "Content-Transfer-Encoding: binary\n\n";
>>> +               echo $boundryHeader;
>>> +               switch ($code) {
>>> +                       case BAD_REQUEST:
>>> +                               $code = '400 Bad Request';
>>> +                               break;
>>> +                       case UNAUTHORIZED:
>>> +                               $code = '401 Unauthorized';
>>> +                               break;
>>> +                       case FORBIDDEN:
>>> +                               $code = '403 Forbidden';
>>> +                               break;
>>> +                       case FORBIDDEN:
>>> +                               $code = '404 Not Found';
>>> +                               break;
>>> +                       case NOT_IMPLEMENTED:
>>> +                               $code = '501 Not Implemented';
>>> +                               break;
>>> +                       case INTERNAL_ERROR:
>>> +                       default:
>>> +                               $code = '200 OK';
>>> +                               break;
>>> +               }
>>> +               echo "$code\n\n";
>>> +               echo $part."\n";
>>> +       }
>>> }
>>>
>>> Modified:
>>> incubator/shindig/trunk/php/src/social-api/converters/OutputJsonConverter.php
>>> URL:
>>> http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/social-api/converters/OutputJsonConverter.php?rev=678068&r1=678067&r2=678068&view=diff
>>>
>>> ==============================================================================
>>> ---
>>> incubator/shindig/trunk/php/src/social-api/converters/OutputJsonConverter.php
>>> (original)
>>> +++
>>> incubator/shindig/trunk/php/src/social-api/converters/OutputJsonConverter.php
>>> Fri Jul 18 15:47:41 2008
>>> @@ -30,6 +30,17 @@
>>>
>>>        function outputBatch(Array $responses, SecurityToken $token)
>>>        {
>>> +               $this->boundryHeaders();
>>> +               foreach ($responses as $response) {
>>> +                       $request = $response['request'];
>>> +                       $response = $response['response'];
>>> +                       $part = json_encode($response);
>>> +                       $this->outputPart($part, $response->getError());
>>> +               }
>>> +       }
>>> +
>>> +       function outputJsonBatch(Array $responses, SecurityToken $token)
>>> +       {
>>>                echo json_encode(array("responses" => $responses, "error"
>>> => false));
>>>        }
>>> }
>>>
>>> Modified:
>>> incubator/shindig/trunk/php/src/social-api/dataservice/PeopleHandler.php
>>> URL:
>>> http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/social-api/dataservice/PeopleHandler.php?rev=678068&r1=678067&r2=678068&view=diff
>>>
>>> ==============================================================================
>>> ---
>>> incubator/shindig/trunk/php/src/social-api/dataservice/PeopleHandler.php
>>> (original)
>>> +++
>>> incubator/shindig/trunk/php/src/social-api/dataservice/PeopleHandler.php Fri
>>> Jul 18 15:47:41 2008
>>> @@ -20,7 +20,9 @@
>>> class PeopleHandler extends DataRequestHandler {
>>>        private $service;
>>>        private static $PEOPLE_PATH =
>>> "/people/{userId}/{groupId}/{personId}";
>>> -       protected static $DEFAULT_PERSON_FIELDS = array("id", "name",
>>> "thumbnailUrl");
>>> +       //FIXME change this back to array("id", "name", "thumbnailUrl")
>>> once the dust settles
>>> +       // on the spec discussion related to this
>>> +       protected static $DEFAULT_PERSON_FIELDS = array('all' => 'all');
>>>
>>>        public function __construct()
>>>        {
>>>
>>> Modified:
>>> incubator/shindig/trunk/php/src/social-api/dataservice/RestRequestItem.php
>>> URL:
>>> http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/social-api/dataservice/RestRequestItem.php?rev=678068&r1=678067&r2=678068&view=diff
>>>
>>> ==============================================================================
>>> ---
>>> incubator/shindig/trunk/php/src/social-api/dataservice/RestRequestItem.php
>>> (original)
>>> +++
>>> incubator/shindig/trunk/php/src/social-api/dataservice/RestRequestItem.php
>>> Fri Jul 18 15:47:41 2008
>>> @@ -45,12 +45,12 @@
>>>
>>>        public function createRequestItemWithRequest($request, $token)
>>>        {
>>> -               $this->url = $request->url;
>>> -               $this->parameters =
>>> $this->createParameterMap($request->url);
>>> +               $this->url = $request['url'];
>>> +               $this->parameters =
>>> $this->createParameterMap($request['url']);
>>>                $this->token = $token;
>>> -               $this->method = $request->method;
>>> -               if (isset($request->postData)) {
>>> -                       $this->postData = $request->postData;
>>> +               $this->method = $request['method'];
>>> +               if (isset($request['postData'])) {
>>> +                       $this->postData = $request['postData'];
>>>                }
>>>        }
>>>
>>>
>>> Modified: incubator/shindig/trunk/php/src/social-api/http/RestServlet.php
>>> URL:
>>> http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/social-api/http/RestServlet.php?rev=678068&r1=678067&r2=678068&view=diff
>>>
>>> ==============================================================================
>>> --- incubator/shindig/trunk/php/src/social-api/http/RestServlet.php
>>> (original)
>>> +++ incubator/shindig/trunk/php/src/social-api/http/RestServlet.php Fri
>>> Jul 18 15:47:41 2008
>>> @@ -51,8 +51,6 @@
>>> require 'src/social-api/converters/OutputAtomConverter.php';
>>> require 'src/social-api/converters/OutputJsonConverter.php';
>>>
>>> -//FIXME Delete should respond with a 204 No Content to indicate success
>>> -
>>> class RestException extends Exception {}
>>>
>>> /*
>>> @@ -64,42 +62,80 @@
>>> define('FORBIDDEN', "forbidden");
>>> define('BAD_REQUEST', "badRequest");
>>> define('INTERNAL_ERROR', "internalError");
>>> +//FIXME Delete should respond with a 204 No Content to indicate success
>>>
>>> class RestServlet extends HttpServlet {
>>>
>>> +       // The json Batch Route is used by the gadgets
>>>        private static $JSON_BATCH_ROUTE = "jsonBatch";
>>> +       // The Batch Proxy route is used the one defined in the RESTful
>>> API specification
>>> +       private static $BATCH_PROXY_ROUTE = "batchProxy";
>>> +
>>> +       public function doGet()
>>> +       {
>>> +               $this->doPost('GET');
>>> +       }
>>>
>>> +       public function doPut()
>>> +       {
>>> +               $this->doPost('PUT');
>>> +       }
>>> +
>>> +       public function doDelete()
>>> +       {
>>> +               $this->doPost('DELETE');
>>> +       }
>>> +
>>>        public function doPost($method = 'POST')
>>>        {
>>> -               $this->setNoCache(true);
>>> -               // if oauth, create a token from it's values instead of
>>> one based on $_get['st']/$_post['st']
>>> -               // NOTE : if no token is provided an anonymous one is
>>> created (owner = viewer = appId = modId = 0)
>>> -               // keep this in mind when creating your data services..
>>> -               $token = $this->getSecurityToken();
>>> -               $outputFormat = $this->getOutputFormat();
>>> -               switch ($outputFormat) {
>>> -                       case 'json':
>>> -
>>> $this->setContentType('application/json');
>>> -                               $outputConverter = new
>>> OutputJsonConverter();
>>> -                               break;
>>> -                       case 'atom':
>>> -
>>> $this->setContentType('application/atom+xml');
>>> -                               $outputConverter = new
>>> OutputAtomConverter();
>>> -                               break;
>>> -                       default:
>>> -                               $this->outputError(new
>>> ResponseItem(NOT_IMPLEMENTED, "Invalid output format"));
>>> -                               break;
>>> -               }
>>> -               if ($this->isBatchUrl()) {
>>> -                       $responses = $this->handleBatchRequest($token);
>>> -                       $outputConverter->outputBatch($responses,
>>> $token);
>>> -               } else {
>>> -                       $response = $this->handleSingleRequest($token,
>>> $method);
>>> -
>>> $outputConverter->outputResponse($response['response'],
>>> $response['request']);
>>> +               try {
>>> +                       $this->setNoCache(true);
>>> +                       // if oauth, create a token from it's values
>>> instead of one based on $_get['st']/$_post['st']
>>> +                       // NOTE : if no token is provided an anonymous
>>> one is created (owner = viewer = appId = modId = 0)
>>> +                       // keep this in mind when creating your data
>>> services..
>>> +                       $token = $this->getSecurityToken();
>>> +                       $outputFormat = $this->getOutputFormat();
>>> +                       switch ($outputFormat) {
>>> +                               case 'json':
>>> +
>>> $this->setContentType('application/json');
>>> +                                       $outputConverter = new
>>> OutputJsonConverter();
>>> +                                       break;
>>> +                               case 'atom':
>>> +
>>> $this->setContentType('application/atom+xml');
>>> +                                       $outputConverter = new
>>> OutputAtomConverter();
>>> +                                       break;
>>> +                               default:
>>> +                                       $this->outputError(new
>>> ResponseItem(NOT_IMPLEMENTED, "Invalid output format"));
>>> +                                       break;
>>> +                       }
>>> +                       if ($this->isJsonBatchUrl()) {
>>> +                               // custom json batch format used by the
>>> gadgets
>>> +                               $responses =
>>> $this->handleJsonBatchRequest($token);
>>> +
>>> $outputConverter->outputJsonBatch($responses, $token);
>>> +                       } elseif ($this->isBatchProxyUrl()) {
>>> +                               // spec compliant batch proxy
>>> +                               $this->noHeaders = true;
>>> +                               $responses =
>>> $this->handleBatchProxyRequest($token);
>>> +                               $outputConverter->outputBatch($responses,
>>> $token);
>>> +                       } else {
>>> +                               // single rest request
>>> +                               $response = $this->handleRequest($token,
>>> $method);
>>> +
>>> $outputConverter->outputResponse($response['response'],
>>> $response['request']);
>>> +                       }
>>> +               } catch (Exception $e) {
>>> +                       header("HTTP/1.0 500 Internal Server Error");
>>> +                       echo "<html><body><h1>500 Internal Server
>>> Error</h1>";
>>> +                       if (Config::get('debug')) {
>>> +                               echo "Message: ".$e->getMessage()."<br
>>> />\n";
>>> +                               echo "<pre>\n";
>>> +                               print_r(debug_backtrace());
>>> +                               echo "\n</pre>";
>>> +                       }
>>> +                       echo "</body></html>";
>>>                }
>>>        }
>>>
>>> -       private function handleSingleRequest($token, $method)
>>> +       private function handleRequest($token, $method)
>>>        {
>>>                $params = $this->getListParams();
>>>                $requestItem = new RestRequestItem();
>>> @@ -109,14 +145,115 @@
>>>                $responseItem = $this->getResponseItem($requestItem);
>>>                return array('request' => $requestItem, 'response' =>
>>> $responseItem);
>>>        }
>>> -
>>> -       private function getRouteFromParameter($pathInfo)
>>> +
>>> +       private function handleJsonBatchRequest($token)
>>>        {
>>> -               $pathInfo = substr($pathInfo, 1);
>>> -               $indexOfNextPathSeparator = strpos($pathInfo, "/");
>>> -               return $indexOfNextPathSeparator != - 1 ?
>>> substr($pathInfo, 0, $indexOfNextPathSeparator) : $pathInfo;
>>> +               // we support both a raw http post (without
>>> application/x-www-form-urlencoded headers) like java does
>>> +               // and a more php / curl safe version of a form post with
>>> 'request' as the post field that holds the request json data
>>> +               if (isset($GLOBALS['HTTP_RAW_POST_DATA']) ||
>>> isset($_POST['request'])) {
>>> +                       $requests = $this->getRequestParams();
>>> +                       $responses = array();
>>> +                       foreach ($requests as $key => $value) {
>>> +                               $requestItem = new RestRequestItem();
>>> +
>>> $requestItem->createRequestItemWithRequest($value, $token);
>>> +                               $responses[$key] =
>>> $this->getResponseItem($requestItem);
>>> +                       }
>>> +                       return $responses;
>>> +               } else {
>>> +                       throw new Exception("No post data set");
>>> +               }
>>> +       }
>>> +
>>> +       private function handleBatchProxyRequest($token)
>>> +       {
>>> +               // Is this is a multipath/mixed post? Check content type:
>>> +               if (isset($GLOBALS['HTTP_RAW_POST_DATA']) &&
>>> strpos($_SERVER['CONTENT_TYPE'], 'multipart/mixed') !== false &&
>>> strpos($_SERVER['CONTENT_TYPE'],'boundary=') !== false) {
>>> +                       // Ok looks swell, see what our boundry is..
>>> +                       $boundry = substr($_SERVER['CONTENT_TYPE'],
>>> strpos($_SERVER['CONTENT_TYPE'],'boundary=') + strlen('boundary='));
>>> +                       // Split up requests per boundry
>>> +                       $requests = explode($boundry,
>>> $GLOBALS['HTTP_RAW_POST_DATA']);
>>> +                       $responses = array();
>>> +                       foreach ($requests as $request) {
>>> +                               $request = trim($request);
>>> +                               if (!empty($request)) {
>>> +                                       // extractBatchRequest() does the
>>> magic parsing of the raw post data to a meaninful request array
>>> +                                       $request =
>>> $this->extractBatchRequest($request);
>>> +                                       $requestItem = new
>>> RestRequestItem();
>>> +
>>> $requestItem->createRequestItemWithRequest($request, $token);
>>> +                                       $responses[] = array('request' =>
>>> $requestItem, 'response' => $this->getResponseItem($requestItem));
>>> +                               }
>>> +                       }
>>> +               } else {
>>> +                       $this->outputError(new ResponseItem(BAD_REQUEST,
>>> "Invalid multipart/mixed request"));
>>> +               }
>>> +               return $responses;
>>>        }
>>>
>>> +       private function extractBatchRequest($request)
>>> +       {
>>> +               /* Multipart request is formatted like:
>>> +                * -batch-a73hdj3dy3mm347ddjjdf
>>> +                * Content-Type: application/http;version=1.1
>>> +                * Content-Transfer-Encoding: binary
>>> +                *
>>> +                * GET
>>> /people/@me/@friends?startPage=5&count=10&format=json
>>> +                * Host: api.example.org
>>> +                * If-None-Match: "837dyfkdi39df"
>>> +                *
>>> +                * but we only want to have the last bit (the actual
>>> request), this filters that down first
>>> +                */
>>> +               $emptyFound = false;
>>> +               $requestLines = explode("\n", $request);
>>> +               $request = '';
>>> +               foreach ($requestLines as $line) {
>>> +                       if ($emptyFound) {
>>> +                               $request .= $line."\n";
>>> +                       } elseif (empty($line)) {
>>> +                               $emptyFound = true;
>>> +                       }
>>> +               }
>>> +               // Now that we have the basic request in $request, split
>>> that up again & parse it into a meaningful representation
>>> +               $firstFound = $emptyFound = false;
>>> +               $requestLines = explode("\n", $request);
>>> +               $request = array();
>>> +               $request['headers'] = array();
>>> +               $request['postData'] = '';
>>> +               foreach ($requestLines as $line) {
>>> +                       if (!$firstFound) {
>>> +                               $firstFound = true;
>>> +                               $parts = explode(' ', trim($line));
>>> +                               if (count($parts) != 2) {
>>> +                                       throw new Exception("Mallshaped
>>> request uri in multipart block");
>>> +                               }
>>> +                               $request['method'] =
>>> strtoupper(trim($parts[0]));
>>> +                               // cut it down to an actual meaningful
>>> url without the prefix/social/rest part right away
>>> +                               $request['url'] = substr(trim($parts[1]),
>>> strlen(Config::get('web_prefix') . '/social/rest'));
>>> +                       } elseif (!$emptyFound && !empty($line)) {
>>> +                               // convert the key to the PHP
>>> 'CONTENT_TYPE' style naming convention.. it's ugly but consitent
>>> +                               $key = str_replace('-', '_',
>>> strtoupper(trim(substr($line, 0, strpos($line, ':')))));
>>> +                               $val = trim(substr($line, strpos($line,
>>> ':') + 1));
>>> +                               $request['headers'][$key] = $val;
>>> +                       } elseif (!$emptyFound && empty($line)) {
>>> +                               $emptyFound = true;
>>> +                       } else {
>>> +                               if (get_magic_quotes_gpc()) {
>>> +                                       $line = stripslashes($line);
>>> +                               }
>>> +                               $request['postData'] .= $line."\n";
>>> +                       }
>>> +               }
>>> +               if (empty($request['postData'])) {
>>> +                       // don't trip the requestItem into thinking there
>>> is postData when there's not
>>> +                       unset($request['postData']);
>>> +               } else {
>>> +                       // if there was a post data blob present, decode
>>> it into an array, the format is based on the
>>> +                       // content type header, which is either
>>> application/json or
>>> +                       $format =
>>> isset($request['headers']['CONTENT_TYPE']) &&
>>> strtolower($request['headers']['CONTENT_TYPE']) == 'application/atom+xml' ?
>>> 'atom' : 'json';
>>> +                       $request['postData'] =
>>> $this->decodeRequests($request['postData'], $format);
>>> +               }
>>> +               return $request;
>>> +       }
>>> +
>>>        private function getResponseItem(RestRequestItem $requestItem)
>>>        {
>>>                $path =
>>> $this->getRouteFromParameter($requestItem->getUrl());
>>> @@ -140,12 +277,26 @@
>>>                        $class = new $class(null);
>>>                        $response = $class->handleMethod($requestItem);
>>>                }
>>> -               if ($response->getError() != null &&
>>> !$this->isBatchUrl()) {
>>> +               if ($response->getError() != null &&
>>> !$this->isJsonBatchUrl() && !$this->isBatchProxyUrl()) {
>>>                        // Can't use http error codes in batch mode,
>>> instead we return the error code in the response item
>>>                        $this->outputError($response);
>>>                }
>>>                return $response;
>>>        }
>>> +
>>> +       private function decodeRequests($requestParam, $format = 'json')
>>> +       {
>>> +               // temp hack until i know what the intended way to detect
>>> format is
>>> +               if ($format == 'json') {
>>> +                       return json_decode($requestParam, true);
>>> +               } elseif ($format == 'atom') {
>>> +                       $xml = simplexml_load_string($requestParam);
>>> +                       print_r($xml);
>>> +                       return $xml;
>>> +               } else {
>>> +                       throw Exception("Invalid or unsupported input
>>> format");
>>> +               }
>>> +       }
>>>
>>>        private function getRequestParams()
>>>        {
>>> @@ -154,44 +305,14 @@
>>>                if (get_magic_quotes_gpc()) {
>>>                        $requestParam = stripslashes($requestParam);
>>>                }
>>> -               $requests = json_decode($requestParam);
>>> -               if ($requests == (isset($GLOBALS['HTTP_RAW_POST_DATA']) ?
>>> $GLOBALS['HTTP_RAW_POST_DATA'] : $post)) {
>>> -                       return new ResponseItem(BAD_REQUEST, "Malformed
>>> json string");
>>> -               }
>>> -               return $requests;
>>> -       }
>>> -
>>> -       private function handleBatchRequest($token)
>>> -       {
>>> -               // we support both a raw http post (without
>>> application/x-www-form-urlencoded headers) like java does
>>> -               // and a more php / curl safe version of a form post with
>>> 'request' as the post field that holds the request json data
>>> -               if (isset($GLOBALS['HTTP_RAW_POST_DATA']) ||
>>> isset($_POST['request'])) {
>>> -                       $requests = $this->getRequestParams();
>>> -                       $responses = array();
>>> -                       foreach ($requests as $key => $value) {
>>> -                               $requestItem = new RestRequestItem();
>>> -
>>> $requestItem->createRequestItemWithRequest($value, $token);
>>> -                               $responses[$key] =
>>> $this->getResponseItem($requestItem);
>>> -                       }
>>> -                       return $responses;
>>> -               } else {
>>> -                       throw new Exception("No post data set");
>>> -               }
>>> -       }
>>> -
>>> -       public function doGet()
>>> -       {
>>> -               $this->doPost('GET');
>>> -       }
>>> -
>>> -       public function doPut()
>>> -       {
>>> -               $this->doPost('PUT');
>>> +               return $this->decodeRequests($requestParam);
>>>        }
>>>
>>> -       public function doDelete()
>>> +       private function getRouteFromParameter($pathInfo)
>>>        {
>>> -               $this->doPost('DELETE');
>>> +               $pathInfo = substr($pathInfo, 1);
>>> +               $indexOfNextPathSeparator = strpos($pathInfo, "/");
>>> +               return $indexOfNextPathSeparator != - 1 ?
>>> substr($pathInfo, 0, $indexOfNextPathSeparator) : $pathInfo;
>>>        }
>>>
>>>        private function outputError(ResponseItem $response)
>>> @@ -217,7 +338,6 @@
>>>                        default:
>>>                                $code = '500 Internal Server Error';
>>>                                break;
>>> -
>>>                }
>>>                header("HTTP/1.0 $code", true);
>>>                echo "$code - $errorMessage";
>>> @@ -265,8 +385,13 @@
>>>                return substr($_SERVER["REQUEST_URI"],
>>> strlen(Config::get('web_prefix') . '/social/rest'));
>>>        }
>>>
>>> -       public function isBatchUrl()
>>> +       public function isJsonBatchUrl()
>>>        {
>>>                return strrpos($_SERVER["REQUEST_URI"],
>>> RestServlet::$JSON_BATCH_ROUTE) > 0;
>>>        }
>>> +
>>> +       public function isBatchProxyUrl()
>>> +       {
>>> +               return strrpos($_SERVER["REQUEST_URI"],
>>> RestServlet::$BATCH_PROXY_ROUTE) > 0;
>>> +       }
>>> }
>>>
>>> Modified:
>>> incubator/shindig/trunk/php/src/social-api/samplecontainer/BasicAppDataService.php
>>> URL:
>>> http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/social-api/samplecontainer/BasicAppDataService.php?rev=678068&r1=678067&r2=678068&view=diff
>>>
>>> ==============================================================================
>>> ---
>>> incubator/shindig/trunk/php/src/social-api/samplecontainer/BasicAppDataService.php
>>> (original)
>>> +++
>>> incubator/shindig/trunk/php/src/social-api/samplecontainer/BasicAppDataService.php
>>> Fri Jul 18 15:47:41 2008
>>> @@ -85,7 +85,7 @@
>>>                switch($groupId->getType()) {
>>>                        case 'self':
>>>                                foreach ($fields as $key) {
>>> -                                       $value = isset($values->$key) ?
>>> $values->$key : (@isset($values[$key]) ? @$values[$key] : null);
>>> +                                       $value = isset($values[$key]) ?
>>> @$values[$key] : null;
>>>
>>>  XmlStateFileFetcher::get()->setAppData($userId->getUserId($token), $key,
>>> $value);
>>>                                }
>>>                                break;
>>>
>>> Modified:
>>> incubator/shindig/trunk/php/src/social-api/samplecontainer/BasicPeopleService.php
>>> URL:
>>> http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/social-api/samplecontainer/BasicPeopleService.php?rev=678068&r1=678067&r2=678068&view=diff
>>>
>>> ==============================================================================
>>> ---
>>> incubator/shindig/trunk/php/src/social-api/samplecontainer/BasicPeopleService.php
>>> (original)
>>> +++
>>> incubator/shindig/trunk/php/src/social-api/samplecontainer/BasicPeopleService.php
>>> Fri Jul 18 15:47:41 2008
>>> @@ -76,7 +76,7 @@
>>>                                if ($id == $token->getOwnerId()) {
>>>                                        $person->setIsOwner(true);
>>>                                }
>>> -                               if (is_array($profileDetails) &&
>>> count($profileDetails)) {
>>> +                               if (is_array($profileDetails) &&
>>> count($profileDetails) && !in_array('all', $profileDetails)) {
>>>                                        $newPerson = array();
>>>                                        $newPerson['isOwner'] =
>>> $person->isOwner;
>>>                                        $newPerson['isViewer'] =
>>> $person->isViewer;
>>>
>>>
>>
>

Reply via email to