You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@shindig.apache.org by li...@apache.org on 2008/03/05 20:43:20 UTC

svn commit: r633998 [1/2] - in /incubator/shindig/trunk: config/ php/ php/gadgets/ php/gadgets/src/ php/gadgets/src/http/ php/js/

Author: lindner
Date: Wed Mar  5 11:43:14 2008
New Revision: 633998

URL: http://svn.apache.org/viewvc?rev=633998&view=rev
Log:
Apply patch from Chris Chabot that provides a new
PHP implementation as specified in SHINDIG-103

Added:
    incubator/shindig/trunk/php/gadgets/
    incubator/shindig/trunk/php/gadgets/.htaccess
    incubator/shindig/trunk/php/gadgets/README
    incubator/shindig/trunk/php/gadgets/config.php
    incubator/shindig/trunk/php/gadgets/index.php
    incubator/shindig/trunk/php/gadgets/src/
    incubator/shindig/trunk/php/gadgets/src/BasicGadgetBlacklist.php
    incubator/shindig/trunk/php/gadgets/src/BasicGadgetSigner.php
    incubator/shindig/trunk/php/gadgets/src/BasicGadgetToken.php
    incubator/shindig/trunk/php/gadgets/src/BasicRemoteContent.php
    incubator/shindig/trunk/php/gadgets/src/BasicRemoteContentFetcher.php
    incubator/shindig/trunk/php/gadgets/src/BidiSubstituter.php
    incubator/shindig/trunk/php/gadgets/src/Cache.php
    incubator/shindig/trunk/php/gadgets/src/FileCache.php
    incubator/shindig/trunk/php/gadgets/src/Gadget.php
    incubator/shindig/trunk/php/gadgets/src/GadgetBlacklist.php
    incubator/shindig/trunk/php/gadgets/src/GadgetContext.php
    incubator/shindig/trunk/php/gadgets/src/GadgetException.php
    incubator/shindig/trunk/php/gadgets/src/GadgetFeature.php
    incubator/shindig/trunk/php/gadgets/src/GadgetFeatureFactory.php
    incubator/shindig/trunk/php/gadgets/src/GadgetFeatureRegistry.php
    incubator/shindig/trunk/php/gadgets/src/GadgetServer.php
    incubator/shindig/trunk/php/gadgets/src/GadgetSigner.php
    incubator/shindig/trunk/php/gadgets/src/GadgetSpec.php
    incubator/shindig/trunk/php/gadgets/src/GadgetSpecParser.php
    incubator/shindig/trunk/php/gadgets/src/GadgetToken.php
    incubator/shindig/trunk/php/gadgets/src/GadgetView.php
    incubator/shindig/trunk/php/gadgets/src/GadgetViewID.php
    incubator/shindig/trunk/php/gadgets/src/HttpProcessingOptions.php
    incubator/shindig/trunk/php/gadgets/src/JsFeatureLoader.php
    incubator/shindig/trunk/php/gadgets/src/JsLibrary.php
    incubator/shindig/trunk/php/gadgets/src/JsLibraryFeatureFactory.php
    incubator/shindig/trunk/php/gadgets/src/MessageBundle.php
    incubator/shindig/trunk/php/gadgets/src/MessageBundleParser.php
    incubator/shindig/trunk/php/gadgets/src/MessageBundleSubstituter.php
    incubator/shindig/trunk/php/gadgets/src/ModuleSubstituter.php
    incubator/shindig/trunk/php/gadgets/src/ProcessingOptions.php
    incubator/shindig/trunk/php/gadgets/src/RemoteContent.php
    incubator/shindig/trunk/php/gadgets/src/RemoteContentException.php
    incubator/shindig/trunk/php/gadgets/src/RemoteContentFetcher.php
    incubator/shindig/trunk/php/gadgets/src/RemoteContentRequest.php
    incubator/shindig/trunk/php/gadgets/src/SpecParserException.php
    incubator/shindig/trunk/php/gadgets/src/Substitutions.php
    incubator/shindig/trunk/php/gadgets/src/UserPrefSubstituter.php
    incubator/shindig/trunk/php/gadgets/src/UserPrefs.php
    incubator/shindig/trunk/php/gadgets/src/http/
    incubator/shindig/trunk/php/gadgets/src/http/FilesServlet.php
    incubator/shindig/trunk/php/gadgets/src/http/GadgetRenderingServlet.php
    incubator/shindig/trunk/php/gadgets/src/http/HttpServlet.php
    incubator/shindig/trunk/php/gadgets/src/http/JsServlet.php
    incubator/shindig/trunk/php/gadgets/src/http/ProxyHandler.php
    incubator/shindig/trunk/php/gadgets/src/http/ProxyServlet.php
    incubator/shindig/trunk/php/gadgets/src/http/RpcServlet.php
Removed:
    incubator/shindig/trunk/php/README
    incubator/shindig/trunk/php/RSA.class.php
    incubator/shindig/trunk/php/comframe.php
    incubator/shindig/trunk/php/config.php
    incubator/shindig/trunk/php/container.php
    incubator/shindig/trunk/php/crypto.php
    incubator/shindig/trunk/php/index.php
    incubator/shindig/trunk/php/js/
    incubator/shindig/trunk/php/key-modulo
    incubator/shindig/trunk/php/key-private
    incubator/shindig/trunk/php/key-public
    incubator/shindig/trunk/php/proxy.php
Modified:
    incubator/shindig/trunk/config/syndicator.js

Modified: incubator/shindig/trunk/config/syndicator.js
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/config/syndicator.js?rev=633998&r1=633997&r2=633998&view=diff
==============================================================================
--- incubator/shindig/trunk/config/syndicator.js (original)
+++ incubator/shindig/trunk/config/syndicator.js Wed Mar  5 11:43:14 2008
@@ -28,8 +28,8 @@
 // as you would for javascript objects, e.g. gadgets.features
 // rather than "features".
 
-// NOTE: Please leave trailing commas to reduce errors when adding new fields.
-// All known common server-side JSON parsers will handle this without issue.
+// NOTE: Please _don't_ leave trailing commas because the php json parser
+// errors out on this.
 
 // Syndicator must be an array; this allows multiple syndicators
 // to share configuration.
@@ -53,20 +53,20 @@
   "core.io" : {
   	// Note: /proxy is an open proxy. Be careful how you explose this!
     "proxyUrl" : "proxy?url=%url%",
-    "jsonProxyUrl" : "proxy?output=js",
+    "jsonProxyUrl" : "proxy?output=js"
   },
   "views" : {
     "default" : {
       "isOnlyVisible" : false,
-      "aliases": ["DASHBOARD"],
+      "aliases": ["DASHBOARD"]
     },
     "profile" : {
-      "isOnlyVisible" : false,
+      "isOnlyVisible" : false
     },
     "canvas" : {
       "isOnlyVisible" : true,
       "aliases" : ["FULL_PAGE"]
-    },
+    }
   },
   "rpc" : {
   	// Path to the relay file. Automatically appended to the parent
@@ -77,7 +77,7 @@
 
     // If true, this will use the legacy ifpc wire format when making rpc
     // requests.
-    "useLegacyProtocol" : false,
+    "useLegacyProtocol" : false
   },
   // Skin defaults
   "skins" : {
@@ -87,7 +87,7 @@
          "BG_POSITION": "",
          "BG_REPEAT": "",
          "FONT_COLOR": "",
-         "ANCHOR_COLOR": "",
+         "ANCHOR_COLOR": ""
     }
-  },
+  }
 }}

Added: incubator/shindig/trunk/php/gadgets/.htaccess
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/gadgets/.htaccess?rev=633998&view=auto
==============================================================================
--- incubator/shindig/trunk/php/gadgets/.htaccess (added)
+++ incubator/shindig/trunk/php/gadgets/.htaccess Wed Mar  5 11:43:14 2008
@@ -0,0 +1,7 @@
+<IfModule mod_rewrite.c>
+RewriteEngine On
+RewriteBase /
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteCond %{REQUEST_FILENAME} !-d
+RewriteRule . /gadgets/index.php [L]
+</IfModule>

Added: incubator/shindig/trunk/php/gadgets/README
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/gadgets/README?rev=633998&view=auto
==============================================================================
--- incubator/shindig/trunk/php/gadgets/README (added)
+++ incubator/shindig/trunk/php/gadgets/README Wed Mar  5 11:43:14 2008
@@ -0,0 +1,17 @@
+Installing and Running The PHP Shindig Gadget Server
+============================================
+
+1) Make sure you have PHP 5.x installed and have the simplexml and json extentions enabled; It also
+   requires apache with .htaccess and mod_rewrite support
+
+2) Copy the files to your web root, and edit the config.php web_prefix and debug settings
+   to your liking. Make sure that the features and javascript directories are also copied
+   or edit your config.php to reflect their location.
+
+3) Hit server at http://<yourhost>/gadgets/ifr?url=<gadget-url>
+   Example: http://<yourhost>/gadgets/ifr?url=http://www.labpixies.com/campaigns/todo/todo.xml
+   Or hit the sample container at http://<yourhost>/gadgets/files/samplecontainer/samplecontainer.html
+
+The PHP source of the gadget server resides in gadgets/src.
+
+For more information, see http://incubator.apache.org/projects/shindig.html

