You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@shindig.apache.org by ch...@apache.org on 2008/05/01 14:36:33 UTC

svn commit: r652498 [2/4] - in /incubator/shindig/trunk/php: ./ gadgets/ src/ src/common/ src/gadgets/ src/gadgets/http/ src/gadgets/samplecontainer/ src/socialdata/ src/socialdata/http/ src/socialdata/opensocial/ src/socialdata/opensocial/model/ src/s...

Added: incubator/shindig/trunk/php/src/gadgets/GadgetSpecParser.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/GadgetSpecParser.php?rev=652498&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/GadgetSpecParser.php (added)
+++ incubator/shindig/trunk/php/src/gadgets/GadgetSpecParser.php Thu May  1 05:36:31 2008
@@ -0,0 +1,188 @@
+<?php
+/*
+ * 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 SpecParserException extends Exception {
+}
+
+class GadgetSpecParser {
+	
+	//public function parse(GadgetId $id, String $xml)
+	public function parse($xml, $context)
+	{
+		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>");
+		}
+		$gadget = new Gadget($context->getGadgetId(), $context);
+		// process ModulePref attributes
+		$this->processModulePrefs($gadget, $doc->ModulePrefs);
+		// process UserPrefs, if any
+		foreach ( $doc->UserPref as $pref ) {
+			$this->processUserPref($gadget, $pref);
+		}
+		foreach ( $doc->Content as $content ) {
+			$this->processContent($gadget, $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($gadget, $feature, true);
+		}
+		foreach ( $doc->ModulePrefs->Optional as $feature ) {
+			$this->processFeature($gadget, $feature, false);
+		}
+		//TODO java version has a todo here for parsing icons
+		return $gadget;
+	}
+	
+	private function processModulePrefs(&$gadget, $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)
+		// See http://code.google.com/apis/gadgets/docs/gadgets-extended-xsd.html 
+		$gadget->title = trim($attributes['title']);
+		$gadget->author = isset($attributes['author']) ? trim($attributes['author']) : '';
+		$gadget->authorEmail = isset($attributes['author_email']) ? trim($attributes['author_email']) : '';
+		$gadget->description = isset($attributes['description']) ? trim($attributes['description']) : '';
+		$gadget->directoryTitle = isset($attributes['directory_title']) ? trim($attributes['directory_title']) : '';
+		$gadget->screenshot = isset($attributes['screenshot']) ? trim($attributes['screenshot']) : '';
+		$gadget->thumbnail = isset($attributes['thumbnail']) ? trim($attributes['thumbnail']) : '';
+		$gadget->titleUrl = isset($attributes['title_url']) ? trim($attributes['title_url']) : '';
+		// Extended spec fields
+		$gadget->authorAffiliation = isset($attributes['author_affiliation']) ? trim($attributes['author_affiliation']) : '';
+		$gadget->authorLocation = isset($attributes['author_location']) ? trim($attributes['author_location']) : '';
+		$gadget->authorPhoto = isset($attributes['author_photo']) ? trim($attributes['author_photo']) : '';
+		$gadget->authorAboutMe = isset($attributes['author_aboutme']) ? trim($attributes['author_aboutme']) : '';
+		$gadget->authorQuote = isset($attributes['author_quote']) ? trim($attributes['author_quote']) : '';
+		$gadget->authorLink = isset($attributes['author_link']) ? trim($attributes['author_link']) : '';
+		$gadget->showStats = isset($attributes['show_stats']) ? trim($attributes['show_stats']) : '';
+		$gadget->showInDirectory = isset($attributes['show_in_directory']) ? trim($attributes['show_in_directory']) : '';
+		$gadget->string = isset($attributes['string']) ? trim($attributes['string']) : '';
+		$gadget->width = isset($attributes['width']) ? trim($attributes['width']) : '';
+		$gadget->height = isset($attributes['height']) ? trim($attributes['height']) : '';
+		$gadget->category = isset($attributes['category']) ? trim($attributes['category']) : '';
+		$gadget->category2 = isset($attributes['category2']) ? trim($attributes['category2']) : '';
+		$gadget->singleton = isset($attributes['singleton']) ? trim($attributes['singleton']) : '';
+		$gadget->renderInline = isset($attributes['render_inline']) ? trim($attributes['render_inline']) : '';
+		$gadget->scaling = isset($attributes['scaling']) ? trim($attributes['scaling']) : '';
+		$gadget->scrolling = isset($attributes['scrolling']) ? trim($attributes['scrolling']) : '';
+		foreach ( $ModulePrefs->Locale as $locale ) {
+			$gadget->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 LocaleSpec();
+		$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(&$gadget, $pref)
+	{
+		$attributes = $pref->attributes();
+		$preference = new UserPref();
+		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;
+			}
+		}
+		$gadget->userPrefs[] = $preference;
+	}
+	
+	private function processContent(&$gadget, $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']);
+			$gadget->contentType = 'URL';
+			$gadget->contentHref = $url;
+		} else {
+			$gadget->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 ) {
+				$gadget->addContent($view, $html);
+			}
+		}
+	}
+	
+	private function processFeature(&$gadget, $feature, $required)
+	{
+		$featureSpec = new FeatureSpec();
+		$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;
+		}
+		$gadget->requires[$featureSpec->name] = $featureSpec;
+	}
+}

Added: incubator/shindig/trunk/php/src/gadgets/GadgetToken.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/GadgetToken.php?rev=652498&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/GadgetToken.php (added)
+++ incubator/shindig/trunk/php/src/gadgets/GadgetToken.php Thu May  1 05:36:31 2008
@@ -0,0 +1,70 @@
+<?php
+/*
+ * 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 conjunction with @code GadgetTokenDecoder.
+ */
+abstract class GadgetToken {
+
+  //FIXME Hmm seems php is refusing to let me make abstract static functions? odd
+  static public function createFromToken($token, $maxage) {}
+  static public function createFromValues($owner, $viewer, $app, $domain, $appUrl, $moduleId) {}
+  
+
+  /**
+   * 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();
+
+  /**
+   * @return the owner from the token, or null if there is none.
+   */
+  abstract public function getOwnerId();
+
+  /**
+   * @return the viewer from the token, or null if there is none.
+   */
+  abstract public function getViewerId();
+
+  /**
+   * @return the application id from the token, or null if there is none.
+   */
+  abstract public function getAppId();
+  
+  /**
+   * @return the domain from the token, or null if there is none.
+   */
+  abstract public function getDomain();
+
+  /**
+   * @return the URL of the application
+   */
+  abstract public function getAppUrl();
+
+  /**
+   * @return the module ID of the application
+   */
+  abstract public function getModuleId();
+}

