blob: 58d109a1ad5702ebca91bf2ec22a13de3d6068a5 [file] [log] [blame]
<?php
namespace apache\shindig\gadgets\render;
use apache\shindig\common\RemoteContentRequest;
use apache\shindig\gadgets\SigningFetcherFactory;
use apache\shindig\common\sample\BasicRemoteContent;
use apache\shindig\common\Config;
use apache\shindig\gadgets\templates\DataPipelining;
use apache\shindig\gadgets\Gadget;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
* TODO Dynamically evaluate the limited EL subset expressions on the following tags:
* Any attribute on os:DataRequest other than @key and @method
* @userId
* @groupId
* @fields
* @startIndex
* @count
* @sortBy
* @sortOrder
* @filterBy
* @filterOp
* @filterValue
* @activityIds
* @href
* @params
* Example:
* <os:PeopleRequest key="PagedFriends" userId="@owner" groupId="@friends" startIndex="${ViewParams.first}" count="20"/>
* <os:HttpRequest href="http://developersite.com/api?ids=${PagedFriends.ids}"/>
*/
class GadgetHrefRenderer extends GadgetBaseRenderer {
/**
* Renders a 'proxied content' view, for reference see:
* http://opensocial-resources.googlecode.com/svn/spec/draft/OpenSocial-Data-Pipelining.xml
*
* @param Gadget $gadget
* @param array $view
*/
public function renderGadget(Gadget $gadget, $view) {
$this->setGadget($gadget);
if (Config::get('P3P') != '') {
header("P3P: " . Config::get('P3P'));
}
/* TODO
* We should really re-add OAuth fetching support some day, uses these view atributes:
* $view['oauthServiceName'], $view['oauthTokenName'], $view['oauthRequestToken'], $view['oauthRequestTokenSecret'];
*/
$authz = $this->getAuthz($view);
$refreshInterval = $this->getRefreshInterval($view);
$href = $this->buildHref($view, $authz, $gadget);
if (count($view['dataPipelining'])) {
$request = new RemoteContentRequest($href, "Content-type: application/json\n");
$request->setMethod('POST');
$request->getOptions()->ignoreCache = $gadget->gadgetContext->getIgnoreCache();
} else {
// no data-pipelining set, use GET and set cache/refresh interval options
$request = new RemoteContentRequest($href);
$request->setMethod('GET');
$request->setRefreshInterval($refreshInterval);
$request->getOptions()->ignoreCache = $gadget->gadgetContext->getIgnoreCache();
}
$signingFetcherFactory = $gadgetSigner = false;
if ($authz != 'none') {
$gadgetSigner = Config::get('security_token_signer');
$gadgetSigner = new $gadgetSigner();
$token = $gadget->gadgetContext->extractAndValidateToken($gadgetSigner);
$request->setToken($token);
$request->setAuthType($authz);
$request->getOptions()->ownerSigned = $this->getSignOwner($view);
$request->getOptions()->viewerSigned = $this->getSignViewer($view);
$signingFetcherFactory = new SigningFetcherFactory(Config::get("private_key_file"));
}
$remoteFetcherClass = Config::get('remote_content_fetcher');
$remoteFetcher = new $remoteFetcherClass();
$basicRemoteContent = new BasicRemoteContent($remoteFetcher, $signingFetcherFactory, $gadgetSigner);
// Cache POST's as if they were GET's, since we don't want to re-fetch and repost the social data for each view
$basicRemoteContent->setCachePostRequest(true);
if (($response = $basicRemoteContent->getCachedRequest($request)) == false) {
// Don't fetch the data-pipelining social data unless we don't have a cached version of the gadget's content
if (isset($view['dataPipelining']) && is_array($view['dataPipelining'])) {
foreach ($view['dataPipelining'] as &$pipeliningRequest) {
if (isset($pipeliningRequest['href'])) {
$pipeliningRequest['href'] = $gadget->substitutions->substituteUri(null, $pipeliningRequest['href']);
}
}
$dataPipeliningResults = DataPipelining::fetch($view['dataPipelining'], $this->context);
// spec stats that the proxied content data-pipelinging data is *not* available to templates (to avoid duplicate posting
// of the data to the gadget dev's server and once to js space), so we don't assign it to the data context, and just
// post the json encoded results to the remote url.
$request->setPostBody(json_encode($dataPipeliningResults));
}
$response = $basicRemoteContent->fetch($request);
}
if ($response->getHttpCode() != '200') {
// an error occured fetching the proxied content's gadget content
$content = '<html><body><h1>An error occured fetching the gadget content</h1><p>http error code: '.$response->getHttpCode().'</p><p>'.$response->getResponseContent().'</body></html>';
} else {
// fetched ok, build the response document and output it
$content = $gadget->substitutions->substitute($response->getResponseContent());
$content = $this->parseTemplates($content);
$content = $this->rewriteContent($content);
$content = $this->addTemplates($content);
}
echo $content;
}
/**
* Builds the outgoing URL by taking the href attribute of the view and appending
* the country, lang, and opensocial query params to it
*
* @param array $view
* @param SecurityToken $token
* @param Gadget $gadget
* @return string the url
*/
private function buildHref($view, $authz, $gadget) {
$href = RemoteContentRequest::transformRelativeUrl($gadget->substitutions->substituteUri(null, $view['href']), $this->context->getUrl());
if (empty($href)) {
throw new \Exception("Invalid empty href in the gadget view");
} // add the required country and lang param to the URL
$lang = isset($_GET['lang']) ? $_GET['lang'] : 'en';
$country = isset($_GET['country']) ? $_GET['country'] : 'US';
$firstSeperator = strpos($href, '?') === false ? '?' : '&';
$href .= $firstSeperator . 'lang=' . urlencode($lang);
$href .= '&country=' . urlencode($country);
if ($authz != 'none') {
$href .= '&opensocial_proxied_content=1';
}
return $href;
}
/**
* Returns the requested refreshInterval (cache time) of the view, or if none is specified
* it will return the configured default_refresh_interval value
*
* @param array $view
* @return int refresh interval
*/
private function getRefreshInterval($view) {
return ! empty($view['refreshInterval']) && is_numeric($view['refreshInterval']) ? $view['refreshInterval'] : Config::get('default_refresh_interval');
}
/**
* Returns the authz attribute of the view, can be 'none', 'signed' or 'oauth'
*
* @param array $view
* @return string authz attribute
*/
private function getAuthz($view) {
return ! empty($view['authz']) ? strtolower($view['authz']) : 'none';
}
/**
* Returns the signOwner attribute of the view (true or false, default is true)
*
* @param array $view
* @return string signOwner attribute
*/
private function getSignOwner($view) {
return ! empty($view['signOwner']) && strcasecmp($view['signOwner'], 'false') == 0 ? false : true;
}
/**
* Returns the signViewer attribute of the view (true or false, default is true)
*
* @param array $view
* @return string signViewer attribute
*/
private function getSignViewer($view) {
return ! empty($view['signViewer']) && strcasecmp($view['signViewer'], 'false') == 0 ? false : true;
}
}