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 2009/02/15 18:35:07 UTC
svn commit: r744702 [1/3] - in /incubator/shindig/trunk/php: ./ config/
src/common/ src/gadgets/ src/gadgets/render/ src/gadgets/rewrite/
src/gadgets/servlet/ test/misc/
Author: chabotc
Date: Sun Feb 15 17:35:05 2009
New Revision: 744702
URL: http://svn.apache.org/viewvc?rev=744702&view=rev
Log:
This commit starts the refactoring of the gadget rendering implementation to accomedate for proxied content, data pipeling and a unified dom tree parser for rewriting, sanatizer and templating. This should be considered *unstable and incomplete* and not used in production.
Added:
incubator/shindig/trunk/php/src/gadgets/GadgetFactory.php
incubator/shindig/trunk/php/src/gadgets/GadgetSpec.php
incubator/shindig/trunk/php/src/gadgets/MakeRequestHandler.php
incubator/shindig/trunk/php/src/gadgets/ProxyBase.php
incubator/shindig/trunk/php/src/gadgets/render/
incubator/shindig/trunk/php/src/gadgets/render/GadgetHrefRenderer.php
incubator/shindig/trunk/php/src/gadgets/render/GadgetHtmlRenderer.php
incubator/shindig/trunk/php/src/gadgets/render/GadgetRenderer.php
incubator/shindig/trunk/php/src/gadgets/render/GadgetUrlRenderer.php
incubator/shindig/trunk/php/src/gadgets/servlet/MakeRequestServlet.php
Removed:
incubator/shindig/trunk/php/src/gadgets/Auth.php
incubator/shindig/trunk/php/src/gadgets/FeatureSpec.php
incubator/shindig/trunk/php/src/gadgets/GadgetException.php
incubator/shindig/trunk/php/src/gadgets/GadgetFeature.php
incubator/shindig/trunk/php/src/gadgets/GadgetFeatureFactory.php
incubator/shindig/trunk/php/src/gadgets/GadgetId.php
incubator/shindig/trunk/php/src/gadgets/GadgetServer.php
incubator/shindig/trunk/php/src/gadgets/GadgetSigner.php
incubator/shindig/trunk/php/src/gadgets/GadgetSpecFactory.php
incubator/shindig/trunk/php/src/gadgets/HttpUtil.php
incubator/shindig/trunk/php/src/gadgets/Icon.php
incubator/shindig/trunk/php/src/gadgets/JsFeatureLoader.php
incubator/shindig/trunk/php/src/gadgets/JsLibrary.php
incubator/shindig/trunk/php/src/gadgets/JsLibraryFeatureFactory.php
incubator/shindig/trunk/php/src/gadgets/LinkSpec.php
incubator/shindig/trunk/php/src/gadgets/LocaleMessageBundle.php
incubator/shindig/trunk/php/src/gadgets/LocaleSpec.php
incubator/shindig/trunk/php/src/gadgets/MessageBundle.php
incubator/shindig/trunk/php/src/gadgets/MessageBundleParser.php
incubator/shindig/trunk/php/src/gadgets/Preload.php
incubator/shindig/trunk/php/src/gadgets/ProxyGadgetContext.php
incubator/shindig/trunk/php/src/gadgets/UserPref.php
incubator/shindig/trunk/php/src/gadgets/UserPrefs.php
incubator/shindig/trunk/php/src/gadgets/ViewSpec.php
incubator/shindig/trunk/php/src/gadgets/rewrite/ContentRewriteFeature.php
incubator/shindig/trunk/php/src/gadgets/rewrite/ContentRewriter.php
Modified:
incubator/shindig/trunk/php/config/container.php
incubator/shindig/trunk/php/index.php
incubator/shindig/trunk/php/src/common/HttpServlet.php
incubator/shindig/trunk/php/src/gadgets/Gadget.php
incubator/shindig/trunk/php/src/gadgets/GadgetContext.php
incubator/shindig/trunk/php/src/gadgets/GadgetFeatureRegistry.php
incubator/shindig/trunk/php/src/gadgets/GadgetSpecParser.php
incubator/shindig/trunk/php/src/gadgets/MetadataContext.php
incubator/shindig/trunk/php/src/gadgets/MetadataHandler.php
incubator/shindig/trunk/php/src/gadgets/ProxyHandler.php
incubator/shindig/trunk/php/src/gadgets/servlet/GadgetRenderingServlet.php
incubator/shindig/trunk/php/src/gadgets/servlet/JsServlet.php
incubator/shindig/trunk/php/src/gadgets/servlet/MetadataServlet.php
incubator/shindig/trunk/php/src/gadgets/servlet/ProxyServlet.php
incubator/shindig/trunk/php/test/misc/testGadget.xml
Modified: incubator/shindig/trunk/php/config/container.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/config/container.php?rev=744702&r1=744701&r2=744702&view=diff
==============================================================================
--- incubator/shindig/trunk/php/config/container.php (original)
+++ incubator/shindig/trunk/php/config/container.php Sun Feb 15 17:35:05 2009
@@ -50,9 +50,17 @@
// Allow plain text security tokens, this is only here to allow the sample files to work. Disable on a production site
'allow_plaintext_token' => true,
+
+ // Is a valid security token required to render a gadget? The token is required for doing signed preloads, but disallowing this
+ // can also help prevent external parties using your rendering server (only for the paranoid :)
+ 'render_token_required' => false,
+
// Compress the inlined javascript, saves upto 50% of the document size
'compress_javascript' => true,
+ // Default refresh interval for proxy/makeRequest's if none is specified in the query
+ 'default_refresh_interval' => 1209587,
+
// The URL Prefix under which shindig lives ie if you have http://myhost.com/shindig/php set web_prefix to /shindig/php
'web_prefix' => '',
// If you changed the web prefix, add the prefix to these too
Modified: incubator/shindig/trunk/php/index.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/index.php?rev=744702&r1=744701&r2=744702&view=diff
==============================================================================
--- incubator/shindig/trunk/php/index.php (original)
+++ incubator/shindig/trunk/php/index.php Sun Feb 15 17:35:05 2009
@@ -50,15 +50,15 @@
'src/common',
'src/common/sample',
'src/gadgets',
- 'src/gadgets/servlet',
+ 'src/gadgets/servlet',
'src/gadgets/oauth',
'src/gadgets/sample',
'src/social',
- 'src/social/servlet',
+ 'src/social/servlet',
'src/social/service',
'src/social/opensocial',
'src/social/model',
- 'src/social/spi',
+ 'src/social/spi',
'src/social/converters',
'src/social/oauth',
'src/social/sample'
@@ -78,15 +78,15 @@
}
$servletMap = array(
- Config::get('web_prefix') . '/gadgets/files' => 'FilesServlet',
- Config::get('web_prefix') . '/gadgets/js' => 'JsServlet',
- Config::get('web_prefix') . '/gadgets/proxy' => 'ProxyServlet',
- Config::get('web_prefix') . '/gadgets/makeRequest' => 'ProxyServlet',
- Config::get('web_prefix') . '/gadgets/ifr' => 'GadgetRenderingServlet',
- Config::get('web_prefix') . '/gadgets/metadata' => 'MetadataServlet',
- Config::get('web_prefix') . '/social/rest' => 'DataServiceServlet',
- Config::get('web_prefix') . '/social/rpc' => 'JsonRpcServlet',
- Config::get('web_prefix') . '/public.crt' => 'CertServlet',
+ Config::get('web_prefix') . '/gadgets/files' => 'FilesServlet',
+ Config::get('web_prefix') . '/gadgets/js' => 'JsServlet',
+ Config::get('web_prefix') . '/gadgets/proxy' => 'ProxyServlet',
+ Config::get('web_prefix') . '/gadgets/makeRequest' => 'MakeRequestServlet',
+ Config::get('web_prefix') . '/gadgets/ifr' => 'GadgetRenderingServlet',
+ Config::get('web_prefix') . '/gadgets/metadata' => 'MetadataServlet',
+ Config::get('web_prefix') . '/social/rest' => 'DataServiceServlet',
+ Config::get('web_prefix') . '/social/rpc' => 'JsonRpcServlet',
+ Config::get('web_prefix') . '/public.crt' => 'CertServlet',
Config::get('web_prefix') . '/public.cer' => 'CertServlet'
);
@@ -109,7 +109,7 @@
if ($servlet) {
$class = new $class();
$method = $_SERVER['REQUEST_METHOD'];
- // Not all clients support the PUT, HEAD & DELETE http methods, they depend on the X-HTTP-Method-Override instead
+ // Not all clients support the PUT, HEAD & DELETE http methods, they depend on the X-HTTP-Method-Override instead
if ($method == 'POST' && isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) {
$method = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'];
}
Modified: incubator/shindig/trunk/php/src/common/HttpServlet.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/common/HttpServlet.php?rev=744702&r1=744701&r2=744702&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/common/HttpServlet.php (original)
+++ incubator/shindig/trunk/php/src/common/HttpServlet.php Sun Feb 15 17:35:05 2009
@@ -53,7 +53,6 @@
if (! $this->noHeaders) {
header("Content-Type: $this->contentType" . (! empty($this->charset) ? "; charset={$this->charset}" : ''));
header('Accept-Ranges: bytes');
- $content = ob_get_contents();
if ($this->noCache) {
header("Cache-Control: no-cache, must-revalidate", true);
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT", true);
@@ -64,21 +63,6 @@
header("Expires: " . gmdate("D, d M Y H:i:s", time() + $this->cacheTime) . " GMT", true);
// Obey browsers (or proxy's) request to send a fresh copy if we recieve a no-cache pragma or cache-control request
if (! isset($_SERVER['HTTP_PRAGMA']) || ! strstr(strtolower($_SERVER['HTTP_PRAGMA']), 'no-cache') && (! isset($_SERVER['HTTP_CACHE_CONTROL']) || ! strstr(strtolower($_SERVER['HTTP_CACHE_CONTROL']), 'no-cache'))) {
- // If the browser send us a E-TAG check if it matches (md5 sum of content), if so send a not modified header instead of content
- $etag = '"' . md5($content) . '"';
- if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] == $etag) {
- header("ETag: \"$etag\"");
- if ($this->lastModified) {
- header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $this->lastModified) . ' GMT', true);
- }
- header("HTTP/1.1 304 Not Modified", true);
- header('Content-Length: 0', true);
- ob_end_clean();
- die();
- }
- header("ETag: $etag");
- // If no etag is present, then check if maybe this browser supports if_modified_since tags,
- // check it against our lastModified (if it's set)
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $this->lastModified && ! isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
$if_modified_since = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
if ($this->lastModified <= $if_modified_since) {
@@ -104,8 +88,8 @@
}
/**
- * Sets the time in seconds that the browser's cache should be
- * considered out of date (through the Expires header)
+ * Sets the time in seconds that the browser's cache should be
+ * considered out of date (through the Expires header)
*
* @param int $time time in seconds
*/
@@ -123,7 +107,7 @@
}
/**
- * Sets the content type of this request (forinstance: text/html or text/javascript, etc)
+ * Sets the content type of this request (forinstance: text/html or text/javascript, etc)
*
* @param string $type content type header to use
*/
@@ -132,7 +116,7 @@
}
/**
- * Returns the current content type
+ * Returns the current content type
*
* @return string content type string
*/
Modified: incubator/shindig/trunk/php/src/gadgets/Gadget.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/Gadget.php?rev=744702&r1=744701&r2=744702&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/Gadget.php (original)
+++ incubator/shindig/trunk/php/src/gadgets/Gadget.php Sun Feb 15 17:35:05 2009
@@ -18,275 +18,489 @@
* under the License.
*/
-/**
- * The main gadget class, this gets filled in by the GadgetSpecParser, etc
- * and contains all the gadget information.
- *
- */
+class GadgetException extends Exception {}
+
class Gadget {
- private $jsLibraries;
- private $substitutions;
- private $userPrefValues;
- private $oAuthSpec;
- private $messageBundle = array();
- private $checksum;
- public $contentTypes = array('HTML', 'URL');
- public $id;
- public $author;
- public $authorEmail;
- public $description;
- public $directoryTitle;
- public $contentData = array();
- public $localeSpecs = array();
- public $preloads = array();
- public $requires = array();
- public $screenshot;
- public $thumbnail;
- public $title;
- public $titleUrl = null;
- public $userPrefs = array();
- public $authorAffiliation;
- public $authorLocation;
- public $authorPhoto;
- public $authorAboutMe;
- public $authorQuote;
- public $authorLink;
- public $showStats;
- public $showInDirectory;
- public $string;
- public $width;
- public $height;
- public $category;
- public $category2;
- public $singleton;
- public $renderInline;
- public $scaling;
- public $scrolling;
- public $views = array();
- public $links = array();
- public $icons = array();
-
- public function __construct($id = false, $context) {
- if ($id) {
- $this->id = $id;
- }
- if ($context->getUserPrefs()) {
- $this->setPrefs($context->getUserPrefs());
+ const DEFAULT_VIEW = 'profile';
+ public $gadgetSpec;
+ public $features;
+ public $substitutions;
+ public $rightToLeft;
+ public $gadgetContext;
+
+ public function __construct(GadgetSpec $gadgetSpec, GadgetContext $gadgetContext) {
+ $this->gadgetSpec = $gadgetSpec;
+ $this->gadgetContext = $gadgetContext;
+ }
+
+ public function getView($viewName) {
+ if (isset($this->gadgetSpec->views[$viewName])) {
+ return $this->gadgetSpec->views[$viewName];
+ } elseif (isset($this->gadgetSpec->views[self::DEFAULT_VIEW])) {
+ return $this->gadgetSpec->views[self::DEFAULT_VIEW];
}
- $this->substitutions = new Substitutions();
- $this->jsLibraries = array();
+ throw new GadgetException("Invalid view specified for this gadget");
}
- public function setId($id) {
- $this->id = $id;
+ /**
+ * @return unknown
+ */
+ public function getAuthor() {
+ return $this->substitutions->substitute($this->gadgetSpec->author);
}
- public function setPrefs($prefs) {
- $this->userPrefValues = $prefs;
+ /**
+ * @return unknown
+ */
+ public function getAuthorAboutme() {
+ return $this->substitutions->substitute($this->gadgetSpec->authorAboutme);
}
- public function getAuthor() {
- return $this->substitutions->substitute($this->author);
+ /**
+ * @return unknown
+ */
+ public function getAuthorAffiliation() {
+ return $this->substitutions->substitute($this->gadgetSpec->authorAffiliation);
}
+ /**
+ * @return unknown
+ */
public function getAuthorEmail() {
- return $this->substitutions->substitute($this->authorEmail);
+ return $this->substitutions->substitute($this->gadgetSpec->authorEmail);
+ }
+
+ /**
+ * @return unknown
+ */
+ public function getAuthorLink() {
+ return $this->substitutions->substitute($this->gadgetSpec->authorLink);
+ }
+
+ /**
+ * @return unknown
+ */
+ public function getAuthorLocation() {
+ return $this->substitutions->substitute($this->gadgetSpec->authorLocation);
+ }
+
+ /**
+ * @return unknown
+ */
+ public function getAuthorPhoto() {
+ return $this->substitutions->substitute($this->gadgetSpec->authorPhoto);
+ }
+
+ /**
+ * @return unknown
+ */
+ public function getAuthorQuote() {
+ return $this->substitutions->substitute($this->gadgetSpec->authorQuote);
}
- public function getMessageBundle() {
- return $this->messageBundle;
+ /**
+ * @return unknown
+ */
+ public function getCategory() {
+ return $this->substitutions->substitute($this->gadgetSpec->category);
+ }
+
+ /**
+ * @return unknown
+ */
+ public function getCategory2() {
+ return $this->substitutions->substitute($this->gadgetSpec->category2);
+ }
+
+ /**
+ * @return unknown
+ */
+ public function getChecksum() {
+ return $this->gadgetSpec->checksum;
}
+ /**
+ * @return unknown
+ */
public function getDescription() {
- return $this->substitutions->substitute($this->description);
+ return $this->substitutions->substitute($this->gadgetSpec->description);
}
+ /**
+ * @return unknown
+ */
public function getDirectoryTitle() {
- return $this->substitutions->substitute($this->directoryTitle);
+ return $this->substitutions->substitute($this->gadgetSpec->directoryTitle);
}
- public function getId() {
- return $this->id;
+ /**
+ * @return unknown
+ */
+ public function getHeight() {
+ return $this->substitutions->substitute($this->gadgetSpec->height);
}
- public function getJsLibraries() {
- return $this->jsLibraries;
+ /**
+ * @return unknown
+ */
+ public function getIcon() {
+ return $this->substitutions->substitute($this->gadgetSpec->icon);
}
- public function addJsLibrary($library) {
- $this->jsLibraries[] = $library;
+ /**
+ * @return unknown
+ */
+ public function getLinks() {
+ return $this->gadgetSpec->links;
}
- public function getLocaleSpecs() {
- return $this->localeSpecs;
+ /**
+ * @return unknown
+ */
+ public function getLocales() {
+ return $this->gadgetSpec->locales;
}
- 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();
- }
+ /**
+ * @return unknown
+ */
+ public function getOptionalFeatures() {
+ return $this->gadgetSpec->optionalFeatures;
}
+ /**
+ * @return unknown
+ */
public function getPreloads() {
- return $this->preloads;
+ return $this->gadgetSpec->preloads;
}
- public function getRequires() {
- return $this->requires;
+ /**
+ * @return unknown
+ */
+ public function getRenderInline() {
+ return $this->substitutions->substitute($this->gadgetSpec->renderInline);
}
+ /**
+ * @return unknown
+ */
+ public function getRequiredFeatures() {
+ return $this->substitutions->substitute($this->gadgetSpec->requiredFeatures);
+ }
+
+ /**
+ * @return unknown
+ */
+ public function getScaling() {
+ return $this->substitutions->substitute($this->gadgetSpec->scaling);
+ }
+
+ /**
+ * @return unknown
+ */
public function getScreenshot() {
- return $this->substitutions->substitute($this->screenshot);
+ return $this->substitutions->substitute($this->gadgetSpec->screenshot);
}
- public function getSubstitutions() {
- return $this->substitutions;
+ /**
+ * @return unknown
+ */
+ public function getScrolling() {
+ return $this->substitutions->substitute($this->gadgetSpec->scrolling);
+ }
+
+ /**
+ * @return unknown
+ */
+ public function getShowInDirectory() {
+ return $this->substitutions->substitute($this->gadgetSpec->showInDirectory);
+ }
+
+ /**
+ * @return unknown
+ */
+ public function getShowStats() {
+ return $this->substitutions->substitute($this->gadgetSpec->showStats);
}
+ /**
+ * @return unknown
+ */
+ public function getSingleton() {
+ return $this->substitutions->substitute($this->gadgetSpec->singleton);
+ }
+
+ /**
+ * @return unknown
+ */
+ public function getString() {
+ return $this->substitutions->substitute($this->gadgetSpec->string);
+ }
+
+ /**
+ * @return unknown
+ */
public function getThumbnail() {
- return $this->substitutions->substitute($this->thumbnail);
+ return $this->substitutions->substitute($this->gadgetSpec->thumbnail);
}
+ /**
+ * @return unknown
+ */
public function getTitle() {
- return $this->substitutions->substitute($this->title);
+ return $this->substitutions->substitute($this->gadgetSpec->title);
}
+ /**
+ * @return unknown
+ */
public function getTitleUrl() {
- $ret = null;
- if (! empty($this->titleUrl)) {
- $ret = $this->substitutions->substitute($this->titleUrl);
- }
- return $ret;
+ return $this->substitutions->substitute($this->gadgetSpec->titleUrl);
}
- public function getAuthorAffiliation() {
- return $this->substitutions->substitute($this->authorAffiliation);
+ /**
+ * @return unknown
+ */
+ public function getUserPrefs() {
+ return $this->gadgetSpec->userPrefs;
}
- public function getAuthorLocation() {
- return $this->substitutions->substitute($this->authorLocation);
+ /**
+ * @return unknown
+ */
+ public function getWidth() {
+ return $this->substitutions->substitute($this->gadgetSpec->width);
}
- public function getAuthorPhoto() {
- return $this->substitutions->substitute($this->authorPhoto);
+ /**
+ * @param unknown_type $author
+ */
+ public function setAuthor($author) {
+ $this->gadgetSpec->author = $author;
}
- public function getAuthorAboutme() {
- return $this->substitutions->substitute($this->authorAboutMe);
+ /**
+ * @param unknown_type $authorAboutme
+ */
+ public function setAuthorAboutme($authorAboutme) {
+ $this->gadgetSpec->authorAboutme = $authorAboutme;
}
- public function getAuthorQuote() {
- return $this->substitutions->substitute($this->authorQuote);
+ /**
+ * @param unknown_type $authorAffiliation
+ */
+ public function setAuthorAffiliation($authorAffiliation) {
+ $this->gadgetSpec->authorAffiliation = $authorAffiliation;
}
- public function getAuthorLink() {
- return $this->substitutions->substitute($this->authorLink);
+ /**
+ * @param unknown_type $authorEmail
+ */
+ public function setAuthorEmail($authorEmail) {
+ $this->gadgetSpec->authorEmail = $authorEmail;
}
- public function getShowStats() {
- return $this->showStats;
+ /**
+ * @param unknown_type $authorLink
+ */
+ public function setAuthorLink($authorLink) {
+ $this->gadgetSpec->authorLink = $authorLink;
}
- public function getShowInDirectory() {
- return $this->showInDirectory;
+ /**
+ * @param unknown_type $authorLocation
+ */
+ public function setAuthorLocation($authorLocation) {
+ $this->gadgetSpec->authorLocation = $authorLocation;
}
- public function getString() {
- return $this->substitutions->substitute($this->string);
+ /**
+ * @param unknown_type $authorPhoto
+ */
+ public function setAuthorPhoto($authorPhoto) {
+ $this->gadgetSpec->authorPhoto = $authorPhoto;
}
- public function getWidth() {
- return $this->width;
+ /**
+ * @param unknown_type $authorQuote
+ */
+ public function setAuthorQuote($authorQuote) {
+ $this->gadgetSpec->authorQuote = $authorQuote;
}
- public function getHeight() {
- return $this->height;
+ /**
+ * @param unknown_type $category
+ */
+ public function setCategory($category) {
+ $this->gadgetSpec->category = $category;
}
- public function getCategory() {
- return $this->substitutions->substitute($this->category);
+ /**
+ * @param unknown_type $category2
+ */
+ public function setCategory2($category2) {
+ $this->gadgetSpec->category2 = $category2;
}
- public function getCategory2() {
- return $this->substitutions->substitute($this->category2);
+ /**
+ * @param unknown_type $checksum
+ */
+ public function setChecksum($checksum) {
+ $this->gadgetSpec->checksum = $checksum;
}
- public function getSingleton() {
- return $this->singleton;
+ /**
+ * @param unknown_type $description
+ */
+ public function setDescription($description) {
+ $this->gadgetSpec->description = $description;
}
- public function getRenderInline() {
- return $this->renderInline;
+ /**
+ * @param unknown_type $directoryTitle
+ */
+ public function setDirectoryTitle($directoryTitle) {
+ $this->gadgetSpec->directoryTitle = $directoryTitle;
}
- public function getScaling() {
- return $this->scaling;
+ /**
+ * @param unknown_type $height
+ */
+ public function setHeight($height) {
+ $this->gadgetSpec->height = $height;
}
- public function getScrolling() {
- return $this->scrolling;
+ /**
+ * @param unknown_type $icon
+ */
+ public function setIcon($icon) {
+ $this->gadgetSpec->icon = $icon;
}
- public function getUserPrefs() {
- return $this->userPrefs;
+ /**
+ * @param unknown_type $links
+ */
+ public function setLinks($links) {
+ $this->gadgetSpec->links = $links;
}
- public function getUserPrefValues() {
- return $this->userPrefValues;
+ /**
+ * @param unknown_type $locales
+ */
+ public function setLocales($locales) {
+ $this->gadgetSpec->locales = $locales;
}
- public function setMessageBundle($messageBundle) {
- $this->messageBundle = $messageBundle;
+ /**
+ * @param unknown_type $optionalFeatures
+ */
+ public function setOptionalFeatures($optionalFeatures) {
+ $this->gadgetSpec->optionalFeatures = $optionalFeatures;
}
- public function getLinks() {
- return $this->links;
+ /**
+ * @param unknown_type $preloads
+ */
+ public function setPreloads($preloads) {
+ $this->gadgetSpec->preloads = $preloads;
}
- public function getLink($rel) {
- foreach ($this->links as $link) {
- if ($link->getRel() == $rel) {
- return $link;
- }
- }
- return false;
+ /**
+ * @param unknown_type $renderInline
+ */
+ public function setRenderInline($renderInline) {
+ $this->gadgetSpec->renderInline = $renderInline;
}
- public function getIcons() {
- return $this->icons;
+ /**
+ * @param unknown_type $requiredFeatures
+ */
+ public function setRequiredFeatures($requiredFeatures) {
+ $this->gadgetSpec->requiredFeatures = $requiredFeatures;
}
- public function getViews() {
- return $this->views;
+ /**
+ * @param unknown_type $scaling
+ */
+ public function setScaling($scaling) {
+ $this->gadgetSpec->scaling = $scaling;
}
- public function getView($viewName) {
- if (isset($this->views[$viewName])) {
- return $this->views[$viewName];
- } elseif (isset($this->views[DEFAULT_VIEW])) {
- return $this->views[DEFAULT_VIEW];
- }
- throw new GadgetException("Invalid view specified for this gadget");
+ /**
+ * @param unknown_type $screenshot
+ */
+ public function setScreenshot($screenshot) {
+ $this->gadgetSpec->screenshot = $screenshot;
}
- public function getOAuthSpec() {
- return $this->oAuthSpec;
+ /**
+ * @param unknown_type $scrolling
+ */
+ public function setScrolling($scrolling) {
+ $this->gadgetSpec->scrolling = $scrolling;
}
- public function setOAuthSpec($oAuthSpec) {
- $this->oAuthSpec = $oAuthSpec;
+ /**
+ * @param unknown_type $showInDirectory
+ */
+ public function setShowInDirectory($showInDirectory) {
+ $this->gadgetSpec->showInDirectory = $showInDirectory;
}
- public function setChecksum($xml) {
- $this->checksum = md5($xml);
+ /**
+ * @param unknown_type $showStats
+ */
+ public function setShowStats($showStats) {
+ $this->gadgetSpec->showStats = $showStats;
}
- public function getChecksum() {
- return $this->checksum;
+ /**
+ * @param unknown_type $singleton
+ */
+ public function setSingleton($singleton) {
+ $this->gadgetSpec->singleton = $singleton;
+ }
+
+ /**
+ * @param unknown_type $string
+ */
+ public function setString($string) {
+ $this->gadgetSpec->string = $string;
+ }
+
+ /**
+ * @param unknown_type $thumbnail
+ */
+ public function setThumbnail($thumbnail) {
+ $this->gadgetSpec->thumbnail = $thumbnail;
+ }
+
+ /**
+ * @param unknown_type $title
+ */
+ public function setTitle($title) {
+ $this->gadgetSpec->title = $title;
+ }
+
+ /**
+ * @param unknown_type $titleUrl
+ */
+ public function setTitleUrl($titleUrl) {
+ $this->gadgetSpec->titleUrl = $titleUrl;
+ }
+
+ /**
+ * @param unknown_type $userPrefs
+ */
+ public function setUserPrefs($userPrefs) {
+ $this->gadgetSpec->userPrefs = $userPrefs;
+ }
+
+ /**
+ * @param unknown_type $width
+ */
+ public function setWidth($width) {
+ $this->gadgetSpec->width = $width;
}
}
Modified: incubator/shindig/trunk/php/src/gadgets/GadgetContext.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/GadgetContext.php?rev=744702&r1=744701&r2=744702&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/GadgetContext.php (original)
+++ incubator/shindig/trunk/php/src/gadgets/GadgetContext.php Sun Feb 15 17:35:05 2009
@@ -18,7 +18,7 @@
* under the License.
*/
-define('DEFAULT_VIEW', 'profile');
+
/*
* GadgetContext contains all contextual variables and classes that are relevant for this request,
@@ -26,11 +26,11 @@
* Server wide variables are stored in config.php
*/
class GadgetContext {
+ const DEFAULT_VIEW = 'profile';
protected $httpFetcher = null;
protected $locale = null;
protected $renderingContext = null;
protected $registry = null;
- protected $userPrefs = null;
protected $gadgetId = null;
protected $view = null;
protected $moduleId = null;
@@ -59,7 +59,7 @@
}
private function getRefreshIntervalParam() {
- return isset($_GET['refresh']) ? $_GET['refresh'] : Config::get('cache_time');
+ return isset($_GET['refresh']) ? $_GET['refresh'] : Config::get('default_refresh_interval');
}
private function getContainerParam() {
@@ -100,7 +100,7 @@
}
private function getViewParam() {
- return ! empty($_GET['view']) ? $_GET['view'] : DEFAULT_VIEW;
+ return ! empty($_GET['view']) ? $_GET['view'] : self::DEFAULT_VIEW;
}
private function instanceBlacklist() {
@@ -112,18 +112,6 @@
}
}
- private function instanceUserPrefs() {
- $prefs = array();
- $userPrefParamPrefix = Config::get('userpref_param_prefix');
- foreach ($_GET as $key => $val) {
- if (substr($key, 0, strlen($userPrefParamPrefix)) == $userPrefParamPrefix) {
- $name = substr($key, strlen($userPrefParamPrefix));
- $prefs[$name] = $val;
- }
- }
- return new UserPrefs($prefs);
- }
-
private function instanceGadgetId($url, $moduleId) {
return new GadgetId($url, $moduleId);
}
@@ -134,8 +122,7 @@
}
private function instanceRegistry() {
- // Profiling showed 40% of the processing time was spend in the feature registry
- // So by caching this and making it a one time initialization, we almost double the performance
+ // feature parsing is very resource intensive so by caching the result this saves upto 30% of the processing time
$featureCache = Config::get('feature_cache');
$featureCache = new $featureCache();
if (! ($registry = $featureCache->get(md5(Config::get('features_path'))))) {
@@ -149,7 +136,7 @@
// Get language and country params, try the GET params first, if their not set try the POST, else use 'all' as default
$language = ! empty($_GET['lang']) ? $_GET['lang'] : (! empty($_POST['lang']) ? $_POST['lang'] : 'all');
$country = ! empty($_GET['country']) ? $_GET['country'] : (! empty($_POST['country']) ? $_POST['country'] : 'all');
- return new Locale($language, $country);
+ return array('lang' => strtolower($language), 'country' => strtoupper($country));
}
private function instanceContainerConfig() {
@@ -189,13 +176,6 @@
return $this->url;
}
- public function getUserPrefs() {
- if ($this->userPrefs == null) {
- $this->setUserPrefs($this->instanceUserPrefs());
- }
- return $this->userPrefs;
- }
-
public function getView() {
return $this->view;
}
@@ -248,10 +228,6 @@
$this->url = $url;
}
- public function setUserPrefs($userPrefs) {
- $this->userPrefs = $userPrefs;
- }
-
public function setView($view) {
$this->view = $view;
}
@@ -301,10 +277,6 @@
return $this->locale;
}
- public function getFeatureRegistry() {
- return $this->registry;
- }
-
/**
* Extracts the 'st' token from the GET or POST params and calls the
* signer to validate the token
@@ -323,6 +295,9 @@
if (count(explode(':', $token)) != 6) {
$token = urldecode(base64_decode($token));
}
+ if (empty($token)) {
+ throw new Exception("Missing or invalid security token");
+ }
return $signer->createToken($token);
}
}
Added: incubator/shindig/trunk/php/src/gadgets/GadgetFactory.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/GadgetFactory.php?rev=744702&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/GadgetFactory.php (added)
+++ incubator/shindig/trunk/php/src/gadgets/GadgetFactory.php Sun Feb 15 17:35:05 2009
@@ -0,0 +1,308 @@
+<?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.
+ */
+
+/**
+ * The Gadget Factory builds a gadget based on the current context and token and returns a fully processed
+ * gadget ready to be rendered.
+ *
+ */
+class GadgetFactory {
+ private $context;
+ private $token;
+
+ public function __construct(GadgetContext $context, $token) {
+ $this->context = $context;
+ $this->token = $token;
+ }
+
+ /**
+ * Returns the processed gadget spec
+ *
+ * @return GadgetSpec
+ */
+ public function createGadget() {
+ $gadgetUrl = $this->context->getUrl();
+ if ($this->context->getBlacklist() != null && $this->context->getBlacklist()->isBlacklisted($gadgetUrl)) {
+ throw new GadgetException("The Gadget ($gadgetUrl) is blacklisted and can not be rendered");
+ }
+ // Fetch the gadget's content and create a GadgetSpec
+ $gadgetContent = $this->fetchGadget($gadgetUrl);
+ $gadgetSpecParser = new GadgetSpecParser();
+ $gadgetSpec = $gadgetSpecParser->parse($gadgetContent);
+ $gadget = new Gadget($gadgetSpec, $this->context);
+
+ // Process the gadget: fetching remote resources, processing & applying the correct translations, user prefs and feature resolving
+ $this->fetchResources($gadget);
+ $this->mergeLocales($gadget);
+ $this->parseUserPrefs($gadget);
+ $this->addSubstitutions($gadget);
+ $this->applySubstitutions($gadget);
+ $this->parseFeatures($gadget);
+ return $gadget;
+ }
+
+ /**
+ * Resolves the Required and Optional features and their dependencies into a real feature list using
+ * the GadgetFeatureRegistry, which can be used to construct the javascript for the gadget
+ *
+ * @param Gadget $gadget
+ */
+ private function parseFeatures(Gadget &$gadget) {
+ $found = $missing = array();
+ if (!$this->context->getRegistry()->resolveFeatures(array_merge($gadget->gadgetSpec->requiredFeatures, $gadget->gadgetSpec->optionalFeatures), $found, $missing)) {
+ $requiredMissing = false;
+ foreach ($missing as $featureName) {
+ if (in_array($featureName, $gadget->gadgetSpec->requiredFeatures)) {
+ $requiredMissing = true;
+ break;
+ }
+ }
+ if ($requiredMissing) {
+ throw new GadgetException("Unknown features: ".implode(',', $missing));
+ }
+ }
+ unset($gadget->gadgetSpec->optionalFeatures);
+ unset($gadget->gadgetSpec->requiredFeatures);
+ $gadget->features = $found;
+ }
+
+ /**
+ * Applies the substitutions to the complex types (preloads, user prefs, etc). Simple
+ * types (author, title, etc) are translated on the fly in the gadget's getFoo() functions
+ */
+ private function applySubstitutions(Gadget &$gadget) {
+ // Apply the substitutions to the UserPrefs
+ foreach ($gadget->gadgetSpec->userPrefs as $key => $pref) {
+ $gadget->gadgetSpec->userPrefs[$key]['name'] = $gadget->substitutions->substitute($pref['name']);
+ $gadget->gadgetSpec->userPrefs[$key]['displayName'] = $gadget->substitutions->substitute($pref['displayName']);
+ $gadget->gadgetSpec->userPrefs[$key]['required'] = $gadget->substitutions->substitute($pref['required']);
+ $gadget->gadgetSpec->userPrefs[$key]['datatype'] = $gadget->substitutions->substitute($pref['datatype']);
+ $gadget->gadgetSpec->userPrefs[$key]['defaultValue'] = $gadget->substitutions->substitute($pref['defaultValue']);
+ $gadget->gadgetSpec->userPrefs[$key]['value'] = $gadget->substitutions->substitute($pref['value']);
+ if (isset($pref['enumValues'])) {
+ foreach ($pref['enumValues'] as $enumKey => $enumVal) {
+ $gadget->gadgetSpec->userPrefs[$key]['enumValues'][$enumKey]['value'] = $gadget->substitutions->substitute($enumVal['value']);
+ $gadget->gadgetSpec->userPrefs[$key]['enumValues'][$enumKey]['displayValue'] = $gadget->substitutions->substitute($enumVal['displayValue']);
+ }
+ }
+ }
+ // Apply substitutions to the preloads
+ foreach ($gadget->gadgetSpec->preloads as $url => $preload) {
+ $gadget->gadgetSpec->preloads[$url]['body'] = $gadget->substitutions->substitute($preload['body']);
+ }
+ }
+
+ /**
+ * Seeds the substitutions class with the user prefs, messages, bidi and module id
+ */
+ private function addSubstitutions(Gadget &$gadget) {
+ $gadget->substitutions = new Substitutions();
+ if ($this->token) {
+ $gadget->substitutions->addSubstitution('MODULE', "ID", $this->token->getModuleId());
+ }
+ if ($gadget->gadgetSpec->locales) {
+ $gadget->substitutions->addSubstitutions('MSG', $gadget->gadgetSpec->locales);
+ }
+ $gadget->substitutions->addSubstitution('BIDI', "START_EDGE", $gadget->rightToLeft ? "right" : "left");
+ $gadget->substitutions->addSubstitution('BIDI', "END_EDGE", $gadget->rightToLeft ? "left" : "right");
+ $gadget->substitutions->addSubstitution('BIDI', "DIR", $gadget->rightToLeft ? "rtl" : "ltr");
+ $gadget->substitutions->addSubstitution('BIDI', "REVERSE_DIR", $gadget->rightToLeft ? "ltr" : "rtl");
+ foreach ($gadget->gadgetSpec->userPrefs as $pref) {
+ $gadget->substitutions->addSubstitution('UP', $gadget->substitutions->substitute($pref['name']), $gadget->substitutions->substitute($pref['value']));
+ }
+ }
+ /**
+ * Process the UserPrefs values based on the current context
+ *
+ * @param Gadget $gadget
+ */
+ private function parseUserPrefs(Gadget &$gadget) {
+ foreach ($gadget->gadgetSpec->userPrefs as $key => $pref) {
+ $queryKey = 'up_'.$pref['name'];
+ $gadget->gadgetSpec->userPrefs[$key]['value'] = isset($_GET[$queryKey]) ? trim(urldecode($_GET[$queryKey])) : $pref['defaultValue'];
+ }
+ }
+
+ /**
+ * Merges all matching Message bundles, with a full match (lang and country) having the
+ * highest priority and all/all having the lowest.
+ *
+ * This distills the locales array's back to one array of translations, which is then exposed
+ * through the $gadget->substitutions class
+ *
+ * @param Gadget $gadget
+ */
+ private function mergeLocales(Gadget $gadget) {
+ if (count($gadget->gadgetSpec->locales)) {
+ $contextLocale = $this->context->getLocale();
+ $locales = $gadget->gadgetSpec->locales;
+ $gadget->rightToLeft = false;
+ $full = $partial = $all = null;
+ foreach ($locales as $locale) {
+ if ($locale['lang'] == $contextLocale['lang'] && $locale['country'] == $contextLocale['country']) {
+ $full = $locale['messageBundle'];
+ $gadget->rightToLeft = $locale['languageDirection'] == 'rtl';
+ } elseif ($locale['lang'] == $contextLocale['lang'] && $locale['country'] == 'all') {
+ $partial = $locale['messageBundle'];
+ } elseif ($locale['country'] == 'all' && $locale['lang'] == 'all') {
+ $all = $locale['messageBundle'];
+ }
+ }
+ $gadget->gadgetSpec->locales = array();
+ // array_merge overwrites duplicate keys from param 2 over param 1, so $full takes precedence over partial, and it over all
+ if ($full) $gadget->gadgetSpec->locales = array_merge($full, $gadget->gadgetSpec->locales);
+ if ($partial) $gadget->gadgetSpec->locales = array_merge($partial, $gadget->gadgetSpec->locales);
+ if ($all) $gadget->gadgetSpec->locales = array_merge($all, $gadget->gadgetSpec->locales);
+ }
+ }
+
+ /**
+ * Fetches all remote resources simultaniously using a multiFetchRequest to optimize rendering time.
+ *
+ * The preloads will be json_encoded to their gadget document injection format, and the locales will
+ * be reduced to only the GadgetContext->getLocale matching entries.
+ *
+ * @param Gadget $gadget
+ * @param GadgetContext $context
+ */
+ private function fetchResources(Gadget &$gadget) {
+ $contextLocale = $this->context->getLocale();
+ $unsignedRequests = $unsignedContexts = $signedRequests = array();
+ foreach ($gadget->getLocales() as $key => $locale) {
+ // Only fetch the locales that match the current context's language and country
+ if (($locale['country'] == 'all' && $locale['lang'] == 'all') || ($locale['lang'] == $contextLocale['lang'] && $locale['country'] == 'all') || ($locale['lang'] == $contextLocale['lang'] && $locale['country'] == $contextLocale['country'])) {
+ if (!empty($locale['messages'])) {
+ // locale matches the current context, add it to the requests queue
+ $unsignedRequests[] = $locale['messages'];
+ }
+ } else {
+ // remove any locales that are not applicable to this context
+ unset($gadget->gadgetSpec->locales[$key]);
+ }
+ }
+ // Add preloads to the request queue
+ foreach ($gadget->getPreloads() as $preload) {
+ if (!empty($preload['href'])) {
+ if (!empty($preload['authz']) && $preload['authz'] == 'SIGNED') {
+ $signedRequests[] = $preload['href'];
+ } else {
+ if ($this->token == '') {
+ throw new GadgetException("Signed preloading requested, but no valid security token set");
+ }
+ $unsignedRequests[] = $preload['href'];
+ }
+ }
+ }
+ // Perform the non-signed requests
+ foreach ($unsignedRequests as $key => $requestUrl) {
+ $request = new RemoteContentRequest($requestUrl);
+ $request->createRemoteContentRequestWithUri($requestUrl);
+ $unsignedRequests[$key] = $request;
+ $unsignedContexts[$key] = $this->context;
+ }
+ $responses = array();
+ if (count($unsignedRequests)) {
+ $brc = new BasicRemoteContent();
+ $resps = $brc->multiFetch($unsignedRequests, $unsignedContexts);
+ foreach ($resps as $response) {
+ $responses[$response->getUrl()] = array(
+ 'body' => $response->getResponseContent(),
+ 'rc' => $response->getHttpCode());
+ }
+ }
+ // Perform the signed requests
+ foreach ($signedRequests as $key => $requestUrl) {
+ $request = new RemoteContentRequest($requestUrl);
+ $request->createRemoteContentRequestWithUri($requestUrl);
+ $signedRequests[$key] = $request;
+ $signingFetcherFactory = new SigningFetcherFactory(Config::get("private_key_file"));
+ $fetcher = $signingFetcherFactory->getSigningFetcher(new BasicRemoteContentFetcher(), $this->token);
+ $req = $fetcher->signRequest($requestUrl, 'GET');
+ $req->setNotSignedUri($requestUrl);
+ $signedRequests[] = $req;
+ }
+ if (count($signedRequests)) {
+ $fetcher = $signingFetcherFactory->getSigningFetcher(new BasicRemoteContentFetcher(), $this->token);
+ $resps = $fetcher->multiFetchRequest($signedRequests);
+ foreach ($resps as $response) {
+ $responses[$response->getNotSignedUrl()] = array(
+ 'body' => $response->getResponseContent(),
+ 'rc' => $response->getHttpCode());
+ }
+ }
+ // assign the results to the gadget locales and preloads (using the url as the key)
+ foreach ($gadget->gadgetSpec->locales as $key => $locale) {
+ if (!empty($locale['messages']) && isset($responses[$locale['messages']]) && $responses[$locale['messages']]['rc'] == 200) {
+ $gadget->gadgetSpec->locales[$key]['messageBundle'] = $this->parseMessageBundle($responses[$locale['messages']]['body']);
+ }
+ }
+ $preloads = array();
+ foreach ($gadget->gadgetSpec->preloads as $key => $preload) {
+ if (!empty($preload['href']) && isset($responses[$preload['href']]) && $responses[$preload['href']]['rc'] == 200) {
+ $preloads[$preload['href']] = $responses[$preload['href']];
+ }
+ }
+ $gadget->gadgetSpec->preloads = $preloads;
+ }
+
+ /**
+ * Parses the (remote / fetched) message bundle xml
+ *
+ * @param string $messageBundleData
+ * @return array (MessageBundle)
+ */
+ private function parseMessageBundle($messageBundleData) {
+ libxml_use_internal_errors(true);
+ $doc = new DOMDocument();
+ if (! $doc->loadXML($messageBundleData, LIBXML_NOCDATA)) {
+ $errors = libxml_get_errors();
+ $errorStr = '';
+ foreach ($errors as $error) {
+ $errorStr .= $error . " \n";
+ }
+ libxml_clear_errors();
+ throw new GadgetSpecException("Error parsing gadget xml:\n$errorStr");
+ }
+ $messageBundle = array();
+ if (($messageBundleNode = $doc->getElementsByTagName('messagebundle')) != null && $messageBundleNode->length > 0) {
+ $messageBundleNode = $messageBundleNode->item(0);
+ $messages = $messageBundleNode->getElementsByTagName('msg');
+ foreach ($messages as $msg) {
+ $messageBundle[$msg->getAttribute('name')] = trim($msg->nodeValue);
+ }
+ }
+ return $messageBundle;
+ }
+
+ /**
+ * Fetches the gadget xml for the requested URL using the http fetcher
+ *
+ * @param unknown_type $gadgetUrl
+ * @return string gadget's xml content
+ */
+ private function fetchGadget($gadgetUrl) {
+ $request = new RemoteContentRequest($gadgetUrl);
+ $xml = $this->context->getHttpFetcher()->fetch($request, $this->context);
+ if ($xml->getHttpCode() != '200') {
+ throw new GadgetException("Failed to retrieve gadget content (recieved http code " . $xml->getHttpCode() . ")");
+ }
+ return $xml->getResponseContent();
+ }
+}
Modified: incubator/shindig/trunk/php/src/gadgets/GadgetFeatureRegistry.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/GadgetFeatureRegistry.php?rev=744702&r1=744701&r2=744702&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/GadgetFeatureRegistry.php (original)
+++ incubator/shindig/trunk/php/src/gadgets/GadgetFeatureRegistry.php Sun Feb 15 17:35:05 2009
@@ -1,4 +1,5 @@
<?php
+
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
@@ -18,64 +19,59 @@
* under the License.
*/
+/**
+ * Class that deals with the processing, loading and dep resolving of the gadget features
+ * Features are javascript libraries that provide an API, like 'opensocial' or 'settitle'
+ *
+ */
class GadgetFeatureRegistry {
- private $features = array();
- private $core = array();
+ public $features;
private $coreDone = false;
+ private $coreFeaturs;
public function __construct($featurePath) {
$this->registerFeatures($featurePath);
}
- public function registerFeatures($featurePath) {
- if (empty($featurePath) || $featurePath == null) {
- return;
- }
- $loader = new JsFeatureLoader();
- $jsFeatures = $loader->loadFeatures($featurePath, $this);
- if (! $this->coreDone) {
- foreach ($jsFeatures as $entry) {
- if (strtolower(substr($entry->name, 0, strlen('core'))) == 'core') {
- $this->core[$entry->name] = $entry->name;
- }
- }
- // 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);
+ public function getFeatureContent($feature, GadgetContext $context, $isGadgetContext) {
+ if (!isset($this->features[$feature])) {
+ throw new GadgetException("Invalid feature: ".htmlentities($feature));
+ }
+ $feature = $this->features[$feature];
+ $filesContext = $isGadgetContext ? 'gadgetJs' : 'containerJs';
+ if (!isset($feature[$filesContext])) {
+ // no javascript specified for this context
+ return '';
+ }
+ $ret = '';
+ foreach ($feature[$filesContext] as $entry) {
+ switch ($entry['type']) {
+ case 'URL':
+ $request = new RemoteContentRequest($entry['content']);
+ $context->getHttpFetcher()->fetch($request, $context);
+ if ($request->getHttpCode() == '200') {
+ $ret .= $request->getResponseContent()."\n";
+ }
+ break;
+ case 'FILE':
+ $file = $feature['basePath'] . '/' . $entry['content'];
+ // add jsmin compression here
+ $ret .= file_get_contents($file). "\n";
+ break;
+ case 'INLINE':
+ $ret .= $entry['content'] . "\n";
+ break;
}
- $this->features[$name] = $entry;
- $this->validateFeatureGraph();
}
- return $entry;
+ return $ret;
}
- 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) {
+ public function resolveFeatures($needed, &$resultsFound, &$resultsMissing) {
$resultsFound = array();
$resultsMissing = array();
if (! count($needed)) {
// Shortcut for gadgets that don't have any explicit dependencies.
- $resultsFound = $this->core;
+ $resultsFound = $this->coreFeatures;
return true;
}
foreach ($needed as $featureName) {
@@ -90,50 +86,141 @@
}
private function addFeatureToResults(&$results, $feature) {
- if (in_array($feature->name, $results)) {
+ if (in_array($feature['name'], $results)) {
return;
}
- foreach ($feature->deps as $dep) {
- //TODO: Temporal fix, double check where empty dependencies are being added
- if (! empty($dep)) {
- $this->addFeatureToResults($results, $this->features[$dep]);
- }
+ foreach ($feature['deps'] as $dep) {
+ $this->addFeatureToResults($results, $this->features[$dep]);
+ }
+ if (!in_array($feature['name'], $results)) {
+ $results[] = $feature['name'];
}
- $results[$feature->name] = $feature->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;
+ /**
+ * Loads the features present in the $featurePath
+ *
+ * @param string $featurePath path to scan
+ */
+ private function registerFeatures($featurePath) {
+ $this->features = array();
+ // Load the features from the shindig/features/features.txt file
+ $featuresFile = $featurePath . '/features.txt';
+ if (File::exists($featuresFile)) {
+ $files = explode("\n", file_get_contents($featuresFile));
+ // custom sort, else core.io seems to bubble up before core, which breaks the dep chain order
+ usort($files, array($this, 'sortFeaturesFiles'));
+ foreach ($files as $file) {
+ if (! empty($file) && strpos($file, 'feature.xml') !== false && substr($file, 0, 1) != '#' && substr($file, 0, 2) != '//') {
+ $file = realpath($featurePath . '/../' . trim($file));
+ $feature = $this->processFile($file);
+ $this->features[$feature['name']] = $feature;
+ }
+ }
+ }
+ // Determine the core features
+ $this->coreFeatures = array();
+ foreach ($this->features as $entry) {
+ if (strtolower(substr($entry['name'], 0, strlen('core'))) == 'core') {
+ $this->coreFeatures[$entry['name']] = $entry['name'];
+ }
+ }
+ // And make sure non-core features depend on core.
+ foreach ($this->features as $key => $entry) {
+ if (strtolower(substr($entry['name'], 0, strlen('core'))) != 'core') {
+ $this->features[$key]['deps'] = array_merge($entry['deps'], $this->coreFeatures);
+ }
+ }
+ }
+
+ /**
+ * Loads the feature's xml content
+ *
+ * @param unknown_type $file
+ * @return unknown
+ */
+ private function processFile($file) {
+ $feature = null;
+ if (File::exists($file)) {
+ if (($content = file_get_contents($file))) {
+ $feature = $this->parse($content, dirname($file));
+ }
+ }
+ return $feature;
+ }
+
+ /**
+ * Parses the feature's XML content
+ *
+ * @param string $content
+ * @param string $path
+ * @return feature array
+ */
+ private function parse($content, $path) {
+ $doc = simplexml_load_string($content);
+ $feature = array();
+ $feature['deps'] = array();
+ $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) {
+ $this->processContext($feature, $gadget, false);
+ }
+ foreach ($doc->container as $container) {
+ $this->processContext($feature, $container, true);
+ }
+ foreach ($doc->dependency as $dependency) {
+ $feature['deps'][trim($dependency)] = trim($dependency);
+ }
+ return $feature;
+ }
+
+ /**
+ * Processes the feature's entries
+ *
+ * @param array $feature
+ * @param string $context
+ * @param boolean $isContainer
+ */
+ 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://" || strtolower(substr($content, 0, strlen("https://"))) == "https://") {
+ $type = 'URL';
+ } else {
+ $type = 'FILE';
+ // skip over any java resource files (res://) since we don't support them
+ if (substr($content, 0, 6) == 'res://') {
+ continue;
+ }
+ $content = $content;
+ }
+ }
+ $library = array('type' => $type, 'content' => $content);
+ if ($library != null) {
+ if ($isContainer) {
+ $feature['containerJs'][] = $library;
+ } else {
+ $feature['gadgetJs'][] = $library;
+ }
}
}
- $this->feature = $feature;
- }
-
- public function getName() {
- return $this->name;
- }
-
- public function getDependencies() {
- return $this->deps;
}
- public function getFeature() {
- return $this->feature;
+ private function sortFeaturesFiles($feature1, $feature2) {
+ $feature1 = basename(str_replace('/feature.xml', '', $feature1));
+ $feature2 = basename(str_replace('/feature.xml', '', $feature2));
+ if ($feature1 == $feature2) {
+ return 0;
+ }
+ return ($feature1 < $feature2) ? - 1 : 1;
}
}
-
Added: incubator/shindig/trunk/php/src/gadgets/GadgetSpec.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/GadgetSpec.php?rev=744702&view=auto
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/GadgetSpec.php (added)
+++ incubator/shindig/trunk/php/src/gadgets/GadgetSpec.php Sun Feb 15 17:35:05 2009
@@ -0,0 +1,65 @@
+<?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 GadgetSpec {
+ /**
+ * MD5 checksum of the xml's content
+ *
+ * @var string
+ */
+ public $checksum;
+
+ // Basic and extended ModulePrefs attributes
+ public $title;
+ public $author;
+ public $authorEmail;
+ public $description;
+ public $directoryTitle;
+ public $screenshot;
+ public $thumbnail;
+ public $titleUrl;
+ public $authorAffiliation;
+ public $authorLocation;
+ public $authorPhoto;
+ public $authorAboutme;
+ public $authorQuote;
+ public $authorLink;
+ public $showStats;
+ public $showInDirectory;
+ public $string;
+ public $width;
+ public $height;
+ public $category;
+ public $category2;
+ public $singleton;
+ public $renderInline;
+ public $scaling;
+ public $scrolling;
+
+ public $preloads;
+ public $locales;
+ public $icon;
+ public $optionalFeatures;
+ public $requiredFeatures;
+
+ public $links;
+ public $userPrefs;
+}
Modified: incubator/shindig/trunk/php/src/gadgets/GadgetSpecParser.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/GadgetSpecParser.php?rev=744702&r1=744701&r2=744702&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/GadgetSpecParser.php (original)
+++ incubator/shindig/trunk/php/src/gadgets/GadgetSpecParser.php Sun Feb 15 17:35:05 2009
@@ -18,248 +18,223 @@
* under the License.
*/
-class SpecParserException extends Exception {
+class GadgetSpecException extends Exception {
}
+/**
+ * Parses the XML content into a GadgetSpec object
+ */
class GadgetSpecParser {
-
- public function parse($xml, $context) {
- if (empty($xml)) {
- throw new SpecParserException("Empty XML document");
- }
- // make sure we can generate a detailed error report
+ /**
+ * Parses the $xmlContent into a Gadget class
+ *
+ * @param string $xmlContent
+ */
+ public function parse($xmlContent) {
libxml_use_internal_errors(true);
- if (($doc = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)) == false) {
- $errors = @libxml_get_errors();
- $xmlErrors = '';
+ $doc = new DOMDocument();
+ if (! $doc->loadXML($xmlContent, LIBXML_NOCDATA)) {
+ $errors = libxml_get_errors();
+ $errorStr = '';
foreach ($errors as $error) {
- $xmlErrors .= $this->displayXmlError($error);
- }
- @libxml_clear_errors();
- throw new SpecParserException("<b>Invalid XML Document</b><br/>\n" . $xmlErrors);
- }
- if (count($doc->ModulePrefs) != 1) {
- throw new SpecParserException("Missing or duplicated <ModulePrefs>");
- }
- $gadget = new Gadget($context->getGadgetId(), $context);
- // record Checksum to trace xml version
- $gadget->setChecksum($xml);
- // process ModulePref attributes
- $this->processModulePrefs($gadget, $doc->ModulePrefs);
- if (isset($doc->ModulePrefs->OAuth)) {
- // process OAuthPref attributes
- $this->processOAuthSpec($gadget, $doc->ModulePrefs->OAuth, $context);
- }
- // process UserPrefs, if any
- foreach ($doc->UserPref as $pref) {
- $this->processUserPref($gadget, $pref);
- }
- // Assume gadget v1
- $explicit_profile = false;
- foreach ($doc->Content as $content) {
- $attributes = $content->attributes();
- if (empty($attributes['type'])) {
- throw new SpecParserException("No content type specified!");
- }
- $view = isset($attributes['view']) ? trim($attributes['view']) : '';
- // Note if we find a profile explicity defined
- if (strpos($view, "profile") !== false) {
- $explicit_profile = true;
- }
- }
- foreach ($doc->Content as $content) {
- $attributes = $content->attributes();
- if (empty($attributes['type'])) {
- throw new SpecParserException("No content type specified!");
- }
- $view = isset($attributes['view']) ? trim($attributes['view']) : '';
- // If view isnt defined and we didnt find a profile explicity defined
- if ($view == '') {
- if (! $explicit_profile) {
- $this->processContent($gadget, $content, array(0 => DEFAULT_VIEW));
- }
- // If view isnt defined and a profile was found, this will catch it
- } else {
- $views = explode(',', $view);
- $this->processContent($gadget, $content, $views);
+ $errorStr .= $error . " \n";
}
+ libxml_clear_errors();
+ throw new GadgetSpecException("Error parsing gadget xml:\n$errorStr");
}
- foreach ($doc->ModulePrefs->Preload as $feature) {
- $gadget->preloads[] = new Preload($feature);
- }
- foreach ($doc->ModulePrefs->Require as $feature) {
- $this->processFeature($gadget, $feature, true);
- }
- foreach ($doc->ModulePrefs->Optional as $feature) {
- $this->processFeature($gadget, $feature, false);
- }
- foreach ($doc->ModulePrefs->Icon as $icon) {
- $this->processIcon($gadget, $icon);
- }
+ //TODO: we could do a XSD schema validation here, but both the schema and most of the gadgets seem to have bugs, so it's not really practical yet (and slow)
+ // $doc->schemaValidate('gadget.xsd');
+ $gadget = new GadgetSpec();
+ $gadget->checksum = md5($xmlContent);
+ $this->parseModulePrefs($doc, $gadget);
+ $this->parseLinks($doc, $gadget);
+ $this->parseUserPrefs($doc, $gadget);
+ $this->parseViews($doc, $gadget);
+ //TODO
+ // OAuthService / OAuthSpec
+ // PipelinedData
return $gadget;
}
- private function processModulePrefs(&$gadget, $ModulePrefs) {
- $attributes = $ModulePrefs->attributes();
- if (empty($attributes['title'])) {
- throw new SpecParserException("Missing or empty \"title\" attribute.");
- }
- // Get ModulePrefs base and extended attributes
- // See http://code.google.com/apis/gadgets/docs/gadgets-extended-xsd.html
- // (trim is used here since it not only cleans up the text, but also auto-casts the SimpleXMLElement to a string)
- $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->Link as $link) {
- $gadget->links[] = $this->processLink($link);
- }
- foreach ($ModulePrefs->Locale as $locale) {
- $gadget->localeSpecs[] = $this->processLocale($locale);
- }
- }
-
- private function processLink($link) {
- $attributes = $link->attributes();
- $rel = isset($attributes['rel']) ? trim($attributes['rel']) : '';
- $href = isset($attributes['href']) ? trim($attributes['href']) : '';
- $method = isset($attributes['method']) ? trim($attributes['method']) : 'GET';
- $link = new LinkSpec($rel, $href, $method);
- return $link;
- }
-
- 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';
- $localeMessageBundles = array();
- if ($messageAttr == '') {
- $parser = new MessageBundleParser();
- $localeMessageBundles = $parser->getMessages($locale);
- }
- $locale = new LocaleSpec();
- $locale->rightToLeft = $rightToLeft;
- $locale->url = $messageAttr;
- $locale->localeMessageBundles = $localeMessageBundles;
- $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']) ? $gadget->getSubstitutions()->substitute(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->required = isset($attributes['required']) ? $gadget->getSubstitutions()->substitute(trim($attributes['required'])) : 'false';
- $preference->dataType = isset($attributes['datatype']) && in_array(strtoupper($attributes['datatype']), $preference->DataTypes) ? strtoupper($attributes['datatype']) : 'STRING';
- $preference->defaultValue = isset($attributes['default_value']) ? $gadget->getSubstitutions()->substitute(trim($attributes['default_value'])) : '';
- if (isset($pref->EnumValue)) {
- foreach ($pref->EnumValue as $enum) {
- $attr = $enum->attributes();
- $valueText = trim($attr['value']);
- $displayText = ! empty($attr['display_value']) ? $gadget->getSubstitutions()->substitute(trim($attr['display_value'])) : $valueText;
- $preference->enumValues[$valueText] = $displayText;
- }
- }
- $gadget->userPrefs[] = $preference;
- }
-
- private function processContent(&$gadget, $content, $views) {
- $html = (string)$content; // no trim here since empty lines can have structural meaning, so typecast to string instead
- foreach ($views as $view) {
- $viewSpec = new ViewSpec($view, $content);
- if (! isset($gadget->views[$view])) {
- $viewSpec->content = $html;
- $gadget->views[$view] = $viewSpec;
- } else {
- if ($gadget->views[$view]->getName() == $viewSpec->getName() && $viewSpec->getType() != $gadget->views[$view]->getType()) {
- throw new SpecParserException("You may not mix content " . " types in the same view.");
+ /**
+ * Parse the gadget views
+ *
+ * @param DOMDocument $doc
+ * @param GadgetSpec $gadget
+ */
+ private function parseViews(DOMDocument &$doc, GadgetSpec &$gadget) {
+ $views = $doc->getElementsByTagName('Content');
+ if (! $views || $views->length < 1) {
+ throw new GadgetSpecException("A gadget needs to have at least one view");
+ }
+ $gadget->views = array();
+ foreach ($views as $viewNode) {
+ if ($viewNode->getAttribute('type' == 'url') && $viewNode->getAttribute('href') == null) {
+ throw new GadgetSpecException("Malformed <Content> href value");
+ }
+ foreach (explode(',', $viewNode->getAttribute('view')) as $view) {
+ $view = trim($view);
+ if (isset($gadget->views[$view])) {
+ $gadget->views[$view]['content'] .= $viewNode->nodeValue;
+ } else {
+ $gadget->views[$view] = array('view' => $view, 'type' => strtoupper($viewNode->getAttribute('type')), 'href' => $viewNode->getAttribute('href'), 'preferedHeight' => $viewNode->getAttribute('prefered_height'),
+ 'preferedWidth' => $viewNode->getAttribute('prefered_width'), 'quirks' => $viewNode->getAttribute('quirks'), 'content' => $viewNode->nodeValue);
}
- $gadget->views[$view]->addContent($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>.");
+ /**
+ * Parses the UserPref entries
+ *
+ * @param DOMDocument $doc
+ * @param GadgetSpec $gadget
+ */
+ private function parseUserPrefs(DOMDocument &$doc, GadgetSpec &$gadget) {
+ $gadget->userPrefs = array();
+ if (($userPrefs = $doc->getElementsByTagName('UserPref')) != null) {
+ foreach ($userPrefs as $prefNode) {
+ $pref = array('name' => $prefNode->getAttribute('name'), 'displayName' => $prefNode->getAttribute('display_name'), 'datatype' => strtoupper($prefNode->getAttribute('datatype')), 'defaultValue' => $prefNode->getAttribute('default_value'),
+ 'required' => $prefNode->getAttribute('required'));
+ if ($pref['datatype'] == 'ENUM') {
+ if (($enumValues = $prefNode->getElementsByTagName('EnumValue')) != null) {
+ $enumVals = array();
+ foreach ($enumValues as $enumNode) {
+ $enumVals[] = array('value' => $enumNode->getAttribute('value'), 'displayValue' => $enumNode->getAttribute('display_value'));
+ }
+ }
+ $pref['enumValues'] = $enumVals;
+ }
+ $gadget->userPrefs[] = $pref;
}
- $name = trim($attr['name']);
- $value = trim($param);
- $featureSpec->params[$name] = $value;
}
- $gadget->requires[$featureSpec->name] = $featureSpec;
- }
-
- private function processIcon(Gadget &$gadget, $icon) {
- $attributes = $icon->attributes();
- $iconSpec = new Icon();
- $iconSpec->content = (string)(trim($icon));
- $iconSpec->mode = isset($attributes['mode']) ? trim($attributes['mode']) : '';
- $iconSpec->type = isset($attributes['type']) ? trim($attributes['type']) : '';
- $gadget->icons[] = $iconSpec;
}
- private function processOAuthSpec(Gadget &$gadget, $OAuthSpec) {
- $oauthSpec = new OAuthSpec($OAuthSpec->Service);
- $gadget->setOAuthSpec($oauthSpec);
- }
-
- private function displayXmlError($error) {
- $return = '';
- switch ($error->level) {
- case LIBXML_ERR_WARNING:
- $return .= "Warning [{$error->code}]: ";
- break;
- case LIBXML_ERR_ERROR:
- $return .= "Error [{$error->code}]: ";
- break;
- case LIBXML_ERR_FATAL:
- $return .= "Fatal Error [{$error->code}]: ";
- break;
- }
- $return .= trim($error->message) . ", Line: {$error->line}" . " Column: {$error->column}";
- if ($error->file) {
- $return .= "File: {$error->file}";
+ /**
+ * Parses the link spec elements
+ *
+ * @param DOMDocument $doc
+ * @param GadgetSpec $gadget
+ */
+ private function parseLinks(DOMDocument &$doc, GadgetSpec &$gadget) {
+ $gadget->links = array();
+ if (($links = $doc->getElementsByTagName('link')) != null) {
+ foreach ($links as $linkNode) {
+ $gadget->links[] = array('rel' => $linkNode->getAttribute('rel'), 'href' => $linkNode->getAttribute('href'), 'method' => strtoupper($linkNode->getAttribute('method')));
+ }
+ }
+ }
+
+ /**
+ * Parses the ModulePrefs section of the xml structure. The ModulePrefs
+ * section is required, so if it's missing or if there's 2 an GadgetSpecException will be thrown.
+ *
+ * This function also parses the ModulePref's child elements (Icon, Features, Preload and Locale)
+ *
+ * @param DOMDocument $doc
+ */
+ private function parseModulePrefs(DOMDocument &$doc, GadgetSpec &$gadget) {
+ $modulePrefs = $doc->getElementsByTagName("ModulePrefs");
+ if ($modulePrefs->length < 1) {
+ throw new GadgetSpecException("Missing ModulePrefs block");
+ } elseif ($modulePrefs->length > 1) {
+ throw new GadgetSpecException("More then one ModulePrefs block found");
+ }
+ $modulePrefs = $modulePrefs->item(0);
+ // parse the ModulePrefs attributes
+ $knownAttributes = array('title', 'author', 'authorEmail', 'description', 'directoryTitle', 'screenshot', 'thumbnail', 'titleUrl', 'authorAffiliation', 'authorLocation', 'authorPhoto', 'authorAboutme', 'authorQuote', 'authorLink', 'showStats',
+ 'showInDirectory', 'string', 'width', 'height', 'category', 'category2', 'singleton', 'renderInline', 'scaling', 'scrolling');
+ foreach ($modulePrefs->attributes as $key => $attribute) {
+ $attrValue = trim($attribute->value);
+ // var format conversion from directory_title => directoryTitle
+ $attrKey = str_replace(' ', '', ucwords(str_replace('_', ' ', $key)));
+ $attrKey[0] = strtolower($attrKey[0]);
+ if (in_array($attrKey, $knownAttributes)) {
+ $gadget->$attrKey = $attrValue;
+ }
+ }
+ // And parse the child nodes
+ $this->parseIcon($modulePrefs, $gadget);
+ $this->parseFeatures($modulePrefs, $gadget);
+ $this->parsePreloads($modulePrefs, $gadget);
+ $this->parseLocales($modulePrefs, $gadget);
+ }
+
+ /**
+ * Parses the (optional) Icon element, returns a Icon class or null
+ *
+ * @param DOMElement $modulePrefs
+ * @param Gadget $gadget
+ */
+ private function parseIcon(DOMElement &$modulePrefs, GadgetSpec &$gadget) {
+ if (($iconNodes = $modulePrefs->getElementsByTagName('Icon')) != null) {
+ if ($iconNodes->length > 1) {
+ throw new GadgetSpecException("A gadget can only have one Icon element");
+ } elseif ($iconNodes->length == 1) {
+ $icon = $iconNodes->item(0);
+ $gadget->icon = $icon->nodeValue;
+ }
+ }
+ }
+
+ /**
+ * Parses the Required and Optional feature entries in the ModulePrefs
+ *
+ * @param DOMElement $modulePrefs
+ * @param Gadget $gadget
+ */
+ private function parseFeatures(DOMElement &$modulePrefs, GadgetSpec &$gadget) {
+ $gadget->requiredFeatures = $gadget->optionalFeatures = array();
+ if (($requiredNodes = $modulePrefs->getElementsByTagName('Require')) != null) {
+ foreach ($requiredNodes as $requiredFeature) {
+ $gadget->requiredFeatures[] = $requiredFeature->getAttribute('feature');
+ }
+ }
+ if (($optionalNodes = $modulePrefs->getElementsByTagName('Optional')) != null) {
+ foreach ($optionalNodes as $optionalFeature) {
+ $gadget->optionalFeatures[] = $optionalFeature->getAttribute('feature');
+ }
+ }
+ }
+
+ /**
+ * Parses the preload elements
+ *
+ * @param DOMElement $modulePrefs
+ * @param Gadget $gadget
+ */
+ private function parsePreloads(DOMElement &$modulePrefs, GadgetSpec &$gadget) {
+ $gadget->preloads = array();
+ if (($preloadNodes = $modulePrefs->getElementsByTagName('Preload')) != null) {
+ foreach ($preloadNodes as $node) {
+ $gadget->preloads[] = array('href' => $node->getAttribute('href'), 'authz' => strtoupper($node->getAttribute('authz')), 'signViewer' => $node->getAttribute('sign_viewer'), 'signOwner' => $node->getAttribute('sign_owner'));
+ }
+ }
+ }
+
+ /**
+ * Parses the Locale (message bundle) entries
+ *
+ * @param DOMElement $modulePrefs
+ * @param Gadget $gadget
+ */
+ private function parseLocales(DOMElement &$modulePrefs, GadgetSpec &$gadget) {
+ $gadget->locales = array();
+ if (($localeNodes = $modulePrefs->getElementsByTagName('Locale')) != null) {
+ foreach ($localeNodes as $node) {
+ $messageBundle = array();
+ if (($messageBundleNode = $node->getElementsByTagName('messagebundle')) != null && $messageBundleNode->length > 0) {
+ // parse inlined messages
+ $messageBundleNode = $messageBundleNode->item(0);
+ $messages = $messageBundleNode->getElementsByTagName('msg');
+ foreach ($messages as $msg) {
+ $messageBundle[$msg->getAttribute('name')] = trim($msg->nodeValue);
+ }
+ }
+ $lang = $node->getAttribute('lang') == '' ? 'all' : strtolower($node->getAttribute('lang'));
+ $country = $node->getAttribute('country') == '' ? 'all' : strtoupper($node->getAttribute('country'));
+ $gadget->locales[] = array('lang' => $lang, 'country' => $country, 'messages' => $node->getAttribute('messages'), 'languageDirection' => $node->getAttribute('language_direction'), 'messageBundle' => $messageBundle);
+ }
}
- return "$return\n";
}
}