Added: incubator/shindig/trunk/php/src/gadgets/GadgetTokenDecoder.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/GadgetTokenDecoder.php?rev=652498&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/GadgetTokenDecoder.php (added)
+++ incubator/shindig/trunk/php/src/gadgets/GadgetTokenDecoder.php Thu May  1 05:36:31 2008
@@ -0,0 +1,34 @@
+<?php
+/*
+ * 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 verification of gadget security tokens.
+ */
+abstract class GadgetTokenDecoder {
+
+  /**
+   * Decrypts and verifies a gadget security token to return a gadget token.
+   * 
+   * @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($tokenString);
+}

Added: incubator/shindig/trunk/php/src/gadgets/JsFeatureLoader.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/JsFeatureLoader.php?rev=652498&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/JsFeatureLoader.php (added)
+++ incubator/shindig/trunk/php/src/gadgets/JsFeatureLoader.php Thu May  1 05:36:31 2008
@@ -0,0 +1,154 @@
+<?php
+/*
+ * 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 {
+	private $debug;
+	
+	public function __construct($debug)
+	{
+		$this->debug = $debug;
+	}
+	
+	public function loadFeatures($path, $registry)
+	{
+		$registered = array();
+		$entries = array();
+		$deps = array();
+		$deps = $this->loadFiles($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)) {
+			foreach ( glob("$path/*") as $file ) {
+				// prevents us from looping over '.', '..' and 'hidden files', this last bit IS 
+				// different from the java version but it's the unix standard really..
+				if (substr(basename($file), 0, 1) != '.') {
+					$features = $this->loadFiles($file, $features);
+				}
+			}
+		} 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, $this->debug);
+			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/src/gadgets/JsLibrary.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/JsLibrary.php?rev=652498&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/JsLibrary.php (added)
+++ incubator/shindig/trunk/php/src/gadgets/JsLibrary.php Thu May  1 05:36:31 2008
@@ -0,0 +1,119 @@
+<?php
+/*
+ * 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;
+	private $featureName; // used to track what feature this belongs to
+	
+
+	public function __construct($type, $content, $featureName = '')
+	{
+		$this->featureName = $featureName;
+		$this->type = $type;
+		$this->content = $content;
+	}
+	
+	public function getType()
+	{
+		return $this->type;
+	}
+	
+	public function getContent()
+	{
+		return $this->content;
+	}
+	
+	public function getFeatureName()
+	{
+		return $this->featureName;
+	}
+	
+	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, $debug)
+	{
+		$feature = '';
+		if ($type == 'FILE') {
+			$feature = dirname($content);
+			if (substr($feature, strlen($feature) - 1, 1) == '/') {
+				// strip tailing /, if any, so that the following strrpos works in any situation
+				$feature = substr($feature, 0, strlen($feature) - 1);
+			}
+			$feature = substr($feature, strrpos($feature, '/') + 1);
+			$content = JsLibrary::loadData($content, $type, $debug);
+		}
+		return new JsLibrary($type, $content, $feature);
+	}
+	
+	static private function loadData($name, $type, $debug)
+	{
+		// we don't really do 'resources', so limiting this to files only
+		if ($type == 'FILE') {
+			return JsLibrary::loadFile($name, $debug);
+		}
+		return null;
+	}
+	
+	static private function loadFile($fileName, $debug)
+	{
+		global $config;
+		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 ($debug || empty($config['compress_command'])) {
+			if (! ($content = @file_get_contents($fileName))) {
+				throw new Exception("JsLibrary error reading file: $fileName");
+			}
+		} else {
+			// attempt to compress the feature javascript file
+			$input_file = escapeshellarg($fileName);
+			$output_file = tempnam($config['cache_root'], 'js-comp');
+			$cmd = sprintf($config['compress_command'], $input_file, $output_file);
+			$null = $status = 0;
+			exec($cmd, $null, $status);
+			if ($status === 0) {
+				// successfully compressed.
+				$content = file_get_contents($output_file);
+			} else {
+				// if the compress_command isn't functioning, just return the plain uncompressed content
+				$content = @file_get_contents($fileName);
+			}
+		}
+		return $content;
+	}
+}
\ No newline at end of file

Added: incubator/shindig/trunk/php/src/gadgets/JsLibraryFeatureFactory.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/JsLibraryFeatureFactory.php?rev=652498&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/JsLibraryFeatureFactory.php (added)
+++ incubator/shindig/trunk/php/src/gadgets/JsLibraryFeatureFactory.php Thu May  1 05:36:31 2008
@@ -0,0 +1,73 @@
+<?php
+/*
+ * 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 JsLibraryFeatureFactory extends GadgetFeatureFactory {
+	private $JsLibraryFeature;
+	
+	public function __construct($gadgetLibraries, $containerLibraries)
+	{
+		// since we don't do strict type checking, this is one constructor for both a class, or a array of classes
+		$this->JsLibraryFeature = new JsLibraryFeature($gadgetLibraries, $containerLibraries);
+	}
+	
+	public function create()
+	{
+		return $this->JsLibraryFeature;
+	}
+	
+	public function getLibraries($context)
+	{
+		return $context == 'GADGET' ? $this->JsLibraryFeature->gadgetLibraries : $this->JsLibraryFeature->containerLibraries;
+	}
+}
+
+class JsLibraryFeature extends GadgetFeature {
+	public $containerLibraries = array();
+	public $gadgetLibraries = array();
+	
+	public function __construct($gadgetLibraries, $containerLibraries)
+	{
+		// we have a single constructor for both a single and multiple libraries, so handle it in code instead
+		if ($gadgetLibraries != null && is_array($gadgetLibraries)) {
+			$this->gadgetLibraries = array_merge($this->gadgetLibraries, $gadgetLibraries);
+		} elseif ($gadgetLibraries != null && $gadgetLibraries instanceof JsLibrary) {
+			$this->gadgetLibraries[] = $gadgetLibraries;
+		}
+		if ($containerLibraries != null && is_array($containerLibraries)) {
+			$this->containerLibraries = array_merge($this->containerLibraries, $containerLibraries);
+		} elseif ($containerLibraries != null && $containerLibraries instanceof JsLibrary) {
+			$this->containerLibraries[] = $containerLibraries;
+		}
+	}
+	
+	public function prepare($gadget, $context, $params)
+	{
+		// do nothing
+	}
+	
+	public function process($gadget, $context, $params)
+	{
+		$libraries = array();
+		$libraries = $context->getRenderingContext() == 'GADGET' ? $this->gadgetLibraries : $this->containerLibraries;
+		foreach ( $libraries as $library ) {
+			$gadget->addJsLibrary($library);
+		}
+	}
+}
\ No newline at end of file

Added: incubator/shindig/trunk/php/src/gadgets/JsonRpcGadgetContext.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/JsonRpcGadgetContext.php?rev=652498&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/JsonRpcGadgetContext.php (added)
+++ incubator/shindig/trunk/php/src/gadgets/JsonRpcGadgetContext.php Thu May  1 05:36:31 2008
@@ -0,0 +1,37 @@
+<?php
+
+class JsonRpcGadgetContext extends GadgetContext {
+	private $locale = null;
+	private $view = null;
+	private $url = null;
+	private $container = null;
+
+	public function __construct($jsonContext, $url)
+	{
+		parent::__construct('GADGET');
+		$this->url = $url;
+		$this->view = $jsonContext->view;
+		$this->locale = new Locale($jsonContext->language, $jsonContext->country);
+		$this->container = $jsonContext->container;
+	}
+	
+	public function getUrl()
+	{
+		return $this->url;
+	}
+	
+	public function getView()
+	{
+		return $this->view;
+	}
+	
+	public function getLocale()
+	{
+		return $this->locale;
+	}
+	
+	public function getContainer()
+	{
+		return $this->container;
+	}
+}
\ No newline at end of file

Added: incubator/shindig/trunk/php/src/gadgets/JsonRpcHandler.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/JsonRpcHandler.php?rev=652498&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/JsonRpcHandler.php (added)
+++ incubator/shindig/trunk/php/src/gadgets/JsonRpcHandler.php Thu May  1 05:36:31 2008
@@ -0,0 +1,76 @@
+<?php
+
+class JsonRpcHandler {
+	
+	public function process($requests)
+	{
+		$response = array();
+		foreach ( $requests->gadgets as $gadget ) {
+			try {
+				$gadgetUrl = $gadget->url;
+				$gadgetModuleId = $gadget->moduleId;
+				$context = new JsonRpcGadgetContext($requests->context, $gadgetUrl);
+				$gadgetServer = new GadgetServer();
+				$gadget = $gadgetServer->processGadget($context);
+				$response[] = $this->makeResponse($gadget, $gadgetModuleId, $gadgetUrl);
+			} catch ( Exception $e ) {
+				$response[] = array('errors' => array($e->getMessage()), 'moduleId' => $gadgetModuleId, 'url' => $gadgetUrl);
+			}
+		}
+		return $response;
+	}
+	
+	private function makeResponse($gadget, $gadgetModuleId, $gadgetUrl)
+	{
+		$response = array();
+		$prefs = array();
+		foreach ($gadget->getUserPrefs() as $pref) {
+			$prefs[$pref->getName()] = array(
+				'displayName' => $pref->getDisplayName(),
+				'type' => $pref->getDataType(),
+				'default' => $pref->getDefaultValue(),
+				'enumValues' => $pref->getEnumValues()
+			);
+		}
+		$features = array();
+		foreach ($gadget->getRequires() as $feature) {
+			$features[] = $feature->getName();
+		}
+		
+		$views = array();
+		
+		//TODO add views and actual iframe url
+		$response['showInDirectory'] = $gadget->getShowInDirectory();
+		$response['width'] = $gadget->getWidth();
+		$response['title'] = $gadget->getTitle();
+		$response['singleton'] = $gadget->getSingleton();
+		$response['categories'] = Array($gadget->getCategory(), $gadget->getCategory2());
+        $response['views'] ='';
+        /*stdClass Object
+                       (
+                            $response['default'] = stdClass Object
+                                (
+                                    $response['type'] = html
+                                    $response['quirks'] = 1
+                                )
+
+                        )*/
+       	$response['description'] = $gadget->getDescription();
+		$response['screenshot'] = $gadget->getScreenShot();
+		$response['thumbnail'] = $gadget->getThumbnail();
+		$response['height'] = $gadget->getHeight();
+		$response['scaling'] = $gadget->getScaling();
+		$response['moduleId'] = $gadgetModuleId;
+		$response['features'] = $features;
+		$response['showStats'] = $gadget->getShowStats();
+		$response['scrolling'] = $gadget->getScrolling();
+		$response['url'] = $gadgetUrl;
+		$response['authorEmail'] = $gadget->getAuthorEmail();
+		$response['titleUrl'] = $gadget->getTitleUrl();
+		$response['directoryTitle'] = $gadget->getDirectoryTitle();
+		$response['author'] = $gadget->getAuthor();
+		$response['iframeUrl'] = "/gadgets/ifr?url=http%3A%2F%2Fwww.google.com%2Fig%2Fmodules%2Fhello.xml&container=default&mid=1&v=db18c863f15d5d1e758a91f2a44881b4&lang=en&country=US";
+		$response['userPrefs'] = $prefs;
+		return $response;
+	}
+}
\ No newline at end of file