Added: incubator/shindig/trunk/php/gadgets/config.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/gadgets/config.php?rev=633998&view=auto
==============================================================================
--- incubator/shindig/trunk/php/gadgets/config.php (added)
+++ incubator/shindig/trunk/php/gadgets/config.php Wed Mar  5 11:43:14 2008
@@ -0,0 +1,47 @@
+<?
+/*
+ * I really detest such config files to be honest, why put configuration in a web document! 
+ * But since PHP lacks a propper way to set application configurations, and any other method 
+ * would be horribly slow (db, xml, ini files etc), so ... here's our config.php
+ */
+
+// The base prefix under which the gadget url's live, if its the root set this to ''
+// don't forget to update your .htaccess to reflect this, as well as your container 
+// javascript like: gadget.setServerBase('/gadgets/');
+
+$config = array(
+	// the prefix of where this code resides
+	'web_prefix' => '/gadgets',
+
+	// location of the features directory on disk. The default setting assumes you did a 
+	// full checkout of the shindig project, and not just the php part.
+	// Otherwise also checkout the features, config and javascript directories and set
+	// these to their locations
+	'features_path' =>  realpath(dirname(__FILE__)).'/../../features/',
+	'syndicator_config' =>  realpath(dirname(__FILE__)).'/../../config/syndicator.js',
+	'javascript_path' =>  realpath(dirname(__FILE__)).'/../../javascript/',
+
+	// Configurable classes to use, this way we provide extensibility for what 
+	// backends the gadget server uses for its logic functionality. 
+	'blacklist_class' => 'BasicGadgetBlacklist',
+	'remote_content' => 'BasicRemoteContent',
+	'gadget_signer' => 'BasicGadgetSigner',
+	'gadget_token' => 'BasicGadgetToken',
+	'data_cache' => 'FileCache',
+
+	// gadget server specific settings
+	'userpref_param_prefix'=> 'up_',
+	'libs_param_name'=> 'libs',
+	'default_js_prefix'=> '/js/',
+	'default_iframe_prefix'=> 'ifr?',
+	
+	// show debugging output ?
+	'debug'=> true,
+	
+	// global cache age policy and location
+	'cache_time'=> 24*60*60,
+	'cache_root'=> '/tmp/shindig',
+	
+	// In some cases we need to know the site root (for features forinstance)
+	'base_path'=> realpath(dirname(__FILE__))
+);

Added: incubator/shindig/trunk/php/gadgets/index.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/gadgets/index.php?rev=633998&view=auto
==============================================================================
--- incubator/shindig/trunk/php/gadgets/index.php (added)
+++ incubator/shindig/trunk/php/gadgets/index.php Wed Mar  5 11:43:14 2008
@@ -0,0 +1,68 @@
+<?
+/*
+ * 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.
+ */
+
+/*
+ * Written by Chris Chabot <ch...@xs4all.nl> - http://www.chabotc.com
+ * 
+ * "It is not the strongest of the species that survives, nor the most intelligent that survives. 
+ * It is the one that is the most adaptable to change" - Darwin
+ * 
+ * So in this php version of shindig we make like java and act like a servlet setup, and using
+ * it's structures as our reference.
+ * 
+ * The .htaccess file redirects all requests that are neither an existing file or directory
+ * to this index.php, and the $servletMap checks which class is associated with it, if a mapping
+ * doesn't exist it display's a 404 error.
+ * 
+ * See config.php for the global settings and backend class selections.
+ * 
+ */
+
+include_once ('config.php');
+
+function __autoload($className)
+{
+	require_once 'src/' . $className . '.php';
+}
+
+$servletMap = array($config['web_prefix'] . '/files' => 'FilesServlet', $config['web_prefix'] . '/js' => 'JsServlet', $config['web_prefix'] . '/proxy' => 'ProxyServlet', $config['web_prefix'] . '/ifr' => 'GadgetRenderingServlet', $config['web_prefix'] . '/rpc' => 'RpcServlet');
+$servlet = false;
+$uri = $_SERVER["REQUEST_URI"];
+foreach ( $servletMap as $url => $class ) {
+	if (substr($uri, 0, strlen($url)) == $url) {
+		$servlet = $class;
+		break;
+	}
+}
+if ($servlet) {
+	// its worth mentioning that $class comes from our internal data array and not from
+	// the url, so the dynamic include is safe :-)
+	include_once ('src/http/HttpServlet.php');
+	include_once ("src/http/{$class}.php");
+	$class = new $class();
+	if (count($_POST)) {
+		$class->doPost();
+	} else {
+		$class->doGet();
+	}
+} else {
+	// Unhandled event, display simple 404 error
+	header("HTTP/1.0 404 Not Found");
+	echo "<html><body><h1>404 Not Found</h1></body></html>";
+}

Added: incubator/shindig/trunk/php/gadgets/src/BasicGadgetBlacklist.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/gadgets/src/BasicGadgetBlacklist.php?rev=633998&view=auto
==============================================================================
--- incubator/shindig/trunk/php/gadgets/src/BasicGadgetBlacklist.php (added)
+++ incubator/shindig/trunk/php/gadgets/src/BasicGadgetBlacklist.php Wed Mar  5 11:43:14 2008
@@ -0,0 +1,43 @@
+<?
+/*
+ * 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.
+ * 
+ */
+
+class BasicGadgetBlacklist {
+	private $rules = array();
+	
+	public function __construct()
+	{
+		global $config;
+		$file = $config['base_path'].'/blacklist.txt';
+		//TODO make this a configurable location
+		if (file_exists($file) && is_readable($file)) {
+			$this->rules = explode("\n", file_get_contents($file));
+		}
+	}
+	
+	function isBlacklisted($url)
+	{
+		foreach ($this->rules as $rule) {
+			if (preg_match($rule, $url)) {
+				return true;	
+			}
+		}
+		return false;
+	}
+}
\ No newline at end of file

