Author: bhofmann
Date: Tue Apr 5 15:57:56 2011
New Revision: 1089098
URL: http://svn.apache.org/viewvc?rev=1089098&view=rev
Log:
SHINDIG-1521: PHP: View level support for Features, Preloads and Locales
Modified:
shindig/trunk/php/src/gadgets/Gadget.php
shindig/trunk/php/src/gadgets/GadgetFactory.php
shindig/trunk/php/src/gadgets/GadgetSpecParser.php
shindig/trunk/php/test/gadgets/GadgetFactoryTest.php
shindig/trunk/php/test/gadgets/GadgetTest.php
Modified: shindig/trunk/php/src/gadgets/Gadget.php
URL:
http://svn.apache.org/viewvc/shindig/trunk/php/src/gadgets/Gadget.php?rev=1089098&r1=1089097&r2=1089098&view=diff
==============================================================================
--- shindig/trunk/php/src/gadgets/Gadget.php (original)
+++ shindig/trunk/php/src/gadgets/Gadget.php Tue Apr 5 15:57:56 2011
@@ -216,7 +216,7 @@ class Gadget {
* @return unknown
*/
public function getRequiredFeatures() {
- return
$this->substitutions->substitute($this->gadgetSpec->requiredFeatures);
+ return $this->gadgetSpec->requiredFeatures;
}
/**
Modified: shindig/trunk/php/src/gadgets/GadgetFactory.php
URL:
http://svn.apache.org/viewvc/shindig/trunk/php/src/gadgets/GadgetFactory.php?rev=1089098&r1=1089097&r2=1089098&view=diff
==============================================================================
--- shindig/trunk/php/src/gadgets/GadgetFactory.php (original)
+++ shindig/trunk/php/src/gadgets/GadgetFactory.php Tue Apr 5 15:57:56 2011
@@ -74,11 +74,11 @@ class GadgetFactory {
protected function parseFeatures(Gadget &$gadget) {
$found = $missing = array();
if (! $this->context->getRegistry()->resolveFeatures(
- array_merge($gadget->gadgetSpec->requiredFeatures,
$gadget->gadgetSpec->optionalFeatures),
+ $this->getNeededFeaturesForView($gadget),
$found, $missing)) {
$requiredMissing = false;
foreach ($missing as $featureName) {
- if (in_array($featureName, $gadget->gadgetSpec->requiredFeatures)) {
+ if (isset($gadget->gadgetSpec->requiredFeatures[$featureName])) {
$requiredMissing = true;
break;
}
@@ -87,12 +87,29 @@ class GadgetFactory {
throw new GadgetException("Unknown features: " . implode(',',
$missing));
}
}
- unset($gadget->gadgetSpec->optionalFeatures);
- unset($gadget->gadgetSpec->requiredFeatures);
$gadget->features = $found;
}
/**
+ * @param Gadget $gadget
+ * @return array
+ */
+ protected function getNeededFeaturesForView(Gadget &$gadget)
+ {
+ $neededFeatures = array();
+
+ $allFeatures = array_merge($gadget->gadgetSpec->requiredFeatures,
$gadget->gadgetSpec->optionalFeatures);
+
+ foreach ($allFeatures as $featureName => $feature) {
+ if (! $feature['views'] || in_array($this->context->getView(),
$feature['views'])) {
+ $neededFeatures[] = $featureName;
+ }
+ }
+
+ return $neededFeatures;
+ }
+
+ /**
* Applies the substitutions to the complex types (preloads, user prefs,
etc). Simple
* types (author, title, etc) are translated on the fly in the gadget's
getFoo() functions
*
@@ -169,15 +186,15 @@ class GadgetFactory {
$contextLocale = $this->context->getLocale();
$locales = $gadget->gadgetSpec->locales;
$gadget->rightToLeft = false;
- $full = $partial = $all = null;
+ $full = $partial = $all = array();
foreach ($locales as $locale) {
if ($locale['lang'] == $contextLocale['lang'] && $locale['country'] ==
$contextLocale['country']) {
- $full = $locale['messageBundle'];
+ $full = array_merge($full, $locale['messageBundle']);
$gadget->rightToLeft = $locale['languageDirection'] == 'rtl';
} elseif ($locale['lang'] == $contextLocale['lang'] &&
$locale['country'] == 'all') {
- $partial = $locale['messageBundle'];
+ $partial = array_merge($partial, $locale['messageBundle']);
} elseif ($locale['country'] == 'all' && $locale['lang'] == 'all') {
- $all = $locale['messageBundle'];
+ $all = array_merge($all, $locale['messageBundle']);
}
}
$gadget->gadgetSpec->locales = array();
@@ -202,7 +219,13 @@ class GadgetFactory {
$unsignedRequests = $signedRequests = array();
foreach ($gadget->gadgetSpec->locales as $key => $locale) {
// Only fetch the locales that match the current context's language and
country
- if (($locale['country'] == 'all' && $locale['lang'] == 'all') ||
($locale['lang'] == $contextLocale['lang'] && $locale['country'] == 'all') ||
($locale['lang'] == $contextLocale['lang'] && $locale['country'] ==
$contextLocale['country'])) {
+ if ($locale['views'] && ! in_array($this->context->getView(),
$locale['views'])) {
+ unset($gadget->gadgetSpec->locales[$key]);
+ continue;
+ }
+ if (($locale['country'] == 'all' && $locale['lang'] == 'all') ||
+ ($locale['lang'] == $contextLocale['lang'] && $locale['country'] ==
'all') ||
+ ($locale['lang'] == $contextLocale['lang'] && $locale['country'] ==
$contextLocale['country'])) {
if (! empty($locale['messages'])) {
$transformedUrl =
RemoteContentRequest::transformRelativeUrl($locale['messages'],
$this->context->getUrl());
if (! $transformedUrl) {
@@ -226,7 +249,11 @@ class GadgetFactory {
}
if (! $gadget->gadgetContext instanceof MetadataGadgetContext) {
// Add preloads to the request queue
- foreach ($gadget->getPreloads() as $preload) {
+ foreach ($gadget->getPreloads() as $id => $preload) {
+ if ($preload['views'] && ! in_array($this->context->getView(),
$preload['views'])) {
+ unset($gadget->gadgetSpec->preloads[$id]);
+ continue;
+ }
if (! empty($preload['href'])) {
$preload['href'] =
$gadget->substitutions->substitute($preload['href']);
$request = new RemoteContentRequest($preload['href']);
@@ -270,31 +297,9 @@ class GadgetFactory {
}
}
}
- // Perform the non-signed requests
- $responses = array();
- if (count($unsignedRequests)) {
- $brc = new BasicRemoteContent();
- $resps = $brc->multiFetch($unsignedRequests);
- foreach ($resps as $response) {
- $responses[$response->getUrl()] = array(
- 'body' => $response->getResponseContent(),
- 'rc' => $response->getHttpCode());
- }
- }
- // Perform the signed requests
- if (count($signedRequests)) {
- $signingFetcherFactory = new
SigningFetcherFactory(Config::get("private_key_file"));
- $remoteFetcherClass = Config::get('remote_content_fetcher');
- $remoteFetcher = new $remoteFetcherClass();
- $remoteContent = new BasicRemoteContent($remoteFetcher,
$signingFetcherFactory);
- $resps = $remoteContent->multiFetch($signedRequests);
- foreach ($resps as $response) {
- $responses[$response->getNotSignedUrl()] = array(
- 'body' => $response->getResponseContent(),
- 'rc' => $response->getHttpCode());
- }
- }
+ $responses = $this->performRequests($unsignedRequests, $signedRequests);
+
// assign the results to the gadget locales and preloads (using the url as
the key)
foreach ($gadget->gadgetSpec->locales as $key => $locale) {
if (! empty($locale['messages']) &&
isset($responses[$locale['messages']]) && $responses[$locale['messages']]['rc']
== 200) {
@@ -322,6 +327,43 @@ class GadgetFactory {
}
/**
+ * perform all requests
+ *
+ * @param array $unsignedRequests
+ * @param array $signedRequests
+ * @return array
+ */
+ protected function performRequests($unsignedRequests, $signedRequests) {
+ // Perform the non-signed requests
+ $responses = array();
+ if (count($unsignedRequests)) {
+ $brc = new BasicRemoteContent();
+ $resps = $brc->multiFetch($unsignedRequests);
+ foreach ($resps as $response) {
+ $responses[$response->getUrl()] = array(
+ 'body' => $response->getResponseContent(),
+ 'rc' => $response->getHttpCode());
+ }
+ }
+
+ // Perform the signed requests
+ if (count($signedRequests)) {
+ $signingFetcherFactory = new
SigningFetcherFactory(Config::get("private_key_file"));
+ $remoteFetcherClass = Config::get('remote_content_fetcher');
+ $remoteFetcher = new $remoteFetcherClass();
+ $remoteContent = new BasicRemoteContent($remoteFetcher,
$signingFetcherFactory);
+ $resps = $remoteContent->multiFetch($signedRequests);
+ foreach ($resps as $response) {
+ $responses[$response->getNotSignedUrl()] = array(
+ 'body' => $response->getResponseContent(),
+ 'rc' => $response->getHttpCode());
+ }
+ }
+
+ return $responses;
+ }
+
+ /**
* Parses the (remote / fetched) message bundle xml
*
* @param string $messageBundleData
Modified: shindig/trunk/php/src/gadgets/GadgetSpecParser.php
URL:
http://svn.apache.org/viewvc/shindig/trunk/php/src/gadgets/GadgetSpecParser.php?rev=1089098&r1=1089097&r2=1089098&view=diff
==============================================================================
--- shindig/trunk/php/src/gadgets/GadgetSpecParser.php (original)
+++ shindig/trunk/php/src/gadgets/GadgetSpecParser.php Tue Apr 5 15:57:56 2011
@@ -76,7 +76,7 @@ class GadgetSpecParser {
if ($viewNode->getAttribute('type' == 'url') &&
$viewNode->getAttribute('href') == null) {
throw new GadgetSpecException("Malformed <Content> href value");
}
- foreach (explode(',', $viewNode->getAttribute('view')) as $view) {
+ foreach ($this->parseViewAttribute($viewNode->getAttribute('view')) as
$view) {
$view = trim($view);
$href = trim($viewNode->getAttribute('href'));
$type = trim(strtoupper($viewNode->getAttribute('type')));
@@ -113,6 +113,19 @@ class GadgetSpecParser {
}
/**
+ *
+ * @param string $attribute
+ * @return array
+ */
+ private function parseViewAttribute($attribute)
+ {
+ if (! $attribute) {
+ return array();
+ }
+ return explode(',', str_replace(' ', '', $attribute));
+ }
+
+ /**
* Parses the UserPref entries
*
* @param DOMDocument $doc
@@ -237,30 +250,32 @@ class GadgetSpecParser {
* @param GadgetSpec $gadget
*/
private function parseFeatures(DOMElement &$modulePrefs, GadgetSpec
&$gadget) {
- $gadget->requiredFeatures = $gadget->optionalFeatures = array();
$requiredNodes = $modulePrefs->getElementsByTagName('Require');
- if ($requiredNodes->length != 0) {
- foreach ($requiredNodes as $requiredFeature) {
- $gadget->requiredFeatures[] =
$requiredFeature->getAttribute('feature');
- if ($requiredFeature->getAttribute('feature') == 'content-rewrite') {
- $this->parseContentRewrite($requiredFeature, $gadget);
- } elseif ($requiredFeature->getAttribute('feature') ==
'opensocial-templates') {
- $this->parseOpenSocialTemplates($requiredFeature, $gadget);
- }
- }
- }
+ $gadget->requiredFeatures = $this->parseFeatureNodes($requiredNodes,
$gadget);
$optionalNodes = $modulePrefs->getElementsByTagName('Optional');
- if ($optionalNodes->length != 0) {
- foreach ($optionalNodes as $optionalFeature) {
- $gadget->optionalFeatures[] =
$optionalFeature->getAttribute('feature');
- // Content-rewrite is a special case since it has Params as child nodes
- if ($optionalFeature->getAttribute('feature') == 'content-rewrite') {
- $this->parseContentRewrite($optionalFeature, $gadget);
- } elseif ($optionalFeature->getAttribute('feature') ==
'opensocial-templates') {
- $this->parseOpenSocialTemplates($optionalFeature, $gadget);
- }
+ $gadget->optionalFeatures = $this->parseFeatureNodes($optionalNodes,
$gadget);
+ }
+
+ /**
+ *
+ * @param DOMNodeList $nodes
+ * @param GadgetSpec $gadget
+ * @return array
+ */
+ private function parseFeatureNodes(DOMNodeList &$nodes, GadgetSpec &$gadget)
{
+ $features = array();
+ foreach ($nodes as $feature) {
+ $features[$feature->getAttribute('feature')] = array(
+ 'views' =>
$this->parseViewAttribute($feature->getAttribute('views')),
+ );
+ // Content-rewrite is a special case since it has Params as child nodes
+ if ($feature->getAttribute('feature') == 'content-rewrite') {
+ $this->parseContentRewrite($feature, $gadget);
+ } elseif ($feature->getAttribute('feature') == 'opensocial-templates') {
+ $this->parseOpenSocialTemplates($feature, $gadget);
}
}
+ return $features;
}
/**
@@ -397,6 +412,7 @@ class GadgetSpecParser {
foreach ($preloadNodes as $node) {
$gadget->preloads[] = array('href' => $node->getAttribute('href'),
'authz' => strtoupper($node->getAttribute('authz')),
+ 'views' => $this->parseViewAttribute($node->getAttribute('views')),
'signViewer' => $node->getAttribute('sign_viewer'),
'signOwner' => $node->getAttribute('sign_owner'));
}
@@ -425,6 +441,7 @@ class GadgetSpecParser {
$gadget->locales[] = array('lang' => $lang, 'country' => $country,
'messages' => $node->getAttribute('messages'),
'languageDirection' => $node->getAttribute('language_direction'),
+ 'views' => $this->parseViewAttribute($node->getAttribute('views')),
'messageBundle' => $messageBundle);
}
}
Modified: shindig/trunk/php/test/gadgets/GadgetFactoryTest.php
URL:
http://svn.apache.org/viewvc/shindig/trunk/php/test/gadgets/GadgetFactoryTest.php?rev=1089098&r1=1089097&r2=1089098&view=diff
==============================================================================
--- shindig/trunk/php/test/gadgets/GadgetFactoryTest.php (original)
+++ shindig/trunk/php/test/gadgets/GadgetFactoryTest.php Tue Apr 5 15:57:56
2011
@@ -88,4 +88,156 @@ class GadgetFactoryTest extends PHPUnit_
$this->assertEquals('title', $gadget->gadgetSpec->title);
$this->assertEquals('<h1>Hello, world!</h1>',
trim($gadget->gadgetSpec->views['home']['content']));
}
+
+ public function testParseFeaturesDependentOnCurrentView() {
+ $_POST = array(
+ 'rawxml' => '<?xml version="1.0" encoding="UTF-8" ?>
+<Module>
+ <ModulePrefs title="title">
+ <Require feature="opensocial-0.8" />
+ <Require feature="pubsub" views="canvas" />
+ <Require feature="flash" views="canvas,profile" />
+ <Optional feature="minimessage" />
+ <Optional feature="invalid" />
+ <Optional feature="pubsub2" views="canvas" />
+ <Optional feature="opensocial-data" views="canvas, profile" />
+ </ModulePrefs>
+ <Content type="html" view="home">
+ </Content>
+</Module>'
+ );
+ $_GET = array();
+ $context = new GadgetContext('GADGET');
+ $context->setView('profile');
+ $gadgetFactory = new GadgetFactory($context, $this->token);
+ $gadget = $gadgetFactory->createGadget();
+
+ $this->assertTrue(in_array('opensocial-0.8', $gadget->features));
+ $this->assertTrue(in_array('flash', $gadget->features));
+ $this->assertTrue(in_array('minimessage', $gadget->features));
+ $this->assertTrue(in_array('opensocial-data', $gadget->features));
+
+ $this->assertFalse(in_array('pubsub', $gadget->features));
+ $this->assertFalse(in_array('pubsub2', $gadget->features));
+ }
+
+ public function testRequiringInvalidFeatureThrowsException() {
+ $this->setExpectedException('GadgetException', 'Unknown features:
invalid');
+ $_POST = array(
+ 'rawxml' => '<?xml version="1.0" encoding="UTF-8" ?>
+<Module>
+ <ModulePrefs title="title">
+ <Require feature="invalid" />
+ </ModulePrefs>
+ <Content type="html" view="home">
+ </Content>
+</Module>'
+ );
+ $_GET = array();
+ $context = new GadgetContext('GADGET');
+ $context->setView('profile');
+ $gadgetFactory = new GadgetFactory($context, $this->token);
+ $gadget = $gadgetFactory->createGadget();
+ }
+
+ public function testParsePreloadsDependentOnCurrentView() {
+ $_POST = array(
+ 'rawxml' => '<?xml version="1.0" encoding="UTF-8" ?>
+<Module>
+ <ModulePrefs title="title">
+ <Preload href="http://www.example.com/one" />
+ <Preload href="http://www.example.com/two" views="canvas"/>
+ <Preload href="http://www.example.com/three" views="canvas,profile"/>
+ </ModulePrefs>
+ <Content type="html" view="home">
+ </Content>
+</Module>'
+ );
+ $_GET = array();
+ $context = new GadgetContext('GADGET');
+ $context->setView('profile');
+ $gadgetFactory = new TestGadgetFactory($context, $this->token);
+ $gadget = $gadgetFactory->createGadget();
+
+ $this->assertEquals('http://www.example.com/one',
$gadget->gadgetSpec->preloads[0]['id']);
+ $this->assertEquals('http://www.example.com/three',
$gadget->gadgetSpec->preloads[1]['id']);
+ $this->assertEquals(2, count($gadget->gadgetSpec->preloads));
+ }
+
+ public function testParseLocalsDependentOnCurrentView() {
+ $_POST = array(
+ 'rawxml' => '<?xml version="1.0" encoding="UTF-8" ?>
+<Module>
+ <ModulePrefs title="title">
+ <Locale messages="http://example.com/helloOne/en_ALL.xml"/>
+ <Locale messages="http://example.com/helloTwo/en_ALL.xml" views="canvas"/>
+ <Locale messages="http://example.com/helloThree/en_ALL.xml"
views="canvas,profile"/>
+ </ModulePrefs>
+ <Content type="html" view="home">
+ </Content>
+</Module>'
+ );
+ $_GET = array();
+ $context = new GadgetContext('GADGET');
+ $context->setView('profile');
+ $gadgetFactory = new TestGadgetFactory($context, $this->token);
+ $gadget = $gadgetFactory->createGadget();
+ $this->assertEquals(array('greetingOne' => 'Hello', 'greetingThree' =>
'Hello'), $gadget->gadgetSpec->locales);
+ }
+}
+
+class TestGadgetFactory extends GadgetFactory
+{
+ private $responses = array(
+ 'http://www.example.com/one' => 'preloadOne',
+ 'http://www.example.com/two' => 'preloadTwo',
+ 'http://www.example.com/three' => 'preloadThree',
+ 'http://example.com/helloOne/en_ALL.xml' => '<?xml version="1.0"
encoding="UTF-8" ?>
+<messagebundle>
+ <msg name="greetingOne">
+ Hello
+ </msg>
+</messagebundle>',
+ 'http://example.com/helloTwo/en_ALL.xml' => '<?xml version="1.0"
encoding="UTF-8" ?>
+<messagebundle>
+ <msg name="greetingTwo">
+ Hello
+ </msg>
+</messagebundle>',
+ 'http://example.com/helloThree/en_ALL.xml' => '<?xml version="1.0"
encoding="UTF-8" ?>
+<messagebundle>
+ <msg name="greetingThree">
+ Hello
+ </msg>
+</messagebundle>',
+ );
+ /**
+ * mock request sending
+ *
+ * @param array $unsignedRequests
+ * @param array $signedRequests
+ * @return array
+ */
+ protected function performRequests($unsignedRequests, $signedRequests) {
+ // Perform the non-signed requests
+ $responses = array();
+ if (count($unsignedRequests)) {
+ foreach ($unsignedRequests as $request) {
+ $responses[$request->getUrl()] = array(
+ 'body' => $this->responses[$request->getUrl()],
+ 'rc' => 200);
+ }
+ }
+
+ // Perform the signed requests
+ if (count($signedRequests)) {
+ foreach ($signedRequests as $request) {
+ $responses[$request->getUrl()] = array(
+ 'body' => $this->responses[$request->getUrl()],
+ 'rc' => 200);
+ }
+ }
+
+ return $responses;
+ }
}
\ No newline at end of file
Modified: shindig/trunk/php/test/gadgets/GadgetTest.php
URL:
http://svn.apache.org/viewvc/shindig/trunk/php/test/gadgets/GadgetTest.php?rev=1089098&r1=1089097&r2=1089098&view=diff
==============================================================================
--- shindig/trunk/php/test/gadgets/GadgetTest.php (original)
+++ shindig/trunk/php/test/gadgets/GadgetTest.php Tue Apr 5 15:57:56 2011
@@ -35,7 +35,13 @@ class MockGadgetFactory extends GadgetFa
width="100" screenshot="screenshot" singleton="true" thumbnail="thumbnail"
string="string" title_url="titleUrl" render_inline="never" scaling="true"
scrolling="true" show_in_directory="true" show_stats="false"
- />
+ >
+ <Require feature="opensocial" />
+ <Require feature="pubsub" views="default, canvas"/>
+ <Require feature="flash" views="mobile"/>
+ <Optional feature="views" />
+ <Optional feature="opensocial-data" views="profile,canvas" />
+ </ModulePrefs>
<UserPref name="name1" default_value="0" datatype="hidden"/>
<UserPref name="name2" default_value="value" datatype="hidden"/>
<Content type="html" view="home">
@@ -248,6 +254,19 @@ class GadgetTest extends PHPUnit_Framewo
$this->assertEquals('titleUrl', $this->gadget->getTitleUrl());
}
+ public function testGetRequiredFeatures() {
+ $this->assertEquals(array(
+ 'opensocial' => array('views' => array()),
+ 'pubsub' => array('views' => array('default', 'canvas')),
+ 'flash' => array('views' => array('mobile'))),
$this->gadget->getRequiredFeatures());
+ }
+
+ public function testGetOptionalFeatures() {
+ $this->assertEquals(array(
+ 'views' => array('views' => array()),
+ 'opensocial-data' => array('views' => array('profile', 'canvas'))),
$this->gadget->getOptionalFeatures());
+ }
+
/**
* Tests Gadget->getUserPrefs()
*/