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()
    */


Reply via email to