Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

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
  • Loading branch information...
commit 0b31234810221c5f0dc2db942aa31a75b275a196 1 parent 34a2ce2
@chillu chillu authored
View
4 docs/en/howto/pagination.md
@@ -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
347 docs/en/reference/built-in-page-controls.md
@@ -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
2  docs/en/reference/restfulservice.md
@@ -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
2  docs/en/reference/siteconfig.md
@@ -1,4 +1,4 @@
-# SiteConfig
+# SiteConfig: Global database content
## Introduction
View
286 docs/en/reference/sitetree.md
@@ -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:
+
+ * `$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.
+
+
+Hierarchy operations (defined on `[api:Hierarchy]`:
-## Limiting Children/Parent
+ * `$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.
-By default, any page type can be the child of any other page type. However, there are 4 static properties that can be
+## 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.
-
-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.
-
- :::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.
+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.
-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:
+## Permission Control
-* **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.
+## Tree Display (Description, Icons and Badges)
-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.
+The page tree in the CMS is a central element to manage page hierarchies,
+hence its display of pages can be customized as well.
-**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();
- }
- } }
- }
-
-
-
-### 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!
+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
- public function getParent() {
- return $this->Parent();
- }
- public function Parent() {
- $parents = $this->Parents();
- if($parents) return $parents->First();
+ 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';
+ // ...
}
-
-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
2  docs/en/reference/templates-upgrading-guide.md
@@ -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
291 docs/en/reference/templates.md
@@ -12,54 +12,37 @@ 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>
-# 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 site’s images and css files. So it is a must for templates!
+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.
-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.
+# Template elements
## Variables
@@ -67,7 +50,6 @@ Variables are things you can use in a template that grab data from the page and
:::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:
+## Looping Over Lists
- <% if $MyDinner=="chicken" %>
- <% if $Wine=="red" %>
- You're doing it wrong
- <% else %>
- Perfect
- <% end_if %>
- <% end_if %>
+The `<% loop %>...<% end_loop %>` tag is used to **iterate** or loop over a collection of items. For example:
-## Looping Over Datasets
+ :::ss
+ <ul>
+ <% loop $Children %>
+ <li>$Title</li>
+ <% end_loop %>
+ </ul>
-The `<% loop %>...<% end_loop %>` tag is used to **iterate** or loop over a collection of items. For example:
+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.
- <ul>
- <% loop $Children %>
- <li>$Title</li>
- <% end_loop %>
- </ul>
+### Position Indicators
-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.
+Inside the loop scope, there are many variables at your disposal to determine the current position
+in the list and iteration:
-The value that given in the `<% loop %>` tags should be a collection variable.
+ * `$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 site’s 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
284 docs/en/topics/page-type-templates.md
@@ -1,135 +1,285 @@
# 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`? That’s 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 – it’s 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 you’re 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
+
+ :::ss
+ $Content
-### $MetaTags
+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.
-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 don’t want to include the title-tag `<title>` (for custom templating), use
-`$MetaTags(false)`.
+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).
-By default `$MetaTags` renders:
+### Menu Loops
:::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" >
+ <% loop $Menu(1) %>...<% end_loop %>
-### <% with $CurrentMember %>...<% end_with %>
+`$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.
-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.
+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`.
-`$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.
+Note that pages with the `ShowInMenus` property set to FALSE will be filtered out
+(its a checkbox in the "Settings" panel of the CMS).
-### $FirstName, $Surname
+### Children Loops
-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
+ <% loop Children %>...<% end_loop %>
-### <% if $Menu(1) %>...<% end_if %>
+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.
-This template code defines a conditional block. Its contents will only be shown if `$Menu(1)` contains anything.
+ :::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 %>
-`$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.
+This will show all children of a page even if the `ShowInMenus` property is set to FALSE.
-### <% loop $Menu(1) %>...<% end_loop %> %>
+### 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.
-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.
+For example, imagine you're on the "bob marley" page,
+which is three levels in: "about us > staff > bob marley".
-### $Link, $Title, $MenuTitle
+* `$Level(1).Title` would return "about us"
+* `$Level(2).Title` would return "staff"
+* `$Level(3).Title` would return "bob marley"
-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.
+To simply retrieve the parent page of the current context (if existing), use the `$Parent` variable.
- * `$Link`: A link to that menu item.
- * `$Title`: The page title of that menu item.
- * `$MenuTitle`: The navigation label of that menu item.
+### Access to a specific Page
+
+ :::ss
+ <% loop Page(my-page) %>...<% end_loop %>`
-### $LinkingMode
+"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`.
-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:
+### 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 this is menu item identifies the section that you're currently in.
+ * `section`: The current page is a child of this menu item, so the current "section"
+
+More common uses:
-By setting the HTML class to this value, you can distinguish the styling of the currently selected menu item.
+ * `$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:
-### $Layout
+ :::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()]`.
-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.
+### SiteConfig: Global settings
-### $Now.Year
+ :::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.
-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.
+### 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 don’t want to include the title-tag `<title>` (for custom templating), use
+`$MetaTags(false)`.
-### $Title
+By default `$MetaTags` renders:
-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.
+ :::ss
+ <title>Title of the Page</title>
+ <meta name="generator" http-equiv="generator" content="SilverStripe 3.0" />
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+
+#### URLSegment
+
+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).
+
+#### ClassName
+
+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:
+
+ :::ss
+ <body class="$ClassName">
+
+In case you want to include parent PHP classes in this list as well,
+use the `$CSSClasses` placeholder instead.
+
+#### BaseHref
+
+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).
+
+### Forms
+
+ :::ss
+ $Form
-### $Content
+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).
-This variable contains the content of the current page.
+### More Advanced Controls
-### $Form
+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:
-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
83 docs/en/topics/page-types.md
@@ -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, you’ll define a class called Page
+Each page type on your website is a sub-class of the `SiteTree` class. Usually, you’ll define a class called `Page`
and use this template to lay out the basic design elements that don’t 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 you’re 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. We’ll 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? That’s 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 – it’s 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 you’re viewing.
-
-See the [Page Type Templates](/topics/page-type-templates) page for more information.
+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).
-## 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).
Please sign in to comment.
Something went wrong with that request. Please try again.