Skip to content
This repository
Browse code

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
Ingo Schommer authored June 27, 2012
4  docs/en/howto/pagination.md
Source Rendered
@@ -21,6 +21,10 @@ information.
21 21
 		return new PaginatedList(Page::get(), $this->request);
22 22
 	}
23 23
 
  24
+Note that the concept of "pages" used in pagination does not necessarily
  25
+mean that we're dealing with `Page` classes, its just a term to describe
  26
+a sub-collection of the list.
  27
+
24 28
 ## Setting Up The Template
25 29
 
26 30
 Now all that remains is to render this list into a template, along with pagination
347  docs/en/reference/built-in-page-controls.md
Source Rendered
... ...
@@ -1,347 +0,0 @@
1  
-# Built-in Page Controls
2  
-
3  
-
4  
-Ever wonder when you use `$Title` and `<% Control Children %>` what else you can call in the templates?. This page is
5  
-here to help with a guide on what template controls you can call.
6  
-
7  
-**Note for advanced users:** These built-in page controls are defined in the [api:SiteTree] classes, which are the
8  
-'root' data-object and controller classes for all the sites.  So if you're dealing with something that isn't a sub-class
9  
-of one of these, our handy reference to 'built-in page controls' won't be so relevant.
10  
-
11  
-
12  
-## Page controls that can't be nested
13  
-
14  
-These page controls are defined on the **controller** which means they can only be used at a top level, not nested
15  
-within another page control.
16  
-
17  
-### Controlling Menus Datafeeds
18  
-
19  
-#### <% control Menu(1) %>, <% control Menu(2) %>, ...
20  
-
21  
-Returns a fixed level menu.  Because this only works in the top level, you can't use it for nested menus.  Use 
22  
-`<% control Children %>` instead. You can nest `<% control Children %>`.
23  
-
24  
-#### <% control ChildrenOf(page-url) %>
25  
-
26  
-This will create a datafeed of the children of the given page. Handy if you want a list of the subpages under staff (eg
27  
-the staff) on the homepage etc
28  
-
29  
-### Controlling Certain Pages
30  
-
31  
-#### <% control Level(1) %>, <% control Level(2) %>, $Level(1).Title, $Level(2).Content, etc
32  
-Returns the current section of the site that we're in, at the level specified by the numbers.  For example, imagine
33  
-you're on the page __about us > staff > bob marley__:
34  
-
35  
-*  `<% control Level(1) %>` would return the about us page
36  
-*  `<% control Level(2) %>` would return the staff page
37  
-*  `<% control Level(3) %>` would return the bob marley page
38  
-
39  
-#### <% control Page(my-page) %>$Title<% end_control %>
40  
-
41  
-"Page" will return a single page from the site tree, looking it up by URL.  You can use it in the `<% control %>` format.
42  
-Can't be called using `$Page(my-page).Title`.
43  
-
44  
-## Page controls that can be used anywhere
45  
-
46  
-These are defined in the data-object and so can be used as nested page controls.  Lucky us! we can control Children of
47  
-Children of Children for example.
48  
-
49  
-### Conditional Logic
50  
-
51  
-SilverStripe supports a simple set of conditional logic
52  
-
53  
-	:::ss
54  
-	<% if Foo %>
55  
-	// if Foo is true or an object do this
56  
-	<% else_if Bar %>
57  
-	// if Bar is true or an object do this
58  
-	<% else %>
59  
-	// then do this by default
60  
-	<% end_if %>
61  
-
62  
-
63  
-See more information on conditional logic on [templates](/topics/templates).
64  
-
65  
-### Site wide settings
66  
-
67  
-Since 2.4.0, SilverStripe provides a generic interface for accessing global properties such as *Site name* or *Site tag
68  
-line*. This interface is implemented by the [api:SiteConfig] class.
69  
-
70  
-### Controlling Parents and Children
71  
-
72  
-#### <% control Children %>
73  
-
74  
-This will return the children of the current page as a nested datafeed.  Useful for nested navigations such as pop-out
75  
-menus.
76  
-
77  
-#### <% control AllChildren %>
78  
-
79  
-This will show all children of a page even if the option 'show in menus?' is unchecked in the tab panel behaviour.
80  
-
81  
-#### <% control Parent %> or $Parent.Title, $Parent.Content, etc
82  
-
83  
-This will return the parent page.  The $ variable format lets us reference an attribute of the parent page directly.
84  
-
85  
-### Site Navigation - Breadcrumbs
86  
-
87  
-#### $Breadcrumbs
88  
-
89  
-This will return a breadcrumbs widget for the current page.  You can call this on any SiteTree descendant, so, for
90  
-example, you could display the breadcrumbs of every search result if you wanted. The Breadcrumbs method returns a string
91  
-of text, so this can't be used as a control block (that is, you can't usefully say "<% control Breadcrumbs %>"). You can
92  
-limit the number of items in the breadcrumbs, as well as whether the breadcrumb items are links.
93  
-
94  
-####  $Breadcrumbs(3)
95  
-
96  
-This returns a maximum of 3 pages in the breadcrumb list, which can be handy if you want to limit the size of your
97  
-breadcrumbs to conform to your page design.
98  
-
99  
-####  <% control Breadcrumbs(3, true) %>
100  
-
101  
-This returns the same, but without any links. This is handy if you want to put the breadcrumb list into another link
102  
-tag.
103  
-
104  
-
105  
-### Links and Classes
106  
-
107  
-#### $LinkingMode, $LinkOrCurrent and $LinkOrSection
108  
-
109  
-These return different linking modes.  $LinkingMode provides the greatest control, outputting 3 different strings:
110  
-
111  
-*  link: Neither this page nor any of its children are current open.
112  
-*  section: A child of this page is currently open, which means that we're currently in this section of the site.
113  
-*  current: This page is currently open.
114  
-
115  
-A useful way of using this is in your menus. You can use the following code below to generate class="current" or
116  
-class="section" on your links. Take the following code
117  
-
118  
-	:::ss
119  
-	<li><a href="$Link" class="$LinkingMode">$Title</a></li>
120  
-
121  
-
122  
-When viewed on the Home page it will render like this
123  
-
124  
-	:::ss
125  
-	<li><a href="home/" class="current">Home</a></li>
126  
-
127  
-
128  
-`$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.
129  
-
130  
-#### <% if LinkOrCurrent = current %>
131  
-
132  
-This is an alternative way to set up your menus - if you want different HTML for the current menu item, you can do
133  
-something like this:
134  
-
135  
-	:::ss
136  
-	<% if LinkOrCurrent = current %>
137  
-	<strong>$Title</strong>
138  
-	<% else %>
139  
-	<a href="$Link">$Title</a>
140  
-	<% end_if %>
141  
-
142  
-
143  
-#### <% if LinkOrSection = section %>
144  
-
145  
-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
146  
-show a second level menu when you are on that page or a child of it
147  
-
148  
-#### <% if InSection(page-url) %>
149  
-
150  
-This if block will pass if we're currently on the page-url page or one of its children.
151  
-
152  
-### Titles and CMS Defined Options
153  
-
154  
-#### $MetaTags
155  
-
156  
-This returns a segment of HTML appropriate for putting into the `<head>` tag.  It will set up title, keywords and
157  
-description meta-tags, based on the CMS content. If you don't want to include the title-tag (for custom templating), use
158  
-**$MetaTags(false)**.
159  
-
160  
-#### $MenuTitle
161  
-
162  
-This is the title that you should put into navigation menus.  CMS authors can choose to put a different menu title from
163  
-the main page title.
164  
-
165  
-#### $Title
166  
-
167  
-This is the title of the page which displays in the browser window and usually is the title of the page.
168  
-
169  
-	:::ss
170  
-	<h1>$Title</h1>
171  
-
172  
-#### $URLSegment
173  
-
174  
-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. (
175  
-when doing this, watch out that it doesn't create invalid id-attributes though.). This is useful for adding a class to
176  
-the body so you can target certain pages. Watch out for pages named clear or anything you might have used in your CSS
177  
-file
178  
-
179  
-	:::ss
180  
-	<body class="$URLSegment">
181  
-
182  
-
183  
-####  $ClassName
184  
-
185  
-Returns the ClassName of the PHP object. Eg if you have a custom HomePage page type with `$ClassName` in the template, it
186  
-will return "HomePage"
187  
-
188  
-#### $BaseHref
189  
-
190  
-Returns the base URL for the current site. This is used to populate the `<base>` tag by default, so if you want to
191  
-override `<% base_tag %>` with a specific piece of HTML, you can do something like `<base href="$BaseHref"></base>`
192  
-
193  
-### Controlling Members and Visitors Data
194  
-
195  
-#### <% control CurrentMember %>, <% if CurrentMember %> or $CurrentMember.FirstName
196  
-
197  
-CurrentMember returns the currently logged in member, if there is one.  All of their details or any special Member page
198  
-controls can be called on this.  Alternately, you can use `<% if CurrentMember %>` to detect whether someone has logged
199  
-in. To Display a welcome message you can do
200  
-
201  
-	:::ss
202  
-	<% if CurrentMember %>
203  
-	  Welcome Back, $CurrentMember.FirstName
204  
-	<% end_if %>
205  
-
206  
-
207  
-If the user is logged in this will print out
208  
-
209  
-	:::ss
210  
-	Welcome Back, Admin
211  
-
212  
-
213  
-#### <% if IsRepeatMember %>
214  
-
215  
-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.
216  
-
217  
-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()`.
218  
-
219  
-Note that in 2.4 this variable was called `$PastMember`.  This still works in 3.0 but is deprecated.
220  
-
221  
-### Date and Time
222  
-
223  
-#### $Now.Nice, $Now.Year
224  
-
225  
-`$Now` returns the current date.  You can call any of the methods from the [api:Date] class on
226  
-it.
227  
-
228  
-#### $Created.Nice, $Created.Ago
229  
-
230  
-`$Created` returns the time the page was created, `$Created.Ago` returns how long ago the page was created. You can also
231  
-call any of methods of the [api:Date] class on it.
232  
-
233  
-#### $LastEdited.Nice, $LastEdited.Ago
234  
-
235  
-`$LastEdited `returns the time the page was modified, `$LastEdited.Ago` returns how long ago the page was modified. You
236  
-can also call any of methods of the [api:Date] class on it.
237  
-
238  
-### DataObjectSet Options
239  
-
240  
-If you are using a DataObjectSet you have a wide range of methods you can call on it from the templates
241  
-
242  
-#### <% if Even %>, <% if Odd %>, $EvenOdd
243  
-
244  
-These controls can be used to do zebra-striping.  `$EvenOdd` will return 'even' or 'odd' as appropriate.
245  
-
246  
-#### <% if First %>, <% if Last %>, <% if Middle %>, $FirstLast
247  
-
248  
-These controls can be used to set up special behaviour for the first and last records of a datafeed.  `<% if Middle %>` is
249  
-set when neither first not last are set.  `$FirstLast` will be 'first', 'last', or ''.
250  
-
251  
-#### $Pos, $TotalItems
252  
-
253  
-`$TotalItems` will return the number of items on this page of the datafeed, and `$Pos` will return a counter starting at 1.
254  
-
255  
-#### $Top
256  
-
257  
-When you're inside a control loop in your template, and want to reference methods on the current controller you're on,
258  
-breaking out of the loop to get it, you can use `$Top` to do so. For example:
259  
-
260  
-	:::ss
261  
-	$URLSegment
262  
-	<% control News %>
263  
-	   $URLSegment <!-- may not return anything, as you're requesting URLSegment on the News objects -->
264  
-	   $Top.URLSegment <!-- returns the same as $URLSegment above -->
265  
-	<% end_control %>
266  
-
267  
-
268  
-##  Properties of a datafeed itself, rather than one of its items
269  
-
270  
-If we have a control such as `<% control SearchResults %>`, there are some properties, such as `$SearchResults.NextLink`,
271  
-that aren't accessible within `<% control SearchResults %>`.  These can be used on any datafeed.
272  
-
273  
-### Search Results
274  
-
275  
-#### <% if SearchResults.MoreThanOnePage %>
276  
-
277  
-Returns true when we have a multi-page datafeed, restricted with a limit.
278  
-
279  
-#### $SearchResults.NextLink, $SearchResults.PrevLink
280  
-
281  
-This returns links to the next and previous page in a multi-page datafeed.  They will return blank if there's no
282  
-appropriate page to go to, so `$PrevLink` will return blank when you're on the first page.  You can therefore use 
283  
-`<% if PrevLink %>` to keep your template tidy.
284  
-
285  
-#### $SearchResults.CurrentPage, $SearchResults.TotalPages
286  
-
287  
-CurrentPage returns the number of the page you're currently on, and TotalPages returns the total number of pages.
288  
-
289  
-#### $SearchResults.TotalItems
290  
-
291  
-This returns the total number of items across all pages.
292  
-
293  
-#### <% control SearchResults.First %>, <% control SearchResults.Last %>
294  
-
295  
-These controls return the first and last item on the current page of the datafeed.
296  
-
297  
-#### <% control SearchResults.Pages %>
298  
-
299  
-This will return another datafeed, listing all of the pages in this datafeed.  It will have the following data
300  
-available:
301  
-
302  
-*  **$PageNum:** page number, starting at 1
303  
-*  **$Link:** a link straight to that page
304  
-*  `<% if CurrentBool %>`:** returns true if you're currently on that page
305  
-
306  
-`<% control SearchResults.Pages(30) %>` will show a maximum of 30 pages, useful in situations where you could get 100s of
307  
-pages returned.
308  
-
309  
-#### $SearchResults.UL
310  
-
311  
-This is a quick way of generating a `<ul>` containing an `<li>` and `<a>` for each item in the datafeed.  Usually too
312  
-restricted to use in a final application, but handy for debugging stuff.
313  
-
314  
-
315  
-## Quick Reference
316  
-
317  
-Below is a list of fields and methods that are typically available for templates (grouped by their source) - use this as
318  
-a quick reference (not all of them are described above):
319  
-### All methods available in Page_Controller
320  
-
321  
-$NexPageLink, $Link, $RelativeLink, $ChildrenOf, $Page, $Level, $Menu, $Section2, $LoginForm, $SilverStripeNavigator,
322  
-$PageComments, $Now, $LinkTo, $AbsoluteLink, $CurrentMember, $PastVisitor, $PastMember, $XML_val, $RAW_val, $SQL_val,
323  
-$JS_val, $ATT_val, $First, $Last, $FirstLast, $MiddleString, $Middle, $Even, $Odd, $EvenOdd, $Pos, $TotalItems,
324  
-$BaseHref, $Debug, $Top
325  
-
326  
-### All fields available in Page_Controller
327  
-
328  
-$ID, $ClassName, $Created, $LastEdited, $URLSegment, $Title, $MenuTitle, $Content, $MetaTitle, $MetaDescription,
329  
-$MetaKeywords, $ShowInMenus, $ShowInSearch, $HomepageForDomain, $ProvideComments, $Sort, $LegacyURL, $HasBrokenFile,
330  
-$HasBrokenLink, $Status, $ReportClass, $ParentID, $Version, $EmailTo, $EmailOnSubmit, $SubmitButtonText,
331  
-$OnCompleteMessage, $Subscribe, $AllNewsletters, $Subject, $ErrorCode, $LinkedPageID, $RedirectionType, $ExternalURL,
332  
-$LinkToID, $VersionID, $CopyContentFromID, $RecordClassName
333  
-
334  
-### All methods available in Page
335  
-
336  
-$Link, $LinkOrCurrent, $LinkOrSection, $LinkingMode, $ElementName, $InSection, $Comments, $Breadcrumbs, $NestedTitle,
337  
-$MetaTags, $ContentSource, $MultipleParents, $TreeTitle, $CMSTreeClasses, $Now, $LinkTo, $AbsoluteLink, $CurrentMember,
338  
-$PastVisitor, $PastMember, $XML_val, $RAW_val, $SQL_val, $JS_val, $ATT_val, $First, $Last, $FirstLast, $MiddleString,
339  
-$Middle, $Even, $Odd, $EvenOdd, $Pos, $TotalItems, $BaseHref, $Top
340  
-
341  
-###  All fields available in Page
342  
-
343  
-$ID, $ClassName, $Created, $LastEdited, $URLSegment, $Title, $MenuTitle, $Content, $MetaTitle, $MetaDescription,
344  
-$MetaKeywords, $ShowInMenus, $ShowInSearch, $HomepageForDomain, $ProvideComments, $Sort, $LegacyURL, $HasBrokenFile,
345  
-$HasBrokenLink, $Status, $ReportClass, $ParentID, $Version, $EmailTo, $EmailOnSubmit, $SubmitButtonText,
346  
-$OnCompleteMessage, $Subscribe, $AllNewsletters, $Subject, $ErrorCode, $LinkedPageID, $RedirectionType, $ExternalURL,
347  
-$LinkToID, $VersionID, $CopyContentFromID, $RecordClassName
2  docs/en/reference/restfulservice.md
Source Rendered
@@ -167,7 +167,7 @@ Put something like this code in mysite/code/Page.php inside class Page_Controlle
167 167
 		}
