OpenSocialDraft

Recent site activity

OpenSocial Templating


Introduction

What is OpenSocial Template?

OpenSocial Template (OST) provides a declarative way for gadget developers to create templates by copying and pasting HTML, then making minor modifications to that HTML. Because OST is part of OpenSocial, it also provides a means to bind to the OpenSocial APIs and custom, developer defined variables. OST accomplishes this through tag libraries and special markup.

The OpenSocial Template spec is composed of three pieces:

  • OpenSocial Template Part 1, Template Processing: Specifies how templates are processed and used by OpenSocial containers.
  • OpenSocial Template Part 2, Standard Tags: Specifies the tag library to be implemented by OpenSocial Containers.
  • OpenSocial Template Part 3, Data Pipelining: Specifies how templates make use of data pipelining.

These three sets of features are deeply intertwined. In order to adequately support the templating functionality, containers need to support all aspects to be truly useful to OpenSocial application developers.

Terminology

The terminology used to describe Open Social Templates is defined in the body of this specification. The key words MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, MAY, and OPTIONAL, when EMPHASIZED, are to be interpreted as described in [#rfc2119 [IETF RFC 2119]]. In addition, the terms defined in the following list are used in building those definitions and in describing the actions of an OSML processor:

  • DataRequest Markup: A declarative syntax for creating, parameterizing, and issuing DataRequests. There will be a one-to-one mapping between possible DataRequest implementations and DataRequest Markup implementations.
  • OpenSocial Templating: A way to define UI output through reusable chunks of xml. A way to standardize UI elements between Containers.
  • JavaScript/JS: The term JavaScript or JS is used to refer to what is more commonly known as [#ECMA262 [ECMAScript]].

Limitations

An OST implementation can fetch, inject, cache, and store data requests for the application before any client side code executes. OST does not represent a full replacement for JavaScript. OST assumes that applications can fallback to JavaScript whenever a combination of application and container cannot make use of a given OST feature.

Dependencies on Other Specifications

Other specifications on which this one depends are listed in Normative References.

Gadget Feature Name

OST creates a new feature name for use in the Gadgets Specification: opensocial-templates. When a Module requires OST, the Module must contain this XML:

<ModulePrefs>
<Require feature="opensocial-templates"/>
</ModulePrefs>

A common usage of the feature would be with other OpenSocial features. Example:

<ModulePrefs title="Hello World">
<Require feature="opensocial-0.9"/>
<!-- Allows templates -->
<Require feature="opensocial-templates"/>
<!-- Allows multiple views to be defined and thus rendered client or server -->
<Require feature="views"/>
</ModulePrefs>

Template Format

The canonical template format is a well-formed XML document. This document can include HTML tags which will be output directly when rendering the template, and custom tags which will be evaluated when rendering the template. Example:

<Template>
<div style="font-size: 20px">Hello world!</div>
</Template>

A common use case will be embedding this XML into an HTML document. The following snippet can be embedded directly into a gadget <Content> section or HTML document:

<script type="text/os-template" name="my:HelloWorld">
<div style="font-size: 20px">Hello world!</div>
</script>

Templates can alternatively be define template markup libraries, which will map to XML namespaces. The set of common OpenSocial functionality is defined in the in Part 2 of the Open Social specification.

Expressions

Expressions can be embedded into the template XML using the syntax ${Expr}. Example template content with an expression:

<div>Hello ${Viewer.Name}</div>

Expressions are defined using a subset of the JSP Expression Language, with a couple of minor modifications. We use a subset of the JSP Expression Language specification. This allows for expressions that are raw variable references, as in the example above, or expressions with operators:

<div>Next step is ${Step + 1}</div>

JSP Expression Language allows you to use most standard operators (comparison, arithmetic, etc.), although some operators have an alternate name for use in XML. ${a < b} becomes ${a lt b}, ${a > b} becomes ${a gt b}, ${a && b} becomes ${a and b}, etc.

Expressions are usually evaluated as strings. The only exception is when expressions are in attributes without additional text content, and in this case, the value of the expression is the object referenced, and this object is passed to the template for processing. In the following example, the viewer is passed in to the os:ShowPerson template.

<os:ShowPerson person="${Viewer}"/>

Strings are escaped before inserting into the HTML document. By default strings are HTML escaped, however based on context they may be URL encoded or escaped as JavaScript strings, depending on context.

Variables

Data is passed into all calls to render templates - i.e.: opensocial.template.processAll(data). The data passed in can be a JSON object, or as an object with a "getProp" method (example below). The templates will have two ‘types’ of variables. One set is a set of global objects with reserved names. The other set includes everything else or all other declared JavaScript variables.

Special Variables

Template processing reserves a small set of variables.

Top

${Top} refers to the data context passed into the template rendering - i.e. opensocial.template.renderAll(data) -> ${Top} == data.

Context

${Context} is a holding area for additional variables needed when processing templates. ${Context} will include the following set of properties:

  • ${Context.UniqueId} A unique ID of the template being rendered. This value is useful for generating HTML IDs.
  • ${Context.Index} The index of the current item in the list that is being processed via a "repeat" tag.
  • ${Context.Count} The count of items in the list that is being processed via a "repeat" tag.

Cur

${Cur} refers to the current item being processed within a repeater.

My

${My} refers to data that is passed into a template. Child elements of tags that are used to call templates are passed as parameters to the template and are accessible via the ${My} variable. If these elements have content inside of them, this content can be rendered using <os:Render content="tagName"/>.

Example of defining a template that uses <os:Render/> and calling this template:

<script type="text/os-template" tag="myapp:BoxWithTitle">
<div class="box-title"><os:Render content="title"/></div>
<div class="box-content"><os:Render content="body"/></div>
</script>
<script type="text/os-template">
<myapp:BoxWithTitle>
<title>This is the title</title>
<body>
<div style="font-size:40px">Boo!</div>
<div>from <os:ShowPerson person="${Top.Viewer}"/></div>
<body>
</myapp:BoxWithTitle>
</script>

Note that the passed in content can include other template tags. These tags are displayed as part of calling <os:Render/>.

You can also call <os:Render> without a "content" attribute. This will render all children of the calling node - this is a useful shorthand for tags with only one content section.

Example:

<script type="text/os-template" tag="myapp:JsLink">
<a href="#" onclick="${My.action}; return false;"><os:Render/></a>
</script>
<script type="text/os-template">
This is a <myapp:JsLink action="alert('boo')">javascript link</a>.
</script>

Precedence Rules for the Special Variables

Special variable names are optional. Expressions will be evaluated against each of the special variable contexts. The first matching variable will be the result of expression evaluation. The order of precedence is $My, $Cur, $Context, and then $Top. For example, ${Cur.Name} == ${Name} unless there exists ${My.Name}. The following examples show why this is helpful:

Example with special variables in place:

<div repeat="${Top.ViewerFriends}">
<div>${Cur.Name}"</div>
<div repeat="${Cur.Phone}">${Cur.Number} of type ${Cur.Type}</div>
</div>

Equivalent script using precedence rules:

<div repeat="${ViewerFriends}">
<div>${Name}"</div>
<div repeat="${Phone}">${Number} of type ${Type}</div>
</div>

"Top" refers to the data context passed in via Javascript, so ${Top} will return the data passed in during template rendering.

Variables are accessed using Foo.Bar (or Foo.Bar.Baz) notation. .Bar maps to getting the property Bar on the JSON object. If Top and Viewer are JSON objects, then

${Top.Viewer.Name}

evaluates to

data['Viewer']['Name']

If an object has a getProp method, then this method is called instead of using the property notation. So if Top and Viewer support this interface, then

${Top.Viewer.Name}

evaluates to

data.getProp('Viewer').getProp('Name')

OpenSocial core objects will be modified to add getProp() support so that you can access properties by name.

Variables can also be accessed using the foo[x] notation, where x is a string, number, or expression that evaluates to a string or number. If ${foo} is an expression that maps to the JS object bar, then ${foo[x]} maps to bar[x] if bar is JSON, or bar.getAt(x) if bar is an object.

Calling Templates

Templates can be called by other templates. You can add a "name" attribute to any template (both on the <Template> element or on the <script> tag for embedded templates). This allows the template to be called by using an XML element of the same name.

Example of defining a template and then calling it:

<script type="text/os-template" tag="myapp:HelloWorld">
<div style="font-size: 40px">Hello World</div>
</script>
<script type="text/os-template">
<myapp:HelloWorld/>
</script>

Parameters can be passed into templates as XML attributes or elements. These parameters are accessed using the special variable ${My}:

<script type="text/os-template" tag="myapp:HelloWorld">
<div style="color: ${My.MessageStyle.color}">Your message is: ${My.message}</div>
</script>
 
<script type="text/os-template">
<myapp:HelloWorld message="Hello World">
<MessageStyle color="blue"/>
</myapp>
</script>

${My.foo} will first look for an attribute named foo in the calling template, and next will look for an element named foo.

Context Attribute

The scope of data to evaluate can be explicitly set with the @context attribute. For example, to repeat over Interests of the first object in Friends, you can use this expression:

<div context="Friends[0]">
<div repeat="Interests">
${Title}
</div>
</div>

Conditional Content

Content can be displayed conditionally based on evaluation of an expression.

Elements with an @if attribute will only be displayed if the if attribute evaluates to true. For example, the following will only be displayed if Top.YourScore == Top.HighScore:
<div if="Top.YourScore == Top.HighScore">You have the high score of ${Top.YourScore}!</div>

Note: An @if attribute takes only an expression, so the surrounding '${}' is allowed, but optional.

The contents of an <os:If> element are only displayed if the condition attribute evaluates to true. The example above rewritten using the <os:If> element:

<os:If condition="Top.YourScore == Top.HighScore">
<div>You have the high score of ${Top.YourScore}!</div>
</os:If>

The @if attribute can be used on any element within a template, some examples are as follows:

<!-- Custom tags -->
<os:ShowPerson person="${Top.Owner}" if="Top.HasOwner"/>
 
<!-- Repeated elements (see below) -->
<div repeat="Top.ViewerFriends" if="Cur.ProfileUrl">
Link to: <a href="${Cur.ProfileUrl}">${Cur.Name}</a>
</div>
 
<!-- Tag contents -->
<os:Button>
<title>Click me!</title>
<help if="Top.Viewer.NeedsHelp">You need help.</help>
</os:Button>

Repeated Elements

Tags can be rendered multiple times based on evaluation of an expression.

Tags with a @repeat attribute are displayed once for each item in evaluating the expression in the repeat attribute. The current item in the repeated list will be put into the ${Cur} variable. Note: As with @if, the ${} expression syntax is optional, but allowed.

Example:

<div repeat="Top.ViewerFriends">
Your friend's name is ${Cur.Name}
</div>

In addition, both the variable and the index can be renamed, by adding a @var attribute and @index attribute respectively. Example:

<!-- Renaming variable $Cur -->
<div repeat="${Top.ViewerFriends}" var="Friend">
Your friend's name is ${Friend.Name}
</div>
 
<!-- Specifying an Index variable -->
<div repeat="rows" index="x">
<div repeat="cols" index="y">
<myapp:DrawPoint row="${x}" col="${y}"/>
</div>
</div>

There are two context variables available during list processing. ${Context.Count} is the count of items in the list and ${Context.Index} is the index of the current item. Example:

<div repeat="${Top.ViewerFriends}">
Showing friend ${Context.Index} of ${Context.Count}:
Your friend's name is ${Cur.Name}
</div>

The contents of an <os:Repeat> element displayed once for each item in evaluating the expression in the expression attribute. The current item in the repeated list will be put into the ${Cur} variable, and a @var attribute can also be used to rename the iteration variable.

<os:Repeat expression="${Top.ViewerFriends}">
<div>
Your friend's name is ${Cur.Name}
</div>
</os:Repeat>

and

<os:Repeat expression="${Top.ViewerFriends}" var="Friend">
<div>
Your friend's name is ${Friend.Name}
</div>
</os:Repeat>

Template Libraries

While inline templates can be useful for visualizing data, eventually you may want to create standalone, reusable templates. In that regard, templates can be packaged into a standalone XML file of the following format:

<Templates xmlns:foo="http://foo.com/">
<Namespace prefix="foo" url="http://foo.com/"/>
 
<Style>
<!-- Set global CSS for your library here -->
.warning { color: red; }
</Style>
 
<JavaScript>
<!-- Define global functions for your library here -->
function usedByAllTemplates() { ... };
</JavaScript>
 
<!-- Simple declarative tag foo:bar -->
<Template tag="foo:bar">
<!-- Define markup for foo:bar here -->
</Template>
 
<!-- Complex tag foo:baz with local CSS and JavaScript -->
<TemplateDef tag="foo:baz">
<Template> <!-- Define markup for foo:baz here --> </Template>
<Style> <!-- Set CSS for foo:baz here --> </Style>
<JavaScript>
<!-- Define functions for the foo:baz template here -->
function usedByFooBaz() { ... };
</JavaScript>
</TemplateDef>
</Templates>

For example:

<?xml version="1.0" encoding="UTF-8"?>
<Templates xmlns:os="http://opensocial.org/templates">
<Namespace prefix="os" url="http://opensocial.org/templates">
<Style>
large-font: {
font-size: 20px;
}
</Style>
<Template name="os:HelloWorld">
<div style="large-font">Hello World!</div>
</Template>
<TemplateDef tag="os:ShowPerson">
<Style>
profile-image: {
padding-right: 5px;
width: 32px;
height: 32px;
}
</Style>
<Template>
<img if="${My.person.ThumbnailUrl}" src="${My.person.ThumbnailUrl}"
class="profile-image"/>

<a href="${My.person.ProfileUrl}" target="_top">${My.person.Name}</a>
</Template>
</TemplateDef>
</Templates>

Syntax:

  • <Templates>: Declares all the namespaces used in this library to the XML parser. You must put this tag at the outer-most level of your XML file and list all the custom and pre-defined namespaces that you use in this library. Use the syntax xmlns:namespace_prefix="namespace_URL" for each namespace. For example, if you use any tags in the OpenSocial namespace via the OpenSocial Templates library, you must include xmlns:os="http://www.opensocial.org/".
  • <Namespace>: Declares your custom namespace to the OpenSocial Templates API. You should only create one namespace per library file. Set the values of your namespace's prefix and URL using the syntax prefix="namespace_prefix" url="namespace_URL"
  • <Style>: Defines style settings in CSS for your library (at the top-level of the XML file) or individual templates (within <TemplateDef/> tags).
  • <JavaScript>: Defines JavaScript functions for your library (at the top-level of the XML file) or individual templates (within <TemplateDef/> tags).
  • <Template>: Sets declarative markup for a template.
  • <TemplateDef>: Defines a more complex template, which can enclose its own local <Template/>, <Style/>, and <JavaScript/> tags.

Containers should load the <os:*> template tags by default, and it is not required that they be defined as a standalone XML file. Other tag libraries can be loaded by default in a container - for example, Example.com might have a set of <example:*> tags available.

Localization

Templates will have the ability to substitute localized text content on a per-language basis.

Gadgets already have a facility for defining localized messages. In the gadget prefs, you can specify the URL for localized message bundles on a per-language basis:

<ModulePrefs title="ListenToThis"> 
<Locale messages="http://www.listentostuff.com/messages.xml"/>
<Locale lang="de" messages="http://www.listentostuff.com/messages-DE.xml"/>
</ModulePrefs>

Each bundle is a list of <msg> elements with localized content inside:

<messagebundle> 
<msg name="PLAY_SONG">Click here to play ${song.title}</msg>
</messagebundle>

Also note that in OpenSocial API 0.9, you will be able to inline <msg> elements into the gadget directly without creating separate files for each locale.

Localized messages will be accessible via the ${Msg} variable:

<a href="${song.url}">${Msg.PLAY_SONG}</a>

Any ${} markup in the message body will be honored - in the above example, the ${song.title} reference in the message will be evaluated at the time of template rendering. Repeaters and conditionals in messages will not be processed. Variable markup will be evaluated in the context in which the message is placed. (Note how the message in the example references the ${song} variable available at the point of message inclusion.)

It's important to realize that this sets ${Msg} apart from other variable inclusion markup - variable references will NOT be processed in any other output.

If a particular message is undefined for a locale and missing from the default bundle (prefs.getMsg(key) returns null in these cases), an error message should be rendered in its place for easy debugging.

References

Normative References

IETF RFC 2119

IETF (Internet Engineering Task Force). RFC 2119: Key words for use in RFCs to Indicate Requirement Levels. Scott Bradner, 1997. (See http://www.ietf.org/rfc/rfc2119.txt.)

Java Server Pages Expression Language

Kin-Man Chung, Pierre Delisle, Mark Roth, May 8, 2006. https://jsp.dev.java.net/spec/jsp-2_1-fr-spec-el.pdf.

Other References

ECMA-262

ECMA (European Computer Manufacturers Association). ECMA-262: ECMAScript Language Specification. 1999. (See http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf.)

  1. Opener on Data Pipelining at the Spec Group