Small text changes, added api links, cont. updating images for tutorials, fixed tutorials from member feedback

This commit is contained in:
Michael Andrewartha 2011-03-09 10:05:51 +13:00 committed by Ingo Schommer
parent e9f20cf9f3
commit f3ac57394d
79 changed files with 722 additions and 647 deletions

View File

@ -406,7 +406,7 @@ to debug.
## Related ## Related
* [Templates](/topics/templates) * [Templates](/topics/templates)
* [Themes](/topics/themes) * [Themes](/topics/themes)
* [Developing Themes](theme-development) * [Developing Themes](/topics/theme-development)
* [Widgets](/topics/widgets) * [Widgets](/topics/widgets)
* [Images](/reference/image) * [Images](/reference/image)
* [Built in page controls](/reference/built-in-page-controls) * [Built in page controls](/reference/built-in-page-controls)

View File

@ -9,7 +9,7 @@ BBCode is used by default in the [blog](http://silverstripe.org/blog-module) and
To add bbcode parsing to a template, instead of $Content use: To add bbcode parsing to a template, instead of $Content use:
:::php :::ss
$Content.Parse(BBCodeParser) $Content.Parse(BBCodeParser)

View File

@ -60,7 +60,7 @@ SilverStripe supports a simple set of conditional logic
<% end_if %> <% end_if %>
See more information on conditional logic on [templates](templates). See more information on conditional logic on [templates](/topics/templates).
### Site wide settings ### Site wide settings

View File

@ -15,7 +15,7 @@ See `[api:TableListField]`.
## Setting Parent/Child-Relations ## Setting Parent/Child-Relations
ComplexTableField tries to determine the parent-relation automatically by looking at the $has_one property on the listed `[api:ComplexTableField]` tries to determine the parent-relation automatically by looking at the $has_one property on the listed
child, or the record loaded into the surrounding form (see getParentClass() and getParentIdName()). You can force a child, or the record loaded into the surrounding form (see getParentClass() and getParentIdName()). You can force a
specific parent relation: specific parent relation:
@ -62,18 +62,18 @@ You can override this behaviour in various ways:
If you don't want several functions to appear (e.g. no add-link), there's several ways: If you don't want several functions to appear (e.g. no add-link), there's several ways:
* Use ComplexTableField->setPermissions(array("show","edit")) to limit the functionality without touching the template * Use `ComplexTableField->setPermissions(array("show","edit"))` to limit the functionality without touching the template
(more secure). Possible values are "show","edit", "delete" and "add". (more secure). Possible values are "show","edit", "delete" and "add".
* Subclass ComplexTableField and override the rendering-mechanism * Subclass `[api:ComplexTableField]` and override the rendering-mechanism
* Use ComplexTableField->setTemplate() and ComplexTableField->setTemplatePopup() to provide custom templates * Use `ComplexTableField->setTemplate()` and `ComplexTableField->setTemplatePopup()` to provide custom templates
### Customising fields and Requirements in the popup ### Customising fields and Requirements in the popup
There are several ways to customise the fields in the popup. Often you would want to display more information in the There are several ways to customise the fields in the popup. Often you would want to display more information in the
popup as there is more real-estate for you to play with. popup as there is more real-estate for you to play with.
ComplexTableField gives you several options to do this. You can either `[api:ComplexTableField]` gives you several options to do this. You can either
* Pass a FieldSet in the constructor. * Pass a FieldSet in the constructor.
* Pass a String in the constructor. * Pass a String in the constructor.
@ -83,7 +83,7 @@ The second will call the String as a method on the source class (Which should re
Popup. Popup.
You can also customise Javascript which is loaded for the Lightbox. As Requirements::clear() is called when the popup is You can also customise Javascript which is loaded for the Lightbox. As Requirements::clear() is called when the popup is
instantiated, ComplexTableField will look for a function to gather any specific requirements that you might need on your instantiated, `[api:ComplexTableField]` will look for a function to gather any specific requirements that you might need on your
source class. (e.g. Inline Javascript or styling). source class. (e.g. Inline Javascript or styling).
For this, create a function called "getRequirementsForPopup". For this, create a function called "getRequirementsForPopup".
@ -116,11 +116,11 @@ You'll have to do something like this in your form:
You have to hack in an ID on the form, as the CMS forms have this, and front end forms usually do not. You have to hack in an ID on the form, as the CMS forms have this, and front end forms usually do not.
It's not a perfect solution, but it works relatively well to get a simple ComplexTableField up and running on the front It's not a perfect solution, but it works relatively well to get a simple `[api:ComplexTableField]` up and running on the front
end. end.
To come: Make it a lot more flexible so tables can be easily used on the front end. It also needs to be flexible enough To come: Make it a lot more flexible so tables can be easily used on the front end. It also needs to be flexible enough
to use a popup as well, out of the box. to use a popup as well, out of the box.
## Subclassing ## Subclassing
@ -144,7 +144,7 @@ Most of the time, you need to override the following methods:
* Find a less fragile solution for accessing this field through the main controller and ReferencedField, e.g. build a * Find a less fragile solution for accessing this field through the main controller and ReferencedField, e.g. build a
seperate CTF-instance (doesn't necessarly have to be connected to the original by ReferencedField) seperate CTF-instance (doesn't necessarly have to be connected to the original by ReferencedField)
* Control width/height of popup by constructor (hardcoded at the moment) * Control width/height of popup by constructor (hardcoded at the moment)
* Integrate search from MemberTableField.php directly on ComplexTableField * Integrate search from MemberTableField.php directly on `[api:ComplexTableField]`
* Less performance-hungry implementation of detail-view paging (don't return all items on a single view) * Less performance-hungry implementation of detail-view paging (don't return all items on a single view)
* Use automatic has-many and many-many functions to return a ComponentSet rather than building the join manually * Use automatic has-many and many-many functions to return a ComponentSet rather than building the join manually
* Javascript/Ajax-Sorting (see http://www.activewidgets.com/grid/ and http://openrico.org/rico/livegrid.page) * Javascript/Ajax-Sorting (see [http://www.activewidgets.com/grid/](http://www.activewidgets.com/grid/) and [http://openrico.org/rico/livegrid.page](http://openrico.org/rico/livegrid.page))

View File

@ -5,7 +5,7 @@ opting for "convention over configuration". This page details what that databas
## Base tables ## Base tables
Each direct sub-class of DataObject will have its own table. Each direct sub-class of `[api:DataObject]` will have its own table.
The following fields are always created. The following fields are always created.
@ -36,21 +36,21 @@ data sub-classed objects across **multiple tables**.
For example, suppose we have the following set of classes: For example, suppose we have the following set of classes:
* Class SiteTree extends DataObject: Title, Content fields * Class `[api:SiteTree]` extends `[api:DataObject]`: Title, Content fields
* Class Page extends SiteTree: Abstract field * Class `[api:Page]` extends `[api:SiteTree]`: Abstract field
* Class NewsSection extends SiteTree: *No special fields* * Class NewsSection extends `[api:SiteTree]`: *No special fields*
* Class NewsArticle extend Page: ArticleDate field * Class NewsArticle extend `[api:Page]`: ArticleDate field
The data for the following classes would be stored across the following tables: The data for the following classes would be stored across the following tables:
* SiteTree * `[api:SiteTree]`
* ID: Int * ID: Int
* ClassName: Enum('SiteTree', 'Page', 'NewsArticle') * ClassName: Enum('SiteTree', 'Page', 'NewsArticle')
* Created: Datetime * Created: Datetime
* LastEdited: Datetime * LastEdited: Datetime
* Title: Varchar * Title: Varchar
* Content: Text * Content: Text
* Page * `[api:Page]`
* ID: Int * ID: Int
* Abstract: Text * Abstract: Text
* NewsArticle * NewsArticle
@ -59,16 +59,16 @@ The data for the following classes would be stored across the following tables:
The way it works is this: The way it works is this:
* "Base classes" are direct sub-classes of DataObject. They are always given a table, whether or not they have special * "Base classes" are direct sub-classes of `[api:DataObject]`. They are always given a table, whether or not they have
fields. This is called the "base table" special fields. This is called the "base table"
* The base table's ClassName field is set to class of the given record. It's an enumeration of all possible * The base table's ClassName field is set to class of the given record. It's an enumeration of all possible
sub-classes of the base class (including the base class itself) sub-classes of the base class (including the base class itself)
* Each sub-class of the base object will also be given its own table, *as long as it has custom fields*. In the * Each sub-class of the base object will also be given its own table, *as long as it has custom fields*. In the
example above, NewsSection didn't have its own data and so an extra table would be redundant. example above, NewsSection didn't have its own data and so an extra table would be redundant.
* In all the tables, ID is the primary key. A matching ID number is used for all parts of a particular record: * In all the tables, ID is the primary key. A matching ID number is used for all parts of a particular record:
record #2 in Page refers to the same object as record #2 in SiteTree. record #2 in Page refers to the same object as record #2 in `[api:SiteTree]`.
To retrieve a news article, SilverStripe joins the SiteTree, Page and NewsArticle tables by their ID fields. We use a To retrieve a news article, SilverStripe joins the `[api:SiteTree]`, `[api:Page]` and NewsArticle tables by their ID fields. We use a
left-join for robustness; if there is no matching record in Page, we can return a record with a blank Article field. left-join for robustness; if there is no matching record in Page, we can return a record with a blank Article field.
## Staging and versioning ## Staging and versioning
@ -126,4 +126,4 @@ other database platforms if we chose to support them.
* We'd like to support more than just MySQL, however, there needs to be a pretty good reason for doing so since it will * We'd like to support more than just MySQL, however, there needs to be a pretty good reason for doing so since it will
become something that needs to be supported for the rest of SilverStripe's life and could easily become an albatross. become something that needs to be supported for the rest of SilverStripe's life and could easily become an albatross.
On the cards are MS SQL, PostgreSQL and SQLite. On the cards are MS SQL, PostgreSQL and SQLite.
* It could be desireable to implement a non-repeating auto-numbering system. * It could be desirable to implement a non-repeating auto-numbering system.

View File

@ -12,7 +12,7 @@ A single database record & abstract class for the data-access-model.
## Basics ## Basics
The call to `DataObject->getCMSFields()` is the centerpiece of every data administration interface in Silverstripe, The call to `DataObject->getCMSFields()` is the centerpiece of every data administration interface in SilverStripe,
which returns a `[api:FieldSet]`''. which returns a `[api:FieldSet]`''.
:::php :::php
@ -33,8 +33,6 @@ These calls retrieve a `[api:FieldSet]` for the area where you intend to work wi
* Requirements: SilverStripe 2.3.* * Requirements: SilverStripe 2.3.*
// bla
:::php :::php
$fields = singleton('MyDataObject')->getCMSFields(); $fields = singleton('MyDataObject')->getCMSFields();
@ -82,8 +80,8 @@ Example: Simple Definition
} }
Searchable fields will be appear in the search interface with a default form field (usually a TextField) and a default Searchable fields will be appear in the search interface with a default form field (usually a `[api:TextField]`) and a default
search filter assigned (usually an ExactMatchFilter). To override these defaults, you can specify additional information search filter assigned (usually an `[api:ExactMatchFilter]`). To override these defaults, you can specify additional information
on `$searchable_fields`: on `$searchable_fields`:
:::php :::php
@ -95,7 +93,7 @@ on `$searchable_fields`:
} }
If you assign a single string value, you can set it to be either a FormField or SearchFilter. To specify both, you can If you assign a single string value, you can set it to be either a `[api:FormField]` or `[api:SearchFilter]`. To specify both, you can
assign an array: assign an array:
:::php :::php
@ -144,7 +142,7 @@ To include relations (''$has_one'', `$has_many` and `$many_many`) in your search
* Requirements: SilverStripe 2.3.* * Requirements: SilverStripe 2.3.*
Summary fields can be used to show a quick overview of the data for a specific DataObject record. Most common use is Summary fields can be used to show a quick overview of the data for a specific `[api:DataObject]` record. Most common use is
their display as table columns, e.g. in the search results of a `[api:ModelAdmin]` CMS interface. their display as table columns, e.g. in the search results of a `[api:ModelAdmin]` CMS interface.
Example: Getting predefined summary fields Example: Getting predefined summary fields

View File

@ -9,7 +9,7 @@ implementation. Have a look at `[api:Object->useCustomClass()]`.
## Usage ## Usage
Your Decorator will nee to be a subclass of DataObjectDecorator or the Extension class. Your Decorator will nee to be a subclass of `[api:DataObjectDecorator]` or the `[api:Extension]` class.
:::php :::php
<?php <?php
@ -27,7 +27,7 @@ class you want to extend.
### Adding a decorator to a built-in class ### Adding a decorator to a built-in class
Sometimes you will want to add decorators to classes that you didn't make. For example, you might want to add the Sometimes you will want to add decorators to classes that you didn't make. For example, you might want to add the
ForumRole decorator to the Member object. `[api:ForumRole]` decorator to the `[api:Member]` object.
:::php :::php
@ -98,19 +98,19 @@ The $fields parameter is passed by reference, as it is an object.
### Custom database generation ### Custom database generation
Some decorators are designed to transparently add more sophisticated data-collection capabilities to your data object. Some decorators are designed to transparently add more sophisticated data-collection capabilities to your data object.
For example, Versioned adds version tracking and staging to any data object that it is applied to. To do this, you need For example, `[api:Versioned]` adds version tracking and staging to any data object that it is applied to. To do this,
to be able to create additional database tables and fields to keep your state stored in. you need to be able to create additional database tables and fields to keep your state stored in.
To do this, define an **augmentDatabase()** method on your decorator. This will be called when db/build is visited. To do this, define an **augmentDatabase()** method on your decorator. This will be called when db/build is visited.
* You can query $this->owner for information about the data object, such as the fields it has * You can query ``$this->owner`` for information about the data object, such as the fields it has
* You can use **DB::requireTable($tableName, $fieldList, $indexList)** to set up your new tables. This function takes * You can use **DB::requireTable($tableName, $fieldList, $indexList)** to set up your new tables. This function takes
care of creating, modifying, or leaving tables as required, based on your desired schema. care of creating, modifying, or leaving tables as required, based on your desired schema.
### Custom write queries ### Custom write queries
If you have customised the generated database, then you probably want to change the way that writes happen. This is If you have customised the generated database, then you probably want to change the way that writes happen. This is
used by Versioned to get an entry written in ClassName_versions whenever an insert/update happens. used by `[api:Versioned]` to get an entry written in ClassName_versions whenever an insert/update happens.
To do this, define the **augmentWrite(&$manipulation)** method. This method is passed a manipulation array representing To do this, define the **augmentWrite(&$manipulation)** method. This method is passed a manipulation array representing
the write about to happen, and is able to amend this as desired, since it is passed by reference. the write about to happen, and is able to amend this as desired, since it is passed by reference.
@ -125,17 +125,17 @@ be modified as needed by your method. Instead of a manipulation array, we have
### Additional methods ### Additional methods
The other thing you may want to do with a decorator is provide a method that can be called on the DataObject that is The other thing you may want to do with a decorator is provide a method that can be called on the `[api:DataObject]` that is
being decorated. For instance, you may add a publish() method to every DataObject that is decorated with Versioned. being decorated. For instance, you may add a publish() method to every `[api:DataObject]` that is decorated with `[api:Versioned]`.
This is as simple as defining a method called publish() on your decorator. Bear in mind, however, that instead of This is as simple as defining a method called publish() on your decorator. Bear in mind, however, that instead of
$this, you should be referring to $this->owner. $this, you should be referring to $this->owner.
* $this = The DataObjectDecorator object. * $this = The `[api:DataObjectDecorator]` object.
* $this->owner = The related DataObject object. * $this->owner = The related `[api:DataObject]` object.
If you want to add your own internal properties, you can add this to the DataObjectDecorator, and these will be referred If you want to add your own internal properties, you can add this to the `[api:DataObjectDecorator]`, and these will be referred
to as $this->propertyName. Every DataObject has an associated DataObjectDecorator instance for each class that it is to as `$this->propertyName`. Every `[api:DataObject]` has an associated `[api:DataObjectDecorator]` instance for each class that it is
decorated by. decorated by.
:::php :::php

View File