168 168
 
169 169
 
170  
-Put something like this code in mysite/templates/Layout/HomePage.ss:
  170
+Put something like this code in `themes/<your-theme>/templates/Layout/HomePage.ss`:
171 171
 
172 172
 	:::ss
173 173
 	<h3>My Latest Del.icio.us Links</h3>
2  docs/en/reference/siteconfig.md
Source Rendered
... ...
@@ -1,4 +1,4 @@
1  
-# SiteConfig
  1
+# SiteConfig: Global database content
2 2
 
3 3
 ## Introduction
4 4
 
286  docs/en/reference/sitetree.md
Source Rendered
... ...
@@ -1,11 +1,13 @@
1  
-
2 1
 # Sitetree
3 2
 
4 3
 ## Introduction
5 4
 
6  
-Basic data-object representing all pages within the site tree. The omnipresent *Page* class (located in
7  
-*mysite/code/Page.php*) is based on this class.
  5
+Basic data-object representing all pages within the site tree. 
  6
+The omnipresent *Page* class (located in `mysite/code/Page.php`) is based on this class.
  7
+
  8
+## Creating, Modifying and Finding Pages
8 9
 
  10
+See the ["datamodel" topic](/topics/datamodel).
9 11
 
10 12
 ## Linking
11 13
 