Added: incubator/shindig/trunk/php/src/gadgets/LocaleSpec.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/LocaleSpec.php?rev=652498&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/LocaleSpec.php (added)
+++ incubator/shindig/trunk/php/src/gadgets/LocaleSpec.php Thu May  1 05:36:31 2008
@@ -0,0 +1,40 @@
+<?php
+/*
+ * 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 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;
+	}
+}

Added: incubator/shindig/trunk/php/src/gadgets/MessageBundle.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/MessageBundle.php?rev=652498&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/MessageBundle.php (added)
+++ incubator/shindig/trunk/php/src/gadgets/MessageBundle.php Thu May  1 05:36:31 2008
@@ -0,0 +1,33 @@
+<?php
+/*
+ * 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 MessageBundle {
+	private $messages;
+	
+	public function __construct($messages = array())
+	{
+		$this->messages = $messages;
+	}
+	
+	public function getMessages()
+	{
+		return $this->messages;
+	}
+}
\ No newline at end of file

Added: incubator/shindig/trunk/php/src/gadgets/MessageBundleParser.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/MessageBundleParser.php?rev=652498&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/MessageBundleParser.php (added)
+++ incubator/shindig/trunk/php/src/gadgets/MessageBundleParser.php Thu May  1 05:36:31 2008
@@ -0,0 +1,40 @@
+<?php
+/*
+ * 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 MessageBundleParser {
+	
+	private function processMessage(&$messages, $msg)
+	{
+		$attr = $msg->attributes();
+		if (isset($attr['name'])) {
+			$messages[trim($attr['name'])] = trim($msg);
+		}
+	}
+	
+	public function parse($xml)
+	{
+		$doc = simplexml_load_string($xml);
+		$messages = array();
+		foreach ( $doc->msg as $msg ) {
+			$this->processMessage($messages, $msg);
+		}
+		return new MessageBundle($messages);
+	}
+}
\ No newline at end of file

Added: incubator/shindig/trunk/php/src/gadgets/ProxyHandler.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/ProxyHandler.php?rev=652498&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/ProxyHandler.php (added)
+++ incubator/shindig/trunk/php/src/gadgets/ProxyHandler.php Thu May  1 05:36:31 2008
@@ -0,0 +1,255 @@
+<?php
+/*
+ * 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.
+ * 
+ */
+
+// according to features/core/io.js, this is high on the list of things to scrap
+define('UNPARSEABLE_CRUFT', "throw 1; < don't be evil' >");
+
+/**
+ * The ProxyHandler class does the actual proxy'ing work. it deals both with
+ * GET and POST based input, and peforms a request based on the input, headers and 
+ * httpmethod params. It also deals with request signing and verification thru the
+ * authz and st (security token) params. 
+ *
+ */
+class ProxyHandler {
+	private $context;
+	
+	public function __construct($context)
+	{
+		$this->context = $context;
+	}
+	
+	/**
+	 * Fetches content and returns it in JSON format
+	 *
+	 * @param string $url the url to fetch
+	 * @param GadgetSigner $signer the request signer to use
+	 * @param string $method the http method to use (get or post) in making the request
+	 */
+	public function fetchJson($url, $signer, $method)
+	{
+		$token = $this->extractAndValidateToken($signer);
+		$originalUrl = $this->validateUrl($url);
+		$signedUrl = $this->signUrl($originalUrl, $token);
+		// Fetch the content and convert it into JSON.
+		// TODO: Fetcher needs to handle variety of HTTP methods.
+		$result = $this->fetchContent($signedUrl, $method);
+		$status = (int)$result->getHttpCode();
+		//header("HTTP/1.1 $status", true);
+		if ($status == 200) {
+			$output = '';
+			$json = array($url => array('body' => $result->getResponseContent(), 'rc' => $status));
+			$json = json_encode($json);
+			$output = UNPARSEABLE_CRUFT . $json;
+			$this->setCachingHeaders();
+			header("Content-Type: application/json; charset=utf-8", true);
+			echo $output;
+		} else {
+			@ob_end_clean();
+			header("HTTP/1.0 404 Not Found", true);
+			echo "<html><body><h1>404 - Not Found</h1></body></html>";
+		}
+		die();
+	}
+	
+	/**
+	 * Fetches the content and returns it as-is using the headers as returned
+	 * by the remote host.
+	 *
+	 * @param string $url the url to retrieve
+	 * @param GadgetSigner $signer the GadgetSigner to use
+	 * @param string $method either get or post
+	 */
+	public function fetch($url, $signer, $method)
+	{
+		$token = $this->extractAndValidateToken($signer);
+		$originalUrl = $this->validateUrl($url);
+		$signedUrl = $this->signUrl($originalUrl, $token);
+		//TODO: Fetcher needs to handle variety of HTTP methods.
+		$result = $this->fetchContent($signedUrl, $method);
+		// TODO: Fetcher needs to handle variety of HTTP methods.
+		$status = (int)$result->getHttpCode();
+		if ($status == 200) {
+			$headers = explode("\n", $result->getResponseHeaders());
+			foreach ( $headers as $header ) {
+				if (strpos($header, ':')) {
+					$key = trim(substr($header, 0, strpos($header, ':')));
+					$val = trim(substr($header, strpos($header, ':') + 1));
+					// filter out headers that would otherwise mess up our output
+					if (strcasecmp($key, "Transfer-Encoding") != 0 && strcasecmp($key, "Cache-Control") != 0 && strcasecmp($key, "Expires") != 0 && strcasecmp($key, "Content-Length") != 0) {
+						header("$key: $val");
+					}
+				}
+			}
+			$this->setCachingHeaders();
+			// then echo the content
+			echo $result->getResponseContent();
+		} else {
+			@ob_end_clean();
+			header("HTTP/1.0 404 Not Found", true);
+			echo "<html><body><h1>404 - Not Found ($status)</h1>";
+			echo "</body></html>";
+		}
+		// make sure the HttpServlet destructor doesn't override ours
+		die();
+	}
+	
+	/**
+	 * Both fetch and fetchJson call this function to retrieve the actual content
+	 *
+	 * @param string $signedUrl the signed url to fetch
+	 * @param string $method either get or post
+	 * @return the filled in request (RemoteContentRequest)
+	 */
+	private function fetchContent($signedUrl, $method)
+	{
+		//TODO get actual character encoding from the request
+		
+
+		// Extract the request headers from the $_SERVER super-global (this -does- unfortunatly mean that any header that php doesn't understand won't be proxied thru though)
+		// if this turns out to be a problem we could add support for HTTP_RAW_HEADERS, but this depends on a php.ini setting, so i'd rather prevent that from being required
+		$headers = '';
+		$context = new GadgetContext('GADGET');
+		$requestHeaders = $this->request_headers();
+		foreach ( $requestHeaders as $key => $val ) {
+			if ($key != 'Keep-alive' && $key != 'Connection' && $key != 'Host' && $key != 'Accept' && $key != 'Accept-Encoding') {
+				// propper curl header format according to http://www.php.net/manual/en/function.curl-setopt.php#80099
+				$headers .= "$key: $val\n";
+			}
+		}
+		if ($method == 'POST') {
+			$data = isset($_GET['postData']) ? $_GET['postData'] : false;
+			if (! $data) {
+				$data = isset($_POST['postData']) ? $_POST['postData'] : false;
+			}
+			$postData = '';
+			if ($data) {
+				$data = urldecode($data);
+				$entries = explode('&', $data);
+				foreach ( $entries as $entry ) {
+					$parts = explode('=', $entry);
+					// Process only if its a valid value=something pair
+					if (count($parts) == 2) {
+						$postData .= urlencode($parts[0]) . '=' . urlencode($parts[1]) . '&';
+					}
+				}
+				// chop of the trailing &
+				if (strlen($postData)) {
+					$postData = substr($postData, 0, strlen($postData) - 1);
+				}
+			}
+			// even if postData is an empty string, it will still post (since RemoteContentRquest checks if its false)
+			// so the request to POST is still honored
+			$request = new RemoteContentRequest($signedUrl, $headers, $postData);
+			$request = $this->context->getHttpFetcher()->fetch($request, $context);
+		} else {
+			$request = new RemoteContentRequest($signedUrl, $headers);
+			$request = $this->context->getHttpFetcher()->fetch($request, $context);
+		}
+		return $request;
+	}
+	
+	/**
+	 * Sets the caching headers (overwriting anything the remote host set) to force
+	 * the browser not to cache this. 
+	 *
+	 */
+	private function setCachingHeaders()
+	{
+		// TODO: Re-implement caching behavior if appropriate.
+		header("Cache-Control: private; max-age=0", true);
+		header("Expires: " . gmdate("D, d M Y H:i:s", time() - 3000) . " GMT", true);
+	}
+	
+	/**
+	 * Empty function, should make something practical here some day.
+	 * it's function should be to validate the given url if its in
+	 * correct http(s):port://location/url format
+	 *
+	 * @param string $url
+	 * @return string the 'validated' url
+	 */
+	private function validateUrl($url)
+	{
+		//TODO should really make a PHP version of the URI class and validate in all the locations the java version does
+		return $url;
+	}
+	
+	/**
+	 * Extracts the 'st' token from the GET or POST params and calls the
+	 * signer to validate the token
+	 *
+	 * @param GadgetSigner $signer the signer to use (configured in config.php)
+	 * @return string the token to use in the signed url
+	 */
+	private function extractAndValidateToken($signer)
+	{
+		if ($signer == null) {
+			return null;
+		}
+		$token = isset($_GET["st"]) ? $_GET["st"] : false;
+		if ($token) {
+			$token = isset($_POST['st']) ? $_POST['st'] : '';
+		}
+		return $signer->createToken($token);
+	}
+	
+	/**
+	 * Signs a url with the GadgetToken
+	 *
+	 * @param string $originalUrl
+	 * @param GadgetToken $token
+	 * @return unknown
+	 */
+	private function signUrl($originalUrl, $token)
+	{
+		$authz = isset($_GET['authz']) ? $_GET['authz'] : false;
+		if (! $authz) {
+			$authz = isset($_POST['authz']) ? $_POST['authz'] : '';
+		}
+		if ($token == null || $authz != 'signed') {
+			return $originalUrl;
+		}
+		$method = isset($_GET['httpMethod']) ? $_GET['httpMethod'] : false;
+		if ($method) {
+			$method = isset($_POST['httpMethod']) ? $_POST['httpMethod'] : 'GET';
+		}
+		return $token->signUrl($originalUrl, $method);
+	}
+	
+	private function request_headers()
+	{
+		// Try to use apache's request headers if available
+		if (function_exists("apache_request_headers")) {
+			if (($headers = apache_request_headers())) {
+				return $headers;
+			}
+		}
+		// if that failed, try to create them from the _SERVER superglobal
+		$headers = array();
+		foreach ( array_keys($_SERVER) as $skey ) {
+			if (substr($skey, 0, 5) == "HTTP_") {
+				$headername = str_replace(" ", "-", ucwords(strtolower(str_replace("_", " ", substr($skey, 0, 5)))));
+				$headers[$headername] = $_SERVER[$skey];
+			}
+		}
+		return $headers;
+	}
+}