@ -6,7 +6,7 @@ This class represents a set of `[api:DataObject]`s, such as the results of a que
[datamodel](/topics/datamodel)-related querying. It implements the [Iterator [datamodel](/topics/datamodel)-related querying. It implements the [Iterator
interface](http://php.net/manual/en/language.oop5.iterations.php) introduced in PHP5. interface](http://php.net/manual/en/language.oop5.iterations.php) introduced in PHP5.
Relations (`has_many`/`many_many`) are described in `[api:ComponentSet]`, a subclass of DataObjectSet. Relations (`has_many`/`many_many`) are described in `[api:ComponentSet]`, a subclass of `[api:DataObjectSet]`.
## Usage ## Usage
@ -56,7 +56,7 @@ This works on the object itself, so do NOT do something like this:
:::php :::php
$sortedSet = $mySet->sort('Lastname'); //ascending $sortedSet = $mySet->sort('Lastname'); //ascending
## Merge with other DataObjectSets ## Merge with other `[api:DataObjectSet]`s
:::php :::php
$myFirstSet->merge($mySecondSet); $myFirstSet->merge($mySecondSet);
@ -88,7 +88,7 @@ It is good practice to check for empty sets before doing any iteration.
### Paging ### Paging
DataObjects have native support for dealing with **pagination**. `[api:DataObject]`s have native support for dealing with **pagination**.
See *setPageLimits*, *setPageLength*, etc. See *setPageLimits*, *setPageLength*, etc.
FIXME Complete pagination documentation FIXME Complete pagination documentation

View File

@ -2,7 +2,7 @@
## Introduction ## Introduction
Director is the first step in the "execution pipeline". It parses the URL, matching it to one of a number of patterns, `[api:Director]` is the first step in the "execution pipeline". It parses the URL, matching it to one of a number of patterns,
and determines the controller, action and any argument to be used. It then runs the controller, which will finally run and determines the controller, action and any argument to be used. It then runs the controller, which will finally run
the viewer and/or perform processing steps. the viewer and/or perform processing steps.
@ -12,7 +12,7 @@ the viewer and/or perform processing steps.
## Redirection ## Redirection
The Director class has a number of methods to facilitate 301 and 302 HTTP redirection. The `[api:Director]` class has a number of methods to facilitate 301 and 302 HTTP redirection.
* **Director::redirect("action-name")**: If there's no slash in the URL passed to redirect, then it is assumed that you * **Director::redirect("action-name")**: If there's no slash in the URL passed to redirect, then it is assumed that you
want to go to a different action on the current controller. want to go to a different action on the current controller.
@ -33,8 +33,8 @@ redirectBack().
You can influence the way URLs are resolved one of 2 ways You can influence the way URLs are resolved one of 2 ways
1. Adding rules to Director in `<yourproject>/_config.php` (See Default Rewrite Rules below for examples) 1. Adding rules to `[api:Director]` in `<yourproject>/_config.php` (See Default Rewrite Rules below for examples)
2. Adding rules in your extended Controller class via the *$url_handlers* static variable 2. Adding rules in your extended `[api:Controller]` class via the *$url_handlers* static variable
See [controller](/topics/controller) for examples and explanations on how the rules get processed for both 1 and 2 above. See [controller](/topics/controller) for examples and explanations on how the rules get processed for both 1 and 2 above.
@ -56,7 +56,7 @@ SilverStripe comes with certain rewrite rules (e.g. for *admin/assets*).
## Links ## Links
* See ModelAsController class for details on controller/model-coupling * See `[api:ModelAsController]` class for details on controller/model-coupling
* See [execution-pipeline](/reference/execution-pipeline) for custom routing * See [execution-pipeline](/reference/execution-pipeline) for custom routing
## API Documentation ## API Documentation

View File

@ -53,7 +53,7 @@ All requests go through main.php, which sets up the environment and then hands c
**See:** The API documentation of `[api:Main]` for information about how main.php processes requests. **See:** The API documentation of `[api:Main]` for information about how main.php processes requests.
## Director and URL patterns ## Director and URL patterns
main.php relies on Director to work out which controller should handle this request. Director will instantiate that main.php relies on `[api:Director]` to work out which controller should handle this request. `[api:Director]` will instantiate that
controller object and then call `[api:Controller::run()]`. controller object and then call `[api:Controller::run()]`.
**See:** The API documentation of `[api:Director]` for information about how Director parses URLs and hands control over to a controller object. **See:** The API documentation of `[api:Director]` for information about how Director parses URLs and hands control over to a controller object.
@ -73,7 +73,7 @@ When you create a function, you can access the ID like this:
## Controllers and actions ## Controllers and actions
Controllers are the building blocks of your application. `[api:Controller]`s are the building blocks of your application.
**See:** The API documentation for `[api:Controller]` **See:** The API documentation for `[api:Controller]`

View File

@ -48,7 +48,7 @@ set {inlcudeDefaultJS} to false and work with behaviour.js.
* `[FileField](api:FileField)`: Simple file upload dialog. * `[FileField](api:FileField)`: Simple file upload dialog.
* `[FileIFrameField](api:FileIFrameField)`: File uploads through an iframe * `[FileIFrameField](api:FileIFrameField)`: File uploads through an iframe
* `[api:Image]`: Image upload through an iframe, with thumbnails and file-selection from existing assets * `[api:ImageField]`: Image upload through an iframe, with thumbnails and file-selection from existing assets
* `[SimpleImageField](api:SimpleImageField)`: SimpleImageField provides an easy way of uploading images to Image has_one * `[SimpleImageField](api:SimpleImageField)`: SimpleImageField provides an easy way of uploading images to Image has_one
relationships. Unlike ImageField, it doesn't use an iframe. relationships. Unlike ImageField, it doesn't use an iframe.

View File

@ -2,6 +2,7 @@
Reference articles complement our auto-generated [API docs](http://api.silverstripe.org) in providing deeper introduction into a specific API. Reference articles complement our auto-generated [API docs](http://api.silverstripe.org) in providing deeper introduction into a specific API.
* [Advanced templates](advanced-templates): Advanced SilverStripe template syntax
* [BBCode](bbcode): Extensible shortcode syntax * [BBCode](bbcode): Extensible shortcode syntax
* [Built-in page controls](built-in-page-controls): Explains the template syntax and available variables/placeholders * [Built-in page controls](built-in-page-controls): Explains the template syntax and available variables/placeholders
* [ComplexTableField](complextablefield): Manage records and their relations inside the CMS * [ComplexTableField](complextablefield): Manage records and their relations inside the CMS
@ -29,7 +30,6 @@ Reference articles complement our auto-generated [API docs](http://api.silverstr
* [StaticPublisher](staticpublisher): Export a page tree as static HTML for better performance and portability * [StaticPublisher](staticpublisher): Export a page tree as static HTML for better performance and portability
* [TableField](tablefield): Add and edit records with inline edits in this form field * [TableField](tablefield): Add and edit records with inline edits in this form field
* [TableListField](tablelistfield): View and delete records in the CMS * [TableListField](tablelistfield): View and delete records in the CMS
* [Templates](templates): Process our data for output in HTML and other formats
* [Typography](typography): CSS file to enable WYSIWYG previews in the CMS * [Typography](typography): CSS file to enable WYSIWYG previews in the CMS
* [urlvariabletools](urlvariabletools): Debug and maintenance switches * [urlvariabletools](urlvariabletools): Debug and maintenance switches
* [Versioned](versioned): Extension for SiteTree and other classes to store old versions and provide "staging" * [Versioned](versioned): Extension for SiteTree and other classes to store old versions and provide "staging"

View File

@ -2,11 +2,11 @@
## Introduction ## Introduction
The Member class is used to represent user accounts on a SilverStripe site (including newsletter recipients). The `[api:Member]` class is used to represent user accounts on a SilverStripe site (including newsletter recipients).
## Testing For Logged In Users ## Testing For Logged In Users
The Member class comes with 2 static methods for getting information about the current logged in user. The `[api:Member]` class comes with 2 static methods for getting information about the current logged in user.
**Member::currentUserID()** **Member::currentUserID()**
@ -38,10 +38,11 @@ Returns the full *Member* Object for the current user, returns *null* if user is
## Subclassing ## Subclassing
<div class="warning" markdown="1"> <div class="warning" markdown="1">
This is the least desirable way of extending the Member class. It's better to use DataObjectDecorator (see below). This is the least desirable way of extending the `[api:Member]` class. It's better to use `[api:DataObjectDecorator]`
(see below).
</div> </div>
You can defined subclasses of member to add extra fields or functionality to the built-in membership system. You can defined subclasses of `[api:Member]` to add extra fields or functionality to the built-in membership system.
:::php :::php
class MyMember extends Member { class MyMember extends Member {
@ -63,8 +64,8 @@ Note that if you want to look this class-name up, you can call Object::getCustom
## Overloading getCMSFields() ## Overloading getCMSFields()
If you overload the built-in function getCMSFields(), then you can change the form that is used to view & edit member If you overload the built-in function getCMSFields(), then you can change the form that is used to view & edit member
details in the newsletter system. This function returns a FieldSet object. You should generally start by calling details in the newsletter system. This function returns a `[api:FieldSet]` object. You should generally start by calling
parent::getCMSFields() and manipulate the FieldSet from there. parent::getCMSFields() and manipulate the `[api:FieldSet]` from there.
:::php :::php
function getCMSFields() { function getCMSFields() {
@ -78,11 +79,11 @@ parent::getCMSFields() and manipulate the FieldSet from there.
## Extending Member or DataObject? ## Extending Member or DataObject?
Basic rule: Class "Member" should just be extended for entities who have some kind of login. Basic rule: Class `[api:Member]` should just be extended for entities who have some kind of login.
If you have different types of Members in the system, you have to make sure that those with login-capabilities have If you have different types of `[api:Member]`s in the system, you have to make sure that those with login-capabilities have
unique email-addresses (as this is used for login-credentials). unique email-addresses (as this is used for login-credentials).
For persons without login-capabilities (e.g. for an address-database), you shouldn't extend member to avoid conflicts For persons without login-capabilities (e.g. for an address-database), you shouldn't extend `[api:Member]` to avoid conflicts
with the Member-database. This enables us to have a different subclass of Member for an email-address with login-data, with the Member-database. This enables us to have a different subclass of `[api:Member]` for an email-address with login-data,
and another subclass for the same email-address in the address-database. and another subclass for the same email-address in the address-database.
## Member Role Decorator ## Member Role Decorator
@ -95,7 +96,7 @@ class. A better way is to use role decorators to add this behaviour.
// OR // OR
Member::add_role('ForumRole'); Member::add_role('ForumRole');
A role decorator is simply a subclass of `[api:DataObjectDecorator]` that is designed to be used to add behaviour to Member. A role decorator is simply a subclass of `[api:DataObjectDecorator]` that is designed to be used to add behaviour to `[api:Member]`.
The roles affect the entire class - all members will get the additional behaviour. However, if you want to restrict The roles affect the entire class - all members will get the additional behaviour. However, if you want to restrict
things, you should add appropriate `[api:Permission::checkMember()]` calls to the role's methods. things, you should add appropriate `[api:Permission::checkMember()]` calls to the role's methods.

View File

@ -104,7 +104,7 @@ fields are searched (e.g. "partial match", "fulltext", etc.) using `$searchable_
## Summary Fields ## Summary Fields
Summary Fields are the columns which are shown in the `[api:TableListField]` when viewing DataObjects. These can be Summary Fields are the columns which are shown in the `[api:TableListField]` when viewing DataObjects. These can be
customized for each DataObject's search results using `$summary_fields`. customized for each `[api:DataObject]`'s search results using `$summary_fields`.
* See `[api:DataObject]` * See `[api:DataObject]`

View File

@ -55,7 +55,7 @@ From a block that shows a summary of the page edits if administrator, nothing if
Often you want to invalidate a cache when any in a set of objects change, or when the objects in a relationship change. Often you want to invalidate a cache when any in a set of objects change, or when the objects in a relationship change.
To help do this, SilverStripe 2.4 also introduces the concept of Aggregates. These calculate and return SQL aggregates To help do this, SilverStripe 2.4 also introduces the concept of Aggregates. These calculate and return SQL aggregates
on sets of DataObjects - the most useful for us being the Max aggregate. on sets of `[api:DataObject]`s - the most useful for us being the Max aggregate.
For example, if we have a menu, we want that menu to update whenever _any_ page is edited, but would like to cache it For example, if we have a menu, we want that menu to update whenever _any_ page is edited, but would like to cache it
otherwise. By using aggregates, that's easy otherwise. By using aggregates, that's easy
@ -237,5 +237,3 @@ Can be re-written as:
<% end_cached %> <% end_cached %>
<% end_cached %> <% end_cached %>

View File

@ -12,7 +12,7 @@ and then select the permissions tab, and add that permission to the list.
The simple usage, Permission::check("PERM_CODE") will detect if the currently logged in member has the given permission. The simple usage, Permission::check("PERM_CODE") will detect if the currently logged in member has the given permission.
See the API docs for more options. See the API docs for more options.
** Group ACLs ** **Group ACLs**
* Call **Permission::check("MY_PERMISSION_CODE")** to see if the current user has MY_PERMISSION_CODE. * Call **Permission::check("MY_PERMISSION_CODE")** to see if the current user has MY_PERMISSION_CODE.
* MY_PERMISSION_CODE can be loaded into the Security admin on the appropriate group, using the "Permissions" tab. * MY_PERMISSION_CODE can be loaded into the Security admin on the appropriate group, using the "Permissions" tab.
@ -22,9 +22,9 @@ You can use whatever codes you like, but for the sanity of developers and users,
## PermissionProvider ## PermissionProvider
PermissionProvider is an interface which lets you define a method *providePermissions()*. This method should return a `[api:PermissionProvider]` is an interface which lets you define a method *providePermissions()*. This method should return a
map of permission code names with a human readable explanation of its purpose (see map of permission code names with a human readable explanation of its purpose (see
[:permission:codes](/reference/permission)). [permissions:codes](/reference/permission)).
:::php :::php
class Page_Controller implements PermissionProvider { class Page_Controller implements PermissionProvider {

View File

@ -133,8 +133,7 @@ In your controller's init() function, add:
## CMS Requirements ## CMS Requirements
The Silverstripe core includes a lot of Requirements by itself. Most of these are collated in `[api:LeftAndMain]`// The SilverStripe core includes a lot of Requirements by itself. Most of these are collated in `[api:LeftAndMain]` first.
first.
## Motivation ## Motivation

View File

@ -2,12 +2,13 @@
## Introduction ## Introduction
RestfulService enables connecting to remote web services which supports REST interface and consume those web services `[api:RestfulService]` enables connecting to remote web services which supports REST interface and consume those web services
(for example Flickr, Youtube, Amazon and etc). RestfulService can parse the XML response (sorry no JSON support) (for example [Flickr](http://www.flickr.com/services/api/), [Youtube](http://code.google.com/apis/youtube/overview.html), Amazon and etc). `[api:RestfulService]` can parse the XML response (sorry no JSON support)
returned from the web service. Further it supports caching of the response, and you can customize the cache interval. returned from the web service. Further it supports caching of the response, and you can customize the cache interval.
To gain the functionality you can either create a new RestfulService object or create a class extending the To gain the functionality you can either create a new `[api:RestfulService]` object or create a class extending the
RestfulService (see `flickrservice` and `youtubeservice` modules). RestfulService (see [flickrservice](http://silverstripe.org/flickr-module/) and
[youtubeservice](http://silverstripe.org/youtube-gallery-module/) modules).
## Examples ## Examples
@ -112,8 +113,8 @@ to get the value of entry node with the namespace media, use:
### Handling Errors ### Handling Errors
If the web service returned an error (for example, API key not available or inadequate parameters) RestfulService could If the web service returned an error (for example, API key not available or inadequate parameters) `[api:RestfulService]`
delgate the error handling to it's descendant class. To handle the errors define a function called errorCatch could delgate the error handling to it's descendant class. To handle the errors define a function called errorCatch
:::php :::php
/* /*
@ -142,7 +143,7 @@ If you want to bypass error handling on your sub-classes you could define that i
## Other Uses ## Other Uses
### How to use RestfulService to easily embed an RSS feed ### How to use `[api:RestfulService]` to easily embed an RSS feed
`[api:RestfulService]` can be used to easily embed an RSS feed (since it's also an xml response) from a site `[api:RestfulService]` can be used to easily embed an RSS feed (since it's also an xml response) from a site
such as del.icio.us such as del.icio.us

View File

@ -5,7 +5,7 @@
Generating RSS/Atom-feeds is just a matter of rendering a `[api:DataObject]` and the Page Comment Interface. Generating RSS/Atom-feeds is just a matter of rendering a `[api:DataObject]` and the Page Comment Interface.
Handled through the `[api:RSSFeed]` class. Handled through the `[api:RSSFeed]` class.
RSSFeed doesn't limit you to generating "article-based" feeds, it is just as easy to create a feed of your current `[api:RSSFeed]` doesn't limit you to generating "article-based" feeds, it is just as easy to create a feed of your current
staff-members. The only logical limitation here is that every item in the RSS-feed should be accessible through a URL on staff-members. The only logical limitation here is that every item in the RSS-feed should be accessible through a URL on
your website, so its advisable to just create feeds from subclasses of `[api:SiteTree]`. your website, so its advisable to just create feeds from subclasses of `[api:SiteTree]`.
@ -31,7 +31,7 @@ your website, so its advisable to just create feeds from subclasses of `[api:Sit
### Example of showing the 10 most recently updated pages ### Example of showing the 10 most recently updated pages
You can use RSSFeed to easily create a feed showing your latest Page updates. Just change mysite/code/Page.php to You can use `[api:RSSFeed]` to easily create a feed showing your latest Page updates. Just change mysite/code/Page.php to
something like this: something like this:
:::php :::php
@ -72,7 +72,7 @@ can also do http://www.yoursite.com/PageComment/rss?pageid=46 where pageid is th
## External Sources ## External Sources
RSSFeed only creates feeds from your own data. We've included the [SimplePie](http://simplepie.org) RSS-parser for `[api:RSSFeed]` only creates feeds from your own data. We've included the [SimplePie](http://simplepie.org) RSS-parser for
accessing feeds from external sources. accessing feeds from external sources.

View File

@ -3,16 +3,16 @@
## Introduction ## Introduction
Manages searching of properties on one or more `[api:DataObject]` types, based on a given set of input parameters. Manages searching of properties on one or more `[api:DataObject]` types, based on a given set of input parameters.
SearchContext is intentionally decoupled from any controller-logic, `[api:SearchContext]` is intentionally decoupled from any controller-logic,
it just receives a set of search parameters and an object class it acts on. it just receives a set of search parameters and an object class it acts on.
The default output of a SearchContext is either a `[api:SQLQuery]` object for further refinement, or a The default output of a `[api:SearchContext]` is either a `[api:SQLQuery]` object for further refinement, or a
`[api:DataObject]` instance. `[api:DataObject]` instance.
In case you need multiple contexts, consider namespacing your request parameters by using `FieldSet->namespace()` on In case you need multiple contexts, consider namespacing your request parameters by using `FieldSet->namespace()` on
the $fields constructor parameter. the $fields constructor parameter.
SearchContext is mainly used by `[api:ModelAdmin]`, our generic data administration interface. Another `[api:SearchContext]` is mainly used by `[api:ModelAdmin]`, our generic data administration interface. Another
implementation can be found in generic frontend search forms through the [genericviews](http://silverstripe.org/generic-views-module) module. implementation can be found in generic frontend search forms through the [genericviews](http://silverstripe.org/generic-views-module) module.
## Requirements ## Requirements
@ -33,9 +33,10 @@ See `[api:DataObject::$searchable_fields]`.
### Customizing fields and filters ### Customizing fields and filters
In this example we're defining three attributes on our MyDataObject subclasss: `PublicProperty`, `HiddenProperty` In this example we're defining three attributes on our MyDataObject subclass: `PublicProperty`, `HiddenProperty`
and `MyDate`. The attribute `HiddenProperty` should not be searchable, and `MyDate` should only search for dates and `MyDate`. The attribute `HiddenProperty` should not be searchable, and `MyDate` should only search for dates
*after* the search entry (with a `GreaterThanFilter`). Similiar to the built-in `DataObject->getDefaultSearchContext()` method, we're building our own `getCustomSearchContext()` variant. *after* the search entry (with a `GreaterThanFilter`). Similiar to the built-in `DataObject->getDefaultSearchContext()`
method, we're building our own `getCustomSearchContext()` variant.
:::php :::php
class MyDataObject extends DataObject { class MyDataObject extends DataObject {
@ -86,11 +87,6 @@ and `MyDate`. The attribute `HiddenProperty` should not be searchable, and `MyDa
} }
### Pagination ### Pagination
For paginating records on multiple pages, you need to get the generated `SQLQuery` before firing off the actual For paginating records on multiple pages, you need to get the generated `SQLQuery` before firing off the actual
@ -184,7 +180,7 @@ Results.PaginationSummary(4) defines how many pages the search will show in the
## Available SearchFilters ## Available SearchFilters
See SearchFilter API Documentation `[api:SearchFilter]` See `[api:SearchFilter]` API Documentation
## API Documentation ## API Documentation
`[api:SearchContext]` `[api:SearchContext]`

View File

@ -6,23 +6,23 @@ the site reports by clicking "Site Reports" in the left hand side bar and select
![](_images/sitereport.png) ![](_images/sitereport.png)
By default the CMS ships with a couple basic reports - By default the CMS ships with a couple of basic reports -
## Default Reports ## Default Reports
* "Empty Pages" which will generate a list of pages without content * "Empty Pages" which will generate a list of pages without content
* "Pages edited in the last 2 weeks" which will list all the pages edited in the last 2 weeks in order of most recently * "Pages edited in the last 2 weeks" which will list all the pages edited in the last 2 weeks in order of most recently
edited. edited.
* "To Do" which displays all the ToDo notes you have added to each page and a link to the page. This is in 2.2.2 and * "To Do" which displays all the ToDo notes you have added to each page and a link to the page. Note: This is in 2.2.2 and
later later
* Also the Ecommerce module provides 2 or 3 reports out of box. Such as All Products, Orders... * Also the Ecommerce module provides 2 or 3 reports out of box. Such as All Products, Orders...
## Creating Custom Reports ## Creating Custom Reports
You can create reports for you own data quickly and easily. A general knowledge of Silverstripe's You can create reports for you own data quickly and easily. A general knowledge of SilverStripe's
[Datamodel](/topics/datamodel) would help before you attempt this. [Datamodel](/topics/datamodel) would help before you attempt this.
Inside the Mysite/Code folder - your projects code, create a file called CustomSideReport or MyProjectSiteReport and Inside the Mysite/Code folder - your projects code, create a file called `CustomSideReport` or `MyProjectSiteReport` and
inside this file we can add our site reports. inside this file we can add our site reports.
CustomSideReport.php CustomSideReport.php
@ -92,5 +92,3 @@ file and add class's as you need them inside that for each report.
## API Documentation ## API Documentation
`[api:ReportAdmin]` `[api:ReportAdmin]`

View File

@ -2,12 +2,12 @@
## Introduction ## Introduction
The SiteConfig panel was introduced in 2.4 for providing a generic interface for managing site wide settings or The `[api:SiteConfig]` panel was introduced in 2.4 for providing a generic interface for managing site wide settings or
functionality which is used throughout the site. Out of the box it provides 2 fields 'Site Name' and 'Site Tagline'. functionality which is used throughout the site. Out of the box it provides 2 fields 'Site Name' and 'Site Tagline'.
## Accessing SiteConfig Options ## Accessing `[api:SiteConfig]` Options
You can access SiteConfig options from any SS template by using the function $SiteConfig.FieldName You can access `[api:SiteConfig]` options from any SS template by using the function $SiteConfig.FieldName
:::ss :::ss
$SiteConfig.Title $SiteConfig.Title
@ -28,8 +28,7 @@ Or if you want to access variables in the PHP you can do
$config->Title $config->Title
## Extending `[api:SiteConfig]`
## Extending SiteConfig
To extend the options available in the panel you can define your own fields via an Extension. To extend the options available in the panel you can define your own fields via an Extension.
@ -59,12 +58,12 @@ Then add a link to your extension in the _config.php file like below.
Object::add_extension('SiteConfig', 'CustomSiteConfig'); Object::add_extension('SiteConfig', 'CustomSiteConfig');
This tells SilverStripe to add the CustomSiteConfig extension to the SiteConfig class. This tells SilverStripe to add the CustomSiteConfig extension to the `[api:SiteConfig]` class.
After adding those two pieces of code, rebuild your database by visiting http://yoursite.com/dev/build and then reload After adding those two pieces of code, rebuild your database by visiting http://yoursite.com/dev/build and then reload
the admin interface. You may need to reload it with a ?flush=1 on the end. the admin interface. You may need to reload it with a ?flush=1 on the end.
You can define as many extensions for SiteConfig as you need. For example if you are developing a module you can define You can define as many extensions for `[api:SiteConfig]` as you need. For example if you are developing a module you can define
your own global settings for the dashboard. your own global settings for the dashboard.
## API Documentation ## API Documentation

View File

@ -5,7 +5,7 @@
An object representing a SQL query. It is easier to deal with object-wrappers than string-parsing a raw SQL-query. This An object representing a SQL query. It is easier to deal with object-wrappers than string-parsing a raw SQL-query. This
object is used by `[api:DataObject]`, though... object is used by `[api:DataObject]`, though...
A word of caution: Dealing with low-level SQL is not encouraged in the Silverstripe [datamodel](/topics/datamodel) for various A word of caution: Dealing with low-level SQL is not encouraged in the SilverStripe [datamodel](/topics/datamodel) for various
reasons. You'll break the behaviour of: reasons. You'll break the behaviour of:
* Custom getters/setters * Custom getters/setters
@ -15,7 +15,7 @@ reasons. You'll break the behaviour of:
* `[api:DataObject]` * `[api:DataObject]`
* Database abstraction * Database abstraction
We'll explain some ways to use *SELECT* with the full power of SQL, but still maintain a connection to the Silverstripe We'll explain some ways to use *SELECT* with the full power of SQL, but still maintain a connection to the SilverStripe
[datamodel](/topics/datamodel). [datamodel](/topics/datamodel).
## Usage ## Usage
@ -109,7 +109,7 @@ Useful for creating dropdowns.
### "Raw" SQL with DB::query() ### "Raw" SQL with DB::query()
This is not recommended for most cases, but you can also use the Silverstripe database-layer to fire off a raw query: This is not recommended for most cases, but you can also use the SilverStripe database-layer to fire off a raw query:
:::php :::php
DB::query("UPDATE Player SET Status='Active'"); DB::query("UPDATE Player SET Status='Active'");
@ -142,10 +142,10 @@ This form of building a query has the following advantages:
* Selection of *ID*, *ClassName*, *RecordClassName*, which are necessary to use *buildDataObjectSet* later on * Selection of *ID*, *ClassName*, *RecordClassName*, which are necessary to use *buildDataObjectSet* later on
* Filtering records for correct *ClassName* * Filtering records for correct *ClassName*
### Transforming a result to DataObjectSet ### Transforming a result to `[api:DataObjectSet]`
This is a commonly used technique inside Silverstripe: Use raw SQL, but transfer the resulting rows back into This is a commonly used technique inside SilverStripe: Use raw SQL, but transfer the resulting rows back into
DataObjects. `[api:DataObject]`s.
:::php :::php
$sqlQuery = new SQLQuery(); $sqlQuery = new SQLQuery();
@ -183,16 +183,16 @@ DataObjects.
var_dump($myFirstPlayer->Status); // undefined, as we didn't LEFT JOIN the BasePlayer-table var_dump($myFirstPlayer->Status); // undefined, as we didn't LEFT JOIN the BasePlayer-table
CAUTION: Depending on the selected columns in your query, you might get into one of the following scenarios: **CAUTION:** Depending on the selected columns in your query, you might get into one of the following scenarios:
* Not all object-properties accessible: You need to take care of selecting the right stuff yourself * Not all object-properties accessible: You need to take care of selecting the right stuff yourself
* Overlayed object-properties: If you *LEFT JOIN* a table which also has a column 'Birthdate' and do a global select on * Overlayed object-properties: If you *LEFT JOIN* a table which also has a column 'Birthdate' and do a global select on
this table, you might not be able to access original object-properties. this table, you might not be able to access original object-properties.
* You can't create DataObjects where no scalar record-data is available, e.g. when using *GROUP BY* * You can't create `[api:DataObject]`s where no scalar record-data is available, e.g. when using *GROUP BY*
* Naming conflicts with custom getters: A getter like Player->getName() will overlay the column-data selected in the * Naming conflicts with custom getters: A getter like Player->getName() will overlay the column-data selected in the
above example above example
Be careful when saving back DataObjects created through *buildDataObjectSet*, you might get strange side-effects due to Be careful when saving back `[api:DataObject]`s created through *buildDataObjectSet*, you might get strange side-effects due to
the issues noted above. the issues noted above.
## Using FormFields with custom SQL ## Using FormFields with custom SQL

View File

@ -2,9 +2,8 @@
## Introduction ## Introduction
TableField behaves in the same manner as `[api:TableListField]`, however allows the editing of existing and adding of `[api:TableField]` behaves in the same manner as `[api:TableListField]`, however allows the editing of existing and adding of
new rows. new rows. The data is saved back by the surrounding form-saving (mostly EditForm->save).
The data is saved back by the surrounding form-saving (mostly EditForm->save).
See `[api:TableListField]` for more documentation on the base-class See `[api:TableListField]` for more documentation on the base-class
@ -66,15 +65,15 @@ Note: You still have to attach some form of `[api:Validator]` to the form to tri
### Nested Table Fields ### Nested Table Fields
When you have TableField inside a `[api:ComplexTableField]`, the parent ID may not be known in your When you have `[api:TableField]` inside a `[api:ComplexTableField]`, the parent ID may not be known in your
getCMSFields() method. In these cases, you can set a value to '$RecordID' in your TableField extra data, and this will getCMSFields() method. In these cases, you can set a value to '$RecordID' in your `[api:TableField]` extra data, and this
be populated with the newly created record id upon save. will be populated with the newly created record id upon save.
## Known Issues ## Known Issues
* A TableField doesn't reload any submitted form-data if the saving is interrupted by a failed validation. After * A `[api:TableField]` doesn't reload any submitted form-data if the saving is interrupted by a failed validation. After
refreshing the form with the validation-errors, the TableField will be blank again. refreshing the form with the validation-errors, the `[api:TableField]` will be blank again.
* You can't add **visible default data** to columns in a TableField, please use *setExtraData* * You can't add **visible default data** to columns in a `[api:TableField]`, please use *setExtraData*
## API Documentation ## API Documentation

View File

@ -8,9 +8,9 @@ Provides customizeable columns, record-deletion by ajax, paging, sorting, CSV-ex
## Example ## Example
Here's an example of a full featured TableListField implementation. It features editing members in the database directly Here's an example of a full featured `[api:TableListField]` implementation. It features editing members in the database
as a button on each record, as well as filtering, and sorting. It also makes use of the 'export' permission, allowing directly as a button on each record, as well as filtering, and sorting. It also makes use of the 'export' permission,
export of data as a CSV. allowing export of data as a CSV.
:::php :::php
function getReportField() { function getReportField() {
@ -90,7 +90,7 @@ For more information on each of the features used in the example, you can read b
$customCsvQuery->select[] = "CONCAT(col1,col2) AS MyCustomSQLColumn"; $customCsvQuery->select[] = "CONCAT(col1,col2) AS MyCustomSQLColumn";
$myTableListField->setCustomCsvQuery($customQuery); $myTableListField->setCustomCsvQuery($customQuery);
TableListField also tries to resolve Component-relations(has_one, has_many) and custom getters automatically: `[api:TableListField]` also tries to resolve Component-relations(has_one, has_many) and custom getters automatically:
:::php :::php
$myTableListField = new TableListField( $myTableListField = new TableListField(
@ -139,7 +139,7 @@ Example (sorting by "FirstName" column):
); );
If you want to sort by custom getters in your DataObject, please reformulate them to a custom SQL column. This If you want to sort by custom getters in your `[api:DataObject]`, please reformulate them to a custom SQL column. This
restriction is needed to avoid performance-hits by caching and sorting potentially large datasets on PHP-level. restriction is needed to avoid performance-hits by caching and sorting potentially large datasets on PHP-level.
### Casting ### Casting
@ -155,7 +155,7 @@ Column-values can be casted, based on the casting-types available through DBObje
### Permissions ### Permissions
Permissions vary in different TableListField-implementations, and are evaluated in the template. Permissions vary in different `[api:TableListField]`-implementations, and are evaluated in the template.
By default, all listed permissions are enabled. By default, all listed permissions are enabled.
:::php :::php
@ -237,7 +237,7 @@ implement averages etc.
) )
); );
In TableField-implementation, these summaries also react to changes in input-fields by javascript. In `[api:TableListField]`-implementation, these summaries also react to changes in input-fields by javascript.
Available methods: Available methods:
* sum * sum
@ -245,7 +245,7 @@ Available methods:
### Grouping ### Grouping
Used to group by a specific column in the DataObject and create partial summaries. Used to group by a specific column in the `[api:DataObject]` and create partial summaries.
Please use only together with addSummary(). Please use only together with addSummary().
(Automatically disables sorting). (Automatically disables sorting).
@ -257,7 +257,7 @@ Please use only together with addSummary().
### Custom Sorting ### Custom Sorting
Please subclass TableListField to implement custom sorting, following the naming-convention Please subclass `[api:TableListField]` to implement custom sorting, following the naming-convention
"`colFunction_<yourFunctionName>`". "`colFunction_<yourFunctionName>`".
:::php :::php
@ -274,7 +274,7 @@ Please subclass TableListField to implement custom sorting, following the naming
In case you want to perform utility-functions like "export" or "print" through action-buttons, In case you want to perform utility-functions like "export" or "print" through action-buttons,
make sure to subclass Utility() which collates all possible actions. make sure to subclass Utility() which collates all possible actions.
### Customizing Look&Feel ### Customizing Look & Feel
You can exchange the used template, e.g. to change applied CSS-classes or the HTML-markup: You can exchange the used template, e.g. to change applied CSS-classes or the HTML-markup:
@ -285,4 +285,4 @@ You can exchange the used template, e.g. to change applied CSS-classes or the HT
## API Documentation ## API Documentation
[api:TableListField] `[api:TableListField]`

View File

@ -4,8 +4,7 @@
SilverStripe lets you customise the style of content in the CMS. SilverStripe lets you customise the style of content in the CMS.
## Usage ## Usage
This is done by setting up a CSS file called This is done by setting up a CSS file called (projectname)/css/typography.css.
(projectname)/css/typography.css
You also need to create a file called (projectname)/css/editor.css with the following content: You also need to create a file called (projectname)/css/editor.css with the following content:
@ -40,8 +39,8 @@ It's important to realise that this CSS file is included directly into the CMS s
can alter the styling of other parts of the interface. While this is novel, it can be dangerous and is probably not can alter the styling of other parts of the interface. While this is novel, it can be dangerous and is probably not
what you're after. what you're after.
The way around this is to limit all your styling selectors to elements inside something with class="typography". The The way around this is to limit all your styling selectors to elements inside something with `class="typography"`. The
other half of this is to put class="typography" onto any area in your template where you would like the styling to be other half of this is to put `class="typography"` onto any area in your template where you would like the styling to be
applied. applied.
**WRONG** **WRONG**
@ -73,7 +72,7 @@ applied.
If you would to include different styles for different sections of your site, you can use class names the same as the If you would to include different styles for different sections of your site, you can use class names the same as the
name of the data fields. This example sets up different paragraph styles for 2 HTML editor fieldsc alled Content and name of the data fields. This example sets up different paragraph styles for 2 HTML editor fields called Content and
OtherContent: OtherContent:
:::css :::css
@ -89,7 +88,7 @@ OtherContent:
### Removing the typography class ### Removing the typography class
Sometimes, it's not enough to add a class, you also want to remove the typography class. You can use the Sometimes, it's not enough to add a class, you also want to remove the typography class. You can use the
HTMLEditorField method setCSSClass. `[api:HTMLEditorField]` method setCSSClass.
This example sets another CSS class typographybis: This example sets another CSS class typographybis:
@ -104,5 +103,4 @@ This example sets another CSS class typographybis:
} }
This functionality will be available in the version 2.0.2 of the CMS. **Note:** This functionality will be available in the version 2.0.2 of the CMS.

View File

@ -3,7 +3,7 @@
## Introduction ## Introduction
This page lists a number of "page options" , "rendering tools" or "special URL variables" that you can use to debug your This page lists a number of "page options" , "rendering tools" or "special URL variables" that you can use to debug your
sapphire applications. These are consumed in PHP using the $_REQUEST or $_GET superglobals throughout the Sapphire sapphire applications. These are consumed in PHP using the $_REQUEST or $_GET super globals throughout the Sapphire
core. core.
**General Usage** **General Usage**
@ -18,8 +18,8 @@ Append the option and corresponding value to your URL in your browser's address
| URL Variable | | Values | | Description | | URL Variable | | Values | | Description |
| ------------ | | ------ | | ----------- | | ------------ | | ------ | | ----------- |
| flush | | 1,all | | This will clear out all cached information about the page. This is used frequently during development - for example, when adding new PHP or SS files. See below for value descriptions. | | flush | | 1,all | | This will clear out all cached information about the page. This is used frequently during development - for example, when adding new PHP or SS files. See below for value descriptions. |
| showtemplate | | 1 | | Show the compiled version of all the templates used, including line numbers. Good when you have a syntax error in a template. Cannot be used on a Live site without **isDev** **flush** can be used with the following values: | | showtemplate | | 1 | | Show the compiled version of all the templates used, including line numbers. Good when you have a syntax error in a template. Cannot be used on a Live site without **isDev**. **flush** can be used with the following values: |
| ?flush=1 | | | | | Flushes the current page and included templates | | ?flush=1 | | | | Flushes the current page and included templates |
| ?flush=all | | | | Flushes the entire template cache | | ?flush=all | | | | Flushes the entire template cache |
## General Testing ## General Testing
@ -29,13 +29,13 @@ Append the option and corresponding value to your URL in your browser's address
| isDev | | 1 | | Put the site into [development mode](/topics/debugging), enabling debugging messages to the browser on a live server. For security, you'll be asked to log in with an administrator log-in | | isDev | | 1 | | Put the site into [development mode](/topics/debugging), enabling debugging messages to the browser on a live server. For security, you'll be asked to log in with an administrator log-in |
| isTest | | 1 | | Put the site into [test mode](/topics/debugging), enabling debugging messages to the admin email and generic errors to the browser on a live server | | isTest | | 1 | | Put the site into [test mode](/topics/debugging), enabling debugging messages to the admin email and generic errors to the browser on a live server |
| debug | | 1 | | Show a collection of debugging information about the director / controller operation | | debug | | 1 | | Show a collection of debugging information about the director / controller operation |
| debug_request | | 1 | | Show all steps of the request from initial HTTPRequest to Controller to Template Rendering | | debug_request | | 1 | | Show all steps of the request from initial `[api:HTTPRequest]` to `[api:Controller]` to Template Rendering |
## Classes and Objects ## Classes and Objects
| URL Variable | | Values | | Description | | URL Variable | | Values | | Description |
| ------------ | | ------ | | ----------- | | ------------ | | ------ | | ----------- |
| debugmanifest | | 1 | | Show the entire Sapphire manifest as currently built (Use /dev/build to rebuild) | | debugmanifest | | 1 | | Show the entire Sapphire manifest as currently built (Use `/dev/build` to rebuild) |
| usetestmanifest | | 1 | | Force use of the default test manifest | | usetestmanifest | | 1 | | Force use of the default test manifest |
| debugmethods | | 1 | | Shows all methods available when an object is constructed (useful when extending classes or using object decorators) | | debugmethods | | 1 | | Shows all methods available when an object is constructed (useful when extending classes or using object decorators) |
| debugfailover | | 1 | | Shows failover methods from classes extended | | debugfailover | | 1 | | Shows failover methods from classes extended |
@ -79,9 +79,9 @@ Redirections](security#redirect_back_to_another_page_after_login) for more infor
| Site URL | | Action | | Site URL | | Action |
| -------- | | ------ | | -------- | | ------ |
| %%http://yoursite.com%%**/dev/build** | | Rebuild the entire database and manifest, see below for additional URL Variables | | http://yoursite.com**/dev/build** | | Rebuild the entire database and manifest, see below for additional URL Variables |
| %%http://yoursite.com%%**/admin/publishall/** | | Publish all pages on the site | | http://yoursite.com**/admin/publishall/** | | Publish all pages on the site |
| %%http://yoursite.com%%**/anypage/images/flush** | | Creates new images for the page by deleting the resized ones and going back to the original to create new resized one | | http://yoursite.com**/anypage/images/flush** | | Creates new images for the page by deleting the resized ones and going back to the original to create new resized one |
### /dev/build ### /dev/build

View File

@ -29,5 +29,3 @@ whenever a blog entry has been published.
parent::onAfterPublish(); parent::onAfterPublish();
} }
} }

View File

@ -19,22 +19,26 @@ It is unclear how this works for data-objects that are not pages.
In the security tab you can make groups for security. The way this was intended was as follows (this may be a counter In the security tab you can make groups for security. The way this was intended was as follows (this may be a counter
intuitive): intuitive):
- employees
1. marketing * employees
- marketing executive * marketing
* marketing executive
Thus, the further up the hierarchy you go the MORE privileges you can get. Similarly, you could have: Thus, the further up the hierarchy you go the MORE privileges you can get. Similarly, you could have:
- members
1. coordinators * members
- admins * coordinators
* admins
Where members have some privileges, coordinators slightly more and administrators the most; having each group inheriting Where members have some privileges, coordinators slightly more and administrators the most; having each group inheriting
privileges from its parent group. privileges from its parent group.
## Permission checking is at class level ## Permission checking is at class level
SilverStripe provides a security mechanism via the *Permission::check* method (see *LeftAndMain.php* for examples on how SilverStripe provides a security mechanism via the *Permission::check* method (see `[api:LeftAndMain]` for examples on how
the admin screens work) the admin screens work)
(next step -- go from *Permission::checkMember*... (next step -- go from *Permission::checkMember*...)
### Nuts and bolts -- figuring it out ### Nuts and bolts -- figuring it out
@ -44,10 +48,10 @@ works.
### Loading the admin page: looking at security ### Loading the admin page: looking at security
If you go to [your site]/admin -- how does that work? If you go to [your site]/admin *Director.php* maps the 'admin' URL request through a `[api:Director]` rule to the
*Director.php* maps the 'admin' URL request through a *Director* rule to the CMSMain controller (see `[api:CMSMain]`, with no arguments. `[api:CMSMain]` controller (see `[api:CMSMain]`, with no arguments).
*CMSMain.init()* calls its parent which, of all things is called *LeftAndMain*. It's in *LeftAndMain* that the *CMSMain.init()* calls its parent which, of all things is called `[api:LeftAndMain]`. It's in `[api:LeftAndMain]` that the
important security checks are made by calling *Permission::check*. important security checks are made by calling *Permission::check*.
`[api:Security::permissionFailure]` is the next utility function you can use to redirect to the login form. `[api:Security::permissionFailure]` is the next utility function you can use to redirect to the login form.

View File

@ -1,9 +1,9 @@
# Common Configuration through _config.php # Common configuration through _config.php
## Introduction ## Introduction
Silverstripe doesn't have a global configuration-array or an interface with all available configuration-options. As all SilverStripe doesn't have a global configuration-array or an interface with all available configuration-options. As all
Silverstripe logic is contained in classes, the appropriate place to configure their behaviour is directly in the class SilverStripe logic is contained in classes, the appropriate place to configure their behaviour is directly in the class
itself. itself.
This lack of a configuration-GUI is on purpose, as we'd like to keep developer-level options where they belong (into This lack of a configuration-GUI is on purpose, as we'd like to keep developer-level options where they belong (into
@ -12,7 +12,7 @@ CMS"](http://www.silverstripe.com/core-team-discussion/flat/2723) for further re
In addition to these principle, some settings are In addition to these principle, some settings are
* Author-level configuration like interface language or date/time formats can be performed in the CMS "My Profile" section (`admin/myprofile`). * Author-level configuration like interface language or date/time formats can be performed in the CMS "My Profile" section (`admin/myprofile`).
* Group-related configuration like [api:HTMLEditorField] settings can be found in the "Security" section (`admin/security`). * Group-related configuration like `[api:HTMLEditorField]` settings can be found in the "Security" section (`admin/security`).
* Site-wide settings like page titles can be set (and extended) on the root tree element in the CMS "Content" section (through the [siteconfig](/reference/siteconfig) API). * Site-wide settings like page titles can be set (and extended) on the root tree element in the CMS "Content" section (through the [siteconfig](/reference/siteconfig) API).
## _ss_environment.php ## _ss_environment.php
@ -24,8 +24,9 @@ See [environment-management](/topics/environment-management).
This file is detected in each folder by `[api:ManifestBuilder]`. This way, every toplevel-folder (=module) This file is detected in each folder by `[api:ManifestBuilder]`. This way, every toplevel-folder (=module)
can have independent configuration-rules. can have independent configuration-rules.
//Please note that this is the only place where you can put in procedural code - all other functionality is wrapped in
classes (see [common-problems](/installation/common-problems)).// Please note that this is the only place where you can put in procedural code - all other functionality is wrapped in
classes (see [common-problems](/installation/common-problems)).
You can call most static methods from _config.php - classes will be loaded as required. Here's a list - **this is You can call most static methods from _config.php - classes will be loaded as required. Here's a list - **this is
incomplete - please add to it** *Try to keep it in alphabetical order too! :)* incomplete - please add to it** *Try to keep it in alphabetical order too! :)*

View File

@ -6,7 +6,7 @@ your SilverStripe site.
## Example ## Example
mysite/code/Controllers/FastFood.php `mysite/code/Controllers/FastFood.php`
:::php :::php
<?php <?php
@ -20,14 +20,14 @@ mysite/code/Controllers/FastFood.php
?> ?>
mysite/_config.php `mysite/_config.php`
:::php :::php
Director::addRules(50, array('fastfood/$Action/$ID/$Name' => 'FastFood_Controller')); Director::addRules(50, array('fastfood/$Action/$ID/$Name' => 'FastFood_Controller'));
Request for '/fastfood/order/24/cheesefries' would result in the following to the $arguments above. If needed, use Request for `/fastfood/order/24/cheesefries` would result in the following to the $arguments above. If needed, use
"?flush=1" on the end of request after making any code changes to your controller. `?flush=1` on the end of request after making any code changes to your controller.
:::ss :::ss
Array Array
@ -42,12 +42,12 @@ Request for '/fastfood/order/24/cheesefries' would result in the following to th
In the above example the URLs were configured using the `[api:Director]` rules in the **_config.php** file. In the above example the URLs were configured using the `[api:Director]` rules in the **_config.php** file.
Alternatively you can specify these in your Controller class via the **$url_handlers** static array (which gets Alternatively you can specify these in your Controller class via the **$url_handlers** static array (which gets
processed by the RequestHandler). processed by the `[api:RequestHandler]`).
This is useful when you want to subvert the fixed action mapping of 'fastfood/order/*' to the function **order**. In This is useful when you want to subvert the fixed action mapping of `fastfood/order/*` to the function **order**. In
the case below we also want any orders coming through '/fastfood/drivethrough/' to use the same order function. the case below we also want any orders coming through `/fastfood/drivethrough/` to use the same order function.
mysite/code/Controllers/FastFood.php `mysite/code/Controllers/FastFood.php`
:::php :::php
class FastFood_Controller extends Controller { class FastFood_Controller extends Controller {
@ -59,15 +59,15 @@ mysite/code/Controllers/FastFood.php
## URL Patterns ## URL Patterns
The RequestHandler class will parse all rules you specify against the following patterns. The `[api:RequestHandler]` class will parse all rules you specify against the following patterns.
**A rule must always start with alphabetical ([A-Za-z]) characters or a $Variable declaration** **A rule must always start with alphabetical ([A-Za-z]) characters or a $Variable declaration**
| Pattern | | Description | | Pattern | Description |
| ----------- | | --------------- | | ----------- | --------------- |
| `$` | | **Param Variable** - Starts the name of a paramater variable, it is optional to match this unless ! is used | | `$` | **Param Variable** - Starts the name of a paramater variable, it is optional to match this unless ! is used |
| `!` | | **Require Variable** - Placing this after a parameter variable requires data to be present for the rule to match | | `!` | **Require Variable** - Placing this after a parameter variable requires data to be present for the rule to match |
| `//` | | **Shift Point** - Declares that only variables denoted with a $ are parsed into the $params AFTER this point in the regex | | `//` | **Shift Point** - Declares that only variables denoted with a $ are parsed into the $params AFTER this point in the regex |
## Examples ## Examples

View File

@ -30,8 +30,9 @@ Or in your template (e.g. `themes/yourtheme/templates/Page.ss`):
:::ss :::ss
<% require css(mymodule/css/my.css) %> <% require css(mymodule/css/my.css) %>
Management through the `Requirements` class has the advantage that modules can include their own CSS files without modifying your template. Management through the `Requirements` class has the advantage that modules can include their own CSS files without modifying
On the other hand, you as a template developer can "block" or change certain CSS files that are included from thirdparty code. your template. On the other hand, you as a template developer can "block" or change certain CSS files that are included from
thirdparty code.
## WYSIWYG editor: typography.css and editor.css ## WYSIWYG editor: typography.css and editor.css

View File

@ -22,9 +22,9 @@ for introducing their usage.
## HTMLText vs. Text, and HTMLVarchar vs. Varchar ## HTMLText vs. Text, and HTMLVarchar vs. Varchar
The database field types HTMLVarchar and Varchar are exactly the same in the database. However, the templating engine The database field types `[api:HTMLVarchar]` and `[api:Varchar]` are exactly the same in the database. However, the
knows to escape the Varchar field and not the HTMLVarchar field. So, it's important you use the right field if you templating engine knows to escape the `[api:Varchar]` field and not the `[api:HTMLVarchar]` field. So, it's important you
don't want to be putting $FieldType.XML everywhere. use the right field if you don't want to be putting $FieldType.XML everywhere.
If you're going to put HTML content into the field, please use the field type with the HTML prefix. Otherwise, you're If you're going to put HTML content into the field, please use the field type with the HTML prefix. Otherwise, you're
going to risk double-escaping your data, forgetting to escape your data, and generally creating a confusing situation. going to risk double-escaping your data, forgetting to escape your data, and generally creating a confusing situation.

View File

@ -20,9 +20,11 @@ See [database-structure](/reference/database-structure) for in-depth information
## Generating the database-schema ## Generating the database-schema
The SilverStripe database-schema is generated automatically by visiting the URL. The SilverStripe database-schema is generated automatically by visiting the URL.
`http://`<mysite>`/dev/build` `http://<mysite>/dev/build`
> Note: You need to be logged in as an administrator to perform this command. <div class="notice" markdown='1'>
Note: You need to be logged in as an administrator to perform this command.
</div>
## Querying Data ## Querying Data
@ -47,7 +49,7 @@ table. **It will NOT return the additionally joined data.** The returned *$reco
`[api:DataObject]`. `[api:DataObject]`.
When using *$join* statements be sure the string is in the proper format for the respective database engine. In MySQL When using *$join* statements be sure the string is in the proper format for the respective database engine. In MySQL
the use of backticks may be necessary when referring Table Names and potentially Columns. (see [MySQL the use of back-ticks may be necessary when referring Table Names and potentially Columns. (see [MySQL
Identifiers](http://dev.mysql.com/doc/refman/5.0/en/identifiers.html)): Identifiers](http://dev.mysql.com/doc/refman/5.0/en/identifiers.html)):
:::php :::php
@ -60,7 +62,6 @@ Identifiers](http://dev.mysql.com/doc/refman/5.0/en/identifiers.html)):
"0, 10"); "0, 10");
## Properties ## Properties
@ -121,11 +122,15 @@ Here we combined a Player's first name and surname, accessible through $myPlayer
} }
} }
**CAUTION: It is common practice to make sure that pairs of custom getters/setter deal with the same data, in a consistent <div class="warning" markdown='1'>
format.** **CAUTION:** It is common practice to make sure that pairs of custom getters/setter deal with the same data, in a consistent
format.
</div>
**CAUTION: Custom setters can be hard to debug: Please double check if you could transform your data in more <div class="warning" markdown='1'>
straight-forward logic embedded to your custom controller or form-saving.** **CAUTION:** Custom setters can be hard to debug: Please double check if you could transform your data in more
straight-forward logic embedded to your custom controller or form-saving.
</div>
### Default Values ### Default Values
@ -139,15 +144,17 @@ new object is created.
); );
} }
> Note: Alternatively you can set defaults directly in the database-schema (rather than the object-model). See <div class="notice" markdown='1'>
Note: Alternatively you can set defaults directly in the database-schema (rather than the object-model). See
[data-types](data-types) for details. [data-types](data-types) for details.
</div>
### Casting ### Casting
Properties defined in *static $db* are automatically casted to their [data-types](data-types) when used in templates. Properties defined in *static $db* are automatically casted to their [data-types](data-types) when used in templates.
You can also cast the return-values of your custom functions (e.g. your "virtual properties"). You can also cast the return-values of your custom functions (e.g. your "virtual properties").
Calling those functions directly will still return whatever type your php-code generates, Calling those functions directly will still return whatever type your php-code generates,
but using the *obj()*-method or accessing through a template will cast the value accordig to the $casting-definition. but using the *obj()*-method or accessing through a template will cast the value according to the $casting-definition.
:::php :::php
class Player extends DataObject { class Player extends DataObject {
@ -164,15 +171,9 @@ but using the *obj()*-method or accessing through a template will cast the value
} }
## Relations ## Relations
Relations are built through static array definitions on a class, in the format:\\ Relations are built through static array definitions on a class, in the format `<relationship-name> => <classname>`
`<relationship-name>` => `<classname>`{php}
### has_one ### has_one
@ -202,8 +203,10 @@ parent element in the tree:
Defines 1-to-many joins. A database-column named ""`<relationship-name>`ID"" will to be created in the child-class. Defines 1-to-many joins. A database-column named ""`<relationship-name>`ID"" will to be created in the child-class.
**CAUTION: Please specify a $has_one-relationship on the related child-class as well, in order to have the necessary <div class="warning" markdown='1'>
accessors available on both ends.** **CAUTION:** Please specify a $has_one-relationship on the related child-class as well, in order to have the necessary
accessors available on both ends.
</div>
:::php :::php
// access with $myTeam->Players() or $player->Team() // access with $myTeam->Players() or $player->Team()
@ -219,7 +222,7 @@ accessors available on both ends.**
} }
To specify multiple has_manys to the same object you can use dot notation to distinguish them like below To specify multiple $has_manys to the same object you can use dot notation to distinguish them like below
:::php :::php
class Person { class Person {
@ -241,7 +244,6 @@ Multiple $has_one relationships are okay if they aren't linking to the same obje
:::php :::php
/** /**
* THIS IS BAD * THIS IS BAD
*/ */
class Team extends DataObject { class Team extends DataObject {
@ -261,8 +263,10 @@ Multiple $has_one relationships are okay if they aren't linking to the same obje
Defines many-to-many joins. A new table, (this-class)_(relationship-name), will be created with a pair of ID fields. Defines many-to-many joins. A new table, (this-class)_(relationship-name), will be created with a pair of ID fields.
**CAUTION: Please specify a $belongs_many_many-relationship on the related class as well, in order to have the necessary <div class="warning" markdown='1'>
accessors available on both ends.** **CAUTION:** Please specify a $belongs_many_many-relationship on the related class as well, in order to have the necessary
accessors available on both ends.
</div>
:::php :::php
// access with $myTeam->Categories() or $myCategory->Teams() // access with $myTeam->Categories() or $myCategory->Teams()
@ -372,10 +376,6 @@ casting data before saving.
); );
### onBeforeWrite ### onBeforeWrite
You can customize saving-behaviour for each DataObject, e.g. for adding security. These functions are private, obviously You can customize saving-behaviour for each DataObject, e.g. for adding security. These functions are private, obviously
@ -411,8 +411,10 @@ Example: Disallow creation of new players if the currently logged-in player is n
} }
> Note: There are no separate methods for *onBeforeCreate* and *onBeforeUpdate*. Please check for the existence of <div class="notice" markdown='1'>
Note: There are no separate methods for *onBeforeCreate* and *onBeforeUpdate*. Please check for the existence of
$this->ID to toggle these two modes, as shown in the example above. $this->ID to toggle these two modes, as shown in the example above.
</div>
### onBeforeDelete ### onBeforeDelete
@ -453,17 +455,13 @@ See `[api:SQLQuery]` for custom *INSERT*, *UPDATE*, *DELETE* queries.
## Decorating DataObjects ## Decorating DataObjects
You can add properties and methods to existing DataObjects like `[api:Member]` (a core class) without hacking core You can add properties and methods to existing `[api:DataObjects]`s like `[api:Member]` (a core class) without hacking core
code or subclassing. code or subclassing.
Please see `[api:DataObjectDecorator]` for a general description, and `[api:Hierarchy]` for our most Please see `[api:DataObjectDecorator]` for a general description, and `[api:Hierarchy]` for our most
popular examples. popular examples.
## FAQ ## FAQ
### Whats the difference between DataObject::get() and a relation-getter? ### Whats the difference between DataObject::get() and a relation-getter?
@ -471,7 +469,7 @@ You can work with both in pretty much the same way, but relationship-getters ret
A `[api:ComponentSet]` with relation-specific functionality. A `[api:ComponentSet]` with relation-specific functionality.
:::php :::php
$myTeam = DataObject::get_by_id('Team',$myPlayer->TeamID); // returns DataObjectSet $myTeam = DataObject::get_by_id('Team',$myPlayer->TeamID); // returns DataObject
$myTeam->add(new Player()); // fails $myTeam->add(new Player()); // fails
$myTeam = $myPlayer->Team(); // returns Componentset $myTeam = $myPlayer->Team(); // returns Componentset

View File

@ -32,17 +32,17 @@ use devmode on a public server very very carefully
Test mode is designed for staging environments or other private collaboration sites before deploying a site live. You do Test mode is designed for staging environments or other private collaboration sites before deploying a site live. You do
not need to use test mode if you do not have a staging environment or a place for testing which is on a public server) not need to use test mode if you do not have a staging environment or a place for testing which is on a public server)
In this mode error messages are hidden from the user and it includes BasicAuth integration if you want to password In this mode error messages are hidden from the user and it includes `[api:BasicAuth]` integration if you want to password
protect the site. protect the site.
To set your site to test mode set this in your mysite/_config.php file To set your site to test mode set this in your `mysite/_config.php` file
:::php :::php
Director::set_environment_type("test"); Director::set_environment_type("test");
A common situation is to enable password protected site viewing on your test site only. You can enable that but adding A common situation is to enable password protected site viewing on your test site only. You can enable that but adding
this to your mysite/_config file this to your `mysite/_config` file
:::php :::php
if(Director::isTest()) BasicAuth::protect_entire_site(); if(Director::isTest()) BasicAuth::protect_entire_site();
@ -54,7 +54,7 @@ Live sites should always run in live mode. Error messages are suppressed from th
to email the developers. This enables near real time reporting of any fatal errors or warnings on the site and can help to email the developers. This enables near real time reporting of any fatal errors or warnings on the site and can help
find any bugs users run into. find any bugs users run into.
To set your site to live mode set this in your mysite/_config.php file To set your site to live mode set this in your `mysite/_config.php` file
:::php :::php
Director::set_environment_type("live"); Director::set_environment_type("live");

View File

@ -2,7 +2,7 @@
## Introduction ## Introduction
The directory-structure in Silverstripe it built on "convention over configuration", so the placement of some files and The directory-structure in SilverStripe it built on "convention over configuration", so the placement of some files and
directories is meaningful to its logic. directories is meaningful to its logic.
## Core Structure ## Core Structure
@ -16,7 +16,7 @@ Directory | Description
## Custom Code Structure ## Custom Code Structure
We're using `<mysite>` as an example - arbitrary directory-names are allowed, as long as they don't collide with We're using `<mysite>` as an example - arbitrary directory-names are allowed, as long as they don't collide with
existing modules or the directories listes in "Core Structure". existing modules or the directories lists in "Core Structure".
| Directory | Description | | Directory | Description |
| --------- | ----------- | | --------- | ----------- |

View File

@ -15,7 +15,7 @@ an external SMTP server (see [PHP documentation for mail()](http://php.net/mail)
By default, emails are sent in both HTML and Plaintext format. By default, emails are sent in both HTML and Plaintext format.
A plaintext representation is automatically generated from the system A plaintext representation is automatically generated from the system
by stripping HTML markup, or transformining it where possible by stripping HTML markup, or transforming it where possible
(e.g. `<strong>text</strong>` is converted to `*text*`). (e.g. `<strong>text</strong>` is converted to `*text*`).
:::php :::php
@ -35,7 +35,7 @@ The default HTML template is located in `sapphire/templates/email/GenericEmail.s
**Requirements: SilverStripe 2.3+** **Requirements: SilverStripe 2.3+**
* Create a SS-template file called, in this example we will use 'MyEmail.ss' inside mysite/templates/email. * Create a SS-template file called, in this example we will use 'MyEmail.ss' inside `mysite/templates/email`.
* Fill this out with the body text for your email. You can use any [SS-template syntax](/topics/templates) (e.g. `<% control %>`, * Fill this out with the body text for your email. You can use any [SS-template syntax](/topics/templates) (e.g. `<% control %>`,
`<% if %>`, $FirstName etc) `<% if %>`, $FirstName etc)
* Choose your template with **setTemplate()** * Choose your template with **setTemplate()**
@ -86,14 +86,14 @@ Usage:
### Administrator Emails ### Administrator Emails
The static function `Email::setAdminEmail()` can be called from a _config.php file to set the address that these The static function `Email::setAdminEmail()` can be called from a `_config.php` file to set the address that these
emails should originate from. This address is used if the `from` field is empty. emails should originate from. This address is used if the `from` field is empty.
### Redirecting Emails ### Redirecting Emails
* Email::send_all_emails_to($address) will redirect all emails sent to the given address. Handy for testing! * `Email::send_all_emails_to($address)` will redirect all emails sent to the given address. Handy for testing!
* Email::cc_all_emails_to() and Email::bcc_all_emails_to() will keep the email going to its original recipients, but * `Email::cc_all_emails_to()` and `Email::bcc_all_emails_to()` will keep the email going to its original recipients, but
add an additional receipient in the BCC/CC header. Good for monitoring system-generated correspondence on the live add an additional recipient in the BCC/CC header. Good for monitoring system-generated correspondence on the live
systems. systems.
:::php :::php
@ -112,7 +112,7 @@ $value)**
.. ..
See http://en.wikipedia.org/wiki/E-mail#Message_header for a list of header names. See [Wikipedia E-mail Message header](http://en.wikipedia.org/wiki/E-mail#Message_header) for a list of header names.
### Newsletters ### Newsletters

View File

@ -8,17 +8,17 @@ them. Why should we have to go through the installation process and re-enter th
when we deploy them to our servers. Additionally, our production host's database connection details will likely be when we deploy them to our servers. Additionally, our production host's database connection details will likely be
different than our local server. different than our local server.
SilverStripe comes with a solution to this: the "_ss_environment.php" file. You can put a single _ss_environment.php SilverStripe comes with a solution to this: the `_ss_environment.php` file. You can put a single `_ss_environment.php`
file in your "projects" folder on your development box, and it will be used by each of your development sites. file in your "projects" folder on your development box, and it will be used by each of your development sites.
## Setting up your development machine with _ss_environment.php ## Setting up your development machine with _ss_environment.php
In this example, we assume that you are managing multiple projects as subfolders of "~/Sites/", and that you can visit In this example, we assume that you are managing multiple projects as subfolders of `~/Sites/`, and that you can visit
these at "http://localhost/". For example, you might have a project at "~/Sites/myproject/", and visit it at these at `http://localhost/`. For example, you might have a project at `~/Sites/myproject/`, and visit it at
"http://localhost/myproject/". `http://localhost/myproject/`.
Create a new file, ~/Sites/_ss_environment.php. Put the following content in it, editing the values of the Create a new file, `~/Sites/_ss_environment.php`. Put the following content in it, editing the values of the
"SS_DATABASE_..."" and "SS_DEFAULT_ADMIN_..." defines as appropriate. "SS_DATABASE_..." and "SS_DEFAULT_ADMIN_..." defines as appropriate.
:::php :::php
<?php <?php
@ -53,21 +53,21 @@ of `$databaseConfig` and `Director::set_dev_servers`, and instead make sure that
## How it works ## How it works
The mechanism by which the "_ss_environment.php" files work is quite simple. Here's how it works: The mechanism by which the `_ss_environment.php` files work is quite simple. Here's how it works:
* At the beginning of SilverStripe's execution, the _ss_environment.php file is searched for, and if it is found, it's * At the beginning of SilverStripe's execution, the `_ss_environment.php` file is searched for, and if it is found, it's
included. SilverStripe looks in 3 places for the file: included. SilverStripe looks in 3 places for the file:
* The site's base folder (ie, a sibling of sapphire, jsparty, and cms) * The site's base folder (ie, a sibling of sapphire, jsparty, and cms)
* The parent of the base folder * The parent of the base folder
* The grandparent of the base folder * The grandparent of the base folder
* The "_ss_environment.php" file sets a number of "define()". * The `_ss_environment.php` file sets a number of "define()".
* "conf/ConfigureFromEnv.php" is included from within your "mysite/_config.php". This file has a number of regular * "conf/ConfigureFromEnv.php" is included from within your `mysite/_config.php`. This file has a number of regular
configuration commands that use those defines as their arguments. If you are curious, open up configuration commands that use those defines as their arguments. If you are curious, open up
"sapphire/conf/ConfigureFromEnv.php" and see for yourself! `sapphire/conf/ConfigureFromEnv.php` and see for yourself!
### An Example ### An Example
This is my '_ss_environment.php' file. I have it placed in '/var', as each of the sites are in a subfolder of '/var'. This is my `_ss_environment.php` file. I have it placed in `/var`, as each of the sites are in a subfolder of `/var`.
:::php :::php
<?php <?php

View File

@ -40,7 +40,7 @@ friendlier - much like the 404 page, the error content can be edited within the
* Set the error code to 500 * Set the error code to 500
* Publish the page. * Publish the page.
**HOW IT WORKS: **The publication script for ErrorPage will write the full HTML content, including the template styling, **HOW IT WORKS: **The publication script for `[api:ErrorPage]` will write the full HTML content, including the template styling,
to assets/error-500.html. The fatal error handler looks for the presence of this file, and if it exists, dumps the to assets/error-500.html. The fatal error handler looks for the presence of this file, and if it exists, dumps the
content. This means that database access isn't required to provide a 500 error page. content. This means that database access isn't required to provide a 500 error page.
@ -55,7 +55,7 @@ You can indicate a log file relative to the site root. The named file will have
(an encoded file containing backtraces and things) will go to a file of a similar name, but with the suffix ".full" (an encoded file containing backtraces and things) will go to a file of a similar name, but with the suffix ".full"
added. added.
<mysite>/_config.php: `<mysite>/_config.php`:
:::php :::php
// log errors and warnings // log errors and warnings
@ -66,7 +66,7 @@ added.
#### Deprecated method (SS 2.3 ?) #### Deprecated method (SS 2.3 ?)
<mysite>/_config.php: `<mysite>/_config.php`:
:::php :::php
Debug::log_errors_to("/my/logfile/path"); Debug::log_errors_to("/my/logfile/path");
@ -77,7 +77,7 @@ added.
In addition to SilverStripe-integrated logging, it is adviseable to fall back to PHPs native logging functionality. A In addition to SilverStripe-integrated logging, it is adviseable to fall back to PHPs native logging functionality. A
script might terminate before it reaches the SilverStripe errorhandling, for example in the case of a fatal error. script might terminate before it reaches the SilverStripe errorhandling, for example in the case of a fatal error.
<mysite>/_config.php: `<mysite>/_config.php`:
:::php :::php
ini_set("log_errors", "On"); ini_set("log_errors", "On");
@ -88,7 +88,8 @@ script might terminate before it reaches the SilverStripe errorhandling, for exa
## Email Logs ## Email Logs
You can send both fatal errors and warnings in your code to a specified email-address. You can send both fatal errors and warnings in your code to a specified email-address.
<mysite>/_config.php:
`<mysite>/_config.php`:
:::php :::php
// log errors and warnings // log errors and warnings

View File

@ -5,14 +5,13 @@ CMSMain is part of the CMS. It is the controller for the content editor.
## Creating another hierarchical editor by subclassing CMSMain ## Creating another hierarchical editor by subclassing CMSMain
Sometimes you'll want to provide an administration interface that is pretty much exactly what CMSMain provides, but it's Sometimes you'll want to provide an administration interface that is pretty much exactly what CMSMain provides, but it's
not appropriate to include your data in with the site content. For example, Hayden developed a hierarchical category not appropriate to include your data in with the site content.
administrator on the Guano application.
Here's how you can do this: Here's how you can do this:
## Using classes other than SiteTree in the site tree ## Using classes other than SiteTree in the site tree
It is possible to use to different classes in two separate site trees. In Guano for example, there is the usual site It is possible to use to different classes in two separate site trees. For example, there is the usual site
content tree and a category tree. To change that find: content tree and a category tree. To change that find:
:::php :::php
@ -50,12 +49,12 @@ where url is the relative link to the page (eg 'admin/categories'). You can chan
## Overloading EditForm ## Overloading EditForm
You may need to overload EditForm if your class does not use the Versioned extension. You may need to overload EditForm if your class does not use the `[api:Versioned]` extension.
## Overloading SiteTreeAsUL ## Overloading SiteTreeAsUL
The tree hints can sometimes cause problems when reorganising the tree, and the CMSMain::SiteTreeAsUL function uses The tree hints can sometimes cause problems when reorganising the tree, and the CMSMain::SiteTreeAsUL function uses
SiteTree explicitly. Use: `[api:SiteTree]` explicitly. Use:
:::php :::php
public function SiteTreeAsUL() { public function SiteTreeAsUL() {
@ -64,4 +63,3 @@ SiteTree explicitly. Use:
return $this->getSiteTreeFor( $this->stat('tree_class') ); return $this->getSiteTreeFor( $this->stat('tree_class') );
} }

View File

@ -6,8 +6,8 @@ Form validation is a combination of PHP and JavaScript
### Introduction ### Introduction
Validators are implemented as an argument to the [api:Form] constructor. You create a required fields validator like Validators are implemented as an argument to the `[api:Form]` constructor. You create a required fields validator like
so. In this case, we're creating a [api:RequiredFields] validator - the [api:Validator] class itself is an abstract so. In this case, we're creating a `[api:RequiredFields]` validator - the `[api:Validator]` class itself is an abstract
class. class.

View File

@ -33,7 +33,7 @@ Example:
## Subclassing a form ## Subclassing a form
It's the reponsibility of your subclass' constructor to call It's the responsibility of your subclass' constructor to call
:::php :::php
parent::__construct() parent::__construct()
@ -56,6 +56,8 @@ $name must be passed - their values depend on where the form is instantiated.
The real difference, however, is that you can then define your controller methods within the form class itself. The real difference, however, is that you can then define your controller methods within the form class itself.
## Form Field Types ## Form Field Types
There are many classes extending `[api:FormField]`. Some examples: There are many classes extending `[api:FormField]`. Some examples:
@ -71,6 +73,7 @@ There are many classes extending `[api:FormField]`. Some examples:
Full overview at [form-field-types](/reference/form-field-types) Full overview at [form-field-types](/reference/form-field-types)
### Using Form Fields ### Using Form Fields
To get these fields automatically rendered into a form element, all you need to do is create a new instance of the To get these fields automatically rendered into a form element, all you need to do is create a new instance of the
@ -192,8 +195,8 @@ First of all, you need to create your form on it's own class, that way you can d
} }
`forTemplate()` tells the Form class to render with a template of return value of `$this->class`, which in this case `forTemplate()` tells the `[api:Form]` class to render with a template of return value of `$this->class`, which in this case
is *MyForm*, the name of the class. If the template doesn't exist, then it falls back to using Form.ss is *MyForm*, the name of the class. If the template doesn't exist, then it falls back to using Form.ss.
*MyForm.ss* should then be placed into your *templates/Includes* directory for your project. Here is an example of *MyForm.ss* should then be placed into your *templates/Includes* directory for your project. Here is an example of
basic customisation: basic customisation:
@ -232,7 +235,7 @@ this case `TextField->Field()` or `EmailField->Field()` which returns an `<input
for the type of field. Pass in the name of the field as the first parameter, as done above, to render it into the for the type of field. Pass in the name of the field as the first parameter, as done above, to render it into the
template. template.
To find more methods, have a look at the Form class, as there is a lot of different methods of customising the form To find more methods, have a look at the `[api:Form]` class, as there is a lot of different methods of customising the form
templates, for example, you could use `<% control Fields %>` instead of specifying each field manually, as we've done templates, for example, you could use `<% control Fields %>` instead of specifying each field manually, as we've done
above. above.

View File

@ -2,7 +2,7 @@
## Introduction ## Introduction
The i18n class (short for "internationalization") in Silverstripe enables you to display templates and PHP code in The i18n class (short for "internationalization") in SilverStripe enables you to display templates and PHP code in
different languages based on your global settings and the preferences of your website users. This process is also known different languages based on your global settings and the preferences of your website users. This process is also known
as l10n (short for "localization"). as l10n (short for "localization").
@ -28,8 +28,8 @@ The i18n class is enabled by default.
### Setting the locale ### Setting the locale
To set the locale you just need to call `[api:i18n::set_locale()]` passing, as a parameter, the name of the locale that you want to To set the locale you just need to call `[api:i18n::set_locale()]` passing, as a parameter, the name of the locale that you
set. want to set.
:::php :::php
//Example 1: setting the locale //Example 1: setting the locale
@ -197,9 +197,10 @@ Sprintf enables us to dynamically replace parts of a translated string, e.g. by
$title $title
) )
<div class="warning" markdown='1'>
**Caution**: In templates (*.ss)-files you can only use ONE argument for your sprintf-support, and can't use spaces **Caution**: In templates (*.ss)-files you can only use ONE argument for your sprintf-support, and can't use spaces
between parameters. between parameters.
</div>
:::php :::php
// in SS-template ($title must be available in the current template-scope) // in SS-template ($title must be available in the current template-scope)
@ -219,7 +220,9 @@ If you want to run the text collector for just one module you can use the 'modul
`http://<mysite>/dev/tasks/i18nTextCollectorTask/?module=cms` `http://<mysite>/dev/tasks/i18nTextCollectorTask/?module=cms`
<div class="notice" markdown='1'>
**Note**: You'll need to install PHPUnit to run the text collector (see [testing-guide](/topics/testing)). **Note**: You'll need to install PHPUnit to run the text collector (see [testing-guide](/topics/testing)).
</div>
## Language tables in PHP ## Language tables in PHP
@ -317,8 +320,9 @@ Example Translation Table (mymodule/javascript/lang/de_DE.js)
* No detecting/conversion of character encodings (we rely fully on UTF-8) * No detecting/conversion of character encodings (we rely fully on UTF-8)
* Translation of graphics/assets * Translation of graphics/assets
* Usage of gettext (too clumsy, too many requirements) * Usage of gettext (too clumsy, too many requirements)
* Displaying multipe languages/encodings on the same page * Displaying multiple languages/encodings on the same page
## Links ## Links
* [http://www.i18nguy.com/](http://www.i18nguy.com/) * [http://www.i18nguy.com/](http://www.i18nguy.com/)
* [balbus.tk i18n notes](http://www.balbus.tk/internationalize)

View File

@ -10,7 +10,7 @@ It is where most documentation should live, and is the natural "second step" aft
* [Data Types](data-types): Types that properties on `DataObject` can have (e.g. `Text` or `Date`) * [Data Types](data-types): Types that properties on `DataObject` can have (e.g. `Text` or `Date`)
* [Datamodel](datamodel): How we use an "Object-relational model" to expose database information in a useful way * [Datamodel](datamodel): How we use an "Object-relational model" to expose database information in a useful way
* [Debugging](debugging): Tracking down errors via logs, URL parameters and profiling * [Debugging](debugging): Tracking down errors via logs, URL parameters and profiling
* [Directory Structure](directory-structure): Whats core files, where do modules and my own project files go? * [Directory Structure](directory-structure): What are core files, where do modules and my own project files go?
* [Emails](email): Configuring and sending emails * [Emails](email): Configuring and sending emails
* [Environment management](environment-management): Sharing configuration details (e.g. database login, passwords) with multiple websites via a `_ss_environment.php` file * [Environment management](environment-management): Sharing configuration details (e.g. database login, passwords) with multiple websites via a `_ss_environment.php` file
* [Error Handling](error-handling): Error messages and filesystem logs * [Error Handling](error-handling): Error messages and filesystem logs
@ -20,7 +20,7 @@ It is where most documentation should live, and is the natural "second step" aft
* [Forms](forms): Create your own form, add fields and create your own form template using the existing `Form` class * [Forms](forms): Create your own form, add fields and create your own form template using the existing `Form` class
* [Internationalization (i18n)](i18n): Displaying templates and PHP code in different languages using i18n * [Internationalization (i18n)](i18n): Displaying templates and PHP code in different languages using i18n
* [Javascript](javascript): Best practices for developing with JavaScript in SilverStripe * [Javascript](javascript): Best practices for developing with JavaScript in SilverStripe
* [Module Development](module-development): Creating a module (also known as "extension" or "plugin") to contain reuseable functionality * [Module Development](module-development): Creating a module (also known as "extension" or "plugin") to contain reusable functionality
* [Modules](modules): Introduction, how to download and install a module (e.g. with blog or forum functionality) * [Modules](modules): Introduction, how to download and install a module (e.g. with blog or forum functionality)
* [Page Types](page-types): What is a "page type" and how do you create one? * [Page Types](page-types): What is a "page type" and how do you create one?
* [Search](search): Searching for properties in the database as well as other documents * [Search](search): Searching for properties in the database as well as other documents
@ -29,7 +29,7 @@ It is where most documentation should live, and is the natural "second step" aft
* [Testing](testing): Functional and Unit Testing with PHPUnit and SilverStripe's testing framework * [Testing](testing): Functional and Unit Testing with PHPUnit and SilverStripe's testing framework
* [Developing Themes](theme-development): Package templates, images and CSS to a reusable theme * [Developing Themes](theme-development): Package templates, images and CSS to a reusable theme
* [Translation](translation): Creating content in multiple languages * [Translation](translation): Creating content in multiple languages
* [Widgets](widgets): Small feature blocks which can be placed on a page by the CMS editor * [Widgets](widgets): Small feature blocks which can be placed on a page by the CMS editor, also outlines how to create and add widgets
## Feedback ## Feedback

View File

@ -9,9 +9,9 @@ practices can be applied to other libraries as well.
## File Inclusion ## File Inclusion
SilverStripe-driven code should use the `Requirements` class to manage clientside dependencies like CSS and JavaScript SilverStripe-driven code should use the `[api:Requirements]` class to manage clientside dependencies like CSS and JavaScript
files, rather than including `<script>` and `<link>` tags in your templates. This has the advantage that a registry files, rather than including `<script>` and `<link>` tags in your templates. This has the advantage that a registry
of requirements can be built up from different places outside of the main controller, for example included `FormField` of requirements can be built up from different places outside of the main controller, for example included `[api:FormField]`
instances. instances.
See [requirements](/reference/requirements) documentation. See [requirements](/reference/requirements) documentation.
@ -27,10 +27,12 @@ SilverStripe CMS uses [jQuery UI](http://ui.jquery.com) on top of jQuery.
For any custom code developed with jQuery, you have four choices to structure it: Custom jQuery Code, a jQuery Plugin, a For any custom code developed with jQuery, you have four choices to structure it: Custom jQuery Code, a jQuery Plugin, a
jQuery UI Widget, or a `jQuery.entwine` behaviour. We'll detail below where each solution is appropriate. jQuery UI Widget, or a `jQuery.entwine` behaviour. We'll detail below where each solution is appropriate.
<div class="hint" markdown='1'>
**Important**: Historically we have been using [PrototypeJS](http://prototypejs.com), which is now discouraged. SilverStripe as a framework doesn't impose a choice of library. It **Important**: Historically we have been using [PrototypeJS](http://prototypejs.com), which is now discouraged. SilverStripe as a framework doesn't impose a choice of library. It
tries to generate meaningful markup which you can alter with other JavaScript libraries as well. Only the CMS itself and tries to generate meaningful markup which you can alter with other JavaScript libraries as well. Only the CMS itself and
certain form widgets require jQuery to function correctly. You can also use jQuery in parallel with other libraries, see certain form widgets require jQuery to function correctly. You can also use jQuery in parallel with other libraries, see
[here](http://docs.jquery.com/Using_jQuery_with_Other_Libraries). [here](http://docs.jquery.com/Using_jQuery_with_Other_Libraries).
</div>
### Custom jQuery Code ### Custom jQuery Code
@ -229,7 +231,7 @@ jQuery with a few lines of code. Your jQuery code will normally end up as a ser
### Don't claim global properties ### Don't claim global properties
Global properties are evil. They are accesible by other scripts, might be overwritten or mis-used. A popular case is the `$` shortcut in different libraries: in PrototypeJS it stands for `document.getElementByID()`, in jQuery for `jQuery()`. Global properties are evil. They are accessible by other scripts, might be overwritten or misused. A popular case is the `$` shortcut in different libraries: in PrototypeJS it stands for `document.getElementByID()`, in jQuery for `jQuery()`.
:::js :::js
// you can't rely on '$' being defined outside of the closure // you can't rely on '$' being defined outside of the closure
@ -506,7 +508,7 @@ To generate documentation for SilverStripe code, use [JSDoc toolkit](http://code
JavaScript, take a look at the [jsdoc cookbook](http://code.google.com/p/jsdoc-toolkit/wiki/CookBook). The `@lends` JavaScript, take a look at the [jsdoc cookbook](http://code.google.com/p/jsdoc-toolkit/wiki/CookBook). The `@lends`
and `@borrows` properties are particularly useful for documenting jQuery-style code. and `@borrows` properties are particularly useful for documenting jQuery-style code.
JSDoc-toolkit is a commandline utility, see [usage](http://code.google.com/p/jsdoc-toolkit/wiki/CommandlineOptions). JSDoc-toolkit is a command line utility, see [usage](http://code.google.com/p/jsdoc-toolkit/wiki/CommandlineOptions).
Example: jQuery.entwine Example: jQuery.entwine

View File

@ -36,7 +36,7 @@ init() function on your module controller classes:
} }
This will use your_project/css/forum.css if it exists, otherwise it falls back to using forum/css/forum.css. This will use `<projectname>/css/forum.css` if it exists, otherwise it falls back to using `forum/css/forum.css`.
## Publication ## Publication
@ -46,10 +46,13 @@ adherence to conventions, writing documentation, and releasing updates. See [con
## Reference ## Reference
**How To:** **How To:**
* [Add a link to your module in the main SilverStripe Admin Menu](/reference/leftandmain)
**
Useful Links:**
* [Add a link to your module in the main SilverStripe Admin Menu](/reference/leftandmain)
**Useful Links:**
* [Modules](modules)
* [Module Release Process](module-release-process)
* [Debugging methods](/topics/debugging) * [Debugging methods](/topics/debugging)
* [URL Variable Tools](/reference/urlvariabletools) - Lists a number of “page options” , “rendering tools” or “special * [URL Variable Tools](/reference/urlvariabletools) - Lists a number of “page options” , “rendering tools” or “special
URL variables” that you can use to debug your sapphire applications URL variables” that you can use to debug your sapphire applications

View File

@ -86,3 +86,8 @@ Exit the editor and then run
svn up svn up
**Useful Links:**
* [Modules](module-developement)
* [Module Release Process](module-release-process)

View File

@ -17,9 +17,9 @@ and use this template to lay out the basic design elements that dont change.
It contains standard HTML markup, with some differences. Well go over these later, but for now, you can see that this It contains standard HTML markup, with some differences. Well go over these later, but for now, you can see that this
file only generates some of the content it sets up the `<html>` tags, deals with the `<head>` section, creates the file only generates some of the content it sets up the `<html>` tags, deals with the `<head>` section, creates the
first-level navigation, and then closes it all off again. See $Layout? Thats what is doing most of the work when you first-level navigation, and then closes it all off again. See $Layout? Thats what is doing most of the work when you
visit a page. Now take a look at mysite/templates/Layout/Page.ss. This as you can see has a lot more markup in it visit a page. Now take a look at `mysite/templates/Layout/Page.ss`. This as you can see has a lot more markup in it
its what is included into $Layout when the Page page type is rendered. Similarly, its what is included into $Layout when the Page page type is rendered. Similarly,
mysite/templates/Layout/HomePage.ss would be rendered into $Layout when the HomePage page type is selected for the `mysite/templates/Layout/HomePage.ss` would be rendered into $Layout when the HomePage page type is selected for the
current page youre viewing. current page youre viewing.
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 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
@ -39,7 +39,7 @@ we want to do to the CMS for this page type in here.
Page types are created using PHP classes. If youre not sure about how these work, [click here for a gentler Page types are created using PHP classes. If youre not sure about how these work, [click here for a gentler
introduction to PHP classes](http://www-128.ibm.com/developerworks/opensource/library/os-phpobj/). 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. We also put Page_Controller in here. Any other 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 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. StaffPage_Image class will go into StaffPage.php.
@ -78,8 +78,8 @@ See [form](/topics/forms) and [tutorial:2-extending-a-basic-site](/tutorials/2-e
### removeFieldFromTab() ### removeFieldFromTab()
Overloading `getCMSFields()` you can call `removeFieldFromTab()` on a `FieldSet` object. For example, if you don't Overloading `getCMSFields()` you can call `removeFieldFromTab()` on a `[api:FieldSet]` object. For example, if you don't
want the MenuTitle field to show on your page, which is inherited from SiteTree. want the MenuTitle field to show on your page, which is inherited from `[api:SiteTree]`.
:::php :::php
class StaffPage extends Page { class StaffPage extends Page {
@ -158,3 +158,4 @@ and [tutorial:3-forms](/tutorials/3-forms).
if ($stageRecord) $stageRecord->delete(); if ($stageRecord) $stageRecord->delete();
$liveRecord = Versioned::get_one_by_stage('SiteTree', 'Live', "SiteTree_Live.ID = $pageID"); $liveRecord = Versioned::get_one_by_stage('SiteTree', 'Live', "SiteTree_Live.ID = $pageID");
if ($liveRecord) $liveRecord->delete(); if ($liveRecord) $liveRecord->delete();

View File

@ -5,10 +5,12 @@
Fulltext search for page content (and other attributes like "Title" or "MetaTags") can be easily added to SilverStripe. Fulltext search for page content (and other attributes like "Title" or "MetaTags") can be easily added to SilverStripe.
See [Tutorial: Site Search](/tutorials/4-site-search) for details. See [Tutorial: Site Search](/tutorials/4-site-search) for details.
## Searching for DataObjects ## Searching for DataObject's
The [api:SearchContext] class provides a good base implementation that you can hook into your own controllers. The `[api:SearchContext]` class provides a good base implementation that you can hook into your own controllers.
A working implementation of searchable DataObjects can be seen in the [api:ModelAdmin] class. A working implementation of searchable DataObjects can be seen in the `[api:ModelAdmin]` class.
[SearchContext](/reference/searchcontext) goes into more detail about setting up a default search form for `[api:DataObject]`s.
## Searching for Documents ## Searching for Documents
@ -21,6 +23,7 @@ dedicated search service like the [sphinx module](http://silverstripe.org/sphinx
* `[api:ModelAdmin]` * `[api:ModelAdmin]`
* `[api:RestfulServer]` * `[api:RestfulServer]`
* [Tutorial: Site Search](/tutorials/4-site-search) * [Tutorial: Site Search](/tutorials/4-site-search)
* [SearchContext](/reference/searchcontext)
* [genericviews module](http://silverstripe.org/generic-views-module) * [genericviews module](http://silverstripe.org/generic-views-module)
* [sphinx module](http://silverstripe.org/sphinx-module) * [sphinx module](http://silverstripe.org/sphinx-module)
* [lucene module](http://silverstripe.org/lucene-module) * [lucene module](http://silverstripe.org/lucene-module)

View File

@ -2,22 +2,21 @@
## Introduction ## Introduction
This page details notes on how to ensure that we develop secure SilverStripe applications. See [security](/topics/security) for This page details notes on how to ensure that we develop secure SilverStripe applications. See [security](/topics/security)
the Silverstripe-class as a starting-point for most security-related functionality. for the Silverstripe-class as a starting-point for most security-related functionality.
See our [contributing guidelines](/misc/contributing#reporting-security-issues) on how See our [contributing guidelines](/misc/contributing#reporting-security-issues) on how to report security issues.
to report security issues.
## SQL Injection ## SQL Injection
The [coding-conventions](/misc/coding-conventions) help guard against SQL injection attacks but still require developer The [coding-conventions](/misc/coding-conventions) help guard against SQL injection attacks but still require developer
dilligence: ensure that any variable you insert into a filter / sort / join clause has been escaped. diligence: ensure that any variable you insert into a filter / sort / join clause has been escaped.
See [http://shiflett.org/articles/sql-injection](http://shiflett.org/articles/sql-injection). See [http://shiflett.org/articles/sql-injection](http://shiflett.org/articles/sql-injection).
### Automatic escaping ### Automatic escaping
Silverstripe automatically runs [addslashes()](http://php.net/addslashes) in DataObject::write() wherever possible. Data SilverStripe automatically runs [addslashes()](http://php.net/addslashes) in DataObject::write() wherever possible. Data
is escaped when saving back to the database, not when writing to object-properties. is escaped when saving back to the database, not when writing to object-properties.
* DataObject::get_by_id() * DataObject::get_by_id()
@ -29,8 +28,10 @@ is escaped when saving back to the database, not when writing to object-properti
* FormField->saveInto() * FormField->saveInto()
* DBField->saveInto() * DBField->saveInto()
Note: It is NOT good practice to "be sure" and convert the data passed to the functions below manually. This might <div class="warning" markdown='1'>
It is NOT good practice to "be sure" and convert the data passed to the functions below manually. This might
result in *double escaping* and alters the actually saved data (e.g. by adding slashes to your content). result in *double escaping* and alters the actually saved data (e.g. by adding slashes to your content).
</div>
### Manual escaping ### Manual escaping
@ -106,8 +107,10 @@ XSS (Cross-Site-Scripting). With some basic guidelines, you can ensure your outp
displaying a blog post in HTML from a trusted author, or escaping a search parameter from an untrusted visitor before displaying a blog post in HTML from a trusted author, or escaping a search parameter from an untrusted visitor before
redisplaying it). redisplaying it).
<div class="notice" markdown='1'>
Note: SilverStripe templates do not remove tags, please use [strip_tags()](http://php.net/strip_tags) for this purpose Note: SilverStripe templates do not remove tags, please use [strip_tags()](http://php.net/strip_tags) for this purpose
or [sanitize](http://htmlpurifier.org/) it correctly. or [sanitize](http://htmlpurifier.org/) it correctly.
</div>
See [http://shiflett.org/articles/foiling-cross-site-attacks](http://shiflett.org/articles/foiling-cross-site-attacks) See [http://shiflett.org/articles/foiling-cross-site-attacks](http://shiflett.org/articles/foiling-cross-site-attacks)
for in-depth information about "Cross-Site-Scripting". for in-depth information about "Cross-Site-Scripting".
@ -253,7 +256,7 @@ Template:
Some rules of thumb: Some rules of thumb:
* Don't concatenate URLs in a template. It only works in extremely simple cases that usually contain bugs. * Don't concatenate URLs in a template. It only works in extremely simple cases that usually contain bugs.
* Use *Controller::join_links()* to concatenate URLs. It deals with querystrings and other such edge cases. * Use *Controller::join_links()* to concatenate URLs. It deals with query strings and other such edge cases.
## Cross-Site Request Forgery (CSRF) ## Cross-Site Request Forgery (CSRF)
@ -292,7 +295,7 @@ Below is an example with different ways you would use this casting technique:
function CaseStudies() { function CaseStudies() {
// cast an ID from URL parameters e.g. (mysite.com/home/action/ID) // cast an ID from URL parameters e.g. (mysite.com/home/action/ID)
$anotherID = (int)Director::urlParams['ID']; $anotherID = (int)Director::urlParam['ID'];
// perform a calculation, the prerequisite being $anotherID must be an integer // perform a calculation, the prerequisite being $anotherID must be an integer
$calc = $anotherID + (5 - 2) / 2; $calc = $anotherID + (5 - 2) / 2;
@ -331,7 +334,6 @@ disallow certain filetypes.
Example configuration for Apache2: Example configuration for Apache2:
<VirtualHost *:80> <VirtualHost *:80>
... ...
<LocationMatch assets/> <LocationMatch assets/>
@ -346,7 +348,6 @@ file in the assets directory. This requires PHP to be loaded as an Apache modul
**/assets/.htaccess** **/assets/.htaccess**
php_flag engine off php_flag engine off
Options -ExecCGI -Includes -Indexes Options -ExecCGI -Includes -Indexes

View File

@ -93,7 +93,7 @@ by using the `$Layout` variable so it makes sense to add the .typography style a
$Layout $Layout
</div> </div>
## Designing reuseable templates ## Designing reusable templates
Although SilverStripe is ultimately flexible in how you create your templates, there's a couple of best practices. These Although SilverStripe is ultimately flexible in how you create your templates, there's a couple of best practices. These
will help you to design templates for modules, and make it easier for other site developers to integrate them into their will help you to design templates for modules, and make it easier for other site developers to integrate them into their
@ -101,7 +101,7 @@ 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 * 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>`) * 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 * Layout templates only include content that could be completely replaced by another module (e.g. a forum thread). It
might be infeasible to do this 100%, but remember that every piece of navigation that needs to appear inside `$Layout` might be infeasible to do this 100%, but remember that every piece of navigation that needs to appear inside `$Layout`
will mean that you have to customise templates when integrating the module. will mean that you have to customise templates when integrating the module.

View File

@ -2,7 +2,7 @@
Functional tests test your controllers. The core of these are the same as unit tests: Functional tests test your controllers. The core of these are the same as unit tests:
* Create a subclass of SapphireTest in the mysite/tests or (module)/tests folder. * Create a subclass of `[api:SapphireTest]` in the `mysite/tests` or `(module)/tests` folder.
* Define static $fixture_file to point to a database YAML file. * Define static $fixture_file to point to a database YAML file.
* Create methods that start with "test" to create your tests. * Create methods that start with "test" to create your tests.
* Assertions are used to work out if a test passed or failed. * Assertions are used to work out if a test passed or failed.
@ -15,7 +15,6 @@ URLs. Here is an example from the subsites module:
static $fixture_file = 'subsites/tests/SubsiteTest.yml'; static $fixture_file = 'subsites/tests/SubsiteTest.yml';
/** /**
* Return a session that has a user logged in as an administrator * Return a session that has a user logged in as an administrator
*/ */
function adminLoggedInSession() { function adminLoggedInSession() {
@ -25,7 +24,6 @@ URLs. Here is an example from the subsites module:
} }
/** /**
* Test generation of the view * Test generation of the view
*/ */
function testBasicView() { function testBasicView() {
@ -50,7 +48,7 @@ URLs. Here is an example from the subsites module:
We are using a new static method here: **Director::test($url, $postVars, $sessionObj)** We are using a new static method here: **Director::test($url, $postVars, $sessionObj)**
Director::test() lets us execute a URL and see what happens. It bypasses HTTP, instead relying on the cleanly Director::test() lets us execute a URL and see what happens. It bypasses HTTP, instead relying on the cleanly
encapsulated execution model of Controller. encapsulated execution model of `[api:Controller]`.
It takes 3 arguments: It takes 3 arguments:
@ -58,7 +56,7 @@ It takes 3 arguments:
* $postVars: Post variables to pass to the URL * $postVars: Post variables to pass to the URL
* $sessionObj: A Session object representing the current session. * $sessionObj: A Session object representing the current session.
And it returns an HTTPResponse object, which will give you the response headers (including redirection), status code, And it returns an `[api:HTTPResponse]` object, which will give you the response headers (including redirection), status code,
and body. and body.
We can use string processing on the body of the response to then see if it fits with our expectations. We can use string processing on the body of the response to then see if it fits with our expectations.

View File

@ -1,7 +1,7 @@
# How To Create a Sapphire Test # How To Create a Sapphire Test
A unit test class will test the behaviour of one of your DataObjects. This simple fragment of SiteTreeTest provides us A unit test class will test the behaviour of one of your `[api:DataObjects]`. This simple fragment of `[api:SiteTreeTest]`
the basics of creating unit tests. provides us the basics of creating unit tests.
:::php :::php
<?php <?php
@ -47,7 +47,7 @@ the basics of creating unit tests.
There are a number of points to note in this code fragment: There are a number of points to note in this code fragment:
* Your test is a **subclass of SapphireTest**. Both unit tests and functional tests are a subclass of SapphireTest. * Your test is a **subclass of SapphireTest**. Both unit tests and functional tests are a subclass of `[api:SapphireTest]`.
* **static $fixture_file** is defined. The testing framework will automatically set up a new database for **each** of * **static $fixture_file** is defined. The testing framework will automatically set up a new database for **each** of
your tests. The initial database content will be sourced from the YML file that you list in $fixture_file. You must your tests. The initial database content will be sourced from the YML file that you list in $fixture_file. You must
define this value. Note also that, for the time being, you can only point to one YML file for each test class. define this value. Note also that, for the time being, you can only point to one YML file for each test class.
@ -55,7 +55,7 @@ define this value. Note also that, for the time being, you can only point to on
database will be rebuilt for each of these. database will be rebuilt for each of these.
* **$this->objFromFixture($className, $identifier)** can be used to select one of the objects named in your fixture * **$this->objFromFixture($className, $identifier)** can be used to select one of the objects named in your fixture
file. To identify to the object, we provide a class name and an identifier. The identifier is specified in the YML file. To identify to the object, we provide a class name and an identifier. The identifier is specified in the YML
file but not saved in the database anywhere. objFromFixture() looks the DataObject up in memory rather than using the file but not saved in the database anywhere. objFromFixture() looks the `[api:DataObject]` up in memory rather than using the
database. This means that you can use it to test the functions responsible for looking up content in the database. database. This means that you can use it to test the functions responsible for looking up content in the database.
* **$this->assertEquals()** is one of the many assert... functions that PHPUnit provides us. See below for more * **$this->assertEquals()** is one of the many assert... functions that PHPUnit provides us. See below for more
information. information.
@ -78,7 +78,7 @@ if it starts with "/".
## The Database YAML file ## The Database YAML file
The main feature of SapphireTest over the raw PHPUnit classes is that SapphireTest will prepare a temporary database for The main feature of `[api:SapphireTest]` over the raw PHPUnit classes is that SapphireTest will prepare a temporary database for
you. The content of that database is provided in a special YAML file. YAML is a simple markup languages that uses tabs you. The content of that database is provided in a special YAML file. YAML is a simple markup languages that uses tabs
and colons instead of the more verbose XML tags, and because of this much better for developers creating files by hand. and colons instead of the more verbose XML tags, and because of this much better for developers creating files by hand.

View File

@ -15,8 +15,8 @@ send emails in your SilverStripe application. Here is a simple example of how y
$e->send(); $e->send();
Normally, the send() method would send an email using PHP's mail() function. However, if you are running a `SapphireTest` test, then it holds off actually sending the email, and instead lets you assert that an email was sent Normally, the send() method would send an email using PHP's mail() function. However, if you are running a `[api:SapphireTest]`
using this method. test, then it holds off actually sending the email, and instead lets you assert that an email was sent using this method.
:::php :::php
$this->assertEmailSent("someone@example.com", null, "/th.*e$/"); $this->assertEmailSent("someone@example.com", null, "/th.*e$/");
@ -25,7 +25,7 @@ using this method.
The arguments are `$to`, `$from`, `$subject`, `$body`, and can be take one of the following three forms: The arguments are `$to`, `$from`, `$subject`, `$body`, and can be take one of the following three forms:
* A string: match exactly that string * A string: match exactly that string
* `null`/''false'': match anything * `null/false`: match anything
* A PERL regular expression (starting with '/'): match that regular expression * A PERL regular expression (starting with '/'): match that regular expression
## How to use it ## How to use it

View File

@ -217,19 +217,20 @@ Maybe even a nice link saying Website Powered by SilverStripe to show your suppo
A bunch of resources feel free to use to make your template awesome A bunch of resources feel free to use to make your template awesome
* http://kuler.adobe.com - Kuler is a great color scheming tool * [http://kuler.adobe.com](http://kuler.adobe.com) - Kuler is a great color scheming tool
* http://blog.html.it/layoutgala/ - 40 super cool CSS layouts for you to use * [http://blog.html.it/layoutgala/](http://blog.html.it/layoutgala/) - 40 super cool CSS layouts for you to use
* http://designmeltdown.com - Great gallery of websites. Browse through and get inspired. * [http://designmeltdown.com](http://designmeltdown.com) - Great gallery of websites. Browse through and get inspired.
* http://validator.w3.org/ - Your template must pass 'or get near' validation. * [http://validator.w3.org/](http://validator.w3.org/) - Your template must pass 'or get near' validation.
* http://famfamfam.com/lab/icons/ - free, beautiful icons. * [http://famfamfam.com/lab/icons/](http://famfamfam.com/lab/icons/) - free, beautiful icons.
* http://cssremix.com - Another CSS site gallery for inspiration. * [http://cssremix.com](http://cssremix.com) - Another CSS site gallery for inspiration.
* http://www.maxdesign.com.au/presentation/process/ - a good process for creating a design * [http://www.maxdesign.com.au/presentation/process/](http://www.maxdesign.com.au/presentation/process/) - a good process for creating a design
## Reference ## Reference
### Overriding ### Overriding
The templating system will search for the appropriate files in the following order: The templating system will search for the appropriate files in the following order:
1. mysite (or other name given to site folder) 1. mysite (or other name given to site folder)
2. themes 2. themes
3. module (eg blog) 3. module (eg blog)
@ -238,7 +239,9 @@ So if, for example, you had a typography.css file for a module in the module fol
directory (eg themes/blackcandy_blog/css/), and in your site folder (eg mysite/css/), the system would use the file directory (eg themes/blackcandy_blog/css/), and in your site folder (eg mysite/css/), the system would use the file
mysite/css/typography.css mysite/css/typography.css
<div class="notice" markdown='1'>
Note: This only applies for CSS and template files. PHP files **do not** get overridden! Note: This only applies for CSS and template files. PHP files **do not** get overridden!
</div>
### Requirements ### Requirements

View File

@ -24,7 +24,7 @@ See [Developing Themes](theme-development) to get an idea of how themes actually
## Submitting your theme to SilverStripe ## Submitting your theme to SilverStripe
If you want to submit your theme to the silverstripe directory then check If you want to submit your theme to the SilverStripe directory then check
* You should ensure your templates are well structured, modular and commented so it's easy for other people to * You should ensure your templates are well structured, modular and commented so it's easy for other people to
customise them. customise them.

View File

@ -60,15 +60,18 @@ Enabling Translatable through *Object::add_extension()* in your *mysite/_config.
} }
Make sure to rebuild the database through /dev/build after enabling translatable. Make sure to rebuild the database through /dev/build after enabling `[api:Translatable]`.
Use the correct set_default_locale() before building the database Use the correct set_default_locale() before building the database
for the first time, as this locale will be written on all new records. for the first time, as this locale will be written on all new records.
#### Setting the default locale #### Setting the default locale
Important: If the "default language" of your site is not english (en_US), <div class="notice" markdown='1'>
please ensure to set the appropriate default language for **Important:** If the "default language" of your site is not english (en_US), please ensure to set the appropriate default
your content before building the database with Translatable enabled: language for your content before building the database with Translatable enabled
</div>
Example:
:::php :::php
Translatable::set_default_locale(<locale>); Translatable::set_default_locale(<locale>);
@ -98,7 +101,7 @@ Getting a translation for an existing instance:
Getting translations through Translatable::set_reading_locale(). Getting translations through Translatable::set_reading_locale().
This is *not* a recommended approach, but sometimes inavoidable (e.g. for Versioned methods). This is *not* a recommended approach, but sometimes unavoidable (e.g. for `[api:Versioned]` methods).
:::php :::php
$origLocale = Translatable::get_reading_locale(); $origLocale = Translatable::get_reading_locale();
@ -117,7 +120,7 @@ Creating a translation:
### Usage for SiteTree ### Usage for SiteTree
Translatable can be used for subclasses of SiteTree as well. `[api:Translatable]` can be used for subclasses of SiteTree as well.
If a child page translation is requested without the parent If a child page translation is requested without the parent
page already having a translation in this language, the extension page already having a translation in this language, the extension
will recursively create translations up the tree. will recursively create translations up the tree.
@ -126,7 +129,7 @@ languages by auto-appending the language code at the end.
You'll need to ensure that the appropriate "reading language" is set You'll need to ensure that the appropriate "reading language" is set
before showing links to other pages on a website through $_GET['locale']. before showing links to other pages on a website through $_GET['locale'].
Pages in different languages can have different publication states Pages in different languages can have different publication states
through the Versioned extension. through the `[api:Versioned]` extension.
Note: You can't get Children() for a parent page in a different language Note: You can't get Children() for a parent page in a different language
through set_reading_locale(). Get the translated parent first. through set_reading_locale(). Get the translated parent first.
@ -145,13 +148,13 @@ through set_reading_locale(). Get the translated parent first.
### Translating custom properties ### Translating custom properties
Keep in mind that the Translatable extension currently doesn't support the exclusion of properties from being translated Keep in mind that the `[api:Translatable]` extension currently doesn't support the exclusion of properties from being
- all custom properties will automatically be fetched from their translated record on the database. This means you don't translated - all custom properties will automatically be fetched from their translated record on the database. This means
have to explicitly mark any custom properties as being translatable. you don't have to explicitly mark any custom properties as being translatable.
The Translatable decorator applies only to the getCMSFields() method on DataObject or SiteTree, not to any fields added The `[api:Translatable]` decorator applies only to the getCMSFields() method on DataObject or SiteTree, not to any fields
in overloaded getCMSFields() implementations. See Translatable->updateCMSFields() for details. By default, custom fields added in overloaded getCMSFields() implementations. See Translatable->updateCMSFields() for details. By default, custom
in the CMS won't show an original readonly value on a translated record, although they will save correctly. You can fields in the CMS won't show an original readonly value on a translated record, although they will save correctly. You can
attach this behaviour to custom fields by using Translatable_Transformation as shown below. attach this behaviour to custom fields by using Translatable_Transformation as shown below.
:::php :::php
@ -195,7 +198,7 @@ URL, add a "locale" GET parameter. The German homepage would also be accessible
For this to work, please ensure that the translated homepage is a direct translation of the default homepage, and not a For this to work, please ensure that the translated homepage is a direct translation of the default homepage, and not a
new page created through "Create page...". new page created through "Create page...".
### Translationgroups ### Translation groups
Each translation can have an associated "master" object in another language which it is based on, Each translation can have an associated "master" object in another language which it is based on,
as defined by the "MasterTranslationID" property. This relation is optional, meaning you can as defined by the "MasterTranslationID" property. This relation is optional, meaning you can
@ -223,26 +226,33 @@ SiteTree_translationgroups database table
### CharacterSets ### CharacterSets
Caution: Does not apply any character-set conversion, it is assumed that all content <div class="warning" markdown='1'>
**Caution:** Does not apply any character-set conversion, it is assumed that all content
is stored and represented in UTF-8 (Unicode). Please make sure your database and is stored and represented in UTF-8 (Unicode). Please make sure your database and
HTML-templates adjust to this. HTML-templates adjust to this.
</div>
### "Default"languages ### "Default" languages
Important: If the "default language" of your site is not english (en_US), <div class="warning" markdown='1'>
**Important:** If the "default language" of your site is not english (en_US),
please ensure to set the appropriate default language for please ensure to set the appropriate default language for
your content before building the database with Translatable enabled: your content before building the database with Translatable enabled
</div>
Example:
:::php :::php
Translatable::set_default_locale(<locale>); Translatable::set_default_locale(<locale>);
### Locales and languagetags ### Locales and language tags
For the Translatable class, a "locale" consists of a language code plus a region code separated by an underscore, For the Translatable class, a "locale" consists of a language code plus a region code separated by an underscore,
for example "de_AT" for German language ("de") in the region Austria ("AT"). for example "de_AT" for German language ("de") in the region Austria ("AT").
See http://www.w3.org/International/articles/language-tags/ for a detailed description. See [http://www.w3.org/International/articles/language-tags/](http://www.w3.org/International/articles/language-tags/)
for a detailed description.
Uninstalling/Disabling Uninstalling/Disabling
@ -257,9 +267,9 @@ in the database.
### Switching languages ### Switching languages
A widget now exists to switch between languages, and is [available A widget now exists to switch between languages, and is [available here](http://www.silverstripe.org/Language-Chooser-Widget/).
here](http://www.silverstripe.org/Language-Chooser-Widget/). You can easily make your own switchers with the following You can easily make your own switchers with the following basic tools. To stay friendly to caches and search engines, each
basic tools. To stay friendly to caches and search engines, each translation of a page must have a unique URL translation of a page must have a unique URL.
By URL: By URL:
@ -284,7 +294,7 @@ By default, SilverStripe core doesn't provide any switching of languages through
SEO-friendly CMS, it contains all this information in the URL. Each page in SilverStripe is aware of its translations SEO-friendly CMS, it contains all this information in the URL. Each page in SilverStripe is aware of its translations
through the *getTranslations()* method. We can use this method in our template to build a simple language switcher. It through the *getTranslations()* method. We can use this method in our template to build a simple language switcher. It
shows all available translations in an unordered list with links to the same page in a different language. The example shows all available translations in an unordered list with links to the same page in a different language. The example
below can be inserted in any of your templates, for example *themes/blackcandy/templates/Layout/Page.ss*. below can be inserted in any of your templates, for example `themes/blackcandy/templates/Layout/Page.ss`.
:::php :::php
<% if Translations %> <% if Translations %>
@ -306,9 +316,10 @@ just work if your locale value is registered in i18n::get_common_locales().
### Page-control ### Page-control
If you want to put static links in your template, which link to a site by their url, normally you can use the <% control If you want to put static links in your template, which link to a site by their url, normally you can use the `<% control
Page(page-url) %>. For sites which use Translatable, this is not possible for more than one language, because the url's Page(page-url) %>`. For sites which use Translatable, this is not possible for more than one language, because the url's
of different pages differ. of different pages differ.
For this case place the following function in your Page_Controller: For this case place the following function in your Page_Controller:
:::php :::php
@ -328,7 +339,8 @@ So, for example if you have a german page "Kontakt", which should be translated
<% control PageByLang(Kontakt,de_DE) %> <% control PageByLang(Kontakt,de_DE) %>
The control displays the link in the right language, depending on the current locale.\\ The control displays the link in the right language, depending on the current locale.
Example: Example:
<% control PageByLang(Kontakt,de_DE) %> <% control PageByLang(Kontakt,de_DE) %>
@ -349,11 +361,11 @@ files, you'll need to [set the i18n locale](/topics/translation#setting_the_i18n
(The reasoning is as follows: Translatable doesn't set the i18n locale. Historically these were two separate systems, (The reasoning is as follows: Translatable doesn't set the i18n locale. Historically these were two separate systems,
but they're reasonably interchangeable for a front-end website. The distinction is mainly valid for the CMS, because you but they're reasonably interchangeable for a front-end website. The distinction is mainly valid for the CMS, because you
want the CMS to be in English (i18n), but edit pages in different languages (Translatable).) want the CMS to be in English (`[api:i18n]`), but edit pages in different languages (`[api:Translatable]`).)
### Migrating from 2.1 datamodel ### Migrating from 2.1 datamodel
The datamodel of Translatable changed significantly between its original release in SilverStripe 2.1 and SilverStripe The datamodel of `[api:Translatable]` changed significantly between its original release in SilverStripe 2.1 and SilverStripe
2.3.2. See our [discussion on the 2.3.2. See our [discussion on the
mailinglist](http://groups.google.com/group/silverstripe-dev/browse_thread/thread/91e26e1f78d3c1b4/bd276dd5bbc56283?lnk=gst&q=translatable#bd276dd5bbc56283). mailinglist](http://groups.google.com/group/silverstripe-dev/browse_thread/thread/91e26e1f78d3c1b4/bd276dd5bbc56283?lnk=gst&q=translatable#bd276dd5bbc56283).
@ -362,8 +374,8 @@ To migrate a database that was built with SilverStripe 2.1.x or 2.2.x, follow th
* Upgrade your SilverStripe installation to at least 2.3.2 (see [upgrading](/installation/upgrading)) * Upgrade your SilverStripe installation to at least 2.3.2 (see [upgrading](/installation/upgrading))
* Backup your database content * Backup your database content
* Login as an administrator * Login as an administrator
* Run http://mysite.com/dev/build * Run `http://mysite.com/dev/build`
* Run http://mysite.com/dev/tasks/MigrateTranslatableTask * Run `http://mysite.com/dev/tasks/MigrateTranslatableTask`
Please see the `[api:MigrateTranslatableTask]` for Please see the `[api:MigrateTranslatableTask]` for
limitations of this migration task - not all your data will be preserved. limitations of this migration task - not all your data will be preserved.
@ -371,8 +383,8 @@ limitations of this migration task - not all your data will be preserved.
### Setting the i18n locale ### Setting the i18n locale
You can set the i18n locale value which is used to format dates, currencies and other regionally different values to the You can set the `[api:i18n]` locale value which is used to format dates, currencies and other regionally different values to
same as your current page locale. the same as your current page locale.
:::php :::php
class Page_Controller extends ContentController { class Page_Controller extends ContentController {
@ -388,8 +400,8 @@ same as your current page locale.
### Adding a new locale ### Adding a new locale
The i18n logic has lookup tables for common locales in i18n::$common_locales, which is a subset of i18n::$all_locales. The `[api:i18n]` logic has lookup tables for common locales in i18n::$common_locales, which is a subset of i18n::$all_locales.
If your locale is not present here, you can simply add it through mysite/_config.php: If your locale is not present here, you can simply add it through `mysite/_config.php`:
:::php :::php
i18n::$common_locales['de_AT'] = 'Deutsch (Oestereich)'; i18n::$common_locales['de_AT'] = 'Deutsch (Oestereich)';
@ -403,4 +415,4 @@ This should e.g. enable you to use `$Locale.Nice` in template code.
* [i18n](i18n): Developer-level documentation of Silverstripe's i18n capabilities * [i18n](i18n): Developer-level documentation of Silverstripe's i18n capabilities
* `[api:Translatable]`: DataObject-interface powering the website-content translations * `[api:Translatable]`: DataObject-interface powering the website-content translations
* ["Translatable ModelAdmin" module](http://silverstripe.org/translatablemodeladmin-module/): An extension which allows * ["Translatable ModelAdmin" module](http://silverstripe.org/translatablemodeladmin-module/): An extension which allows
translations of DataObjects inside `[api:ModelAdmin]` translations of `[api:DataObject]`s inside `[api:ModelAdmin]`

View File

@ -2,39 +2,46 @@
## Introduction ## Introduction
Widgets are small pieces of functionality such as showing the latest Comments or Flickr Photos. They normally display on [Widgets](http://silverstripe.org/widgets) are small pieces of functionality such as showing the latest Comments or Flickr Photos. They normally display on
the sidebar of your website. To check out a what a Widget can do watch the video http://silverstripe.org/widgets and try the sidebar of your website. To check out a what a [Widget](http://silverstripe.org/widgets) can do watch the
out the demo site http://silverstripe.com/assets/screencasts/SilverStripe-Blog-DragDrop-Widgets.swf [Widget video](http://silverstripe.com/assets/screencasts/SilverStripe-Blog-DragDrop-Widgets.swf) and try out the
[demo site](http://demo.silverstripe.org/)
## How to Use A Widget ## How to Use A Widget
### Downloading and Contributing Widgets ### Downloading and Contributing Widgets
* To download widgets visit [Widgets section](http://silverstripe.org/widgets) * To download widgets visit [Widgets section](http://silverstripe.org/widgets)
* Upload widgets you want to share to * Upload widgets you want to share to
[http://silverstripe.org/widgets/manage/add](http://silverstripe.org/widgets/manage/add). Make sure you read the [http://silverstripe.org/widgets/manage/add](http://silverstripe.org/widgets/manage/add). Make sure you read the
packaging instructions at the bottom of the page about how to make your widget package. packaging instructions at the bottom of the page about how to make your widget package.
### Installing a widget ### Installing a widget
By following the "Packaging" rules below, widgets are easily installed. By following the "Packaging" rules below, widgets are easily installed.
* Download the file and unzip to the main folder of your SilverStripe website, e.g. to "/widget_twitter/". The folder * Install the [blog module](http://www.silverstripe.org/blog-module/) (by default only the Blog has widgets enabled)
* Download the file and unzip to the main folder of your SilverStripe website, e.g. to `/widget_<widget-name>/`. The folder
will contain a few files, which generally won't need editing or reading. will contain a few files, which generally won't need editing or reading.
* Run dev/build * Run `http://my-website.com/dev/build`
* Login to the CMS and go to the 'Blog' page. Choose the "widgets" tab and drag n drop the new widget to activate it. * Login to the CMS and go to the 'Blog' page. Choose the "widgets" tab and drag n drop the new widget to activate it.
* Your blog will now have the widget shown. * Your blog will now have the widget shown
### Adding widgets to other pages ### Adding widgets to other pages
As of 2.2.1 this is this is a way to add widgets to other pages (by default only the Blog has widgets enabled). In the <div class="notice" markdown='1'>
future releases we will hopefully make widgets part of SiteTree therefore available on every page. In the mean time you As of version 2.2.1 there is a way to add widgets to other pages. In future releases we will hopefully make widgets part
have to do a couple things to get a Widget to work on a page. of SiteTree therefore available on every page.
</div>
You have to do a couple things to get a Widget to work on a page.
First step is to add an WidgetArea to the Database to store the widget details. Then you have to edit the CMS to add a First step is to add an WidgetArea to the Database to store the widget details. Then you have to edit the CMS to add a
Widget Form to manage the widgets. An example of this is below Widget Form to manage the widgets. An example of this is below
** mysite/code/Page.php ** **mysite/code/Page.php**
:::php :::php
class Page extends SiteTree { class Page extends SiteTree {
@ -53,18 +60,16 @@ Widget Form to manage the widgets. An example of this is below
} }
Then in your Template you need to call $SideBar whereever you want to render the widget Then in your Template you need to call $SideBar wherever you want to render the widget
Eg for blackcandy I put this above the closing `</div>` For example: using the blackcandy theme I put this piece of code above the closing `</div>`
** themes/myThemeName/templates/Includes/Sidebar.ss ** **themes/blackcandy/templates/Includes/Sidebar.ss**
:::ss :::ss
$Sidebar $Sidebar
## Writing your own widgets ## Writing your own widgets
To create a Widget you need at least three files - a php file containing the class, a template file of the same name and To create a Widget you need at least three files - a php file containing the class, a template file of the same name and
@ -180,7 +185,7 @@ To render the widget, simply include $SilverStripeFeed in your template:
As directed in the definition of SilverStripeFeed(), the Widget will be rendered through the WidgetHolder template. This As directed in the definition of SilverStripeFeed(), the Widget will be rendered through the WidgetHolder template. This
is pre-defined at /sapphire/templates/WidgetHolder.ss and simply consists of: is pre-defined at `/sapphire/templates/WidgetHolder.ss` and simply consists of:
:::ss :::ss
<div class="WidgetHolder"> <div class="WidgetHolder">
@ -190,14 +195,14 @@ is pre-defined at /sapphire/templates/WidgetHolder.ss and simply consists of:
You can override the WidgetHolder.ss and Widget.ss templates in your theme too by adding WidgetHolder and Widget You can override the WidgetHolder.ss and Widget.ss templates in your theme too by adding WidgetHolder and Widget
templates to ** themes/myThemeName/templates/Includes/ ** templates to `themes/myThemeName/templates/Includes/`
### Changing the title of your widget ### Changing the title of your widget
To change the title of your widget, you need to override the Title() method. By default, this simply returns the $title To change the title of your widget, you need to override the Title() method. By default, this simply returns the $title
variable. For example, to set your widgets title to 'Hello World!', you could use: variable. For example, to set your widgets title to 'Hello World!', you could use:
** widgets_yourWidget/YourWidgetWidget.php ** **widgets_yourWidget/YourWidgetWidget.php**
:::php :::php
function Title() { function Title() {
@ -221,13 +226,13 @@ This returns the value inputted in the CMS, if it's set or what is in the $title
### Forms within Widgets ### Forms within Widgets
*Requires SilverStripe 2.4 or newer* **Requires SilverStripe 2.4 or newer**
To implement a form inside a widget, you need to implement a custom controller for your widget to return this form. Make To implement a form inside a widget, you need to implement a custom controller for your widget to return this form. Make
sure that your controller follows the usual naming conventions, and it will be automatically picked up by the sure that your controller follows the usual naming conventions, and it will be automatically picked up by the
`[api:WidgetArea]` rendering in your *Page.ss* template. `[api:WidgetArea]` rendering in your *Page.ss* template.
*mysite/code/MyWidget.php* **mysite/code/MyWidget.php**
:::php :::php
class MyWidget extends Widget { class MyWidget extends Widget {
@ -258,16 +263,17 @@ sure that your controller follows the usual naming conventions, and it will be a
To output this form, modify your widget template. To output this form, modify your widget template.
*mysite/templates/MyWidget.ss* **mysite/templates/MyWidget.ss**
:::ss :::ss
$Content $Content
$MyFormName $MyFormName
<div class="notice" markdown='1'>
Note: The necessary controller actions are only present in subclasses of `[api:Page_Controller]`. To use **Note:** The necessary controller actions are only present in subclasses of `[api:Page_Controller]`. To use
widget forms in other controller subclasses, have a look at *ContentController->handleWidget()* and widget forms in other controller subclasses, have a look at *ContentController->handleWidget()* and
*ContentController::$url_handlers*. *ContentController::$url_handlers*.
</div>
See an [alternative recipe for SilverStripe 2.3 or earlier](http://doc.silverstripe.org/old/recipes/widget-forms-2.3). See an [alternative recipe for SilverStripe 2.3 or earlier](http://doc.silverstripe.org/old/recipes/widget-forms-2.3).
@ -275,9 +281,9 @@ See an [alternative recipe for SilverStripe 2.3 or earlier](http://doc.silverstr
If you currently have a blog installed, the widget fields are going to double up on those pages (as the blog extends the If you currently have a blog installed, the widget fields are going to double up on those pages (as the blog extends the
Page class). One way to fix this is to comment out line 30 in BlogHolder.php and remove the DB entry by running a Page class). One way to fix this is to comment out line 30 in BlogHolder.php and remove the DB entry by running a
/db/build. `http://www.mysite.com/db/build`.
** blog/code/BlogHolder.php ** **blog/code/BlogHolder.php**
:::php :::php
<?php <?php
@ -304,7 +310,7 @@ Then you can use the Widget area you defined on Page.php
### Packaging ### Packaging
For a widget to be put in our official widget database they must follow this convention - If the name of your widget was For a widget to be put in our official widget database they must follow this convention - If the name of your widget was
"TwitterWidget" then: "YourName" then:
#### File Structure for your widget #### File Structure for your widget
@ -323,19 +329,21 @@ would understand, then make it configurable in the _config.php file.
This way, the CMS remains an application designed for content authors, and not developers. This way, the CMS remains an application designed for content authors, and not developers.
** widget_name/_config.php ** *widget_name/_config.php*
:::php :::php
<?php /* */ ?> <?php /* */ ?>
** Example Widget Structure ** **Example Widget Structure**
![](_images/widget_demo.gif) ![](_images/widget_demo.gif)
#### How to make the Package #### How to make the Package
* Make a tar.gz file called widgets_flickr-0.1.tar.gz (where 0.1 is the version number). * Make a tar.gz file called widgets_YourName-0.1.tar.gz (where 0.1 is the version number).
* Ensure when you "unzip" the compressed file it has everything the "widgets_flickr" folder with everything inside * Ensure when you "unzip" the compressed file it has everything the "widgets_YourName" folder with everything inside
it. it.
* If made official, it will be given these locations at silverstripe.com: * If made official, it will be given these locations at silverstripe.com:
* SVN location: http://svn.silverstripe.com/open/modules/widgets/flickr/trunk * SVN location: http://svn.silverstripe.com/open/modules/widgets/flickr/trunk

View File

@ -627,7 +627,7 @@ a new *StaffHolder* called "Staff" in the "About Us" section, and create some *S
### Creating the staff section templates ### Creating the staff section templates
The staff section templates aren't too difficult to create, thanks to the utility methods provided by the *Image* class. The staff section templates aren't too difficult to create, thanks to the utility methods provided by the `[api:Image]` class.
**themes/tutorial/templates/Layout/StaffHolder.ss** **themes/tutorial/templates/Layout/StaffHolder.ss**
@ -650,7 +650,7 @@ The staff section templates aren't too difficult to create, thanks to the utilit
</div> </div>
This template is very similar to the *ArticleHolder* template. The *FirstSentence* method of the `[api:Text]` class This template is very similar to the *ArticleHolder* template. The *SetWidth* method of the `[api:Image]` class
will resize the image before sending it to the browser. The resized image is cached, so the server doesn't have to will resize the image before sending it to the browser. The resized image is cached, so the server doesn't have to
resize the image every time the page is viewed. resize the image every time the page is viewed.

View File

@ -47,7 +47,7 @@ instructions below as well.
If you are using SilverStripe 2.2 or earlier then you need to define your own code. The first step in implementing If you are using SilverStripe 2.2 or earlier then you need to define your own code. The first step in implementing
search on your site is to create a form for the user to type their query. Create a function named *SearchForm* on the search on your site is to create a form for the user to type their query. Create a function named *SearchForm* on the
*Page_Controller* class (//mysite/code/Page.php//). *Page_Controller* class (/mysite/code/Page.php).
:::php :::php
class Page_Controller extends ContentController { class Page_Controller extends ContentController {
@ -66,6 +66,69 @@ search on your site is to create a form for the user to type their query. Create
} }
} }
### Custom CSS code
Add the following css code to the *themes/tutorial/css/layout.css* file. This will style the header form and search
results page.
:::css
#Header form {
float:right;
width:160px;
margin:25px 25px 0px 25px;
}
#Header form * {
display:inline !important;
}
#Header form div {
}
#Header form input.text {
width:110px;
color:#000;
background:#f0f0f0;
border:1px solid #aaa;
padding:3px;
}
#Header form input.action {
font-weight:bold;
}
.searchResults h2 {
font-size:2.2em;
font-weight:normal;
color:#0083C8;
margin-bottom:15px;
}
.searchResults p.searchQuery {
color:#333;
margin-bottom:10px;
}
.searchResults ul#SearchResults li {
margin-bottom:20px;
}
ul#SearchResults p {
font-size:1.1em;
font-weight:normal;
line-height:2em;
color:#333;
}
ul#SearchResults a.searchResultHeader {
font-size:1.3em;
font-weight:bold;
color:#0083C8;
text-decoration:none;
margin:20px 0 8px 0;
padding-left:20px;
background:url(../images/treeicons/search-file.gif) no-repeat left center;
}
ul#SearchResults a {
text-decoration:none;
color:#0083C8;
}
ul#SearchResults a:hover {
border-bottom:1px dotted #0083C8;
}
## Adding the search form ## Adding the search form

View File

@ -7,6 +7,11 @@ to the *$db* array and how to add an image using the *$has_one* array and so cre
the *Image* table by storing the id of the respective *Image* in the first table. This tutorial explores all this the *Image* table by storing the id of the respective *Image* in the first table. This tutorial explores all this
relations between [DataObjects](/topics/datamodel#relations) and the way to manage them easily. relations between [DataObjects](/topics/datamodel#relations) and the way to manage them easily.
<div class="notice" markdown='1'>
I'm using the default tutorial theme in the following examples so the templates may vary or you may need to change
the template code in this example to fit your theme
</div>
## What are we working towards? ## What are we working towards?
To simulate these relations between objects, we are going to simulate the management via the CMS of the **[Google Summer To simulate these relations between objects, we are going to simulate the management via the CMS of the **[Google Summer
@ -79,9 +84,11 @@ The first step is to create the student and project objects.
function getCMSFields_forPopup() { function getCMSFields_forPopup() {
$fields = new FieldSet(); $fields = new FieldSet();
$fields->push( new TextField( 'FirstName', 'First Name' ) ); $fields->push( new TextField( 'FirstName', 'First Name' ) );
$fields->push( new TextField( 'Lastname' ) ); $fields->push( new TextField( 'Lastname' ) );
$fields->push( new TextField( 'Nationality' ) ); $fields->push( new TextField( 'Nationality' ) );
return $fields; return $fields;
} }
@ -262,6 +269,8 @@ The first step is to create the mentor object and set the relation with the *Stu
} }
class Mentor_Controller extends Page_Controller {} class Mentor_Controller extends Page_Controller {}
*tutorial/code/Student.php*
:::php :::php
class Student extends DataObject { class Student extends DataObject {
@ -279,6 +288,8 @@ respective *Mentor* in the *Student* table.
The second step is to add the table in the method *getCMSFields* which will allow you to manage the *has_many* relation. The second step is to add the table in the method *getCMSFields* which will allow you to manage the *has_many* relation.
*tutorial/code/Mentor.php*
:::php :::php
class Mentor extends Page { class Mentor extends Page {
@ -308,6 +319,7 @@ The second step is to add the table in the method *getCMSFields* which will allo
} }
} }
class Mentor_Controller extends Page_Controller {}
To know more about the parameters of the *HasManyComplexTableField* constructor, [check](#project_-_student_relation) To know more about the parameters of the *HasManyComplexTableField* constructor, [check](#project_-_student_relation)
those of the *HasOneComplexTableField* constructor. those of the *HasOneComplexTableField* constructor.
@ -356,6 +368,8 @@ The first step is to create the module object and set the relation with the *Pro
*tutorial/code/Module.php* *tutorial/code/Module.php*
:::php :::php
<?php
class Module extends DataObject { class Module extends DataObject {
static $db = array( static $db = array(
@ -374,6 +388,8 @@ The first step is to create the module object and set the relation with the *Pro
} }
*tutorial/code/Project.php*
:::php :::php
class Project extends Page { class Project extends Page {
@ -424,9 +440,10 @@ To know more about the parameters of the *ManyManyComplexTableField* constructor
[check](#project_-_student_relation) those of the *HasOneComplexTableField* [check](#project_-_student_relation) those of the *HasOneComplexTableField*
constructor. constructor.
Don't forget to rebuild the database using <div class="tip" markdown='1'>
[http://localhost:3000/db/build?flush=1](http://localhost:3000/db/build?flush=1) before you proceed to the next part of Don't forget to rebuild the database using *dev/build?flush=1* before you
this tutorial. proceed to the next part of this tutorial.
</div>
Select now one of the *Project* page, go in the tab panel *Modules* and add all the modules listed Select now one of the *Project* page, go in the tab panel *Modules* and add all the modules listed
[above](#what-are-we-working-towards) by clicking on the link **Add A [above](#what-are-we-working-towards) by clicking on the link **Add A
@ -459,7 +476,7 @@ We will see in this section how to display all these relations but also how to c
For every kind of *Page* or *DataObject*, you can access to their relations thanks to the **control** loop. For every kind of *Page* or *DataObject*, you can access to their relations thanks to the **control** loop.
**__1. GSOC Projects__** **1. GSOC Projects**
Let's start with the *ProjectsHolder* page created before. For this template, we are will display the same table than Let's start with the *ProjectsHolder* page created before. For this template, we are will display the same table than
[above](#what-are-we-working-towards). [above](#what-are-we-working-towards).
@ -469,18 +486,13 @@ Let's start with the *ProjectsHolder* page created before. For this template, we
*tutorial/templates/Layout/ProjectsHolder.ss* *tutorial/templates/Layout/ProjectsHolder.ss*
:::ss :::ss
<div class="typography"> <% include Menu2 %>
<% if Menu(2) %>
<% include SideBar %>
<div id="Content">
<% end_if %>
<div id="Content" class="typography">
<% if Level(2) %> <% if Level(2) %>
<% include BreadCrumbs %> <% include BreadCrumbs %>
<% end_if %> <% end_if %>
<h2>$Title</h2>
$Content $Content
<table> <table>
@ -535,28 +547,18 @@ Let's start with the *ProjectsHolder* page created before. For this template, we
</table> </table>
$Form $Form
$PageComments
<% if Menu(2) %>
</div>
<% end_if %>
</div> </div>
*tutorial/templates/Includes/SideBar.ss* <div class="notice" markdown='1'>
You might want to move the include above the typography div in your layouts to get rid of the bullets. If you are using the blackcandy template: You might want to move the `<% include Sidebar %>`
(tutorial/templates/Includes/SideBar.ss) include in the *tutorial/templates/Layout/Page.ss* template above
:::ss the typography div to get rid of the bullets
<% if Menu(2) %> </div>
<ul id="Menu2">
<% control Menu(2) %>
<li class="$LinkingMode"><a href="$Link" title="Go to the &quot;{$Title}&quot; page">$MenuTitle</a></li>
<% end_control %>
</ul>
<% end_if %>
**__2. Project__** **2. Project**
We know now how to easily access and show [relations](../topics/datamodel#relations) between *DataObject* in a template. We know now how to easily access and show [relations](../topics/datamodel#relations) between *DataObject* in a template.
@ -567,22 +569,15 @@ We can now do the same for every *Project* page by creating its own template.
*tutorial/templates/Layout/Project.ss* *tutorial/templates/Layout/Project.ss*
:::ss :::ss
<div class="typography"> <% include Menu2 %>
<% if Menu(2) %>
<% include SideBar %>
<div id="Content">
<% end_if %>
<div id="Content" class="typography">
<% if Level(2) %> <% if Level(2) %>
<% include BreadCrumbs %> <% include BreadCrumbs %>
<% end_if %> <% end_if %>
<h2>$Title</h2>
$Content $Content
<h3>Student</h3>
<% if MyStudent %> <% if MyStudent %>
<% control MyStudent %> <% control MyStudent %>
<p>First Name: <strong>$FirstName</strong></p> <p>First Name: <strong>$FirstName</strong></p>
@ -618,11 +613,7 @@ We can now do the same for every *Project* page by creating its own template.
<% end_if %> <% end_if %>
$Form $Form
$PageComments
<% if Menu(2) %>
</div>
<% end_if %>
</div> </div>
@ -653,7 +644,7 @@ from templates either within a control block or dot notation.
} }
We can now modify the *Project* template. We can now modify the *Project.ss* template.
:::ss :::ss
@ -677,7 +668,9 @@ We can now modify the *Project* template.
... ...
<div class="notice" markdown='1'>
Remember to add `?flush=1` to the url when refreshing the project page or otherwise you will get a template error
</div>
In the *Project* template, it has been really easy to display the **1-to-1** relation with a *Student* object just by In the *Project* template, it has been really easy to display the **1-to-1** relation with a *Student* object just by
calling the variable **$MyStudent**. This has been made possible thanks to the code below present in the *Project* calling the variable **$MyStudent**. This has been made possible thanks to the code below present in the *Project*
@ -692,7 +685,7 @@ class.
However, in the *Student* class, there is no any code relating to the **1-to-1** relation with a *Project* *Page*. So However, in the *Student* class, there is no any code relating to the **1-to-1** relation with a *Project* *Page*. So
how to access it from a *Student* *DataObject* ? how to access it from a *Student* *DataObject* ?
**__3. Mentor__** **3. Mentor**
In this template, we are gonna try to access the *Project* details from a *Student* *DataObject*. In this template, we are gonna try to access the *Project* details from a *Student* *DataObject*.
@ -722,18 +715,10 @@ That's how we can use this function in the *Mentor* template.
*tutorial/templates/Layout/Mentor.ss* *tutorial/templates/Layout/Mentor.ss*
:::ss :::ss
<div class="typography"> <% include Menu2 %>
<% if Menu(2) %>
<% include SideBar %>
<div id="Content">
<% end_if %>
<% if Level(2) %>
<% include BreadCrumbs %>
<% end_if %>
<h2>$Title</h2>
<div id="Content" class="typography">
<% include Breadcrumbs %>
$Content $Content
<h3>Personal Details</h3> <h3>Personal Details</h3>
@ -774,13 +759,7 @@ That's how we can use this function in the *Mentor* template.
<% end_if %> <% end_if %>
$Form $Form
$PageComments
<% if Menu(2) %>
</div> </div>
<% end_if %>
</div>
## Summary ## Summary
@ -791,4 +770,6 @@ CMS and how to display them on the website.
## Download the code ## Download the code
You can download all the [complete code](http://doc.silverstripe.org/src/github/master/sapphire/docs/en/tutorials/_images/tutorial5-completecode.zip) of this tutorial. Download all the [code](http://doc.silverstripe.org/src/github/master/sapphire/docs/en/tutorials/_images/tutorial5-completecode.zip) for this tutorial.
You can also download the [code](http://doc.silverstripe.org/src/github/master/sapphire/docs/en/tutorials/_images/tutorial5-completecode-blackcandy.zip) for use in the blackcandy template.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 50 KiB