@@ -15,6 +17,11 @@ Basic data-object representing all pages within the site tree. The omnipresent *
15 17
 	// right
16 18
 	$mylink = $mypage->Link(); // alternatively: AbsoluteLink(), RelativeLink()
17 19
 
  20
+In a nutshell, the nested URLs feature means that your site URLs now reflect the actual parent/child page structure of
  21
+your site. The URLs map directly to the chain of parent and child pages. The
  22
+below table shows a quick summary of what these changes mean for your site:
  23
+
  24
+![url table](http://silverstripe.org/assets/screenshots/Nested-URLs-Table.png)
18 25
 
19 26
 ## Querying
20 27
 
@@ -27,46 +34,72 @@ might consist of more than one *URLSegment*).
27 34
 	// right
28 35
 	$mypage = SiteTree::get_by_link('<mylink>');
29 36
 
  37
+### Versioning
  38
+	
  39
+The `SiteTree` class automatically has an extension applied to it: `[Versioned](api:Versioned)`.
  40
+This provides the basis for the CMS to operate on different stages,
  41
+and allow authors to save their changes without publishing them to
  42
+website visitors straight away.
  43
+`Versioned` is a generic extension which can be applied to any `DataObject`,
  44
+so most of its functionality is explained in the `["versioning" topic](/topics/versioning)`.
30 45
 
  46