Added: incubator/shindig/trunk/php/src/gadgets/Substitutions.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/Substitutions.php?rev=652498&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/Substitutions.php (added)
+++ incubator/shindig/trunk/php/src/gadgets/Substitutions.php Thu May  1 05:36:31 2008
@@ -0,0 +1,57 @@
+<?php
+/*
+ * 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 Substitutions {
+	private $types = array('MESSAGE' => 'MSG', 'BIDI' => 'BIDI', 'USER_PREF' => 'UP', 'MODULE' => 'MODULE');
+	
+	private $substitutions = array();
+	
+	public function __construct()
+	{
+		foreach ( $this->types as $type ) {
+			$this->substitutions[$type] = array();
+		}
+	}
+	
+	public function addSubstitution($type, $key, $value)
+	{
+		$this->substitutions[$type]["__{$type}_{$key}__"] = $value;
+	}
+	
+	public function addSubstitutions($type, $array)
+	{
+		foreach ( $array as $key => $value ) {
+			$this->addSubstitution($type, $key, $value);
+		}
+	}
+	
+	public function substitute($input)
+	{
+		foreach ( $this->types as $type ) {
+			$input = $this->substituteType($type, $input);
+		}
+		return $input;
+	}
+	
+	public function substituteType($type, $input)
+	{
+		return str_replace(array_keys($this->substitutions[$type]), array_values($this->substitutions[$type]), $input);
+	}
+}
\ No newline at end of file

Added: incubator/shindig/trunk/php/src/gadgets/UserPref.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/UserPref.php?rev=652498&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/UserPref.php (added)
+++ incubator/shindig/trunk/php/src/gadgets/UserPref.php Thu May  1 05:36:31 2008
@@ -0,0 +1,60 @@
+<?php
+/*
+ * 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 UserPref {
+	public $DataTypes = array('STRING', 'HIDDEN', 'BOOL', 'ENUM', 'LIST', 'NUMBER');
+	public $dataType;
+	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;
+	}
+}

Added: incubator/shindig/trunk/php/src/gadgets/UserPrefs.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/UserPrefs.php?rev=652498&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/UserPrefs.php (added)
+++ incubator/shindig/trunk/php/src/gadgets/UserPrefs.php Thu May  1 05:36:31 2008
@@ -0,0 +1,38 @@
+<?php
+/*
+ * 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 UserPrefs {
+	private $prefs = array();
+	
+	public function __construct($prefs)
+	{
+		$this->prefs = $prefs;
+	}
+	
+	public function getPrefs()
+	{
+		return $this->prefs;
+	}
+	
+	public function getPref($name)
+	{
+		return isset($this->prefs[$name]) ? $this->prefs[$name] : null;
+	}
+}
\ No newline at end of file

Added: incubator/shindig/trunk/php/src/gadgets/http/FilesServlet.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/http/FilesServlet.php?rev=652498&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/http/FilesServlet.php (added)
+++ incubator/shindig/trunk/php/src/gadgets/http/FilesServlet.php Thu May  1 05:36:31 2008
@@ -0,0 +1,68 @@
+<?php
+/*
+ * 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 serves files from the shindig_root/javascript directory, it was created
+ * so that the shindig examples and javascript files would work out of the box with
+ * the php version too
+ */
+class FilesServlet extends HttpServlet {
+	
+	/**
+	 * Handles the get file request, if the file exists and is in the correct
+	 * location it's echo'd to the browser (with a basic content type guessing
+	 * based on the file extention, ie .js becomes text/javascript).
+	 * If the file location falls outside of the shindig/javascript root a
+	 * 400 Bad Request is returned, and if the file is inside of the root
+	 * but doesn't exist a 404 error is returned
+	 */
+	public function doGet()
+	{
+		$file = str_replace(Config::get('web_prefix') . '/gadgets/files/', '', $_SERVER["REQUEST_URI"]);
+		$file = Config::get('javascript_path') . $file;
+		// make sure that the real path name is actually in the javascript_path, so people can't abuse this to read
+		// your private data from disk .. otherwise this would be a huge privacy and security issue 
+		if (substr(realpath($file), 0, strlen(realpath(Config::get('javascript_path')))) != realpath(Config::get('javascript_path'))) {
+			header("HTTP/1.0 400 Bad Request", true);
+			echo "<html><body><h1>400 - Bad Request</h1></body></html>";
+			die();
+		}
+		// if the file doesn't exist or can't be read, give a 404 error
+		if (!file_exists($file) || !is_readable($file) || !is_file($file)) {
+			header("HTTP/1.0 404 Not Found", true);
+			echo "<html><body><h1>404 - Not Found</h1></body></html>";
+			die();
+		}
+		$dot = strrpos($file, '.');
+		if ($dot) {
+			$ext = strtolower(substr($file, $dot+1));
+			if ($ext == 'html' || $ext == 'htm') {
+				$this->setContentType('text/html');
+			} elseif ($ext == 'js') {
+				$this->setContentType('text/javascript');
+			} elseif ($ext == 'css') {
+				$this->setContentType('text/css');
+			}
+		}
+		$this->setNoCache(true);
+		$this->setLastModified(filemtime($file));
+		readfile($file);
+	}
+}