Added: incubator/shindig/trunk/php/gadgets/src/BasicGadgetSigner.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/gadgets/src/BasicGadgetSigner.php?rev=633998&view=auto
==============================================================================
--- incubator/shindig/trunk/php/gadgets/src/BasicGadgetSigner.php (added)
+++ incubator/shindig/trunk/php/gadgets/src/BasicGadgetSigner.php Wed Mar  5 11:43:14 2008
@@ -0,0 +1,58 @@
+<?
+/*
+ * 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.
+ * 
+ */
+
+/*
+ * A GadgetSigner implementation that just provides dummy data to satisfy
+ * tests and API calls. Do not use this for any security applications.
+ */
+
+
+class BasicGadgetSigner extends GadgetSigner {
+	private $timeToLive;
+	
+	/**
+	 * Creates a signer with 24 hour token expiry
+	 */
+	public function __construct($timeToLive = false)
+	{
+		$this->timeToLive = $timeToLive ? $timeToLive : 24 * 60 * 60 * 1000;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 * This implementation only validates non-empty tokens. Empty tokens
+	 * are considered to always be valid.
+	 */
+	public function createToken($stringToken)
+	{
+		if ($stringToken != null && ! empty($stringToken)) {
+			$parts = explode('$', $stringToken);
+			if (count($parts) != 2) {
+				throw new GadgetException("Invalid token format.");
+			}
+			$expiry = intval($parts[1]);
+			$currentTimeMillis = time() * 1000;
+			if ($expiry < $currentTimeMillis) {
+				throw new GadgetException("Expired token.");
+			}
+		}
+		return new BasicGadgetToken($stringToken);
+	}
+}
\ No newline at end of file

Added: incubator/shindig/trunk/php/gadgets/src/BasicGadgetToken.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/gadgets/src/BasicGadgetToken.php?rev=633998&view=auto
==============================================================================
--- incubator/shindig/trunk/php/gadgets/src/BasicGadgetToken.php (added)
+++ incubator/shindig/trunk/php/gadgets/src/BasicGadgetToken.php Wed Mar  5 11:43:14 2008
@@ -0,0 +1,53 @@
+<?
+/*
+ * 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.
+ * 
+ */
+
+/*
+ * Primitive token implementation that uses stings as tokens.
+ */
+
+class BasicGadgetToken extends GadgetToken {
+	private $token;
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public function toSerialForm()
+	{
+		return $this->token;
+	}
+	
+	/**
+	 * Generates a token from an input string
+	 * @param token String form of token
+	 */
+	public function BasicGadgetToken($token)
+	{
+		$this->token = $token;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 * Signer that does not sign.
+	 */
+	public function signUrl($uri, $httpMethod)
+	{
+		return $uri;
+	}
+}

Added: incubator/shindig/trunk/php/gadgets/src/BasicRemoteContent.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/gadgets/src/BasicRemoteContent.php?rev=633998&view=auto
==============================================================================
--- incubator/shindig/trunk/php/gadgets/src/BasicRemoteContent.php (added)
+++ incubator/shindig/trunk/php/gadgets/src/BasicRemoteContent.php Wed Mar  5 11:43:14 2008
@@ -0,0 +1,56 @@
+<?
+/*
+ * 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.
+ * 
+ */
+
+class BasicRemoteContent extends RemoteContent {
+	
+	public function fetch($requests, $options)
+	{
+		global $config;
+		$cache = new $config['data_cache']();
+		if ($requests instanceof RemoteContentRequest) {
+			$requests = array($requests);
+		}
+		$remoteContentFetcher = new BasicRemoteContentFetcher();
+		$ret = array();
+		foreach ( $requests as $request ) {
+			if (! ($request instanceof RemoteContentRequest)) {
+				throw new RemoteContentException("Invalid request type in remoteContent");
+			}
+			// determine which requests we can load from cache, and which we have to actually fetch
+			if (! $options->ignoreCache && ($cachedRequest = $cache->get(md5($request->toHash()))) !== false) {
+				$ret[] = $cachedRequest;
+			} else {
+				$remoteContentFetcher->addRequest($request);
+			}
+		}
+		if ($remoteContentFetcher->pendingRequests()) {
+			$requests = $remoteContentFetcher->fetchRequests();
+			// store the objects we just fetched in cache
+			foreach ( $requests as $request ) {
+				if ($request->getHttpCode() == '200') {
+					// only cache requests that returned a 200 OK
+					$cache->set(md5($request->toHash()), $request);
+				}
+			}
+			$ret = array_merge($requests, $ret);
+		}
+		return $ret;
+	}
+}
\ No newline at end of file

Added: incubator/shindig/trunk/php/gadgets/src/BasicRemoteContentFetcher.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/gadgets/src/BasicRemoteContentFetcher.php?rev=633998&view=auto
==============================================================================
--- incubator/shindig/trunk/php/gadgets/src/BasicRemoteContentFetcher.php (added)
+++ incubator/shindig/trunk/php/gadgets/src/BasicRemoteContentFetcher.php Wed Mar  5 11:43:14 2008
@@ -0,0 +1,97 @@
+<?
+/*
+ * 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.
+ * 
+ */
+
+/*
+ * Basic remote content fetcher, uses curl_multi to fetch multiple resources at the same time
+ */
+
+class BasicRemoteContentFetcher extends remoteContentFetcher {
+	private $requests = array();
+	
+	public function addRequests($requests)
+	{
+		foreach ( $request as $request ) {
+			$this->addRequest($request);
+		}
+	}
+	
+	public function addRequest(remoteContentRequest $request)
+	{
+		$url = $request->getUrl();
+		if (empty($url)) {
+			throw new remoteContentException("Invalid or empty url specified in remoteContentFetcher");
+		}
+		$this->requests[] = $request;
+	}
+	
+	public function fetchRequests()
+	{
+		$ret = array();
+		$mh = curl_multi_init();
+		// Setup each request based on its options
+		foreach ( $this->requests as $request ) {
+			$request->handle = curl_init();
+			curl_setopt($request->handle, CURLOPT_URL, $request->getUrl());
+			curl_setopt($request->handle, CURLOPT_FOLLOWLOCATION, 1);
+			curl_setopt($request->handle, CURLOPT_RETURNTRANSFER, 1);
+			curl_setopt($request->handle, CURLOPT_HEADER, 1);
+			if ($request->hasHeaders()) {
+				curl_setopt($request->handle, CURLOPT_HTTPHEADER, array($request->getHeaders()));
+			}
+			if ($request->isPost()) {
+				curl_setopt($request->handle, CURLOPT_POST, 1);
+				curl_setopt($request->handle, CURLOPT_POSTFIELDS, $request->getPostBody());
+			}
+			curl_multi_add_handle($mh, $request->handle);
+		}
+		// Execute the multi fetch
+		$running = null;
+		do {
+			curl_multi_exec($mh, $running);
+		} while ( $running > 0 );
+		
+		// Done, close handles
+		foreach ( $this->requests as $key => $request ) {
+			$content = curl_multi_getcontent($request->handle);
+			$body = substr($content, strpos($content, "\r\n\r\n") + 4);
+			$header = substr($content, 0, strpos($content, "\r\n\r\n"));
+			$httpCode = curl_getinfo($request->handle, CURLINFO_HTTP_CODE);
+			$contentType = curl_getinfo($request->handle, CURLINFO_CONTENT_TYPE);
+			$request->setHttpCode($httpCode);
+			$request->setContentType($contentType);
+			$request->setResponseHeaders($header);
+			$request->setResponseContent($body);
+			$request->setResponseSize(strlen($content));
+			curl_multi_remove_handle($mh, $request->handle);
+			unset($request->handle);
+		}
+		curl_multi_close($mh);
+		
+		$ret = $this->requests;
+		// empty our requests queue
+		$this->requests = array();
+		return $ret;
+	}
+	
+	public function pendingRequests()
+	{
+		return count($this->requests);
+	}
+}

Added: incubator/shindig/trunk/php/gadgets/src/BidiSubstituter.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/gadgets/src/BidiSubstituter.php?rev=633998&view=auto
==============================================================================
--- incubator/shindig/trunk/php/gadgets/src/BidiSubstituter.php (added)
+++ incubator/shindig/trunk/php/gadgets/src/BidiSubstituter.php Wed Mar  5 11:43:14 2008
@@ -0,0 +1,74 @@
+<?
+/*
+ * 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.
+ * 
+ */
+
+class BidiSubstituter extends GadgetFeatureFactory {
+	private $feature;
+	
+	public function __construct()
+	{
+		$this->feature = new BidiSubstituterFeature();
+	}
+	
+	public function create()
+	{
+		return $this->feature;
+	}
+}
+
+class BidiSubstituterFeature extends GadgetFeature {
+	private function getLocaleSpec($spec, $locale)
+	{
+		$localeSpecs = $spec->getLocaleSpecs();
+		foreach ( $localeSpecs as $locSpec ) {
+			//FIXME equals, localeSpec, much unknown here still..
+			if ($locSpec->getLocale()->equals($locale)) {
+				return $locSpec;
+			}
+		}
+		return null;
+	}
+	
+	public function prepare($spec, $context, $params)
+	{
+		// Nothing here.
+	}
+	
+	public function process($gadget, $context, $params)
+	{
+		$subst = $gadget->getSubstitutions();
+		$locale = $context->getLocale();
+		// Find an appropriate locale for the ltr flag.
+		$locSpec = $this->getLocaleSpec($gadget, $locale);
+		if ($locSpec == null) {
+			$locSpec = $this->getLocaleSpec($gadget, new Locale($locale->getLanguage(), "all"));
+		}
+		if ($locSpec == null) {
+			$locSpec = $this->getLocaleSpec($gadget, new Locale("all", "all"));
+		}
+		$rtl = false;
+		if ($locSpec != null) {
+			$rtl = $locSpec->isRightToLeft();
+		}
+		$subst->addSubstitution('BIDI', "START_EDGE", $rtl ? "right" : "left");
+		$subst->addSubstitution('BIDI', "END_EDGE", $rtl ? "left" : "right");
+		$subst->addSubstitution('BIDI', "DIR", $rtl ? "rtl" : "ltr");
+		$subst->addSubstitution('BIDI', "REVERSE_DIR", $rtl ? "ltr" : "rtl");
+	}
+}
\ No newline at end of file

Added: incubator/shindig/trunk/php/gadgets/src/Cache.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/gadgets/src/Cache.php?rev=633998&view=auto
==============================================================================
--- incubator/shindig/trunk/php/gadgets/src/Cache.php (added)
+++ incubator/shindig/trunk/php/gadgets/src/Cache.php Wed Mar  5 11:43:14 2008
@@ -0,0 +1,27 @@
+<?
+/*
+ * 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.
+ * 
+ */
+
+class CacheException extends Exception {}
+
+abstract class Cache {
+	abstract function get($key);
+	abstract function set($key, $value);
+	abstract function delete($key);
+}
\ No newline at end of file

Added: incubator/shindig/trunk/php/gadgets/src/FileCache.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/gadgets/src/FileCache.php?rev=633998&view=auto
==============================================================================
--- incubator/shindig/trunk/php/gadgets/src/FileCache.php (added)
+++ incubator/shindig/trunk/php/gadgets/src/FileCache.php Wed Mar  5 11:43:14 2008
@@ -0,0 +1,90 @@
+<?
+/*
+ * 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.
+ * 
+ */
+
+/*
+ * This class impliments a basic on disk caching. That will work fine on a single host
+ * but on a multi server setup this could lead to some problems due to inconsistencies
+ * between the various cached versions on the different servers. Other methods like
+ * memcached should be used instead really.
+ * 
+ * When using this file based backend, its adviced to make a cron job that scans thru the
+ * cache dir, and removes all files that are older then 24 hours (or whatever your
+ * config's CACHE_TIME is set too).
+ */
+
+//TODO add cache stampeding prevention using file locking mechanisms
+
+class FileCache extends Cache {
+	
+	function get($key)
+	{
+		global $config;
+		$cacheFile = $this->getCacheFile($key);
+		if (file_exists($cacheFile) && is_readable($cacheFile)) {
+			$now = time();
+			if (($mtime = filemtime($cacheFile)) !== false && ($now - $mtime) < $config['cache_time']) {
+				if (($data = @file_get_contents($cacheFile)) !== false){
+					$data = unserialize($data);
+					return $data;
+				}
+			}
+		}
+		return false;
+	}
+	
+	function set($key, $value)
+	{
+		// use the first 2 characters of the hash as a directory prefix
+		// this should prevent slowdowns due to huge directory listings
+		// and thus give some basic amount of scalability
+		$cacheDir = $this->getCacheDir($key);
+		$cacheFile = $this->getCacheFile($key);
+		if (! is_dir($cacheDir)) {
+			if (! @mkdir($cacheDir, 0755, true)) {
+				throw new CacheException("Could not create cache directory");
+			}
+		}
+		// we serialize the whole request object, since we don't only want the
+		// responseContent but also the postBody used, headers, size, etc
+		$data = serialize($value);
+		if (! file_put_contents($cacheFile, $data)) {
+			throw new CacheException("Could not store data in cache file");
+		}
+	}
+	
+	function delete($key)
+	{
+		$file = $this->getCacheFile($key);
+		if (! @unlink($file)) {
+			throw new CacheException("Cache file could not be deleted");
+		}
+	}
+	
+	private function getCacheDir($hash)
+	{
+		global $config;
+		return $config['cache_root'] . '/' . substr($hash, 0, 2);
+	}
+	
+	private function getCacheFile($hash)
+	{
+		return $this->getCacheDir($hash) . '/' . $hash;
+	}
+}
\ No newline at end of file

Added: incubator/shindig/trunk/php/gadgets/src/Gadget.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/gadgets/src/Gadget.php?rev=633998&view=auto
==============================================================================
--- incubator/shindig/trunk/php/gadgets/src/Gadget.php (added)
+++ incubator/shindig/trunk/php/gadgets/src/Gadget.php Wed Mar  5 11:43:14 2008
@@ -0,0 +1,202 @@
+<?
+/*
+ * 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.
+ * 
+ */
+
+final class GadgetId extends GadgetViewID {
+	private $uri;
+	private $moduleId;
+	
+	public function GadgetId($uri, $moduleId)
+	{
+		$this->uri = $uri;
+		$this->moduleId = $moduleId;
+	}
+	
+	public function getURI()
+	{
+		return $this->uri;
+	}
+	
+	public function getModuleId()
+	{
+		return $this->moduleId;
+	}
+	
+	public function getKey()
+	{
+		return $this->getURI();
+	}
+}
+
+class Gadget extends GadgetSpec {
+	public $baseSpec;
+	private $jsLibraries;
+	private $substitutions;
+	private $userPrefValues;
+	private $currentMessageBundle = array();
+	
+	public function __construct($id, GadgetSpec $baseSpec, $prefs)
+	{
+		$this->id = $id;
+		$this->baseSpec = $baseSpec;
+		$this->substitutions = new Substitutions();
+		$this->userPrefValues = $prefs;
+		$this->jsLibraries = array();
+	}
+	
+	public function getAuthor()
+	{
+		return $this->substitutions->substitute($this->baseSpec->getAuthor());
+	}
+	
+	public function getAuthorEmail()
+	{
+		return $this->substitutions->substitute($this->baseSpec->getAuthorEmail());
+	}
+	
+	public function getBaseSpec()
+	{
+		return $this->baseSpec;
+	}
+	
+	public function getContentData($view = false)
+	{
+		return $this->substitutions->substitute($this->baseSpec->getContentData($view));
+	}
+	
+	public function getContentHref()
+	{
+		return $this->substitutions->substitute($this->baseSpec->getContentHref());
+	}
+	
+	public function getContentType()
+	{
+		return $this->baseSpec->getContentType();
+	}
+	
+	public function getCurrentMessageBundle()
+	{
+		return $this->currentMessageBundle;
+	}
+	
+	public function getDescription()
+	{
+		return $this->substitutions->substitute($this->baseSpec->getDescription());
+	}
+	
+	public function getDirectoryTitle()
+	{
+		return $this->substitutions->substitute($this->baseSpec->getDirectoryTitle());
+	}
+	
+	public function getIcons()
+	{
+		return $this->baseSpec->getIcons();
+	}
+	
+	public function getId()
+	{
+		return $this->id;
+	}
+	
+	public function getJsLibraries()
+	{
+		return $this->jsLibraries;
+	}
+	
+	public function addJsLibrary($library)
+	{
+		$this->jsLibraries[] = $library;
+	}
+	
+	public function getLocaleSpecs()
+	{
+		return $this->baseSpec->getLocaleSpecs();
+	}
+	
+	public function getFeatureParams($gadget, $feature)
+	{
+		//FIXME not working atm
+		$spec = $gadget->getRequires();
+		$spec = isset($spec[$feature->getName()]) ? $spec[$feature->getName()] : null;
+		if ($spec == null) {
+			return array();
+		} else {
+			return $spec->getParams();
+		}
+	}
+	
+	public function getPreloads()
+	{
+		$ret = array();
+		foreach ( $this->baseSpec->getPreloads() as $preload ) {
+			$ret[] = $this->substitutions->substitute($preload);
+		}
+		return $ret;
+	}
+	
+	public function getRequires()
+	{
+		return $this->baseSpec->getRequires();
+	}
+	
+	public function getScreenshot()
+	{
+		return $this->substitutions->substitute($this->baseSpec->getScreenshot());
+	}
+	
+	public function getSubstitutions()
+	{
+		return $this->substitutions;
+	}
+	
+	public function getThumbnail()
+	{
+		return $this->substitutions->substitute($this->baseSpec->getThumbnail());
+	}
+	
+	public function getTitle()
+	{
+		return $this->substitutions->substitute($this->baseSpec->getTitle());
+	}
+	
+	public function getTitleURI()
+	{
+		$ret = null;
+		if (($uri = $this->baseSpec->getTitleURI()) != null) {
+			$ret = $this->substitutions->substitute($uri);
+		}
+		return $ret;
+	}
+	
+	public function getUserPrefs()
+	{
+		return $this->baseSpec->getUserPrefs();
+	}
+	
+	public function getUserPrefValues()
+	{
+		return $this->userPrefValues;
+	}
+	
+	public function setCurrentMessageBundle($messageBundle)
+	{
+		$this->currentMessageBundle = $messageBundle;
+	}
+}
\ No newline at end of file

Added: incubator/shindig/trunk/php/gadgets/src/GadgetBlacklist.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/gadgets/src/GadgetBlacklist.php?rev=633998&view=auto
==============================================================================
--- incubator/shindig/trunk/php/gadgets/src/GadgetBlacklist.php (added)
+++ incubator/shindig/trunk/php/gadgets/src/GadgetBlacklist.php Wed Mar  5 11:43:14 2008
@@ -0,0 +1,23 @@
+<?
+/*
+ * 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.
+ * 
+ */
+
+abstract class GadgetBlacklist {
+	abstract function isBlacklisted($url);
+}
\ No newline at end of file

Added: incubator/shindig/trunk/php/gadgets/src/GadgetContext.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/gadgets/src/GadgetContext.php?rev=633998&view=auto
==============================================================================
--- incubator/shindig/trunk/php/gadgets/src/GadgetContext.php (added)
+++ incubator/shindig/trunk/php/gadgets/src/GadgetContext.php Wed Mar  5 11:43:14 2008
@@ -0,0 +1,67 @@
+<?
+/*
+ * 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.
+ * 
+ */
+
+/*
+ * the httpFetcher (RemoteContentFetcher) is our cache too, so we skip the
+ * messageBundleCache vars and params.
+ */
+
+class GadgetContext {
+	private $httpFetcher;
+	private $locale;
+	private $options;
+	private $renderingContext;
+	private $registry;
+	
+	public function __construct(RemoteContent $httpFetcher, Locale $locale, $renderingContext, ProcessingOptions $options, GadgetFeatureRegistry $registry)
+	{
+		$this->httpFetcher = $httpFetcher;
+		$this->locale = $locale;
+		$this->renderingContext = $renderingContext;
+		$this->options = $options;
+		$this->registry = $registry;
+	}
+	
+	public function getRenderingContext()
+	{
+		return $this->renderingContext;
+	}
+	
+	public function getHttpFetcher()
+	{
+		return $this->httpFetcher;
+	}
+	
+	public function getLocale()
+	{
+		return $this->locale;
+	}
+	
+	public function getFeatureRegistry()
+	{
+		return $this->registry;
+	}
+	
+	public function getOptions()
+	{
+		return $this->options;
+	}
+
+}
\ No newline at end of file

Added: incubator/shindig/trunk/php/gadgets/src/GadgetException.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/gadgets/src/GadgetException.php?rev=633998&view=auto
==============================================================================
--- incubator/shindig/trunk/php/gadgets/src/GadgetException.php (added)
+++ incubator/shindig/trunk/php/gadgets/src/GadgetException.php Wed Mar  5 11:43:14 2008
@@ -0,0 +1,21 @@
+<?
+/*
+ * 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.
+ * 
+ */
+
+class GadgetException extends Exception {}
\ No newline at end of file

Added: incubator/shindig/trunk/php/gadgets/src/GadgetFeature.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/gadgets/src/GadgetFeature.php?rev=633998&view=auto
==============================================================================
--- incubator/shindig/trunk/php/gadgets/src/GadgetFeature.php (added)
+++ incubator/shindig/trunk/php/gadgets/src/GadgetFeature.php Wed Mar  5 11:43:14 2008
@@ -0,0 +1,24 @@
+<?
+/*
+ * 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.
+ * 
+ */
+
+abstract class GadgetFeature {
+	abstract public function prepare($gadget, $context, $params);
+	abstract public function process($gadget, $context, $params);
+}
\ No newline at end of file

Added: incubator/shindig/trunk/php/gadgets/src/GadgetFeatureFactory.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/gadgets/src/GadgetFeatureFactory.php?rev=633998&view=auto
==============================================================================
--- incubator/shindig/trunk/php/gadgets/src/GadgetFeatureFactory.php (added)
+++ incubator/shindig/trunk/php/gadgets/src/GadgetFeatureFactory.php Wed Mar  5 11:43:14 2008
@@ -0,0 +1,23 @@
+<?
+/*
+ * 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.
+ * 
+ */
+
+abstract class GadgetFeatureFactory {
+	abstract public function create();
+}
\ No newline at end of file

Added: incubator/shindig/trunk/php/gadgets/src/GadgetFeatureRegistry.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/gadgets/src/GadgetFeatureRegistry.php?rev=633998&view=auto
==============================================================================
--- incubator/shindig/trunk/php/gadgets/src/GadgetFeatureRegistry.php (added)
+++ incubator/shindig/trunk/php/gadgets/src/GadgetFeatureRegistry.php Wed Mar  5 11:43:14 2008
@@ -0,0 +1,170 @@
+<?
+/*
+ * 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.
+ * 
+ */
+
+class GadgetFeatureRegistry {
+	private $features = array();
+	private $core = array();
+	private $coreDone = false;
+	
+	public function __construct($featurePath)
+	{
+		$this->registerFeatures($featurePath);
+	}
+	
+	public function registerFeatures($featurePath)
+	{
+		if (empty($featurePath) || $featurePath == null) {
+			return;
+		}
+		$FEAT_MSG_BUNDLE = "core.msgbundlesubst";
+		$FEAT_BIDI = "core.bidisubst";
+		$FEAT_MODULE = "core.modulesubst";
+		$FEAT_USER_PREF_SUBST = "core.prefsubst";
+		$coreDeps = array();
+		$loader = new JsFeatureLoader();
+		$jsFeatures = $loader->loadFeatures($featurePath, $this);
+		if (! $this->coreDone) {
+			foreach ( $jsFeatures as $entry ) {
+				if (strtolower(substr($entry->name, 0, strlen('core'))) == 'core') {
+					$coreDeps[] = $entry->name;
+					$this->core[$entry->name] = $entry->name;
+				}
+			}
+			$this->core[$FEAT_MSG_BUNDLE] = $FEAT_MSG_BUNDLE;
+			$this->register($FEAT_MSG_BUNDLE, $coreDeps, new MessageBundleSubstituter());
+			$this->core[$FEAT_BIDI] = $FEAT_BIDI;
+			$this->register($FEAT_BIDI, $coreDeps, new BidiSubstituter());
+			$this->core[$FEAT_MODULE] = $FEAT_MODULE;
+			$this->register($FEAT_MODULE, $coreDeps, new ModuleSubstituter());
+			$this->core[$FEAT_USER_PREF_SUBST] = $FEAT_USER_PREF_SUBST;
+			$this->register($FEAT_USER_PREF_SUBST, $coreDeps, new UserPrefSubstituter());
+			// Make sure non-core features depend on core.
+			foreach ( $jsFeatures as $entry ) {
+				if (strtolower(substr($entry->name, 0, strlen('core'))) != 'core') {
+					$entry->deps = array_merge($entry->deps, $this->core);
+				}
+			}
+			$this->coreDone = true;
+		}
+	}
+	
+	public function register($name, $deps, $feature)
+	{
+		// Core entries must come first.
+		$entry = isset($this->features[$name]) ? $this->features[$name] : null;
+		if ($entry == null) {
+			$entry = new GadgetFeatureRegistryEntry($name, $deps, $feature, $this);
+			if ($this->coreDone) {
+				$entry->deps = array_merge($entry->deps, $this->core);
+			}
+			$this->features[$name] = $entry;
+			$this->validateFeatureGraph();
+		}
+		return $entry;
+	}
+	
+	private function validateFeatureGraph()
+	{
+		// TODO: ensure that features form a DAG and that all deps are provided
+	}
+	
+	public function getAllFeatures()
+	{
+		return $this->features;
+	}
+	
+	public function getIncludedFeatures($needed, &$resultsFound, &$resultsMissing)
+	{
+		$resultsFound = array();
+		$resultsMissing = array();
+		if (! count($needed)) {
+			// Shortcut for gadgets that don't have any explicit dependencies.
+			$resultsFound = $this->core;
+			return true;
+		}
+		foreach ( $needed as $featureName ) {
+			$entry = isset($this->features[$featureName]) ? $this->features[$featureName] : null;
+			if ($entry == null) {
+				$resultsMissing[] = $featureName;
+			} else {
+				$this->addEntryToSet($resultsFound, $entry);
+			}
+		}
+		return count($resultsMissing) == 0;
+	}
+	
+	private function addEntryToSet(&$results, $entry)
+	{
+		foreach ( $entry->deps as $dep ) {
+			$this->addEntryToSet($results, $this->features[$dep]);
+		}
+		$results[$entry->name] = $entry->name;
+	}
+	
+	public function getEntry($name)
+	{
+		return $this->features[$name];
+	}
+}
+
+// poor man's namespacing
+class GadgetFeatureRegistryEntry {
+	public $name;
+	public $deps = array();
+	public $feature;
+	
+	public function __construct($name, $deps, $feature, $registry)
+	{
+		$this->name = $name;
+		if ($deps != null) {
+			foreach ( $deps as $dep ) {
+				$entry = $registry->getEntry($dep);
+				$this->deps[$entry->name] = $entry->name;
+			}
+		}
+		$this->feature = $feature;
+	}
+	
+	public function getName()
+	{
+		return $this->name;
+	}
+	
+	public function getDependencies()
+	{
+		return $this->deps;
+	}
+	
+	public function getFeature()
+	{
+		return $this->feature;
+	}
+}
+
+class NoOpFeature extends GadgetFeature {
+	
+	public function prepare($gadget, $context, $params)
+	{
+	}
+	
+	public function process($gadget, $context, $params)
+	{
+	}
+}

Added: incubator/shindig/trunk/php/gadgets/src/GadgetServer.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/gadgets/src/GadgetServer.php?rev=633998&view=auto
==============================================================================
--- incubator/shindig/trunk/php/gadgets/src/GadgetServer.php (added)
+++ incubator/shindig/trunk/php/gadgets/src/GadgetServer.php Wed Mar  5 11:43:14 2008
@@ -0,0 +1,115 @@
+<?
+/*
+ * 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.
+ * 
+ */
+
+/*
+ * This isn't a multi threaded java envirioment, so we do things a bit more straightforward with context blocks and workflows,
+ * which means departing from how the shinding java implementation works but it saves a lot 'dead' code here
+ */
+
+class GadgetServer {
+	private $registry;
+	private $blacklist;
+	private $gc;
+	private $gadgetId;
+	private $userPrefs;
+	private $renderingContext;
+	private $options;
+	private $locale;
+	private $httpFetcher;
+	
+	public function processGadget($gadgetId, $userPrefs, $locale, $rctx, $options, $httpFetcher, $registry)
+	{
+		global $config;
+		$this->gadgetId = $gadgetId;
+		$this->userPrefs = $userPrefs;
+		$this->renderingContext = $rctx;
+		$this->locale = $locale;
+		$this->registry = $registry;
+		$this->options = $options;
+		$this->httpFetcher = $httpFetcher;
+		$this->gc = new GadgetContext($httpFetcher, $locale, $rctx, $options, $registry);
+		$this->blacklist = new $config['blacklist_class']();
+		$gadget = $this->specLoad();
+		$this->featuresLoad($gadget);
+		return $gadget;
+	}
+	
+	private function specLoad()
+	{
+		if ($this->blacklist != null && $this->blacklist->isBlacklisted($this->gadgetId->getURI())) {
+			throw new GadgetException("Gadget is blacklisted");
+		}
+		$request = new remoteContentRequest($this->gadgetId->getURI());
+		list($xml) = $this->httpFetcher->fetch($request, $this->options);
+		if ($xml->getHttpCode() != '200') {
+			throw new GadgetException("Failed to retrieve gadget content");
+		}
+		$specParser = new GadgetSpecParser();
+		$spec = $specParser->parse($xml->getResponseContent());
+		$gadget = new Gadget($this->gadgetId, $spec, $this->userPrefs);
+		return $gadget;
+	}
+	
+	private function featuresLoad($gadget)
+	{
+		$requires = $gadget->getRequires();
+		$needed = array();
+		$optionalNames = array();
+		foreach ( $requires as $key => $entry ) {
+			$needed[] = $key;
+			if ($entry->isOptional()) {
+				$optionalNames[] = $key;
+			}
+		}
+		$resultsFound = array();
+		$resultsMissing = array();
+		$missingOptional = array();
+		$missingRequired = array();
+		$this->registry->getIncludedFeatures($needed, $resultsFound, $resultsMissing);
+		foreach ( $resultsMissing as $missingResult ) {
+			if (in_array($missingResult, $optionalNames)) {
+				$missingOptional[$missingResult] = $missingResult;
+			} else {
+				$missingRequired[$missingResult] = $missingResult;
+			}
+		}
+		if (count($missingRequired)) {
+			throw new GadgetException("Unsupported feature(s): " . implode(', ', $missingRequired));
+		}
+		// create features
+		$features = array();
+		foreach ( $resultsFound as $entry ) {
+			$features[$entry] = $this->registry->getEntry($entry)->getFeature()->create();
+		}
+		// prepare them
+		foreach ( $features as $key => $feature ) {
+			$params = $gadget->getFeatureParams($gadget, $this->registry->getEntry($key));
+			$feature->prepare($gadget, $this->gc, $params);
+		}
+		// and process them
+		foreach ( $features as $key => $feature ) {
+			$params = $gadget->getFeatureParams($gadget, $this->registry->getEntry($key));
+			$feature->process($gadget, $this->gc, $params);
+		}
+	}
+}
+
+
+

Added: incubator/shindig/trunk/php/gadgets/src/GadgetSigner.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/gadgets/src/GadgetSigner.php?rev=633998&view=auto
==============================================================================
--- incubator/shindig/trunk/php/gadgets/src/GadgetSigner.php (added)
+++ incubator/shindig/trunk/php/gadgets/src/GadgetSigner.php Wed Mar  5 11:43:14 2008
@@ -0,0 +1,48 @@
+<?
+/*
+ * 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.
+ * 
+ */
+
+/**
+ *  Handles generation of signing tokens for various request types.
+ *  Implementations are free to define their own signing parameters in any
+ *  way that is suitable for their site.
+ */
+abstract class GadgetSigner {
+	/**
+	 * Generates a token for the given gadget.
+	 * Implementations should also add their own user-related context data
+	 * to the token.
+	 * 
+	 * Or generates a token from an input string. This call must produce a token that
+	 * will validate against a token produced directly from a gadget so that the
+	 * following function will always returns a valid GadgetToken:
+	 *
+	 * <code>
+	 * GadgetToken testToken(Gadget gadget, GadgetSigner signer) {
+	 *   GadgetToken token = signer.createToken(gadget);
+	 *   return signer.createToken(token.toSerialForm());
+	 * }
+	 * </code>
+	 *
+	 * @param tokenString String representation of the token to be created.
+	 * @return The token representation of the input data.
+	 * @throws GadgetException If tokenString is not a valid token
+	 */
+	abstract public function createToken($gadget);
+}

Added: incubator/shindig/trunk/php/gadgets/src/GadgetSpec.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/gadgets/src/GadgetSpec.php?rev=633998&view=auto
==============================================================================
--- incubator/shindig/trunk/php/gadgets/src/GadgetSpec.php (added)
+++ incubator/shindig/trunk/php/gadgets/src/GadgetSpec.php Wed Mar  5 11:43:14 2008
@@ -0,0 +1,102 @@
+<?
+/*
+ * 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.
+ * 
+ */
+
+define('DEFAULT_VIEW', 'default');
+
+// Locale class doesn't exist in php, so to allow the code base to be closer to the java one, were faking one
+class Locale {
+	public $language;
+	public $country;
+	
+	public function __construct($language, $country)
+	{
+		$this->language = $language;
+		$this->country = $country;
+	}
+	public function equals($obj)
+	{
+		if (!($obj instanceof Locale)) {
+			return false;
+		}
+		return ($obj->language == $this->language && $obj->country == $this->country);
+	}
+	
+	public function getLanguage()
+	{
+		return $this->language;
+	}
+	
+	public function getCountry()
+	{
+		return $this->country;
+	}
+	
+}
+
+abstract class Icon {
+	abstract public function getURI();
+	abstract public function getMode();
+	abstract public function getType();
+}
+
+abstract class LocaleSpec {
+	abstract public function getLocale();
+	abstract public function getURI();
+	abstract public function isRightToLeft();
+}
+
+abstract class FeatureSpec {
+	abstract public function getName();
+	abstract public function getParams();
+	abstract public function isOptional();
+}
+
+abstract class UserPref {
+	// enums are not suported in php, so we store our allowed values, and programaticly check for consitency later on
+	public $DataTypes = array('STRING', 'HIDDEN', 'BOOL', 'ENUM', 'LIST', 'NUMBER');
+	public $dataType;
+	abstract public function getName();
+	abstract public function getDisplayName();
+	abstract public function getDefaultValue();
+	abstract public function isRequired();
+	abstract public function getDataType();
+	abstract public function getEnumValues();
+}
+
+abstract class GadgetSpec {
+	// As in UserPref, no enums so fake it
+	public $contentTypes    = array('HTML', 'URL');
+	abstract public function getAuthor();
+	abstract public function getAuthorEmail();
+	abstract public function getContentData($view = false);
+	abstract public function getContentHref();
+	abstract public function getContentType();
+	abstract public function getDescription();
+	abstract public function getDirectoryTitle();
+	abstract public function getIcons();
+	abstract public function getLocaleSpecs();
+	abstract public function getPreloads();
+	abstract public function getRequires();
+	abstract public function getScreenshot();
+	abstract public function getThumbnail();
+	abstract public function getTitle();
+	abstract public function getTitleURI();
+	abstract public function getUserPrefs();
+}
\ No newline at end of file

Added: incubator/shindig/trunk/php/gadgets/src/GadgetSpecParser.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/gadgets/src/GadgetSpecParser.php?rev=633998&view=auto
==============================================================================
--- incubator/shindig/trunk/php/gadgets/src/GadgetSpecParser.php (added)
+++ incubator/shindig/trunk/php/gadgets/src/GadgetSpecParser.php Wed Mar  5 11:43:14 2008
@@ -0,0 +1,388 @@
+<?
+/*
+ * 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.
+ * 
+ */
+
+class GadgetSpecParser {
+	//public function parse(GadgetId $id, String $xml)
+	public function parse($xml)
+	{
+		$id = 1;
+		if (empty($xml)) {
+			throw new SpecParserException("Empty XML document");
+		}
+		//TODO add libxml_get_errors() functionality so we can have a bit more understandable errors..
+		if (($doc = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)) == false) {
+			throw new SpecParserException("Invalid XML document");
+		}
+		if (count($doc->ModulePrefs) != 1) {
+			throw new SpecParserException("Missing or duplicated <ModulePrefs>");
+		}
+		$spec = new ParsedGadgetSpec();
+		$spec->id = $id;
+		// process ModulePref attributes
+		$this->processModulePrefs($id, $spec, $doc->ModulePrefs);
+		// process UserPrefs, if any
+		foreach ( $doc->UserPref as $pref ) {
+			$this->processUserPref($spec, $pref);
+		}
+		foreach ( $doc->Content as $content ) {
+			$this->processContent($spec, $content);
+		}
+		//FIXME : should we add an else { throw new SpecParserException("Missing <Content> block"); } here ? Java version doesn't but it seems like we should ?
+		foreach ( $doc->ModulePrefs->Require as $feature ) {
+			$this->processFeature($spec, $feature, true);
+		}
+		foreach ( $doc->ModulePrefs->Optional as $feature ) {
+			$this->processFeature($spec, $feature, false);
+		}
+		//TODO java version has a todo here for parsing icons
+		return $spec;
+	}
+	
+	private function processModulePrefs($id, &$spec, $ModulePrefs)
+	{
+		$attributes = $ModulePrefs->attributes();
+		if (empty($attributes['title'])) {
+			throw new SpecParserException("Missing or empty \"title\" attribute.");
+		}
+		// Get ModulePrefs attributes
+		// (trim is used here since it not only cleans up the text, but also auto-casts the SimpleXMLElement to a string)
+		$spec->title = trim($attributes['title']);
+		$spec->author = isset($attributes['author']) ? trim($attributes['author']) : '';
+		$spec->authorEmail = isset($attributes['author_email']) ? trim($attributes['author_email']) : '';
+		$spec->description = isset($attributes['description']) ? trim($attributes['description']) : '';
+		$spec->directoryTitle = isset($attributes['directory_title']) ? trim($attributes['directory_title']) : '';
+		$spec->screenshot = isset($attributes['screenshot']) ? trim($attributes['screenshot']) : '';
+		$spec->thumbnail = isset($attributes['thumbnail']) ? trim($attributes['thumbnail']) : '';
+		$spec->titleUrl = isset($attributes['title_url']) ? trim($attributes['title_url']) : '';
+		foreach ( $ModulePrefs->Locale as $locale ) {
+			$spec->localeSpecs[] = $this->processLocale($locale);
+		}
+	
+	}
+	
+	private function processLocale($locale)
+	{
+		$attributes = $locale->attributes();
+		$messageAttr = isset($attributes['messages']) ? trim($attributes['messages']) : '';
+		$languageAttr = isset($attributes['lang']) ? trim($attributes['lang']) : 'all';
+		$countryAttr = isset($attributes['country']) ? trim($attributes['country']) : 'all';
+		$rtlAttr = isset($attributes['language_direction']) ? trim($attributes['language_direction']) : '';
+		$rightToLeft = $rtlAttr == 'rtl';
+		$locale = new ParsedMessageBundle();
+		$locale->rightToLeft = $rightToLeft;
+		//FIXME java seems to use a baseurl here, probably for the http:// part but i'm not sure yet. Should verify behavior later to see if i got it right
+		$locale->url = $messageAttr;
+		$locale->locale = new Locale($languageAttr, $countryAttr);
+		return $locale;
+	}
+	
+	private function processUserPref(&$spec, $pref)
+	{
+		$attributes = $pref->attributes();
+		$preference = new ParsedUserPref();
+		if (empty($attributes['name'])) {
+			throw new SpecParserException("All UserPrefs must have name attributes.");
+		}
+		$preference->name = trim($attributes['name']);
+		$preference->displayName = isset($attributes['display_name']) ? trim($attributes['display_name']) : '';
+		// if its set -and- in our valid 'enum' of types, use it, otherwise assume STRING, to try and emulate java's enum behavior
+		$preference->dataType = isset($attributes['datatype']) && in_array(strtoupper($attributes['datatype']), $preference->DataTypes) ? strtoupper($attributes['datatype']) : 'STRING';
+		$preference->defaultValue = isset($attributes['default_value']) ? trim($attributes['default_value']) : '';
+		if (isset($pref->EnumValue)) {
+			foreach ( $pref->EnumValue as $enum ) {
+				$attr = $enum->attributes();
+				// java based shindig doesn't throw an exception here, but it -is- invalid and should trigger a parse error
+				if (empty($attr['value'])) {
+					throw new SpecParserException("EnumValue must have a value field.");
+				}
+				$valueText = trim($attr['value']);
+				$displayText = ! empty($attr['display_value']) ? trim($attr['display_value']) : $valueText;
+				$preference->enumValues[$valueText] = $displayText;
+			}
+		}
+		$spec->userPrefs[] = $preference;
+	}
+	
+	private function processContent(&$spec, $content)
+	{
+		$attributes = $content->attributes();
+		if (empty($attributes['type'])) {
+			throw new SpecParserException("No content type specified!");
+		}
+		$type = trim($attributes['type']);
+		if ($type == 'url') {
+			if (empty($attributes['href'])) {
+				throw new SpecParserException("Malformed <Content> href value");
+			}
+			$url = trim($attributes['href']);
+			$spec->contentType = 'URL';
+			$spec->contentHref = $url;
+		} else {
+			$spec->contentType = 'HTML';
+			$html = (string)$content; // no trim here since empty lines can have structural meaning, so typecast to string instead
+			$view = isset($attributes['view']) ? trim($attributes['view']) : '';
+			$views = explode(',', $view);
+			foreach ( $views as $view ) {
+				$spec->addContent($view, $html);
+			}
+		}
+	}
+	
+	private function processFeature(&$spec, $feature, $required)
+	{
+		$featureSpec = new ParsedFeatureSpec();
+		$attributes = $feature->attributes();
+		if (empty($attributes['feature'])) {
+			throw new SpecParserException("Feature not specified in <" . (required ? "Required" : "Optional") . "> tag");
+		}
+		$featureSpec->name = trim($attributes['feature']);
+		$featureSpec->optional = ! $required;
+		foreach ( $feature->Param as $param ) {
+			$attr = $param->attributes();
+			if (empty($attr['name'])) {
+				throw new SpecParserException("Missing name attribute in <Param>.");
+			}
+			$name = trim($attr['name']);
+			$value = trim($param);
+			$featureSpec->params[$name] = $value;
+		}
+		$spec->requires[$featureSpec->name] = $featureSpec;
+	}
+}
+
+class ParsedIcon extends Icon {
+	public $mode;
+	public $url;
+	public $type;
+	
+	public function getMode()
+	{
+		return $this->mode;
+	}
+	
+	public function getURI()
+	{
+		return $this->url;
+	}
+	
+	public function getType()
+	{
+		return $this->type;
+	}
+}
+
+class ParsedFeatureSpec extends FeatureSpec {
+	public $name;
+	public $params = array();
+	public $optional;
+	
+	public function getName()
+	{
+		return $this->name;
+	}
+	
+	public function getParams()
+	{
+		return $this->params;
+	}
+	
+	public function isOptional()
+	{
+		return $this->optional;
+	}
+}
+
+class ParsedUserPref extends UserPref {
+	public $name;
+	public $displayName;
+	public $defaultValue;
+	public $required;
+	public $enumValues;
+	public $contentType;
+	
+	public function getName()
+	{
+		return $this->name;
+	}
+	
+	public function getDisplayName()
+	{
+		return $this->displayName;
+	}
+	
+	public function getDefaultValue()
+	{
+		return $this->defaultValue;
+	}
+	
+	public function isRequired()
+	{
+		return $this->required;
+	}
+	
+	public function getDataType()
+	{
+		return $this->dataType;
+	}
+	
+	public function getEnumValues()
+	{
+		return $this->enumValues;
+	}
+}
+
+class ParsedMessageBundle extends LocaleSpec {
+	public $url;
+	public $locale;
+	public $rightToLeft;
+	
+	public function getURI()
+	{
+		return $this->url;
+	}
+	
+	public function getLocale()
+	{
+		return $this->locale;
+	}
+	
+	public function isRightToLeft()
+	{
+		return $this->rightToLeft;
+	}
+}
+
+class ParsedGadgetSpec extends GadgetSpec {
+	public $id;
+	public $author;
+	public $authorEmail;
+	public $description;
+	public $directoryTitle;
+	public $contentType;
+	public $contentHref;
+	public $contentData = array();
+	public $icons = array();
+	public $localeSpecs = array();
+	public $preloads = array();
+	public $requires = array();
+	public $screenshot;
+	public $thumbnail;
+	public $title;
+	public $titleUrl;
+	public $userPrefs = array();
+	
+	public function addContent($view, $data)
+	{
+		if (empty($view)) {
+			$view = DEFAULT_VIEW;
+		}
+		if (! isset($this->contentData[$view])) {
+			$this->contentData[$view] = '';
+		}
+		$this->contentData[$view] .= $data;
+	}
+	
+	public function getAuthor()
+	{
+		return $this->author;
+	}
+	
+	public function getAuthorEmail()
+	{
+		return $this->authorEmail;
+	}
+	
+	public function getContentData($view = false)
+	{
+		if ($this->contentType != 'HTML') {
+			throw new SpecParserException("getContentData() requires contentType HTML");
+		}
+		if (empty($view) || !$view) {
+			$view = DEFAULT_VIEW;
+		}
+		return isset($this->contentData[$view]) ? trim($this->contentData[$view]) : '';
+	}
+	
+	public function getContentHref()
+	{
+		return $this->getContentType() == 'URL' ? $this->contentHref : null;
+	}
+	
+	public function getContentType()
+	{
+		return $this->contentType;
+	}
+	
+	public function getDescription()
+	{
+		return $this->description;
+	}
+	
+	public function getDirectoryTitle()
+	{
+		return $this->directoryTitle;
+	}
+	
+	public function getIcons()
+	{
+		return $this->icons;
+	}
+	
+	public function getLocaleSpecs()
+	{
+		return $this->localeSpecs;
+	}
+	
+	public function getPreloads()
+	{
+		return $this->preloads;
+	}
+	
+	public function getRequires()
+	{
+		return $this->requires;
+	}
+	
+	public function getScreenshot()
+	{
+		return $this->screenshot;
+	}
+	
+	public function getThumbnail()
+	{
+		return $this->thumbnail;
+	}
+	
+	public function getTitle()
+	{
+		return $this->title;
+	}
+	
+	public function getTitleURI()
+	{
+		return $this->titleUrl;
+	}
+	
+	public function getUserPrefs()
+	{
+		return $this->userPrefs;
+	}
+}
+

Added: incubator/shindig/trunk/php/gadgets/src/GadgetToken.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/gadgets/src/GadgetToken.php?rev=633998&view=auto
==============================================================================
--- incubator/shindig/trunk/php/gadgets/src/GadgetToken.php (added)
+++ incubator/shindig/trunk/php/gadgets/src/GadgetToken.php Wed Mar  5 11:43:14 2008
@@ -0,0 +1,46 @@
+<?
+/*
+ * 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.
+ * 
+ */
+
+/**
+ * An abstract representation of a signing token.
+ * Use in conjuction with @code GadgetSigner.
+ */
+
+abstract class GadgetToken {
+	
+	/**
+	 * Serializes the token into a string. This can be the exact same as
+	 * toString; using a different name here is only to force interface
+	 * compliance.
+	 *
+	 * @return A string representation of the token.
+	 */
+	abstract public function toSerialForm();
+	
+	/**
+	 * Sign a URL using this token
+	 * @param uri The URL to sign
+	 * @param httpMethod The HTTP method used
+	 * @param parameters associated with the signing request
+	 * @return The signed URL
+	 * @throws GadgetException
+	 */
+	abstract public function signUrl($uri, $httpMethod);
+}
\ No newline at end of file

Added: incubator/shindig/trunk/php/gadgets/src/GadgetView.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/gadgets/src/GadgetView.php?rev=633998&view=auto
==============================================================================
--- incubator/shindig/trunk/php/gadgets/src/GadgetView.php (added)
+++ incubator/shindig/trunk/php/gadgets/src/GadgetView.php Wed Mar  5 11:43:14 2008
@@ -0,0 +1,26 @@
+<?
+/*
+ * 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.
+ * 
+ */
+
+abstract class GadgetView extends GadgetSpec {
+	public $id; // ID
+	abstract public function getId();
+	abstract public function getSubstitutions();
+	abstract public function getCurrentMessageBundle();
+}
\ No newline at end of file

Added: incubator/shindig/trunk/php/gadgets/src/GadgetViewID.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/gadgets/src/GadgetViewID.php?rev=633998&view=auto
==============================================================================
--- incubator/shindig/trunk/php/gadgets/src/GadgetViewID.php (added)
+++ incubator/shindig/trunk/php/gadgets/src/GadgetViewID.php Wed Mar  5 11:43:14 2008
@@ -0,0 +1,25 @@
+<?
+/*
+ * 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.
+ * 
+ */
+
+abstract class GadgetViewID {
+	abstract public function getURI();
+	abstract public function getModuleId();
+	abstract public function getKey();
+}

Added: incubator/shindig/trunk/php/gadgets/src/HttpProcessingOptions.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/gadgets/src/HttpProcessingOptions.php?rev=633998&view=auto
==============================================================================
--- incubator/shindig/trunk/php/gadgets/src/HttpProcessingOptions.php (added)
+++ incubator/shindig/trunk/php/gadgets/src/HttpProcessingOptions.php Wed Mar  5 11:43:14 2008
@@ -0,0 +1,27 @@
+<?
+/*
+ * 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.
+ * 
+ */
+
+class HttpProcessingOptions extends ProcessingOptions {
+	public function __construct()
+	{
+		$this->ignoreCache = (isset($_GET['nocache']) && intval($_GET['nocache']) == 1) || (isset($_GET['bpc']) && intval($_GET['bpc']) == 1);
+		$this->focedJsLibs = isset($_GET['libs']) ? trim($_GET['libs']) : null;
+	}
+}
\ No newline at end of file

Added: incubator/shindig/trunk/php/gadgets/src/JsFeatureLoader.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/gadgets/src/JsFeatureLoader.php?rev=633998&view=auto
==============================================================================
--- incubator/shindig/trunk/php/gadgets/src/JsFeatureLoader.php (added)
+++ incubator/shindig/trunk/php/gadgets/src/JsFeatureLoader.php Wed Mar  5 11:43:14 2008
@@ -0,0 +1,159 @@
+<?
+/*
+ * 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.
+ * 
+ */
+
+class JsFeatureLoader {
+	
+	public function loadFeatures($path, $registry)
+	{
+		global $config;
+		$registered = array();
+		$entries = array(); // list of GadgetFeatureEntry's
+		$deps = array();
+		// Profiling revealed that we spend ~50% of our processing time in loadFiles,
+		// so the caching of the dep loading just about doubles our performance :)
+		// Only downside is that if you modify the features directory, your have to clean the cache too
+		$cache = new $config['data_cache']();
+		if (($deps = $cache->get(md5($path))) === false) {
+			$deps = $this->loadFiles($path, $deps);
+			$cache->set(md5($path), $deps);
+		}
+		// This ensures that we register everything in the right order.
+		foreach ( $deps as $entry ) {
+			$feature = $entry;
+			$feat = $this->register($registry, $feature, $registered, $deps);
+			if ($feat != null) {
+				$entries[] = $feat;
+			}
+		}
+		return $entries;
+	}
+	
+	private function loadFiles($path, &$features)
+	{
+		if (is_dir($path)) {
+			$dh = @opendir($path);
+			while ( ($file = @readdir($dh)) !== false ) {
+				// prevents us from looping over '.', '..' and 'hidden files', this last bit IS 
+				// different from the java version but unix standard really..
+				if (substr($file, 0, 1) != '.') {
+					$features = $this->loadFiles($path.'/'.$file, $features);
+				}
+			}
+			@closedir($dh);
+		} else {
+			if (basename($path) == 'feature.xml') {
+				$feature = $this->processFile($path);
+				if ($feature != null) {
+					$features[$feature->name] = $feature;
+				}
+			}
+		}
+		return $features;
+	}
+	
+	private function processFile($file)
+	{
+		$feature = null;
+		if (file_exists($file) && is_file($file) && is_readable($file)) {
+			if (($content = @file_get_contents($file))) {
+				$feature = $this->parse($content, dirname($file) . '/');
+			}
+		}
+		return $feature;
+	}
+	
+	private function register(&$registry, $feature, $registered, $all)
+	{
+		if (isset($registered[$feature->name])) {
+			return null;
+		}
+		foreach ( $feature->deps as $dep ) {
+			if (isset($all[$dep]) && ! in_array($dep, $registered)) {
+				$this->register($registry, $all[$dep], $registered, $all);
+			}
+		}
+		$factory = new JsLibraryFeatureFactory($feature->gadgetJs, $feature->containerJs);
+		$registered[] = $feature->name;
+		return $registry->register($feature->name, $feature->deps, $factory);
+	}
+	
+	private function parse($content, $path)
+	{
+		$doc = simplexml_load_string($content);
+		$feature = new ParsedFeature();
+		$feature->basePath = $path;
+		if (! isset($doc->name)) {
+			throw new GadgetException('Invalid name in feature: ' . $path);
+		}
+		$feature->name = trim($doc->name);
+		
+		foreach ( $doc->gadget as $gadget ) {
+			$feature = $this->processContext($feature, $gadget, false);
+		}
+		foreach ( $doc->container as $container ) {
+			$feature = $this->processContext($feature, $container, true);
+		}
+		foreach ( $doc->dependency as $dependency ) {
+			$feature->deps[] = trim($dependency);
+		}
+		return $feature;
+	}
+	
+	private function processContext(&$feature, $context, $isContainer)
+	{
+		foreach ( $context->script as $script ) {
+			$attributes = $script->attributes();
+			if (! isset($attributes['src'])) {
+				// inline content
+				$type = 'INLINE';
+				$content = (string)$script;
+			
+			} else {
+				$content = trim($attributes['src']);
+				if (strtolower(substr($content, 0, strlen("http://"))) == "http://") {
+					$type = 'URL';
+				} elseif (strtolower(substr($content, 0, strlen("//"))) == "//") {
+					$type = 'URL';
+				} else {
+					// as before, we skip over the resource parts since we dont support them
+					$type = 'FILE';
+					$content = $feature->basePath.'/'.$content;
+				}
+			}
+			$library = jsLibrary::create($type, $content);
+			if ($library != null) {
+				if ($isContainer) {
+					$feature->containerJs[] = $library;
+				} else {
+					$feature->gadgetJs[] = $library;
+				}
+			}
+		}
+		return $feature;
+	}
+}
+
+class ParsedFeature {
+	public $name = "";
+	public $basePath = "";
+	public $containerJs = array();
+	public $gadgetJs = array();
+	public $deps = array();
+}

Added: incubator/shindig/trunk/php/gadgets/src/JsLibrary.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/gadgets/src/JsLibrary.php?rev=633998&view=auto
==============================================================================
--- incubator/shindig/trunk/php/gadgets/src/JsLibrary.php (added)
+++ incubator/shindig/trunk/php/gadgets/src/JsLibrary.php Wed Mar  5 11:43:14 2008
@@ -0,0 +1,87 @@
+<?
+/*
+ * 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.
+ * 
+ */
+
+class jsLibrary {
+	private $types = array('FILE', 'RESOURCE', 'URL', 'INLINE');
+	private $type;
+	private $content;
+	
+	public function __construct($type, $content)
+	{
+		$this->type = $type;
+		$this->content = $content;
+	}
+	
+	public function getType()
+	{
+		return $this->type;
+	}
+	
+	public function getContent()
+	{
+		return $this->content;
+	}
+	
+	public function toString()
+	{
+		if ($this->type == 'URL') {
+			return "<script src=\"" . $this->content . "\"></script>";
+		} else {
+			return "<script><!--\n" . $this->content . "\n--></script>";
+		}
+	}
+	
+	static function create($type, $content)
+	{
+		if ($type == 'FILE' || $type == 'RESOURCE') {
+			$content = jsLibrary::loadData($content, $type);
+		}
+		return new JsLibrary($type, $content);
+	}
+	
+	static private function loadData($name, $type)
+	{
+		// we don't really do 'resources', so limiting this to files only
+		if ($type == 'FILE') {
+			return jsLibrary::loadFile($name);
+		}
+		return null;
+	}
+	
+	static private function loadFile($fileName)
+	{
+		if (empty($fileName)) {
+			return '';
+		}
+		if (!file_exists($fileName)) {
+			throw new Exception("JsLibrary file missing: $fileName");
+		}
+		if (!is_file($fileName)) {
+			throw new Exception("JsLibrary file is not a file: $fileName");
+		}
+		if (!is_readable($fileName)) {
+			throw new Exception("JsLibrary file not readable: $fileName");
+		}
+		if (!($content = @file_get_contents($fileName))) {
+			throw new Exception("jsLibrary error reading file: $fileName");
+		}
+		return $content;
+	}
+}
\ No newline at end of file