ENHANCEMENT: Updated the template language documentation, based on work done by mrmorphic.

This commit is contained in:
Mark Stephens 2011-04-18 21:11:09 +12:00 committed by Sam Minnee
parent 99b2430565
commit 288369f1a9
8 changed files with 829 additions and 545 deletions

View File

@ -2,6 +2,7 @@
## Overview ##
* New template engine
* New CMS interface design
* "Page Content" and "Page Settings" are split into two interfaces
* Image/Link insertion moved into a modal dialog instead of a sidebar
@ -12,6 +13,10 @@
## Upgrading ##
### New template engine
The template engine has been completely rewritten, and although it is generally backward compatible, there are new features and some features have been deprecated. See the [template upgrading guide](/reference/templates-upgrading-guide) and the [template reference](/reference/templates) for more information.
### New user interface for CMS ###
Most aspects of the interface have been redesigned, which necessitated a substantial

View File

@ -1,413 +0,0 @@
# Advanced Template Syntax
The following control codes are available. For a more details list see [built-in-page-controls](/reference/built-in-page-controls):
### Variables
:::ss
$Property
$Property(param)
$Property.SubProperty
These **variables** will call a method/field on the object and insert the returned value as a string into the template.
* `$Property` will call `$obj->Property()` (or the field `$obj->Property`)
* `$Property(param)` will call `$obj->Property("param")`
* `$Property.SubProperty` will call `$obj->Property()->SubProperty()` (or field equivalents)
If a variable returns a string, that string will be inserted into the template. If the variable returns an object, then
the system will attempt to render the object through its forTemplate() method. If the `forTemplate()` method has not been
defined, the system will return an error.
Note you also cannot past a variable into a variable, so using `$Property($Value)` within your template will not work
### Includes
Within SilverStripe templates we have the ability to include other templates from the Includes directory using the SS
'include' tag. For example, the following code would include the `Includes/SideBar.ss` code:
:::ss
<% include SideBar %>
The "include" tag can be particularly helpful for nested functionality. In this example, the include only happens if
a variable is true
:::ss
<% if CurrentMember %>
<% include MembersOnlyInclude %>
<% end_if %>
You can also perform includes using the Requirements Class via the template controls. See the section on
[Includes in Templates](requirements#including_inside_template_files) for more details and examples.
:::ss
<% require themedCSS(LeftNavMenu) %>
### Controls
:::ss
<% control Property %>
... content ...
<% end_control %>
<% control Property.SubProperty %>
... content ...
<% end_control %>
<% control Property(param) %>
... content ...
<% end_control %>
Control blocks reference the same methods / fields as variables. Think of it as a foreach loop in PHP or other template
languages. `<% control Property %>` gets the same data as `$Property`. However, instead of interpreting the result as a
string, control blocks interpret the result as an object or a array of objects. The content between `<% control %>` and
`<% end_control %>` acts as a sub-template that is used to render the object returned.
In this example, `$A` and `$B` refer to `$obj->Property()->A()` and `$obj->Property()->B()`.
:::ss
<% control Property %>
<span>$A</span>
<span>$B</span>
<% end_control %>
If the method/field returned is an iterator such as a `[api:DataObject]`, then the control block will be repeated for
each element of that iterator. This is the cornerstone of all menu and list generation in SilverStripe.
In this example, `Menu(1)` returns a `[api:DataObjectSet]` listing each top level main menu item (for more info on `Menu(1)`:
[Making a Navigation System](/tutorials/1-building-a-basic-site#Making-a-Navigation-System)). The `<a>`
tag is repeated once for each main menu item, and the `$Link` and `$Title` values for each menu item is substituted in.
:::ss
<% control Menu(1) %>
<a href="$Link">$Title</a>
<% end_control %>
### If blocks
:::ss
<% if Property %>
... optional content ...
<% else_if OtherProperty %>
... alternative content ...
<% else %>
... alternative content ...
<% end_if %>
<% if Property == value %>
<% else %>
<% end_if %>
<% if Property != value %>
<% end_if %>
<% if Property && Property2 %>
<% end_if %>
<% if Property || Property2 %>
<% end_if %>
If blocks let you mark off optional content in your template. The optional content will only be shown if the requested
field / method returns a nonzero value. In the second syntax, the optional content will only be shown if the requested
field / method returns the value you specify. You should **not** include quotes around the value.
The `<% else %>` blocks perform as you would expect - content between `<% else %>` and `<% end_if %>` is shown if the first
block fails. `<% else %>` is an optional part of the syntax - you can just use `<% if %>` and `<% end_if %>` if that's
appropriate.
### Modulus and MultipleOf
New in 2.4 you can use 2 new controls $Modulus and $MultipleOf to help build column layouts.
:::ss
$Modulus(value, offset) // returns an int
$MultipleOf(factor, offset) // returns a boolean.
The following example demonstrates how you can use $Modulus(4) to generate custom column names based on your control statement. Note that this works for any control statement (not just children)
:::ss
<% control Children %>
<div class="column-{$Modulus(4)}">
...
</div>
<% end_control %>
Will return you column-3, column-2, column-1, column-0, column-3 etc. You can use these as styling hooks to float, position as you need.
You can also use $MultipleOf(value, offset) to help build columned layouts. In this case we want to add a <br> after every 3th item
:::ss
<% control Children %>
<% if MultipleOf(3) %>
<br>
<% end_if %>
<% end_control %>
### Comments
Using standard HTML comments is supported. These comments will be included in the published site.
:::ss
$EditForm <!-- Some Comment About the Edit Form -->
However you can also use special SilverStripe comments which will be stripped out of the published site. This is useful
for adding notes for other developers but for things you don't want published in the public html.
:::ss
$EditForm <%-- This is Located in MemberEditForm.php --%>
### Formatting Template Values
The following example takes the Title field of our object, casts it to a `[api:Varchar]` object, and then calls
the `$XML` object on that Varchar object.
:::ss
<% control Title %>
$XML
<% end_control %>
Note that this code can be more concisely represented as follows:
:::ss
$Title.XML
See [data-types](/topics/data-types) for more information.
### Escaping
Sometimes you will have template tags which need to roll into one another. This can often result in SilverStripe looking
for a "FooBar" value rather than a "Foo" and then "Bar" value or when you have a string directly before or after the
variable you will need to escape the specific variable. In the following example `$Foo` is `3`.
:::ss
$Foopx // returns "" (as it looks for a Foopx value)
{$Foo}px // returns "3px" (CORRECT)
Or when having a `$` sign in front of the variable
:::ss
$$Foo // returns ""
${$Foo} // returns "$3"
### Partial Caching
From SilverStripe 2.4 you can specify a block to cache between requests
:::ss
<% cacheblock 'slowoperation', LastEdited %>
$SlowOperation
<% end_cacheblock %>
See [partial-caching](/reference/partial-caching) for more information.
## Built In Template Variables and Controls
Out of the box, the template engine gives you lots of neat little variables and controls which you will find useful. For
a list of all the controls see [built-in-page-controls](/reference/built-in-page-controls).
## Creating your own Template Variables and Controls
There are two ways you can extend the template variables you have available. You can create a new database field in your
`$db` or if you do not need the variable to be editable in the cms you can create a function which returns a value in your
`Page.php` class.
:::php
**mysite/code/Page.php**
...
function MyCustomValue() {
return "Hi, this is my site";
}
Will give you the ability to call `$MyCustomValue` from anywhere in your template.
:::ss
I've got one thing to say to you: <i>$MyCustomValue</i>
// output "I've got one thing to say to you: <i>Hi, this is my site</i>"
Your function could return a single value as above or it could be a subclass of `[api:ArrayData]` for example a
`[api:DataObject]` with many values then each of these could be accessible via a control loop
:::php
..
function MyCustomValues() {
return new ArrayData(array("Hi" => "Kia Ora", "Name" => "John Smith"));
}
And now you could call these values by using
:::ss
<% control MyCustomValues %>
$Hi , $Name
<% end_control %>
// output "Kia Ora , John Smith"
Or by using the dot notation you would have
:::ss
$MyCustomValues.Hi , $MyCustomValues.Name
// output "Kia Ora , John Smith"
### Side effects
All functions that provide data to templates must have no side effects, as the value is cached after first access.
For example, this Controller method
:::php
private $counter = 0;
function Counter() {
$this->counter += 1;
return $this->counter;
}
and this template
:::ss
$Counter, $Counter, $Counter
will give "1, 1, 1", not "1, 2, 3"
## Calling templates from PHP code
This is all very well and good, but how do the templates actually get called?
Templates do nothing on their own. Rather, they are used to render *a particular object*. All of the `<% if %>`, `<%control %>`,
and variable codes are methods or parameters that are called *on that object*. All that is necessary is
that the object is an instance of `[api:ViewableData]` (or one of its subclasses).
The key is `[api:ViewableData::renderWith()]`. This method is passed a For example, within the controller's default action,
there is an instruction of the following sort:
:::php
$controller->renderWith("TemplateName");
Here's what this line does:
* First `renderWith()` constructs a new object: `$template = new SSViewer("TemplateName");`
* `[api:SSViewer]` will take the content of `TemplateName.ss`, and turn it into PHP code.
* Then `renderWith()` passes the controller to `$template->process($controller);`
* `SSViewer::process()` will execute the PHP code generated from `TemplateName.ss` and return the results.
`renderWith()` returns a string - the populated template. In essence, it uses a template to cast an object to a string.
`renderWith()` can also be passed an array of template names. If this is done, then `renderWith()` will use the first
available template name.
Below is an example of how to implement renderWith. In the example below the page is rendered using the myAjaxTemplate
if the page is called by an ajax function (using `[api:Director::is_ajax()]`). Note that the index function is called by
default if it exists and there is no action in the url parameters.
:::php
class MyPage_Controller extends Page_Controller {
function init(){
parent::init();
}
function index() {
if(Director::is_ajax()) {
return $this->renderWith("myAjaxTemplate");
}
else {
return Array();// execution as usual in this case...
}
}
}
### How does ViewableData work?
ViewableData provides two methods that perform the casting necessary for templates to work as we have described.
* `obj("Parameter")` - Return the given field / method as an object, casting if necessary
* `XML_val("Parameter)` - Return the given field / method as a scalar, converting to an XML-safe format and casting if
necessary
These methods work as described in the syntax section above. SSViewer calls these methods when processing templates.
However, if you want, you can call `obj()` and `val()` yourself.
## Fragment Link rewriting
Fragment links are links with a "#" in them. A frequent use-case is to use fragment links to point to different
sections of the current page. For example, we might have this in our template.
For, example, we might have this on http://www.example.com/my-long-page/
:::ss
<ul>
<li><a href="#section1">Section 1</a></li>
<li><a href="#section2">Section 2</a></li>
</ul>
So far, so obvious. However, things get tricky because of we have set our `<base>` tag to point to the root of your
site. So, when you click the first link you will be sent to http://www.example.com/#section1 instead of
http://www.example.com/my-long-page/#section1
In order to prevent this situation, the SSViewer template renderer will automatically rewrite any fragment link that
doesn't specify a URL before the fragment, prefixing the URL of the current page. For our example above, the following
would be created:
:::ss
<ul>
<li><a href="my-long-page/#section1">Section 1</a></li>
<li><a href="my-long-page/#section2">Section 2</a></li>
</ul>
There are cases where this can be unhelpful. HTML fragments created from Ajax responses are the most common. In these
situations, you can disable fragment link rewriting like so:
:::php
SSViewer::setOption('rewriteHashlinks', false);
## Casting and Escaping
Method and variables names that deal with strings or arrays of strings should have one of the following 5 prefixes:
* **RAW_** Raw plain text, as a user would like to see it, without any HTML tags
* **XML_** Text suitable for insertion into an HTML or XML data-set. This may contain HTML content, for example if the
content came from a WYSIWYG editor.
* **JS_** Data that can safely be inserted into JavaScript code.
* **ATT_** Data that can safely be inserted into an XML or HTML attribute.
The same prefixes are used for both strings and arrays of strings. We did this to keep things simple: passing a string
with the wrong encoding is a far subtler a problem than passing an array instead of a string, and therefore much harder
to debug.
## Related
* [Templates](/topics/templates)
* [Themes](/topics/themes)
* [Developing Themes](/topics/theme-development)
* [Widgets](/topics/widgets)
* [Images](/reference/image)
* [Built in page controls](/reference/built-in-page-controls)
* [Including Templates](/reference/requirements)

View File

@ -0,0 +1,96 @@
# Formal Treatment of the Template Language
This document gives you the maximum level of detail of how the template engine works. If you just want to know how to write templates, you probably want to read [this page](/reference/templates) instead.
## White space
## Expressions
### Literals
Definition:
literal := number | stringLiteral
number := digit { digit }*
stringLiteral := "\"" { character } * "\"" |
"'" { character } * "'"
Notes:
* digits in a number comprise a single token, so cannot have spaces
* any printable character can be included inside a string literal except the opening quote, unless
prefixed by a backslash (TODO: check this assumption with Hamish - its probably not right)
### Words
A word is used to identify a name of something, e.g. a property or method name. Words must start
with an alphabetic character or underscore, with subsequent characters being alphanumeric or underscore:
word := A-Za-z_ { A-Za-z0-9_ }*
### Properties and methods (variables)
Examples:
$PropertyName.Name
$MethodName
$MethodName("foo").SomeProperty
$MethodName("foo", "bar")
$MethodName("foo", $PropertyName)
Definition:
injection := "$" lookup | "{" "$" lookup "}"
call := word [ "(" argument { "," argument } ")" ]
lookup := call { "." call }*
argument := literal |
lookup |
"$" lookup
Notes:
* A word encountered with no parameter can be parsed as either a property or a method. TODO:
document the exact rules for resolution.
* TODO: include Up and Top in here. Not syntactic elements, but worth mentioning their semantics.
* TODO: consider the 2.4 syntax literals. These have been excluded as we don't want to encourage their
use.
### Operators
<exp> == <exp>
<exp> = <exp>
<exp> != <exp>
<exp> || <exp>
<exp> && <exp>
* TODO: document operator precedence, and include a descripton of what the operators do. || and && are short-circuit? Diff
between = and == or are they equivalent.
## Comments
## If
if := ifPart elseIfPart* elsePart endIfPart
ifPart := "<%" "if" ifCondition "%>" template
elseIfPart := "<%" "else_if" ifCondition "%>" template
elsePart := "<%" "else" "%>" template
endIfPart := "<%" "end_if" "%>"
ifCondition := TODO
template := TODO
## Require
## Loop
## With
## Translation
## Cache block
TODO to be elaborated
<cached arguments>...<end_cached>
<cacheblock arguments>...<end_cacheblock>
<uncached>...<uncached>

View File

@ -0,0 +1,42 @@
# Moving from SilverStripe 2 to SilverStripe 3
These are the main changes to the SiverStripe 3 template language.
## Control
The `<% control var %>...<% end_control %>` in SilverStripe prior to version 3 has two different meanings. Firstly, if the control variable is a collection (e.g. DataObjectSet), then `<% control %>` iterates over that set. If it's a non-iteratable object, however, `<% control %>` introduces a new scope, which is used to render the inner template code. This dual-use is confusing to some people, and doesn't allow a collection of objects to be used as a scope.
In SilverStripe 3, the first usage (iteration) is replaced by `<% loop var %>`. The second usage (scoping) is replaced by `<% with var %>`
## Literals in Expressions
Prior to SilverStripe 3, literal values can appear in certain parts of an expression. For example, in the expression `<% if mydinner=kipper %>`, `mydinner` is treated as a property or method on the page or controller, and `kipper` is treated as a literal. This is fairly limited in use.
Literals can now be quoted, so that both literals and non-literals can be used in contexts where only literals were allowed before. This makes it possible to write the following:
* `<% if $mydinner=="kipper" %>...` which compares to the literal "kipper"
* `<% if $mydinner==$yourdinner %>...` which compares to another property or method on the page called `yourdinner`
Certain forms that are currently used in SilverStripe 2.x are still supported in SilverStripe 3 for backwards compatibility:
* `<% if mydinner==yourdinner %>...` is still interpreted as `mydinner` being a property or method, and `yourdinner` being a literal. It is strongly recommended to change to the new syntax in new implementations. The 2.x syntax is likely to be deprecated in the future.
Similarly, in SilverStripe 2.x, method parameters are treated as literals: `MyMethod(foo)` is now equivalent to `$MyMethod("foo")`. `$MyMethod($foo)` passes a variable to the method, which is only supported in SilverStripe 3.
## Method Parameters
Methods can now take an arbitrary number of parameters:
$MyMethod($foo,"active", $CurrentMember.FirstName)
Parameter values can be arbitrary expressions, including a mix of literals, variables, and even other method calls.
## Less sensitivity around spaces
Within a tag, a single space is equivalent to multiple consequetive spaces. e.g.
<% if $Foo %>
is equivalent to
<% if $Foo %>

View File

@ -0,0 +1,536 @@
# Templates
## Introduction
SilverStripe templates consist of HTML code augmented with special control codes, described below. Because of this, you
can have as much control of your site's HTML code as you like.
Because the SilverStripe templating language is a string processing language it can therefore be used to make other
text-based data formats, such as XML or RTF.
Here is a very simple template:
:::ss
<html>
<%-- This is my first template --%>
<head>
<% base_tag %>
<title>$Title</title>
$MetaTags
</head>
<body>
<div id="Container">
<div id="Header">
<h1>Bob's Chicken Shack</h1>
<% with $CurrentMember %>
<p>You are logged in as $FirstName $Surname.</p>
<% end_if %>
</div>
<div id="Navigation">
<% if $Menu(1) %>
<ul>
<% loop $Menu(1) %>
<li><a href="$Link" title="Go to the $Title page" class="$LinkingMode">$MenuTitle</a></li>
<% end_loop %>
</ul>
<% end_if %>
</div>
<div class="typography">
$Layout
</div>
<div id="Footer">
<p>Copyright $Now.Year</p>
</div>
</div>
</body>
</html>
# Template elements
### Base Tag
The `<% base_tag %>` placeholder is replaced with the HTML base element. Relative links within a document (such as `<img
src="someimage.jpg" />`) will become relative to the URI specified in the base tag. This ensures the browser knows where
to locate your sites images and css files. So it is a must for templates!
It renders in the template as `<base href="http://www.mydomain.com" /><!--[if lte IE 6]></base><![endif]-->`
### Layout Tag
In every SilverStripe theme there is a default `Page.ss` file in the `/templates` folder. `$Layout` appears in this file
and is a core variable which includes a Layout template inside the `/templates/Layout` folder once the page is rendered.
By default the `/templates/Layout/Page.ss` file is included in the html template.
## Variables
Variables are things you can use in a template that grab data from the page and put in the HTML document. For example:
:::ss
$Title
This inserts the value of the Title field of the page being displayed in place of `$Title`. This type of variable is called a **property**. It is often something that can be edited in the CMS. Variables can be chained together, and include arguments.
:::ss
$Property
$Property(param)
$Property.SubProperty
These **variables** will call a method/field on the object and insert the returned value as a string into the template.
* `$Property` will call `$obj->Property()` (or the field `$obj->Property`)
* `$Property(param)` will call `$obj->Property("param")`
* `$Property.SubProperty` will call `$obj->Property()->SubProperty()` (or field equivalents)
If a variable returns a string, that string will be inserted into the template. If the variable returns an object, then
the system will attempt to render the object through its forTemplate() method. If the `forTemplate()` method has not been
defined, the system will return an error.
SilverStripe provides lots of properties and methods. For more details on built-in page controls and variables, see http://doc.silverstripe.org/sapphire/en/reference/built-in-page-controls
### Escaping
Sometimes you will have template tags which need to roll into one another. This can often result in SilverStripe looking
for a "FooBar" value rather than a "Foo" and then "Bar" value or when you have a string directly before or after the
variable you will need to escape the specific variable. In the following example `$Foo` is `3`.
:::ss
$Foopx // returns "" (as it looks for a Foopx value)
{$Foo}px // returns "3px" (CORRECT)
Or when having a `$` sign in front of the variable
:::ss
$$Foo // returns ""
${$Foo} // returns "$3"
## Includes
Within SilverStripe templates we have the ability to include other templates from the Includes directory using the SS
'include' tag. For example, the following code would include the `Includes/SideBar.ss` code:
:::ss
<% include SideBar %>
The "include" tag can be particularly helpful for nested functionality. In this example, the include only happens if
a variable is true
:::ss
<% if CurrentMember %>
<% include MembersOnlyInclude %>
<% end_if %>
You can also perform includes using the Requirements Class via the template controls. See the section on
[Includes in Templates](requirements#including_inside_template_files) for more details and examples.
:::ss
<% require themedCSS(LeftNavMenu) %>
### Including CSS and JavaScript files (a.k.a "Requirements")
See [CSS](/topics/css) and [Javascript](/topics/javascript) topics for individual including of files and
[requirements](reference/requirements) for good examples of including both Javascript and CSS files.
## Conditional Logic
You can conditionally include markup in the output. That is, test for something that is true or false, and based on that test, control what gets output.
The simplest if block is to check for the presence of a value.
<% if $CurrentMember %>
<p>You are logged in as $CurrentMember.FirstName $CurrentMember.Surname.</p>
<% end_if %>
The following compares a page property called `MyDinner` with the value in quotes, `kipper`, which is a **literal**. If true, the text inside the if-block is output.
<% if $MyDinner="kipper" %>
Yummy, kipper for tea.
<% end_if %>
Note that inside a tag like this, variables should have a '$' prefix, and literals should have quotes. SilverStripe 2.4 didn't include the quotes or $ prefix, and while this still works, we recommend the new syntax as it is less ambiguous.
This example shows the use of the `else` option. The markup after `else` is output if the tested condition is *not* true.
<% if $MyDinner="kipper" %>
Yummy, kipper for tea
<% else %>
I wish I could have kipper :-(
<% end_if %>
This example shows the user of `else\_if`. There can be any number of `else\_if` clauses. The conditions are tested from first to last, until one of them is true, and the markup for that condition is used. If none of the conditions are true, the markup in the `else` clause is used, if that clause is present.
<% if $MyDinner="quiche" %>
Real men don't eat quiche
<% else_if $MyDinner=$YourDinner %>
We both have good taste
<% else %>
Can I have some of your chips?
<% end_if %>
This example shows the use of `not` to negate the test.
<% if not $DinnerInOven %>
I'm going out for dinner tonight.
<% end_if %>
You can combine two or more conditions with `||` ("or"). The markup is used if *either* of the conditions is true.
<% if $MyDinner=="kipper" || $MyDinner=="salmon" %>
yummy, fish for tea
<% end_if %>
You can combine two or more conditions with `&&` ("and"). The markup is used if *both* of the conditions are true.
<% if $MyDinner=="quiche" && $YourDinner=="kipper" %>
Lets swap dinners
<% end_if %>
As you'd expect, these can be nested:
<% if $MyDinner=="chicken" %>
<% if $Wine=="red" %>
You're doing it wrong
<% else %>
Perfect
<% end_if %>
<% end_if %>
## Looping Over Datasets
The `<% loop %>...<% end_loop %>` tag is used to **iterate** or loop over a collection of items. For example:
<ul>
<% loop $Children %>
<li>$Title</li>
<% end_loop %>
</ul>
This loops over the children of a page, and generates an unordered list showing the Title property from each one. Note that $Title <i>inside</i> the loop refers to the Title property on each object that is looped over, not the current page. (To refer to the current page's Title property inside the loop, you can do `$Up.Title`. More about `Up` later.
The value that given in the `<% loop %>` tags should be a collection variable.
### Modulus and MultipleOf
New in 2.4 you can use 2 new controls $Modulus and $MultipleOf to help build column layouts.
:::ss
$Modulus(value, offset) // returns an int
$MultipleOf(factor, offset) // returns a boolean.
The following example demonstrates how you can use $Modulus(4) to generate custom column names based on your control statement. Note that this works for any control statement (not just children)
:::ss
<% control Children %>
<div class="column-{$Modulus(4)}">
...
</div>
<% end_control %>
Will return you column-3, column-2, column-1, column-0, column-3 etc. You can use these as styling hooks to float, position as you need.
You can also use $MultipleOf(value, offset) to help build columned layouts. In this case we want to add a <br> after every 3th item
:::ss
<% control Children %>
<% if MultipleOf(3) %>
<br>
<% end_if %>
<% end_control %>
## Scope
In the `<% loop %>` section, we saw an example of two **scopes**. Outside the `<% loop %>...<% end_loop %>`, we were in the scope of the page. But inside the loop, we were in the scope of an item in the list. The scope determines where the value comes from when you refer to a variable. Typically the outer scope of a page type's layout template is the page that is currently being rendered. The outer scope of an included template is the scope that it was included into.
### With
The `<% with %>...<% end_with %>` tag lets you introduce a new scope. Consider the following example:
<% with $CurrentMember %>
Hello $FirstName, welcome back. Your current balance is $Balance.
<% end_with %>
Outside the `<% with %>...<% end_with %>`, we are in the page scope. Inside it, we are in the scope of `$CurrentMember`. We can refer directly to properties and methods of that member. So $FirstName is equivalent to $CurrentMember.FirstName. This keeps the markup clean, and if the scope is a complicated expression we don't have to repeat it on each reference of a property.
`<% with %>` also lets us use a collection as a scope, so we can access properties of the collection itself, instead of iterating over it. For example:
$Children.Length
returns the number of items in the $Children collection.
### Top
$Top.Title
### Up
When we are in a scope, we sometimes want to refer to the scope outside the <% loop %> or <% with %>. We can do that easily by using $Up.
$Up.Owner
## Formatting Template Values
The following example takes the Title field of our object, casts it to a `[api:Varchar]` object, and then calls
the `$XML` object on that Varchar object.
:::ss
<% with Title %>
$XML
<% end_with %>
Note that this code can be more concisely represented as follows:
:::ss
$Title.XML
See [data-types](/topics/data-types) for more information.
## Translations
Translations are easy to use with a template, and give access to SilverStripe's translation facilities. Here is an example:
<%t Member.WELCOME 'Welcome {name} to {site}' name=$Member.Name site="Foobar.com" %>
Pulling apart this example we see:
* `Member.WELCOME` is an identifier in the translation system, for which different translations may be available. This string may include named placeholders, in braces.
* `'Welcome {name} to {site}'` is the default string used, if there is no translation for Member.WELCOME in the current locale. This contains named placeholders.
* `name=$Member.Name` assigns a value to the named placeholder `name`. This value is substituted into the translation string wherever `{name}` appears in that string. In this case, it is assigning a value from a property `Member.Name`
* `site="Foobar.com"` assigns a literal value to another named placeholder, `site`.
## Comments
Using standard HTML comments is supported. These comments will be included in the published site.
:::ss
$EditForm <!-- Some Comment About the Edit Form -->
However you can also use special SilverStripe comments which will be stripped out of the published site. This is useful
for adding notes for other developers but for things you don't want published in the public html.
:::ss
$EditForm <%-- This is Located in MemberEditForm.php --%>
## Partial Caching
Partial caching lets you define blocks of your template that are cached for better performance. See [Partial Caching](/reference/partial-caching.md) for more information.
## Creating your own Template Variables and Controls
There are two ways you can extend the template variables you have available. You can create a new database field in your
`$db` or if you do not need the variable to be editable in the cms you can create a function which returns a value in your
`Page.php` class.
:::php
**mysite/code/Page.php**
...
function MyCustomValue() {
return "Hi, this is my site";
}
Will give you the ability to call `$MyCustomValue` from anywhere in your template.
:::ss
I've got one thing to say to you: <i>$MyCustomValue</i>
// output "I've got one thing to say to you: <i>Hi, this is my site</i>"
Your function could return a single value as above or it could be a subclass of `[api:ArrayData]` for example a
`[api:DataObject]` with many values then each of these could be accessible via a control loop
:::php
..
function MyCustomValues() {
return new ArrayData(array("Hi" => "Kia Ora", "Name" => "John Smith"));
}
And now you could call these values by using
:::ss
<% control MyCustomValues %>
$Hi , $Name
<% end_control %>
// output "Kia Ora , John Smith"
Or by using the dot notation you would have
:::ss
$MyCustomValues.Hi , $MyCustomValues.Name
// output "Kia Ora , John Smith"
### Side effects
All functions that provide data to templates must have no side effects, as the value is cached after first access.
For example, this Controller method
:::php
private $counter = 0;
function Counter() {
$this->counter += 1;
return $this->counter;
}
and this template
:::ss
$Counter, $Counter, $Counter
will give "1, 1, 1", not "1, 2, 3"
### Casting and Escaping
Method and variables names that deal with strings or arrays of strings should have one of the following 5 prefixes:
* **RAW_** Raw plain text, as a user would like to see it, without any HTML tags
* **XML_** Text suitable for insertion into an HTML or XML data-set. This may contain HTML content, for example if the
content came from a WYSIWYG editor.
* **JS_** Data that can safely be inserted into JavaScript code.
* **ATT_** Data that can safely be inserted into an XML or HTML attribute.
The same prefixes are used for both strings and arrays of strings. We did this to keep things simple: passing a string
with the wrong encoding is a far subtler a problem than passing an array instead of a string, and therefore much harder
to debug.
## .typography style
By default, SilverStripe includes the `theme/css/typography.css` file into the Content area. So you should always include the
typography style around the main body of the site so both styles appear in the CMS and on the template. Where the main body of
the site is can vary, but usually it is included in the /Layout files. These files are included into the main Page.ss template
by using the `$Layout` variable so it makes sense to add the .typography style around $Layout.
:::ss
<div class="typography">
$Layout
</div>
## Calling templates from PHP code
This is all very well and good, but how do the templates actually get called?
Templates do nothing on their own. Rather, they are used to render *a particular object*. All of the `<% if %>`, `<%control %>`,
and variable codes are methods or parameters that are called *on that object*. All that is necessary is
that the object is an instance of `[api:ViewableData]` (or one of its subclasses).
The key is `[api:ViewableData::renderWith()]`. This method is passed a For example, within the controller's default action,
there is an instruction of the following sort:
:::php
$controller->renderWith("TemplateName");
Here's what this line does:
* First `renderWith()` constructs a new object: `$template = new SSViewer("TemplateName");`
* `[api:SSViewer]` will take the content of `TemplateName.ss`, and turn it into PHP code.
* Then `renderWith()` passes the controller to `$template->process($controller);`
* `SSViewer::process()` will execute the PHP code generated from `TemplateName.ss` and return the results.
`renderWith()` returns a string - the populated template. In essence, it uses a template to cast an object to a string.
`renderWith()` can also be passed an array of template names. If this is done, then `renderWith()` will use the first
available template name.
Below is an example of how to implement renderWith. In the example below the page is rendered using the myAjaxTemplate
if the page is called by an ajax function (using `[api:Director::is_ajax()]`). Note that the index function is called by
default if it exists and there is no action in the url parameters.
:::php
class MyPage_Controller extends Page_Controller {
function init(){
parent::init();
}
function index() {
if(Director::is_ajax()) {
return $this->renderWith("myAjaxTemplate");
}
else {
return Array();// execution as usual in this case...
}
}
}
## Fragment Link rewriting
Fragment links are links with a "#" in them. A frequent use-case is to use fragment links to point to different
sections of the current page. For example, we might have this in our template.
For, example, we might have this on http://www.example.com/my-long-page/
:::ss
<ul>
<li><a href="#section1">Section 1</a></li>
<li><a href="#section2">Section 2</a></li>
</ul>
So far, so obvious. However, things get tricky because of we have set our `<base>` tag to point to the root of your
site. So, when you click the first link you will be sent to http://www.example.com/#section1 instead of
http://www.example.com/my-long-page/#section1
In order to prevent this situation, the SSViewer template renderer will automatically rewrite any fragment link that
doesn't specify a URL before the fragment, prefixing the URL of the current page. For our example above, the following
would be created:
:::ss
<ul>
<li><a href="my-long-page/#section1">Section 1</a></li>
<li><a href="my-long-page/#section2">Section 2</a></li>
</ul>
There are cases where this can be unhelpful. HTML fragments created from Ajax responses are the most common. In these
situations, you can disable fragment link rewriting like so:
:::php
SSViewer::setOption('rewriteHashlinks', false);
## Designing reusable templates
Although SilverStripe is ultimately flexible in how you create your templates, there's a couple of best practices. These
will help you to design templates for modules, and make it easier for other site developers to integrate them into their
own base templates.
* Most of your templates should be Layout templates
* Build your templates as a [Theme](/topics/themes) so you can easily re-use and exchange them
* Your layout template should include a standard markup structure (`<div id="Layout">$Layout</div>`)
* Layout templates only include content that could be completely replaced by another module (e.g. a forum thread). It
might be infeasible to do this 100%, but remember that every piece of navigation that needs to appear inside `$Layout`
will mean that you have to customise templates when integrating the module.
* Any CSS applied to layout templates should be flexible width. This means the surrounding root template can set its
width independently.
* Don't include any navigation elements in your Layout templates, they should be contained in the root template.
* Break down your templates into groups of includes. Site integrators would then have the power to override individual
includes, rather than entire templates.
For more information about templates go to the [Advanced Templates](/reference/advanced-templates) page.
## Related
* [Built in page controls](/reference/built-in-page-controls)
* [Page Type Templates](/topics/page-type-templates)
* [Typography](/reference/typography)
* [Themes](/topics/themes)
* [Widgets](/topics/widgets)
* [Images](/reference/image)
* [Tutorial 1: Building a basic site](/tutorials/1-building-a-basic-site)
* [Tutorial 2: Extending a basic site](/tutorials/2-extending-a-basic-site)
* [Developing Themes](/topics/theme-development)
* [Templates: formal syntax description](/reference/templates-formal-syntax)

View File

@ -0,0 +1,135 @@
# Building templates for page types
Much of your work building a SilverStripe site will involve the creation of templates for your [page types](/topics/page-types). SilverStripe has its own template language, which is described in full [here](/reference/templates).
SilverStripe templates consist of HTML code augmented with special control codes, described below. Because of this, you can have as much control of your site's HTML code as you like.
Take a look at mysite/templates/Page.ss. It contains standard HTML markup, with some extra tags. You can see that this file only generates some of the content it sets up the `<html>` tags, deals with the `<head>` section, creates the first-level navigation, and then closes it all off again. See `$Layout`? Thats what is doing most of the work when you visit a page.
Now take a look at `mysite/templates/Layout/Page.ss`. This as you can see has a lot more markup in it its what is included into `$Layout` when the Page page type is rendered. Similarly, `mysite/templates/Layout/HomePage.ss` would be rendered into `$Layout` when the HomePage page type is selected for the current page youre viewing.
Here is a very simple pair of templates. We shall explain their contents item by item.
`templates/Page.ss`
:::ss
<html>
<%-- This is my first template --%>
<head>
<% base_tag %>
<title>$Title</title>
$MetaTags
</head>
<body>
<div id="Container">
<div id="Header">
<h1>Bob's Chicken Shack</h1>
<% with $CurrentMember %>
<p>You are logged in as $FirstName $Surname.</p>
<% end_with %>
</div>
<div id="Navigation">
<% if $Menu(1) %>
<ul>
<% loop $Menu(1) %>
<li><a href="$Link" title="Go to the $Title page" class="$LinkingMode">$MenuTitle</a></li>
<% end_loop %>
</ul>
<% end_if %>
</div>
<div class="typography">
$Layout
</div>
<div id="Footer">
<p>Copyright $Now.Year</p>
</div>
</div>
</body>
</html>
`templates/Layout/Page.ss`
<h1>$Title</h1>
$Content
$Form
## <%-- This is my first template --%>
This is a comment. Like HTML comments, these tags let you include explanatory information in your comments. Unlike HTML comments, these tags won't be included in the HTML file downloaded by your visitors.
## <% base_tag %>
This tag must always appear in the `<head>` of your templates. SilverStripe uses a combination of a site-wide base tag and relative links to ensure that a site can function when loaded into a subdirectory on your webserver, as this is handy when developing a site. For more information see the [templates reference](/reference/templates#base-tag)
### $MetaTags
The `$MetaTags` placeholder in a template returns a segment of HTML appropriate for putting into the `<head>` tag. It
will set up title, keywords and description meta-tags, based on the CMS content and is editable in the 'Meta-data' tab
on a per-page basis. If you dont want to include the title-tag `<title>` (for custom templating), use
`$MetaTags(false)`.
By default `$MetaTags` renders:
:::ss
<title>Title of the Page</title>
<meta name="generator" http-equiv="generator" content="SilverStripe 2.0" >
<meta http-equiv="Content-type" content="text/html; charset=utf-8" >
### <% with $CurrentMember %>...<% end_with %>
This is a "with" block. A with block will change the "scope" of the template, so that all template variables inside that block will contain values from the $CurrentMember object, rather than from the page being rendered.
`$CurrentMember` is an object with information about the currently logged in member. If no-one is logged in, then it's blank. In that case, the entire `<% with $CurrentMember %>` block will be omitted.
### $FirstName, $Surname
These two variables come from the `$CurrentMember` object, because they are inside the `<% with $CurrentMember %>` block. In particular, they will contain the first name and surname of the currently logged in member.
### <% if $Menu(1) %>...<% end_if %>
This template code defines a conditional block. Its contents will only be shown if `$Menu(1)` contains anything.
`$Menu(1)` is a built-in page control that defines the top-level menu. You can also create a sub-menu using `$Menu(2)`, and a third-level menu using using `$Menu(3)`, etc.
### <% loop $Menu(1) %>...<% end_loop %> %>
This template code defines a repeating element. `$Menu(1)`. Like `<% with %>`, the loop block will change the "scope" of your template, which means that all of the template variables you use inside it will refer to a menu item. The template code will be repeated once per menu item, with the scope set to that menu item's page.
### $Link, $Title, $MenuTitle
Because these 3 variables are within the repeating element, then their values will come from that repeating element. In this case, they will be the values of each menu item.
* `$Link`: A link to that menu item.
* `$Title`: The page title of that menu item.
* `$MenuTitle`: The navigation label of that menu item.
### $LinkingMode
Once again, this is a variable that will be source from the menu item. This variable differs for each menu item, and will be set to one of these 3 values:
* `link`: You are neither on this page nor in this section.
* `current`: You are currently on this page.
* `section`: The current page is a child of this menu item; so this is menu item identifies the section that you're currently in.
By setting the HTML class to this value, you can distinguish the styling of the currently selected menu item.
### $Layout
This variable will be replaced with the the rendered version of `templates/Layout/Page.ss`. If you create a page type that is a subclass of Page, then it is possible to only define `templates/Layout/MySubclassPage.ss`. In that case, then the rendered version of `templates/Layout/MySubclassPage.ss` wil be inserted into the `$Layout` variable in `templates/Page.ss`. This is a good way of defining a single main template and page specific sub-templates.
### $Now.Year
This will return the current year. `$Now` returns an `SS_Datetime` object, which has a number of methods, such as `Year`. See [the API documentation](api:SS_Datetime) for a list of all the methods.
### $Title
This is the same template code as used in the title attribute of your navgation. However, because we are using it outside of the `<% loop Menu(1) >` block, it will return the title of the current page, rather than the title of the menu item. We use this to make our main page title.
### $Content
This variable contains the content of the current page.
### $Form
Very often, a page will contain some content and a form of some kind. For example, the log-in page has a log-in form. If you are on such a page, this variable will contain the HTML content of the form. Putting it just below $Content is a good default.

View File

@ -12,15 +12,10 @@ when the form is submitted
All the pages on the base installation are of the page type "Page". See
[tutorial:2-extending-a-basic-site](/tutorials/2-extending-a-basic-site) for a good introduction to page-types.
## Page type templates
Each page type on your website is a sub-class of the SiteTree class. Usually, youll define a class called Page
and use this template to lay out the basic design elements that dont change. Take a look at mysite/templates/Page.ss.
It contains standard HTML markup, with some differences. Well go over these later, but for now, you can see that this
file only generates some of the content it sets up the `<html>` tags, deals with the `<head>` section, creates the
first-level navigation, and then closes it all off again. See $Layout? Thats what is doing most of the work when you
visit a page. Now take a look at `mysite/templates/Layout/Page.ss`. This as you can see has a lot more markup in it
its what is included into $Layout when the Page page type is rendered. Similarly,
`mysite/templates/Layout/HomePage.ss` would be rendered into $Layout when the HomePage page type is selected for the
current page youre viewing.
and use this template to lay out the basic design elements that dont change.
Why do we sub-class Page for everything? The easiest way to explain this is to use the example of a search form. If we
create a search form on the Page class, then any other sub-class can also use it in their templates. This saves us
@ -43,6 +38,18 @@ We put the Page class into a file called Page.php inside `mysite/code`. We also
classes that are based on Page for example, the class Page_AnythingElse will also go into Page.php. Likewise, the
StaffPage_Image class will go into StaffPage.php.
## Templates
Take a look at mysite/templates/Page.ss. It contains standard HTML markup, with some differences. Well go over
these later, but for now, you can see that this file only generates some of the content it sets up the
`<html>` tags, deals with the `<head>` section, creates the first-level navigation, and then closes it all off again.
See $Layout? Thats what is doing most of the work when you visit a page. Now take a look at `mysite/templates/Layout/Page.ss`.
This as you can see has a lot more markup in it its what is included into $Layout when the Page page type is rendered.
Similarly, `mysite/templates/Layout/HomePage.ss` would be rendered into $Layout when the HomePage page type is selected for the
current page youre viewing.
See the [Page Type Templates](/topics/page-type-templates) page for more information.
## Adding database-fields
Adding database fields is a simple process. You define them in an array of the static variable `$db`, this array is

View File

@ -1,124 +0,0 @@
# Templates
## Introduction
SilverStripe templates consist of HTML code augmented with special control codes, described below. Because of this, you
can have as much control of your site's HTML code as you like.
Because the SilverStripe templating language is a string processing language it can therefore be used to make other
text-based data formats, such as XML or RTF.
Here is a very simple template:
:::ss
<html>
<head>
<% base_tag %>
<title>$Title</title>
$MetaTags
</head>
<body>
<div id="Container">
<div id="Header">
<h1>Bob's Chicken Shack</h1>
</div>
<div id="Navigation">
<% if Menu(1) %>
<ul>
<% control Menu(1) %>
<li><a href="$Link" title="Go to the $Title page" class="$LinkingMode">$MenuTitle</a></li>
<% end_control %>
</ul>
<% end_if %>
</div>
<div class="typography">
$Layout
</div>
<div id="Footer">
<p>Copyright $Now.Year</p>
</div>
</div>
</body>
</html>
<%-- comment --%>
## Default Template Syntax
These tags appear on the default template and should be included in every theme:
### Base Tag
The `<% base_tag %>` placeholder is replaced with the HTML base element. Relative links within a document (such as `<img
src="someimage.jpg" />`) will become relative to the URI specified in the base tag. This ensures the browser knows where
to locate your sites images and css files. So it is a must for templates!
It renders in the template as `<base href="http://www.mydomain.com" />`
### Meta Tags
The `$MetaTags` placeholder in a template returns a segment of HTML appropriate for putting into the `<head>` tag. It
will set up title, keywords and description meta-tags, based on the CMS content and is editable in the 'Meta-data' tab
on a per-page basis. If you dont want to include the title-tag `<title>` (for custom templating), use
`$MetaTags(false)`.
By default `$MetaTags` renders:
:::ss
<title>Title of the Page</title>
<meta name="generator" http-equiv="generator" content="SilverStripe 2.0" >
<meta http-equiv="Content-type" content="text/html; charset=utf-8" >
TODO Explain SiteTree properties and SiteTree->MetaTags() overloading
### Including CSS and JavaScript files
See [CSS](/topics/css) and [Javascript](/topics/javascript) topics for individual including of files and
[requirements](reference/requirements) for good examples of including both Javascript and CSS files.
### Layout Tag
In every SilverStripe theme there is a default `Page.ss` file in the `/templates` folder. `$Layout` appears in this file
and is a core variable which includes a Layout template inside the `/templates/Layout` folder once the page is rendered.
By default the `/templates/Layout/Page.ss` file is included in the html template.
### .typography style
By default, SilverStripe includes the `theme/css/typography.css` file into the Content area. So you should always include the
typography style around the main body of the site so both styles appear in the CMS and on the template. Where the main body of
the site is can vary, but usually it is included in the /Layout files. These files are included into the main Page.ss template
by using the `$Layout` variable so it makes sense to add the .typography style around $Layout.
:::ss
<div class="typography">
$Layout
</div>
## Designing reusable templates
Although SilverStripe is ultimately flexible in how you create your templates, there's a couple of best practices. These
will help you to design templates for modules, and make it easier for other site developers to integrate them into their
own base templates.
* Most of your templates should be Layout templates
* Build your templates as a [Theme](/topics/themes) so you can easily re-use and exchange them
* Your layout template should include a standard markup structure (`<div id="Layout">$Layout</div>`)
* Layout templates only include content that could be completely replaced by another module (e.g. a forum thread). It
might be infeasible to do this 100%, but remember that every piece of navigation that needs to appear inside `$Layout`
will mean that you have to customise templates when integrating the module.
* Any CSS applied to layout templates should be flexible width. This means the surrounding root template can set its
width independently.
* Don't include any navigation elements in your Layout templates, they should be contained in the root template.
* Break down your templates into groups of includes. Site integrators would then have the power to override individual
includes, rather than entire templates.
For more information about templates go to the [Advanced Templates](/reference/advanced-templates) page.
## Related
* [Advanced Templates](/reference/advanced-templates)
* [Typography](/reference/typography)
* [themes](/topics/themes)
* [widgets](/topics/widgets)
* [images](/reference/image)
* [Tutorial 1: Building a basic site](/tutorials/1-building-a-basic-site)
* [Tutorial 2: Extending a basic site](/tutorials/2-extending-a-basic-site)