+Since `SiteTree` makes heavy use of the extension, it adds some additional
  47
+functionality and helpers on top of it.
31 48
 
32  
-## Nested/Hierarchical URLs
  49
+Permission control:
33 50
 
34  
-In a nutshell, the nested URLs feature means that your site URLs now reflect the actual parent/child page structure of
35  
-your site. The URLs map directly to the chain of parent and child pages. The
36  
-below table shows a quick summary of what these changes mean for your site:
  51
+	:::php
  52
+	class MyPage extends Page {
  53
+		function canPublish($member = null) {
  54
+			// return boolean from custom logic
  55
+		}
  56
+		function canDeleteFromLive($member = null) {
  57
+			// return boolean from custom logic
  58
+		}
  59
+	}
37 60
 
38  
-![url table](http://silverstripe.org/assets/screenshots/Nested-URLs-Table.png)
  61
+Stage operations:
  62
+
  63
+ * `$page->doUnpublish()`: removes the "Live" record, with additional permission checks,
  64
+	as well as special logic for VirtualPage and RedirectorPage associations
  65
+ * `$page->doPublish()`: Inverse of doUnpublish()
  66
+ * `$page->doRevertToLive()`: Reverts current record to live state (makes sense to save to "draft" stage afterwards)
  67
+ * `$page->doRestoreToStage()`: Restore the content in the active copy of this SiteTree page to the stage site.
  68
+	
  69
+
  70
+Hierarchy operations (defined on `[api:Hierarchy]`:
39 71
 
40  
-## Limiting Children/Parent
  72
+ * `$page->liveChildren()`: Return results only from live table
  73
+ * `$page->stageChildren()`: Return results from the stage table
  74
+ * `$page->AllHistoricalChildren()`: Return all the children this page had, including pages that were deleted from both stage & live.
  75
+ * `$page->AllChildrenIncludingDeleted()`: Return all children, including those that have been deleted but are still in live.
41 76
 
42  
-By default, any page type can be the child of any other page type.  However, there are 4 static properties that can be
  77
+## Limiting Hierarchy
  78
+
  79
+By default, any page type can be the child of any other page type.  
  80
+However, there are static properties that can be
43 81
 used to set up restrictions that will preserve the integrity of the page hierarchy.
44 82
 
  83
+Example: Restrict blog entry pages to nesting underneath their blog holder
  84
+
45 85
 	:::php
46 86
 	class BlogHolder extends Page {
47  
-	
48 87
 	  // Blog holders can only contain blog entries
49 88
 	  static $allowed_children = array("BlogEntry");
50  
-	
51 89
 	  static $default_child = "BlogEntry";
52  
-	
53  
-	...
  90
+	  // ...
  91
+	}
54 92
 	
55 93
 	class BlogEntry extends Page {
56 94
 	  // Blog entries can't contain children
57 95
 	  static $allowed_children = "none";
58  
-	
59  
-	  static $default_parent = "blog";
60  
-	
61 96
 	  static $can_be_root = false;
62  
-	
63  
-	...
64  
-	
  97
+	  // ...
  98
+	}	
65 99
 	
66 100
 	class Page extends SiteTree {
67 101
 	  // Don't let BlogEntry pages be underneath Pages.  Only underneath Blog holders.
68 102
 	  static $allowed_children = array("*Page,", "BlogHolder");
69  
-	  
70 103
 	}
71 104
 
72 105
 
@@ -77,210 +110,33 @@ subclasses.  Otherwise, the class and all its subclasses are allowed.
77 110
 *  **default_child:** If a page is allowed more than 1 type of child, you can set a default.  This is the value that
78 111
 will be automatically selected in the page type dropdown when you create a page in the CMS.
79 112
 
80  
-*  **default_parent:** This should be set to the *URLSegment* of a specific page, not to a class name.  If you have
81  
-asked to create a page of a particular type that's not allowed underneath the page that you have selected, then the
82  
-default_parent page will be selected.  For example, if you have a gallery page open in the CMS, and you select add blog
83  
-entry, you can set your site up to automatically select the blog page as a parent.
84  
-
85 113
 *  **can_be_root:** This is a boolean variable.  It lets you specify whether the given page type can be in the top
86 114
 level.
87 115
 
88  
-Note that there is no allowed_parents control.  To set this, you will need to specify the allowed_children of all other
89  
-page types to exclude the page type in question.  IMO this is less than ideal; it's possible that in a future release we
90  
-will add allowed_parents, but right now we're trying to limit the amount of mucking around with the API we do.
91  
-
92  
-Here is an overview of everything you can add to a class that extends sitetree.  NOTE: this example will not work, but
93  
-it is a good starting point, for choosing your customisation.
94  
-
95  
-	:::php
96  
-	class Page extends SiteTree {
97  
-	
98  
-		// tree customisation
99  
-	
100  
-		static $icon = "";
101  
-		static $allowed_children = array("SiteTree"); // set to string "none" or array of classname(s)
102  
-		static $default_child = "Page"; //one classname
103  
-		static $default_parent = null; // NOTE: has to be a URL segment NOT a class name
104  
-		static $can_be_root = true; //
105  
-		static $hide_ancestor = null; //dont show ancestry class
106  
-	
107  
-		// extensions and functionality
108  
-	
109  
-		static $versioning = array();
110  
-		static $default_sort = "Sort";
111  
-		/static $extensions = array();
112  
-		public static $breadcrumbs_delimiter = " &raquo; ";
113  
-	
114  
-	
115  
-		public function canCreate() {
116  
-			//here is a trick to only allow one (e.g. holder) of a page
117  
-			$class = $this->class;
118  
-			return !$class::get()->Count();
119  
-		}
120  
-	
121  
-		public function canDelete() {
122  
-			return false;
123  
-		}
124  
-	
125  
-		public function getCMSFields() {
126  
-			$fields = parent::getCMSFields();
127  
-			return $fields;
128  
-		}
129  
-
130  
-
131  
-## Recipes
132  
-
133  
-### Automatic Child Selection
134  
-
135  
-By default, `[api:SiteTree]` class to build a tree using the ParentID field.  However, sometimes, you want to change
136  
-this default behaviour.
  116
+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.
137 117
 
138  
-For example, in our e-commerce module, we use a many-to-many join, Product::Parents, to let you put Products in multiple
139  
-groups.  Here's how to implement such a change:
  118
+## Permission Control
140 119
 
141  
-*  **Set up your new data model:** Create the appropriate many-many join or whatever it is that you're going to use to
142  
-store parents.
143 120
 
144  
-*  **Define stageChildren method:** This method should return the children of the current page, for the current version.
145  
- If you use DataObject::get, the `[api:Versioned]` class will rewrite your query to access the live site when
146  
-appropriate.
147 121
 
148  
-*  **Define liveChildren method:** The method should return the children of the current page, for the live site.
  122
+## Tree Display (Description, Icons and Badges)
149 123
 
150  
-Both the CMS and the site's data controls will make use of this, so navigation, breadcrumbs, etc will be updated.  If 1
151  
-node appears in the tree more than once, it will be represented differently. 
  124
+The page tree in the CMS is a central element to manage page hierarchies,
  125
+hence its display of pages can be customized as well.
152 126
 
153  
-**TO DO:** Work out this representation.
154  
-
155  
-
156  
-###  Custom Children Getters
157  
-
158  
-Returning custom children for a specific `SiteTree` subclass can be handy to influence the tree display within the
159  
-CMS. An example of custom children might be products which belong to multiple categories. One category would get its
160  
-products from a `$many_many` join rather than the default relations.
161  
-
162  
-Children objects are generated from two functions `stageChildren()` and `liveChildren()` and the tree generation in
163  
-the CMS is calculated from `numChildren()`. Please keep in mind that the returned children should still be instances
164  
-of `SiteTree`.
165  
-
166  
-Example:
167  
-
168  
-	:::php
169  
-	class MyProduct extends Page {
170  
-		static $belongs_many_many = array(
171  
-			'MyCategories' => 'MyCategory'
172  
-		);
173  
-	}
174  
-	class MyCategory extends Page {
175  
-		static $many_many = array(
176  
-			'MyProducts' => 'MyProduct'
177  
-		);
178  
-		public function stageChildren($showAll = false) {
179  
-			// @todo Implement $showAll
180  
-			return $this->MyProducts();
181  
-		}
182  
-	
183  
-		public function liveChildren($showAll = false) {
184  
-			// @todo Implement $showAll
185  
-			return $this->MyProducts();
186  
-		}
187  
-		public function numChildren() {
188  
-			return $this->MyProducts()->Count();
189  
-		}
190  
-	}	}
191  
-	}
192  
-
193  
-
194  
-
195  
-### Multiple parents in the tree
196  
-
197  
-The `[api:LeftAndMain]` tree supports multiple parents.  We overload CMSTreeClasses and make it include "manyparents" in
198  
-the class list.
199  
-
200  
-	:::php
201  
-	public function CMSTreeClasses($controller) {
202  
-		return parent::CMSTreeClasses($controller) . ' manyparents';
203  
-	}
204  
-
205  
-
206  
-Don't forget to define a new Parent() method that also references your new many-many join (or however it is you've set
207  
-up the hierarchy!
  127
+On a most basic level, you can specify a custom page icon
  128
+to make it easier for CMS authors to identify pages of this type,
  129
+when navigating the tree or adding a new page:
208 130
 
209 131
 	:::php
210  
-	public function getParent() {
211  
-	  return $this->Parent();
212  
-	}
213  
-	public function Parent() {
214  
-	  $parents = $this->Parents();
215  
-	  if($parents) return $parents->First();
  132
+	class StaggPage extends Page {
  133
+		static $singular_name = 'Staff Directory';
  134
+		static $plural_name = 'Staff Directories';
  135
+		static $description = 'Two-column layout with a list of staff members';
  136
+		static $icon = 'mysite/images/staff-icon.png';
  137
+		// ...
216 138
 	}
217 139
 
218  
-
219  
-Sometimes, you don't want to mess with the CMS in this manner.  In this case, leave stageChildren() and liveChildren()
220  
-as-is, and instead make another method, such as ChildProducts(), to get the data from your many-many join.
221  
-
222  
-### Dynamic Grouping
223  
-
224  
-Something that has been talked about [here](http://www.silverstripe.com/site-builders-forum/flat/15416#post15940) is the
225  
-concept of "dynamic grouping".  In essence, it means adding navigational tree nodes to the tree that don't correspond to
226  
-a database record.
227  
-
228  
-How would we do this?  In our example, we're going to update BlogHolder to show BlogEntry children grouped into months.
229  
-
230  
-We will create a class called BlogMonthTreeNode, which will extend ViewableData instead of DataRecord, since it's not
231  
-saved into the database.  This will represent our dynamic groups.
232  
-
233  
-### LeftAndMain::getSiteTreeFor()
234  
-
235  
-Currently LeftAndMain::getSiteTreeFor() Calls LeftAndMain::getRecord($id) to get a new record.  We need to instead
236  
-create a new public function getTreeRecord($id) which will be able to create BlogMonthTreeNode objects as well as look up
237  
-SiteTree records from the database.
238  
-
239  
-The IDs don't **need** be numeric; so we can set the system to allow for 2 $id formats.
240  
-
241  
-*  (ID): A regular SiteTree object
242  
-*  BlogMonthTreeNode-(BlogHolderID)-(Year)-(Month): A BlogMonthTreeNode object
243  
-
244  
-To keep the code generic, we will assume that if the $id isn't numeric, then we should explode('-', $id), and use the
245  
-first part as the classname, and all the remaining parts as arguments to the constructor.
246  
-
247  
-Your BlogMonthTreeNode constructor will then need to take $blogHolderID, $year, $month as arguments.
248  
-
249  
-### Divorcing front-end site's Children() and the CMS's AllChildrenIncludingDeleted()
250  
-
251  
-We need a way of cleanly specifying that there are two different child sources - children for the CMS tree, and children
252  
-for the front-end site.
253  
-
254  
-*  We currently have stageChildren() / liveChildren()
255  
-*  We should probably add cmsStageChildren() and cmsLiveChildren() into the mix, for SiteTree.
256  
-
257  
-AllChildrenIncludingDeleted() could then call the "cms..." versions of the functions, but if we were to to this, we
258  
-should probably rename AllChildrenIncludingDeleted() to CMSTreeChildren() or something like that.
259  
-
260  
-### BlogHolder::cmsStageChildren() & BlogHolder::cmsLiveChildren()
261  
-
262  
-We will need to define these methods, to 
263  
-
264  
-*  Get the stage/live children of the page, grouped by month
265  
-*  For each entry returned, generate a new BlogMonthTreeNode object.
266  
-*  Return that as a dataobjectset.
267  
-
268  
-### BlogMonthTreeNode
269  
-
270  
-*  Parameter 'ID': should return 'BlogMonthTreeNode-(BlogHolderID)-(Year)-(Month)'.  You can do  this by implementing
271  
-getID().
272  
-*  Methods cmsStageChildren() and cmsLiveChildren(): These should return the blog-entries for that month.
273  
-
274  
-After that, there will be some other things to tweak, like the tree icons.
275  
-
276  
-### Where to from here?
277  
-
278  
-This is a lot of work for the specific example of blog-entries grouped by month.  Instead of BlogMonthTreeNode, you
279  
-could genericise this to a DynamicTreeGroup class, which would let you specify the parent node, the type of grouping,
280  
-and the specific group.
281  
-
282  
-## TODO
283  
-Clean up this documentation
284  
-
285  
-## API Documentation
286  
-`[api:Sitetree]`
  140
+You can also add custom "badges" to each page in the tree,
  141
+which denote status. Built-in examples are "Draft" and "Deleted" flags.
  142
+This is detailed in the ["Customize the CMS Tree" howto](/howto/customize-cms-tree).
2  docs/en/reference/templates-upgrading-guide.md
Source Rendered
@@ -2,7 +2,7 @@
2 2
 
3 3
 These are the main changes to the SiverStripe 3 template language.
4 4
 
5  
-## Control
  5
+## Control blocks: Loops vs. Scope
6 6
 
7 7
 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.
8 8
 
291  docs/en/reference/templates.md
Source Rendered
@@ -12,54 +12,37 @@ Here is a very simple template:
12 12
 
13 13
 	:::ss
14 14
 	<html>
15  
-		<%-- This is my first template --%>
16 15
 		<head>
17 16
 			<% base_tag %>
18 17
 			<title>$Title</title>
19  
-			$MetaTags
  18
+			<% require themedCSS(screen) %>
20 19
 		</head>
21 20
 		<body>
22  
-		<div id="Container">
23  
-			<div id="Header">
  21
+			<header>
24 22
 				<h1>Bob's Chicken Shack</h1>
25  
-				<% with $CurrentMember %>
26  
-				<p>You are logged in as $FirstName $Surname.</p>
27  
-				<% end_if %>
28  
-			</div>
29  
-			<div id="Navigation">
30  
-				<% if $Menu(1) %>
31  
-				<ul>
32  
-					<% loop $Menu(1) %>	  
33  
-					<li><a href="$Link" title="Go to the $Title page" class="$LinkingMode">$MenuTitle</a></li>
34  
-					<% end_loop %>
35  
-				</ul>
36  
-				<% end_if %>
37  
-			</div>
38  
-			<div class="typography">
39  
-				$Layout
40  
-			</div>
41  
-			<div id="Footer">
42  
-				<p>Copyright $Now.Year</p>
43  
-			</div>
44  
-		</div>
  23
+			</header>
  24
+
  25
+			<% with $CurrentMember %>
  26
+				<p>Welcome $FirstName $Surname.</p>
  27
+			<% end_with %>
  28
+			
  29
+			<% if Dishes %>
  30
+			<ul>
  31
+				<% loop Dishes %>	  
  32
+					<li>$Title ($Price.Nice)</li>
  33
+				<% end_loop %>
  34
+			</ul>
  35
+			<% end_if %>
  36
+			
  37
+			<% include Footer %>
45 38
 		</body>
46 39
 	</html>
47 40
 
48  
-# Template elements
49  
-
50  
-### Base Tag
51  
-
52  
-The `<% base_tag %>` placeholder is replaced with the HTML base element. Relative links within a document (such as `<img
53  
-src="someimage.jpg" />`) will become relative to the URI specified in the base tag. This ensures the browser knows where
54  
-to locate your site’s images and css files. So it is a must for templates!
  41
+More sophisticated use of templates for pages managed in the CMS,
  42
+including template inheritance and navigation loops
  43
+is documented in the [page types](/topics/page-types) topic.
55 44
 
56  
-It renders in the template as `<base href="http://www.mydomain.com" /><!--[if lte IE 6]></base><![endif]-->`
57  
-
58  
-### Layout Tag
59  
-
60  
-In every SilverStripe theme there is a default `Page.ss` file in the `/templates` folder. `$Layout` appears in this file
61  
-and is a core variable which includes a Layout template inside the `/templates/Layout` folder once the page is rendered. 
62  
-By default the `/templates/Layout/Page.ss` file is included in the html template.
  45
+# Template elements
63 46
 
64 47
 ## Variables
65 48
 
@@ -67,7 +50,6 @@ Variables are things you can use in a template that grab data from the page and
67 50
 
68 51
 	:::ss
69 52
 	$Title
70  
-	
71 53
 
72 54
 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.
73 55
 
@@ -141,81 +123,90 @@ See [CSS](/topics/css) and [Javascript](/topics/javascript) topics for individua
141 123
 [requirements](reference/requirements) for good examples of including both Javascript and CSS files.
142 124
 
143 125
 ## Conditional Logic
  126
+
144 127
 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.
145 128
 
146 129
 The simplest if block is to check for the presence of a value.
147 130
 
148  
-    <% if $CurrentMember %>
149  
-        <p>You are logged in as $CurrentMember.FirstName $CurrentMember.Surname.</p>
150  
-    <% end_if %>
  131
+	:::ss
  132
+  <% if $CurrentMember %>
  133
+      <p>You are logged in as $CurrentMember.FirstName $CurrentMember.Surname.</p>
  134
+  <% end_if %>
151 135
 
152 136
 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.
153 137
 
154  
-    <% if $MyDinner="kipper" %>
155  
-        Yummy, kipper for tea.
156  
-    <% end_if %>
  138
+	:::ss
  139
+  <% if $MyDinner="kipper" %>
  140
+      Yummy, kipper for tea.
  141
+  <% end_if %>
157 142
 
158 143
 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.
159 144
 
160 145
 This example shows the use of the `else` option. The markup after `else` is output if the tested condition is *not* true.
161 146
 
162  
-    <% if $MyDinner="kipper" %>
163  
-        Yummy, kipper for tea
164  
-    <% else %>
165  
-        I wish I could have kipper :-(
166  
-    <% end_if %>
  147
+	:::ss
  148
+  <% if $MyDinner="kipper" %>
  149
+      Yummy, kipper for tea
  150
+  <% else %>
  151
+      I wish I could have kipper :-(
  152
+  <% end_if %>
167 153
 
168 154
 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.
169 155
 
170  
-    <% if $MyDinner="quiche" %>
171  
-        Real men don't eat quiche
172  
-    <% else_if $MyDinner=$YourDinner %>
173  
-        We both have good taste
174  
-    <% else %>
175  
-        Can I have some of your chips?
176  
-    <% end_if %>
  156
+	:::ss
  157
+  <% if $MyDinner="quiche" %>
  158
+      Real men don't eat quiche
  159
+  <% else_if $MyDinner=$YourDinner %>
  160
+      We both have good taste
  161
+  <% else %>
  162
+      Can I have some of your chips?
  163
+  <% end_if %>
177 164
 
178 165
 This example shows the use of `not` to negate the test.
179 166
 
180  
-    <% if not $DinnerInOven %>
181  
-        I'm going out for dinner tonight.
182  
-    <% end_if %>
  167
+	:::ss
  168
+  <% if not $DinnerInOven %>
  169
+      I'm going out for dinner tonight.
  170
+  <% end_if %>
183 171
 
184 172
 You can combine two or more conditions with `||` ("or"). The markup is used if *either* of the conditions is true.
185 173
 
186  
-    <% if $MyDinner=="kipper" || $MyDinner=="salmon" %>
187  
-        yummy, fish for tea
188  
-    <% end_if %>
  174
+	:::ss
  175
+  <% if $MyDinner=="kipper" || $MyDinner=="salmon" %>
  176
+      yummy, fish for tea
  177
+  <% end_if %>
189 178
 
190 179
 You can combine two or more conditions with `&&` ("and"). The markup is used if *both* of the conditions are true.
191 180
 
192  
-    <% if $MyDinner=="quiche" && $YourDinner=="kipper" %>
193  
-        Lets swap dinners
194  
-    <% end_if %>
  181
+	:::ss
  182
+  <% if $MyDinner=="quiche" && $YourDinner=="kipper" %>
  183
+      Lets swap dinners
  184
+  <% end_if %>
195 185
 
196  
-As you'd expect, these can be nested:
  186
+## Looping Over Lists
197 187
 
198  
-    <% if $MyDinner=="chicken" %>
199  
-        <% if $Wine=="red" %>
200  
-            You're doing it wrong
201  
-        <% else %>
202  
-            Perfect
203  
-        <% end_if %>
204  
-    <% end_if %>
  188
+The `<% loop %>...<% end_loop %>` tag is used to **iterate** or loop over a collection of items. For example:
205 189
 
206  
-## Looping Over Datasets
  190
+	:::ss
  191
+	<ul>
  192
+	<% loop $Children %>
  193
+	  <li>$Title</li>
  194
+	<% end_loop %>
  195
+	</ul>
207 196
 
208  
-The `<% loop %>...<% end_loop %>` tag is used to **iterate** or loop over a collection of items. For example:
  197
+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.
209 198
 
210  
-    <ul>
211  
-    <% loop $Children %>
212  
-      <li>$Title</li>
213  
-    <% end_loop %>
214  
-    </ul>
  199
+### Position Indicators
215 200
 
216  
-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.
  201
+Inside the loop scope, there are many variables at your disposal to determine the current position
  202
+in the list and iteration:
217 203
 
218  
-The value that given in the `<% loop %>` tags should be a collection variable.
  204
+ * `$Even`, `$Odd`: Returns boolean, handy for zebra striping
  205
+ * `$EvenOdd`: Returns a string, either 'even' or 'odd'. Useful for CSS classes.
  206
+ * `$First`, `$Last`, `$Middle`: Booleans about the position in the list
  207
+ * `$FirstLast`: Returns a string, "first", "last", or "". Useful for CSS classes.
  208
+ * `$Pos`: The current position in the list (integer). Will start at 1.
  209
+ * `$TotalItems`: Number of items in the list (integer)
219 210
 
220 211
 ### Modulus and MultipleOf
221 212
 
@@ -249,6 +240,8 @@ You can also use $MultipleOf(value, offset) to help build columned layouts. In t
249 240
 
250 241
 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.
251 242
 
  243
+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`.
  244
+
252 245
 ### With
253 246
 
254 247
 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,
265 258
 
266 259
 returns the number of items in the $Children collection.
267 260
 
268  
-### Top
  261
+## Pagination
269 262
 
270  
-    $Top.Title
  263
+Lists can be paginated, and looped over page-by-page.
  264
+For this to work, the list needs to be wrapped in a `[api:PaginatedList]`.
  265
+The process is explained in detail on the ["pagination" howto](/howto/pagination).
  266
+The list is split up in multiple "pages", each . Note that "page" is this context
  267
+does not necessarily refer to a `Page` class (although it often happens to be one).
271 268
 
272  
-### Up
  269
+ * `$MoreThanOnePage`: Returns true when we have a multi-page list, restricted with a limit.
  270
+ * `$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.
  271
+ * `$CurrentPage`: Current page iterated on
  272
+ * `$TotalPages`: Total number of pages
  273
+ * `$TotalItems`: This returns the total number of items across all pages.
  274
+ * `$Pages`: The actual (limited) list of records, use in an inner loop
  275
+ * `$PageNum`: Page number, starting at 1 (within `$Pages`)
  276
+ * `$Link`: Links to the current controller URL, setting this page as current via a GET parameter (within `$Pages`)
  277
+*  `$CurrentBool`: Returns true if you're currently on that page (within `$Pages`)
273 278
 
274  
-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.
  279
+## Formatting and Casting
275 280
 
276  
-    $Up.Owner
  281
+Properties are usually auto-escaped in templates to ensure consistent representation,
  282
+and avoid format clashes like displaying unescaped ampersands in HTML.
  283
+By default, values are escaped as `XML`, which is equivalent to `HTML` for this purpose.
  284
+There's some exceptions to this rule, see the ["security" topic](/topics/security).
277 285
 
278  
-
279  
-## Formatting Template Values
280  
-
281  
-The following example takes the Title field of our object, casts it to a `[api:Varchar]` object, and then calls
282  
-the `$XML` object on that Varchar object.
  286
+In case you want to explicitly allow unescaped HTML input,
  287
+the property can be cast as `[api:HTMLText]`.
  288
+The following example takes the `Content` field in a `SiteTree` class,
  289
+which is of this type. It forces the content into an explicitly escaped format.
283 290
 
284 291
 	:::ss
285  
-	<% with Title %>
286  
-	$XML
287  
-	<% end_with %>
  292
+	$Content.XML // transforms e.g. "<em>alert</em>" to "&lt;em&gt;alert&lt;/em&gt;"
288 293
 
289  
-Note that this code can be more concisely represented as follows:
  294
+Apart from value formatting, there's many methods to transform them as well,
  295
+For example, the built in `$Now` placeholder is an instance of `[api:Date]`,
  296
+and returns the current date in a standard system format. 
  297
+Since its an object, you can use the helper methods to return other formats:
290 298
 
291 299
 	:::ss
292  
-	$Title.XML
  300
+	$Now.Year // Current year
  301
+	$Now.Nice // Localized date, based on i18n::get_locale()
293 302
 
294 303
 See [data-types](/topics/data-types) for more information.
295 304
 
@@ -311,49 +320,64 @@ Pulling apart this example we see:
311 320
 Using standard HTML comments is supported. These comments will be included in the published site. 
312 321
 
313 322
 	:::ss
314  
-	$EditForm <!-- Some Comment About the Edit Form -->
  323
+	$EditForm <!-- Some public comment about the form -->
315 324
 
316 325
 
317 326
 However you can also use special SilverStripe comments which will be stripped out of the published site. This is useful