Consolidated template and page-type docs

- Removed duplicated content from page-type-templates (was more or less a variation of the content in templates.md)
- Removed built-in page-controls, which was a bit of a dumping ground for unconnected topics.
  Moved the majority to page-type-templates
- Removed all recipes from "sitetree" docs, since they were outdated or hacky (like grouping of records, or implementing custom *children() method on subclasses)
- Added pagination, escaping, base_tag, CurrentMember to template docs
- Removed default_parent docs from SiteTree, as this setting doesn't have any effect looking at core
This commit is contained in:
Ingo Schommer 2012-06-27 16:09:21 +02:00
parent 34a2ce23d8
commit 0b31234810
9 changed files with 482 additions and 849 deletions

View File

@ -21,6 +21,10 @@ information.
return new PaginatedList(Page::get(), $this->request);
}
Note that the concept of "pages" used in pagination does not necessarily
mean that we're dealing with `Page` classes, its just a term to describe
a sub-collection of the list.
## Setting Up The Template
Now all that remains is to render this list into a template, along with pagination

View File

@ -1,347 +0,0 @@
# Built-in Page Controls
Ever wonder when you use `$Title` and `<% Control Children %>` what else you can call in the templates?. This page is
here to help with a guide on what template controls you can call.
**Note for advanced users:** These built-in page controls are defined in the [api:SiteTree] classes, which are the
'root' data-object and controller classes for all the sites. So if you're dealing with something that isn't a sub-class
of one of these, our handy reference to 'built-in page controls' won't be so relevant.
## Page controls that can't be nested
These page controls are defined on the **controller** which means they can only be used at a top level, not nested
within another page control.
### Controlling Menus Datafeeds
#### <% control Menu(1) %>, <% control Menu(2) %>, ...
Returns a fixed level menu. Because this only works in the top level, you can't use it for nested menus. Use
`<% control Children %>` instead. You can nest `<% control Children %>`.
#### <% control ChildrenOf(page-url) %>
This will create a datafeed of the children of the given page. Handy if you want a list of the subpages under staff (eg
the staff) on the homepage etc
### Controlling Certain Pages
#### <% control Level(1) %>, <% control Level(2) %>, $Level(1).Title, $Level(2).Content, etc
Returns the current section of the site that we're in, at the level specified by the numbers. For example, imagine
you're on the page __about us > staff > bob marley__:
* `<% control Level(1) %>` would return the about us page
* `<% control Level(2) %>` would return the staff page
* `<% control Level(3) %>` would return the bob marley page
#### <% control Page(my-page) %>$Title<% end_control %>
"Page" will return a single page from the site tree, looking it up by URL. You can use it in the `<% control %>` format.
Can't be called using `$Page(my-page).Title`.
## Page controls that can be used anywhere
These are defined in the data-object and so can be used as nested page controls. Lucky us! we can control Children of
Children of Children for example.
### Conditional Logic
SilverStripe supports a simple set of conditional logic
:::ss
<% if Foo %>
// if Foo is true or an object do this
<% else_if Bar %>
// if Bar is true or an object do this
<% else %>
// then do this by default
<% end_if %>
See more information on conditional logic on [templates](/topics/templates).
### Site wide settings
Since 2.4.0, SilverStripe provides a generic interface for accessing global properties such as *Site name* or *Site tag
line*. This interface is implemented by the [api:SiteConfig] class.
### Controlling Parents and Children
#### <% control Children %>
This will return the children of the current page as a nested datafeed. Useful for nested navigations such as pop-out
menus.
#### <% control AllChildren %>
This will show all children of a page even if the option 'show in menus?' is unchecked in the tab panel behaviour.
#### <% control Parent %> or $Parent.Title, $Parent.Content, etc
This will return the parent page. The $ variable format lets us reference an attribute of the parent page directly.
### Site Navigation - Breadcrumbs
#### $Breadcrumbs
This will return a breadcrumbs widget for the current page. You can call this on any SiteTree descendant, so, for
example, you could display the breadcrumbs of every search result if you wanted. The Breadcrumbs method returns a string
of text, so this can't be used as a control block (that is, you can't usefully say "<% control Breadcrumbs %>"). You can
limit the number of items in the breadcrumbs, as well as whether the breadcrumb items are links.
#### $Breadcrumbs(3)
This returns a maximum of 3 pages in the breadcrumb list, which can be handy if you want to limit the size of your
breadcrumbs to conform to your page design.
#### <% control Breadcrumbs(3, true) %>
This returns the same, but without any links. This is handy if you want to put the breadcrumb list into another link
tag.
### Links and Classes
#### $LinkingMode, $LinkOrCurrent and $LinkOrSection
These return different linking modes. $LinkingMode provides the greatest control, outputting 3 different strings:
* link: Neither this page nor any of its children are current open.
* section: A child of this page is currently open, which means that we're currently in this section of the site.
* current: This page is currently open.
A useful way of using this is in your menus. You can use the following code below to generate class="current" or
class="section" on your links. Take the following code
:::ss
<li><a href="$Link" class="$LinkingMode">$Title</a></li>
When viewed on the Home page it will render like this
:::ss
<li><a href="home/" class="current">Home</a></li>
`$LinkOrCurrent` ignores the section status, returning link instead. `$LinkOrSection` ignores the current status, returning section instead. Both of these options can simplify your CSS when you only have 2 different cases to consider.
#### <% if LinkOrCurrent = current %>
This is an alternative way to set up your menus - if you want different HTML for the current menu item, you can do
something like this:
:::ss
<% if LinkOrCurrent = current %>
<strong>$Title</strong>
<% else %>
<a href="$Link">$Title</a>
<% end_if %>
#### <% if LinkOrSection = section %>
Will return true if you are on the current page OR a child page of the page. Useful for menus which you only want to
show a second level menu when you are on that page or a child of it
#### <% if InSection(page-url) %>
This if block will pass if we're currently on the page-url page or one of its children.
### Titles and CMS Defined Options
#### $MetaTags
This 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. If you don't want to include the title-tag (for custom templating), use
**$MetaTags(false)**.
#### $MenuTitle
This is the title that you should put into navigation menus. CMS authors can choose to put a different menu title from
the main page title.
#### $Title
This is the title of the page which displays in the browser window and usually is the title of the page.
:::ss
<h1>$Title</h1>
#### $URLSegment
This returns the part of the URL of the page you're currently on. Could be handy to use as an id on your body-tag. (
when doing this, watch out that it doesn't create invalid id-attributes though.). This is useful for adding a class to
the body so you can target certain pages. Watch out for pages named clear or anything you might have used in your CSS
file
:::ss
<body class="$URLSegment">
#### $ClassName
Returns the ClassName of the PHP object. Eg if you have a custom HomePage page type with `$ClassName` in the template, it
will return "HomePage"
#### $BaseHref
Returns the base URL for the current site. This is used to populate the `<base>` tag by default, so if you want to
override `<% base_tag %>` with a specific piece of HTML, you can do something like `<base href="$BaseHref"></base>`
### Controlling Members and Visitors Data
#### <% control CurrentMember %>, <% if CurrentMember %> or $CurrentMember.FirstName
CurrentMember returns the currently logged in member, if there is one. All of their details or any special Member page
controls can be called on this. Alternately, you can use `<% if CurrentMember %>` to detect whether someone has logged
in. To Display a welcome message you can do
:::ss
<% if CurrentMember %>
Welcome Back, $CurrentMember.FirstName
<% end_if %>
If the user is logged in this will print out
:::ss
Welcome Back, Admin
#### <% if IsRepeatMember %>
Detect the visitor's previous experience with the site. `$IsRepeatMember` will return true if the visitor has signed up or logged in on the site before.
Note that as of version 2.4 `$PastVisitor` is deprecated. If you wish to check if a visitor has been to the site before, set a cookie with `Cookie::set()` and test for it with `Cookie::get()`.
Note that in 2.4 this variable was called `$PastMember`. This still works in 3.0 but is deprecated.
### Date and Time
#### $Now.Nice, $Now.Year
`$Now` returns the current date. You can call any of the methods from the [api:Date] class on
it.
#### $Created.Nice, $Created.Ago
`$Created` returns the time the page was created, `$Created.Ago` returns how long ago the page was created. You can also
call any of methods of the [api:Date] class on it.
#### $LastEdited.Nice, $LastEdited.Ago
`$LastEdited `returns the time the page was modified, `$LastEdited.Ago` returns how long ago the page was modified. You
can also call any of methods of the [api:Date] class on it.
### DataObjectSet Options
If you are using a DataObjectSet you have a wide range of methods you can call on it from the templates
#### <% if Even %>, <% if Odd %>, $EvenOdd
These controls can be used to do zebra-striping. `$EvenOdd` will return 'even' or 'odd' as appropriate.
#### <% if First %>, <% if Last %>, <% if Middle %>, $FirstLast
These controls can be used to set up special behaviour for the first and last records of a datafeed. `<% if Middle %>` is
set when neither first not last are set. `$FirstLast` will be 'first', 'last', or ''.
#### $Pos, $TotalItems
`$TotalItems` will return the number of items on this page of the datafeed, and `$Pos` will return a counter starting at 1.
#### $Top
When you're inside a control loop in your template, and want to reference methods on the current controller you're on,
breaking out of the loop to get it, you can use `$Top` to do so. For example:
:::ss
$URLSegment
<% control News %>
$URLSegment <!-- may not return anything, as you're requesting URLSegment on the News objects -->
$Top.URLSegment <!-- returns the same as $URLSegment above -->
<% end_control %>
## Properties of a datafeed itself, rather than one of its items
If we have a control such as `<% control SearchResults %>`, there are some properties, such as `$SearchResults.NextLink`,
that aren't accessible within `<% control SearchResults %>`. These can be used on any datafeed.
### Search Results
#### <% if SearchResults.MoreThanOnePage %>
Returns true when we have a multi-page datafeed, restricted with a limit.
#### $SearchResults.NextLink, $SearchResults.PrevLink
This returns links to the next and previous page in a multi-page datafeed. They will return blank if there's no
appropriate page to go to, so `$PrevLink` will return blank when you're on the first page. You can therefore use
`<% if PrevLink %>` to keep your template tidy.
#### $SearchResults.CurrentPage, $SearchResults.TotalPages
CurrentPage returns the number of the page you're currently on, and TotalPages returns the total number of pages.
#### $SearchResults.TotalItems
This returns the total number of items across all pages.
#### <% control SearchResults.First %>, <% control SearchResults.Last %>
These controls return the first and last item on the current page of the datafeed.
#### <% control SearchResults.Pages %>
This will return another datafeed, listing all of the pages in this datafeed. It will have the following data
available:
* **$PageNum:** page number, starting at 1
* **$Link:** a link straight to that page
* `<% if CurrentBool %>`:** returns true if you're currently on that page
`<% control SearchResults.Pages(30) %>` will show a maximum of 30 pages, useful in situations where you could get 100s of
pages returned.
#### $SearchResults.UL
This is a quick way of generating a `<ul>` containing an `<li>` and `<a>` for each item in the datafeed. Usually too
restricted to use in a final application, but handy for debugging stuff.
## Quick Reference
Below is a list of fields and methods that are typically available for templates (grouped by their source) - use this as
a quick reference (not all of them are described above):
### All methods available in Page_Controller
$NexPageLink, $Link, $RelativeLink, $ChildrenOf, $Page, $Level, $Menu, $Section2, $LoginForm, $SilverStripeNavigator,
$PageComments, $Now, $LinkTo, $AbsoluteLink, $CurrentMember, $PastVisitor, $PastMember, $XML_val, $RAW_val, $SQL_val,
$JS_val, $ATT_val, $First, $Last, $FirstLast, $MiddleString, $Middle, $Even, $Odd, $EvenOdd, $Pos, $TotalItems,
$BaseHref, $Debug, $Top
### All fields available in Page_Controller
$ID, $ClassName, $Created, $LastEdited, $URLSegment, $Title, $MenuTitle, $Content, $MetaTitle, $MetaDescription,
$MetaKeywords, $ShowInMenus, $ShowInSearch, $HomepageForDomain, $ProvideComments, $Sort, $LegacyURL, $HasBrokenFile,
$HasBrokenLink, $Status, $ReportClass, $ParentID, $Version, $EmailTo, $EmailOnSubmit, $SubmitButtonText,
$OnCompleteMessage, $Subscribe, $AllNewsletters, $Subject, $ErrorCode, $LinkedPageID, $RedirectionType, $ExternalURL,
$LinkToID, $VersionID, $CopyContentFromID, $RecordClassName
### All methods available in Page
$Link, $LinkOrCurrent, $LinkOrSection, $LinkingMode, $ElementName, $InSection, $Comments, $Breadcrumbs, $NestedTitle,
$MetaTags, $ContentSource, $MultipleParents, $TreeTitle, $CMSTreeClasses, $Now, $LinkTo, $AbsoluteLink, $CurrentMember,
$PastVisitor, $PastMember, $XML_val, $RAW_val, $SQL_val, $JS_val, $ATT_val, $First, $Last, $FirstLast, $MiddleString,
$Middle, $Even, $Odd, $EvenOdd, $Pos, $TotalItems, $BaseHref, $Top
### All fields available in Page
$ID, $ClassName, $Created, $LastEdited, $URLSegment, $Title, $MenuTitle, $Content, $MetaTitle, $MetaDescription,
$MetaKeywords, $ShowInMenus, $ShowInSearch, $HomepageForDomain, $ProvideComments, $Sort, $LegacyURL, $HasBrokenFile,
$HasBrokenLink, $Status, $ReportClass, $ParentID, $Version, $EmailTo, $EmailOnSubmit, $SubmitButtonText,
$OnCompleteMessage, $Subscribe, $AllNewsletters, $Subject, $ErrorCode, $LinkedPageID, $RedirectionType, $ExternalURL,
$LinkToID, $VersionID, $CopyContentFromID, $RecordClassName