Added: incubator/shindig/trunk/php/src/gadgets/http/GadgetRenderingServlet.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/http/GadgetRenderingServlet.php?rev=652498&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/http/GadgetRenderingServlet.php (added)
+++ incubator/shindig/trunk/php/src/gadgets/http/GadgetRenderingServlet.php Thu May  1 05:36:31 2008
@@ -0,0 +1,298 @@
+<?php
+/*
+ * 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 deals with the gadget rendering requests (in default config this
+ * would be /gadgets/ifr?url=<some gadget's url>). It uses the gadget server and
+ * gadget context to render the xml to a valid html file, and outputs it.
+ * 
+ */
+class GadgetRenderingServlet extends HttpServlet {
+	private $context;
+	
+	/**
+	 * Creates the gadget using the GadgetServer class and calls outputGadget
+	 *
+	 */
+	public function doGet()
+	{
+		try {
+			if (empty($_GET['url'])) {
+				throw new GadgetException("Missing required parameter: url");
+			}
+			// GadgetContext builds up all the contextual variables (based on the url or post) 
+			// plus instances all required classes (feature registry, fetcher, blacklist, etc)
+			$this->context = new GadgetContext('GADGET');
+			// Unfortunatly we can't do caja content filtering here, hoping we'll have a RPC service
+			// or command line caja to use for this at some point 
+			$gadgetServer = new GadgetServer();
+			$gadget = $gadgetServer->processGadget($this->context);
+			$this->outputGadget($gadget, $this->context);
+		} catch ( Exception $e ) {
+			$this->outputError($e);
+		}
+	}
+	
+	/**
+	 * If an error occured (Exception) this function echo's the Exception's message
+	 * and if the config['debug'] is true, shows the debug backtrace in a div
+	 *
+	 * @param Exception $e the exception to show
+	 */
+	private function outputError($e)
+	{
+		header("HTTP/1.0 400 Bad Request", true, 400);
+		echo "<html><body>";
+		echo "<h1>Error</h1>";
+		echo $e->getMessage();
+		if (Config::get('debug')) {
+			echo "<p><b>Debug backtrace</b></p><div style='overflow:auto; height:300px; border:1px solid #000000'><pre>";
+			print_r(debug_backtrace());
+			echo "</pre></div>>";
+		}
+		echo "</body></html>";
+	}
+	
+	/**
+	 * Takes the gadget to output, and depending on its content type calls either outputHtml-
+	 * or outputUrlGadget
+	 *
+	 * @param Gadget $gadget gadget to render
+	 * @param string $view the view to render (only valid with a html content type)
+	 */
+	private function outputGadget($gadget, $view)
+	{
+		switch ( $gadget->getContentType()) {
+			case 'HTML' :
+				$this->outputHtmlGadget($gadget, $view);
+				break;
+			case 'URL' :
+				$this->outputUrlGadget($gadget);
+				break;
+		}
+	}
+	
+	/**
+	 * Outputs a html content type gadget.
+	 * It creates a html page, with the javascripts from the features inline into the page, plus
+	 * calls to 'gadgets.config.init' with the container configuration (config/container.js) and
+	 * 'gadgets.Prefs.setMessages_' with all the substitutions. For external javascripts it adds
+	 * a <script> tag.
+	 *
+	 * @param Gadget $gadget
+	 * @param GadgetContext $context
+	 */
+	private function outputHtmlGadget($gadget, $context)
+	{
+		$this->setContentType("text/html; charset=UTF-8");
+		$output = '';
+		$output .= "<html>\n<head>\n";
+		// TODO: This is so wrong. (todo copied from java shindig, but i would agree with it :))
+		$output .= "<style type=\"text/css\">body,td,div,span,p{font-family:arial,sans-serif;} a {color:#0000cc;}a:visited {color:#551a8b;}a:active {color:#ff0000;}body{margin: 0px;padding: 0px;background-color:white;}</style>\n";
+		$output .= "</head>\n<body>\n";
+		$externJs = "";
+		$inlineJs = "";
+		$externFmt = "<script src=\"%s\"></script>";
+		$forcedLibs = $context->getForcedJsLibs();
+		foreach ( $gadget->getJsLibraries() as $library ) {
+			$type = $library->getType();
+			if ($type == 'URL') {
+				// TODO: This case needs to be handled more gracefully by the js
+				// servlet. We should probably inline external JS as well.
+				$externJs .= sprintf($externFmt, $library->getContent()) . "\n";
+			} else if ($type == 'INLINE') {
+				$inlineJs .= $library->getContent() . "\n";
+			} else {
+				// FILE or RESOURCE
+				if ($forcedLibs == null) {
+					$inlineJs .= $library->getContent() . "\n";
+				}
+				// otherwise it was already included by config.forceJsLibs.
+			}
+		}
+		// Forced libs first.
+		if (! empty($forcedLibs)) {
+			$libs = explode(':', $forcedLibs);
+			$output .= sprintf($externFmt, $this->getJsUrl($libs)) . "\n";
+		}
+		if (strlen($inlineJs) > 0) {
+			$output .= "<script><!--\n" . $inlineJs . "\n-->\n</script>\n";
+		}
+		if (strlen($externJs) > 0) {
+			$output .= $externJs;
+		}
+		
+		$output .= "<script><!--\n";
+		$output .= $this->appendJsConfig($context, $gadget);
+		$output .= $this->appendMessages($gadget);
+		$output .= "-->\n</script>\n";
+		
+		$gadgetExceptions = array();
+		$content = $gadget->getContentData($context->getView());
+		if (empty($content)) {
+			// unknown view
+			$gadgetExceptions[] = "View: '" . $context->getView() . "' invalid for gadget: " . $gadget->getId()->getKey();
+		}
+		if (count($gadgetExceptions)) {
+			throw new GadgetException(print_r($gadgetExceptions, true));
+		}
+		$output .= $content . "\n";
+		$output .= "<script>gadgets.util.runOnLoadHandlers();</script>\n";
+		$output .= "</body>\n</html>";
+		if (Config::get('P3P') != '') {
+			header("P3P: ".Config::get('P3P'));
+		}
+		echo $output;
+	}
+	
+	/**
+	 * Output's a URL content type gadget, it adds libs=<list:of:js:libraries>.js and user preferences
+	 * to the href url, and redirects the browser to it
+	 *
+	 * @param Gadget $gadget
+	 */
+	private function outputUrlGadget($gadget)
+	{
+		// Preserve existing query string parameters.
+		$redirURI = $gadget->getContentHref();
+		$queryStr = strpos($redirURI, '?') !== false ? substr($redirURI, strpos($redirURI, '?')) : '';
+		$query = $queryStr;
+		// TODO: userprefs on the fragment rather than query string
+		$query .= $this->getPrefsQueryString($gadget->getUserPrefValues());
+		$libs = array();
+		$forcedLibs = Config::get('focedJsLibs');
+		if ($forcedLibs == null) {
+			$reqs = $gadget->getRequires();
+			foreach ( $reqs as $key => $val ) {
+				$libs[] = $key;
+			}
+		} else {
+			$libs = explode(':', $forcedLibs);
+		}
+		$query .= $this->appendLibsToQuery($libs, $gadget);
+		// code bugs out with me because of the invalid url syntax since we dont have a URI class to fix it for us
+		// this works around that
+		if (substr($query, 0, 1) == '&') {
+			$query = '?' . substr($query, 1);
+		}
+		$redirURI .= $query;
+		header('Location: ' . $redirURI);
+		die();
+	}
+	
+	/**
+	 * Returns the requested libs (from getjsUrl) with the libs_param_name prepended
+	 * ie: in libs=core:caja:etc.js format
+	 *
+	 * @param string $libs the libraries
+	 * @param Gadget $gadget
+	 * @return string the libs=... string to append to the redirection url
+	 */
+	private function appendLibsToQuery($libs, $gadget)
+	{
+		$ret = "&";
+		$ret .= Config::get('libs_param_name');
+		$ret .= "=";
+		$ret .= $this->getJsUrl($libs, $gadget);
+		return $ret;
+	}
+	
+	/**
+	 * Returns the user preferences in &up_<name>=<val> format
+	 *
+	 * @param array $libs array of features this gadget requires
+	 * @param Gadget $gadget
+	 * @return string the up_<name>=<val> string to use in the redirection url
+	 */
+	private function getPrefsQueryString($prefVals)
+	{
+		$ret = '';
+		foreach ( $prefVals->getPrefs() as $key => $val ) {
+			$ret .= '&';
+			$ret .= Config::get('userpref_param_prefix');
+			$ret .= urlencode($key);
+			$ret .= '=';
+			$ret .= urlencode($val);
+		}
+		return $ret;
+	}
+	
+	/**
+	 * generates the library string (core:caja:etc.js) including a checksum of all the
+	 * javascript content (?v=<sha1 of js) for cache busting
+	 *
+	 * @param string $libs
+	 * @param Gadget $gadget
+	 * @return string the list of libraries in core:caja:etc.js?v=checksum> format
+	 */
+	private function getJsUrl($libs, $gadget)
+	{
+		$buf = '';
+		if (! is_array($libs) || ! count($libs)) {
+			$buf = 'core';
+		} else {
+			$firstDone = false;
+			foreach ( $libs as $lib ) {
+				if ($firstDone) {
+					$buf .= ':';
+				} else {
+					$firstDone = true;
+				}
+				$buf .= $lib;
+			}
+		}
+		// Build a version string from the sha1() checksum of all included javascript
+		// to ensure the client always has the right version
+		$inlineJs = '';
+		foreach ( $gadget->getJsLibraries() as $library ) {
+			$type = $library->getType();
+			if ($type != 'URL') {
+				$inlineJs .= $library->getContent() . "\n";
+			}
+		}
+		$buf .= ".js?v=" . sha1($inlineJs);
+		return $buf;
+	}
+	
+	private function appendJsConfig($context, $gadget)
+	{
+		$container = $context->getContainer();
+		$containerConfig = $context->getContainerConfig();
+		$gadgetConfig = array();
+		$featureConfig = $containerConfig->getConfig($container, 'gadgets.features');
+		foreach ( $gadget->getJsLibraries() as $library ) {
+			$feature = $library->getFeatureName();
+			if (! isset($gadgetConfig[$feature]) && ! empty($featureConfig[$feature])) {
+				$gadgetConfig[$feature] = $featureConfig[$feature];
+			}
+		}
+		return "gadgets.config.init(" . json_encode($gadgetConfig) . ");\n";
+	}
+	
+	private function appendMessages($gadget)
+	{
+		$msgs = '';
+		if ($gadget->getMessageBundle()) {
+			$bundle = $gadget->getMessageBundle();
+			$msgs = json_encode($bundle->getMessages());
+		}
+		return "gadgets.Prefs.setMessages_($msgs);\n";
+	}
+}

