You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shindig.apache.org by ch...@apache.org on 2009/07/29 23:01:24 UTC
svn commit: r799076 - in /incubator/shindig/trunk/php/src/gadgets:
GadgetFactory.php GadgetSpec.php GadgetSpecParser.php
render/GadgetBaseRenderer.php render/GadgetHtmlRenderer.php
templates/TemplateParser.php
Author: chabotc
Date: Wed Jul 29 21:01:23 2009
New Revision: 799076
URL: http://svn.apache.org/viewvc?rev=799076&view=rev
Log:
- Add support for <Param name="requireLibrary">http://www.example.com/templates.xml</Param> external libraries
- Honor the disableAutoProcessing param
- Support IF attributes on OSML nodes (repeat is still todo)
- lots and lots of small fixes
Modified:
incubator/shindig/trunk/php/src/gadgets/GadgetFactory.php
incubator/shindig/trunk/php/src/gadgets/GadgetSpec.php
incubator/shindig/trunk/php/src/gadgets/GadgetSpecParser.php
incubator/shindig/trunk/php/src/gadgets/render/GadgetBaseRenderer.php
incubator/shindig/trunk/php/src/gadgets/render/GadgetHtmlRenderer.php
incubator/shindig/trunk/php/src/gadgets/templates/TemplateParser.php
Modified: incubator/shindig/trunk/php/src/gadgets/GadgetFactory.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/GadgetFactory.php?rev=799076&r1=799075&r2=799076&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/GadgetFactory.php (original)
+++ incubator/shindig/trunk/php/src/gadgets/GadgetFactory.php Wed Jul 29 21:01:23 2009
@@ -235,6 +235,15 @@
}
}
}
+ // Add template libraries to the request queue
+ if ($gadget->gadgetSpec->templatesRequireLibraries) {
+ foreach ($gadget->gadgetSpec->templatesRequireLibraries as $libraryUrl) {
+ $request = new RemoteContentRequest($libraryUrl);
+ $request->createRemoteContentRequestWithUri($libraryUrl);
+ $request->getOptions()->ignoreCache = $this->context->getIgnoreCache();
+ $unsignedRequests[] = $request;
+ }
+ }
}
// Perform the non-signed requests
$responses = array();
@@ -264,13 +273,24 @@
$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[] = array_merge(array('id' => $preload['href']), $responses[$preload['href']]);
- }
+ if (! $gadget->gadgetContext instanceof MetadataGadgetContext) {
+ $preloads = array();
+ foreach ($gadget->gadgetSpec->preloads as $key => $preload) {
+ if (! empty($preload['href']) && isset($responses[$preload['href']]) && $responses[$preload['href']]['rc'] == 200) {
+ $preloads[] = array_merge(array('id' => $preload['href']), $responses[$preload['href']]);
+ }
+ }
+ $gadget->gadgetSpec->preloads = $preloads;
+ if ($gadget->gadgetSpec->templatesRequireLibraries) {
+ $requiredLibraries = array();
+ foreach ($gadget->gadgetSpec->templatesRequireLibraries as $key => $libraryUrl) {
+ if (isset($responses[$libraryUrl]) && $responses[$libraryUrl]['rc'] == 200) {
+ $requiredLibraries[$libraryUrl] = $responses[$libraryUrl]['body'];
+ }
+ }
+ $gadget->gadgetSpec->templatesRequireLibraries = $requiredLibraries;
+ }
}
- $gadget->gadgetSpec->preloads = $preloads;
}
/**
Modified: incubator/shindig/trunk/php/src/gadgets/GadgetSpec.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/GadgetSpec.php?rev=799076&r1=799075&r2=799076&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/GadgetSpec.php (original)
+++ incubator/shindig/trunk/php/src/gadgets/GadgetSpec.php Wed Jul 29 21:01:23 2009
@@ -64,4 +64,8 @@
public $userPrefs;
public $rewrite = null;
public $oauth = null;
+
+ // used to track os-templating
+ public $templatesRequireLibraries = false;
+ public $templatesDisableAutoProcessing = false;
}
Modified: incubator/shindig/trunk/php/src/gadgets/GadgetSpecParser.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/GadgetSpecParser.php?rev=799076&r1=799075&r2=799076&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/GadgetSpecParser.php (original)
+++ incubator/shindig/trunk/php/src/gadgets/GadgetSpecParser.php Wed Jul 29 21:01:23 2009
@@ -1,4 +1,5 @@
<?php
+
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
@@ -35,7 +36,7 @@
libxml_use_internal_errors(true);
$doc = new DOMDocument();
if (! $doc->loadXML($xmlContent, LIBXML_NOCDATA)) {
- throw new GadgetSpecException("Error parsing gadget xml:\n".XmlError::getErrors($xmlContent));
+ throw new GadgetSpecException("Error parsing gadget xml:\n" . XmlError::getErrors($xmlContent));
}
//TODO: we could do a XSD schema validation here, but both the schema and most of the gadgets seem to have some form of schema
// violatons, so it's not really practical yet (and slow)
@@ -79,10 +80,21 @@
if (isset($gadget->views[$view])) {
$gadget->views[$view]['content'] .= $viewNode->nodeValue;
} else {
- $gadget->views[$view] = array('view' => $view, 'type' => $type, 'href' => $href, 'preferedHeight' => $viewNode->getAttribute('prefered_height'), 'preferedWidth' => $viewNode->getAttribute('prefered_width'),
- 'quirks' => $viewNode->getAttribute('quirks'), 'content' => $viewNode->nodeValue, 'authz' => $viewNode->getAttribute('authz'), 'oauthServiceName' => $viewNode->getAttribute('oauth_service_name'),
- 'oauthTokenName' => $viewNode->getAttribute('oauth_token_name'), 'oauthRequestToken' => $viewNode->getAttribute('oauth_request_token'), 'oauthRequestTokenSecret' => $viewNode->getAttribute('oauth_request_token_secret'),
- 'signOwner' => $viewNode->getAttribute('sign_owner'), 'signViewer' => $viewNode->getAttribute('sign_viewer'), 'refreshInterval' => $viewNode->getAttribute('refresh_interval'), 'dataPipelining' => $dataPipeliningRequests);
+ $gadget->views[$view] = array('view' => $view, 'type' => $type,
+ 'href' => $href,
+ 'preferedHeight' => $viewNode->getAttribute('prefered_height'),
+ 'preferedWidth' => $viewNode->getAttribute('prefered_width'),
+ 'quirks' => $viewNode->getAttribute('quirks'),
+ 'content' => $viewNode->nodeValue,
+ 'authz' => $viewNode->getAttribute('authz'),
+ 'oauthServiceName' => $viewNode->getAttribute('oauth_service_name'),
+ 'oauthTokenName' => $viewNode->getAttribute('oauth_token_name'),
+ 'oauthRequestToken' => $viewNode->getAttribute('oauth_request_token'),
+ 'oauthRequestTokenSecret' => $viewNode->getAttribute('oauth_request_token_secret'),
+ 'signOwner' => $viewNode->getAttribute('sign_owner'),
+ 'signViewer' => $viewNode->getAttribute('sign_viewer'),
+ 'refreshInterval' => $viewNode->getAttribute('refresh_interval'),
+ 'dataPipelining' => $dataPipeliningRequests);
}
}
}
@@ -98,13 +110,18 @@
$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'),
+ $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'));
+ $enumVals[] = array(
+ 'value' => $enumNode->getAttribute('value'),
+ 'displayValue' => $enumNode->getAttribute('display_value'));
}
}
$pref['enumValues'] = $enumVals;
@@ -124,7 +141,9 @@
$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')));
+ $gadget->links[] = array('rel' => $linkNode->getAttribute('rel'),
+ 'href' => $linkNode->getAttribute('href'),
+ 'method' => strtoupper($linkNode->getAttribute('method')));
}
}
}
@@ -146,8 +165,11 @@
}
$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');
+ $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
@@ -193,6 +215,11 @@
if (($requiredNodes = $modulePrefs->getElementsByTagName('Require')) != null) {
foreach ($requiredNodes as $requiredFeature) {
$gadget->requiredFeatures[] = $requiredFeature->getAttribute('feature');
+ if ($requiredFeature->getAttribute('feature') == 'content-rewrite') {
+ $this->parseContentRewrite($requiredFeature, $gadget);
+ } elseif ($requiredFeature->getAttribute('feature') == 'opensocial-templates') {
+ $this->parseOpenSocialTemplates($requiredFeature, $gadget);
+ }
}
}
if (($optionalNodes = $modulePrefs->getElementsByTagName('Optional')) != null) {
@@ -201,6 +228,8 @@
// Content-rewrite is a special case since it has Params as child nodes
if ($optionalFeature->getAttribute('feature') == 'content-rewrite') {
$this->parseContentRewrite($optionalFeature, $gadget);
+ } elseif ($optionalFeature->getAttribute('feature') == 'opensocial-templates') {
+ $this->parseOpenSocialTemplates($optionalFeature, $gadget);
}
}
}
@@ -256,6 +285,33 @@
}
/**
+ * Parses the opensocial-template params (if any), supported params are:
+ * <Require feature="opensocial-templates">
+ * <Param name="requireLibrary">http://www.example.com/templates.xml</Param>
+ * <Param name="disableAutoProcessing">false</Param>
+ * </Require>
+ * @param DOMElement $feature
+ * @param GadgetSpec $gadget
+ */
+ private function parseOpenSocialTemplates(DOMElement $feature, GadgetSpec &$gadget) {
+ $requireLibraries = array();
+ if (($paramNodes = $feature->getElementsByTagName('Param')) != null) {
+ foreach ($paramNodes as $param) {
+ $paramName = $param->getAttribute('name');
+ $paramValue = trim($param->nodeValue);
+ if ($paramName == 'disableAutoProcessing') {
+ $gadget->templatesDisableAutoProcessing = $paramValue != 'false';
+ } elseif ($paramName == 'requireLibrary') {
+ $requireLibraries[] = $paramValue;
+ }
+ }
+ }
+ if (count($requireLibraries)) {
+ $gadget->templatesRequireLibraries = $requireLibraries;
+ }
+ }
+
+ /**
* Parses the content-rewrite feature's params, possible params entries are:
* <Param name="expires">86400</Param>
* <Param name="include-url">*</Param>
@@ -311,7 +367,10 @@
$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'));
+ $gadget->preloads[] = array('href' => $node->getAttribute('href'),
+ 'authz' => strtoupper($node->getAttribute('authz')),
+ 'signViewer' => $node->getAttribute('sign_viewer'),
+ 'signOwner' => $node->getAttribute('sign_owner'));
}
}
}
@@ -337,7 +396,10 @@
}
$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);
+ $gadget->locales[] = array('lang' => $lang, 'country' => $country,
+ 'messages' => $node->getAttribute('messages'),
+ 'languageDirection' => $node->getAttribute('language_direction'),
+ 'messageBundle' => $messageBundle);
}
}
}
Modified: incubator/shindig/trunk/php/src/gadgets/render/GadgetBaseRenderer.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/render/GadgetBaseRenderer.php?rev=799076&r1=799075&r2=799076&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/render/GadgetBaseRenderer.php (original)
+++ incubator/shindig/trunk/php/src/gadgets/render/GadgetBaseRenderer.php Wed Jul 29 21:01:23 2009
@@ -20,10 +20,6 @@
require_once 'src/gadgets/templates/DataPipelining.php';
-
-//TODO check if the opensocial-templates feature has disableAutoProcessing = true as param, if so don't
-//TODO if all templates were processed server side, also remove the templates with tag="foo" params to clean up the output
-
class EmptyClass {
}
@@ -70,6 +66,7 @@
* so javascript can take care of them
*/
public function addTemplates($content) {
+ // If $this->gadget->gadgetSpec->templatesDisableAutoProcessing == true, unparsedTemplates will be empty, so the setting is ignored here
if (count($this->unparsedTemplates)) {
foreach ($this->unparsedTemplates as $key => $val) {
$content = str_replace("<template_$key></template_$key>", $val . "\n", $content);
@@ -88,6 +85,10 @@
* @param string $content html to parse
*/
public function parseTemplates($content) {
+ if ($this->gadget->gadgetSpec->templatesDisableAutoProcessing) {
+ // The only code path to this location is if content-rewriting is enabled but disableAutoProcessing = true
+ return null;
+ }
$osTemplates = array();
$osDataRequests = array();
// First extract all the os-data tags, and execute those in a single combined request, saves latency
@@ -105,27 +106,31 @@
preg_match_all('/(<script.*type="text\/(os-template)".*>)(.*)(<\/script>)/imxsU', $content, $osTemplates);
$templateLibrary = false;
if (count($osTemplates[0])) {
- // only load the template parser if there's any templates in the gadget content
- require_once 'src/gadgets/templates/TemplateParser.php';
+ // only load the template parser if there's any templates in the gadget content
+ require_once 'src/gadgets/templates/TemplateParser.php';
require_once 'src/gadgets/templates/TemplateLibrary.php';
- $templateLibrary = new TemplateLibrary($this->gadget->gadgetContext);
- }
- foreach ($osTemplates[0] as $match) {
- if (($renderedTemplate = $this->renderTemplate($match, $templateLibrary)) !== false) {
- // Template was rendered, insert the rendered html into the document
- $content = str_replace($match, $renderedTemplate, $content);
- } else {
+ $templateLibrary = new TemplateLibrary($this->gadget->gadgetContext);
+ if ($this->gadget->gadgetSpec->templatesRequireLibraries) {
+ foreach ($this->gadget->gadgetSpec->templatesRequireLibraries as $library) {
+ $templateLibrary->addTemplateLibrary($library);
+ }
+ }
+ foreach ($osTemplates[0] as $match) {
+ if (($renderedTemplate = $this->renderTemplate($match, $templateLibrary)) !== false) {
+ // Template was rendered, insert the rendered html into the document
+ $content = str_replace($match, $renderedTemplate, $content);
+ } else {
/*
* The template could not be rendered, this could happen because:
* - @require is present, and at least one of the required pieces of data is unavailable
* - @name is present
* - @autoUpdate == true
- * - disableAutoProcessing param on the opensocial-templates feature is true
* So set a magic marker (<template_$index>) that after the dom document parsing will be replaced with the original script content
*/
- $index = count($this->unparsedTemplates);
- $this->unparsedTemplates[$index] = $match;
- $content = str_replace($match, "<template_$index></template_$index>", $content);
+ $index = count($this->unparsedTemplates);
+ $this->unparsedTemplates[$index] = $match;
+ $content = str_replace($match, "<template_$index></template_$index>", $content);
+ }
}
}
return $content;
@@ -221,25 +226,26 @@
}
// if $childNode->tag exists, add to global $templateLibraries array, else parse normally
$childNodeTag = $childNode->getAttribute('tag');
- if (!empty($childNodeTag)) {
- if (isset($this->templateLibraries[$childNode->getAttribute('tag')])) {
- throw new ExpressionException("Template ".htmlentities($childNode->getAttribute('tag'))." was already defined");
- }
- $templateLibrary->addTemplateByNode($childNode);
+ if (! empty($childNodeTag)) {
+ if (isset($this->templateLibraries[$childNode->getAttribute('tag')])) {
+ throw new ExpressionException("Template " . htmlentities($childNode->getAttribute('tag')) . " was already defined");
+ }
+ $templateLibrary->addTemplateByNode($childNode);
} else {
- // Everything checked out, proceeding to render the template
- $parser = new TemplateParser();
- $parser->process($childNode, $this->dataContext, $templateLibrary);
- // unwrap the output, ie we only want the script block's content and not the main <script></script> node
- $output = new DOMDocument(null, 'utf-8');
- foreach ($childNode->childNodes as $node) {
- $outNode = $output->importNode($node, true);
- $output->appendChild($outNode);
- }
- // Restore single tags to their html variant, and remove the xml header
- $ret = str_replace(array(
- '<?xml version="" encoding="utf-8"?>', '<br/>'), array('', '<br>'), $output->saveXML());
- return $ret;
+ // Everything checked out, proceeding to render the template
+ $parser = new TemplateParser();
+ $parser->process($childNode, $this->dataContext, $templateLibrary);
+ // unwrap the output, ie we only want the script block's content and not the main <script></script> node
+ $output = new DOMDocument(null, 'utf-8');
+ foreach ($childNode->childNodes as $node) {
+ $outNode = $output->importNode($node, true);
+ $output->appendChild($outNode);
+ }
+ // Restore single tags to their html variant, and remove the xml header
+ $ret = str_replace(array(
+ '<?xml version="" encoding="utf-8"?>', '<br/>'), array('',
+ '<br>'), $output->saveXML());
+ return $ret;
}
}
return false;
Modified: incubator/shindig/trunk/php/src/gadgets/render/GadgetHtmlRenderer.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/render/GadgetHtmlRenderer.php?rev=799076&r1=799075&r2=799076&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/render/GadgetHtmlRenderer.php (original)
+++ incubator/shindig/trunk/php/src/gadgets/render/GadgetHtmlRenderer.php Wed Jul 29 21:01:23 2009
@@ -43,7 +43,7 @@
$domRewrite = false;
if (isset($gadget->gadgetSpec->rewrite) || Config::get('rewrite_by_default')) {
$domRewrite = true;
- } elseif (strpos($view['content'], 'text/os-data') !== false || strpos($view['content'], 'text/os-template') !== false) {
+ } elseif ((strpos($view['content'], 'text/os-data') !== false || strpos($view['content'], 'text/os-template') !== false) && ($gadget->gadgetSpec->templatesDisableAutoProcessing == false)) {
$domRewrite = true;
}
if (!$domRewrite) {
Modified: incubator/shindig/trunk/php/src/gadgets/templates/TemplateParser.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/templates/TemplateParser.php?rev=799076&r1=799075&r2=799076&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/templates/TemplateParser.php (original)
+++ incubator/shindig/trunk/php/src/gadgets/templates/TemplateParser.php Wed Jul 29 21:01:23 2009
@@ -18,11 +18,9 @@
* under the License.
*/
-//TODO os:repeat (and <foo repeat="" var="">) has a var="foo" param that hasn't been implmemented yet
-//TODO for some reason the OSML spec stats you have to <Require feature="osml"> to use the os:Name etc tags yet no such feature exists, and for the code path's here it's not required at all..
+//TODO support repeat tags on OSML tags, ie this should work: <os:Html repeat="${Bar}" />
+//TODO support os:render
//TODO remove the os-templates javascript if all the templates are rendered on the server (saves many Kb's in gadget size)
-//TODO support for OSML tag functions and extensions (os:render, osx:flash, osx:parsejson, etc)
-//TODO support os-template tags on OSML tags, ie this should work: <os:Html if="${Foo}" repeat="${Bar}" />
require_once 'ExpressionParser.php';
@@ -181,9 +179,19 @@
}
// Make sure the repeat variable doesn't show up in the cloned nodes (otherwise it would infinit recurse on this->parseNode())
$node->removeAttribute('repeat');
+ // Is a named var requested?
+ $variableName = $node->getAttribute('var') ? trim($node->getAttribute('var')) : false;
+ // Store the current 'Cur', index and count state, we might be in a nested repeat loop
+ $previousCount = isset($this->dataContext['Context']['Count']) ? $this->dataContext['Context']['Count'] : null;
+ $previousIndex = isset($this->dataContext['Context']['Index']) ? $this->dataContext['Context']['Index'] : null;
+ $previousCur = $this->dataContext['Cur'];
// For information on the loop context, see http://opensocial-resources.googlecode.com/svn/spec/0.9/OpenSocial-Templating.xml#rfc.section.10.1
$this->dataContext['Context']['Count'] = count($expressionResult);
foreach ($expressionResult as $index => $entry) {
+ if ($variableName) {
+ // this is cheating a little since we're not putting it on the top level scope, the variable resolver will check 'Cur' first though so myVar.Something will still resolve correctly
+ $this->dataContext['Cur'][$variableName] = $entry;
+ }
$this->dataContext['Cur'] = $entry;
$this->dataContext['Context']['Index'] = $index;
// Clone this node and it's children
@@ -193,11 +201,18 @@
// And parse it (using the global + loop context)
$this->parseNode($newNode, true);
}
- // Remove the original (unparsed) node
- // And remove the loop data context entries
- $this->dataContext['Cur'] = array();
- unset($this->dataContext['Context']['Index']);
- unset($this->dataContext['Context']['Count']);
+ // Restore our previous data context state
+ $this->dataContext['Cur'] = $previousCur;
+ if ($previousCount) {
+ $this->dataContext['Context']['Count'] = $previousCount;
+ } else {
+ unset($this->dataContext['Context']['Count']);
+ }
+ if ($previousIndex) {
+ $this->dataContext['Context']['Index'] = $previousIndex;
+ } else {
+ unset($this->dataContext['Context']['Index']);
+ }
return $node;
break;
@@ -285,6 +300,12 @@
*/
private function parseOsmlNode(DOMNode &$node) {
$tagName = strtolower($node->tagName);
+ if (!$this->checkIf($node)) {
+ // If the OSML tag contains an if attribute and the expression evaluates to false
+ // flag it for removal and don't process it
+ return $node;
+ }
+
switch ($tagName) {
/****** Control statements ******/
@@ -300,9 +321,19 @@
if (! is_array($expressionResult)) {
throw new ExpressionException("Can't repeat on a singular var");
}
+ // Store the current 'Cur', index and count state, we might be in a nested repeat loop
+ $previousCount = isset($this->dataContext['Context']['Count']) ? $this->dataContext['Context']['Count'] : null;
+ $previousIndex = isset($this->dataContext['Context']['Index']) ? $this->dataContext['Context']['Index'] : null;
+ $previousCur = $this->dataContext['Cur'];
+ // Is a named var requested?
+ $variableName = $node->getAttribute('var') ? trim($node->getAttribute('var')) : false;
// For information on the loop context, see http://opensocial-resources.googlecode.com/svn/spec/0.9/OpenSocial-Templating.xml#rfc.section.10.1
$this->dataContext['Context']['Count'] = count($expressionResult);
foreach ($expressionResult as $index => $entry) {
+ if ($variableName) {
+ // this is cheating a little since we're not putting it on the top level scope, the variable resolver will check 'Cur' first though so myVar.Something will still resolve correctly
+ $this->dataContext['Cur'][$variableName] = $entry;
+ }
$this->dataContext['Cur'] = $entry;
$this->dataContext['Context']['Index'] = $index;
foreach ($node->childNodes as $childNode) {
@@ -311,10 +342,19 @@
$this->parseNode($newNode);
}
}
- $node->parentNode->removeChild($node);
- $this->dataContext['Cur'] = array();
- unset($this->dataContext['Context']['Index']);
- unset($this->dataContext['Context']['Count']);
+ // Restore our previous data context state
+ $this->dataContext['Cur'] = $previousCur;
+ if ($previousCount) {
+ $this->dataContext['Context']['Count'] = $previousCount;
+ } else {
+ unset($this->dataContext['Context']['Count']);
+ }
+ if ($previousIndex) {
+ $this->dataContext['Context']['Index'] = $previousIndex;
+ } else {
+ unset($this->dataContext['Context']['Index']);
+ }
+ return $node;
break;
case 'os:if':
@@ -350,14 +390,14 @@
case 'os:peopleselector':
$this->parseLibrary('os:PeopleSelector', $node);
-
break;
case 'os:html':
if (! $node->getAttribute('code')) {
throw new ExpressionException("Invalid os:Html tag, missing code attribute");
}
- $node->parentNode->replaceChild($node->ownerDocument->createTextNode($node->getAttribute('code')), $node);
+ $node->parentNode->insertBefore($node->ownerDocument->createTextNode($node->getAttribute('code')), $node);
+ return $node;
break;
case 'os:render':
@@ -390,4 +430,24 @@
}
return false;
}
+
+ /**
+ * Misc function that checks if the OSML tag $node has an if attribute, returns
+ * true if the expression is true or no if attribute is set
+ *
+ * @param DOMElement $node
+ */
+ private function checkIf(DOMElement &$node) {
+ if (($if = $node->getAttribute('if'))) {
+ $expressions = array();
+ preg_match_all('/(\$\{)(.*)(\})/imsxU', $if, $expressions);
+ if (! count($expressions[2])) {
+ throw new ExpressionException("Invalid os:If tag, missing condition expression");
+ }
+ $expression = $expressions[2][0];
+ $expressionResult = ExpressionParser::evaluate($expression, $this->dataContext);
+ return $expressionResult ? true : false;
+ }
+ return true;
+ }
}