View File

@ -167,7 +167,7 @@ Put something like this code in mysite/code/Page.php inside class Page_Controlle
}
Put something like this code in mysite/templates/Layout/HomePage.ss:
Put something like this code in `themes/<your-theme>/templates/Layout/HomePage.ss`:
:::ss
<h3>My Latest Del.icio.us Links</h3>

View File

@ -1,4 +1,4 @@
# SiteConfig
# SiteConfig: Global database content
## Introduction

View File

@ -1,11 +1,13 @@
# Sitetree
## Introduction
Basic data-object representing all pages within the site tree. The omnipresent *Page* class (located in
*mysite/code/Page.php*) is based on this class.
Basic data-object representing all pages within the site tree.
The omnipresent *Page* class (located in `mysite/code/Page.php`) is based on this class.
## Creating, Modifying and Finding Pages
See the ["datamodel" topic](/topics/datamodel).
## Linking
@ -15,6 +17,11 @@ Basic data-object representing all pages within the site tree. The omnipresent *
// right
$mylink = $mypage->Link(); // alternatively: AbsoluteLink(), RelativeLink()
In a nutshell, the nested URLs feature means that your site URLs now reflect the actual parent/child page structure of
your site. The URLs map directly to the chain of parent and child pages. The
below table shows a quick summary of what these changes mean for your site:
![url table](http://silverstripe.org/assets/screenshots/Nested-URLs-Table.png)
## Querying
@ -27,46 +34,72 @@ might consist of more than one *URLSegment*).
// right
$mypage = SiteTree::get_by_link('<mylink>');
### Versioning
The `SiteTree` class automatically has an extension applied to it: `[Versioned](api:Versioned)`.
This provides the basis for the CMS to operate on different stages,
and allow authors to save their changes without publishing them to
website visitors straight away.
`Versioned` is a generic extension which can be applied to any `DataObject`,
so most of its functionality is explained in the `["versioning" topic](/topics/versioning)`.
Since `SiteTree` makes heavy use of the extension, it adds some additional
functionality and helpers on top of it.
## Nested/Hierarchical URLs
Permission control:
In a nutshell, the nested URLs feature means that your site URLs now reflect the actual parent/child page structure of
your site. The URLs map directly to the chain of parent and child pages. The
below table shows a quick summary of what these changes mean for your site:
:::php
class MyPage extends Page {
function canPublish($member = null) {
// return boolean from custom logic
}
function canDeleteFromLive($member = null) {
// return boolean from custom logic
}
}
![url table](http://silverstripe.org/assets/screenshots/Nested-URLs-Table.png)
Stage operations:
## Limiting Children/Parent
* `$page->doUnpublish()`: removes the "Live" record, with additional permission checks,
as well as special logic for VirtualPage and RedirectorPage associations
* `$page->doPublish()`: Inverse of doUnpublish()
* `$page->doRevertToLive()`: Reverts current record to live state (makes sense to save to "draft" stage afterwards)
* `$page->doRestoreToStage()`: Restore the content in the active copy of this SiteTree page to the stage site.
By default, any page type can be the child of any other page type. However, there are 4 static properties that can be
Hierarchy operations (defined on `[api:Hierarchy]`:
* `$page->liveChildren()`: Return results only from live table
* `$page->stageChildren()`: Return results from the stage table
* `$page->AllHistoricalChildren()`: Return all the children this page had, including pages that were deleted from both stage & live.
* `$page->AllChildrenIncludingDeleted()`: Return all children, including those that have been deleted but are still in live.
## Limiting Hierarchy
By default, any page type can be the child of any other page type.
However, there are static properties that can be
used to set up restrictions that will preserve the integrity of the page hierarchy.
Example: Restrict blog entry pages to nesting underneath their blog holder
:::php
class BlogHolder extends Page {
// Blog holders can only contain blog entries
static $allowed_children = array("BlogEntry");
static $default_child = "BlogEntry";
...
// ...
}
class BlogEntry extends Page {
// Blog entries can't contain children
static $allowed_children = "none";
static $default_parent = "blog";
static $can_be_root = false;
...
// ...
}
class Page extends SiteTree {
// Don't let BlogEntry pages be underneath Pages. Only underneath Blog holders.
static $allowed_children = array("*Page,", "BlogHolder");
}
@ -77,210 +110,33 @@ subclasses. Otherwise, the class and all its subclasses are allowed.
* **default_child:** If a page is allowed more than 1 type of child, you can set a default. This is the value that
will be automatically selected in the page type dropdown when you create a page in the CMS.
* **default_parent:** This should be set to the *URLSegment* of a specific page, not to a class name. If you have
asked to create a page of a particular type that's not allowed underneath the page that you have selected, then the
default_parent page will be selected. For example, if you have a gallery page open in the CMS, and you select add blog
entry, you can set your site up to automatically select the blog page as a parent.
* **can_be_root:** This is a boolean variable. It lets you specify whether the given page type can be in the top
level.
Note that there is no allowed_parents control. To set this, you will need to specify the allowed_children of all other
page types to exclude the page type in question. IMO this is less than ideal; it's possible that in a future release we
will add allowed_parents, but right now we're trying to limit the amount of mucking around with the API we do.
Note that there is no allowed_parents` control. To set this, you will need to specify the `allowed_children` of all other page types to exclude the page type in question.
Here is an overview of everything you can add to a class that extends sitetree. NOTE: this example will not work, but
it is a good starting point, for choosing your customisation.
## Permission Control
## Tree Display (Description, Icons and Badges)
The page tree in the CMS is a central element to manage page hierarchies,
hence its display of pages can be customized as well.
On a most basic level, you can specify a custom page icon
to make it easier for CMS authors to identify pages of this type,
when navigating the tree or adding a new page:
:::php
class Page extends SiteTree {
// tree customisation
static $icon = "";
static $allowed_children = array("SiteTree"); // set to string "none" or array of classname(s)
static $default_child = "Page"; //one classname
static $default_parent = null; // NOTE: has to be a URL segment NOT a class name
static $can_be_root = true; //
static $hide_ancestor = null; //dont show ancestry class
// extensions and functionality
static $versioning = array();
static $default_sort = "Sort";
/static $extensions = array();
public static $breadcrumbs_delimiter = " &raquo; ";
public function canCreate() {
//here is a trick to only allow one (e.g. holder) of a page
$class = $this->class;
return !$class::get()->Count();
}
public function canDelete() {
return false;
}
public function getCMSFields() {
$fields = parent::getCMSFields();
return $fields;
}
## Recipes
### Automatic Child Selection
By default, `[api:SiteTree]` class to build a tree using the ParentID field. However, sometimes, you want to change
this default behaviour.
For example, in our e-commerce module, we use a many-to-many join, Product::Parents, to let you put Products in multiple
groups. Here's how to implement such a change:
* **Set up your new data model:** Create the appropriate many-many join or whatever it is that you're going to use to
store parents.
* **Define stageChildren method:** This method should return the children of the current page, for the current version.
If you use DataObject::get, the `[api:Versioned]` class will rewrite your query to access the live site when
appropriate.
* **Define liveChildren method:** The method should return the children of the current page, for the live site.
Both the CMS and the site's data controls will make use of this, so navigation, breadcrumbs, etc will be updated. If 1
node appears in the tree more than once, it will be represented differently.
**TO DO:** Work out this representation.
### Custom Children Getters
Returning custom children for a specific `SiteTree` subclass can be handy to influence the tree display within the
CMS. An example of custom children might be products which belong to multiple categories. One category would get its
products from a `$many_many` join rather than the default relations.
Children objects are generated from two functions `stageChildren()` and `liveChildren()` and the tree generation in
the CMS is calculated from `numChildren()`. Please keep in mind that the returned children should still be instances
of `SiteTree`.
Example:
:::php
class MyProduct extends Page {
static $belongs_many_many = array(
'MyCategories' => 'MyCategory'
);
}
class MyCategory extends Page {
static $many_many = array(
'MyProducts' => 'MyProduct'
);
public function stageChildren($showAll = false) {
// @todo Implement $showAll
return $this->MyProducts();
}
public function liveChildren($showAll = false) {
// @todo Implement $showAll
return $this->MyProducts();
}
public function numChildren() {
return $this->MyProducts()->Count();
}
} }
class StaggPage extends Page {
static $singular_name = 'Staff Directory';
static $plural_name = 'Staff Directories';
static $description = 'Two-column layout with a list of staff members';
static $icon = 'mysite/images/staff-icon.png';
// ...
}
### Multiple parents in the tree
The `[api:LeftAndMain]` tree supports multiple parents. We overload CMSTreeClasses and make it include "manyparents" in
the class list.
:::php
public function CMSTreeClasses($controller) {
return parent::CMSTreeClasses($controller) . ' manyparents';
}
Don't forget to define a new Parent() method that also references your new many-many join (or however it is you've set
up the hierarchy!
:::php
public function getParent() {
return $this->Parent();
}
public function Parent() {
$parents = $this->Parents();
if($parents) return $parents->First();
}
Sometimes, you don't want to mess with the CMS in this manner. In this case, leave stageChildren() and liveChildren()
as-is, and instead make another method, such as ChildProducts(), to get the data from your many-many join.
### Dynamic Grouping
Something that has been talked about [here](http://www.silverstripe.com/site-builders-forum/flat/15416#post15940) is the
concept of "dynamic grouping". In essence, it means adding navigational tree nodes to the tree that don't correspond to
a database record.
How would we do this? In our example, we're going to update BlogHolder to show BlogEntry children grouped into months.
We will create a class called BlogMonthTreeNode, which will extend ViewableData instead of DataRecord, since it's not
saved into the database. This will represent our dynamic groups.
### LeftAndMain::getSiteTreeFor()
Currently LeftAndMain::getSiteTreeFor() Calls LeftAndMain::getRecord($id) to get a new record. We need to instead
create a new public function getTreeRecord($id) which will be able to create BlogMonthTreeNode objects as well as look up
SiteTree records from the database.
The IDs don't **need** be numeric; so we can set the system to allow for 2 $id formats.
* (ID): A regular SiteTree object
* BlogMonthTreeNode-(BlogHolderID)-(Year)-(Month): A BlogMonthTreeNode object
To keep the code generic, we will assume that if the $id isn't numeric, then we should explode('-', $id), and use the
first part as the classname, and all the remaining parts as arguments to the constructor.
Your BlogMonthTreeNode constructor will then need to take $blogHolderID, $year, $month as arguments.
### Divorcing front-end site's Children() and the CMS's AllChildrenIncludingDeleted()
We need a way of cleanly specifying that there are two different child sources - children for the CMS tree, and children
for the front-end site.
* We currently have stageChildren() / liveChildren()
* We should probably add cmsStageChildren() and cmsLiveChildren() into the mix, for SiteTree.
AllChildrenIncludingDeleted() could then call the "cms..." versions of the functions, but if we were to to this, we
should probably rename AllChildrenIncludingDeleted() to CMSTreeChildren() or something like that.
### BlogHolder::cmsStageChildren() & BlogHolder::cmsLiveChildren()
We will need to define these methods, to
* Get the stage/live children of the page, grouped by month
* For each entry returned, generate a new BlogMonthTreeNode object.
* Return that as a dataobjectset.
### BlogMonthTreeNode
* Parameter 'ID': should return 'BlogMonthTreeNode-(BlogHolderID)-(Year)-(Month)'. You can do this by implementing
getID().
* Methods cmsStageChildren() and cmsLiveChildren(): These should return the blog-entries for that month.
After that, there will be some other things to tweak, like the tree icons.
### Where to from here?
This is a lot of work for the specific example of blog-entries grouped by month. Instead of BlogMonthTreeNode, you
could genericise this to a DynamicTreeGroup class, which would let you specify the parent node, the type of grouping,
and the specific group.
## TODO
Clean up this documentation
## API Documentation
`[api:Sitetree]`
You can also add custom "badges" to each page in the tree,
which denote status. Built-in examples are "Draft" and "Deleted" flags.
This is detailed in the ["Customize the CMS Tree" howto](/howto/customize-cms-tree).

View File

@ -2,7 +2,7 @@
These are the main changes to the SiverStripe 3 template language.
## Control
## Control blocks: Loops vs. Scope
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.

View File

@ -12,62 +12,44 @@ Here is a very simple template:
:::ss
<html>
<%-- This is my first template --%>
<head>
<% base_tag %>
<title>$Title</title>
$MetaTags
<% require themedCSS(screen) %>
</head>
<body>
<div id="Container">
<div id="Header">
<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>
</header>
<% with $CurrentMember %>
<p>Welcome $FirstName $Surname.</p>
<% end_with %>
<% if Dishes %>
<ul>
<% loop Dishes %>
<li>$Title ($Price.Nice)</li>
<% end_loop %>
</ul>
<% end_if %>
<% include Footer %>
</body>
</html>
More sophisticated use of templates for pages managed in the CMS,
including template inheritance and navigation loops
is documented in the [page types](/topics/page-types) topic.
# 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.
@ -141,81 +123,90 @@ See [CSS](/topics/css) and [Javascript](/topics/javascript) topics for individua
[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 %>
:::ss
<% 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 %>
:::ss
<% 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 %>
:::ss
<% 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 %>
:::ss
<% 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 %>
:::ss
<% 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 %>
:::ss
<% 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 %>
:::ss
<% 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
## Looping Over Lists
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>
:::ss
<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.
This loops over the children of a page, and generates an unordered list showing the `Title` property from each one. Note that `$Title` *inside* 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.
### Position Indicators
Inside the loop scope, there are many variables at your disposal to determine the current position
in the list and iteration:
* `$Even`, `$Odd`: Returns boolean, handy for zebra striping
* `$EvenOdd`: Returns a string, either 'even' or 'odd'. Useful for CSS classes.
* `$First`, `$Last`, `$Middle`: Booleans about the position in the list
* `$FirstLast`: Returns a string, "first", "last", or "". Useful for CSS classes.
* `$Pos`: The current position in the list (integer). Will start at 1.
* `$TotalItems`: Number of items in the list (integer)
### Modulus and MultipleOf
@ -249,6 +240,8 @@ You can also use $MultipleOf(value, offset) to help build columned layouts. In t
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.
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`.
### With
The `<% with %>...<% end_with %>` tag lets you introduce a new scope. Consider the following example:
@ -265,31 +258,47 @@ Outside the `<% with %>...<% end_with %>`, we are in the page scope. Inside it,
returns the number of items in the $Children collection.
### Top
## Pagination
$Top.Title
Lists can be paginated, and looped over page-by-page.
For this to work, the list needs to be wrapped in a `[api:PaginatedList]`.
The process is explained in detail on the ["pagination" howto](/howto/pagination).
The list is split up in multiple "pages", each . Note that "page" is this context
does not necessarily refer to a `Page` class (although it often happens to be one).
### Up
* `$MoreThanOnePage`: Returns true when we have a multi-page list, restricted with a limit.
* `$NextLink`, `$PrevLink`: This returns links to the next and previous page in a multi-page datafeed. They will return blank if there's no appropriate page to go to, so `$PrevLink` will return blank when you're on the first page.
* `$CurrentPage`: Current page iterated on
* `$TotalPages`: Total number of pages
* `$TotalItems`: This returns the total number of items across all pages.
* `$Pages`: The actual (limited) list of records, use in an inner loop
* `$PageNum`: Page number, starting at 1 (within `$Pages`)
* `$Link`: Links to the current controller URL, setting this page as current via a GET parameter (within `$Pages`)
* `$CurrentBool`: Returns true if you're currently on that page (within `$Pages`)
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.
## Formatting and Casting
$Up.Owner
Properties are usually auto-escaped in templates to ensure consistent representation,
and avoid format clashes like displaying unescaped ampersands in HTML.
By default, values are escaped as `XML`, which is equivalent to `HTML` for this purpose.
There's some exceptions to this rule, see the ["security" topic](/topics/security).
## 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.
In case you want to explicitly allow unescaped HTML input,
the property can be cast as `[api:HTMLText]`.
The following example takes the `Content` field in a `SiteTree` class,
which is of this type. It forces the content into an explicitly escaped format.
:::ss
<% with Title %>
$XML
<% end_with %>
$Content.XML // transforms e.g. "<em>alert</em>" to "&lt;em&gt;alert&lt;/em&gt;"
Note that this code can be more concisely represented as follows:
Apart from value formatting, there's many methods to transform them as well,
For example, the built in `$Now` placeholder is an instance of `[api:Date]`,
and returns the current date in a standard system format.
Since its an object, you can use the helper methods to return other formats:
:::ss
$Title.XML
$Now.Year // Current year
$Now.Nice // Localized date, based on i18n::get_locale()
See [data-types](/topics/data-types) for more information.
@ -311,49 +320,64 @@ Pulling apart this example we see:
Using standard HTML comments is supported. These comments will be included in the published site.
:::ss
$EditForm <!-- Some Comment About the Edit Form -->
$EditForm <!-- Some public comment about the 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 --%>
$EditForm <%-- Some hidden comment about the form --%>
## 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.
Partial caching lets you define blocks of your template that are cached for better performance. See [Partial Caching](/reference/partial-caching) for more information.
## Creating your own Template Variables and Controls
### 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]-->`
## CurrentMember
Returns the currently logged in member, if there is one.
All of their details or any special Member page controls can be called on this.
Alternately, you can use `<% if CurrentMember %>` to detect whether someone has logged
in.
:::ss
<% if CurrentMember %>
Welcome Back, $CurrentMember.FirstName
<% end_if %>
## Custom 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**
...
// mysite/code/Page.php
public function MyCustomValue() {
return "Hi, this is my site";
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>
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
..
// ...
public function MyCustomValues() {
return new ArrayData(array("Hi" => "Kia Ora", "Name" => "John Smith"));
return new ArrayData(array("Hi" => "Kia Ora", "Name" => "John Smith"));
}
@ -363,23 +387,17 @@ And now you could call these values by using
<% with MyCustomValues %>
$Hi , $Name
<% end_with %>
// 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
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;
@ -396,22 +414,7 @@ and this template
$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.
will render as "1, 1, 1", not "1, 2, 3"
## .typography style
@ -466,8 +469,7 @@ default if it exists and there is no action in the url parameters.
public function index() {
if(Director::is_ajax()) {
return $this->renderWith("myAjaxTemplate");
}
else {
} else {
return Array();// execution as usual in this case...
}
}
@ -508,7 +510,18 @@ situations, you can disable fragment link rewriting like so:
:::php
SSViewer::setOption('rewriteHashlinks', false);
### More Advanced Controls
Template variables and controls are just PHP properties and methods
on the underlying controllers and model classes.
We've just shown you the most common once, in practice
you can use any public API on those classes, and [extend](/reference/dataextension) them
with your own. To get an overview on what's available to you,
we recommend that you dive into the API docs for the following classes:
* `[api:Controller]`: Generic controller class
* `[api:DataObject]`: Generic model class
* `[api:ViewableData]`: Underlying object class for pretty much anything displayable
## Designing reusable templates
@ -516,7 +529,7 @@ Although SilverStripe is ultimately flexible in how you create your templates, t
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
* 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
@ -524,7 +537,7 @@ might be infeasible to do this 100%, but remember that every piece of navigation
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.
* 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.

View File

@ -1,68 +1,227 @@
# 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).
Much of your work building a SilverStripe site will involve the creation of
templates for your own page types. SilverStripe has its own template language.
Its basic features like variables, blocks and loops are described in our ["templates" reference guide](/reference/templates).
In this guide, we'll show you specific uses for creating page layouts.
This assumes you are familiar with the concept of ["page types"](/topics/page-types).
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.
To get a feel for what those templates look like, let's have a look at an abbreviated example. In your webroot, these templates are usually located in `themes/<your-theme>/templates`.
Replace the `<your-theme>` placeholder accordingly, most likely you're using a theme called "simple")
Most of the magic happens in `Page.ss` and `Layout/Page.ss`.
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`
`themes/<your-theme>/templates/Page.ss`
:::ss
<html>
<%-- This is my first template --%>
<head>
<% base_tag %>
<title>$Title</title>
$MetaTags
<title>$SiteConfig.Title | $Title</title>
$MetaTags(false)
</head>
<body>
<div id="Container">
<div id="Header">
<header>
<h1>Bob's Chicken Shack</h1>
<% with $CurrentMember %>
<p>You are logged in as $FirstName $Surname.</p>
<% end_with %>
</div>
<div id="Navigation">
</header>
<navigation>
<% if $Menu(1) %>
<ul>
<% loop $Menu(1) %>
<li><a href="$Link" title="Go to the $Title page" class="$LinkingMode">$MenuTitle</a></li>
<li><a href="$Link" class="$LinkingMode">$MenuTitle</a></li>
<% end_loop %>
</ul>
<% end_if %>
</div>
</navigation>
<div class="typography">
$Layout
</div>
<div id="Footer">
<p>Copyright $Now.Year</p>
</div>
</div>
</body>
</html>
`templates/Layout/Page.ss`
`themes/<your-theme>/templates/Layout/Page.ss`
<h1>$Title</h1>
<h2>$Title</h2>
$Content
$Form
## <%-- This is my first template --%>
### Template inheritance through $Layout
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.
Our example shows two templates, both called `Page.ss`.
One is located in the `templates/` "root" folder, the other one in a `templates/Layout/` subfolder.
This "inner template" is used by the `$Layout` placeholder in the "root template",
and is inherited based on the underlying PHP classes (read more about template inheritance
on the ["page types" topic](/topics/page-types)).
## <% base_tag %>
"Layout" is a fixed naming convention,
you can't use the same pattern for other folder names.
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)
### Page Content
### $MetaTags
:::ss
$Content
This variable in the `Layout` template contains the main content of the current page,
edited through the WYSIWIG editor in the CMS.
It returns the database content of the `SiteTree.Content` property.
Please note that this database content can be "staged",
meaning that draft content edited in the CMS can be different from published content
shown to your website visitors. In templates, you don't need to worry about this distinction.
The `$Content` variable contain the published content by default,
and only preview draft content if explicitly requested (e.g. by the "preview" feature in the CMS)
(see the ["versioning" topic](topics/versioning) for more details).
### Menu Loops
:::ss
<% loop $Menu(1) %>...<% end_loop %>
`$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 so forth.
The `<% loop $Menu(1) %>...<% end_loop %>` block defines a repeating element.
It 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. In this case, a menu item refers to an instance
of the `Page` class, so you can access all properties defined on there, for example `$Title`.
Note that pages with the `ShowInMenus` property set to FALSE will be filtered out
(its a checkbox in the "Settings" panel of the CMS).
### Children Loops
:::ss
<% loop Children %>...<% end_loop %>
Will loop over all children of the current page context.
Helpful to create page-specific subnavigations.
Most likely, you'll want to use `<% loop Menu %>` for your main menus,
since its independent of the page context.
:::ss
<% loop ChildrenOf(<my-page-url>) %>...<% end_loop %>
Will create a list of the children of the given page,
as identified by its `URLSegment` value. This can come in handy because its not dependent
on the context of the current page. For example, it would allow you to list all staff member pages
underneath a "staff" holder on any page, regardless if its on the top level or elsewhere.
:::ss
<% loop allChildren %>...<% end_loop %>
This will show all children of a page even if the `ShowInMenus` property is set to FALSE.
### Access to Parent and Level Pages
:::ss
<% with $Level(1) %>
$Title
<% end_with %>
Will return a page in the current path, at the level specified by the numbers.
It is based on the current page context, looking back through its parent pages.
For example, imagine you're on the "bob marley" page,
which is three levels in: "about us > staff > bob marley".
* `$Level(1).Title` would return "about us"
* `$Level(2).Title` would return "staff"
* `$Level(3).Title` would return "bob marley"
To simply retrieve the parent page of the current context (if existing), use the `$Parent` variable.
### Access to a specific Page
:::ss
<% loop Page(my-page) %>...<% end_loop %>`
"Page" will return a single page from the site tree, looking it up by URL. You can use it in the `<% loop %>` format.
Can't be called using `$Page(my-page).Title`.
### Title and Menu Title
The CMS provides two fields to label a page: "Title" and "Menu Title".
"Title" is the title in its full length, while "Menu Title" can be
a shorter version suitable for size-constrained menus.
If "Menu Title" is left blank by the CMS author, it'll just default to "Title".
### Links and Linking Modes
:::ss
$LinkingMode
Each menu item we loop over knows its location on the website, so can generate a link to it.
This happens through the `[api:SiteTree->Link()]` method behind the scenes.
We're not using the direct database property `SiteTree.URLSegment` here
because pages can be nested, so the link needs to be generated on the fly.
In the template syntax, there's no distinction between a method and a property though.
The link is relative by default (see `<% base_tag %>`),
you can get an absolute one including the domain through [$AbsoluteLink](api:SiteTree->AbsoluteLink())`.
In addition, each menu item gets some context information relative
to the page you're currently viewing, contained in the `$LinkingMode` placeholder.
By setting a HTML class to this value, you can distinguish the styling of
the currently selected menu item. It can have the following 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 the current "section"
More common uses:
* `$LinkOrCurrent`: Determines if the item is the current page. Returns "link" or "current" strings.
* `$LinkOrSection`: Determines if the item is in the current section, so in the path towards the current page. Useful for menus which you only want to show a second level menu when you are on that page or a child of it. Returns "link" or "section" strings.
* `InSection(page-url)`: This if block will pass if we're currently on the page-url page or one of its children.
Example: Only show the menu item linked if its the current one:
:::ss
<% if LinkOrCurrent = current %>
$Title
<% else %>
<a href="$Link">$Title</a>
<% end_if %>
### Breadcrumbs
Breadcrumbs are the path of parent pages which needs to be taken
to reach the current page, and can be a great navigation aid for website users.
While you can achieve breadcrumbs through the `<% Level(<level>) %>` control already,
there's a nicer shortcut: The `$Breadcrumbs` control.
It uses its own template defined in `BreadcrumbsTemplate.ss`.
Simply place a file with the same name in your `themes/<your-theme>/templates`
folder to customize its output. Here's the default template:
:::ss
<% if Pages %>
<% loop Pages %>
<% if Last %>$Title.XML<% else %><a href="$Link">$MenuTitle.XML</a> &raquo;<% end_if %>
<% end_loop %>
<% end_if %>
For more customization options like limiting the amount of breadcrumbs,
take a look at `[api:SiteTree->Breadcrumbs()]`.
### SiteConfig: Global settings
:::ss
$SiteConfig.Title
The ["SiteConfig"](/reference/siteconfig) object allows content authors
to modify global data in the CMS, rather than PHP code.
By default, this includes a website title and tagline
(as opposed to the title of a specific page).
It can be extended to hold other data, for example a logo image
which can be uploaded through the CMS.
The object is available to all page templates through the `$SiteConfig` placeholder.
### 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
@ -73,63 +232,54 @@ 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" >
<meta name="generator" http-equiv="generator" content="SilverStripe 3.0" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
### <% with $CurrentMember %>...<% end_with %>
#### URLSegment
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.
This returns the part of the URL of the page you're currently on.
Shouldn't be used for linking to a page, since the link
is a composite value based on all parent pages as well (through the `$Link` variable).
`$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.
#### ClassName
### $FirstName, $Surname
Returns the class of the underlying `Page` record.
This can be handy to add to your `<body>` tag to influence
CSS styles and JavaScript behaviour based on the page type used:
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.
:::ss
<body class="$ClassName">
### <% if $Menu(1) %>...<% end_if %>
In case you want to include parent PHP classes in this list as well,
use the `$CSSClasses` placeholder instead.
This template code defines a conditional block. Its contents will only be shown if `$Menu(1)` contains anything.
#### BaseHref
`$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.
Returns the base URL for the current site.
This is used to populate the `<base>` tag by default.
Can be handy to prefix custom links (not generated through `SiteTree->Link()`),
to ensure they work correctly when the webroot is hosted in a subfolder
rather than its own domain (a common development setup).
### <% loop $Menu(1) %>...<% end_loop %> %>
### Forms
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.
:::ss
$Form
### $Link, $Title, $MenuTitle
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, the `$Form` variable will contain the HTML content of the form. Placing it just below `$Content` is a good default. Behind the scenes,
it maps to the `Page_Controller->Form()` method. You can add more forms by implementing
new methods there (see ["forms" topic](/topics/forms) for details).
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.
### More Advanced Controls
* `$Link`: A link to that menu item.
* `$Title`: The page title of that menu item.
* `$MenuTitle`: The navigation label of that menu item.
Template variables and controls are just PHP properties and methods
on the underlying controllers and model classes.
We've just shown you the most common once, in practice
you can use any public API on those classes, and [extend](/reference/dataextension) them
with your own. To get an overview on what's available to you,
we recommend that you dive into the API docs for the following classes:
### $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.
* `[api:ContentController]`: The main controller responsible for handling pages
* `[api:Controller]`: Generic controller (not specific to pages)
* `[api:DataObject]`: Underlying model class for page objects
* `[api:ViewableData]`: Underlying object class for pretty much anything displayable

View File

@ -4,23 +4,18 @@
Page Types are the basic building blocks of any SilverStripe website. A page type can define:
* The template or templates that are used to display content
* What fields are available to edit in the CMS
* Behaviour specific to a page type for example a contact form on the Contact Us page that sends an email
when the form is submitted
* Templates being used to display content
* Form fields available to edit content in the CMS
* Behaviour specific to a page type. For example a contact form on a Contact Us page type, sending an email when the form is submitted
All the pages on the base installation are of the page type "Page". See
All the pages on the base installation are of the page type called "Page". See
[tutorial:2-extending-a-basic-site](/tutorials/2-extending-a-basic-site) for a good introduction to page-types.
## Page type templates
## Class and Template Inheritance
Each page type on your website is a sub-class of the SiteTree class. Usually, youll define a class called Page
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.
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
re-defining commonly used forms or controls in every class we use.
![](_images/pagetype-inheritance.png)
Each page type is represented by two classes: a data object and a controller. In the diagrams above and below, the data
@ -31,34 +26,28 @@ we want to do to the CMS for this page type in here.
![](_images/controllers-and-dataobjects.png)
Page types are created using PHP classes. If youre not sure about how these work, [click here for a gentler
introduction to PHP classes](http://www-128.ibm.com/developerworks/opensource/library/os-phpobj/).
We put the `Page` class into a file called `Page.php` inside `mysite/code`.
As a convention, we also put the `Page_Controller` class in the same file.
We put the Page class into a file called Page.php inside `mysite/code`. We also put Page_Controller in here. Any other
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.
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
re-defining commonly used forms or controls in every class we use.
## 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.
Page type templates work much the same as other [templates](/reference/templates) in SilverStripe
(see ). There's some specialized controls and placeholders, as well as built-in inheritance.
This is explained in a more in-depth topic at [Page Type Templates](/topics/page-type-templates).
See the [Page Type Templates](/topics/page-type-templates) page for more information.
## Adding database-fields
## Adding Database Fields
Adding database fields is a simple process. You define them in an array of the static variable `$db`, this array is
added on the object class. For example, Page or StaffPage. Every time you run db/build to recompile the manifest, it
checks if any new entries are added to the `$db` array and adds any fields to the database that are missing.
For example, you may want an additional field on a StaffPage class which extends Page, called Author. Author is a
standard text field, and can be [casted](/topics/datamodel) as a variable character object in php (VARCHAR in SQL). In the
following example, our Author field is casted as a variable character object with maximum characters of 50. This is
For example, you may want an additional field on a `StaffPage` class which extends `Page`, called `Author`. `Author` is a
standard text field, and can be [casted](/topics/datamodel) as a variable character object in php (`VARCHAR` in SQL). In the
following example, our `Author` field is casted as a variable character object with maximum characters of 50. This is
especially useful if you know how long your source data needs to be.
:::php
@ -77,7 +66,7 @@ especially useful if you know how long your source data needs to be.
See [datamodel](/topics/datamodel) for a more detailed explanation on adding database fields, and how the SilverStripe data
model works.
## Adding formfields and tabs
## Adding Form Fields and Tabs
See [form](/topics/forms) and [tutorial:2-extending-a-basic-site](/tutorials/2-extending-a-basic-site)
@ -133,36 +122,4 @@ This will also work if you want to remove a whole tab e.g. $fields->removeByName
Metadata tab.
For more information on forms, see [form](/topics/forms), [tutorial:2-extending-a-basic-site](/tutorials/2-extending-a-basic-site)
and [tutorial:3-forms](/tutorials/3-forms).
## Creating a new page:
:::php
$page = new Page();
$page->ParentID = 18; //if you want it to be a child of a certain other page...
$page->Title = "Crazy page";
$page->MetaTitle = "madness";
$page->PageTitle = "Funny";
$page->writeToStage('Stage');
$page->publish('Stage', 'Live');
## Updating a page:
:::php
$page = Page::get()->filter("ParentID", 18)->First();
$page->Title = "More Serious";
$page->writeToStage('Stage');
$page->Publish('Stage', 'Live');
$page->Status = "Published";
## Deleting pages:
:::php
$pageID = $page->ID;
$stageRecord = Versioned::get_one_by_stage('SiteTree', 'Stage', "SiteTree.ID = $pageID");
if ($stageRecord) $stageRecord->delete();
$liveRecord = Versioned::get_one_by_stage('SiteTree', 'Live', "SiteTree_Live.ID = $pageID");
if ($liveRecord) $liveRecord->delete();
and [tutorial:3-forms](/tutorials/3-forms).