Added: incubator/shindig/trunk/php/src/gadgets/http/JsServlet.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/http/JsServlet.php?rev=652498&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/http/JsServlet.php (added)
+++ incubator/shindig/trunk/php/src/gadgets/http/JsServlet.php Thu May  1 05:36:31 2008
@@ -0,0 +1,103 @@
+<?php
+/*
+ * 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 event handler deals with the /js/core:caja:etc.js request which content type=url gadgets can use
+ * to retrieve our features javascript code
+ */
+class JsServlet extends HttpServlet {
+	
+	public function doGet()
+	{
+		$this->noHeaders = true;
+		if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
+			header("HTTP/1.1 304 Not Modified");
+			header('Content-Length: 0');
+			die();
+		}
+		$uri = strtolower($_SERVER["REQUEST_URI"]);
+		$uri = substr($uri, strrpos($uri, '/') + 1);
+		// remove any params that would confuse our parser
+		if (strpos($uri, '?')) {
+			$uri = substr($uri, 0, strpos($uri, '?'));
+		}
+		if (strpos($uri, '.js') !== false) {
+			$uri = substr($uri, 0, strlen($uri) - 3);
+		}
+		$needed = array();
+		if (strpos($uri, ':')) {
+			$needed = explode(':', $uri);
+		} else {
+			$needed[] = $uri;
+		}
+		$found = array();
+		$missing = array();
+		$registry = new GadgetFeatureRegistry(Config::get('features_path'));
+		if ($registry->getIncludedFeatures($needed, $found, $missing)) {
+			$containerParam = isset($_GET["c"]) ? $_GET["c"] : '';
+			$context = $containerParam == '1' ? 'CONTAINER' : 'GADGET';
+			$jsData = '';
+			$done = array();
+			do {
+				foreach ($found as $entry) {
+					if (! in_array($entry, $done)) {
+						$feat = $registry->getEntry($entry);
+						$feature = $feat->getFeature();
+						if ($feature instanceof JsLibraryFeatureFactory) {
+							$jsLib = $feature;
+							foreach ($jsLib->getLibraries($context) as $lib) {
+								if ($lib->getType() != 'URL') {
+									$jsData .= $lib->getContent();
+								}
+							}
+						}
+						$done[] = $entry;
+					}
+				}
+			} while (count($done) != count($found));
+			if (! strlen($jsData)) {
+				header("HTTP/1.0 404 Not Found", true);
+				die();
+			}
+			if (! isset($_GET['c']) || $_GET['c'] != 1) {
+				$contents = preg_replace('/\/\/.*$/m', '', preg_replace('@/\\*(?:.|[\\n\\r])*?\\*/@', '', file_get_contents(Config::get('container_config'))));
+				$containerData = json_decode($contents, true);
+				$jsData .= "\ngadgets.config.init(" . json_encode($containerData['gadgets.features']) . ");\n";
+			}
+			$this->setCachingHeaders();
+			header('Content-Length: ' . strlen($jsData));
+			header("Content-Type: text/javascript");
+			echo $jsData;
+		} else {
+			header("HTTP/1.0 404 Not Found", true);
+		
+		}
+		die();
+	}
+
+	private function setCachingHeaders()
+	{
+		header("Expires: Tue, 01 Jan 2030 00:00:01 GMT");
+		// IE seems to need this (10 years should be enough).
+		header("Cache-Control: public,max-age=315360000");
+		// Firefox requires this for certain cases.
+		header("Last-Modified: " . gmdate('D, d M Y H:i:s', time()));
+	}
+}

Added: incubator/shindig/trunk/php/src/gadgets/http/JsonRpcServlet.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/http/JsonRpcServlet.php?rev=652498&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/http/JsonRpcServlet.php (added)
+++ incubator/shindig/trunk/php/src/gadgets/http/JsonRpcServlet.php Thu May  1 05:36:31 2008
@@ -0,0 +1,57 @@
+<?php
+/*
+ * 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 JsonRpcServlet extends HttpServlet {
+	public function doPost()
+	{
+		try {
+			// we support both a raw http post (without application/x-www-form-urlencoded headers) like java does
+			// and a more php / curl safe version of a form post with 'request' as the post field that holds the request json data
+			if (isset($GLOBALS['HTTP_RAW_POST_DATA']) || isset($_POST['request'])) {
+				$request = json_decode(isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : $_POST['request']);
+				if ($request == (isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : $_POST['request'])) {
+					throw new Exception("Malformed json string");
+				}
+				$handler = new JsonRpcHandler();
+				$response = $handler->process($request);
+				echo json_encode(array('gadgets' => $response));
+			} else {
+				throw new Exception("No post data set");
+			}
+		} catch (Exception $e) {
+			header("HTTP/1.0 500 Internal Server Error", true, 500);
+			echo "<html><body><h1>Internal Server Error</h1><br />";
+			if (Config::get('debug')) {
+				echo $e->getMessage()."<br /><pre>";
+				print_r(debug_backtrace());
+				echo "</pre>";
+			}
+			echo "</body></html>";
+		}
+	}
+	
+	public function doGet()
+	{
+		header("HTTP/1.0 400 Bad Request", true, 400);
+		echo "<html><body>";
+		echo "<h1>Error</h1>";
+		echo "<body></html>";
+	}
+}
\ No newline at end of file

Added: incubator/shindig/trunk/php/src/gadgets/http/ProxyServlet.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/http/ProxyServlet.php?rev=652498&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/http/ProxyServlet.php (added)
+++ incubator/shindig/trunk/php/src/gadgets/http/ProxyServlet.php Thu May  1 05:36:31 2008
@@ -0,0 +1,56 @@
+<?php
+/*
+ * 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 ProxyServlet extends HttpServlet {
+	
+	public function doGet()
+	{
+		global $config;
+		$this->noHeaders = true;
+		$context = new GadgetContext('GADGET');
+		// those should be doable in one statement, but php seems to still evauluate the second ? and : pair,
+		// so throws an error about undefined index on post, even though it found it in get ... odd bug 
+		$url = isset($_GET['url']) ? $_GET['url'] : false;
+		if (! $url) {
+			$url = isset($_POST['url']) ? $_POST['url'] : false;
+		}
+		$url = urldecode($url);
+		$method = isset($_GET['httpMethod']) ? $_GET['httpMethod'] : false;
+		if (! $method) {
+			$method = isset($_POST['httpMethod']) ? $_POST['httpMethod'] : 'GET';
+		}
+		if (! $url) {
+			header("HTTP/1.0 400 Bad Request", true);
+			echo "<html><body><h1>400 - Missing url parameter</h1></body></html>";
+		}
+		$gadgetSigner = new $config['gadget_signer']();
+		$proxyHandler = new ProxyHandler($context);
+		if (! empty($_GET['output']) && $_GET['output'] == 'js') {
+			$proxyHandler->fetchJson($url, $gadgetSigner, $method);
+		} else {
+			$proxyHandler->fetch($url, $gadgetSigner, $method);
+		}
+	}
+	
+	public function doPost()
+	{
+		$this->doGet();
+	}
+}
\ No newline at end of file

Added: incubator/shindig/trunk/php/src/gadgets/samplecontainer/BasicBlobCrypter.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/samplecontainer/BasicBlobCrypter.php?rev=652498&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/samplecontainer/BasicBlobCrypter.php (added)
+++ incubator/shindig/trunk/php/src/gadgets/samplecontainer/BasicBlobCrypter.php Thu May  1 05:36:31 2008
@@ -0,0 +1,36 @@
+<?php
+/**
+ * This class provides basic binary blob encryption and decryption, for use with the security token
+ *
+ */
+
+//FIXME make this compatible with the java's blobcrypter
+class BasicBlobCrypter extends BlobCrypter {
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public function wrap($in)
+	{
+		if(is_array($in)) {
+			$in = implode(":", $in);
+		}
+		return $in;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public function unwrap($in, $maxAgeSec)
+	{
+		$data = explode(":", $in);
+		$rta = array();
+		$rta['o'] = $data[0];
+		$rta['a'] = $data[1];
+		$rta['v'] = $data[2];
+		$rta['d'] = $data[3];
+		$rta['u'] = $data[4];
+		$rta['m'] = $data[5];
+		return $rta;
+	}
+}

Added: incubator/shindig/trunk/php/src/gadgets/samplecontainer/BasicGadgetBlacklist.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/samplecontainer/BasicGadgetBlacklist.php?rev=652498&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/samplecontainer/BasicGadgetBlacklist.php (added)
+++ incubator/shindig/trunk/php/src/gadgets/samplecontainer/BasicGadgetBlacklist.php Thu May  1 05:36:31 2008
@@ -0,0 +1,43 @@
+<?php
+/*
+ * 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/src/gadgets/samplecontainer/BasicGadgetToken.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/samplecontainer/BasicGadgetToken.php?rev=652498&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/samplecontainer/BasicGadgetToken.php (added)
+++ incubator/shindig/trunk/php/src/gadgets/samplecontainer/BasicGadgetToken.php Thu May  1 05:36:31 2008
@@ -0,0 +1,137 @@
+<?php
+/*
+ * 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 {
+  /** serialized form of the token */
+  private $token;
+  
+  /** data from the token */
+  private $tokenData;
+  
+  /** tool to use for signing and encrypting the token */
+  private $crypter;
+
+  private $INSECURE_KEY = "{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}";
+  
+  private $OWNER_KEY = "o";
+  private $APP_KEY = "a";
+  private $VIEWER_KEY = "v";
+  private $DOMAIN_KEY = "d";
+  private $APPURL_KEY = "u";
+  private $MODULE_KEY = "m";
+
+  /**
+   * {@inheritDoc}
+   */
+  public function toSerialForm()
+  {
+    return $this->token;
+  }
+  
+  /**
+   * Generates a token from an input string
+   * @param token String form of token
+   * @param maxAge max age of the token (in seconds)
+   * @throws BlobCrypterException 
+   */
+  static public function createFromToken($token, $maxAge)
+  {
+	return new BasicBlobCrypter($token, $maxAge, null, null, null, null, null, null);
+  }
+  
+  /**
+   * Generates a token from an input array of values
+   * @param owner owner of this gadget
+   * @param viewer viewer of this gadget
+   * @param app application id
+   * @param domain domain of the container
+   * @param appUrl url where the application lives
+   * @param moduleId module id of this gadget 
+   * @throws BlobCrypterException 
+   */
+  static public function createFromValues($owner, $viewer, $app, $domain, $appUrl, $moduleId)
+  {
+  	return new BasicBlobCrypter(null, null, $owner, $viewer, $app, $domain, $appUrl, $moduleId);
+  }
+  
+  
+  public function __construct($token, $maxAge, $owner, $viewer, $app, $domain, $appUrl, $moduleId)
+  {
+  	$this->crypter = new BasicBlobCrypter();
+  	if (!empty($token)) {
+      $this->token = $token;
+      $this->tokenData = $this->crypter->unwrap($token, $maxAge);  	
+  	} else {
+      $this->tokenData = array();
+      $this->tokenData[$this->OWNER_KEY] = $owner;
+      $this->tokenData[$this->VIEWER_KEY] = $viewer;
+      $this->tokenData[$this->APP_KEY] = $app;
+      $this->tokenData[$this->DOMAIN_KEY] = $domain;
+      $this->tokenData[$this->APPURL_KEY] = $appUrl;
+      $this->tokenData[$this->MODULE_KEY] = $moduleId;
+      $this->token = $this->crypter->wrap($this->tokenData);
+  	}
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public function getAppId() {
+    return $this->tokenData[$this->APP_KEY];
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public function getDomain() {
+    return $this->tokenData[$this->DOMAIN_KEY];
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public function getOwnerId() {
+    return $this->tokenData[$this->OWNER_KEY];
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public function getViewerId() {
+    return $this->tokenData[$this->VIEWER_KEY];
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public function getAppUrl() {
+    return $this->tokenData[$this->APPURL_KEY];
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public function getModuleId() {
+    return intval($this->tokenData[$this->MODULE_KEY]);
+  }
+}
\ No newline at end of file

Added: incubator/shindig/trunk/php/src/gadgets/samplecontainer/BasicGadgetTokenDecoder.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/samplecontainer/BasicGadgetTokenDecoder.php?rev=652498&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/samplecontainer/BasicGadgetTokenDecoder.php (added)
+++ incubator/shindig/trunk/php/src/gadgets/samplecontainer/BasicGadgetTokenDecoder.php Thu May  1 05:36:31 2008
@@ -0,0 +1,61 @@
+<?php
+/*
+ * 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 GadgetTokenDecoder implementation that just provides dummy data to satisfy
+ * tests and API calls. Do not use this for any security applications.
+ */
+class BasicGadgetTokenDecoder extends GadgetTokenDecoder {
+	private $OWNER_INDEX = 0;
+	private $VIEWER_INDEX = 1;
+	private $APP_ID_INDEX = 2;
+	private $CONTAINER_INDEX = 3;
+	private $APP_URL_INDEX = 4;
+	private $MODULE_ID_INDEX = 5;
+
+	/**
+ 	* {@inheritDoc}
+	*
+ 	* Returns a token with some faked out values.
+ 	*/
+	public function createToken($stringToken)
+	{
+		if (empty($stringToken)) {
+			//FIXME an empty token should generate an exception instead of
+			// being ignored, however currently a proxy request always has
+			// an empty token (bug presumably), so without this hack it doesn't work
+			// reinstate the throw new GadgetException('INVALID_GADGET_TOKEN'); once thats fixed
+			return;
+		}
+		try {
+			$tokens = explode(":", $stringToken);
+			return new BasicGadgetToken(null,null,
+				urldecode($tokens[$this->OWNER_INDEX]),
+				urldecode($tokens[$this->VIEWER_INDEX]),
+				urldecode($tokens[$this->APP_ID_INDEX]),
+				urldecode($tokens[$this->CONTAINER_INDEX]),
+				urldecode($tokens[$this->APP_URL_INDEX]),
+				urldecode($tokens[$this->MODULE_ID_INDEX])
+			);
+		} catch (Exception $e) {
+			throw new GadgetException('INVALID_GADGET_TOKEN');
+		}
+	}
+}

Added: incubator/shindig/trunk/php/src/gadgets/samplecontainer/BasicRemoteContent.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/samplecontainer/BasicRemoteContent.php?rev=652498&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/samplecontainer/BasicRemoteContent.php (added)
+++ incubator/shindig/trunk/php/src/gadgets/samplecontainer/BasicRemoteContent.php Thu May  1 05:36:31 2008
@@ -0,0 +1,43 @@
+<?php
+/*
+ * 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($request, $context)
+	{
+		global $config;
+		$cache = new $config['data_cache']();
+		$remoteContentFetcher = new BasicRemoteContentFetcher();
+		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 (! $context->getIgnoreCache() && ($cachedRequest = $cache->get($request->toHash())) !== false) {
+			$ret = $cachedRequest;
+		} else {
+			$ret = $remoteContentFetcher->fetchRequest($request);
+			// only cache requests that returned a 200 OK
+			if ($request->getHttpCode() == '200') {
+				$cache->set($request->toHash(), $request);
+			}
+		}
+		return $ret;
+	}
+}
\ No newline at end of file