mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
remove tabs
This commit is contained in:
parent
5d6b7c85d7
commit
50c8a02bff
@ -66,10 +66,10 @@ Due to some changes to `mod_dir` in [Apache 2.4](http://httpd.apache.org/docs/cu
|
|||||||
|
|
||||||
```
|
```
|
||||||
<IfModule mod_rewrite.c>
|
<IfModule mod_rewrite.c>
|
||||||
# Turn off index.php handling requests to the homepage fixes issue in apache >=2.4
|
# Turn off index.php handling requests to the homepage fixes issue in apache >=2.4
|
||||||
<IfModule mod_dir.c>
|
<IfModule mod_dir.c>
|
||||||
DirectoryIndex disabled
|
DirectoryIndex disabled
|
||||||
</IfModule>
|
</IfModule>
|
||||||
# ------ #
|
# ------ #
|
||||||
</IfModule>
|
</IfModule>
|
||||||
```
|
```
|
||||||
|
@ -15,10 +15,11 @@ If you can log-in to the CMS as an administrator, append `?isDev=1` to any URL t
|
|||||||
"dev mode". If you can't log-in in the first place because of the error, add this directive to your `mysite/_config/config.yml`
|
"dev mode". If you can't log-in in the first place because of the error, add this directive to your `mysite/_config/config.yml`
|
||||||
(don't forget to remove it afterwards!):
|
(don't forget to remove it afterwards!):
|
||||||
|
|
||||||
:::php
|
```yml
|
||||||
Director:
|
Director:
|
||||||
# temporary debugging statement
|
# temporary debugging statement
|
||||||
environment_type: 'dev'
|
environment_type: 'dev'
|
||||||
|
```
|
||||||
|
|
||||||
<div class="warning" markdown='1'>
|
<div class="warning" markdown='1'>
|
||||||
On "live" environments, the `?isDev=1` solution is preferred, as it means that your other visitors don't see ugly
|
On "live" environments, the `?isDev=1` solution is preferred, as it means that your other visitors don't see ugly
|
||||||
@ -31,10 +32,10 @@ Due to some changes to `mod_dir` in [Apache 2.4](http://httpd.apache.org/docs/cu
|
|||||||
|
|
||||||
```
|
```
|
||||||
<IfModule mod_rewrite.c>
|
<IfModule mod_rewrite.c>
|
||||||
# Turn off index.php handling requests to the homepage fixes issue in apache >=2.4
|
# Turn off index.php handling requests to the homepage fixes issue in apache >=2.4
|
||||||
<IfModule mod_dir.c>
|
<IfModule mod_dir.c>
|
||||||
DirectoryIndex disabled
|
DirectoryIndex disabled
|
||||||
</IfModule>
|
</IfModule>
|
||||||
# ------ #
|
# ------ #
|
||||||
</IfModule>
|
</IfModule>
|
||||||
```
|
```
|
||||||
@ -109,16 +110,16 @@ $path = dirname(__FILE__);
|
|||||||
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::SELF_FIRST);
|
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::SELF_FIRST);
|
||||||
$matched = false;
|
$matched = false;
|
||||||
foreach($files as $name => $file){
|
foreach($files as $name => $file){
|
||||||
if($file->getExtension() != 'php') continue;
|
if($file->getExtension() != 'php') continue;
|
||||||
if(preg_match('/thirdparty|vendor/',$file->getPathname())) continue;
|
if(preg_match('/thirdparty|vendor/',$file->getPathname())) continue;
|
||||||
$content = file_get_contents($file->getPathname());
|
$content = file_get_contents($file->getPathname());
|
||||||
if(preg_match('/^[[:blank:]]+<\?' . 'php/', $content)) {
|
if(preg_match('/^[[:blank:]]+<\?' . 'php/', $content)) {
|
||||||
echo sprintf("%s: Space before opening bracket\n", $file->getPathname());
|
echo sprintf("%s: Space before opening bracket\n", $file->getPathname());
|
||||||
$matched = true;
|
$matched = true;
|
||||||
}
|
}
|
||||||
if(preg_match('/^\?' . '>\n?[[:blank:]]+/m', $content)) {
|
if(preg_match('/^\?' . '>\n?[[:blank:]]+/m', $content)) {
|
||||||
echo sprintf("%s: Space after closing bracket\n", $file->getPathname());
|
echo sprintf("%s: Space after closing bracket\n", $file->getPathname());
|
||||||
$matched = true;
|
$matched = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -180,23 +180,23 @@ Full example of composer.json with the SSAutoGitIgnore installed and enabled
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"name": "silverstripe/installer",
|
"name": "silverstripe/installer",
|
||||||
"description": "The SilverStripe Framework Installer",
|
"description": "The SilverStripe Framework Installer",
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=5.5.0",
|
"php": ">=5.5.0",
|
||||||
"silverstripe/cms": "^4.0",
|
"silverstripe/cms": "^4.0",
|
||||||
"silverstripe/framework": "^4.0",
|
"silverstripe/framework": "^4.0",
|
||||||
"silverstripe-themes/simple": "~3.2.0"
|
"silverstripe-themes/simple": "~3.2.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"silverstripe/docsviewer": "^3.0",
|
"silverstripe/docsviewer": "^3.0",
|
||||||
"gdmedia/ss-auto-git-ignore": "^1.0"
|
"gdmedia/ss-auto-git-ignore": "^1.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"post-update-cmd": "GDM\\SSAutoGitIgnore\\UpdateScript::Go"
|
"post-update-cmd": "GDM\\SSAutoGitIgnore\\UpdateScript::Go"
|
||||||
},
|
},
|
||||||
"minimum-stability": "dev",
|
"minimum-stability": "dev",
|
||||||
"prefer-stable": true
|
"prefer-stable": true
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -48,9 +48,9 @@ database credentials could be the same. To do this, you can add this snippet to
|
|||||||
|
|
||||||
```php
|
```php
|
||||||
try {
|
try {
|
||||||
(new \Dotenv\Dotenv('/path/to/env/'))->load();
|
(new \Dotenv\Dotenv('/path/to/env/'))->load();
|
||||||
} catch (\Dotenv\Exception\InvalidPathException $e) {
|
} catch (\Dotenv\Exception\InvalidPathException $e) {
|
||||||
// no file found
|
// no file found
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -112,27 +112,27 @@ for a template file in the *simple/templates* folder, with the name `<PageType>`
|
|||||||
Open *themes/simple/templates/Page.ss*. It uses standard HTML apart from these exceptions:
|
Open *themes/simple/templates/Page.ss*. It uses standard HTML apart from these exceptions:
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
<% base_tag %>
|
<% base_tag %>
|
||||||
```
|
```
|
||||||
|
|
||||||
The base_tag variable is replaced with the HTML [base element](http://www.w3.org/TR/html401/struct/links.html#h-12.4). This
|
The base_tag variable is replaced with the HTML [base element](http://www.w3.org/TR/html401/struct/links.html#h-12.4). This
|
||||||
ensures the browser knows where to locate your site's images and css files.
|
ensures the browser knows where to locate your site's images and css files.
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
$Title
|
$Title
|
||||||
$SiteConfig.Title
|
$SiteConfig.Title
|
||||||
```
|
```
|
||||||
|
|
||||||
These two variables are found within the html `<title>` tag, and are replaced by the "Page Name" and "Settings -> Site Title" fields in the CMS.
|
These two variables are found within the html `<title>` tag, and are replaced by the "Page Name" and "Settings -> Site Title" fields in the CMS.
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
$MetaTags
|
$MetaTags
|
||||||
```
|
```
|
||||||
|
|
||||||
The MetaTags variable will add meta tags, which are used by search engines. You can define your meta tags in the tab fields at the bottom of the content editor in the CMS.
|
The MetaTags variable will add meta tags, which are used by search engines. You can define your meta tags in the tab fields at the bottom of the content editor in the CMS.
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
$Layout
|
$Layout
|
||||||
```
|
```
|
||||||
|
|
||||||
The Layout variable is replaced with the contents of a template file with the same name as the page type we are using.
|
The Layout variable is replaced with the contents of a template file with the same name as the page type we are using.
|
||||||
@ -140,7 +140,7 @@ The Layout variable is replaced with the contents of a template file with the sa
|
|||||||
Open *themes/simple/templates/Layout/Page.ss*. You will see more HTML and more SilverStripe template replacement tags and variables.
|
Open *themes/simple/templates/Layout/Page.ss*. You will see more HTML and more SilverStripe template replacement tags and variables.
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
$Content
|
$Content
|
||||||
```
|
```
|
||||||
|
|
||||||
The Content variable is replaced with the content of the page currently being viewed. This allows you to make all changes to
|
The Content variable is replaced with the content of the page currently being viewed. This allows you to make all changes to
|
||||||
@ -151,7 +151,7 @@ browser and are either prefixed with a dollar sign ($)
|
|||||||
or placed between SilverStripe template tags:
|
or placed between SilverStripe template tags:
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
<% %>
|
<% %>
|
||||||
```
|
```
|
||||||
|
|
||||||
**Flushing the cache**
|
**Flushing the cache**
|
||||||
@ -169,7 +169,7 @@ Open up *themes/simple/templates/Includes/Navigation.ss*
|
|||||||
The Menu for our site is created using a **loop**. Loops allow us to iterate over a data set, and render each item using a sub-template.
|
The Menu for our site is created using a **loop**. Loops allow us to iterate over a data set, and render each item using a sub-template.
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
<% loop $Menu(1) %>
|
<% loop $Menu(1) %>
|
||||||
```
|
```
|
||||||
|
|
||||||
returns a set of first level menu items. We can then use the template variable
|
returns a set of first level menu items. We can then use the template variable
|
||||||
@ -179,13 +179,13 @@ returns a set of first level menu items. We can then use the template variable
|
|||||||
|
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
<ul>
|
<ul>
|
||||||
<% loop $Menu(1) %>
|
<% loop $Menu(1) %>
|
||||||
<li class="<% if $isCurrent %>current<% else_if $isSection %>section<% end_if %>">
|
<li class="<% if $isCurrent %>current<% else_if $isSection %>section<% end_if %>">
|
||||||
<a href="$Link" title="$Title.XML">$MenuTitle.XML</a>
|
<a href="$Link" title="$Title.XML">$MenuTitle.XML</a>
|
||||||
</li>
|
</li>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</ul>
|
</ul>
|
||||||
```
|
```
|
||||||
|
|
||||||
Here we've created an unordered list called *Menu1*, which *themes/simple/css/layout.css* will style into the menu.
|
Here we've created an unordered list called *Menu1*, which *themes/simple/css/layout.css* will style into the menu.
|
||||||
@ -204,15 +204,15 @@ A useful feature is highlighting the current page the user is looking at. We can
|
|||||||
For example, if you were here: "Home > Company > Staff > Bob Smith", you may want to highlight 'Company' to say you are in that section.
|
For example, if you were here: "Home > Company > Staff > Bob Smith", you may want to highlight 'Company' to say you are in that section.
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
<li class="<% if $isCurrent %>current<% else_if $isSection %>section<% end_if %>">
|
<li class="<% if $isCurrent %>current<% else_if $isSection %>section<% end_if %>">
|
||||||
<a href="$Link" title="$Title.XML">$MenuTitle.XML</a>
|
<a href="$Link" title="$Title.XML">$MenuTitle.XML</a>
|
||||||
</li>
|
</li>
|
||||||
```
|
```
|
||||||
|
|
||||||
you will then be able to target a section in css (*simple/css/layout.css*), e.g.:
|
you will then be able to target a section in css (*simple/css/layout.css*), e.g.:
|
||||||
|
|
||||||
```css
|
```css
|
||||||
.section { background:#ccc; }
|
.section { background:#ccc; }
|
||||||
```
|
```
|
||||||
|
|
||||||
## A second level of navigation
|
## A second level of navigation
|
||||||
@ -235,16 +235,16 @@ Great, we now have a hierarchical site structure! Let's look at how this is crea
|
|||||||
Adding a second level menu is very similar to adding the first level menu. Open up */themes/simple/templates/Includes/Sidebar.ss* template and look at the following code:
|
Adding a second level menu is very similar to adding the first level menu. Open up */themes/simple/templates/Includes/Sidebar.ss* template and look at the following code:
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
<ul>
|
<ul>
|
||||||
<% loop $Menu(2) %>
|
<% loop $Menu(2) %>
|
||||||
<li class="<% if $isCurrent %>current<% else_if $isSection %>section<% end_if %>">
|
<li class="<% if $isCurrent %>current<% else_if $isSection %>section<% end_if %>">
|
||||||
<a href="$Link" title="Go to the $Title.XML page">
|
<a href="$Link" title="Go to the $Title.XML page">
|
||||||
<span class="arrow">→</span>
|
<span class="arrow">→</span>
|
||||||
<span class="text">$MenuTitle.XML</span>
|
<span class="text">$MenuTitle.XML</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</ul>
|
</ul>
|
||||||
```
|
```
|
||||||
|
|
||||||
This should look very familiar. It is the same idea as our first menu, except the loop block now uses *Menu(2)* instead of *Menu(1)*.
|
This should look very familiar. It is the same idea as our first menu, except the loop block now uses *Menu(2)* instead of *Menu(1)*.
|
||||||
@ -257,20 +257,20 @@ Look again in the *Sidebar.ss* file and you will see that the menu is surrounded
|
|||||||
like this:
|
like this:
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
<% if $Menu(2) %>
|
<% if $Menu(2) %>
|
||||||
...
|
...
|
||||||
<ul>
|
<ul>
|
||||||
<% loop $Menu(2) %>
|
<% loop $Menu(2) %>
|
||||||
<li class="<% if $isCurrent %>current<% else_if $isSection %>section<% end_if %>">
|
<li class="<% if $isCurrent %>current<% else_if $isSection %>section<% end_if %>">
|
||||||
<a href="$Link" title="Go to the $Title.XML page">
|
<a href="$Link" title="Go to the $Title.XML page">
|
||||||
<span class="arrow">→</span>
|
<span class="arrow">→</span>
|
||||||
<span class="text">$MenuTitle.XML</span>
|
<span class="text">$MenuTitle.XML</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</ul>
|
</ul>
|
||||||
...
|
...
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
```
|
```
|
||||||
|
|
||||||
The if block only includes the code inside it if the condition is true. In this case, it checks for the existence of
|
The if block only includes the code inside it if the condition is true. In this case, it checks for the existence of
|
||||||
@ -282,11 +282,11 @@ Now that we have two levels of navigation, it would also be useful to include so
|
|||||||
Open up */themes/simple/templates/Includes/BreadCrumbs.ss* template and look at the following code:
|
Open up */themes/simple/templates/Includes/BreadCrumbs.ss* template and look at the following code:
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
<% if $Level(2) %>
|
<% if $Level(2) %>
|
||||||
<div id="Breadcrumbs">
|
<div id="Breadcrumbs">
|
||||||
$Breadcrumbs
|
$Breadcrumbs
|
||||||
</div>
|
</div>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
```
|
```
|
||||||
|
|
||||||
Breadcrumbs are only useful on pages that aren't in the top level. We can ensure that we only show them if we aren't in
|
Breadcrumbs are only useful on pages that aren't in the top level. We can ensure that we only show them if we aren't in
|
||||||
@ -295,7 +295,7 @@ the top level with another if statement.
|
|||||||
The *Level* page control allows you to get data from the page's parents, e.g. if you used *Level(1)*, you could use:
|
The *Level* page control allows you to get data from the page's parents, e.g. if you used *Level(1)*, you could use:
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
$Level(1).Title
|
$Level(1).Title
|
||||||
```
|
```
|
||||||
|
|
||||||
to get the top level page title. In this case, we merely use it to check the existence of a second level page: if one exists then we include breadcrumbs.
|
to get the top level page title. In this case, we merely use it to check the existence of a second level page: if one exists then we include breadcrumbs.
|
||||||
@ -309,25 +309,25 @@ Feel free to experiment with the if and loop statements. For example, you could
|
|||||||
The following example runs an if statement and a loop on *Children*, checking to see if any sub-pages exist within each top level navigation item. You will need to come up with your own CSS to correctly style this approach.
|
The following example runs an if statement and a loop on *Children*, checking to see if any sub-pages exist within each top level navigation item. You will need to come up with your own CSS to correctly style this approach.
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
<ul>
|
<ul>
|
||||||
<% loop $Menu(1) %>
|
<% loop $Menu(1) %>
|
||||||
<li class="<% if $isCurrent %>current<% else_if $isSection %>section<% end_if %>">
|
<li class="<% if $isCurrent %>current<% else_if $isSection %>section<% end_if %>">
|
||||||
<a href="$Link" title="$Title.XML">$MenuTitle.XML</a>
|
<a href="$Link" title="$Title.XML">$MenuTitle.XML</a>
|
||||||
<% if $Children %>
|
<% if $Children %>
|
||||||
<ul>
|
<ul>
|
||||||
<% loop $Children %>
|
<% loop $Children %>
|
||||||
<li class="<% if $isCurrent %>current<% else_if $isSection %>section<% end_if %>">
|
<li class="<% if $isCurrent %>current<% else_if $isSection %>section<% end_if %>">
|
||||||
<a href="$Link" title="Go to the $Title.XML page">
|
<a href="$Link" title="Go to the $Title.XML page">
|
||||||
<span class="arrow">→</span>
|
<span class="arrow">→</span>
|
||||||
<span class="text">$MenuTitle.XML</span>
|
<span class="text">$MenuTitle.XML</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</ul>
|
</ul>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
</li>
|
</li>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</ul>
|
</ul>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -393,21 +393,21 @@ To create a new template layout, create a copy of *Page.ss* (found in *themes/si
|
|||||||
First, we don't need the breadcrumbs and the secondary menu for the homepage. Let's remove them:
|
First, we don't need the breadcrumbs and the secondary menu for the homepage. Let's remove them:
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
<% include SideBar %>
|
<% include SideBar %>
|
||||||
```
|
```
|
||||||
|
|
||||||
We'll also replace the title text with an image. Find this line:
|
We'll also replace the title text with an image. Find this line:
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
<h1>$Title</h1>
|
<h1>$Title</h1>
|
||||||
```
|
```
|
||||||
|
|
||||||
and replace it with:
|
and replace it with:
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
<div id="Banner">
|
<div id="Banner">
|
||||||
<img src="http://www.silverstripe.org/assets/SilverStripe-200.png" alt="Homepage image" />
|
<img src="http://www.silverstripe.org/assets/SilverStripe-200.png" alt="Homepage image" />
|
||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
Your Home page should now look like this:
|
Your Home page should now look like this:
|
||||||
|
@ -68,21 +68,21 @@ We'll start with the *ArticlePage* page type. First we create the model, a class
|
|||||||
**mysite/code/ArticlePage.php**
|
**mysite/code/ArticlePage.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use Page;
|
use Page;
|
||||||
|
|
||||||
class ArticlePage extends Page
|
class ArticlePage extends Page
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
**mysite/code/ArticlePageController.php**
|
**mysite/code/ArticlePageController.php**
|
||||||
```php
|
```php
|
||||||
use PageController;
|
use PageController;
|
||||||
|
|
||||||
class ArticlePageController extends PageController
|
class ArticlePageController extends PageController
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -95,21 +95,21 @@ Let's create the *ArticleHolder* page type.
|
|||||||
**mysite/code/ArticleHolder.php**
|
**mysite/code/ArticleHolder.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use Page;
|
use Page;
|
||||||
|
|
||||||
class ArticleHolder extends Page
|
class ArticleHolder extends Page
|
||||||
{
|
{
|
||||||
private static $allowed_children = ['ArticlePage'];
|
private static $allowed_children = ['ArticlePage'];
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
**mysite/code/ArticleHolderController.php**
|
**mysite/code/ArticleHolderController.php**
|
||||||
```php
|
```php
|
||||||
use PageController;
|
use PageController;
|
||||||
|
|
||||||
class ArticleHolderController extends PageController
|
class ArticleHolderController extends PageController
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Here we have done something interesting: the *$allowed_children* field. This is one of a number of static fields we can define to change the properties of a page type. The *$allowed_children* field is an array of page types that are allowed
|
Here we have done something interesting: the *$allowed_children* field. This is one of a number of static fields we can define to change the properties of a page type. The *$allowed_children* field is an array of page types that are allowed
|
||||||
@ -132,17 +132,17 @@ the $db array to add extra fields to the database. It would be nice to know when
|
|||||||
it. Add a *$db* property definition in the *ArticlePage* class:
|
it. Add a *$db* property definition in the *ArticlePage* class:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use Page;
|
use Page;
|
||||||
|
|
||||||
class ArticlePage extends Page
|
class ArticlePage extends Page
|
||||||
{
|
{
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Date' => 'Date',
|
'Date' => 'Date',
|
||||||
'Author' => 'Text'
|
'Author' => 'Text'
|
||||||
];
|
];
|
||||||
|
|
||||||
// .....
|
// .....
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Every entry in the array is a *key => value* pair. The **key** is the name of the field, and the **value** is the type. See ["data types and casting"](/developer_guides/model/data_types_and_casting) for a complete list of types.
|
Every entry in the array is a *key => value* pair. The **key** is the name of the field, and the **value** is the type. See ["data types and casting"](/developer_guides/model/data_types_and_casting) for a complete list of types.
|
||||||
@ -157,33 +157,33 @@ When we rebuild the database, we will see that the *ArticlePage* table has been
|
|||||||
To add our new fields to the CMS we have to override the *getCMSFields()* method, which is called by the CMS when it creates the form to edit a page. Add the method to the *ArticlePage* class.
|
To add our new fields to the CMS we have to override the *getCMSFields()* method, which is called by the CMS when it creates the form to edit a page. Add the method to the *ArticlePage* class.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use Page;
|
use Page;
|
||||||
|
|
||||||
class ArticlePage extends Page
|
class ArticlePage extends Page
|
||||||
{
|
{
|
||||||
// ...
|
// ...
|
||||||
|
|
||||||
public function getCMSFields()
|
public function getCMSFields()
|
||||||
{
|
{
|
||||||
$fields = parent::getCMSFields();
|
$fields = parent::getCMSFields();
|
||||||
|
|
||||||
$dateField = new DateField('Date');
|
$dateField = new DateField('Date');
|
||||||
$dateField->setConfig('showcalendar', true);
|
$dateField->setConfig('showcalendar', true);
|
||||||
$fields->addFieldToTab('Root.Main', $dateField, 'Content');
|
$fields->addFieldToTab('Root.Main', $dateField, 'Content');
|
||||||
$fields->addFieldToTab('Root.Main', new TextField('Author'), 'Content');
|
$fields->addFieldToTab('Root.Main', new TextField('Author'), 'Content');
|
||||||
|
|
||||||
return $fields;
|
return $fields;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Let's walk through this method.
|
Let's walk through this method.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$fields = parent::getCMSFields();
|
$fields = parent::getCMSFields();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -191,8 +191,8 @@ Firstly, we get the fields from the parent class; we want to add fields, not rep
|
|||||||
returned is a [FieldList](api:SilverStripe\Forms\FieldList) object.
|
returned is a [FieldList](api:SilverStripe\Forms\FieldList) object.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$fields->addFieldToTab('Root.Main', new TextField('Author'), 'Content');
|
$fields->addFieldToTab('Root.Main', new TextField('Author'), 'Content');
|
||||||
$fields->addFieldToTab('Root.Main', new DateField('Date'), 'Content');
|
$fields->addFieldToTab('Root.Main', new DateField('Date'), 'Content');
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -208,7 +208,7 @@ We have added two fields: A simple [TextField](api:SilverStripe\Forms\TextField)
|
|||||||
There are many more fields available in the default installation, listed in ["form field types"](/developer_guides/forms/field_types/common_subclasses).
|
There are many more fields available in the default installation, listed in ["form field types"](/developer_guides/forms/field_types/common_subclasses).
|
||||||
|
|
||||||
```php
|
```php
|
||||||
return $fields;
|
return $fields;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -227,50 +227,50 @@ To make the date field a bit more user friendly, you can add a dropdown calendar
|
|||||||
the date field will have the date format defined by your locale.
|
the date field will have the date format defined by your locale.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use Page;
|
use Page;
|
||||||
|
|
||||||
class ArticlePage extends Page
|
class ArticlePage extends Page
|
||||||
{
|
{
|
||||||
|
|
||||||
// .....
|
// .....
|
||||||
|
|
||||||
public function getCMSFields()
|
public function getCMSFields()
|
||||||
{
|
{
|
||||||
$fields = parent::getCMSFields();
|
$fields = parent::getCMSFields();
|
||||||
|
|
||||||
$dateField = new DateField('Date', 'Article Date (for example: 20/12/2010)');
|
$dateField = new DateField('Date', 'Article Date (for example: 20/12/2010)');
|
||||||
$dateField->setConfig('showcalendar', true);
|
$dateField->setConfig('showcalendar', true);
|
||||||
$dateField->setConfig('dateformat', 'dd/MM/YYYY');
|
$dateField->setConfig('dateformat', 'dd/MM/YYYY');
|
||||||
|
|
||||||
$fields->addFieldToTab('Root.Main', $dateField, 'Content');
|
$fields->addFieldToTab('Root.Main', $dateField, 'Content');
|
||||||
$fields->addFieldToTab('Root.Main', new TextField('Author', 'Author Name'), 'Content');
|
$fields->addFieldToTab('Root.Main', new TextField('Author', 'Author Name'), 'Content');
|
||||||
|
|
||||||
return $fields;
|
return $fields;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Let's walk through these changes.
|
Let's walk through these changes.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$dateField = new DateField('Date', 'Article Date (for example: 20/12/2010)');
|
$dateField = new DateField('Date', 'Article Date (for example: 20/12/2010)');
|
||||||
```
|
```
|
||||||
|
|
||||||
*$dateField* is declared in order to change the configuration of the DateField.
|
*$dateField* is declared in order to change the configuration of the DateField.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$dateField->setConfig('showcalendar', true);
|
$dateField->setConfig('showcalendar', true);
|
||||||
```
|
```
|
||||||
|
|
||||||
By enabling *showCalendar* you show a calendar overlay when clicking on the field.
|
By enabling *showCalendar* you show a calendar overlay when clicking on the field.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$dateField->setConfig('dateformat', 'dd/MM/YYYY');
|
$dateField->setConfig('dateformat', 'dd/MM/YYYY');
|
||||||
```
|
```
|
||||||
|
|
||||||
*dateFormat* allows you to specify how you wish the date to be entered and displayed in the CMS field. See the [DBDateField](api:SilverStripe\ORM\FieldType\DBDateField) documentation for more configuration options.
|
*dateFormat* allows you to specify how you wish the date to be entered and displayed in the CMS field. See the [DBDateField](api:SilverStripe\ORM\FieldType\DBDateField) documentation for more configuration options.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$fields->addFieldToTab('Root.Main', new TextField('Author', 'Author Name'), 'Content');
|
$fields->addFieldToTab('Root.Main', new TextField('Author', 'Author Name'), 'Content');
|
||||||
```
|
```
|
||||||
|
|
||||||
By default the field name *'Date'* or *'Author'* is shown as the title, however this might not be that helpful so to change the title, add the new title as the second argument.
|
By default the field name *'Date'* or *'Author'* is shown as the title, however this might not be that helpful so to change the title, add the new title as the second argument.
|
||||||
@ -289,19 +289,19 @@ First, the template for displaying a single article:
|
|||||||
**themes/simple/templates/Layout/ArticlePage.ss**
|
**themes/simple/templates/Layout/ArticlePage.ss**
|
||||||
|
|
||||||
|
|
||||||
:::ss
|
```ss
|
||||||
<% include SideBar %>
|
<% include SideBar %>
|
||||||
<div class="content-container unit size3of4 lastUnit">
|
<div class="content-container unit size3of4 lastUnit">
|
||||||
<article>
|
<article>
|
||||||
<h1>$Title</h1>
|
<h1>$Title</h1>
|
||||||
<div class="news-details">
|
<div class="news-details">
|
||||||
<p>Posted on $Date.Nice by $Author</p>
|
<p>Posted on $Date.Nice by $Author</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">$Content</div>
|
<div class="content">$Content</div>
|
||||||
</article>
|
</article>
|
||||||
$Form
|
$Form
|
||||||
</div>
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
Most of the code is just like the regular Page.ss, we include an informational div with the date and the author of the Article.
|
Most of the code is just like the regular Page.ss, we include an informational div with the date and the author of the Article.
|
||||||
|
|
||||||
@ -322,24 +322,24 @@ We'll now create a template for the article holder. We want our news section to
|
|||||||
|
|
||||||
**themes/simple/templates/Layout/ArticleHolder.ss**
|
**themes/simple/templates/Layout/ArticleHolder.ss**
|
||||||
|
|
||||||
:::ss
|
```ss
|
||||||
<% include SideBar %>
|
<% include SideBar %>
|
||||||
<div class="content-container unit size3of4 lastUnit">
|
<div class="content-container unit size3of4 lastUnit">
|
||||||
<article>
|
<article>
|
||||||
<h1>$Title</h1>
|
<h1>$Title</h1>
|
||||||
$Content
|
$Content
|
||||||
<div class="content">$Content</div>
|
<div class="content">$Content</div>
|
||||||
</article>
|
</article>
|
||||||
<% loop $Children %>
|
<% loop $Children %>
|
||||||
<article>
|
<article>
|
||||||
<h2><a href="$Link" title="Read more on "{$Title}"">$Title</a></h2>
|
<h2><a href="$Link" title="Read more on "{$Title}"">$Title</a></h2>
|
||||||
<p>$Content.FirstParagraph</p>
|
<p>$Content.FirstParagraph</p>
|
||||||
<a href="$Link" title="Read more on "{$Title}"">Read more >></a>
|
<a href="$Link" title="Read more on "{$Title}"">Read more >></a>
|
||||||
</article>
|
</article>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
$Form
|
$Form
|
||||||
</div>
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
Here we use the page control *Children*. As the name suggests, this control allows you to iterate over the children of a page. In this case, the children are our news articles. The *$Link* variable will give the address of the article which we can use to create a link, and the *FirstParagraph* function of the [DBHTMLText](api:SilverStripe\ORM\FieldType\DBHTMLText) field gives us a nice summary of the article. The function strips all tags from the paragraph extracted.
|
Here we use the page control *Children*. As the name suggests, this control allows you to iterate over the children of a page. In this case, the children are our news articles. The *$Link* variable will give the address of the article which we can use to create a link, and the *FirstParagraph* function of the [DBHTMLText](api:SilverStripe\ORM\FieldType\DBHTMLText) field gives us a nice summary of the article. The function strips all tags from the paragraph extracted.
|
||||||
|
|
||||||
@ -356,38 +356,39 @@ Cut the code between "loop Children" in *ArticleHolder.ss** and replace it with
|
|||||||
|
|
||||||
**themes/simple/templates/Layout/ArticleHolder.ss**
|
**themes/simple/templates/Layout/ArticleHolder.ss**
|
||||||
|
|
||||||
:::ss
|
```ss
|
||||||
...
|
...
|
||||||
<% loop $Children %>
|
<% loop $Children %>
|
||||||
<% include ArticleTeaser %>
|
<% include ArticleTeaser %>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
...
|
...
|
||||||
|
```
|
||||||
Paste the code that was in ArticleHolder into a new include file called ArticleTeaser.ss:
|
Paste the code that was in ArticleHolder into a new include file called ArticleTeaser.ss:
|
||||||
|
|
||||||
**themes/simple/templates/Includes/ArticleTeaser.ss**
|
**themes/simple/templates/Includes/ArticleTeaser.ss**
|
||||||
|
|
||||||
:::ss
|
```ss
|
||||||
<article>
|
<article>
|
||||||
<h2><a href="$Link" title="Read more on "{$Title}"">$Title</a></h2>
|
<h2><a href="$Link" title="Read more on "{$Title}"">$Title</a></h2>
|
||||||
<p>$Content.FirstParagraph</p>
|
<p>$Content.FirstParagraph</p>
|
||||||
<a href="$Link" title="Read more on "{$Title}"">Read more >></a>
|
<a href="$Link" title="Read more on "{$Title}"">Read more >></a>
|
||||||
</article>
|
</article>
|
||||||
|
```
|
||||||
|
|
||||||
### Changing the icons of pages in the CMS
|
### Changing the icons of pages in the CMS
|
||||||
|
|
||||||
Now let's make a purely cosmetic change that nevertheless helps to make the information presented in the CMS clearer.
|
Now let's make a purely cosmetic change that nevertheless helps to make the information presented in the CMS clearer.
|
||||||
Add the following field to the *ArticleHolder* and *ArticlePage* classes:
|
Add the following field to the *ArticleHolder* and *ArticlePage* classes:
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
private static $icon = "cms/images/treeicons/news-file.gif";
|
private static $icon = "cms/images/treeicons/news-file.gif";
|
||||||
|
```
|
||||||
|
|
||||||
And this one to the *HomePage* class:
|
And this one to the *HomePage* class:
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
private static $icon = "cms/images/treeicons/home-file.png";
|
private static $icon = "cms/images/treeicons/home-file.png";
|
||||||
|
```
|
||||||
|
|
||||||
This will change the icons for the pages in the CMS.
|
This will change the icons for the pages in the CMS.
|
||||||
|
|
||||||
@ -404,12 +405,12 @@ It would be nice to greet page visitors with a summary of the latest news when t
|
|||||||
**mysite/code/HomePage.php**
|
**mysite/code/HomePage.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// ...
|
// ...
|
||||||
public function LatestNews($num=5)
|
public function LatestNews($num=5)
|
||||||
{
|
{
|
||||||
$holder = ArticleHolder::get()->First();
|
$holder = ArticleHolder::get()->First();
|
||||||
return ($holder) ? ArticlePage::get()->filter('ParentID', $holder->ID)->sort('Date DESC')->limit($num) : false;
|
return ($holder) ? ArticlePage::get()->filter('ParentID', $holder->ID)->sort('Date DESC')->limit($num) : false;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
This function simply runs a database query that gets the latest news articles from the database. By default, this is five, but you can change it by passing a number to the function. See the [Data Model and ORM](/developer_guides/model/data_model_and_orm) documentation for details. We can reference this function as a page control in our *HomePage* template:
|
This function simply runs a database query that gets the latest news articles from the database. By default, this is five, but you can change it by passing a number to the function. See the [Data Model and ORM](/developer_guides/model/data_model_and_orm) documentation for details. We can reference this function as a page control in our *HomePage* template:
|
||||||
@ -417,12 +418,12 @@ This function simply runs a database query that gets the latest news articles fr
|
|||||||
**themes/simple/templates/Layout/Homepage.ss**
|
**themes/simple/templates/Layout/Homepage.ss**
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
<!-- ... -->
|
<!-- ... -->
|
||||||
<div class="content">$Content</div>
|
<div class="content">$Content</div>
|
||||||
</article>
|
</article>
|
||||||
<% loop $LatestNews %>
|
<% loop $LatestNews %>
|
||||||
<% include ArticleTeaser %>
|
<% include ArticleTeaser %>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
```
|
```
|
||||||
|
|
||||||
When SilverStripe comes across a variable or page control it doesn't recognize, it first passes control to the controller. If the controller doesn't have a function for the variable or page control, it then passes control to the data object. If it has no matching functions, it then searches its database fields. Failing that it will return nothing.
|
When SilverStripe comes across a variable or page control it doesn't recognize, it first passes control to the controller. If the controller doesn't have a function for the variable or page control, it then passes control to the data object. If it has no matching functions, it then searches its database fields. Failing that it will return nothing.
|
||||||
@ -440,15 +441,15 @@ An RSS feed is something that no news section should be without. SilverStripe ma
|
|||||||
**mysite/code/ArticleHolder.php**
|
**mysite/code/ArticleHolder.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
private static $allowed_actions = [
|
private static $allowed_actions = [
|
||||||
'rss'
|
'rss'
|
||||||
];
|
];
|
||||||
|
|
||||||
public function rss()
|
public function rss()
|
||||||
{
|
{
|
||||||
$rss = new RSSFeed($this->Children(), $this->Link(), "The coolest news around");
|
$rss = new RSSFeed($this->Children(), $this->Link(), "The coolest news around");
|
||||||
return $rss->outputToBrowser();
|
return $rss->outputToBrowser();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Ensure that when you have input the code to implement an RSS feed; flush the webpage afterwards
|
Ensure that when you have input the code to implement an RSS feed; flush the webpage afterwards
|
||||||
@ -465,11 +466,11 @@ Now all we need is to let the user know that our RSS feed exists. Add this funct
|
|||||||
**mysite/code/ArticleHolder.php**
|
**mysite/code/ArticleHolder.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function init()
|
public function init()
|
||||||
{
|
{
|
||||||
RSSFeed::linkToFeed($this->Link() . "rss");
|
RSSFeed::linkToFeed($this->Link() . "rss");
|
||||||
parent::init();
|
parent::init();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
This automatically generates a link-tag in the header of our template. The *init* function is then called on the parent class to ensure any initialization the parent would have done if we hadn't overridden the *init* function is still called. Depending on your browser, you can see the RSS feed link in the address bar.
|
This automatically generates a link-tag in the header of our template. The *init* function is then called on the parent class to ensure any initialization the parent would have done if we hadn't overridden the *init* function is still called. Depending on your browser, you can see the RSS feed link in the address bar.
|
||||||
@ -481,25 +482,25 @@ Now that we have a complete news section, let's take a look at the staff section
|
|||||||
**mysite/code/StaffHolder.php**
|
**mysite/code/StaffHolder.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use Page;
|
use Page;
|
||||||
|
|
||||||
class StaffHolder extends Page
|
class StaffHolder extends Page
|
||||||
{
|
{
|
||||||
private static $db = [];
|
private static $db = [];
|
||||||
private static $has_one = [];
|
private static $has_one = [];
|
||||||
private static $allowed_children = [StaffPage::class];
|
private static $allowed_children = [StaffPage::class];
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**mysite/code/StaffHolderController.php**
|
**mysite/code/StaffHolderController.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use PageController;
|
use PageController;
|
||||||
|
|
||||||
class StaffHolderController extends PageController
|
class StaffHolderController extends PageController
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Nothing here should be new. The *StaffPage* page type is more interesting though. Each staff member has a portrait image. We want to make a permanent connection between this image and the specific *StaffPage* (otherwise we could simply insert an image in the *$Content* field).
|
Nothing here should be new. The *StaffPage* page type is more interesting though. Each staff member has a portrait image. We want to make a permanent connection between this image and the specific *StaffPage* (otherwise we could simply insert an image in the *$Content* field).
|
||||||
@ -507,37 +508,37 @@ Nothing here should be new. The *StaffPage* page type is more interesting though
|
|||||||
**mysite/code/StaffPage.php**
|
**mysite/code/StaffPage.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\AssetAdmin\Forms\UploadField;
|
use SilverStripe\AssetAdmin\Forms\UploadField;
|
||||||
use SilverStripe\Assets\Image;
|
use SilverStripe\Assets\Image;
|
||||||
|
|
||||||
class StaffPage extends Page
|
class StaffPage extends Page
|
||||||
{
|
{
|
||||||
private static $db = [];
|
private static $db = [];
|
||||||
|
|
||||||
private static $has_one = [
|
private static $has_one = [
|
||||||
'Photo' => Image::class
|
'Photo' => Image::class
|
||||||
];
|
];
|
||||||
|
|
||||||
public function getCMSFields()
|
public function getCMSFields()
|
||||||
{
|
{
|
||||||
$fields = parent::getCMSFields();
|
$fields = parent::getCMSFields();
|
||||||
|
|
||||||
$fields->addFieldToTab("Root.Images", new UploadField('Photo'));
|
$fields->addFieldToTab("Root.Images", new UploadField('Photo'));
|
||||||
|
|
||||||
return $fields;
|
return $fields;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**mysite/code/StaffPageController.php**
|
**mysite/code/StaffPageController.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use PageController;
|
use PageController;
|
||||||
|
|
||||||
class StaffPageController extends PageController
|
class StaffPageController extends PageController
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Instead of adding our *Image* as a field in *$db*, we have used the *$has_one* array. This is because an *Image* is not a simple database field like all the fields we have seen so far, but has its own database table. By using the *$has_one* array, we create a relationship between the *StaffPage* table and the *Image* table by storing the id of the respective *Image* in the *StaffPage* table.
|
Instead of adding our *Image* as a field in *$db*, we have used the *$has_one* array. This is because an *Image* is not a simple database field like all the fields we have seen so far, but has its own database table. By using the *$has_one* array, we create a relationship between the *StaffPage* table and the *Image* table by storing the id of the respective *Image* in the *StaffPage* table.
|
||||||
@ -561,23 +562,23 @@ The staff section templates aren't too difficult to create, thanks to the utilit
|
|||||||
**themes/simple/templates/Layout/StaffHolder.ss**
|
**themes/simple/templates/Layout/StaffHolder.ss**
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
<% include SideBar %>
|
<% include SideBar %>
|
||||||
<div class="content-container unit size3of4 lastUnit">
|
<div class="content-container unit size3of4 lastUnit">
|
||||||
<article>
|
<article>
|
||||||
<h1>$Title</h1>
|
<h1>$Title</h1>
|
||||||
$Content
|
$Content
|
||||||
<div class="content">$Content</div>
|
<div class="content">$Content</div>
|
||||||
</article>
|
</article>
|
||||||
<% loop $Children %>
|
<% loop $Children %>
|
||||||
<article>
|
<article>
|
||||||
<h2><a href="$Link" title="Read more on "{$Title}"">$Title</a></h2>
|
<h2><a href="$Link" title="Read more on "{$Title}"">$Title</a></h2>
|
||||||
$Photo.ScaleWidth(150)
|
$Photo.ScaleWidth(150)
|
||||||
<p>$Content.FirstParagraph</p>
|
<p>$Content.FirstParagraph</p>
|
||||||
<a href="$Link" title="Read more on "{$Title}"">Read more >></a>
|
<a href="$Link" title="Read more on "{$Title}"">Read more >></a>
|
||||||
</article>
|
</article>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
$Form
|
$Form
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -592,16 +593,16 @@ The *StaffPage* template is also very straight forward.
|
|||||||
**themes/simple/templates/Layout/StaffPage.ss**
|
**themes/simple/templates/Layout/StaffPage.ss**
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
<% include SideBar %>
|
<% include SideBar %>
|
||||||
<div class="content-container unit size3of4 lastUnit">
|
<div class="content-container unit size3of4 lastUnit">
|
||||||
<article>
|
<article>
|
||||||
<h1>$Title</h1>
|
<h1>$Title</h1>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
$Photo.ScaleWidth(433)
|
$Photo.ScaleWidth(433)
|
||||||
$Content</div>
|
$Content</div>
|
||||||
</article>
|
</article>
|
||||||
$Form
|
$Form
|
||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
Here we use the *ScaleWidth* method to get a different sized image from the same source image. You should now have
|
Here we use the *ScaleWidth* method to get a different sized image from the same source image. You should now have
|
||||||
|
@ -19,7 +19,7 @@ To enable the search engine you need to include the following code in your `mysi
|
|||||||
This will enable fulltext search on page content as well as names of all files in the `/assets` folder.
|
This will enable fulltext search on page content as well as names of all files in the `/assets` folder.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
FulltextSearchable::enable();
|
FulltextSearchable::enable();
|
||||||
```
|
```
|
||||||
|
|
||||||
After including that in your `_config.php` you will need to rebuild the database by visiting [http://localhost/your_site_name/dev/build](http://localhost/your_site_name/dev/build) in your web browser (replace localhost/your_site_name with a domain if applicable). This will add fulltext search columns.
|
After including that in your `_config.php` you will need to rebuild the database by visiting [http://localhost/your_site_name/dev/build](http://localhost/your_site_name/dev/build) in your web browser (replace localhost/your_site_name with a domain if applicable). This will add fulltext search columns.
|
||||||
@ -36,14 +36,14 @@ To add the search form, we can add `$SearchForm` anywhere in our templates. In t
|
|||||||
**themes/simple/templates/Includes/Header.ss**
|
**themes/simple/templates/Includes/Header.ss**
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
...
|
...
|
||||||
<% if $SearchForm %>
|
<% if $SearchForm %>
|
||||||
<span class="search-dropdown-icon">L</span>
|
<span class="search-dropdown-icon">L</span>
|
||||||
<div class="search-bar">
|
<div class="search-bar">
|
||||||
$SearchForm
|
$SearchForm
|
||||||
</div>
|
</div>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
<% include Navigation %>
|
<% include Navigation %>
|
||||||
```
|
```
|
||||||
|
|
||||||
This displays as:
|
This displays as:
|
||||||
@ -58,22 +58,22 @@ is applied via `FulltextSearchable::enable()`
|
|||||||
**cms/code/search/ContentControllerSearchExtension.php**
|
**cms/code/search/ContentControllerSearchExtension.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Core\Extension;
|
use SilverStripe\Core\Extension;
|
||||||
|
|
||||||
class ContentControllerSearchExtension extends Extension
|
class ContentControllerSearchExtension extends Extension
|
||||||
{
|
{
|
||||||
...
|
...
|
||||||
|
|
||||||
public function results($data, $form, $request)
|
public function results($data, $form, $request)
|
||||||
{
|
{
|
||||||
$data = [
|
$data = [
|
||||||
'Results' => $form->getResults(),
|
'Results' => $form->getResults(),
|
||||||
'Query' => $form->getSearchQuery(),
|
'Query' => $form->getSearchQuery(),
|
||||||
'Title' => _t('SearchForm.SearchResults', 'Search Results')
|
'Title' => _t('SearchForm.SearchResults', 'Search Results')
|
||||||
];
|
];
|
||||||
return $this->owner->customise($data)->renderWith(['Page_results', 'Page']);
|
return $this->owner->customise($data)->renderWith(['Page_results', 'Page']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -105,56 +105,56 @@ class.
|
|||||||
*themes/simple/templates/Layout/Page_results.ss*
|
*themes/simple/templates/Layout/Page_results.ss*
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
<div id="Content" class="searchResults">
|
<div id="Content" class="searchResults">
|
||||||
<h1>$Title</h1>
|
<h1>$Title</h1>
|
||||||
|
|
||||||
<% if $Query %>
|
<% if $Query %>
|
||||||
<p class="searchQuery"><strong>You searched for "{$Query}"</strong></p>
|
<p class="searchQuery"><strong>You searched for "{$Query}"</strong></p>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
|
|
||||||
<% if $Results %>
|
<% if $Results %>
|
||||||
<ul id="SearchResults">
|
<ul id="SearchResults">
|
||||||
<% loop $Results %>
|
<% loop $Results %>
|
||||||
<li>
|
<li>
|
||||||
<a class="searchResultHeader" href="$Link">
|
<a class="searchResultHeader" href="$Link">
|
||||||
<% if $MenuTitle %>
|
<% if $MenuTitle %>
|
||||||
$MenuTitle
|
$MenuTitle
|
||||||
<% else %>
|
<% else %>
|
||||||
$Title
|
$Title
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
</a>
|
</a>
|
||||||
<p>$Content.LimitWordCountXML</p>
|
<p>$Content.LimitWordCountXML</p>
|
||||||
<a class="readMoreLink" href="$Link"
|
<a class="readMoreLink" href="$Link"
|
||||||
title="Read more about "{$Title}""
|
title="Read more about "{$Title}""
|
||||||
>Read more about "{$Title}"...</a>
|
>Read more about "{$Title}"...</a>
|
||||||
</li>
|
</li>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</ul>
|
</ul>
|
||||||
<% else %>
|
<% else %>
|
||||||
<p>Sorry, your search query did not return any results.</p>
|
<p>Sorry, your search query did not return any results.</p>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
|
|
||||||
<% if $Results.MoreThanOnePage %>
|
<% if $Results.MoreThanOnePage %>
|
||||||
<div id="PageNumbers">
|
<div id="PageNumbers">
|
||||||
<% if $Results.NotLastPage %>
|
<% if $Results.NotLastPage %>
|
||||||
<a class="next" href="$Results.NextLink" title="View the next page">Next</a>
|
<a class="next" href="$Results.NextLink" title="View the next page">Next</a>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
<% if $Results.NotFirstPage %>
|
<% if $Results.NotFirstPage %>
|
||||||
<a class="prev" href="$Results.PrevLink" title="View the previous page">Prev</a>
|
<a class="prev" href="$Results.PrevLink" title="View the previous page">Prev</a>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
<span>
|
<span>
|
||||||
<% loop $Results.Pages %>
|
<% loop $Results.Pages %>
|
||||||
<% if $CurrentBool %>
|
<% if $CurrentBool %>
|
||||||
$PageNum
|
$PageNum
|
||||||
<% else %>
|
<% else %>
|
||||||
<a href="$Link" title="View page number $PageNum">$PageNum</a>
|
<a href="$Link" title="View page number $PageNum">$PageNum</a>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</span>
|
</span>
|
||||||
<p>Page $Results.CurrentPage of $Results.TotalPages</p>
|
<p>Page $Results.CurrentPage of $Results.TotalPages</p>
|
||||||
</div>
|
</div>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
Then finally add ?flush=1 to the URL and you should see the new template.
|
Then finally add ?flush=1 to the URL and you should see the new template.
|
||||||
|
@ -20,18 +20,18 @@ Let's look at a simple example:
|
|||||||
**mysite/code/Player.php**
|
**mysite/code/Player.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Player extends DataObject
|
class Player extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'PlayerNumber' => 'Int',
|
'PlayerNumber' => 'Int',
|
||||||
'FirstName' => 'Varchar(255)',
|
'FirstName' => 'Varchar(255)',
|
||||||
'LastName' => 'Text',
|
'LastName' => 'Text',
|
||||||
'Birthday' => 'Date'
|
'Birthday' => 'Date'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -79,18 +79,18 @@ system. Instead, it will generate a new `ID` by adding 1 to the current maximum
|
|||||||
**mysite/code/Player.php**
|
**mysite/code/Player.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Player extends DataObject
|
class Player extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'PlayerNumber' => 'Int',
|
'PlayerNumber' => 'Int',
|
||||||
'FirstName' => 'Varchar(255)',
|
'FirstName' => 'Varchar(255)',
|
||||||
'LastName' => 'Text',
|
'LastName' => 'Text',
|
||||||
'Birthday' => 'Date'
|
'Birthday' => 'Date'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -115,13 +115,13 @@ Generates the following `SQL`.
|
|||||||
A new instance of a [DataObject](api:SilverStripe\ORM\DataObject) can be created using the `new` syntax.
|
A new instance of a [DataObject](api:SilverStripe\ORM\DataObject) can be created using the `new` syntax.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$player = new Player();
|
$player = new Player();
|
||||||
```
|
```
|
||||||
|
|
||||||
Or, a better way is to use the `create` method.
|
Or, a better way is to use the `create` method.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$player = Player::create();
|
$player = Player::create();
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="notice" markdown='1'>
|
<div class="notice" markdown='1'>
|
||||||
@ -133,22 +133,22 @@ Database columns and properties can be set as class properties on the object. Th
|
|||||||
of the values through a custom `__set()` method.
|
of the values through a custom `__set()` method.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$player->FirstName = "Sam";
|
$player->FirstName = "Sam";
|
||||||
$player->PlayerNumber = 07;
|
$player->PlayerNumber = 07;
|
||||||
```
|
```
|
||||||
|
|
||||||
To save the `DataObject` to the database, use the `write()` method. The first time `write()` is called, an `ID` will be
|
To save the `DataObject` to the database, use the `write()` method. The first time `write()` is called, an `ID` will be
|
||||||
set.
|
set.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$player->write();
|
$player->write();
|
||||||
```
|
```
|
||||||
|
|
||||||
For convenience, the `write()` method returns the record's ID. This is particularly useful when creating new records.
|
For convenience, the `write()` method returns the record's ID. This is particularly useful when creating new records.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$player = Player::create();
|
$player = Player::create();
|
||||||
$id = $player->write();
|
$id = $player->write();
|
||||||
```
|
```
|
||||||
|
|
||||||
## Querying Data
|
## Querying Data
|
||||||
@ -157,28 +157,28 @@ With the `Player` class defined we can query our data using the `ORM` or Object-
|
|||||||
shortcuts and methods for fetching, sorting and filtering data from our database.
|
shortcuts and methods for fetching, sorting and filtering data from our database.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$players = Player::get();
|
$players = Player::get();
|
||||||
// returns a `DataList` containing all the `Player` objects.
|
// returns a `DataList` containing all the `Player` objects.
|
||||||
|
|
||||||
$player = Player::get()->byID(2);
|
$player = Player::get()->byID(2);
|
||||||
// returns a single `Player` object instance that has the ID of 2.
|
// returns a single `Player` object instance that has the ID of 2.
|
||||||
|
|
||||||
echo $player->ID;
|
echo $player->ID;
|
||||||
// returns the players 'ID' column value
|
// returns the players 'ID' column value
|
||||||
|
|
||||||
echo $player->dbObject('LastEdited')->Ago();
|
echo $player->dbObject('LastEdited')->Ago();
|
||||||
// calls the `Ago` method on the `LastEdited` property.
|
// calls the `Ago` method on the `LastEdited` property.
|
||||||
```
|
```
|
||||||
|
|
||||||
The `ORM` uses a "fluent" syntax, where you specify a query by chaining together different methods. Two common methods
|
The `ORM` uses a "fluent" syntax, where you specify a query by chaining together different methods. Two common methods
|
||||||
are `filter()` and `sort()`:
|
are `filter()` and `sort()`:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$members = Player::get()->filter([
|
$members = Player::get()->filter([
|
||||||
'FirstName' => 'Sam'
|
'FirstName' => 'Sam'
|
||||||
])->sort('Surname');
|
])->sort('Surname');
|
||||||
|
|
||||||
// returns a `DataList` containing all the `Player` records that have the `FirstName` of 'Sam'
|
// returns a `DataList` containing all the `Player` records that have the `FirstName` of 'Sam'
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -194,27 +194,27 @@ It's smart enough to generate a single efficient query at the last moment in tim
|
|||||||
result set in PHP. In `MySQL` the query generated by the ORM may look something like this
|
result set in PHP. In `MySQL` the query generated by the ORM may look something like this
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$players = Player::get()->filter([
|
$players = Player::get()->filter([
|
||||||
'FirstName' => 'Sam'
|
'FirstName' => 'Sam'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$players = $players->sort('Surname');
|
$players = $players->sort('Surname');
|
||||||
|
|
||||||
// executes the following single query
|
// executes the following single query
|
||||||
// SELECT * FROM Player WHERE FirstName = 'Sam' ORDER BY Surname
|
// SELECT * FROM Player WHERE FirstName = 'Sam' ORDER BY Surname
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
This also means that getting the count of a list of objects will be done with a single, efficient query.
|
This also means that getting the count of a list of objects will be done with a single, efficient query.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$players = Player::get()->filter([
|
$players = Player::get()->filter([
|
||||||
'FirstName' => 'Sam'
|
'FirstName' => 'Sam'
|
||||||
])->sort('Surname');
|
])->sort('Surname');
|
||||||
|
|
||||||
// This will create an single SELECT COUNT query
|
// This will create an single SELECT COUNT query
|
||||||
// SELECT COUNT(*) FROM Player WHERE FirstName = 'Sam'
|
// SELECT COUNT(*) FROM Player WHERE FirstName = 'Sam'
|
||||||
echo $players->Count();
|
echo $players->Count();
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -223,21 +223,21 @@ This also means that getting the count of a list of objects will be done with a
|
|||||||
`get()` returns a `DataList` instance. You can loop over `DataList` instances in both PHP and templates.
|
`get()` returns a `DataList` instance. You can loop over `DataList` instances in both PHP and templates.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$players = Player::get();
|
$players = Player::get();
|
||||||
|
|
||||||
foreach($players as $player) {
|
foreach($players as $player) {
|
||||||
echo $player->FirstName;
|
echo $player->FirstName;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Notice that we can step into the loop safely without having to check if `$players` exists. The `get()` call is robust, and will at worst return an empty `DataList` object. If you do want to check if the query returned any records, you can use the `exists()` method, e.g.
|
Notice that we can step into the loop safely without having to check if `$players` exists. The `get()` call is robust, and will at worst return an empty `DataList` object. If you do want to check if the query returned any records, you can use the `exists()` method, e.g.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$players = Player::get();
|
$players = Player::get();
|
||||||
|
|
||||||
if($players->exists()) {
|
if($players->exists()) {
|
||||||
// do something here
|
// do something here
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
See the [Lists](lists) documentation for more information on dealing with [SS_List](api:SilverStripe\ORM\SS_List) instances.
|
See the [Lists](lists) documentation for more information on dealing with [SS_List](api:SilverStripe\ORM\SS_List) instances.
|
||||||
@ -248,16 +248,16 @@ There are a couple of ways of getting a single DataObject from the ORM. If you k
|
|||||||
can use `byID($id)`:
|
can use `byID($id)`:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$player = Player::get()->byID(5);
|
$player = Player::get()->byID(5);
|
||||||
```
|
```
|
||||||
|
|
||||||
`get()` returns a [DataList](api:SilverStripe\ORM\DataList) instance. You can use operations on that to get back a single record.
|
`get()` returns a [DataList](api:SilverStripe\ORM\DataList) instance. You can use operations on that to get back a single record.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$players = Player::get();
|
$players = Player::get();
|
||||||
|
|
||||||
$first = $players->first();
|
$first = $players->first();
|
||||||
$last = $players->last();
|
$last = $players->last();
|
||||||
```
|
```
|
||||||
|
|
||||||
## Sorting
|
## Sorting
|
||||||
@ -265,38 +265,38 @@ can use `byID($id)`:
|
|||||||
If would like to sort the list by `FirstName` in a ascending way (from A to Z).
|
If would like to sort the list by `FirstName` in a ascending way (from A to Z).
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// Sort can either be Ascending (ASC) or Descending (DESC)
|
// Sort can either be Ascending (ASC) or Descending (DESC)
|
||||||
$players = Player::get()->sort('FirstName', 'ASC');
|
$players = Player::get()->sort('FirstName', 'ASC');
|
||||||
|
|
||||||
// Ascending is implied
|
// Ascending is implied
|
||||||
$players = Player::get()->sort('FirstName');
|
$players = Player::get()->sort('FirstName');
|
||||||
```
|
```
|
||||||
|
|
||||||
To reverse the sort
|
To reverse the sort
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$players = Player::get()->sort('FirstName', 'DESC');
|
$players = Player::get()->sort('FirstName', 'DESC');
|
||||||
|
|
||||||
// or..
|
// or..
|
||||||
$players = Player::get()->sort('FirstName', 'ASC')->reverse();
|
$players = Player::get()->sort('FirstName', 'ASC')->reverse();
|
||||||
```
|
```
|
||||||
|
|
||||||
However you might have several entries with the same `FirstName` and would like to sort them by `FirstName` and
|
However you might have several entries with the same `FirstName` and would like to sort them by `FirstName` and
|
||||||
`LastName`
|
`LastName`
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$players = Players::get()->sort([
|
$players = Players::get()->sort([
|
||||||
'FirstName' => 'ASC',
|
'FirstName' => 'ASC',
|
||||||
'LastName'=>'ASC'
|
'LastName'=>'ASC'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also sort randomly. Using the `DB` class, you can get the random sort method per database type.
|
You can also sort randomly. Using the `DB` class, you can get the random sort method per database type.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$random = DB::get_conn()->random();
|
$random = DB::get_conn()->random();
|
||||||
$players = Player::get()->sort($random)
|
$players = Player::get()->sort($random)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Filtering Results
|
## Filtering Results
|
||||||
@ -304,9 +304,9 @@ You can also sort randomly. Using the `DB` class, you can get the random sort me
|
|||||||
The `filter()` method filters the list of objects that gets returned.
|
The `filter()` method filters the list of objects that gets returned.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$players = Player::get()->filter([
|
$players = Player::get()->filter([
|
||||||
'FirstName' => 'Sam'
|
'FirstName' => 'Sam'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -319,29 +319,29 @@ value that you want to filter to.
|
|||||||
So, this would return only those players called "Sam Minnée".
|
So, this would return only those players called "Sam Minnée".
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$players = Player::get()->filter([
|
$players = Player::get()->filter([
|
||||||
'FirstName' => 'Sam',
|
'FirstName' => 'Sam',
|
||||||
'LastName' => 'Minnée',
|
'LastName' => 'Minnée',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// SELECT * FROM Player WHERE FirstName = 'Sam' AND LastName = 'Minnée'
|
// SELECT * FROM Player WHERE FirstName = 'Sam' AND LastName = 'Minnée'
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
There is also a shorthand way of getting Players with the FirstName of Sam.
|
There is also a shorthand way of getting Players with the FirstName of Sam.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$players = Player::get()->filter('FirstName', 'Sam');
|
$players = Player::get()->filter('FirstName', 'Sam');
|
||||||
```
|
```
|
||||||
|
|
||||||
Or if you want to find both Sam and Sig.
|
Or if you want to find both Sam and Sig.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$players = Player::get()->filter(
|
$players = Player::get()->filter(
|
||||||
'FirstName', ['Sam', 'Sig']
|
'FirstName', ['Sam', 'Sig']
|
||||||
);
|
);
|
||||||
|
|
||||||
// SELECT * FROM Player WHERE FirstName IN ('Sam', 'Sig')
|
// SELECT * FROM Player WHERE FirstName IN ('Sam', 'Sig')
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -349,10 +349,10 @@ You can use [SearchFilters](searchfilters) to add additional behavior to your `f
|
|||||||
exact match.
|
exact match.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$players = Player::get()->filter([
|
$players = Player::get()->filter([
|
||||||
'FirstName:StartsWith' => 'S'
|
'FirstName:StartsWith' => 'S'
|
||||||
'PlayerNumber:GreaterThan' => '10'
|
'PlayerNumber:GreaterThan' => '10'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -361,37 +361,37 @@ exact match.
|
|||||||
Use the `filterAny()` method to match multiple criteria non-exclusively (with an "OR" disjunctive),
|
Use the `filterAny()` method to match multiple criteria non-exclusively (with an "OR" disjunctive),
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$players = Player::get()->filterAny([
|
$players = Player::get()->filterAny([
|
||||||
'FirstName' => 'Sam',
|
'FirstName' => 'Sam',
|
||||||
'Age' => 17,
|
'Age' => 17,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// SELECT * FROM Player WHERE ("FirstName" = 'Sam' OR "Age" = '17')
|
// SELECT * FROM Player WHERE ("FirstName" = 'Sam' OR "Age" = '17')
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
You can combine both conjunctive ("AND") and disjunctive ("OR") statements.
|
You can combine both conjunctive ("AND") and disjunctive ("OR") statements.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$players = Player::get()
|
$players = Player::get()
|
||||||
->filter([
|
->filter([
|
||||||
'LastName' => 'Minnée'
|
'LastName' => 'Minnée'
|
||||||
])
|
])
|
||||||
->filterAny([
|
->filterAny([
|
||||||
'FirstName' => 'Sam',
|
'FirstName' => 'Sam',
|
||||||
'Age' => 17,
|
'Age' => 17,
|
||||||
]);
|
]);
|
||||||
// SELECT * FROM Player WHERE ("LastName" = 'Minnée' AND ("FirstName" = 'Sam' OR "Age" = '17'))
|
// SELECT * FROM Player WHERE ("LastName" = 'Minnée' AND ("FirstName" = 'Sam' OR "Age" = '17'))
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
You can use [SearchFilters](searchfilters) to add additional behavior to your `filterAny` command.
|
You can use [SearchFilters](searchfilters) to add additional behavior to your `filterAny` command.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$players = Player::get()->filterAny([
|
$players = Player::get()->filterAny([
|
||||||
'FirstName:StartsWith' => 'S'
|
'FirstName:StartsWith' => 'S'
|
||||||
'PlayerNumber:GreaterThan' => '10'
|
'PlayerNumber:GreaterThan' => '10'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -406,19 +406,19 @@ For instance, the below code will select only values that do not match the given
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$players = Player::get()->filter('FirstName:not', 'Sam');
|
$players = Player::get()->filter('FirstName:not', 'Sam');
|
||||||
// ... WHERE "FirstName" != 'Sam' OR "FirstName" IS NULL
|
// ... WHERE "FirstName" != 'Sam' OR "FirstName" IS NULL
|
||||||
// Returns rows with any value (even null) other than Sam
|
// Returns rows with any value (even null) other than Sam
|
||||||
```
|
```
|
||||||
|
|
||||||
If null values should be excluded, include the null in your check.
|
If null values should be excluded, include the null in your check.
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$players = Player::get()->filter('FirstName:not', ['Sam', null]);
|
$players = Player::get()->filter('FirstName:not', ['Sam', null]);
|
||||||
// ... WHERE "FirstName" != 'Sam' AND "FirstName" IS NOT NULL
|
// ... WHERE "FirstName" != 'Sam' AND "FirstName" IS NOT NULL
|
||||||
// Only returns non-null values for "FirstName" that aren't Sam.
|
// Only returns non-null values for "FirstName" that aren't Sam.
|
||||||
// Strictly the IS NOT NULL isn't necessary, but is included for explicitness
|
// Strictly the IS NOT NULL isn't necessary, but is included for explicitness
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -426,9 +426,9 @@ It is also often useful to filter by all rows with either empty or null for a gi
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$players = Player::get()->filter('FirstName', [null, '']);
|
$players = Player::get()->filter('FirstName', [null, '']);
|
||||||
// ... WHERE "FirstName" == '' OR "FirstName" IS NULL
|
// ... WHERE "FirstName" == '' OR "FirstName" IS NULL
|
||||||
// Returns rows with FirstName which is either empty or null
|
// Returns rows with FirstName which is either empty or null
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -437,17 +437,17 @@ It is also often useful to filter by all rows with either empty or null for a gi
|
|||||||
You can use aggregate expressions in your filters, as well.
|
You can use aggregate expressions in your filters, as well.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// get the teams that have more than 10 players
|
// get the teams that have more than 10 players
|
||||||
$teams = Team::get()->filter('Players.Count():GreaterThan', 10);
|
$teams = Team::get()->filter('Players.Count():GreaterThan', 10);
|
||||||
|
|
||||||
// get the teams with at least one player who has scored 5 or more points
|
// get the teams with at least one player who has scored 5 or more points
|
||||||
$teams = Team::get()->filter('Players.Min(PointsScored):GreaterThanOrEqual', 5);
|
$teams = Team::get()->filter('Players.Min(PointsScored):GreaterThanOrEqual', 5);
|
||||||
|
|
||||||
// get the teams with players who are averaging more than 15 points
|
// get the teams with players who are averaging more than 15 points
|
||||||
$teams = Team::get()->filter('Players.Avg(PointsScored):GreaterThan', 15);
|
$teams = Team::get()->filter('Players.Avg(PointsScored):GreaterThan', 15);
|
||||||
|
|
||||||
// get the teams whose players have scored less than 300 points combined
|
// get the teams whose players have scored less than 300 points combined
|
||||||
$teams = Team::get()->filter('Players.Sum(PointsScored):LessThan', 300);
|
$teams = Team::get()->filter('Players.Sum(PointsScored):LessThan', 300);
|
||||||
```
|
```
|
||||||
|
|
||||||
### filterByCallback
|
### filterByCallback
|
||||||
@ -467,9 +467,9 @@ for each record, if the callback returns true, this record will be added to the
|
|||||||
The below example will get all `Players` aged over 10.
|
The below example will get all `Players` aged over 10.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$players = Player::get()->filterByCallback(function($item, $list) {
|
$players = Player::get()->filterByCallback(function($item, $list) {
|
||||||
return ($item->Age() > 10);
|
return ($item->Age() > 10);
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
### Exclude
|
### Exclude
|
||||||
@ -477,49 +477,49 @@ The below example will get all `Players` aged over 10.
|
|||||||
The `exclude()` method is the opposite to the filter in that it removes entries from a list.
|
The `exclude()` method is the opposite to the filter in that it removes entries from a list.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$players = Player::get()->exclude('FirstName', 'Sam');
|
$players = Player::get()->exclude('FirstName', 'Sam');
|
||||||
|
|
||||||
// SELECT * FROM Player WHERE FirstName != 'Sam'
|
// SELECT * FROM Player WHERE FirstName != 'Sam'
|
||||||
```
|
```
|
||||||
|
|
||||||
Remove both Sam and Sig..
|
Remove both Sam and Sig..
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$players = Player::get()->exclude(
|
$players = Player::get()->exclude(
|
||||||
'FirstName', ['Sam','Sig']
|
'FirstName', ['Sam','Sig']
|
||||||
);
|
);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
`Exclude` follows the same pattern as filter, so for removing only Sam Minnée from the list:
|
`Exclude` follows the same pattern as filter, so for removing only Sam Minnée from the list:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$players = Player::get()->exclude([
|
$players = Player::get()->exclude([
|
||||||
'FirstName' => 'Sam',
|
'FirstName' => 'Sam',
|
||||||
'Surname' => 'Minnée',
|
'Surname' => 'Minnée',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
And removing Sig and Sam with that are either age 17 or 43.
|
And removing Sig and Sam with that are either age 17 or 43.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$players = Player::get()->exclude([
|
$players = Player::get()->exclude([
|
||||||
'FirstName' => ['Sam', 'Sig'],
|
'FirstName' => ['Sam', 'Sig'],
|
||||||
'Age' => [17, 43]
|
'Age' => [17, 43]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// SELECT * FROM Player WHERE ("FirstName" NOT IN ('Sam','Sig) OR "Age" NOT IN ('17', '43'));
|
// SELECT * FROM Player WHERE ("FirstName" NOT IN ('Sam','Sig) OR "Age" NOT IN ('17', '43'));
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
You can use [SearchFilters](searchfilters) to add additional behavior to your `exclude` command.
|
You can use [SearchFilters](searchfilters) to add additional behavior to your `exclude` command.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$players = Player::get()->exclude([
|
$players = Player::get()->exclude([
|
||||||
'FirstName:EndsWith' => 'S'
|
'FirstName:EndsWith' => 'S'
|
||||||
'PlayerNumber:LessThanOrEqual' => '10'
|
'PlayerNumber:LessThanOrEqual' => '10'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -528,18 +528,18 @@ You can use [SearchFilters](searchfilters) to add additional behavior to your `e
|
|||||||
You can subtract entries from a [DataList](api:SilverStripe\ORM\DataList) by passing in another DataList to `subtract()`
|
You can subtract entries from a [DataList](api:SilverStripe\ORM\DataList) by passing in another DataList to `subtract()`
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$sam = Player::get()->filter('FirstName', 'Sam');
|
$sam = Player::get()->filter('FirstName', 'Sam');
|
||||||
$players = Player::get();
|
$players = Player::get();
|
||||||
|
|
||||||
$noSams = $players->subtract($sam);
|
$noSams = $players->subtract($sam);
|
||||||
```
|
```
|
||||||
|
|
||||||
Though for the above example it would probably be easier to use `filter()` and `exclude()`. A better use case could be
|
Though for the above example it would probably be easier to use `filter()` and `exclude()`. A better use case could be
|
||||||
when you want to find all the members that does not exist in a Group.
|
when you want to find all the members that does not exist in a Group.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// ... Finding all members that does not belong to $group.
|
// ... Finding all members that does not belong to $group.
|
||||||
$otherMembers = Member::get()->subtract($group->Members());
|
$otherMembers = Member::get()->subtract($group->Members());
|
||||||
```
|
```
|
||||||
|
|
||||||
### Limit
|
### Limit
|
||||||
@ -547,7 +547,7 @@ when you want to find all the members that does not exist in a Group.
|
|||||||
You can limit the amount of records returned in a DataList by using the `limit()` method.
|
You can limit the amount of records returned in a DataList by using the `limit()` method.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$members = Member::get()->limit(5);
|
$members = Member::get()->limit(5);
|
||||||
```
|
```
|
||||||
|
|
||||||
`limit()` accepts two arguments, the first being the amount of results you want returned, with an optional second
|
`limit()` accepts two arguments, the first being the amount of results you want returned, with an optional second
|
||||||
@ -555,8 +555,8 @@ parameter to specify the offset, which allows you to tell the system where to st
|
|||||||
offset, if not provided as an argument, will default to 0.
|
offset, if not provided as an argument, will default to 0.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// Return 10 members with an offset of 4 (starting from the 5th result).
|
// Return 10 members with an offset of 4 (starting from the 5th result).
|
||||||
$members = Member::get()->sort('Surname')->limit(10, 4);
|
$members = Member::get()->sort('Surname')->limit(10, 4);
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="alert">
|
<div class="alert">
|
||||||
@ -573,11 +573,11 @@ For instance, the below model will be stored in the table name `BannerImage`
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
namespace SilverStripe\BannerManager;
|
namespace SilverStripe\BannerManager;
|
||||||
class BannerImage extends \DataObject
|
class BannerImage extends \DataObject
|
||||||
{
|
{
|
||||||
private static $table_name = 'BannerImage';
|
private static $table_name = 'BannerImage';
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that any model class which does not explicitly declare a `table_name` config option will have a name
|
Note that any model class which does not explicitly declare a `table_name` config option will have a name
|
||||||
@ -608,14 +608,14 @@ table and column.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function countDuplicates($model, $fieldToCheck)
|
public function countDuplicates($model, $fieldToCheck)
|
||||||
{
|
{
|
||||||
$table = DataObject::getSchema()->tableForField($model, $field);
|
$table = DataObject::getSchema()->tableForField($model, $field);
|
||||||
$query = new SQLSelect();
|
$query = new SQLSelect();
|
||||||
$query->setFrom("\"{$table}\"");
|
$query->setFrom("\"{$table}\"");
|
||||||
$query->setWhere(["\"{$table}\".\"{$field}\"" => $model->$fieldToCheck]);
|
$query->setWhere(["\"{$table}\".\"{$field}\"" => $model->$fieldToCheck]);
|
||||||
return $query->count();
|
return $query->count();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Raw SQL
|
### Raw SQL
|
||||||
@ -635,7 +635,7 @@ you need it to, you may also consider extending the ORM with new data types or f
|
|||||||
You can specify a WHERE clause fragment (that will be combined with other filters using AND) with the `where()` method:
|
You can specify a WHERE clause fragment (that will be combined with other filters using AND) with the `where()` method:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$members = Member::get()->where("\"FirstName\" = 'Sam'")
|
$members = Member::get()->where("\"FirstName\" = 'Sam'")
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Joining Tables
|
#### Joining Tables
|
||||||
@ -647,12 +647,12 @@ You can specify a join with the `innerJoin` and `leftJoin` methods. Both of the
|
|||||||
* An optional alias.
|
* An optional alias.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// Without an alias
|
// Without an alias
|
||||||
$members = Member::get()
|
$members = Member::get()
|
||||||
->leftJoin("Group_Members", "\"Group_Members\".\"MemberID\" = \"Member\".\"ID\"");
|
->leftJoin("Group_Members", "\"Group_Members\".\"MemberID\" = \"Member\".\"ID\"");
|
||||||
|
|
||||||
$members = Member::get()
|
$members = Member::get()
|
||||||
->innerJoin("Group_Members", "\"Rel\".\"MemberID\" = \"Member\".\"ID\"", "Rel");
|
->innerJoin("Group_Members", "\"Rel\".\"MemberID\" = \"Member\".\"ID\"", "Rel");
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="alert" markdown="1">
|
<div class="alert" markdown="1">
|
||||||
@ -666,15 +666,15 @@ Define the default values for all the `$db` fields. This example sets the "Statu
|
|||||||
whenever a new object is created.
|
whenever a new object is created.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Player extends DataObject
|
class Player extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $defaults = [
|
private static $defaults = [
|
||||||
"Status" => 'Active',
|
"Status" => 'Active',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -693,20 +693,20 @@ time.
|
|||||||
For example, suppose we have the following set of classes:
|
For example, suppose we have the following set of classes:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
use Page;
|
use Page;
|
||||||
|
|
||||||
class Page extends SiteTree
|
class Page extends SiteTree
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
class NewsPage extends Page
|
class NewsPage extends Page
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Summary' => 'Text'
|
'Summary' => 'Text'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -714,26 +714,26 @@ The data for the following classes would be stored across the following tables:
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
SiteTree:
|
SiteTree:
|
||||||
- ID: Int
|
- ID: Int
|
||||||
- ClassName: Enum('SiteTree', 'Page', 'NewsPage')
|
- ClassName: Enum('SiteTree', 'Page', 'NewsPage')
|
||||||
- Created: Datetime
|
- Created: Datetime
|
||||||
- LastEdited: Datetime
|
- LastEdited: Datetime
|
||||||
- Title: Varchar
|
- Title: Varchar
|
||||||
- Content: Text
|
- Content: Text
|
||||||
NewsPage:
|
NewsPage:
|
||||||
- ID: Int
|
- ID: Int
|
||||||
- Summary: Text
|
- Summary: Text
|
||||||
```
|
```
|
||||||
|
|
||||||
Accessing the data is transparent to the developer.
|
Accessing the data is transparent to the developer.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$news = NewsPage::get();
|
$news = NewsPage::get();
|
||||||
|
|
||||||
foreach($news as $article) {
|
foreach($news as $article) {
|
||||||
echo $article->Title;
|
echo $article->Title;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The way the ORM stores the data is this:
|
The way the ORM stores the data is this:
|
||||||
|
@ -16,26 +16,26 @@ A 1-to-1 relation creates a database-column called "`<relationship-name>`ID", in
|
|||||||
"TeamID" on the "Player"-table.
|
"TeamID" on the "Player"-table.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Team extends DataObject
|
class Team extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Title' => 'Varchar'
|
'Title' => 'Varchar'
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $has_many = [
|
private static $has_many = [
|
||||||
'Players' => 'Player'
|
'Players' => 'Player'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
class Player extends DataObject
|
class Player extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $has_one = [
|
private static $has_one = [
|
||||||
"Team" => "Team",
|
"Team" => "Team",
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -45,24 +45,24 @@ and provides a short syntax for accessing the related object.
|
|||||||
At the database level, the `has_one` creates a `TeamID` field on `Player`. A `has_many` field does not impose any database changes. It merely injects a new method into the class to access the related records (in this case, `Players()`)
|
At the database level, the `has_one` creates a `TeamID` field on `Player`. A `has_many` field does not impose any database changes. It merely injects a new method into the class to access the related records (in this case, `Players()`)
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$player = Player::get()->byId(1);
|
$player = Player::get()->byId(1);
|
||||||
|
|
||||||
$team = $player->Team();
|
$team = $player->Team();
|
||||||
// returns a 'Team' instance.
|
// returns a 'Team' instance.
|
||||||
|
|
||||||
echo $player->Team()->Title;
|
echo $player->Team()->Title;
|
||||||
// returns the 'Title' column on the 'Team' or `getTitle` if it exists.
|
// returns the 'Title' column on the 'Team' or `getTitle` if it exists.
|
||||||
```
|
```
|
||||||
|
|
||||||
The relationship can also be navigated in [templates](../templates).
|
The relationship can also be navigated in [templates](../templates).
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% with $Player %>
|
<% with $Player %>
|
||||||
<% if $Team %>
|
<% if $Team %>
|
||||||
Plays for $Team.Title
|
Plays for $Team.Title
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
<% end_with %>
|
<% end_with %>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Polymorphic has_one
|
## Polymorphic has_one
|
||||||
@ -77,30 +77,30 @@ To specify that a has_one relation is polymorphic set the type to 'DataObject'.
|
|||||||
Ideally, the associated has_many (or belongs_to) should be specified with dot notation.
|
Ideally, the associated has_many (or belongs_to) should be specified with dot notation.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Player extends DataObject
|
class Player extends DataObject
|
||||||
{
|
{
|
||||||
private static $has_many = [
|
private static $has_many = [
|
||||||
"Fans" => "Fan.FanOf"
|
"Fans" => "Fan.FanOf"
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
class Team extends DataObject
|
class Team extends DataObject
|
||||||
{
|
{
|
||||||
private static $has_many = [
|
private static $has_many = [
|
||||||
"Fans" => "Fan.FanOf"
|
"Fans" => "Fan.FanOf"
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type of object returned by $fan->FanOf() will vary
|
// Type of object returned by $fan->FanOf() will vary
|
||||||
class Fan extends DataObject
|
class Fan extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
// Generates columns FanOfID and FanOfClass
|
// Generates columns FanOfID and FanOfClass
|
||||||
private static $has_one = [
|
private static $has_one = [
|
||||||
"FanOf" => "DataObject"
|
"FanOf" => "DataObject"
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -122,26 +122,26 @@ available on both ends.
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Team extends DataObject
|
class Team extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Title' => 'Varchar'
|
'Title' => 'Varchar'
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $has_many = [
|
private static $has_many = [
|
||||||
'Players' => 'Player'
|
'Players' => 'Player'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
class Player extends DataObject
|
class Player extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $has_one = [
|
private static $has_one = [
|
||||||
"Team" => "Team",
|
"Team" => "Team",
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -149,40 +149,40 @@ Much like the `has_one` relationship, `has_many` can be navigated through the `O
|
|||||||
you will get an instance of [HasManyList](api:SilverStripe\ORM\HasManyList) rather than the object.
|
you will get an instance of [HasManyList](api:SilverStripe\ORM\HasManyList) rather than the object.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$team = Team::get()->first();
|
$team = Team::get()->first();
|
||||||
|
|
||||||
echo $team->Players();
|
echo $team->Players();
|
||||||
// [HasManyList]
|
// [HasManyList]
|
||||||
|
|
||||||
echo $team->Players()->Count();
|
echo $team->Players()->Count();
|
||||||
// returns '14';
|
// returns '14';
|
||||||
|
|
||||||
foreach($team->Players() as $player) {
|
foreach($team->Players() as $player) {
|
||||||
echo $player->FirstName;
|
echo $player->FirstName;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
To specify multiple `$has_many` to the same object you can use dot notation to distinguish them like below:
|
To specify multiple `$has_many` to the same object you can use dot notation to distinguish them like below:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Person extends DataObject
|
class Person extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $has_many = [
|
private static $has_many = [
|
||||||
"Managing" => "Company.Manager",
|
"Managing" => "Company.Manager",
|
||||||
"Cleaning" => "Company.Cleaner",
|
"Cleaning" => "Company.Cleaner",
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
class Company extends DataObject
|
class Company extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $has_one = [
|
private static $has_one = [
|
||||||
"Manager" => "Person",
|
"Manager" => "Person",
|
||||||
"Cleaner" => "Person"
|
"Cleaner" => "Person"
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -191,14 +191,14 @@ named.
|
|||||||
|
|
||||||
If you're using the default scaffolded form fields with multiple `has_one` relationships, you will end up with a CMS field for each relation. If you don't want these you can remove them by their IDs:
|
If you're using the default scaffolded form fields with multiple `has_one` relationships, you will end up with a CMS field for each relation. If you don't want these you can remove them by their IDs:
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
public function getCMSFields()
|
public function getCMSFields()
|
||||||
{
|
{
|
||||||
$fields = parent::getCMSFields();
|
$fields = parent::getCMSFields();
|
||||||
$fields->removeByName(array('ManagerID', 'CleanerID'));
|
$fields->removeByName(array('ManagerID', 'CleanerID'));
|
||||||
return $fields;
|
return $fields;
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## belongs_to
|
## belongs_to
|
||||||
|
|
||||||
@ -211,22 +211,22 @@ Similarly with `$has_many`, dot notation can be used to explicitly specify the `
|
|||||||
This is not mandatory unless the relationship would be otherwise ambiguous.
|
This is not mandatory unless the relationship would be otherwise ambiguous.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Team extends DataObject
|
class Team extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $has_one = [
|
private static $has_one = [
|
||||||
'Coach' => 'Coach'
|
'Coach' => 'Coach'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
class Coach extends DataObject
|
class Coach extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $belongs_to = [
|
private static $belongs_to = [
|
||||||
'Team' => 'Team.Coach'
|
'Team' => 'Team.Coach'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -246,10 +246,10 @@ The only difference being you will get an instance of [ManyManyList](api:SilverS
|
|||||||
[ManyManyThroughList](api:SilverStripe\ORM\ManyManyThroughList) rather than the object.
|
[ManyManyThroughList](api:SilverStripe\ORM\ManyManyThroughList) rather than the object.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$team = Team::get()->byId(1);
|
$team = Team::get()->byId(1);
|
||||||
|
|
||||||
$supporters = $team->Supporters();
|
$supporters = $team->Supporters();
|
||||||
// returns a 'ManyManyList' instance.
|
// returns a 'ManyManyList' instance.
|
||||||
```
|
```
|
||||||
|
|
||||||
### Automatic many_many table
|
### Automatic many_many table
|
||||||
@ -263,26 +263,26 @@ config to add extra columns.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Team extends DataObject
|
class Team extends DataObject
|
||||||
{
|
{
|
||||||
private static $many_many = [
|
private static $many_many = [
|
||||||
"Supporters" => "Supporter",
|
"Supporters" => "Supporter",
|
||||||
];
|
];
|
||||||
private static $many_many_extraFields = [
|
private static $many_many_extraFields = [
|
||||||
'Supporters' => [
|
'Supporters' => [
|
||||||
'Ranking' => 'Int'
|
'Ranking' => 'Int'
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
class Supporter extends DataObject
|
class Supporter extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $belongs_many_many = [
|
private static $belongs_many_many = [
|
||||||
"Supports" => "Team",
|
"Supports" => "Team",
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### many_many through relationship joined on a separate DataObject
|
### many_many through relationship joined on a separate DataObject
|
||||||
@ -308,35 +308,35 @@ or child record.
|
|||||||
The syntax for `belongs_many_many` is unchanged.
|
The syntax for `belongs_many_many` is unchanged.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Team extends DataObject
|
class Team extends DataObject
|
||||||
{
|
{
|
||||||
private static $many_many = [
|
private static $many_many = [
|
||||||
"Supporters" => [
|
"Supporters" => [
|
||||||
'through' => 'TeamSupporter',
|
'through' => 'TeamSupporter',
|
||||||
'from' => 'Team',
|
'from' => 'Team',
|
||||||
'to' => 'Supporter',
|
'to' => 'Supporter',
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
class Supporter extends DataObject
|
class Supporter extends DataObject
|
||||||
{
|
{
|
||||||
private static $belongs_many_many = [
|
private static $belongs_many_many = [
|
||||||
"Supports" => "Team",
|
"Supports" => "Team",
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
class TeamSupporter extends DataObject
|
class TeamSupporter extends DataObject
|
||||||
{
|
{
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Ranking' => 'Int',
|
'Ranking' => 'Int',
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $has_one = [
|
private static $has_one = [
|
||||||
'Team' => 'Team',
|
'Team' => 'Team',
|
||||||
'Supporter' => 'Supporter'
|
'Supporter' => 'Supporter'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
In order to filter on the join table during queries, you can use the class name of the joining table
|
In order to filter on the join table during queries, you can use the class name of the joining table
|
||||||
@ -344,8 +344,8 @@ for any sql conditions.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$team = Team::get()->byId(1);
|
$team = Team::get()->byId(1);
|
||||||
$supporters = $team->Supporters()->where(['"TeamSupporter"."Ranking"' => 1]);
|
$supporters = $team->Supporters()->where(['"TeamSupporter"."Ranking"' => 1]);
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: ->filter() currently does not support joined fields natively due to the fact that the
|
Note: ->filter() currently does not support joined fields natively due to the fact that the
|
||||||
@ -359,11 +359,11 @@ The joined record can be accessed via `Join` or `TeamSupporter` property (many_m
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% with $Supporter %>
|
<% with $Supporter %>
|
||||||
<% loop $Supports %>
|
<% loop $Supports %>
|
||||||
Supports $Title <% if $TeamSupporter %>(rank $TeamSupporter.Ranking)<% end_if %>
|
Supports $Title <% if $TeamSupporter %>(rank $TeamSupporter.Ranking)<% end_if %>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
<% end_with %>
|
<% end_with %>
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also use `$Join` in place of the join class alias (`$TeamSupporter`), if your template
|
You can also use `$Join` in place of the join class alias (`$TeamSupporter`), if your template
|
||||||
@ -379,24 +379,24 @@ distinguish them like below:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Category extends DataObject
|
class Category extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $many_many = [
|
private static $many_many = [
|
||||||
'Products' => 'Product',
|
'Products' => 'Product',
|
||||||
'FeaturedProducts' => 'Product'
|
'FeaturedProducts' => 'Product'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
class Product extends DataObject
|
class Product extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $belongs_many_many = [
|
private static $belongs_many_many = [
|
||||||
'Categories' => 'Category.Products',
|
'Categories' => 'Category.Products',
|
||||||
'FeaturedInCategories' => 'Category.FeaturedProducts'
|
'FeaturedInCategories' => 'Category.FeaturedProducts'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -413,15 +413,15 @@ encapsulated by [HasManyList](api:SilverStripe\ORM\HasManyList) and [ManyManyLis
|
|||||||
and `remove()` method.
|
and `remove()` method.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$team = Team::get()->byId(1);
|
$team = Team::get()->byId(1);
|
||||||
|
|
||||||
// create a new supporter
|
// create a new supporter
|
||||||
$supporter = new Supporter();
|
$supporter = new Supporter();
|
||||||
$supporter->Name = "Foo";
|
$supporter->Name = "Foo";
|
||||||
$supporter->write();
|
$supporter->write();
|
||||||
|
|
||||||
// add the supporter.
|
// add the supporter.
|
||||||
$team->Supporters()->add($supporter);
|
$team->Supporters()->add($supporter);
|
||||||
```
|
```
|
||||||
|
|
||||||
## Custom Relations
|
## Custom Relations
|
||||||
@ -432,20 +432,20 @@ You can use the ORM to get a filtered result list without writing any SQL. For e
|
|||||||
See [DataObject::$has_many](api:SilverStripe\ORM\DataObject::$has_many) for more info on the described relations.
|
See [DataObject::$has_many](api:SilverStripe\ORM\DataObject::$has_many) for more info on the described relations.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Team extends DataObject
|
class Team extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $has_many = [
|
private static $has_many = [
|
||||||
"Players" => "Player"
|
"Players" => "Player"
|
||||||
];
|
];
|
||||||
|
|
||||||
public function ActivePlayers()
|
public function ActivePlayers()
|
||||||
{
|
{
|
||||||
return $this->Players()->filter('Status', 'Active');
|
return $this->Players()->filter('Status', 'Active');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -12,32 +12,32 @@ modify.
|
|||||||
[SS_List](api:SilverStripe\ORM\SS_List) implements `IteratorAggregate`, allowing you to loop over the instance.
|
[SS_List](api:SilverStripe\ORM\SS_List) implements `IteratorAggregate`, allowing you to loop over the instance.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$members = Member::get();
|
$members = Member::get();
|
||||||
|
|
||||||
foreach($members as $member) {
|
foreach($members as $member) {
|
||||||
echo $member->Name;
|
echo $member->Name;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Or in the template engine:
|
Or in the template engine:
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% loop $Members %>
|
<% loop $Members %>
|
||||||
<!-- -->
|
<!-- -->
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Finding an item by value.
|
## Finding an item by value.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// $list->find($key, $value);
|
// $list->find($key, $value);
|
||||||
|
|
||||||
//
|
//
|
||||||
$members = Member::get();
|
$members = Member::get();
|
||||||
|
|
||||||
echo $members->find('ID', 4)->FirstName;
|
echo $members->find('ID', 4)->FirstName;
|
||||||
// returns 'Sam'
|
// returns 'Sam'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Maps
|
## Maps
|
||||||
@ -45,35 +45,35 @@ Or in the template engine:
|
|||||||
A map is an array where the array indexes contain data as well as the values. You can build a map from any list
|
A map is an array where the array indexes contain data as well as the values. You can build a map from any list
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$members = Member::get()->map('ID', 'FirstName');
|
$members = Member::get()->map('ID', 'FirstName');
|
||||||
|
|
||||||
// $members = array(
|
// $members = array(
|
||||||
// 1 => 'Sam'
|
// 1 => 'Sam'
|
||||||
// 2 => 'Sig'
|
// 2 => 'Sig'
|
||||||
// 3 => 'Will'
|
// 3 => 'Will'
|
||||||
// );
|
// );
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
This functionality is provided by the [Map](api:SilverStripe\ORM\Map) class, which can be used to build a map around any `SS_List`.
|
This functionality is provided by the [Map](api:SilverStripe\ORM\Map) class, which can be used to build a map around any `SS_List`.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$members = Member::get();
|
$members = Member::get();
|
||||||
$map = new Map($members, 'ID', 'FirstName');
|
$map = new Map($members, 'ID', 'FirstName');
|
||||||
```
|
```
|
||||||
|
|
||||||
## Column
|
## Column
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$members = Member::get();
|
$members = Member::get();
|
||||||
|
|
||||||
echo $members->column('Email');
|
echo $members->column('Email');
|
||||||
|
|
||||||
// returns array(
|
// returns array(
|
||||||
// 'sam@silverstripe.com',
|
// 'sam@silverstripe.com',
|
||||||
// 'sig@silverstripe.com',
|
// 'sig@silverstripe.com',
|
||||||
// 'will@silverstripe.com'
|
// 'will@silverstripe.com'
|
||||||
// );
|
// );
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -82,15 +82,15 @@ This functionality is provided by the [Map](api:SilverStripe\ORM\Map) class, whi
|
|||||||
[ArrayList](api:SilverStripe\ORM\ArrayList) exists to wrap a standard PHP array in the same API as a database backed list.
|
[ArrayList](api:SilverStripe\ORM\ArrayList) exists to wrap a standard PHP array in the same API as a database backed list.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$sam = Member::get()->byId(5);
|
$sam = Member::get()->byId(5);
|
||||||
$sig = Member::get()->byId(6);
|
$sig = Member::get()->byId(6);
|
||||||
|
|
||||||
$list = new ArrayList();
|
$list = new ArrayList();
|
||||||
$list->push($sam);
|
$list->push($sam);
|
||||||
$list->push($sig);
|
$list->push($sig);
|
||||||
|
|
||||||
echo $list->Count();
|
echo $list->Count();
|
||||||
// returns '2'
|
// returns '2'
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -14,18 +14,18 @@ In the `Player` example, we have four database columns each with a different dat
|
|||||||
**mysite/code/Player.php**
|
**mysite/code/Player.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Player extends DataObject
|
class Player extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'PlayerNumber' => 'Int',
|
'PlayerNumber' => 'Int',
|
||||||
'FirstName' => 'Varchar(255)',
|
'FirstName' => 'Varchar(255)',
|
||||||
'LastName' => 'Text',
|
'LastName' => 'Text',
|
||||||
'Birthday' => 'Date'
|
'Birthday' => 'Date'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -55,21 +55,21 @@ For complex default values for newly instantiated objects see [Dynamic Default V
|
|||||||
For simple values you can make use of the `$defaults` array. For example:
|
For simple values you can make use of the `$defaults` array. For example:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Car extends DataObject
|
class Car extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Wheels' => 'Int',
|
'Wheels' => 'Int',
|
||||||
'Condition' => 'Enum(array("New","Fair","Junk"))'
|
'Condition' => 'Enum(array("New","Fair","Junk"))'
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $defaults = [
|
private static $defaults = [
|
||||||
'Wheels' => 4,
|
'Wheels' => 4,
|
||||||
'Condition' => 'New'
|
'Condition' => 'New'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -88,17 +88,17 @@ For enum values, it's the second parameter.
|
|||||||
For example:
|
For example:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Car extends DataObject
|
class Car extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Wheels' => 'Int(4)',
|
'Wheels' => 'Int(4)',
|
||||||
'Condition' => 'Enum(array("New","Fair","Junk"), "New")',
|
'Condition' => 'Enum(array("New","Fair","Junk"), "New")',
|
||||||
'Make' => 'Varchar(["default" => "Honda"]),
|
'Make' => 'Varchar(["default" => "Honda"]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -113,34 +113,34 @@ object we can control the formatting and it allows us to call methods defined fr
|
|||||||
**mysite/code/Player.php**
|
**mysite/code/Player.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\FieldType\DBField;
|
use SilverStripe\ORM\FieldType\DBField;
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Player extends DataObject
|
class Player extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
..
|
..
|
||||||
|
|
||||||
public function getName()
|
public function getName()
|
||||||
{
|
{
|
||||||
return DBField::create_field('Varchar', $this->FirstName . ' '. $this->LastName);
|
return DBField::create_field('Varchar', $this->FirstName . ' '. $this->LastName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Then we can refer to a new `Name` column on our `Player` instances. In templates we don't need to use the `get` prefix.
|
Then we can refer to a new `Name` column on our `Player` instances. In templates we don't need to use the `get` prefix.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$player = Player::get()->byId(1);
|
$player = Player::get()->byId(1);
|
||||||
|
|
||||||
echo $player->Name;
|
echo $player->Name;
|
||||||
// returns "Sam Minnée"
|
// returns "Sam Minnée"
|
||||||
|
|
||||||
echo $player->getName();
|
echo $player->getName();
|
||||||
// returns "Sam Minnée";
|
// returns "Sam Minnée";
|
||||||
|
|
||||||
echo $player->getName()->LimitCharacters(2);
|
echo $player->getName()->LimitCharacters(2);
|
||||||
// returns "Sa.."
|
// returns "Sa.."
|
||||||
```
|
```
|
||||||
|
|
||||||
## Casting
|
## Casting
|
||||||
@ -148,20 +148,20 @@ Then we can refer to a new `Name` column on our `Player` instances. In templates
|
|||||||
Rather than manually returning objects from your custom functions. You can use the `$casting` property.
|
Rather than manually returning objects from your custom functions. You can use the `$casting` property.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Player extends DataObject
|
class Player extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $casting = [
|
private static $casting = [
|
||||||
"Name" => 'Varchar',
|
"Name" => 'Varchar',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function getName()
|
public function getName()
|
||||||
{
|
{
|
||||||
return $this->FirstName . ' '. $this->LastName;
|
return $this->FirstName . ' '. $this->LastName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -173,14 +173,14 @@ On the most basic level, the class can be used as simple conversion class from o
|
|||||||
number.
|
number.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
DBField::create_field('Double', 1.23456)->Round(2); // results in 1.23
|
DBField::create_field('Double', 1.23456)->Round(2); // results in 1.23
|
||||||
```
|
```
|
||||||
|
|
||||||
Of course that's much more verbose than the equivalent PHP call. The power of [DBField](api:SilverStripe\ORM\FieldType\DBField) comes with its more
|
Of course that's much more verbose than the equivalent PHP call. The power of [DBField](api:SilverStripe\ORM\FieldType\DBField) comes with its more
|
||||||
sophisticated helpers, like showing the time difference to the current date:
|
sophisticated helpers, like showing the time difference to the current date:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
DBField::create_field('Date', '1982-01-01')->TimeDiff(); // shows "30 years ago"
|
DBField::create_field('Date', '1982-01-01')->TimeDiff(); // shows "30 years ago"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Casting ViewableData
|
## Casting ViewableData
|
||||||
@ -189,26 +189,26 @@ Most objects in SilverStripe extend from [ViewableData](api:SilverStripe\View\Vi
|
|||||||
context. Through a `$casting` array, arbitrary properties and getters can be casted:
|
context. Through a `$casting` array, arbitrary properties and getters can be casted:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\View\ViewableData;
|
use SilverStripe\View\ViewableData;
|
||||||
|
|
||||||
class MyObject extends ViewableData
|
class MyObject extends ViewableData
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $casting = [
|
private static $casting = [
|
||||||
'MyDate' => 'Date'
|
'MyDate' => 'Date'
|
||||||
];
|
];
|
||||||
|
|
||||||
public function getMyDate()
|
public function getMyDate()
|
||||||
{
|
{
|
||||||
return '1982-01-01';
|
return '1982-01-01';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$obj = new MyObject;
|
$obj = new MyObject;
|
||||||
$obj->getMyDate(); // returns string
|
$obj->getMyDate(); // returns string
|
||||||
$obj->MyDate; // returns string
|
$obj->MyDate; // returns string
|
||||||
$obj->obj('MyDate'); // returns object
|
$obj->obj('MyDate'); // returns object
|
||||||
$obj->obj('MyDate')->InPast(); // returns boolean
|
$obj->obj('MyDate')->InPast(); // returns boolean
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -231,19 +231,19 @@ The following example will use the result of `getStatus` instead of the 'Status'
|
|||||||
database column using `dbObject`.
|
database column using `dbObject`.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Player extends DataObject
|
class Player extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
"Status" => "Enum(array('Active', 'Injured', 'Retired'))"
|
"Status" => "Enum(array('Active', 'Injured', 'Retired'))"
|
||||||
];
|
];
|
||||||
|
|
||||||
public function getStatus()
|
public function getStatus()
|
||||||
{
|
{
|
||||||
return (!$this->obj("Birthday")->InPast()) ? "Unborn" : $this->dbObject('Status')->Value();
|
return (!$this->obj("Birthday")->InPast()) ? "Unborn" : $this->dbObject('Status')->Value();
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -19,39 +19,39 @@ a `ModelAdmin` record.
|
|||||||
Example: Disallow creation of new players if the currently logged-in player is not a team-manager.
|
Example: Disallow creation of new players if the currently logged-in player is not a team-manager.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Security\Security;
|
use SilverStripe\Security\Security;
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Player extends DataObject
|
class Player extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $has_many = [
|
private static $has_many = [
|
||||||
"Teams"=>"Team"
|
"Teams"=>"Team"
|
||||||
];
|
];
|
||||||
|
|
||||||
public function onBeforeWrite()
|
public function onBeforeWrite()
|
||||||
{
|
{
|
||||||
// check on first write action, aka "database row creation" (ID-property is not set)
|
// check on first write action, aka "database row creation" (ID-property is not set)
|
||||||
if(!$this->isInDb()) {
|
if(!$this->isInDb()) {
|
||||||
$currentPlayer = Security::getCurrentUser();
|
$currentPlayer = Security::getCurrentUser();
|
||||||
|
|
||||||
if(!$currentPlayer->IsTeamManager()) {
|
if(!$currentPlayer->IsTeamManager()) {
|
||||||
user_error('Player-creation not allowed', E_USER_ERROR);
|
user_error('Player-creation not allowed', E_USER_ERROR);
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check on every write action
|
// check on every write action
|
||||||
if(!$this->record['TeamID']) {
|
if(!$this->record['TeamID']) {
|
||||||
user_error('Cannot save player without a valid team', E_USER_ERROR);
|
user_error('Cannot save player without a valid team', E_USER_ERROR);
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// CAUTION: You are required to call the parent-function, otherwise
|
// CAUTION: You are required to call the parent-function, otherwise
|
||||||
// SilverStripe will not execute the request.
|
// SilverStripe will not execute the request.
|
||||||
parent::onBeforeWrite();
|
parent::onBeforeWrite();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -63,27 +63,27 @@ Example: Checking for a specific [permission](permissions) to delete this type o
|
|||||||
member is logged in who belongs to a group containing the permission "PLAYER_DELETE".
|
member is logged in who belongs to a group containing the permission "PLAYER_DELETE".
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Security\Permission;
|
use SilverStripe\Security\Permission;
|
||||||
use SilverStripe\Security\Security;
|
use SilverStripe\Security\Security;
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Player extends DataObject
|
class Player extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $has_many = [
|
private static $has_many = [
|
||||||
"Teams" => "Team"
|
"Teams" => "Team"
|
||||||
];
|
];
|
||||||
|
|
||||||
public function onBeforeDelete()
|
public function onBeforeDelete()
|
||||||
{
|
{
|
||||||
if(!Permission::check('PLAYER_DELETE')) {
|
if(!Permission::check('PLAYER_DELETE')) {
|
||||||
Security::permissionFailure($this);
|
Security::permissionFailure($this);
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
parent::onBeforeDelete();
|
parent::onBeforeDelete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -17,17 +17,17 @@ you can put on field names to change this behavior. These are represented as `Se
|
|||||||
An example of a `SearchFilter` in use:
|
An example of a `SearchFilter` in use:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// fetch any player that starts with a S
|
// fetch any player that starts with a S
|
||||||
$players = Player::get()->filter([
|
$players = Player::get()->filter([
|
||||||
'FirstName:StartsWith' => 'S',
|
'FirstName:StartsWith' => 'S',
|
||||||
'PlayerNumber:GreaterThan' => '10'
|
'PlayerNumber:GreaterThan' => '10'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// to fetch any player that's name contains the letter 'z'
|
// to fetch any player that's name contains the letter 'z'
|
||||||
$players = Player::get()->filterAny([
|
$players = Player::get()->filterAny([
|
||||||
'FirstName:PartialMatch' => 'z',
|
'FirstName:PartialMatch' => 'z',
|
||||||
'LastName:PartialMatch' => 'z'
|
'LastName:PartialMatch' => 'z'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -45,22 +45,22 @@ config:
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|
||||||
SilverStripe\Core\Injector\Injector:
|
SilverStripe\Core\Injector\Injector:
|
||||||
DataListFilter.CustomMatch:
|
DataListFilter.CustomMatch:
|
||||||
class: MyVendor/Search/CustomMatchFilter
|
class: MyVendor/Search/CustomMatchFilter
|
||||||
```
|
```
|
||||||
|
|
||||||
The following is a query which will return everyone whose first name starts with "S", either lowercase or uppercase:
|
The following is a query which will return everyone whose first name starts with "S", either lowercase or uppercase:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$players = Player::get()->filter([
|
$players = Player::get()->filter([
|
||||||
'FirstName:StartsWith:nocase' => 'S'
|
'FirstName:StartsWith:nocase' => 'S'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// use :not to perform a converse operation to filter anything but a 'W'
|
// use :not to perform a converse operation to filter anything but a 'W'
|
||||||
$players = Player::get()->filter([
|
$players = Player::get()->filter([
|
||||||
'FirstName:StartsWith:not' => 'W'
|
'FirstName:StartsWith:not' => 'W'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -17,32 +17,32 @@ code.
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Security\Permission;
|
use SilverStripe\Security\Permission;
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class MyDataObject extends DataObject
|
class MyDataObject extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
public function canView($member = null)
|
public function canView($member = null)
|
||||||
{
|
{
|
||||||
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
|
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function canEdit($member = null)
|
public function canEdit($member = null)
|
||||||
{
|
{
|
||||||
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
|
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function canDelete($member = null)
|
public function canDelete($member = null)
|
||||||
{
|
{
|
||||||
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
|
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function canCreate($member = null)
|
public function canCreate($member = null)
|
||||||
{
|
{
|
||||||
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
|
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="alert" markdown="1">
|
<div class="alert" markdown="1">
|
||||||
|
@ -19,15 +19,15 @@ For example, if you want to run a simple `COUNT` SQL statement,
|
|||||||
the following three statements are functionally equivalent:
|
the following three statements are functionally equivalent:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// Through raw SQL.
|
// Through raw SQL.
|
||||||
$count = DB::query('SELECT COUNT(*) FROM "Member"')->value();
|
$count = DB::query('SELECT COUNT(*) FROM "Member"')->value();
|
||||||
|
|
||||||
// Through SQLSelect abstraction layer.
|
// Through SQLSelect abstraction layer.
|
||||||
$query = new SQLSelect();
|
$query = new SQLSelect();
|
||||||
$count = $query->setFrom('Member')->setSelect('COUNT(*)')->value();
|
$count = $query->setFrom('Member')->setSelect('COUNT(*)')->value();
|
||||||
|
|
||||||
// Through the ORM.
|
// Through the ORM.
|
||||||
$count = Member::get()->count();
|
$count = Member::get()->count();
|
||||||
```
|
```
|
||||||
|
|
||||||
If you do use raw SQL, you'll run the risk of breaking
|
If you do use raw SQL, you'll run the risk of breaking
|
||||||
@ -58,29 +58,29 @@ conditional filters, grouping, limiting, and sorting.
|
|||||||
E.g.
|
E.g.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
|
|
||||||
$sqlQuery = new SQLSelect();
|
$sqlQuery = new SQLSelect();
|
||||||
$sqlQuery->setFrom('Player');
|
$sqlQuery->setFrom('Player');
|
||||||
$sqlQuery->selectField('FieldName', 'Name');
|
$sqlQuery->selectField('FieldName', 'Name');
|
||||||
$sqlQuery->selectField('YEAR("Birthday")', 'Birthyear');
|
$sqlQuery->selectField('YEAR("Birthday")', 'Birthyear');
|
||||||
$sqlQuery->addLeftJoin('Team','"Player"."TeamID" = "Team"."ID"');
|
$sqlQuery->addLeftJoin('Team','"Player"."TeamID" = "Team"."ID"');
|
||||||
$sqlQuery->addWhere(['YEAR("Birthday") = ?' => 1982]);
|
$sqlQuery->addWhere(['YEAR("Birthday") = ?' => 1982]);
|
||||||
// $sqlQuery->setOrderBy(...);
|
// $sqlQuery->setOrderBy(...);
|
||||||
// $sqlQuery->setGroupBy(...);
|
// $sqlQuery->setGroupBy(...);
|
||||||
// $sqlQuery->setHaving(...);
|
// $sqlQuery->setHaving(...);
|
||||||
// $sqlQuery->setLimit(...);
|
// $sqlQuery->setLimit(...);
|
||||||
// $sqlQuery->setDistinct(true);
|
// $sqlQuery->setDistinct(true);
|
||||||
|
|
||||||
// Get the raw SQL (optional) and parameters
|
// Get the raw SQL (optional) and parameters
|
||||||
$rawSQL = $sqlQuery->sql($parameters);
|
$rawSQL = $sqlQuery->sql($parameters);
|
||||||
|
|
||||||
// Execute and return a Query object
|
// Execute and return a Query object
|
||||||
$result = $sqlQuery->execute();
|
$result = $sqlQuery->execute();
|
||||||
|
|
||||||
// Iterate over results
|
// Iterate over results
|
||||||
foreach($result as $row) {
|
foreach($result as $row) {
|
||||||
echo $row['BirthYear'];
|
echo $row['BirthYear'];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -96,31 +96,31 @@ object instead.
|
|||||||
For example, creating a `SQLDelete` object
|
For example, creating a `SQLDelete` object
|
||||||
|
|
||||||
```php
|
```php
|
||||||
|
|
||||||
$query = SQLDelete::create()
|
$query = SQLDelete::create()
|
||||||
->setFrom('"SiteTree"')
|
->setFrom('"SiteTree"')
|
||||||
->setWhere(['"SiteTree"."ShowInMenus"' => 0]);
|
->setWhere(['"SiteTree"."ShowInMenus"' => 0]);
|
||||||
$query->execute();
|
$query->execute();
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternatively, turning an existing `SQLSelect` into a delete
|
Alternatively, turning an existing `SQLSelect` into a delete
|
||||||
|
|
||||||
```php
|
```php
|
||||||
|
|
||||||
$query = SQLSelect::create()
|
$query = SQLSelect::create()
|
||||||
->setFrom('"SiteTree"')
|
->setFrom('"SiteTree"')
|
||||||
->setWhere(['"SiteTree"."ShowInMenus"' => 0])
|
->setWhere(['"SiteTree"."ShowInMenus"' => 0])
|
||||||
->toDelete();
|
->toDelete();
|
||||||
$query->execute();
|
$query->execute();
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Directly querying the database
|
Directly querying the database
|
||||||
|
|
||||||
```php
|
```php
|
||||||
|
|
||||||
DB::prepared_query('DELETE FROM "SiteTree" WHERE "SiteTree"."ShowInMenus" = ?', [0]);
|
DB::prepared_query('DELETE FROM "SiteTree" WHERE "SiteTree"."ShowInMenus" = ?', [0]);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -169,29 +169,29 @@ SQLInsert also includes the following api methods:
|
|||||||
E.g.
|
E.g.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$update = SQLUpdate::create('"SiteTree"')->addWhere(['ID' => 3]);
|
$update = SQLUpdate::create('"SiteTree"')->addWhere(['ID' => 3]);
|
||||||
|
|
||||||
// assigning a list of items
|
// assigning a list of items
|
||||||
$update->addAssignments([
|
$update->addAssignments([
|
||||||
'"Title"' => 'Our Products',
|
'"Title"' => 'Our Products',
|
||||||
'"MenuTitle"' => 'Products'
|
'"MenuTitle"' => 'Products'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Assigning a single value
|
// Assigning a single value
|
||||||
$update->assign('"MenuTitle"', 'Products');
|
$update->assign('"MenuTitle"', 'Products');
|
||||||
|
|
||||||
// Assigning a value using parameterised expression
|
// Assigning a value using parameterised expression
|
||||||
$title = 'Products';
|
$title = 'Products';
|
||||||
$update->assign('"MenuTitle"', [
|
$update->assign('"MenuTitle"', [
|
||||||
'CASE WHEN LENGTH("MenuTitle") > LENGTH(?) THEN "MenuTitle" ELSE ? END' =>
|
'CASE WHEN LENGTH("MenuTitle") > LENGTH(?) THEN "MenuTitle" ELSE ? END' =>
|
||||||
[$title, $title]
|
[$title, $title]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Assigning a value using a pure SQL expression
|
// Assigning a value using a pure SQL expression
|
||||||
$update->assignSQL('"Date"', 'NOW()');
|
$update->assignSQL('"Date"', 'NOW()');
|
||||||
|
|
||||||
// Perform the update
|
// Perform the update
|
||||||
$update->execute();
|
$update->execute();
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -202,25 +202,25 @@ these are translated internally as multiple single row inserts.
|
|||||||
For example,
|
For example,
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$insert = SQLInsert::create('"SiteTree"');
|
$insert = SQLInsert::create('"SiteTree"');
|
||||||
|
|
||||||
// Add multiple rows in a single call. Note that column names do not need
|
// Add multiple rows in a single call. Note that column names do not need
|
||||||
// to be symmetric
|
// to be symmetric
|
||||||
$insert->addRows([
|
$insert->addRows([
|
||||||
['"Title"' => 'Home', '"Content"' => '<p>This is our home page</p>'],
|
['"Title"' => 'Home', '"Content"' => '<p>This is our home page</p>'],
|
||||||
['"Title"' => 'About Us', '"ClassName"' => 'AboutPage']
|
['"Title"' => 'About Us', '"ClassName"' => 'AboutPage']
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Adjust an assignment on the last row
|
// Adjust an assignment on the last row
|
||||||
$insert->assign('"Content"', '<p>This is about us</p>');
|
$insert->assign('"Content"', '<p>This is about us</p>');
|
||||||
|
|
||||||
// Add another row
|
// Add another row
|
||||||
$insert->addRow(['"Title"' => 'Contact Us']);
|
$insert->addRow(['"Title"' => 'Contact Us']);
|
||||||
|
|
||||||
$columns = $insert->getColumns();
|
$columns = $insert->getColumns();
|
||||||
// $columns will be array('"Title"', '"Content"', '"ClassName"');
|
// $columns will be array('"Title"', '"Content"', '"ClassName"');
|
||||||
|
|
||||||
$insert->execute();
|
$insert->execute();
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -232,19 +232,19 @@ e.g. when you want a single column rather than a full-blown object representatio
|
|||||||
Example: Get the count from a relationship.
|
Example: Get the count from a relationship.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$sqlQuery = new SQLSelect();
|
$sqlQuery = new SQLSelect();
|
||||||
$sqlQuery->setFrom('Player');
|
$sqlQuery->setFrom('Player');
|
||||||
$sqlQuery->addSelect('COUNT("Player"."ID")');
|
$sqlQuery->addSelect('COUNT("Player"."ID")');
|
||||||
$sqlQuery->addWhere(['"Team"."ID"' => 99]);
|
$sqlQuery->addWhere(['"Team"."ID"' => 99]);
|
||||||
$sqlQuery->addLeftJoin('Team', '"Team"."ID" = "Player"."TeamID"');
|
$sqlQuery->addLeftJoin('Team', '"Team"."ID" = "Player"."TeamID"');
|
||||||
$count = $sqlQuery->execute()->value();
|
$count = $sqlQuery->execute()->value();
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that in the ORM, this call would be executed in an efficient manner as well:
|
Note that in the ORM, this call would be executed in an efficient manner as well:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$count = $myTeam->Players()->count();
|
$count = $myTeam->Players()->count();
|
||||||
```
|
```
|
||||||
|
|
||||||
### Mapping
|
### Mapping
|
||||||
@ -255,12 +255,12 @@ This can be useful for creating dropdowns.
|
|||||||
Example: Show player names with their birth year, but set their birth dates as values.
|
Example: Show player names with their birth year, but set their birth dates as values.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$sqlQuery = new SQLSelect();
|
$sqlQuery = new SQLSelect();
|
||||||
$sqlQuery->setFrom('Player');
|
$sqlQuery->setFrom('Player');
|
||||||
$sqlQuery->setSelect('Birthdate');
|
$sqlQuery->setSelect('Birthdate');
|
||||||
$sqlQuery->selectField('CONCAT("Name", ' - ', YEAR("Birthdate")', 'NameWithBirthyear');
|
$sqlQuery->selectField('CONCAT("Name", ' - ', YEAR("Birthdate")', 'NameWithBirthyear');
|
||||||
$map = $sqlQuery->execute()->map();
|
$map = $sqlQuery->execute()->map();
|
||||||
$field = new DropdownField('Birthdates', 'Birthdates', $map);
|
$field = new DropdownField('Birthdates', 'Birthdates', $map);
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that going through SQLSelect is just necessary here
|
Note that going through SQLSelect is just necessary here
|
||||||
@ -268,20 +268,20 @@ because of the custom SQL value transformation (`YEAR()`).
|
|||||||
An alternative approach would be a custom getter in the object definition.
|
An alternative approach would be a custom getter in the object definition.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Player extends DataObject
|
class Player extends DataObject
|
||||||
{
|
{
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Name' => 'Varchar',
|
'Name' => 'Varchar',
|
||||||
'Birthdate' => 'Date'
|
'Birthdate' => 'Date'
|
||||||
];
|
];
|
||||||
function getNameWithBirthyear() {
|
function getNameWithBirthyear() {
|
||||||
return date('y', $this->Birthdate);
|
return date('y', $this->Birthdate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$players = Player::get();
|
$players = Player::get();
|
||||||
$map = $players->map('Name', 'NameWithBirthyear');
|
$map = $players->map('Name', 'NameWithBirthyear');
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -22,27 +22,27 @@ write, and respond appropriately if it isn't.
|
|||||||
The return value of `validate()` is a [ValidationResult](api:SilverStripe\ORM\ValidationResult) object.
|
The return value of `validate()` is a [ValidationResult](api:SilverStripe\ORM\ValidationResult) object.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class MyObject extends DataObject
|
class MyObject extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Country' => 'Varchar',
|
'Country' => 'Varchar',
|
||||||
'Postcode' => 'Varchar'
|
'Postcode' => 'Varchar'
|
||||||
];
|
];
|
||||||
|
|
||||||
public function validate()
|
public function validate()
|
||||||
{
|
{
|
||||||
$result = parent::validate();
|
$result = parent::validate();
|
||||||
|
|
||||||
if($this->Country == 'DE' && $this->Postcode && strlen($this->Postcode) != 5) {
|
if($this->Country == 'DE' && $this->Postcode && strlen($this->Postcode) != 5) {
|
||||||
$result->error('Need five digits for German postcodes');
|
$result->error('Need five digits for German postcodes');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -21,15 +21,15 @@ also track versioned history.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Versioned\Versioned;
|
use SilverStripe\Versioned\Versioned;
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class MyStagedModel extends DataObject
|
class MyStagedModel extends DataObject
|
||||||
{
|
{
|
||||||
private static $extensions = [
|
private static $extensions = [
|
||||||
Versioned::class
|
Versioned::class
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternatively, staging can be disabled, so that only versioned changes are tracked for your model. This
|
Alternatively, staging can be disabled, so that only versioned changes are tracked for your model. This
|
||||||
@ -37,14 +37,14 @@ can be specified by setting the constructor argument to "Versioned"
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class VersionedModel extends DataObject
|
class VersionedModel extends DataObject
|
||||||
{
|
{
|
||||||
private static $extensions = [
|
private static $extensions = [
|
||||||
"SilverStripe\\ORM\\Versioning\\Versioned('Versioned')"
|
"SilverStripe\\ORM\\Versioning\\Versioned('Versioned')"
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="notice" markdown="1">
|
<div class="notice" markdown="1">
|
||||||
@ -87,13 +87,13 @@ By default, all records are retrieved from the "Draft" stage (so the `MyRecord`
|
|||||||
explicitly request a certain stage through various getters on the `Versioned` class.
|
explicitly request a certain stage through various getters on the `Versioned` class.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// Fetching multiple records
|
// Fetching multiple records
|
||||||
$stageRecords = Versioned::get_by_stage('MyRecord', Versioned::DRAFT);
|
$stageRecords = Versioned::get_by_stage('MyRecord', Versioned::DRAFT);
|
||||||
$liveRecords = Versioned::get_by_stage('MyRecord', Versioned::LIVE);
|
$liveRecords = Versioned::get_by_stage('MyRecord', Versioned::LIVE);
|
||||||
|
|
||||||
// Fetching a single record
|
// Fetching a single record
|
||||||
$stageRecord = Versioned::get_by_stage('MyRecord', Versioned::DRAFT)->byID(99);
|
$stageRecord = Versioned::get_by_stage('MyRecord', Versioned::DRAFT)->byID(99);
|
||||||
$liveRecord = Versioned::get_by_stage('MyRecord', Versioned::LIVE)->byID(99);
|
$liveRecord = Versioned::get_by_stage('MyRecord', Versioned::LIVE)->byID(99);
|
||||||
```
|
```
|
||||||
|
|
||||||
### Historical Versions
|
### Historical Versions
|
||||||
@ -102,7 +102,7 @@ The above commands will just retrieve the latest version of its respective stage
|
|||||||
in the `<class>_versions` tables.
|
in the `<class>_versions` tables.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$historicalRecord = Versioned::get_version('MyRecord', <record-id>, <version-id>);
|
$historicalRecord = Versioned::get_version('MyRecord', <record-id>, <version-id>);
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="alert" markdown="1">
|
<div class="alert" markdown="1">
|
||||||
@ -115,9 +115,9 @@ objects, which expose the same database information as a `DataObject`, but also
|
|||||||
a record was published.
|
a record was published.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$record = MyRecord::get()->byID(99); // stage doesn't matter here
|
$record = MyRecord::get()->byID(99); // stage doesn't matter here
|
||||||
$versions = $record->allVersions();
|
$versions = $record->allVersions();
|
||||||
echo $versions->First()->Version; // instance of Versioned_Version
|
echo $versions->First()->Version; // instance of Versioned_Version
|
||||||
```
|
```
|
||||||
|
|
||||||
### Writing Versions and Changing Stages
|
### Writing Versions and Changing Stages
|
||||||
@ -141,21 +141,21 @@ done via one of several ways:
|
|||||||
See "DataObject ownership" for reference on dependant objects.
|
See "DataObject ownership" for reference on dependant objects.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$record = Versioned::get_by_stage('MyRecord', Versioned::DRAFT)->byID(99);
|
$record = Versioned::get_by_stage('MyRecord', Versioned::DRAFT)->byID(99);
|
||||||
$record->MyField = 'changed';
|
$record->MyField = 'changed';
|
||||||
// will update `MyRecord` table (assuming Versioned::current_stage() == 'Stage'),
|
// will update `MyRecord` table (assuming Versioned::current_stage() == 'Stage'),
|
||||||
// and write a row to `MyRecord_versions`.
|
// and write a row to `MyRecord_versions`.
|
||||||
$record->write();
|
$record->write();
|
||||||
// will copy the saved record information to the `MyRecord_Live` table
|
// will copy the saved record information to the `MyRecord_Live` table
|
||||||
$record->publishRecursive();
|
$record->publishRecursive();
|
||||||
```
|
```
|
||||||
|
|
||||||
Similarly, an "unpublish" operation does the reverse, and removes a record from a specific stage.
|
Similarly, an "unpublish" operation does the reverse, and removes a record from a specific stage.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$record = MyRecord::get()->byID(99); // stage doesn't matter here
|
$record = MyRecord::get()->byID(99); // stage doesn't matter here
|
||||||
// will remove the row from the `MyRecord_Live` table
|
// will remove the row from the `MyRecord_Live` table
|
||||||
$record->deleteFromStage(Versioned::LIVE);
|
$record->deleteFromStage(Versioned::LIVE);
|
||||||
```
|
```
|
||||||
|
|
||||||
### Forcing the Current Stage
|
### Forcing the Current Stage
|
||||||
@ -164,11 +164,11 @@ The current stage is stored as global state on the object. It is usually modifie
|
|||||||
is initialized. But it can also be set and reset temporarily to force a specific operation to run on a certain stage.
|
is initialized. But it can also be set and reset temporarily to force a specific operation to run on a certain stage.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$origMode = Versioned::get_reading_mode(); // save current mode
|
$origMode = Versioned::get_reading_mode(); // save current mode
|
||||||
$obj = MyRecord::getComplexObjectRetrieval(); // returns 'Live' records
|
$obj = MyRecord::getComplexObjectRetrieval(); // returns 'Live' records
|
||||||
Versioned::set_reading_mode(Versioned::DRAFT); // temporarily overwrite mode
|
Versioned::set_reading_mode(Versioned::DRAFT); // temporarily overwrite mode
|
||||||
$obj = MyRecord::getComplexObjectRetrieval(); // returns 'Stage' records
|
$obj = MyRecord::getComplexObjectRetrieval(); // returns 'Stage' records
|
||||||
Versioned::set_reading_mode($origMode); // reset current mode
|
Versioned::set_reading_mode($origMode); // reset current mode
|
||||||
```
|
```
|
||||||
|
|
||||||
### DataObject ownership
|
### DataObject ownership
|
||||||
@ -191,32 +191,32 @@ without requiring any custom code.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Versioned\Versioned;
|
use SilverStripe\Versioned\Versioned;
|
||||||
use SilverStripe\Assets\Image;
|
use SilverStripe\Assets\Image;
|
||||||
use Page;
|
use Page;
|
||||||
|
|
||||||
class MyPage extends Page
|
class MyPage extends Page
|
||||||
{
|
{
|
||||||
private static $has_many = [
|
private static $has_many = [
|
||||||
'Banners' => Banner::class
|
'Banners' => Banner::class
|
||||||
];
|
];
|
||||||
private static $owns = [
|
private static $owns = [
|
||||||
'Banners'
|
'Banners'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
class Banner extends Page
|
class Banner extends Page
|
||||||
{
|
{
|
||||||
private static $extensions = [
|
private static $extensions = [
|
||||||
Versioned::class
|
Versioned::class
|
||||||
];
|
];
|
||||||
private static $has_one = [
|
private static $has_one = [
|
||||||
'Parent' => MyPage::class,
|
'Parent' => MyPage::class,
|
||||||
'Image' => Image::class,
|
'Image' => Image::class,
|
||||||
];
|
];
|
||||||
private static $owns = [
|
private static $owns = [
|
||||||
'Image'
|
'Image'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -236,35 +236,35 @@ that can be used to traverse between each, and then by ensuring you configure bo
|
|||||||
E.g.
|
E.g.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Versioned\Versioned;
|
use SilverStripe\Versioned\Versioned;
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class MyParent extends DataObject
|
class MyParent extends DataObject
|
||||||
{
|
{
|
||||||
private static $extensions = [
|
private static $extensions = [
|
||||||
Versioned::class
|
Versioned::class
|
||||||
];
|
];
|
||||||
private static $owns = [
|
private static $owns = [
|
||||||
'ChildObjects'
|
'ChildObjects'
|
||||||
];
|
];
|
||||||
public function ChildObjects()
|
public function ChildObjects()
|
||||||
{
|
{
|
||||||
return MyChild::get();
|
return MyChild::get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class MyChild extends DataObject
|
class MyChild extends DataObject
|
||||||
{
|
{
|
||||||
private static $extensions = [
|
private static $extensions = [
|
||||||
Versioned::class
|
Versioned::class
|
||||||
];
|
];
|
||||||
private static $owned_by = [
|
private static $owned_by = [
|
||||||
'Parent'
|
'Parent'
|
||||||
];
|
];
|
||||||
public function Parent()
|
public function Parent()
|
||||||
{
|
{
|
||||||
return MyParent::get()->first();
|
return MyParent::get()->first();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -286,7 +286,7 @@ smaller modifications of the generated `DataList` objects.
|
|||||||
Example: Get the first 10 live records, filtered by creation date:
|
Example: Get the first 10 live records, filtered by creation date:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$records = Versioned::get_by_stage('MyRecord', Versioned::LIVE)->limit(10)->sort('Created', 'ASC');
|
$records = Versioned::get_by_stage('MyRecord', Versioned::LIVE)->limit(10)->sort('Created', 'ASC');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Permissions
|
### Permissions
|
||||||
@ -395,11 +395,11 @@ to force a specific stage, we recommend the `Controller->init()` method for this
|
|||||||
|
|
||||||
**mysite/code/MyController.php**
|
**mysite/code/MyController.php**
|
||||||
```php
|
```php
|
||||||
public function init()
|
public function init()
|
||||||
{
|
{
|
||||||
parent::init();
|
parent::init();
|
||||||
Versioned::set_stage(Versioned::DRAFT);
|
Versioned::set_stage(Versioned::DRAFT);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Controllers
|
### Controllers
|
||||||
|
@ -13,46 +13,46 @@ customise those fields as required.
|
|||||||
An example is `DataObject`, SilverStripe will automatically create your CMS interface so you can modify what you need.
|
An example is `DataObject`, SilverStripe will automatically create your CMS interface so you can modify what you need.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class MyDataObject extends DataObject
|
class MyDataObject extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'IsActive' => 'Boolean',
|
'IsActive' => 'Boolean',
|
||||||
'Title' => 'Varchar',
|
'Title' => 'Varchar',
|
||||||
'Content' => 'Text'
|
'Content' => 'Text'
|
||||||
];
|
];
|
||||||
|
|
||||||
public function getCMSFields()
|
public function getCMSFields()
|
||||||
{
|
{
|
||||||
// parent::getCMSFields() does all the hard work and creates the fields for Title, IsActive and Content.
|
// parent::getCMSFields() does all the hard work and creates the fields for Title, IsActive and Content.
|
||||||
$fields = parent::getCMSFields();
|
$fields = parent::getCMSFields();
|
||||||
$fields->dataFieldByName('IsActive')->setTitle('Is active?');
|
$fields->dataFieldByName('IsActive')->setTitle('Is active?');
|
||||||
|
|
||||||
return $fields;
|
return $fields;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
To fully customise your form fields, start with an empty FieldList.
|
To fully customise your form fields, start with an empty FieldList.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
|
|
||||||
public function getCMSFields()
|
public function getCMSFields()
|
||||||
{
|
{
|
||||||
$fields = FieldList::create(
|
$fields = FieldList::create(
|
||||||
TabSet::create("Root.Main",
|
TabSet::create("Root.Main",
|
||||||
CheckboxSetField::create('IsActive','Is active?'),
|
CheckboxSetField::create('IsActive','Is active?'),
|
||||||
TextField::create('Title'),
|
TextField::create('Title'),
|
||||||
TextareaField::create('Content')
|
TextareaField::create('Content')
|
||||||
->setRows(5)
|
->setRows(5)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
return $fields;
|
return $fields;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also alter the fields of built-in and module `DataObject` classes through your own
|
You can also alter the fields of built-in and module `DataObject` classes through your own
|
||||||
@ -64,16 +64,16 @@ The `$searchable_fields` property uses a mixed array format that can be used to
|
|||||||
system. The default is a set of array values listing the fields.
|
system. The default is a set of array values listing the fields.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class MyDataObject extends DataObject
|
class MyDataObject extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $searchable_fields = [
|
private static $searchable_fields = [
|
||||||
'Name',
|
'Name',
|
||||||
'ProductCode'
|
'ProductCode'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -82,16 +82,16 @@ default search filter assigned (usually an [ExactMatchFilter](api:SilverStripe\O
|
|||||||
additional information on `$searchable_fields`:
|
additional information on `$searchable_fields`:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class MyDataObject extends DataObject
|
class MyDataObject extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $searchable_fields = [
|
private static $searchable_fields = [
|
||||||
'Name' => 'PartialMatchFilter',
|
'Name' => 'PartialMatchFilter',
|
||||||
'ProductCode' => 'NumericField'
|
'ProductCode' => 'NumericField'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -99,59 +99,59 @@ If you assign a single string value, you can set it to be either a [FormField](a
|
|||||||
both, you can assign an array:
|
both, you can assign an array:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class MyDataObject extends DataObject
|
class MyDataObject extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $searchable_fields = [
|
private static $searchable_fields = [
|
||||||
'Name' => [
|
'Name' => [
|
||||||
'field' => 'TextField',
|
'field' => 'TextField',
|
||||||
'filter' => 'PartialMatchFilter',
|
'filter' => 'PartialMatchFilter',
|
||||||
],
|
],
|
||||||
'ProductCode' => [
|
'ProductCode' => [
|
||||||
'title' => 'Product code #',
|
'title' => 'Product code #',
|
||||||
'field' => 'NumericField',
|
'field' => 'NumericField',
|
||||||
'filter' => 'PartialMatchFilter',
|
'filter' => 'PartialMatchFilter',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
To include relations (`$has_one`, `$has_many` and `$many_many`) in your search, you can use a dot-notation.
|
To include relations (`$has_one`, `$has_many` and `$many_many`) in your search, you can use a dot-notation.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Team extends DataObject
|
class Team extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Title' => 'Varchar'
|
'Title' => 'Varchar'
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $many_many = [
|
private static $many_many = [
|
||||||
'Players' => 'Player'
|
'Players' => 'Player'
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $searchable_fields = [
|
private static $searchable_fields = [
|
||||||
'Title',
|
'Title',
|
||||||
'Players.Name',
|
'Players.Name',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
class Player extends DataObject
|
class Player extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Name' => 'Varchar',
|
'Name' => 'Varchar',
|
||||||
'Birthday' => 'Date'
|
'Birthday' => 'Date'
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $belongs_many_many = [
|
private static $belongs_many_many = [
|
||||||
'Teams' => 'Team'
|
'Teams' => 'Team'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -161,79 +161,79 @@ Summary fields can be used to show a quick overview of the data for a specific [
|
|||||||
is their display as table columns, e.g. in the search results of a [ModelAdmin](api:SilverStripe\Admin\ModelAdmin) CMS interface.
|
is their display as table columns, e.g. in the search results of a [ModelAdmin](api:SilverStripe\Admin\ModelAdmin) CMS interface.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class MyDataObject extends DataObject
|
class MyDataObject extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Name' => 'Text',
|
'Name' => 'Text',
|
||||||
'OtherProperty' => 'Text',
|
'OtherProperty' => 'Text',
|
||||||
'ProductCode' => 'Int',
|
'ProductCode' => 'Int',
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $summary_fields = [
|
private static $summary_fields = [
|
||||||
'Name',
|
'Name',
|
||||||
'ProductCode'
|
'ProductCode'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
To include relations or field manipulations in your summaries, you can use a dot-notation.
|
To include relations or field manipulations in your summaries, you can use a dot-notation.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class OtherObject extends DataObject
|
class OtherObject extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Title' => 'Varchar'
|
'Title' => 'Varchar'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
class MyDataObject extends DataObject
|
class MyDataObject extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Name' => 'Text',
|
'Name' => 'Text',
|
||||||
'Description' => 'HTMLText'
|
'Description' => 'HTMLText'
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $has_one = [
|
private static $has_one = [
|
||||||
'OtherObject' => 'OtherObject'
|
'OtherObject' => 'OtherObject'
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $summary_fields = [
|
private static $summary_fields = [
|
||||||
'Name' => 'Name',
|
'Name' => 'Name',
|
||||||
'Description.Summary' => 'Description (summary)',
|
'Description.Summary' => 'Description (summary)',
|
||||||
'OtherObject.Title' => 'Other Object Title'
|
'OtherObject.Title' => 'Other Object Title'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Non-textual elements (such as images and their manipulations) can also be used in summaries.
|
Non-textual elements (such as images and their manipulations) can also be used in summaries.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class MyDataObject extends DataObject
|
class MyDataObject extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Name' => 'Text'
|
'Name' => 'Text'
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $has_one = [
|
private static $has_one = [
|
||||||
'HeroImage' => 'Image'
|
'HeroImage' => 'Image'
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $summary_fields = [
|
private static $summary_fields = [
|
||||||
'Name' => 'Name',
|
'Name' => 'Name',
|
||||||
'HeroImage.CMSThumbnail' => 'Hero Image'
|
'HeroImage.CMSThumbnail' => 'Hero Image'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -25,20 +25,20 @@ Indexes are represented on a `DataObject` through the `DataObject::$indexes` arr
|
|||||||
descriptor. There are several supported notations:
|
descriptor. There are several supported notations:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class MyObject extends DataObject
|
class MyObject extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $indexes = [
|
private static $indexes = [
|
||||||
'<column-name>' => true,
|
'<column-name>' => true,
|
||||||
'<index-name>' => [
|
'<index-name>' => [
|
||||||
'type' => '<type>',
|
'type' => '<type>',
|
||||||
'columns' => ['<column-name>', '<other-column-name>'],
|
'columns' => ['<column-name>', '<other-column-name>'],
|
||||||
],
|
],
|
||||||
'<index-name>' => ['<column-name>', '<other-column-name>'],
|
'<index-name>' => ['<column-name>', '<other-column-name>'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The `<column-name>` is used to put a standard non-unique index on the column specified. For complex or large tables
|
The `<column-name>` is used to put a standard non-unique index on the column specified. For complex or large tables
|
||||||
@ -55,20 +55,20 @@ support the following:
|
|||||||
**mysite/code/MyTestObject.php**
|
**mysite/code/MyTestObject.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class MyTestObject extends DataObject
|
class MyTestObject extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'MyField' => 'Varchar',
|
'MyField' => 'Varchar',
|
||||||
'MyOtherField' => 'Varchar',
|
'MyOtherField' => 'Varchar',
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $indexes = [
|
private static $indexes = [
|
||||||
'MyIndexName' => ['MyField', 'MyOtherField'],
|
'MyIndexName' => ['MyField', 'MyOtherField'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Complex/Composite Indexes
|
## Complex/Composite Indexes
|
||||||
|
@ -10,30 +10,30 @@ object!
|
|||||||
A simple example is to set a field to the current date and time:
|
A simple example is to set a field to the current date and time:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
/**
|
/**
|
||||||
* Sets the Date field to the current date.
|
* Sets the Date field to the current date.
|
||||||
*/
|
*/
|
||||||
public function populateDefaults()
|
public function populateDefaults()
|
||||||
{
|
{
|
||||||
$this->Date = date('Y-m-d');
|
$this->Date = date('Y-m-d');
|
||||||
parent::populateDefaults();
|
parent::populateDefaults();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
It's also possible to get the data from any other source, or another object, just by using the usual data retrieval
|
It's also possible to get the data from any other source, or another object, just by using the usual data retrieval
|
||||||
methods. For example:
|
methods. For example:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
/**
|
/**
|
||||||
* This method combines the Title of the parent object with the Title of this
|
* This method combines the Title of the parent object with the Title of this
|
||||||
* object in the FullTitle field.
|
* object in the FullTitle field.
|
||||||
*/
|
*/
|
||||||
public function populateDefaults()
|
public function populateDefaults()
|
||||||
{
|
{
|
||||||
if($parent = $this->Parent()) {
|
if($parent = $this->Parent()) {
|
||||||
$this->FullTitle = $parent->Title . ': ' . $this->Title;
|
$this->FullTitle = $parent->Title . ': ' . $this->Title;
|
||||||
} else {
|
} else {
|
||||||
$this->FullTitle = $this->Title;
|
$this->FullTitle = $this->Title;
|
||||||
}
|
}
|
||||||
parent::populateDefaults();
|
parent::populateDefaults();
|
||||||
}
|
}
|
||||||
```
|
```
|
@ -35,23 +35,23 @@ along with a method that returns the first letter of the title. This
|
|||||||
will be used both for grouping and for the title in the template.
|
will be used both for grouping and for the title in the template.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Module extends DataObject
|
class Module extends DataObject
|
||||||
{
|
{
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Title' => 'Text'
|
'Title' => 'Text'
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the first letter of the module title, used for grouping.
|
* Returns the first letter of the module title, used for grouping.
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getTitleFirstLetter()
|
public function getTitleFirstLetter()
|
||||||
{
|
{
|
||||||
return $this->Title[0];
|
return $this->Title[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -59,24 +59,24 @@ The next step is to create a method or variable that will contain/return all the
|
|||||||
sorted by title. For this example this will be a method on the `Page` class.
|
sorted by title. For this example this will be a method on the `Page` class.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
use SilverStripe\ORM\GroupedList;
|
use SilverStripe\ORM\GroupedList;
|
||||||
|
|
||||||
class Page extends SiteTree
|
class Page extends SiteTree
|
||||||
{
|
{
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all modules, sorted by their title.
|
* Returns all modules, sorted by their title.
|
||||||
* @return GroupedList
|
* @return GroupedList
|
||||||
*/
|
*/
|
||||||
public function getGroupedModules()
|
public function getGroupedModules()
|
||||||
{
|
{
|
||||||
return GroupedList::create(Module::get()->sort('Title'));
|
return GroupedList::create(Module::get()->sort('Title'));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The final step is to render this into a template. The `GroupedBy()` method breaks up the set into
|
The final step is to render this into a template. The `GroupedBy()` method breaks up the set into
|
||||||
@ -84,16 +84,16 @@ a number of sets, grouped by the field that is passed as the parameter.
|
|||||||
In this case, the `getTitleFirstLetter()` method defined earlier is used to break them up.
|
In this case, the `getTitleFirstLetter()` method defined earlier is used to break them up.
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
<%-- Modules list grouped by TitleFirstLetter --%>
|
<%-- Modules list grouped by TitleFirstLetter --%>
|
||||||
<h2>Modules</h2>
|
<h2>Modules</h2>
|
||||||
<% loop $GroupedModules.GroupedBy(TitleFirstLetter) %>
|
<% loop $GroupedModules.GroupedBy(TitleFirstLetter) %>
|
||||||
<h3>$TitleFirstLetter</h3>
|
<h3>$TitleFirstLetter</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<% loop $Children %>
|
<% loop $Children %>
|
||||||
<li>$Title</li>
|
<li>$Title</li>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</ul>
|
</ul>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Grouping Sets By Month
|
## Grouping Sets By Month
|
||||||
@ -109,61 +109,61 @@ which is automatically set when the record is first written to the database.
|
|||||||
This will have a method which returns the month it was posted in:
|
This will have a method which returns the month it was posted in:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Module extends DataObject
|
class Module extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the month name this news item was posted in.
|
* Returns the month name this news item was posted in.
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getMonthCreated()
|
public function getMonthCreated()
|
||||||
{
|
{
|
||||||
return date('F', strtotime($this->Created));
|
return date('F', strtotime($this->Created));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The next step is to create a method that will return all records that exist,
|
The next step is to create a method that will return all records that exist,
|
||||||
sorted by month name from January to December. This can be accomplshed by sorting by the `Created` field:
|
sorted by month name from January to December. This can be accomplshed by sorting by the `Created` field:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
use SilverStripe\ORM\GroupedList;
|
use SilverStripe\ORM\GroupedList;
|
||||||
|
|
||||||
class Page extends SiteTree
|
class Page extends SiteTree
|
||||||
{
|
{
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all news items, sorted by the month they were posted
|
* Returns all news items, sorted by the month they were posted
|
||||||
* @return GroupedList
|
* @return GroupedList
|
||||||
*/
|
*/
|
||||||
public function getGroupedModulesByDate()
|
public function getGroupedModulesByDate()
|
||||||
{
|
{
|
||||||
return GroupedList::create(Module::get()->sort('Created'));
|
return GroupedList::create(Module::get()->sort('Created'));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
The final step is the render this into the template using the [GroupedList::GroupedBy()](api:SilverStripe\ORM\GroupedList::GroupedBy()) method.
|
The final step is the render this into the template using the [GroupedList::GroupedBy()](api:SilverStripe\ORM\GroupedList::GroupedBy()) method.
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
// Modules list grouped by the Month Posted
|
// Modules list grouped by the Month Posted
|
||||||
<h2>Modules</h2>
|
<h2>Modules</h2>
|
||||||
<% loop $GroupedModulesByDate.GroupedBy(MonthCreated) %>
|
<% loop $GroupedModulesByDate.GroupedBy(MonthCreated) %>
|
||||||
<h3>$MonthCreated</h3>
|
<h3>$MonthCreated</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<% loop $Children %>
|
<% loop $Children %>
|
||||||
<li>$Title ($Created.Nice)</li>
|
<li>$Title ($Created.Nice)</li>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</ul>
|
</ul>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
```
|
```
|
||||||
## Related
|
## Related
|
||||||
|
|
||||||
|
@ -13,32 +13,32 @@ An example of a SilverStripe template is below:
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<% base_tag %>
|
<% base_tag %>
|
||||||
<title>$Title</title>
|
<title>$Title</title>
|
||||||
<% require themedCSS("screen") %>
|
<% require themedCSS("screen") %>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<h1>Bob's Chicken Shack</h1>
|
<h1>Bob's Chicken Shack</h1>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<% with $CurrentMember %>
|
<% with $CurrentMember %>
|
||||||
<p>Welcome $FirstName $Surname.</p>
|
<p>Welcome $FirstName $Surname.</p>
|
||||||
<% end_with %>
|
<% end_with %>
|
||||||
|
|
||||||
<% if $Dishes %>
|
<% if $Dishes %>
|
||||||
<ul>
|
<ul>
|
||||||
<% loop $Dishes %>
|
<% loop $Dishes %>
|
||||||
<li>$Title ($Price.Nice)</li>
|
<li>$Title ($Price.Nice)</li>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</ul>
|
</ul>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
|
|
||||||
<% include Footer %>
|
<% include Footer %>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="note">
|
<div class="note">
|
||||||
@ -70,7 +70,7 @@ alphabetic character or underscore, with subsequent characters being alphanumeri
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
$Title
|
$Title
|
||||||
```
|
```
|
||||||
|
|
||||||
This inserts the value of the Title database field of the page being displayed in place of `$Title`.
|
This inserts the value of the Title database field of the page being displayed in place of `$Title`.
|
||||||
@ -79,9 +79,9 @@ Variables can be chained together, and include arguments.
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
$Foo
|
$Foo
|
||||||
$Foo(param)
|
$Foo(param)
|
||||||
$Foo.Bar
|
$Foo.Bar
|
||||||
```
|
```
|
||||||
|
|
||||||
These variables will call a method / field on the object and insert the returned value as a string into the template.
|
These variables will call a method / field on the object and insert the returned value as a string into the template.
|
||||||
@ -104,17 +104,17 @@ Variables can come from your database fields, or custom methods you define on yo
|
|||||||
**mysite/code/Page.php**
|
**mysite/code/Page.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function UsersIpAddress()
|
public function UsersIpAddress()
|
||||||
{
|
{
|
||||||
return $this->getRequest()->getIP();
|
return $this->getRequest()->getIP();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**mysite/code/Page.ss**
|
**mysite/code/Page.ss**
|
||||||
|
|
||||||
```html
|
```html
|
||||||
|
|
||||||
<p>You are coming from $UsersIpAddress.</p>
|
<p>You are coming from $UsersIpAddress.</p>
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="node" markdown="1">
|
<div class="node" markdown="1">
|
||||||
@ -130,11 +130,11 @@ record and any subclasses of those two.
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
$Title
|
$Title
|
||||||
// returns the page `Title` property
|
// returns the page `Title` property
|
||||||
|
|
||||||
$Content
|
$Content
|
||||||
// returns the page `Content` property
|
// returns the page `Content` property
|
||||||
```
|
```
|
||||||
|
|
||||||
## Conditional Logic
|
## Conditional Logic
|
||||||
@ -143,18 +143,18 @@ The simplest conditional block is to check for the presence of a value (does not
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% if $CurrentMember %>
|
<% if $CurrentMember %>
|
||||||
<p>You are logged in as $CurrentMember.FirstName $CurrentMember.Surname.</p>
|
<p>You are logged in as $CurrentMember.FirstName $CurrentMember.Surname.</p>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
```
|
```
|
||||||
|
|
||||||
A conditional can also check for a value other than falsy.
|
A conditional can also check for a value other than falsy.
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% if $MyDinner == "kipper" %>
|
<% if $MyDinner == "kipper" %>
|
||||||
Yummy, kipper for tea.
|
Yummy, kipper for tea.
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="notice" markdown="1">
|
<div class="notice" markdown="1">
|
||||||
@ -165,24 +165,24 @@ Conditionals can also provide the `else` case.
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% if $MyDinner == "kipper" %>
|
<% if $MyDinner == "kipper" %>
|
||||||
Yummy, kipper for tea
|
Yummy, kipper for tea
|
||||||
<% else %>
|
<% else %>
|
||||||
I wish I could have kipper :-(
|
I wish I could have kipper :-(
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
```
|
```
|
||||||
|
|
||||||
`else_if` commands can be used to handle multiple `if` statements.
|
`else_if` commands can be used to handle multiple `if` statements.
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% if $MyDinner == "quiche" %>
|
<% if $MyDinner == "quiche" %>
|
||||||
Real men don't eat quiche
|
Real men don't eat quiche
|
||||||
<% else_if $MyDinner == $YourDinner %>
|
<% else_if $MyDinner == $YourDinner %>
|
||||||
We both have good taste
|
We both have good taste
|
||||||
<% else %>
|
<% else %>
|
||||||
Can I have some of your chips?
|
Can I have some of your chips?
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Negation
|
### Negation
|
||||||
@ -191,9 +191,9 @@ The inverse of `<% if %>` is `<% if not %>`.
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% if not $DinnerInOven %>
|
<% if not $DinnerInOven %>
|
||||||
I'm going out for dinner tonight.
|
I'm going out for dinner tonight.
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Boolean Logic
|
### Boolean Logic
|
||||||
@ -204,18 +204,18 @@ If *either* of the conditions is true.
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% if $MyDinner == "kipper" || $MyDinner == "salmon" %>
|
<% if $MyDinner == "kipper" || $MyDinner == "salmon" %>
|
||||||
yummy, fish for tea
|
yummy, fish for tea
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
```
|
```
|
||||||
|
|
||||||
If *both* of the conditions are true.
|
If *both* of the conditions are true.
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% if $MyDinner == "quiche" && $YourDinner == "kipper" %>
|
<% if $MyDinner == "quiche" && $YourDinner == "kipper" %>
|
||||||
Lets swap dinners
|
Lets swap dinners
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Inequalities
|
### Inequalities
|
||||||
@ -224,9 +224,9 @@ You can use inequalities like `<`, `<=`, `>`, `>=` to compare numbers.
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% if $Number >= "5" && $Number <= "10" %>
|
<% if $Number >= "5" && $Number <= "10" %>
|
||||||
Number between 5 and 10
|
Number between 5 and 10
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Includes
|
## Includes
|
||||||
@ -237,8 +237,8 @@ an additional `Includes` directory will be inserted into the resolved path just
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% include SideBar %> <!-- chooses templates/Includes/Sidebar.ss -->
|
<% include SideBar %> <!-- chooses templates/Includes/Sidebar.ss -->
|
||||||
<% include MyNamespace/SideBar %> <!-- chooses templates/MyNamespace/Includes/Sidebar.ss -->
|
<% include MyNamespace/SideBar %> <!-- chooses templates/MyNamespace/Includes/Sidebar.ss -->
|
||||||
```
|
```
|
||||||
|
|
||||||
When using subfolders in your template structure
|
When using subfolders in your template structure
|
||||||
@ -246,7 +246,7 @@ When using subfolders in your template structure
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% include MyNamespace/SideBar %> <!-- chooses templates/MyNamespace/Includes/Sidebar.ss -->
|
<% include MyNamespace/SideBar %> <!-- chooses templates/MyNamespace/Includes/Sidebar.ss -->
|
||||||
```
|
```
|
||||||
|
|
||||||
The `include` tag can be particularly helpful for nested functionality and breaking large templates up. In this example,
|
The `include` tag can be particularly helpful for nested functionality and breaking large templates up. In this example,
|
||||||
@ -254,9 +254,9 @@ the include only happens if the user is logged in.
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% if $CurrentMember %>
|
<% if $CurrentMember %>
|
||||||
<% include MembersOnlyInclude %>
|
<% include MembersOnlyInclude %>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
```
|
```
|
||||||
|
|
||||||
Includes can't directly access the parent scope when the include is included. However you can pass arguments to the
|
Includes can't directly access the parent scope when the include is included. However you can pass arguments to the
|
||||||
@ -264,9 +264,9 @@ include.
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% with $CurrentMember %>
|
<% with $CurrentMember %>
|
||||||
<% include MemberDetails Top=$Top, Name=$Name %>
|
<% include MemberDetails Top=$Top, Name=$Name %>
|
||||||
<% end_with %>
|
<% end_with %>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Looping Over Lists
|
## Looping Over Lists
|
||||||
@ -276,13 +276,13 @@ collection.
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<h1>Children of $Title</h1>
|
<h1>Children of $Title</h1>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<% loop $Children %>
|
<% loop $Children %>
|
||||||
<li>$Title</li>
|
<li>$Title</li>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</ul>
|
</ul>
|
||||||
```
|
```
|
||||||
|
|
||||||
This snippet loops over the children of a page, and generates an unordered list showing the `Title` property from each
|
This snippet loops over the children of a page, and generates an unordered list showing the `Title` property from each
|
||||||
@ -305,55 +305,55 @@ Sorting the list by a given field.
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<% loop $Children.Sort(Title, ASC) %>
|
<% loop $Children.Sort(Title, ASC) %>
|
||||||
<li>$Title</li>
|
<li>$Title</li>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</ul>
|
</ul>
|
||||||
```
|
```
|
||||||
|
|
||||||
Limiting the number of items displayed.
|
Limiting the number of items displayed.
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<% loop $Children.Limit(10) %>
|
<% loop $Children.Limit(10) %>
|
||||||
<li>$Title</li>
|
<li>$Title</li>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</ul>
|
</ul>
|
||||||
```
|
```
|
||||||
|
|
||||||
Reversing the loop.
|
Reversing the loop.
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<% loop $Children.Reverse %>
|
<% loop $Children.Reverse %>
|
||||||
<li>$Title</li>
|
<li>$Title</li>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</ul>
|
</ul>
|
||||||
```
|
```
|
||||||
|
|
||||||
Filtering the loop.
|
Filtering the loop.
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<% loop $Children.Filter('School', 'College') %>
|
<% loop $Children.Filter('School', 'College') %>
|
||||||
<li>$Title</li>
|
<li>$Title</li>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</ul>
|
</ul>
|
||||||
```
|
```
|
||||||
|
|
||||||
Methods can also be chained.
|
Methods can also be chained.
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<% loop $Children.Filter('School', 'College').Sort(Score, DESC) %>
|
<% loop $Children.Filter('School', 'College').Sort(Score, DESC) %>
|
||||||
<li>$Title</li>
|
<li>$Title</li>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</ul>
|
</ul>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Position Indicators
|
### Position Indicators
|
||||||
@ -373,15 +373,15 @@ iteration.
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<% loop $Children.Reverse %>
|
<% loop $Children.Reverse %>
|
||||||
<% if First %>
|
<% if First %>
|
||||||
<li>My Favourite</li>
|
<li>My Favourite</li>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
|
|
||||||
<li class="$EvenOdd">Child $Pos of $TotalItems - $Title</li>
|
<li class="$EvenOdd">Child $Pos of $TotalItems - $Title</li>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</ul>
|
</ul>
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="info" markdown="1">
|
<div class="info" markdown="1">
|
||||||
@ -395,19 +395,19 @@ $Modulus and $MultipleOf can help to build column and grid layouts.
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
// returns an int
|
// returns an int
|
||||||
$Modulus(value, offset)
|
$Modulus(value, offset)
|
||||||
|
|
||||||
// returns a boolean.
|
// returns a boolean.
|
||||||
$MultipleOf(factor, offset)
|
$MultipleOf(factor, offset)
|
||||||
|
|
||||||
<% loop $Children %>
|
<% loop $Children %>
|
||||||
<div class="column-{$Modulus(4)}">
|
<div class="column-{$Modulus(4)}">
|
||||||
...
|
...
|
||||||
</div>
|
</div>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
|
|
||||||
// returns <div class="column-3">, <div class="column-2">,
|
// returns <div class="column-3">, <div class="column-2">,
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="hint" markdown="1">
|
<div class="hint" markdown="1">
|
||||||
@ -420,11 +420,11 @@ after every 3rd item.
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% loop $Children %>
|
<% loop $Children %>
|
||||||
<% if $MultipleOf(3) %>
|
<% if $MultipleOf(3) %>
|
||||||
<br>
|
<br>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Escaping
|
### Escaping
|
||||||
@ -433,24 +433,24 @@ Sometimes you will have template tags which need to roll into one another. Use `
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
$Foopx // will returns "" (as it looks for a `Foopx` value)
|
$Foopx // will returns "" (as it looks for a `Foopx` value)
|
||||||
{$Foo}px // returns "3px" (CORRECT)
|
{$Foo}px // returns "3px" (CORRECT)
|
||||||
```
|
```
|
||||||
|
|
||||||
Or when having a `$` sign in front of the variable such as displaying money.
|
Or when having a `$` sign in front of the variable such as displaying money.
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
$$Foo // returns ""
|
$$Foo // returns ""
|
||||||
${$Foo} // returns "$3"
|
${$Foo} // returns "$3"
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also use a backslash to escape the name of the variable, such as:
|
You can also use a backslash to escape the name of the variable, such as:
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
$Foo // returns "3"
|
$Foo // returns "3"
|
||||||
\$Foo // returns "$Foo"
|
\$Foo // returns "$Foo"
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="hint" markdown="1">
|
<div class="hint" markdown="1">
|
||||||
@ -481,15 +481,15 @@ When in a particular scope, `$Up` takes the scope back to the previous level.
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<h1>Children of '$Title'</h1>
|
<h1>Children of '$Title'</h1>
|
||||||
|
|
||||||
<% loop $Children %>
|
<% loop $Children %>
|
||||||
<p>Page '$Title' is a child of '$Up.Title'</p>
|
<p>Page '$Title' is a child of '$Up.Title'</p>
|
||||||
|
|
||||||
<% loop $Children %>
|
<% loop $Children %>
|
||||||
<p>Page '$Title' is a grandchild of '$Up.Up.Title'</p>
|
<p>Page '$Title' is a grandchild of '$Up.Up.Title'</p>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
```
|
```
|
||||||
|
|
||||||
Given the following structure, it will output the text.
|
Given the following structure, it will output the text.
|
||||||
@ -514,11 +514,11 @@ Additional selectors implicitely change the scope so you need to put additional
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<h1>Children of '$Title'</h1>
|
<h1>Children of '$Title'</h1>
|
||||||
<% loop $Children.Sort('Title').First %>
|
<% loop $Children.Sort('Title').First %>
|
||||||
<%-- We have two additional selectors in the loop expression so... --%>
|
<%-- We have two additional selectors in the loop expression so... --%>
|
||||||
<p>Page '$Title' is a child of '$Up.Up.Up.Title'</p>
|
<p>Page '$Title' is a child of '$Up.Up.Up.Title'</p>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Top
|
#### Top
|
||||||
@ -528,15 +528,15 @@ page. The previous example could be rewritten to use the following syntax.
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<h1>Children of '$Title'</h1>
|
<h1>Children of '$Title'</h1>
|
||||||
|
|
||||||
<% loop $Children %>
|
<% loop $Children %>
|
||||||
<p>Page '$Title' is a child of '$Top.Title'</p>
|
<p>Page '$Title' is a child of '$Top.Title'</p>
|
||||||
|
|
||||||
<% loop $Children %>
|
<% loop $Children %>
|
||||||
<p>Page '$Title' is a grandchild of '$Top.Title'</p>
|
<p>Page '$Title' is a grandchild of '$Top.Title'</p>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
```
|
```
|
||||||
|
|
||||||
### With
|
### With
|
||||||
@ -545,16 +545,16 @@ The `<% with %>` tag lets you change into a new scope. Consider the following ex
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% with $CurrentMember %>
|
<% with $CurrentMember %>
|
||||||
Hello, $FirstName, welcome back. Your current balance is $Balance.
|
Hello, $FirstName, welcome back. Your current balance is $Balance.
|
||||||
<% end_with %>
|
<% end_with %>
|
||||||
```
|
```
|
||||||
|
|
||||||
This is functionalty the same as the following:
|
This is functionalty the same as the following:
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
Hello, $CurrentMember.FirstName, welcome back. Your current balance is $CurrentMember.Balance
|
Hello, $CurrentMember.FirstName, welcome back. Your current balance is $CurrentMember.Balance
|
||||||
```
|
```
|
||||||
|
|
||||||
Notice that the first example is much tidier, as it removes the repeated use of the `$CurrentMember` accessor.
|
Notice that the first example is much tidier, as it removes the repeated use of the `$CurrentMember` accessor.
|
||||||
@ -569,7 +569,7 @@ refer directly to properties and methods of the [Member](api:SilverStripe\Securi
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
$Me
|
$Me
|
||||||
```
|
```
|
||||||
|
|
||||||
## Comments
|
## Comments
|
||||||
@ -578,7 +578,7 @@ Using standard HTML comments is supported. These comments will be included in th
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
$EditForm <!-- Some public comment about the form -->
|
$EditForm <!-- Some public comment about the form -->
|
||||||
```
|
```
|
||||||
|
|
||||||
However you can also use special SilverStripe comments which will be stripped out of the published site. This is useful
|
However you can also use special SilverStripe comments which will be stripped out of the published site. This is useful
|
||||||
@ -586,7 +586,7 @@ for adding notes for other developers but for things you don't want published in
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
$EditForm <%-- Some hidden comment about the form --%>
|
$EditForm <%-- Some hidden comment about the form --%>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Related
|
## Related
|
||||||
|
@ -32,11 +32,11 @@ functionality may not be included.
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<% base_tag %>
|
<% base_tag %>
|
||||||
|
|
||||||
..
|
..
|
||||||
</head>
|
</head>
|
||||||
```
|
```
|
||||||
|
|
||||||
The `<% base_tag %>` placeholder is replaced with the HTML base element. Relative links within a document (such as <img
|
The `<% base_tag %>` placeholder is replaced with the HTML base element. Relative links within a document (such as <img
|
||||||
@ -53,17 +53,17 @@ A `<% base_tag %>` is nearly always required or assumed by SilverStripe to exist
|
|||||||
|
|
||||||
Returns the currently logged in [Member](api:SilverStripe\Security\Member) instance, if there is one logged in.```ss
|
Returns the currently logged in [Member](api:SilverStripe\Security\Member) instance, if there is one logged in.```ss
|
||||||
|
|
||||||
<% if $CurrentMember %>
|
<% if $CurrentMember %>
|
||||||
Welcome Back, $CurrentMember.FirstName
|
Welcome Back, $CurrentMember.FirstName
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Title and Menu Title
|
## Title and Menu Title
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
$Title
|
$Title
|
||||||
$MenuTitle
|
$MenuTitle
|
||||||
```
|
```
|
||||||
|
|
||||||
Most objects within SilverStripe will respond to `$Title` (i.e they should have a `Title` database field or at least a
|
Most objects within SilverStripe will respond to `$Title` (i.e they should have a `Title` database field or at least a
|
||||||
@ -80,7 +80,7 @@ If `MenuTitle` is left blank by the CMS author, it'll just default to the value
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
$Content
|
$Content
|
||||||
```
|
```
|
||||||
|
|
||||||
It returns the database content of the `Content` property. With the CMS Module, this is the value of the WYSIWYG editor
|
It returns the database content of the `Content` property. With the CMS Module, this is the value of the WYSIWYG editor
|
||||||
@ -104,7 +104,7 @@ web pages. You'll need to install it via `composer`.
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
$SiteConfig.Title
|
$SiteConfig.Title
|
||||||
```
|
```
|
||||||
|
|
||||||
The [SiteConfig](../configuration/siteconfig) object allows content authors to modify global data in the CMS, rather
|
The [SiteConfig](../configuration/siteconfig) object allows content authors to modify global data in the CMS, rather
|
||||||
@ -128,30 +128,30 @@ By default `$MetaTags` renders:
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<title>Title of the Page</title>
|
<title>Title of the Page</title>
|
||||||
<meta name="generator" http-equiv="generator" content="SilverStripe 3.0" />
|
<meta name="generator" http-equiv="generator" content="SilverStripe 3.0" />
|
||||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
||||||
```
|
```
|
||||||
|
|
||||||
`$MetaTags(false)` will render```ss
|
`$MetaTags(false)` will render```ss
|
||||||
|
|
||||||
<meta name="generator" http-equiv="generator" content="SilverStripe 3.0" />
|
<meta name="generator" http-equiv="generator" content="SilverStripe 3.0" />
|
||||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
||||||
```
|
```
|
||||||
|
|
||||||
If using `$MetaTags(false)` we can provide a more custom `title`.
|
If using `$MetaTags(false)` we can provide a more custom `title`.
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
$MetaTags(false)
|
$MetaTags(false)
|
||||||
<title>$Title - Bob's Fantasy Football</title>
|
<title>$Title - Bob's Fantasy Football</title>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Links
|
## Links
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<a href="$Link">..</a>
|
<a href="$Link">..</a>
|
||||||
```
|
```
|
||||||
|
|
||||||
All objects that could be accessible in SilverStripe should define a `Link` method and an `AbsoluteLink` method. Link
|
All objects that could be accessible in SilverStripe should define a `Link` method and an `AbsoluteLink` method. Link
|
||||||
@ -160,19 +160,19 @@ link.
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
$Link
|
$Link
|
||||||
<!-- returns /about-us/offices/ -->
|
<!-- returns /about-us/offices/ -->
|
||||||
|
|
||||||
$AbsoluteLink
|
$AbsoluteLink
|
||||||
<!-- returns http://yoursite.com/about-us/offices/ -->
|
<!-- returns http://yoursite.com/about-us/offices/ -->
|
||||||
```
|
```
|
||||||
|
|
||||||
### Linking Modes
|
### Linking Modes
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
$isSection
|
$isSection
|
||||||
$isCurrent
|
$isCurrent
|
||||||
```
|
```
|
||||||
|
|
||||||
When looping over a list of `SiteTree` instances through a `<% loop $Menu %>` or `<% loop $Children %>`, `$isSection` and `$isCurrent`
|
When looping over a list of `SiteTree` instances through a `<% loop $Menu %>` or `<% loop $Children %>`, `$isSection` and `$isCurrent`
|
||||||
@ -182,11 +182,11 @@ For instance, to only show the menu item linked if it's the current one:
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% if $isCurrent %>
|
<% if $isCurrent %>
|
||||||
$Title
|
$Title
|
||||||
<% else %>
|
<% else %>
|
||||||
<a href="$Link">$Title</a>
|
<a href="$Link">$Title</a>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
```
|
```
|
||||||
|
|
||||||
An example for checking for `current` or `section` is as follows:
|
An example for checking for `current` or `section` is as follows:
|
||||||
@ -202,9 +202,9 @@ An example for checking for `current` or `section` is as follows:
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% if $InSection(about-us) %>
|
<% if $InSection(about-us) %>
|
||||||
<p>You are viewing the about us section</p>
|
<p>You are viewing the about us section</p>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
```
|
```
|
||||||
|
|
||||||
### URLSegment
|
### URLSegment
|
||||||
@ -215,11 +215,11 @@ It can be used within templates to generate anchors or other CSS classes.
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<div id="section-$URLSegment">
|
<div id="section-$URLSegment">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- returns <div id="section-offices"> -->
|
<!-- returns <div id="section-offices"> -->
|
||||||
```
|
```
|
||||||
|
|
||||||
## ClassName
|
## ClassName
|
||||||
@ -230,18 +230,18 @@ behavior based on the page type used:
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<body class="$ClassName">
|
<body class="$ClassName">
|
||||||
|
|
||||||
<!-- returns <body class="HomePage">, <body class="BlogPage"> -->
|
<!-- returns <body class="HomePage">, <body class="BlogPage"> -->
|
||||||
```
|
```
|
||||||
|
|
||||||
## Children Loops
|
## Children Loops
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% loop $Children %>
|
<% loop $Children %>
|
||||||
|
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
```
|
```
|
||||||
|
|
||||||
Will loop over all Children records of the current object context. Children are pages that sit under the current page in
|
Will loop over all Children records of the current object context. Children are pages that sit under the current page in
|
||||||
@ -256,9 +256,9 @@ context.
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% loop $ChildrenOf(<my-page-url>) %>
|
<% loop $ChildrenOf(<my-page-url>) %>
|
||||||
|
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
```
|
```
|
||||||
|
|
||||||
Will create a list of the children of the given page, as identified by its `URLSegment` value. This can come in handy
|
Will create a list of the children of the given page, as identified by its `URLSegment` value. This can come in handy
|
||||||
@ -274,18 +274,18 @@ preference, `AllChildren` does not filter by `ShowInMenus`.
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% loop $AllChildren %>
|
<% loop $AllChildren %>
|
||||||
...
|
...
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Menu Loops
|
### Menu Loops
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% loop $Menu(1) %>
|
<% loop $Menu(1) %>
|
||||||
...
|
...
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
```
|
```
|
||||||
|
|
||||||
`$Menu(1)` returns the top-level menu of the website. You can also create a sub-menu using `$Menu(2)`, and so forth.
|
`$Menu(1)` returns the top-level menu of the website. You can also create a sub-menu using `$Menu(2)`, and so forth.
|
||||||
@ -298,9 +298,9 @@ Pages with the `ShowInMenus` property set to `false` will be filtered out.
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% with $Page(my-page) %>
|
<% with $Page(my-page) %>
|
||||||
$Title
|
$Title
|
||||||
<% end_with %>
|
<% end_with %>
|
||||||
```
|
```
|
||||||
|
|
||||||
Page will return a single page from site, looking it up by URL.
|
Page will return a single page from site, looking it up by URL.
|
||||||
@ -311,9 +311,9 @@ Page will return a single page from site, looking it up by URL.
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% with $Level(1) %>
|
<% with $Level(1) %>
|
||||||
$Title
|
$Title
|
||||||
<% end_with %>
|
<% end_with %>
|
||||||
```
|
```
|
||||||
|
|
||||||
Will return a page in the current path, at the level specified by the numbers. It is based on the current page context,
|
Will return a page in the current path, at the level specified by the numbers. It is based on the current page context,
|
||||||
@ -329,13 +329,13 @@ For example, imagine you're on the "bob marley" page, which is three levels in:
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<!-- given we're on 'Bob Marley' in "about us > staff > bob marley" -->
|
<!-- given we're on 'Bob Marley' in "about us > staff > bob marley" -->
|
||||||
|
|
||||||
$Parent.Title
|
$Parent.Title
|
||||||
<!-- returns 'staff' -->
|
<!-- returns 'staff' -->
|
||||||
|
|
||||||
$Parent.Parent.Title
|
$Parent.Parent.Title
|
||||||
<!-- returns 'about us' -->
|
<!-- returns 'about us' -->
|
||||||
```
|
```
|
||||||
|
|
||||||
## Navigating Scope
|
## Navigating Scope
|
||||||
@ -352,18 +352,18 @@ While you can achieve breadcrumbs through the `$Level(<level>)` control manually
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
$Breadcrumbs
|
$Breadcrumbs
|
||||||
```
|
```
|
||||||
|
|
||||||
By default, it uses the template defined in `cms/templates/BreadcrumbsTemplate.ss`
|
By default, it uses the template defined in `cms/templates/BreadcrumbsTemplate.ss`
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% if $Pages %>
|
<% if $Pages %>
|
||||||
<% loop $Pages %>
|
<% loop $Pages %>
|
||||||
<% if $Last %>$Title.XML<% else %><a href="$Link">$MenuTitle.XML</a> »<% end_if %>
|
<% if $Last %>$Title.XML<% else %><a href="$Link">$MenuTitle.XML</a> »<% end_if %>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="info" markdown="1">
|
<div class="info" markdown="1">
|
||||||
@ -375,7 +375,7 @@ To customise the markup that the `$Breadcrumbs` generates, copy `cms/templates/B
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
$Form
|
$Form
|
||||||
```
|
```
|
||||||
|
|
||||||
A page will normally contain some content and potentially a form of some kind. For example, the log-in page has a the
|
A page will normally contain some content and potentially a form of some kind. For example, the log-in page has a the
|
||||||
|
@ -17,9 +17,9 @@ The `Requirements` class can work with arbitrary file paths.
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% require css("<my-module-dir>/css/some_file.css") %>
|
<% require css("<my-module-dir>/css/some_file.css") %>
|
||||||
<% require themedCSS("some_themed_file") %>
|
<% require themedCSS("some_themed_file") %>
|
||||||
<% require javascript("<my-module-dir>/javascript/some_file.js") %>
|
<% require javascript("<my-module-dir>/javascript/some_file.js") %>
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="alert" markdown="1">
|
<div class="alert" markdown="1">
|
||||||
@ -97,11 +97,11 @@ You can also use the second argument to add the 'async' and/or 'defer attributes
|
|||||||
|
|
||||||
```php
|
```php
|
||||||
Requirements::javascript(
|
Requirements::javascript(
|
||||||
"<my-module-dir>/javascript/some_file.js",
|
"<my-module-dir>/javascript/some_file.js",
|
||||||
[
|
[
|
||||||
"async" => true,
|
"async" => true,
|
||||||
"defer" => true,
|
"defer" => true,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -281,13 +281,13 @@ class MyMinifier implements Requirements_Minifier
|
|||||||
* @param string $type Either js or css
|
* @param string $type Either js or css
|
||||||
* @param string $filename Name of file to display in case of error
|
* @param string $filename Name of file to display in case of error
|
||||||
* @return string minified content
|
* @return string minified content
|
||||||
*/
|
*/
|
||||||
public function minify ($content, $type, $fileName)
|
public function minify ($content, $type, $fileName)
|
||||||
{
|
{
|
||||||
// Minify $content;
|
// Minify $content;
|
||||||
|
|
||||||
return $minifiedContent;
|
return $minifiedContent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -16,11 +16,11 @@ name in the `mysite/templates/email` folder or in the `themes/your_theme/templat
|
|||||||
|
|
||||||
**mysite/templates/email/GenericEmail.ss**
|
**mysite/templates/email/GenericEmail.ss**
|
||||||
|
|
||||||
:::ss
|
```ss
|
||||||
$Body
|
$Body
|
||||||
|
|
||||||
<p>Thanks from Bob's Fantasy Football League.</p>
|
<p>Thanks from Bob's Fantasy Football League.</p>
|
||||||
|
```
|
||||||
All emails going out of our application will have the footer `Thanks from Bob's Fantasy Football Leaguee` added.
|
All emails going out of our application will have the footer `Thanks from Bob's Fantasy Football Leaguee` added.
|
||||||
|
|
||||||
<div class="alert" markdown="1">
|
<div class="alert" markdown="1">
|
||||||
@ -103,19 +103,19 @@ footer and navigation will remain the same and we don't want to replicate this w
|
|||||||
|
|
||||||
**mysite/templates/Page.ss**
|
**mysite/templates/Page.ss**
|
||||||
```ss
|
```ss
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
..
|
..
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<% include Header %>
|
<% include Header %>
|
||||||
<% include Navigation %>
|
<% include Navigation %>
|
||||||
|
|
||||||
$Layout
|
$Layout
|
||||||
|
|
||||||
<% include Footer %>
|
<% include Footer %>
|
||||||
</body>
|
</body>
|
||||||
``
|
``
|
||||||
**mysite/templates/Layout/Page.ss**
|
**mysite/templates/Layout/Page.ss**
|
||||||
```ss
|
```ss
|
||||||
|
@ -9,22 +9,22 @@ All functions that provide data to templates must have no side effects, as the v
|
|||||||
example, this controller method will not behave as you might imagine.
|
example, this controller method will not behave as you might imagine.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
private $counter = 0;
|
private $counter = 0;
|
||||||
|
|
||||||
public function Counter()
|
public function Counter()
|
||||||
{
|
{
|
||||||
$this->counter += 1;
|
$this->counter += 1;
|
||||||
|
|
||||||
return $this->counter;
|
return $this->counter;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
$Counter, $Counter, $Counter
|
$Counter, $Counter, $Counter
|
||||||
|
|
||||||
// returns 1, 1, 1
|
// returns 1, 1, 1
|
||||||
```
|
```
|
||||||
|
|
||||||
When we render `$Counter` to the template we would expect the value to increase and output `1, 2, 3`. However, as
|
When we render `$Counter` to the template we would expect the value to increase and output `1, 2, 3`. However, as
|
||||||
@ -38,9 +38,9 @@ from the database to display, the contents of the area are fetched from a [cache
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% cached 'MyCachedContent', LastEdited %>
|
<% cached 'MyCachedContent', LastEdited %>
|
||||||
$Title
|
$Title
|
||||||
<% end_cached %>
|
<% end_cached %>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ summary: Definition of the syntax for writing i18n compatible templates.
|
|||||||
Translations are easy to use with a template, and give access to SilverStripe's translation facilities. Here is an
|
Translations are easy to use with a template, and give access to SilverStripe's translation facilities. Here is an
|
||||||
example:
|
example:
|
||||||
```ss
|
```ss
|
||||||
<%t Foo.BAR 'Bar' %>
|
<%t Foo.BAR 'Bar' %>
|
||||||
|
|
||||||
<%t Member.WELCOME 'Welcome {name} to {site}' name=$Member.Name site="Foobar.com" %>
|
<%t Member.WELCOME 'Welcome {name} to {site}' name=$Member.Name site="Foobar.com" %>
|
||||||
```
|
```
|
||||||
|
@ -14,11 +14,11 @@ output the result of the [DBHtmlText::FirstParagraph()](api:SilverStripe\ORM\Fie
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
$Content.FirstParagraph
|
$Content.FirstParagraph
|
||||||
<!-- returns the result of HtmlText::FirstParagragh() -->
|
<!-- returns the result of HtmlText::FirstParagragh() -->
|
||||||
|
|
||||||
$LastEdited.Format("d/m/Y")
|
$LastEdited.Format("d/m/Y")
|
||||||
<!-- returns the result of SS_Datetime::Format("d/m/Y") -->
|
<!-- returns the result of SS_Datetime::Format("d/m/Y") -->
|
||||||
```
|
```
|
||||||
|
|
||||||
Any public method from the object in scope can be called within the template. If that method returns another
|
Any public method from the object in scope can be called within the template. If that method returns another
|
||||||
@ -26,14 +26,14 @@ Any public method from the object in scope can be called within the template. If
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
$Content.FirstParagraph.NoHTML
|
$Content.FirstParagraph.NoHTML
|
||||||
<!-- "First Paragraph" -->
|
<!-- "First Paragraph" -->
|
||||||
|
|
||||||
<p>Copyright {$Now.Year}</p>
|
<p>Copyright {$Now.Year}</p>
|
||||||
<!-- "Copyright 2014" -->
|
<!-- "Copyright 2014" -->
|
||||||
|
|
||||||
<div class="$URLSegment.LowerCase">
|
<div class="$URLSegment.LowerCase">
|
||||||
<!-- <div class="about-us"> -->
|
<!-- <div class="about-us"> -->
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="notice" markdown="1">
|
<div class="notice" markdown="1">
|
||||||
@ -48,23 +48,23 @@ provide default template for an object.
|
|||||||
|
|
||||||
**mysite/code/Page.php**
|
**mysite/code/Page.php**
|
||||||
```php
|
```php
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
|
||||||
class Page extends SiteTree
|
class Page extends SiteTree
|
||||||
{
|
{
|
||||||
|
|
||||||
public function forTemplate()
|
public function forTemplate()
|
||||||
{
|
{
|
||||||
return "Page: ". $this->Title;
|
return "Page: ". $this->Title;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**mysite/templates/Page.ss**
|
**mysite/templates/Page.ss**
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
$Me
|
$Me
|
||||||
<!-- returns Page: Home -->
|
<!-- returns Page: Home -->
|
||||||
```
|
```
|
||||||
|
|
||||||
## Casting
|
## Casting
|
||||||
@ -74,20 +74,20 @@ content that method sends back, or, provide a type in the `$casting` array for t
|
|||||||
to a template, SilverStripe will ensure that the object is wrapped in the correct type and values are safely escaped.
|
to a template, SilverStripe will ensure that the object is wrapped in the correct type and values are safely escaped.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
|
||||||
class Page extends SiteTree
|
class Page extends SiteTree
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $casting = [
|
private static $casting = [
|
||||||
'MyCustomMethod' => 'HTMLText'
|
'MyCustomMethod' => 'HTMLText'
|
||||||
];
|
];
|
||||||
|
|
||||||
public function MyCustomMethod()
|
public function MyCustomMethod()
|
||||||
{
|
{
|
||||||
return "<h1>This is my header</h1>";
|
return "<h1>This is my header</h1>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ top level menu with a nested second level using the `Menu` loop and a `Children`
|
|||||||
|
|
||||||
**mysite/templates/Page.ss**
|
**mysite/templates/Page.ss**
|
||||||
|
|
||||||
:::ss
|
```ss
|
||||||
<ul>
|
<ul>
|
||||||
<% loop $Menu(1) %>
|
<% loop $Menu(1) %>
|
||||||
<li>
|
<li>
|
||||||
@ -27,7 +27,7 @@ top level menu with a nested second level using the `Menu` loop and a `Children`
|
|||||||
</li>
|
</li>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</ul>
|
</ul>
|
||||||
|
```
|
||||||
## Related
|
## Related
|
||||||
|
|
||||||
* [Template Syntax](../syntax)
|
* [Template Syntax](../syntax)
|
||||||
|
@ -10,7 +10,7 @@ The `PaginatedList` will automatically set up query limits and read the request
|
|||||||
|
|
||||||
**mysite/code/Page.php**
|
**mysite/code/Page.php**
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
/**
|
/**
|
||||||
* Returns a paginated list of all pages in the site.
|
* Returns a paginated list of all pages in the site.
|
||||||
*/
|
*/
|
||||||
@ -20,6 +20,7 @@ The `PaginatedList` will automatically set up query limits and read the request
|
|||||||
|
|
||||||
return new PaginatedList($list, $this->getRequest());
|
return new PaginatedList($list, $this->getRequest());
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
<div class="notice" markdown="1">
|
<div class="notice" markdown="1">
|
||||||
Note that the concept of "pages" used in pagination does not necessarily mean that we're dealing with `Page` classes,
|
Note that the concept of "pages" used in pagination does not necessarily mean that we're dealing with `Page` classes,
|
||||||
@ -33,19 +34,19 @@ The first step is to simply list the objects in the template:
|
|||||||
|
|
||||||
**mysite/templates/Page.ss**
|
**mysite/templates/Page.ss**
|
||||||
|
|
||||||
:::ss
|
```ss
|
||||||
<ul>
|
<ul>
|
||||||
<% loop $PaginatedPages %>
|
<% loop $PaginatedPages %>
|
||||||
<li><a href="$Link">$Title</a></li>
|
<li><a href="$Link">$Title</a></li>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</ul>
|
</ul>
|
||||||
|
```
|
||||||
By default this will display 10 pages at a time. The next step is to add pagination controls below this so the user can
|
By default this will display 10 pages at a time. The next step is to add pagination controls below this so the user can
|
||||||
switch between pages:
|
switch between pages:
|
||||||
|
|
||||||
**mysite/templates/Page.ss**
|
**mysite/templates/Page.ss**
|
||||||
|
|
||||||
:::ss
|
```ss
|
||||||
<% if $PaginatedPages.MoreThanOnePage %>
|
<% if $PaginatedPages.MoreThanOnePage %>
|
||||||
<% if $PaginatedPages.NotFirstPage %>
|
<% if $PaginatedPages.NotFirstPage %>
|
||||||
<a class="prev" href="$PaginatedPages.PrevLink">Prev</a>
|
<a class="prev" href="$PaginatedPages.PrevLink">Prev</a>
|
||||||
@ -65,6 +66,7 @@ switch between pages:
|
|||||||
<a class="next" href="$PaginatedPages.NextLink">Next</a>
|
<a class="next" href="$PaginatedPages.NextLink">Next</a>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
|
```
|
||||||
|
|
||||||
If there is more than one page, this block will render a set of pagination controls in the form
|
If there is more than one page, this block will render a set of pagination controls in the form
|
||||||
`[1] ... [3] [4] [5] [6] [7] ... [10]`.
|
`[1] ... [3] [4] [5] [6] [7] ... [10]`.
|
||||||
@ -76,18 +78,19 @@ that you wish to display on the current page. In this situation the automatic li
|
|||||||
will break the pagination. You can disable automatic limiting using the [PaginatedList::setLimitItems()](api:SilverStripe\ORM\PaginatedList::setLimitItems()) method
|
will break the pagination. You can disable automatic limiting using the [PaginatedList::setLimitItems()](api:SilverStripe\ORM\PaginatedList::setLimitItems()) method
|
||||||
when using custom lists.
|
when using custom lists.
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
$myPreLimitedList = Page::get()->limit(10);
|
$myPreLimitedList = Page::get()->limit(10);
|
||||||
|
|
||||||
$pages = new PaginatedList($myPreLimitedList, $this->getRequest());
|
$pages = new PaginatedList($myPreLimitedList, $this->getRequest());
|
||||||
$pages->setLimitItems(false);
|
$pages->setLimitItems(false);
|
||||||
|
```
|
||||||
|
|
||||||
## Setting the limit of items
|
## Setting the limit of items
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
$pages = new PaginatedList(Page::get(), $this->getRequest());
|
$pages = new PaginatedList(Page::get(), $this->getRequest());
|
||||||
$pages->setPageLength(25);
|
$pages->setPageLength(25);
|
||||||
|
```
|
||||||
|
|
||||||
If you set this limit to 0 it will disable paging entirely, effectively causing it to appear as a single page
|
If you set this limit to 0 it will disable paging entirely, effectively causing it to appear as a single page
|
||||||
list.
|
list.
|
||||||
|
@ -5,12 +5,12 @@ title: Disable Anchor Rewriting
|
|||||||
Anchor links are links with a "#" in them. A frequent use-case is to use anchor links to point to different sections of
|
Anchor links are links with a "#" in them. A frequent use-case is to use anchor links to point to different sections of
|
||||||
the current page. For example, we might have this in our template:
|
the current page. For example, we might have this in our template:
|
||||||
|
|
||||||
:::ss
|
```ss
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="#section1">Section 1</a></li>
|
<li><a href="#section1">Section 1</a></li>
|
||||||
<li><a href="#section2">Section 2</a></li>
|
<li><a href="#section2">Section 2</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
```
|
||||||
|
|
||||||
Things get tricky because of we have set our `<base>` tag to point to the root of the site. So, when you click the
|
Things get tricky because of we have set our `<base>` tag to point to the root of the site. So, when you click the
|
||||||
first link you will be sent to http://yoursite.com/#section1 instead of http://yoursite.com/my-long-page/#section1
|
first link you will be sent to http://yoursite.com/#section1 instead of http://yoursite.com/my-long-page/#section1
|
||||||
@ -19,24 +19,27 @@ In order to prevent this situation, the SSViewer template renderer will automati
|
|||||||
doesn't specify a URL before the anchor, prefixing the URL of the current page. For our example above, the following
|
doesn't specify a URL before the anchor, prefixing the URL of the current page. For our example above, the following
|
||||||
would be created in the final HTML
|
would be created in the final HTML
|
||||||
|
|
||||||
:::ss
|
```ss
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="my-long-page/#section1">Section 1</a></li>
|
<li><a href="my-long-page/#section1">Section 1</a></li>
|
||||||
<li><a href="my-long-page/#section2">Section 2</a></li>
|
<li><a href="my-long-page/#section2">Section 2</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
```
|
||||||
|
|
||||||
There are cases where this can be unhelpful. HTML anchors created from Ajax responses are the most common. In these
|
There are cases where this can be unhelpful. HTML anchors created from Ajax responses are the most common. In these
|
||||||
situations, you can disable anchor link rewriting by setting the `SSViewer.rewrite_hash_links` configuration value to
|
situations, you can disable anchor link rewriting by setting the `SSViewer.rewrite_hash_links` configuration value to
|
||||||
`false`.
|
`false`.
|
||||||
|
|
||||||
**mysite/_config/app.yml**
|
**mysite/_config/app.yml**
|
||||||
|
|
||||||
|
```yml
|
||||||
SSViewer:
|
SSViewer:
|
||||||
rewrite_hash_links: false
|
rewrite_hash_links: false
|
||||||
|
```
|
||||||
|
|
||||||
Or, a better way is to call this just for the rendering phase of this particular file:
|
Or, a better way is to call this just for the rendering phase of this particular file:
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
public function RenderCustomTemplate()
|
public function RenderCustomTemplate()
|
||||||
{
|
{
|
||||||
Config::inst()->update('SSViewer', 'rewrite_hash_links', false);
|
Config::inst()->update('SSViewer', 'rewrite_hash_links', false);
|
||||||
@ -44,4 +47,5 @@ Or, a better way is to call this just for the rendering phase of this particular
|
|||||||
Config::inst()->update('SSViewer', 'rewrite_hash_links', true);
|
Config::inst()->update('SSViewer', 'rewrite_hash_links', true);
|
||||||
|
|
||||||
return $html;
|
return $html;
|
||||||
}
|
}
|
||||||
|
```
|
@ -9,26 +9,26 @@ subclass the base `Controller` class.
|
|||||||
**mysite/code/controllers/TeamController.php**
|
**mysite/code/controllers/TeamController.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Control\Controller;
|
use SilverStripe\Control\Controller;
|
||||||
|
|
||||||
class TeamController extends Controller
|
class TeamController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $allowed_actions = [
|
private static $allowed_actions = [
|
||||||
'players',
|
'players',
|
||||||
'index'
|
'index'
|
||||||
];
|
];
|
||||||
|
|
||||||
public function index(HTTPRequest $request)
|
public function index(HTTPRequest $request)
|
||||||
{
|
{
|
||||||
// ..
|
// ..
|
||||||
}
|
}
|
||||||
|
|
||||||
public function players(HTTPRequest $request)
|
public function players(HTTPRequest $request)
|
||||||
{
|
{
|
||||||
print_r($request->allParams());
|
print_r($request->allParams());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -51,13 +51,13 @@ Make sure that after you have modified the `routes.yml` file, that you clear you
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
---
|
---
|
||||||
Name: mysiteroutes
|
Name: mysiteroutes
|
||||||
After: framework/routes#coreroutes
|
After: framework/routes#coreroutes
|
||||||
---
|
---
|
||||||
SilverStripe\Control\Director:
|
SilverStripe\Control\Director:
|
||||||
rules:
|
rules:
|
||||||
'teams//$Action/$ID/$Name': 'TeamController'
|
'teams//$Action/$ID/$Name': 'TeamController'
|
||||||
```
|
```
|
||||||
|
|
||||||
For more information about creating custom routes, see the [Routing](routing) documentation.
|
For more information about creating custom routes, see the [Routing](routing) documentation.
|
||||||
@ -81,62 +81,62 @@ Action methods can return one of four main things:
|
|||||||
**mysite/code/controllers/TeamController.php**
|
**mysite/code/controllers/TeamController.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
/**
|
/**
|
||||||
* Return some additional data to the current response that is waiting to go out, this makes $Title set to
|
* Return some additional data to the current response that is waiting to go out, this makes $Title set to
|
||||||
* 'MyTeamName' and continues on with generating the response.
|
* 'MyTeamName' and continues on with generating the response.
|
||||||
*/
|
*/
|
||||||
public function index(HTTPRequest $request)
|
public function index(HTTPRequest $request)
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'Title' => 'My Team Name'
|
'Title' => 'My Team Name'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We can manually create a response and return that to ignore any previous data.
|
* We can manually create a response and return that to ignore any previous data.
|
||||||
*/
|
*/
|
||||||
public function someaction(HTTPRequest $request)
|
public function someaction(HTTPRequest $request)
|
||||||
{
|
{
|
||||||
$this->setResponse(new HTTPResponse());
|
$this->setResponse(new HTTPResponse());
|
||||||
$this->getResponse()->setStatusCode(400);
|
$this->getResponse()->setStatusCode(400);
|
||||||
$this->getResponse()->setBody('invalid');
|
$this->getResponse()->setBody('invalid');
|
||||||
|
|
||||||
return $this->getResponse();
|
return $this->getResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Or, we can modify the response that is waiting to go out.
|
* Or, we can modify the response that is waiting to go out.
|
||||||
*/
|
*/
|
||||||
public function anotheraction(HTTPRequest $request)
|
public function anotheraction(HTTPRequest $request)
|
||||||
{
|
{
|
||||||
$this->getResponse()->setStatusCode(400);
|
$this->getResponse()->setStatusCode(400);
|
||||||
|
|
||||||
return $this->getResponse();
|
return $this->getResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We can render HTML and leave SilverStripe to set the response code and body.
|
* We can render HTML and leave SilverStripe to set the response code and body.
|
||||||
*/
|
*/
|
||||||
public function htmlaction()
|
public function htmlaction()
|
||||||
{
|
{
|
||||||
return $this->customise(new ArrayData([
|
return $this->customise(new ArrayData([
|
||||||
'Title' => 'HTML Action'
|
'Title' => 'HTML Action'
|
||||||
]))->renderWith('MyCustomTemplate');
|
]))->renderWith('MyCustomTemplate');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We can send stuff to the browser which isn't HTML
|
* We can send stuff to the browser which isn't HTML
|
||||||
*/
|
*/
|
||||||
public function ajaxaction()
|
public function ajaxaction()
|
||||||
{
|
{
|
||||||
$this->getResponse()->setBody(json_encode([
|
$this->getResponse()->setBody(json_encode([
|
||||||
'json' => true
|
'json' => true
|
||||||
]));
|
]));
|
||||||
|
|
||||||
$this->getResponse()->addHeader("Content-type", "application/json");
|
$this->getResponse()->addHeader("Content-type", "application/json");
|
||||||
|
|
||||||
return $this->getResponse().
|
return $this->getResponse().
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -167,10 +167,10 @@ Each controller should define a `Link()` method. This should be used to avoid ha
|
|||||||
**mysite/code/controllers/TeamController.php**
|
**mysite/code/controllers/TeamController.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function Link($action = null)
|
public function Link($action = null)
|
||||||
{
|
{
|
||||||
return Controller::join_links('teams', $action);
|
return Controller::join_links('teams', $action);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="info" markdown="1">
|
<div class="info" markdown="1">
|
||||||
|
@ -12,31 +12,31 @@ Any action you define on a controller must be defined in a `$allowed_actions` st
|
|||||||
directly calling methods that they shouldn't.
|
directly calling methods that they shouldn't.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Control\Controller;
|
use SilverStripe\Control\Controller;
|
||||||
|
|
||||||
class MyController extends Controller
|
class MyController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $allowed_actions = [
|
private static $allowed_actions = [
|
||||||
// someaction can be accessed by anyone, any time
|
// someaction can be accessed by anyone, any time
|
||||||
'someaction',
|
'someaction',
|
||||||
|
|
||||||
// So can otheraction
|
// So can otheraction
|
||||||
'otheraction' => true,
|
'otheraction' => true,
|
||||||
|
|
||||||
// restrictedaction can only be people with ADMIN privilege
|
// restrictedaction can only be people with ADMIN privilege
|
||||||
'restrictedaction' => 'ADMIN',
|
'restrictedaction' => 'ADMIN',
|
||||||
|
|
||||||
// restricted to uses that have the 'CMS_ACCESS_CMSMain' access
|
// restricted to uses that have the 'CMS_ACCESS_CMSMain' access
|
||||||
'cmsrestrictedaction' => 'CMS_ACCESS_CMSMain',
|
'cmsrestrictedaction' => 'CMS_ACCESS_CMSMain',
|
||||||
|
|
||||||
// complexaction can only be accessed if $this->canComplexAction() returns true.
|
// complexaction can only be accessed if $this->canComplexAction() returns true.
|
||||||
'complexaction' => '->canComplexAction',
|
'complexaction' => '->canComplexAction',
|
||||||
|
|
||||||
// complexactioncheck can only be accessed if $this->canComplexAction("MyRestrictedAction", false, 42) is true.
|
// complexactioncheck can only be accessed if $this->canComplexAction("MyRestrictedAction", false, 42) is true.
|
||||||
'complexactioncheck' => '->canComplexAction("MyRestrictedAction", false, 42)',
|
'complexactioncheck' => '->canComplexAction("MyRestrictedAction", false, 42)',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -48,88 +48,88 @@ An action named "index" is white listed by default, unless `allowed_actions` is
|
|||||||
is specifically restricted.
|
is specifically restricted.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Control\Controller;
|
use SilverStripe\Control\Controller;
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
class MyController extends Controller
|
class MyController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
// allowed without an $allowed_action defined
|
// allowed without an $allowed_action defined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`$allowed_actions` can be defined on `Extension` classes applying to the controller.
|
`$allowed_actions` can be defined on `Extension` classes applying to the controller.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Core\Extension;
|
use SilverStripe\Core\Extension;
|
||||||
|
|
||||||
class MyExtension extends Extension
|
class MyExtension extends Extension
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $allowed_actions = [
|
private static $allowed_actions = [
|
||||||
'mycustomaction'
|
'mycustomaction'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Only public methods can be made accessible.
|
Only public methods can be made accessible.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Control\Controller;
|
use SilverStripe\Control\Controller;
|
||||||
|
|
||||||
class MyController extends Controller
|
class MyController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $allowed_actions = [
|
private static $allowed_actions = [
|
||||||
'secure',
|
'secure',
|
||||||
// secureaction won't work as it's private.
|
// secureaction won't work as it's private.
|
||||||
];
|
];
|
||||||
|
|
||||||
public function secure()
|
public function secure()
|
||||||
{
|
{
|
||||||
// ..
|
// ..
|
||||||
}
|
}
|
||||||
|
|
||||||
private function secureaction()
|
private function secureaction()
|
||||||
{
|
{
|
||||||
// ..
|
// ..
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
If a method on a parent class is overwritten, access control for it has to be redefined as well.
|
If a method on a parent class is overwritten, access control for it has to be redefined as well.
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Control\Controller;
|
use SilverStripe\Control\Controller;
|
||||||
|
|
||||||
class MyController extends Controller
|
class MyController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $allowed_actions = [
|
private static $allowed_actions = [
|
||||||
'action',
|
'action',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function action()
|
public function action()
|
||||||
{
|
{
|
||||||
// ..
|
// ..
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class MyChildController extends MyController
|
class MyChildController extends MyController
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $allowed_actions = [
|
private static $allowed_actions = [
|
||||||
'action', // required as we are redefining action
|
'action', // required as we are redefining action
|
||||||
];
|
];
|
||||||
|
|
||||||
public function action()
|
public function action()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -142,26 +142,26 @@ Access checks on parent classes need to be overwritten via the [Configuration AP
|
|||||||
Form action methods should **not** be included in `$allowed_actions`. However, the form method **should** be included
|
Form action methods should **not** be included in `$allowed_actions`. However, the form method **should** be included
|
||||||
as an `allowed_action`.
|
as an `allowed_action`.
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Forms\Form;
|
use SilverStripe\Forms\Form;
|
||||||
use SilverStripe\Control\Controller;
|
use SilverStripe\Control\Controller;
|
||||||
|
|
||||||
class MyController extends Controller
|
class MyController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $allowed_actions = [
|
private static $allowed_actions = [
|
||||||
'ContactForm' // use the Form method, not the action
|
'ContactForm' // use the Form method, not the action
|
||||||
];
|
];
|
||||||
|
|
||||||
public function ContactForm()
|
public function ContactForm()
|
||||||
{
|
{
|
||||||
return new Form(..);
|
return new Form(..);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function doContactForm($data, $form)
|
public function doContactForm($data, $form)
|
||||||
{
|
{
|
||||||
// ..
|
// ..
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -171,24 +171,24 @@ Each method responding to a URL can also implement custom permission checks, e.g
|
|||||||
the passed request data.
|
the passed request data.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Control\Controller;
|
use SilverStripe\Control\Controller;
|
||||||
|
|
||||||
class MyController extends Controller
|
class MyController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $allowed_actions = [
|
private static $allowed_actions = [
|
||||||
'myaction'
|
'myaction'
|
||||||
];
|
];
|
||||||
|
|
||||||
public function myaction($request)
|
public function myaction($request)
|
||||||
{
|
{
|
||||||
if(!$request->getVar('apikey')) {
|
if(!$request->getVar('apikey')) {
|
||||||
return $this->httpError(403, 'No API key provided');
|
return $this->httpError(403, 'No API key provided');
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'valid';
|
return 'valid';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -207,23 +207,23 @@ execution. This behavior can be used to implement permission checks.
|
|||||||
`init` is called for any possible action on the controller and before any specific method such as `index`.
|
`init` is called for any possible action on the controller and before any specific method such as `index`.
|
||||||
</div>
|
</div>
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Security\Permission;
|
use SilverStripe\Security\Permission;
|
||||||
use SilverStripe\Control\Controller;
|
use SilverStripe\Control\Controller;
|
||||||
|
|
||||||
class MyController extends Controller
|
class MyController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $allowed_actions = [];
|
private static $allowed_actions = [];
|
||||||
|
|
||||||
public function init()
|
public function init()
|
||||||
{
|
{
|
||||||
parent::init();
|
parent::init();
|
||||||
|
|
||||||
if(!Permission::check('ADMIN')) {
|
if(!Permission::check('ADMIN')) {
|
||||||
return $this->httpError(403);
|
return $this->httpError(403);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -10,17 +10,17 @@ HTTP header.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$this->redirect('goherenow');
|
$this->redirect('goherenow');
|
||||||
// redirect to Page::goherenow(), i.e on the contact-us page this will redirect to /contact-us/goherenow/
|
// redirect to Page::goherenow(), i.e on the contact-us page this will redirect to /contact-us/goherenow/
|
||||||
|
|
||||||
$this->redirect('goherenow/');
|
$this->redirect('goherenow/');
|
||||||
// redirect to the URL on yoursite.com/goherenow/. (note the trailing slash)
|
// redirect to the URL on yoursite.com/goherenow/. (note the trailing slash)
|
||||||
|
|
||||||
$this->redirect('http://google.com');
|
$this->redirect('http://google.com');
|
||||||
// redirect to http://google.com
|
// redirect to http://google.com
|
||||||
|
|
||||||
$this->redirectBack();
|
$this->redirectBack();
|
||||||
// go back to the previous page.
|
// go back to the previous page.
|
||||||
```
|
```
|
||||||
|
|
||||||
## Status Codes
|
## Status Codes
|
||||||
@ -28,8 +28,8 @@ HTTP header.
|
|||||||
The `redirect()` method takes an optional HTTP status code, either `301` for permanent redirects, or `302` for
|
The `redirect()` method takes an optional HTTP status code, either `301` for permanent redirects, or `302` for
|
||||||
temporary redirects (default).
|
temporary redirects (default).
|
||||||
```php
|
```php
|
||||||
$this->redirect('/', 302);
|
$this->redirect('/', 302);
|
||||||
// go back to the homepage, don't cache that this page has moved
|
// go back to the homepage, don't cache that this page has moved
|
||||||
```
|
```
|
||||||
|
|
||||||
## Redirection in URL Handling
|
## Redirection in URL Handling
|
||||||
@ -39,9 +39,9 @@ operator.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
private static $url_handlers = [
|
private static $url_handlers = [
|
||||||
'players/john' => '~>coach'
|
'players/john' => '~>coach'
|
||||||
];
|
];
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -8,12 +8,12 @@ can be rendered out using custom templates using `setTemplate`.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$form = new Form(..);
|
$form = new Form(..);
|
||||||
$form->setTemplate('MyCustomFormTemplate');
|
$form->setTemplate('MyCustomFormTemplate');
|
||||||
|
|
||||||
// or, just a field
|
// or, just a field
|
||||||
$field = new TextField(..);
|
$field = new TextField(..);
|
||||||
$field->setTemplate('MyCustomTextField');
|
$field->setTemplate('MyCustomTextField');
|
||||||
```
|
```
|
||||||
|
|
||||||
Both `MyCustomTemplate.ss` and `MyCustomTextField.ss` should be located in **mysite/templates/forms/** or the same directory as the core.
|
Both `MyCustomTemplate.ss` and `MyCustomTextField.ss` should be located in **mysite/templates/forms/** or the same directory as the core.
|
||||||
@ -37,27 +37,27 @@ For [FormField](api:SilverStripe\Forms\FormField) instances, there are several o
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$field = new TextField();
|
$field = new TextField();
|
||||||
|
|
||||||
$field->setTemplate('CustomTextField');
|
$field->setTemplate('CustomTextField');
|
||||||
// Sets the template for the <input> tag. i.e '<input $AttributesHTML />'
|
// Sets the template for the <input> tag. i.e '<input $AttributesHTML />'
|
||||||
|
|
||||||
$field->setFieldHolderTemplate('CustomTextField_Holder');
|
$field->setFieldHolderTemplate('CustomTextField_Holder');
|
||||||
// Sets the template for the wrapper around the text field. i.e
|
// Sets the template for the wrapper around the text field. i.e
|
||||||
// '<div class="text">'
|
// '<div class="text">'
|
||||||
//
|
//
|
||||||
// The actual FormField is rendered into the holder via the `$Field`
|
// The actual FormField is rendered into the holder via the `$Field`
|
||||||
// variable.
|
// variable.
|
||||||
//
|
//
|
||||||
// setFieldHolder() is used in most `Form` instances and needs to output
|
// setFieldHolder() is used in most `Form` instances and needs to output
|
||||||
// labels, error messages and the like.
|
// labels, error messages and the like.
|
||||||
|
|
||||||
$field->setSmallFieldHolderTemplate('CustomTextField_Holder_Small');
|
$field->setSmallFieldHolderTemplate('CustomTextField_Holder_Small');
|
||||||
// Sets the template for the wrapper around the text field.
|
// Sets the template for the wrapper around the text field.
|
||||||
//
|
//
|
||||||
// The difference here is the small field holder template is used when the
|
// The difference here is the small field holder template is used when the
|
||||||
// field is embedded within another field. For example, if the field is
|
// field is embedded within another field. For example, if the field is
|
||||||
// part of a `FieldGroup` or `CompositeField` alongside other fields.
|
// part of a `FieldGroup` or `CompositeField` alongside other fields.
|
||||||
```
|
```
|
||||||
|
|
||||||
All templates are rendered within the scope of the [FormField](api:SilverStripe\Forms\FormField). To understand more about Scope within Templates as
|
All templates are rendered within the scope of the [FormField](api:SilverStripe\Forms\FormField). To understand more about Scope within Templates as
|
||||||
|
@ -22,16 +22,16 @@ The `SecurityToken` automatically added looks something like:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$form = new Form(..);
|
$form = new Form(..);
|
||||||
echo $form->getSecurityToken()->getValue();
|
echo $form->getSecurityToken()->getValue();
|
||||||
|
|
||||||
// 'c443076989a7f24cf6b35fe1360be8683a753e2c'
|
// 'c443076989a7f24cf6b35fe1360be8683a753e2c'
|
||||||
```
|
```
|
||||||
|
|
||||||
This token value is passed through the rendered Form HTML as a [HiddenField](api:SilverStripe\Forms\HiddenField).
|
This token value is passed through the rendered Form HTML as a [HiddenField](api:SilverStripe\Forms\HiddenField).
|
||||||
```html
|
```html
|
||||||
|
|
||||||
<input type="hidden" name="SecurityID" value="c443076989a7f24cf6b35fe1360be8683a753e2c" class="hidden" />
|
<input type="hidden" name="SecurityID" value="c443076989a7f24cf6b35fe1360be8683a753e2c" class="hidden" />
|
||||||
```
|
```
|
||||||
|
|
||||||
The token should be present whenever a operation has a side effect such as a `POST` operation.
|
The token should be present whenever a operation has a side effect such as a `POST` operation.
|
||||||
@ -41,8 +41,8 @@ normally require a security token).
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$form = new Form(..);
|
$form = new Form(..);
|
||||||
$form->disableSecurityToken();
|
$form->disableSecurityToken();
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="alert" markdown="1">
|
<div class="alert" markdown="1">
|
||||||
@ -58,13 +58,13 @@ application errors or edge cases. If you need to disable this setting follow the
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$form = new Form(..);
|
$form = new Form(..);
|
||||||
|
|
||||||
$form->setFormMethod('POST');
|
$form->setFormMethod('POST');
|
||||||
$form->setStrictFormMethodCheck(false);
|
$form->setStrictFormMethodCheck(false);
|
||||||
|
|
||||||
// or alternative short notation..
|
// or alternative short notation..
|
||||||
$form->setFormMethod('POST', false);
|
$form->setFormMethod('POST', false);
|
||||||
```
|
```
|
||||||
|
|
||||||
## Spam and Bot Attacks
|
## Spam and Bot Attacks
|
||||||
|
@ -11,36 +11,36 @@ To make an entire [Form](api:SilverStripe\Forms\Form) read-only.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$form = new Form(..);
|
$form = new Form(..);
|
||||||
$form->makeReadonly();
|
$form->makeReadonly();
|
||||||
```
|
```
|
||||||
|
|
||||||
To make all the fields within a [FieldList](api:SilverStripe\Forms\FieldList) read-only (i.e to make fields read-only but not buttons).
|
To make all the fields within a [FieldList](api:SilverStripe\Forms\FieldList) read-only (i.e to make fields read-only but not buttons).
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$fields = new FieldList(..);
|
$fields = new FieldList(..);
|
||||||
$fields = $fields->makeReadonly();
|
$fields = $fields->makeReadonly();
|
||||||
```
|
```
|
||||||
|
|
||||||
To make a [FormField](api:SilverStripe\Forms\FormField) read-only you need to know the name of the form field or call it direct on the object
|
To make a [FormField](api:SilverStripe\Forms\FormField) read-only you need to know the name of the form field or call it direct on the object
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$field = new TextField(..);
|
$field = new TextField(..);
|
||||||
$field = $field->performReadonlyTransformation();
|
$field = $field->performReadonlyTransformation();
|
||||||
|
|
||||||
$fields = new FieldList(
|
$fields = new FieldList(
|
||||||
$field
|
$field
|
||||||
);
|
);
|
||||||
|
|
||||||
// Or,
|
// Or,
|
||||||
$field = new TextField(..);
|
$field = new TextField(..);
|
||||||
$field->setReadonly(true);
|
$field->setReadonly(true);
|
||||||
|
|
||||||
$fields = new FieldList(
|
$fields = new FieldList(
|
||||||
$field
|
$field
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
## Disabled FormFields
|
## Disabled FormFields
|
||||||
@ -49,11 +49,11 @@ Disabling [FormField](api:SilverStripe\Forms\FormField) instances, sets the `dis
|
|||||||
a normal form, but set the `disabled` attribute on the `input` tag.
|
a normal form, but set the `disabled` attribute on the `input` tag.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$field = new TextField(..);
|
$field = new TextField(..);
|
||||||
$field->setDisabled(true);
|
$field->setDisabled(true);
|
||||||
|
|
||||||
echo $field->forTemplate();
|
echo $field->forTemplate();
|
||||||
|
|
||||||
// returns '<input type="text" class="text" .. disabled="disabled" />'
|
// returns '<input type="text" class="text" .. disabled="disabled" />'
|
||||||
|
|
||||||
```
|
```
|
@ -23,41 +23,41 @@ display up to two levels of tabs in the interface.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$fields->addFieldToTab('Root.Main', new TextField(..));
|
$fields->addFieldToTab('Root.Main', new TextField(..));
|
||||||
```
|
```
|
||||||
|
|
||||||
## Removing a field from a tab
|
## Removing a field from a tab
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$fields->removeFieldFromTab('Root.Main', 'Content');
|
$fields->removeFieldFromTab('Root.Main', 'Content');
|
||||||
```
|
```
|
||||||
|
|
||||||
## Creating a new tab
|
## Creating a new tab
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$fields->addFieldToTab('Root.MyNewTab', new TextField(..));
|
$fields->addFieldToTab('Root.MyNewTab', new TextField(..));
|
||||||
```
|
```
|
||||||
|
|
||||||
## Moving a field between tabs
|
## Moving a field between tabs
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$content = $fields->dataFieldByName('Content');
|
$content = $fields->dataFieldByName('Content');
|
||||||
|
|
||||||
$fields->removeFieldFromTab('Root.Main', 'Content');
|
$fields->removeFieldFromTab('Root.Main', 'Content');
|
||||||
$fields->addFieldToTab('Root.MyContent', $content);
|
$fields->addFieldToTab('Root.MyContent', $content);
|
||||||
```
|
```
|
||||||
|
|
||||||
## Add multiple fields at once
|
## Add multiple fields at once
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$fields->addFieldsToTab('Root.Content', [
|
$fields->addFieldsToTab('Root.Content', [
|
||||||
TextField::create('Name'),
|
TextField::create('Name'),
|
||||||
TextField::create('Email')
|
TextField::create('Email')
|
||||||
]);
|
]);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -16,28 +16,28 @@ The following example will add a simple DateField to your Page, allowing you to
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Forms\DateField;
|
use SilverStripe\Forms\DateField;
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
|
||||||
class Page extends SiteTree
|
class Page extends SiteTree
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'MyDate' => 'Date',
|
'MyDate' => 'Date',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function getCMSFields()
|
public function getCMSFields()
|
||||||
{
|
{
|
||||||
$fields = parent::getCMSFields();
|
$fields = parent::getCMSFields();
|
||||||
|
|
||||||
$fields->addFieldToTab(
|
$fields->addFieldToTab(
|
||||||
'Root.Main',
|
'Root.Main',
|
||||||
DateField::create('MyDate', 'Enter a date')
|
DateField::create('MyDate', 'Enter a date')
|
||||||
);
|
);
|
||||||
|
|
||||||
return $fields;
|
return $fields;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -48,10 +48,10 @@ This is only necessary if you want to opt-out of the built-in browser localisati
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// will display a date in the following format: 31/06/2012
|
// will display a date in the following format: 31/06/2012
|
||||||
DateField::create('MyDate')
|
DateField::create('MyDate')
|
||||||
->setHTML5(false)
|
->setHTML5(false)
|
||||||
->setDateFormat('dd/MM/yyyy');
|
->setDateFormat('dd/MM/yyyy');
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="info" markdown="1">
|
<div class="info" markdown="1">
|
||||||
@ -66,9 +66,9 @@ Sets the minimum and maximum allowed date values using the `min` and `max` confi
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
DateField::create('MyDate')
|
DateField::create('MyDate')
|
||||||
->setMinDate('-7 days')
|
->setMinDate('-7 days')
|
||||||
->setMaxDate('2012-12-31')
|
->setMaxDate('2012-12-31')
|
||||||
```
|
```
|
||||||
|
|
||||||
## Formatting Hints
|
## Formatting Hints
|
||||||
@ -79,17 +79,17 @@ field description as an example.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$dateField = DateField::create('MyDate');
|
$dateField = DateField::create('MyDate');
|
||||||
|
|
||||||
// Show long format as text below the field
|
// Show long format as text below the field
|
||||||
$dateField->setDescription(_t(
|
$dateField->setDescription(_t(
|
||||||
'FormField.Example',
|
'FormField.Example',
|
||||||
'e.g. {format}',
|
'e.g. {format}',
|
||||||
[ 'format' => $dateField->getDateFormat() ]
|
[ 'format' => $dateField->getDateFormat() ]
|
||||||
));
|
));
|
||||||
|
|
||||||
// Alternatively, set short format as a placeholder in the field
|
// Alternatively, set short format as a placeholder in the field
|
||||||
$dateField->setAttribute('placeholder', $dateField->getDateFormat());
|
$dateField->setAttribute('placeholder', $dateField->getDateFormat());
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="notice" markdown="1">
|
<div class="notice" markdown="1">
|
||||||
|
@ -17,24 +17,24 @@ functionality. It is usually added through the [DataObject::getCMSFields()](api:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Forms\FieldList;
|
use SilverStripe\Forms\FieldList;
|
||||||
use SilverStripe\Forms\HTMLEditor\HTMLEditorField;
|
use SilverStripe\Forms\HTMLEditor\HTMLEditorField;
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class MyObject extends DataObject
|
class MyObject extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Content' => 'HTMLText'
|
'Content' => 'HTMLText'
|
||||||
];
|
];
|
||||||
|
|
||||||
public function getCMSFields()
|
public function getCMSFields()
|
||||||
{
|
{
|
||||||
return new FieldList(
|
return new FieldList(
|
||||||
new HTMLEditorField('Content')
|
new HTMLEditorField('Content')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -51,25 +51,25 @@ This is particularly useful if you need different configurations for multiple [H
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Forms\FieldList;
|
use SilverStripe\Forms\FieldList;
|
||||||
use SilverStripe\Forms\HTMLEditor\HTMLEditorField;
|
use SilverStripe\Forms\HTMLEditor\HTMLEditorField;
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class MyObject extends DataObject
|
class MyObject extends DataObject
|
||||||
{
|
{
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Content' => 'HTMLText',
|
'Content' => 'HTMLText',
|
||||||
'OtherContent' => 'HTMLText'
|
'OtherContent' => 'HTMLText'
|
||||||
];
|
];
|
||||||
|
|
||||||
public function getCMSFields()
|
public function getCMSFields()
|
||||||
{
|
{
|
||||||
return new FieldList([
|
return new FieldList([
|
||||||
new HTMLEditorField('Content'),
|
new HTMLEditorField('Content'),
|
||||||
new HTMLEditorField('OtherContent', 'Other content', $this->OtherContent, 'myConfig')
|
new HTMLEditorField('OtherContent', 'Other content', $this->OtherContent, 'myConfig')
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ transparently generate the relevant underlying TinyMCE code.
|
|||||||
**mysite/_config.php**
|
**mysite/_config.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
HtmlEditorConfig::get('cms')->enablePlugins('media');
|
HtmlEditorConfig::get('cms')->enablePlugins('media');
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="notice" markdown="1">
|
<div class="notice" markdown="1">
|
||||||
@ -118,7 +118,7 @@ configuration. Here is an example of adding a `ssmacron` button after the `charm
|
|||||||
**mysite/_config.php**
|
**mysite/_config.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
HtmlEditorConfig::get('cms')->insertButtonsAfter('charmap', 'ssmacron');
|
HtmlEditorConfig::get('cms')->insertButtonsAfter('charmap', 'ssmacron');
|
||||||
```
|
```
|
||||||
|
|
||||||
Buttons can also be removed:
|
Buttons can also be removed:
|
||||||
@ -126,7 +126,7 @@ Buttons can also be removed:
|
|||||||
**mysite/_config.php**
|
**mysite/_config.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
HtmlEditorConfig::get('cms')->removeButtons('tablecontrols', 'blockquote', 'hr');
|
HtmlEditorConfig::get('cms')->removeButtons('tablecontrols', 'blockquote', 'hr');
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="notice" markdown="1">
|
<div class="notice" markdown="1">
|
||||||
@ -147,18 +147,18 @@ from the HTML source by the editor.
|
|||||||
**mysite/_config.php**
|
**mysite/_config.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// Add start and type attributes for <ol>, add <object> and <embed> with all attributes.
|
// Add start and type attributes for <ol>, add <object> and <embed> with all attributes.
|
||||||
HtmlEditorConfig::get('cms')->setOption(
|
HtmlEditorConfig::get('cms')->setOption(
|
||||||
'extended_valid_elements',
|
'extended_valid_elements',
|
||||||
'img[class|src|alt|title|hspace|vspace|width|height|align|onmouseover|onmouseout|name|usemap],' .
|
'img[class|src|alt|title|hspace|vspace|width|height|align|onmouseover|onmouseout|name|usemap],' .
|
||||||
'iframe[src|name|width|height|title|align|allowfullscreen|frameborder|marginwidth|marginheight|scrolling],' .
|
'iframe[src|name|width|height|title|align|allowfullscreen|frameborder|marginwidth|marginheight|scrolling],' .
|
||||||
'object[classid|codebase|width|height|data|type],' .
|
'object[classid|codebase|width|height|data|type],' .
|
||||||
'embed[src|type|pluginspage|width|height|autoplay],' .
|
'embed[src|type|pluginspage|width|height|autoplay],' .
|
||||||
'param[name|value],' .
|
'param[name|value],' .
|
||||||
'map[class|name|id],' .
|
'map[class|name|id],' .
|
||||||
'area[shape|coords|href|target|alt],' .
|
'area[shape|coords|href|target|alt],' .
|
||||||
'ol[start|type]'
|
'ol[start|type]'
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="notice" markdown="1">
|
<div class="notice" markdown="1">
|
||||||
@ -174,7 +174,7 @@ You can enable them through [HtmlEditorConfig::enablePlugins()](api:SilverStripe
|
|||||||
**mysite/_config.php**
|
**mysite/_config.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
HtmlEditorConfig::get('cms')->enablePlugins(['myplugin' => '../../../mysite/javascript/myplugin/editor_plugin.js']);
|
HtmlEditorConfig::get('cms')->enablePlugins(['myplugin' => '../../../mysite/javascript/myplugin/editor_plugin.js']);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -229,7 +229,7 @@ In case you want to adhere to HTML4 instead, use the following configuration:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
HtmlEditorConfig::get('cms')->setOption('element_format', 'html');
|
HtmlEditorConfig::get('cms')->setOption('element_format', 'html');
|
||||||
```
|
```
|
||||||
|
|
||||||
By default, TinyMCE and SilverStripe will generate valid HTML5 markup, but it will strip out HTML5 tags like
|
By default, TinyMCE and SilverStripe will generate valid HTML5 markup, but it will strip out HTML5 tags like
|
||||||
@ -254,23 +254,23 @@ Example: Remove field for "image captions"
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Core\Extension;
|
use SilverStripe\Core\Extension;
|
||||||
|
|
||||||
// File: mysite/code/MyToolbarExtension.php
|
// File: mysite/code/MyToolbarExtension.php
|
||||||
class MyToolbarExtension extends Extension
|
class MyToolbarExtension extends Extension
|
||||||
{
|
{
|
||||||
public function updateFieldsForImage(&$fields, $url, $file)
|
public function updateFieldsForImage(&$fields, $url, $file)
|
||||||
{
|
{
|
||||||
$fields->removeByName('CaptionText');
|
$fields->removeByName('CaptionText');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// File: mysite/_config.php
|
// File: mysite/_config.php
|
||||||
ModalController::add_extension('MyToolbarExtension');
|
ModalController::add_extension('MyToolbarExtension');
|
||||||
```
|
```
|
||||||
|
|
||||||
Adding functionality is a bit more advanced, you'll most likely
|
Adding functionality is a bit more advanced, you'll most likely
|
||||||
@ -298,17 +298,17 @@ of the CMS you have to take care of instantiate yourself:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Admin\ModalController;
|
use SilverStripe\Admin\ModalController;
|
||||||
use SilverStripe\Control\Controller;
|
use SilverStripe\Control\Controller;
|
||||||
|
|
||||||
// File: mysite/code/MyController.php
|
// File: mysite/code/MyController.php
|
||||||
class MyObjectController extends Controller
|
class MyObjectController extends Controller
|
||||||
{
|
{
|
||||||
public function Modals()
|
public function Modals()
|
||||||
{
|
{
|
||||||
return ModalController::create($this, "Modals");
|
return ModalController::create($this, "Modals");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: The dialogs rely on CMS-access, e.g. for uploading and browsing files,
|
Note: The dialogs rely on CMS-access, e.g. for uploading and browsing files,
|
||||||
@ -316,10 +316,10 @@ so this is considered advanced usage of the field.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// File: mysite/_config.php
|
// File: mysite/_config.php
|
||||||
HtmlEditorConfig::get('cms')->disablePlugins('ssbuttons');
|
HtmlEditorConfig::get('cms')->disablePlugins('ssbuttons');
|
||||||
HtmlEditorConfig::get('cms')->removeButtons('sslink', 'ssmedia');
|
HtmlEditorConfig::get('cms')->removeButtons('sslink', 'ssmedia');
|
||||||
HtmlEditorConfig::get('cms')->addButtonsToLine(2, 'link', 'media');
|
HtmlEditorConfig::get('cms')->addButtonsToLine(2, 'link', 'media');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Developing a wrapper to use a different WYSIWYG editors with HTMLEditorField
|
### Developing a wrapper to use a different WYSIWYG editors with HTMLEditorField
|
||||||
@ -342,22 +342,22 @@ Assuming you have the module installed, here's how you enable its use in `mysite
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
HtmlEditorConfig::get('cms')->enablePlugins('spellchecker', 'contextmenu');
|
HtmlEditorConfig::get('cms')->enablePlugins('spellchecker', 'contextmenu');
|
||||||
HtmlEditorConfig::get('cms')->addButtonsToLine(2, 'spellchecker');
|
HtmlEditorConfig::get('cms')->addButtonsToLine(2, 'spellchecker');
|
||||||
HtmlEditorConfig::get('cms')->setOption(
|
HtmlEditorConfig::get('cms')->setOption(
|
||||||
'spellchecker_rpc_url',
|
'spellchecker_rpc_url',
|
||||||
THIRDPARTY_DIR . '/tinymce-spellchecker/rpc.php'
|
THIRDPARTY_DIR . '/tinymce-spellchecker/rpc.php'
|
||||||
);
|
);
|
||||||
HtmlEditorConfig::get('cms')->setOption('browser_spellcheck', false);
|
HtmlEditorConfig::get('cms')->setOption('browser_spellcheck', false);
|
||||||
```
|
```
|
||||||
|
|
||||||
Now change the default spellchecker in `framework/thirdparty/tinymce-spellchecker/config.php`:
|
Now change the default spellchecker in `framework/thirdparty/tinymce-spellchecker/config.php`:
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
$config['general.engine'] = 'PSpell';
|
$config['general.engine'] = 'PSpell';
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ tabular data in a format that is easy to view and modify. It can be thought of a
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$field = new GridField($name, $title, $list);
|
$field = new GridField($name, $title, $list);
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="hint" markdown='1'>
|
<div class="hint" markdown='1'>
|
||||||
@ -29,23 +29,23 @@ actions such as deleting records.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Forms\GridField\GridField;
|
use SilverStripe\Forms\GridField\GridField;
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
|
||||||
class Page extends SiteTree
|
class Page extends SiteTree
|
||||||
{
|
{
|
||||||
|
|
||||||
public function getCMSFields()
|
public function getCMSFields()
|
||||||
{
|
{
|
||||||
$fields = parent::getCMSFields();
|
$fields = parent::getCMSFields();
|
||||||
|
|
||||||
$fields->addFieldToTab('Root.Pages',
|
$fields->addFieldToTab('Root.Pages',
|
||||||
new GridField('Pages', 'All pages', SiteTree::get())
|
new GridField('Pages', 'All pages', SiteTree::get())
|
||||||
);
|
);
|
||||||
|
|
||||||
return $fields;
|
return $fields;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
This will display a bare bones `GridField` instance under `Pages` tab in the CMS. As we have not specified the
|
This will display a bare bones `GridField` instance under `Pages` tab in the CMS. As we have not specified the
|
||||||
@ -65,82 +65,82 @@ the `getConfig()` method on `GridField`.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Forms\GridField\GridField;
|
use SilverStripe\Forms\GridField\GridField;
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
|
||||||
class Page extends SiteTree
|
class Page extends SiteTree
|
||||||
{
|
{
|
||||||
|
|
||||||
public function getCMSFields()
|
public function getCMSFields()
|
||||||
{
|
{
|
||||||
$fields = parent::getCMSFields();
|
$fields = parent::getCMSFields();
|
||||||
|
|
||||||
$fields->addFieldToTab('Root.Pages',
|
$fields->addFieldToTab('Root.Pages',
|
||||||
$grid = new GridField('Pages', 'All pages', SiteTree::get())
|
$grid = new GridField('Pages', 'All pages', SiteTree::get())
|
||||||
);
|
);
|
||||||
|
|
||||||
// GridField configuration
|
// GridField configuration
|
||||||
$config = $grid->getConfig();
|
$config = $grid->getConfig();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Modification of existing components can be done by fetching that component.
|
// Modification of existing components can be done by fetching that component.
|
||||||
// Consult the API documentation for each component to determine the configuration
|
// Consult the API documentation for each component to determine the configuration
|
||||||
// you can do.
|
// you can do.
|
||||||
//
|
//
|
||||||
$dataColumns = $config->getComponentByType('GridFieldDataColumns');
|
$dataColumns = $config->getComponentByType('GridFieldDataColumns');
|
||||||
|
|
||||||
$dataColumns->setDisplayFields([
|
$dataColumns->setDisplayFields([
|
||||||
'Title' => 'Title',
|
'Title' => 'Title',
|
||||||
'Link'=> 'URL',
|
'Link'=> 'URL',
|
||||||
'LastEdited' => 'Changed'
|
'LastEdited' => 'Changed'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $fields;
|
return $fields;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
With the `GridFieldConfig` instance, we can modify the behavior of the `GridField`.
|
With the `GridFieldConfig` instance, we can modify the behavior of the `GridField`.
|
||||||
```php
|
```php
|
||||||
// `GridFieldConfig::create()` will create an empty configuration (no components).
|
// `GridFieldConfig::create()` will create an empty configuration (no components).
|
||||||
$config = GridFieldConfig::create();
|
$config = GridFieldConfig::create();
|
||||||
|
|
||||||
// add a component
|
// add a component
|
||||||
$config->addComponent(new GridFieldDataColumns());
|
$config->addComponent(new GridFieldDataColumns());
|
||||||
|
|
||||||
// Update the GridField with our custom configuration
|
// Update the GridField with our custom configuration
|
||||||
$gridField->setConfig($config);
|
$gridField->setConfig($config);
|
||||||
```
|
```
|
||||||
|
|
||||||
`GridFieldConfig` provides a number of methods to make setting the configuration easier. We can insert a component
|
`GridFieldConfig` provides a number of methods to make setting the configuration easier. We can insert a component
|
||||||
before another component by passing the second parameter.
|
before another component by passing the second parameter.
|
||||||
```php
|
```php
|
||||||
$config->addComponent(new GridFieldFilterHeader(), 'GridFieldDataColumns');
|
$config->addComponent(new GridFieldFilterHeader(), 'GridFieldDataColumns');
|
||||||
```
|
```
|
||||||
|
|
||||||
We can add multiple components in one call.
|
We can add multiple components in one call.
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$config->addComponents(
|
$config->addComponents(
|
||||||
new GridFieldDataColumns(),
|
new GridFieldDataColumns(),
|
||||||
new GridFieldToolbarHeader()
|
new GridFieldToolbarHeader()
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
Or, remove a component.
|
Or, remove a component.
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$config->removeComponentsByType('GridFieldDeleteAction');
|
$config->removeComponentsByType('GridFieldDeleteAction');
|
||||||
```
|
```
|
||||||
|
|
||||||
Fetch a component to modify it later on.
|
Fetch a component to modify it later on.
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$component = $config->getComponentByType('GridFieldFilterHeader')
|
$component = $config->getComponentByType('GridFieldFilterHeader')
|
||||||
```
|
```
|
||||||
|
|
||||||
Here is a list of components for use bundled with the core framework. Many more components are provided by third-party
|
Here is a list of components for use bundled with the core framework. Many more components are provided by third-party
|
||||||
@ -169,17 +169,17 @@ A simple read-only and paginated view of records with sortable and searchable he
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$config = GridFieldConfig_Base::create();
|
$config = GridFieldConfig_Base::create();
|
||||||
|
|
||||||
$gridField->setConfig($config);
|
$gridField->setConfig($config);
|
||||||
|
|
||||||
// Is the same as adding the following components..
|
// Is the same as adding the following components..
|
||||||
// .. new GridFieldToolbarHeader()
|
// .. new GridFieldToolbarHeader()
|
||||||
// .. new GridFieldSortableHeader()
|
// .. new GridFieldSortableHeader()
|
||||||
// .. new GridFieldFilterHeader()
|
// .. new GridFieldFilterHeader()
|
||||||
// .. new GridFieldDataColumns()
|
// .. new GridFieldDataColumns()
|
||||||
// .. new GridFieldPageCount('toolbar-header-right')
|
// .. new GridFieldPageCount('toolbar-header-right')
|
||||||
// .. new GridFieldPaginator($itemsPerPage)
|
// .. new GridFieldPaginator($itemsPerPage)
|
||||||
```
|
```
|
||||||
|
|
||||||
### GridFieldConfig_RecordViewer
|
### GridFieldConfig_RecordViewer
|
||||||
@ -199,13 +199,13 @@ this record.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$config = GridFieldConfig_RecordViewer::create();
|
$config = GridFieldConfig_RecordViewer::create();
|
||||||
|
|
||||||
$gridField->setConfig($config);
|
$gridField->setConfig($config);
|
||||||
|
|
||||||
// Same as GridFieldConfig_Base with the addition of
|
// Same as GridFieldConfig_Base with the addition of
|
||||||
// .. new GridFieldViewButton(),
|
// .. new GridFieldViewButton(),
|
||||||
// .. new GridFieldDetailForm()
|
// .. new GridFieldDetailForm()
|
||||||
```
|
```
|
||||||
|
|
||||||
### GridFieldConfig_RecordEditor
|
### GridFieldConfig_RecordEditor
|
||||||
@ -224,14 +224,14 @@ Permission control for editing and deleting the record uses the `canEdit()` and
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$config = GridFieldConfig_RecordEditor::create();
|
$config = GridFieldConfig_RecordEditor::create();
|
||||||
|
|
||||||
$gridField->setConfig($config);
|
$gridField->setConfig($config);
|
||||||
|
|
||||||
// Same as GridFieldConfig_RecordViewer with the addition of
|
// Same as GridFieldConfig_RecordViewer with the addition of
|
||||||
// .. new GridFieldAddNewButton(),
|
// .. new GridFieldAddNewButton(),
|
||||||
// .. new GridFieldEditButton(),
|
// .. new GridFieldEditButton(),
|
||||||
// .. new GridFieldDeleteAction()
|
// .. new GridFieldDeleteAction()
|
||||||
```
|
```
|
||||||
|
|
||||||
### GridFieldConfig_RelationEditor
|
### GridFieldConfig_RelationEditor
|
||||||
@ -239,9 +239,9 @@ Permission control for editing and deleting the record uses the `canEdit()` and
|
|||||||
Similar to `GridFieldConfig_RecordEditor`, but adds features to work on a record's has-many or many-many relationships.
|
Similar to `GridFieldConfig_RecordEditor`, but adds features to work on a record's has-many or many-many relationships.
|
||||||
As such, it expects the list used with the `GridField` to be a instance of `RelationList`.
|
As such, it expects the list used with the `GridField` to be a instance of `RelationList`.
|
||||||
```php
|
```php
|
||||||
$config = GridFieldConfig_RelationEditor::create();
|
$config = GridFieldConfig_RelationEditor::create();
|
||||||
|
|
||||||
$gridField->setConfig($config);
|
$gridField->setConfig($config);
|
||||||
```
|
```
|
||||||
|
|
||||||
This configuration adds the ability to searched for existing records and add a relationship
|
This configuration adds the ability to searched for existing records and add a relationship
|
||||||
@ -258,10 +258,10 @@ The `GridFieldDetailForm` component drives the record viewing and editing form.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$form = $gridField->getConfig()->getComponentByType('GridFieldDetailForm');
|
$form = $gridField->getConfig()->getComponentByType('GridFieldDetailForm');
|
||||||
$form->setFields(new FieldList(
|
$form->setFields(new FieldList(
|
||||||
new TextField('Title')
|
new TextField('Title')
|
||||||
));
|
));
|
||||||
```
|
```
|
||||||
|
|
||||||
### many_many_extraFields
|
### many_many_extraFields
|
||||||
@ -277,61 +277,61 @@ The namespace notation is `ManyMany[<extradata-field-name>]`, so for example `Ma
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Forms\TextField;
|
use SilverStripe\Forms\TextField;
|
||||||
use SilverStripe\Forms\GridField\GridField;
|
use SilverStripe\Forms\GridField\GridField;
|
||||||
use SilverStripe\Forms\GridField\GridFieldConfig_RelationEditor;
|
use SilverStripe\Forms\GridField\GridFieldConfig_RelationEditor;
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Team extends DataObject
|
class Team extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Name' => 'Text'
|
'Name' => 'Text'
|
||||||
];
|
];
|
||||||
|
|
||||||
public static $many_many = [
|
public static $many_many = [
|
||||||
'Players' => 'Player'
|
'Players' => 'Player'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
class Player extends DataObject
|
class Player extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Name' => 'Text'
|
'Name' => 'Text'
|
||||||
];
|
];
|
||||||
|
|
||||||
public static $many_many = [
|
public static $many_many = [
|
||||||
'Teams' => 'Team'
|
'Teams' => 'Team'
|
||||||
];
|
];
|
||||||
|
|
||||||
public static $many_many_extraFields = [
|
public static $many_many_extraFields = [
|
||||||
'Teams' => [
|
'Teams' => [
|
||||||
'Position' => 'Text'
|
'Position' => 'Text'
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
public function getCMSFields()
|
public function getCMSFields()
|
||||||
{
|
{
|
||||||
$fields = parent::getCMSFields();
|
$fields = parent::getCMSFields();
|
||||||
|
|
||||||
if($this->ID) {
|
if($this->ID) {
|
||||||
$teamFields = singleton('Team')->getCMSFields();
|
$teamFields = singleton('Team')->getCMSFields();
|
||||||
$teamFields->addFieldToTab(
|
$teamFields->addFieldToTab(
|
||||||
'Root.Main',
|
'Root.Main',
|
||||||
// The "ManyMany[<extradata-name>]" convention
|
// The "ManyMany[<extradata-name>]" convention
|
||||||
new TextField('ManyMany[Position]', 'Current Position')
|
new TextField('ManyMany[Position]', 'Current Position')
|
||||||
);
|
);
|
||||||
|
|
||||||
$config = GridFieldConfig_RelationEditor::create();
|
$config = GridFieldConfig_RelationEditor::create();
|
||||||
$config->getComponentByType('GridFieldDetailForm')->setFields($teamFields);
|
$config->getComponentByType('GridFieldDetailForm')->setFields($teamFields);
|
||||||
|
|
||||||
$gridField = new GridField('Teams', 'Teams', $this->Teams(), $config);
|
$gridField = new GridField('Teams', 'Teams', $this->Teams(), $config);
|
||||||
$fields->findOrMakeTab('Root.Teams')->replaceField('Teams', $gridField);
|
$fields->findOrMakeTab('Root.Teams')->replaceField('Teams', $gridField);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $fields;
|
return $fields;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -354,8 +354,8 @@ bottom right of the table.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$config->addComponent(new GridFieldButtonRow('after'));
|
$config->addComponent(new GridFieldButtonRow('after'));
|
||||||
$config->addComponent(new GridFieldPrintButton('buttons-after-right'));
|
$config->addComponent(new GridFieldPrintButton('buttons-after-right'));
|
||||||
```
|
```
|
||||||
|
|
||||||
### Creating your own Fragments
|
### Creating your own Fragments
|
||||||
@ -366,16 +366,16 @@ create an area rendered before the table wrapped in a simple `<div>`.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
class MyAreaComponent implements GridField_HTMLProvider
|
class MyAreaComponent implements GridField_HTMLProvider
|
||||||
{
|
{
|
||||||
|
|
||||||
public function getHTMLFragments( $gridField)
|
public function getHTMLFragments( $gridField)
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'before' => '<div class="my-area">$DefineFragment(my-area)</div>'
|
'before' => '<div class="my-area">$DefineFragment(my-area)</div>'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -389,16 +389,16 @@ Now you can add other components into this area by returning them as an array fr
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
class MyShareLinkComponent implements GridField_HTMLProvider
|
class MyShareLinkComponent implements GridField_HTMLProvider
|
||||||
{
|
{
|
||||||
|
|
||||||
public function getHTMLFragments( $gridField)
|
public function getHTMLFragments( $gridField)
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'my-area' => '<a href>...</a>'
|
'my-area' => '<a href>...</a>'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -406,7 +406,7 @@ Your new area can also be used by existing components, e.g. the [GridFieldPrintB
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
new GridFieldPrintButton('my-component-area');
|
new GridFieldPrintButton('my-component-area');
|
||||||
```
|
```
|
||||||
|
|
||||||
## Creating a Custom GridFieldComponent
|
## Creating a Custom GridFieldComponent
|
||||||
|
@ -11,66 +11,66 @@ code for a `Form` is to create it as a subclass to `Form`. Let's look at a examp
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Forms\FieldList;
|
use SilverStripe\Forms\FieldList;
|
||||||
use SilverStripe\Forms\RequiredFields;
|
use SilverStripe\Forms\RequiredFields;
|
||||||
use SilverStripe\Forms\Form;
|
use SilverStripe\Forms\Form;
|
||||||
use SilverStripe\Forms\HeaderField;
|
use SilverStripe\Forms\HeaderField;
|
||||||
use SilverStripe\Forms\OptionsetField;
|
use SilverStripe\Forms\OptionsetField;
|
||||||
use SilverStripe\Forms\CompositeField;
|
use SilverStripe\Forms\CompositeField;
|
||||||
use SilverStripe\Forms\CheckboxSetField;
|
use SilverStripe\Forms\CheckboxSetField;
|
||||||
use SilverStripe\Forms\NumericField;
|
use SilverStripe\Forms\NumericField;
|
||||||
use SilverStripe\Forms\FormAction;
|
use SilverStripe\Forms\FormAction;
|
||||||
use SilverStripe\CMS\Controllers\ContentController;
|
use SilverStripe\CMS\Controllers\ContentController;
|
||||||
|
|
||||||
class PageController extends ContentController
|
class PageController extends ContentController
|
||||||
{
|
{
|
||||||
|
|
||||||
public function SearchForm()
|
public function SearchForm()
|
||||||
{
|
{
|
||||||
$fields = new FieldList(
|
$fields = new FieldList(
|
||||||
HeaderField::create('Header', 'Step 1. Basics'),
|
HeaderField::create('Header', 'Step 1. Basics'),
|
||||||
OptionsetField::create('Type', '', [
|
OptionsetField::create('Type', '', [
|
||||||
'foo' => 'Search Foo',
|
'foo' => 'Search Foo',
|
||||||
'bar' => 'Search Bar',
|
'bar' => 'Search Bar',
|
||||||
'baz' => 'Search Baz'
|
'baz' => 'Search Baz'
|
||||||
]),
|
]),
|
||||||
|
|
||||||
CompositeField::create(
|
CompositeField::create(
|
||||||
HeaderField::create('Header2', 'Step 2. Advanced '),
|
HeaderField::create('Header2', 'Step 2. Advanced '),
|
||||||
CheckboxSetField::create('Foo', 'Select Option', [
|
CheckboxSetField::create('Foo', 'Select Option', [
|
||||||
'qux' => 'Search Qux'
|
'qux' => 'Search Qux'
|
||||||
]),
|
]),
|
||||||
|
|
||||||
CheckboxSetField::create('Category', 'Category', [
|
CheckboxSetField::create('Category', 'Category', [
|
||||||
'Foo' => 'Foo',
|
'Foo' => 'Foo',
|
||||||
'Bar' => 'Bar'
|
'Bar' => 'Bar'
|
||||||
]),
|
]),
|
||||||
|
|
||||||
NumericField::create('Minimum', 'Minimum'),
|
NumericField::create('Minimum', 'Minimum'),
|
||||||
NumericField::create('Maximum', 'Maximum')
|
NumericField::create('Maximum', 'Maximum')
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
$actions = new FieldList(
|
$actions = new FieldList(
|
||||||
FormAction::create('doSearchForm', 'Search')
|
FormAction::create('doSearchForm', 'Search')
|
||||||
);
|
);
|
||||||
|
|
||||||
$required = new RequiredFields([
|
$required = new RequiredFields([
|
||||||
'Type'
|
'Type'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$form = new Form($this, 'SearchForm', $fields, $actions, $required);
|
$form = new Form($this, 'SearchForm', $fields, $actions, $required);
|
||||||
$form->setFormMethod('GET');
|
$form->setFormMethod('GET');
|
||||||
|
|
||||||
$form->addExtraClass('no-action-styles');
|
$form->addExtraClass('no-action-styles');
|
||||||
$form->disableSecurityToken();
|
$form->disableSecurityToken();
|
||||||
$form->loadDataFrom($_REQUEST);
|
$form->loadDataFrom($_REQUEST);
|
||||||
|
|
||||||
return $form;
|
return $form;
|
||||||
}
|
}
|
||||||
|
|
||||||
..
|
..
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -81,70 +81,70 @@ should be. Good practice would be to move this to a subclass and create a new in
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Forms\FieldList;
|
use SilverStripe\Forms\FieldList;
|
||||||
use SilverStripe\Forms\RequiredFields;
|
use SilverStripe\Forms\RequiredFields;
|
||||||
use SilverStripe\Forms\HeaderField;
|
use SilverStripe\Forms\HeaderField;
|
||||||
use SilverStripe\Forms\OptionsetField;
|
use SilverStripe\Forms\OptionsetField;
|
||||||
use SilverStripe\Forms\CompositeField;
|
use SilverStripe\Forms\CompositeField;
|
||||||
use SilverStripe\Forms\CheckboxSetField;
|
use SilverStripe\Forms\CheckboxSetField;
|
||||||
use SilverStripe\Forms\NumericField;
|
use SilverStripe\Forms\NumericField;
|
||||||
use SilverStripe\Forms\FormAction;
|
use SilverStripe\Forms\FormAction;
|
||||||
use SilverStripe\Forms\Form;
|
use SilverStripe\Forms\Form;
|
||||||
|
|
||||||
class SearchForm extends Form
|
class SearchForm extends Form
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Our constructor only requires the controller and the name of the form
|
* Our constructor only requires the controller and the name of the form
|
||||||
* method. We'll create the fields and actions in here.
|
* method. We'll create the fields and actions in here.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function __construct($controller, $name)
|
public function __construct($controller, $name)
|
||||||
{
|
{
|
||||||
$fields = new FieldList(
|
$fields = new FieldList(
|
||||||
HeaderField::create('Header', 'Step 1. Basics'),
|
HeaderField::create('Header', 'Step 1. Basics'),
|
||||||
OptionsetField::create('Type', '', [
|
OptionsetField::create('Type', '', [
|
||||||
'foo' => 'Search Foo',
|
'foo' => 'Search Foo',
|
||||||
'bar' => 'Search Bar',
|
'bar' => 'Search Bar',
|
||||||
'baz' => 'Search Baz'
|
'baz' => 'Search Baz'
|
||||||
]),
|
]),
|
||||||
|
|
||||||
CompositeField::create(
|
CompositeField::create(
|
||||||
HeaderField::create('Header2', 'Step 2. Advanced '),
|
HeaderField::create('Header2', 'Step 2. Advanced '),
|
||||||
CheckboxSetField::create('Foo', 'Select Option', [
|
CheckboxSetField::create('Foo', 'Select Option', [
|
||||||
'qux' => 'Search Qux'
|
'qux' => 'Search Qux'
|
||||||
]),
|
]),
|
||||||
|
|
||||||
CheckboxSetField::create('Category', 'Category', [
|
CheckboxSetField::create('Category', 'Category', [
|
||||||
'Foo' => 'Foo',
|
'Foo' => 'Foo',
|
||||||
'Bar' => 'Bar'
|
'Bar' => 'Bar'
|
||||||
]),
|
]),
|
||||||
|
|
||||||
NumericField::create('Minimum', 'Minimum'),
|
NumericField::create('Minimum', 'Minimum'),
|
||||||
NumericField::create('Maximum', 'Maximum')
|
NumericField::create('Maximum', 'Maximum')
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
$actions = new FieldList(
|
$actions = new FieldList(
|
||||||
FormAction::create('doSearchForm', 'Search')
|
FormAction::create('doSearchForm', 'Search')
|
||||||
);
|
);
|
||||||
|
|
||||||
$required = new RequiredFields([
|
$required = new RequiredFields([
|
||||||
'Type'
|
'Type'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// now we create the actual form with our fields and actions defined
|
// now we create the actual form with our fields and actions defined
|
||||||
// within this class
|
// within this class
|
||||||
parent::__construct($controller, $name, $fields, $actions, $required);
|
parent::__construct($controller, $name, $fields, $actions, $required);
|
||||||
|
|
||||||
// any modifications we need to make to the form.
|
// any modifications we need to make to the form.
|
||||||
$this->setFormMethod('GET');
|
$this->setFormMethod('GET');
|
||||||
|
|
||||||
$this->addExtraClass('no-action-styles');
|
$this->addExtraClass('no-action-styles');
|
||||||
$this->disableSecurityToken();
|
$this->disableSecurityToken();
|
||||||
$this->loadDataFrom($_REQUEST);
|
$this->loadDataFrom($_REQUEST);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -154,21 +154,21 @@ Our controller will now just have to create a new instance of this form object.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SearchForm;
|
use SearchForm;
|
||||||
use SilverStripe\CMS\Controllers\ContentController;
|
use SilverStripe\CMS\Controllers\ContentController;
|
||||||
|
|
||||||
class PageController extends ContentController
|
class PageController extends ContentController
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $allowed_actions = [
|
private static $allowed_actions = [
|
||||||
'SearchForm',
|
'SearchForm',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function SearchForm()
|
public function SearchForm()
|
||||||
{
|
{
|
||||||
return new SearchForm($this, 'SearchForm');
|
return new SearchForm($this, 'SearchForm');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -13,22 +13,22 @@ totally custom template to meet our needs. To do this, we'll provide the class w
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
|
|
||||||
public function SearchForm()
|
public function SearchForm()
|
||||||
{
|
{
|
||||||
$fields = new FieldList(
|
$fields = new FieldList(
|
||||||
TextField::create('q')
|
TextField::create('q')
|
||||||
);
|
);
|
||||||
|
|
||||||
$actions = new FieldList(
|
$actions = new FieldList(
|
||||||
FormAction::create('doSearch', 'Search')
|
FormAction::create('doSearch', 'Search')
|
||||||
);
|
);
|
||||||
|
|
||||||
$form = new Form($this, 'SearchForm', $fields, $actions);
|
$form = new Form($this, 'SearchForm', $fields, $actions);
|
||||||
$form->setTemplate('SearchForm');
|
$form->setTemplate('SearchForm');
|
||||||
|
|
||||||
return $form;
|
return $form;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**mysite/templates/Includes/SearchForm.ss**
|
**mysite/templates/Includes/SearchForm.ss**
|
||||||
@ -36,15 +36,15 @@ totally custom template to meet our needs. To do this, we'll provide the class w
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<form $FormAttributes>
|
<form $FormAttributes>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
$Fields.dataFieldByName(q)
|
$Fields.dataFieldByName(q)
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<div class="Actions">
|
<div class="Actions">
|
||||||
<% loop $Actions %>$Field<% end_loop %>
|
<% loop $Actions %>$Field<% end_loop %>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
```
|
```
|
||||||
|
|
||||||
`SearchForm.ss` will be executed within the scope of the `Form` object so has access to any of the methods and
|
`SearchForm.ss` will be executed within the scope of the `Form` object so has access to any of the methods and
|
||||||
|
@ -19,66 +19,66 @@ below:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
class GridFieldCustomAction implements GridField_ColumnProvider, GridField_ActionProvider
|
class GridFieldCustomAction implements GridField_ColumnProvider, GridField_ActionProvider
|
||||||
{
|
{
|
||||||
|
|
||||||
public function augmentColumns($gridField, &$columns)
|
public function augmentColumns($gridField, &$columns)
|
||||||
{
|
{
|
||||||
if(!in_array('Actions', $columns)) {
|
if(!in_array('Actions', $columns)) {
|
||||||
$columns[] = 'Actions';
|
$columns[] = 'Actions';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getColumnAttributes($gridField, $record, $columnName)
|
public function getColumnAttributes($gridField, $record, $columnName)
|
||||||
{
|
{
|
||||||
return ['class' => 'grid-field__col-compact'];
|
return ['class' => 'grid-field__col-compact'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getColumnMetadata($gridField, $columnName)
|
public function getColumnMetadata($gridField, $columnName)
|
||||||
{
|
{
|
||||||
if($columnName == 'Actions') {
|
if($columnName == 'Actions') {
|
||||||
return ['title' => ''];
|
return ['title' => ''];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getColumnsHandled($gridField)
|
public function getColumnsHandled($gridField)
|
||||||
{
|
{
|
||||||
return ['Actions'];
|
return ['Actions'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getColumnContent($gridField, $record, $columnName)
|
public function getColumnContent($gridField, $record, $columnName)
|
||||||
{
|
{
|
||||||
if(!$record->canEdit()) return;
|
if(!$record->canEdit()) return;
|
||||||
|
|
||||||
$field = GridField_FormAction::create(
|
$field = GridField_FormAction::create(
|
||||||
$gridField,
|
$gridField,
|
||||||
'CustomAction'.$record->ID,
|
'CustomAction'.$record->ID,
|
||||||
'Do Action',
|
'Do Action',
|
||||||
"docustomaction",
|
"docustomaction",
|
||||||
['RecordID' => $record->ID]
|
['RecordID' => $record->ID]
|
||||||
);
|
);
|
||||||
|
|
||||||
return $field->Field();
|
return $field->Field();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getActions($gridField)
|
public function getActions($gridField)
|
||||||
{
|
{
|
||||||
return ['docustomaction'];
|
return ['docustomaction'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleAction(GridField $gridField, $actionName, $arguments, $data)
|
public function handleAction(GridField $gridField, $actionName, $arguments, $data)
|
||||||
{
|
{
|
||||||
if($actionName == 'docustomaction') {
|
if($actionName == 'docustomaction') {
|
||||||
// perform your action here
|
// perform your action here
|
||||||
|
|
||||||
// output a success message to the user
|
// output a success message to the user
|
||||||
Controller::curr()->getResponse()->setStatusCode(
|
Controller::curr()->getResponse()->setStatusCode(
|
||||||
200,
|
200,
|
||||||
'Do Custom Action Done.'
|
'Do Custom Action Done.'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -91,14 +91,14 @@ manipulating the `GridFieldConfig` instance if required.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// option 1: creating a new GridField with the CustomAction
|
// option 1: creating a new GridField with the CustomAction
|
||||||
$config = GridFieldConfig::create();
|
$config = GridFieldConfig::create();
|
||||||
$config->addComponent(new GridFieldCustomAction());
|
$config->addComponent(new GridFieldCustomAction());
|
||||||
|
|
||||||
$gridField = new GridField('Teams', 'Teams', $this->Teams(), $config);
|
$gridField = new GridField('Teams', 'Teams', $this->Teams(), $config);
|
||||||
|
|
||||||
// option 2: adding the CustomAction to an exisitng GridField
|
// option 2: adding the CustomAction to an exisitng GridField
|
||||||
$gridField->getConfig()->addComponent(new GridFieldCustomAction());
|
$gridField->getConfig()->addComponent(new GridFieldCustomAction());
|
||||||
```
|
```
|
||||||
|
|
||||||
For documentation on adding a Component to a `GridField` created by `ModelAdmin`
|
For documentation on adding a Component to a `GridField` created by `ModelAdmin`
|
||||||
|
@ -6,34 +6,34 @@ Let's start by defining a new `ContactPage` page type:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Forms\FieldList;
|
use SilverStripe\Forms\FieldList;
|
||||||
use SilverStripe\Forms\TextField;
|
use SilverStripe\Forms\TextField;
|
||||||
use SilverStripe\Forms\EmailField;
|
use SilverStripe\Forms\EmailField;
|
||||||
use SilverStripe\Forms\TextareaField;
|
use SilverStripe\Forms\TextareaField;
|
||||||
use SilverStripe\Forms\FormAction;
|
use SilverStripe\Forms\FormAction;
|
||||||
use SilverStripe\Forms\Form;
|
use SilverStripe\Forms\Form;
|
||||||
use Page;
|
use Page;
|
||||||
use PageController;
|
use PageController;
|
||||||
|
|
||||||
class ContactPage extends Page
|
class ContactPage extends Page
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
class ContactPageController extends PageController
|
class ContactPageController extends PageController
|
||||||
{
|
{
|
||||||
private static $allowed_actions = ['Form'];
|
private static $allowed_actions = ['Form'];
|
||||||
public function Form()
|
public function Form()
|
||||||
{
|
{
|
||||||
$fields = new FieldList(
|
$fields = new FieldList(
|
||||||
new TextField('Name'),
|
new TextField('Name'),
|
||||||
new EmailField('Email'),
|
new EmailField('Email'),
|
||||||
new TextareaField('Message')
|
new TextareaField('Message')
|
||||||
);
|
);
|
||||||
$actions = new FieldList(
|
$actions = new FieldList(
|
||||||
new FormAction('submit', 'Submit')
|
new FormAction('submit', 'Submit')
|
||||||
);
|
);
|
||||||
return new Form($this, 'Form', $fields, $actions);
|
return new Form($this, 'Form', $fields, $actions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -43,27 +43,27 @@ There's quite a bit in this function, so we'll step through one piece at a time.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$fields = new FieldList(
|
$fields = new FieldList(
|
||||||
new TextField('Name'),
|
new TextField('Name'),
|
||||||
new EmailField('Email'),
|
new EmailField('Email'),
|
||||||
new TextareaField('Message')
|
new TextareaField('Message')
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
First we create all the fields we want in the contact form, and put them inside a FieldList. You can find a list of form fields available on the [FormField](api:SilverStripe\Forms\FormField) page.
|
First we create all the fields we want in the contact form, and put them inside a FieldList. You can find a list of form fields available on the [FormField](api:SilverStripe\Forms\FormField) page.
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$actions = FieldList(
|
$actions = FieldList(
|
||||||
new FormAction('submit', 'Submit')
|
new FormAction('submit', 'Submit')
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
We then create a [FieldList](api:SilverStripe\Forms\FieldList) of the form actions, or the buttons that submit the form. Here we add a single form action, with the name 'submit', and the label 'Submit'. We'll use the name of the form action later.
|
We then create a [FieldList](api:SilverStripe\Forms\FieldList) of the form actions, or the buttons that submit the form. Here we add a single form action, with the name 'submit', and the label 'Submit'. We'll use the name of the form action later.
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
return new Form($this, 'Form', $fields, $actions);
|
return new Form($this, 'Form', $fields, $actions);
|
||||||
```
|
```
|
||||||
|
|
||||||
Finally we create the `Form` object and return it. The first argument is the controller that the form is on – this is almost always $this. The second argument is the name of the form – this has to be the same as the name of the function that creates the form, so we've used 'Form'. The third and fourth arguments are the fields and actions we created earlier.
|
Finally we create the `Form` object and return it. The first argument is the controller that the form is on – this is almost always $this. The second argument is the name of the form – this has to be the same as the name of the function that creates the form, so we've used 'Form'. The third and fourth arguments are the fields and actions we created earlier.
|
||||||
@ -82,36 +82,36 @@ Now that we have a contact form, we need some way of collecting the data submitt
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Control\Email\Email;
|
use SilverStripe\Control\Email\Email;
|
||||||
use PageController;
|
use PageController;
|
||||||
|
|
||||||
class ContactPageController extends PageController
|
class ContactPageController extends PageController
|
||||||
{
|
{
|
||||||
private static $allowed_actions = ['Form'];
|
private static $allowed_actions = ['Form'];
|
||||||
public function Form()
|
public function Form()
|
||||||
{
|
{
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
public function submit($data, $form)
|
public function submit($data, $form)
|
||||||
{
|
{
|
||||||
$email = new Email();
|
$email = new Email();
|
||||||
|
|
||||||
$email->setTo('siteowner@mysite.com');
|
$email->setTo('siteowner@mysite.com');
|
||||||
$email->setFrom($data['Email']);
|
$email->setFrom($data['Email']);
|
||||||
$email->setSubject("Contact Message from {$data["Name"]}");
|
$email->setSubject("Contact Message from {$data["Name"]}");
|
||||||
|
|
||||||
$messageBody = "
|
$messageBody = "
|
||||||
<p><strong>Name:</strong> {$data['Name']}</p>
|
<p><strong>Name:</strong> {$data['Name']}</p>
|
||||||
<p><strong>Message:</strong> {$data['Message']}</p>
|
<p><strong>Message:</strong> {$data['Message']}</p>
|
||||||
";
|
";
|
||||||
$email->setBody($messageBody);
|
$email->setBody($messageBody);
|
||||||
$email->send();
|
$email->send();
|
||||||
return [
|
return [
|
||||||
'Content' => '<p>Thank you for your feedback.</p>',
|
'Content' => '<p>Thank you for your feedback.</p>',
|
||||||
'Form' => ''
|
'Form' => ''
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -137,12 +137,12 @@ The framework comes with a predefined validator called [RequiredFields](api:Silv
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function Form()
|
public function Form()
|
||||||
{
|
{
|
||||||
// ...
|
// ...
|
||||||
$validator = new RequiredFields('Name', 'Message');
|
$validator = new RequiredFields('Name', 'Message');
|
||||||
return new Form($this, 'Form', $fields, $actions, $validator);
|
return new Form($this, 'Form', $fields, $actions, $validator);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
We've created a RequiredFields object, passing the name of the fields we want to be required. The validator we have created is then passed as the fifth argument of the form constructor. If we now try to submit the form without filling out the required fields, JavaScript validation will kick in, and the user will be presented with a message about the missing fields. If the user has JavaScript disabled, PHP validation will kick in when the form is submitted, and the user will be redirected back to the Form with messages about their missing fields.
|
We've created a RequiredFields object, passing the name of the fields we want to be required. The validator we have created is then passed as the fifth argument of the form constructor. If we now try to submit the form without filling out the required fields, JavaScript validation will kick in, and the user will be presented with a message about the missing fields. If the user has JavaScript disabled, PHP validation will kick in when the form is submitted, and the user will be redirected back to the Form with messages about their missing fields.
|
||||||
|
@ -29,23 +29,23 @@ be marked `private static` and follow the `lower_case_with_underscores` structur
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use Page;
|
use Page;
|
||||||
|
|
||||||
class MyClass extends Page
|
class MyClass extends Page
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @config
|
* @config
|
||||||
*/
|
*/
|
||||||
private static $option_one = true;
|
private static $option_one = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @config
|
* @config
|
||||||
*/
|
*/
|
||||||
private static $option_two = [];
|
private static $option_two = [];
|
||||||
|
|
||||||
// ..
|
// ..
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -55,12 +55,12 @@ This can be done by calling the static method [Config::inst()](api:SilverStripe\
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$config = Config::inst()->get('MyClass', 'property');
|
$config = Config::inst()->get('MyClass', 'property');
|
||||||
```
|
```
|
||||||
|
|
||||||
Or through the `config()` object on the class.
|
Or through the `config()` object on the class.
|
||||||
```php
|
```php
|
||||||
$config = $this->config()->get('property')';
|
$config = $this->config()->get('property')';
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that by default `Config::inst()` returns only an immutable version of config. Use `Config::modify()`
|
Note that by default `Config::inst()` returns only an immutable version of config. Use `Config::modify()`
|
||||||
@ -87,44 +87,44 @@ To set those configuration options on our previously defined class we can define
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
MyClass:
|
MyClass:
|
||||||
option_one: false
|
option_one: false
|
||||||
option_two:
|
option_two:
|
||||||
- Foo
|
- Foo
|
||||||
- Bar
|
- Bar
|
||||||
- Baz
|
- Baz
|
||||||
```
|
```
|
||||||
|
|
||||||
To use those variables in your application code:
|
To use those variables in your application code:
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$me = new MyClass();
|
$me = new MyClass();
|
||||||
|
|
||||||
echo $me->config()->option_one;
|
echo $me->config()->option_one;
|
||||||
// returns false
|
// returns false
|
||||||
|
|
||||||
echo implode(', ', $me->config()->option_two);
|
echo implode(', ', $me->config()->option_two);
|
||||||
// returns 'Foo, Bar, Baz'
|
// returns 'Foo, Bar, Baz'
|
||||||
|
|
||||||
echo Config::inst()->get('MyClass', 'option_one');
|
echo Config::inst()->get('MyClass', 'option_one');
|
||||||
// returns false
|
// returns false
|
||||||
|
|
||||||
echo implode(', ', Config::inst()->get('MyClass', 'option_two'));
|
echo implode(', ', Config::inst()->get('MyClass', 'option_two'));
|
||||||
// returns 'Foo, Bar, Baz'
|
// returns 'Foo, Bar, Baz'
|
||||||
|
|
||||||
Config::modify()->set('MyClass', 'option_one', true);
|
Config::modify()->set('MyClass', 'option_one', true);
|
||||||
|
|
||||||
echo Config::inst()->get('MyClass', 'option_one');
|
echo Config::inst()->get('MyClass', 'option_one');
|
||||||
// returns true
|
// returns true
|
||||||
|
|
||||||
// You can also use the static version
|
// You can also use the static version
|
||||||
MyClass::config()->option_two = [
|
MyClass::config()->option_two = [
|
||||||
'Qux'
|
'Qux'
|
||||||
];
|
];
|
||||||
|
|
||||||
echo implode(', ', MyClass::config()->option_one);
|
echo implode(', ', MyClass::config()->option_one);
|
||||||
// returns 'Qux'
|
// returns 'Qux'
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -204,16 +204,16 @@ The name of the files within the applications `_config` directly are arbitrary.
|
|||||||
The structure of each YAML file is a series of headers and values separated by YAML document separators.
|
The structure of each YAML file is a series of headers and values separated by YAML document separators.
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
---
|
---
|
||||||
Name: adminroutes
|
Name: adminroutes
|
||||||
After:
|
After:
|
||||||
- '#rootroutes'
|
- '#rootroutes'
|
||||||
- '#coreroutes'
|
- '#coreroutes'
|
||||||
---
|
---
|
||||||
Director:
|
Director:
|
||||||
rules:
|
rules:
|
||||||
'admin': 'AdminRootController'
|
'admin': 'AdminRootController'
|
||||||
---
|
---
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="info">
|
<div class="info">
|
||||||
@ -254,16 +254,16 @@ keys is a list of reference paths to other value sections. A basic example:
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
---
|
---
|
||||||
Name: adminroutes
|
Name: adminroutes
|
||||||
After:
|
After:
|
||||||
- '#rootroutes'
|
- '#rootroutes'
|
||||||
- '#coreroutes'
|
- '#coreroutes'
|
||||||
---
|
---
|
||||||
Director:
|
Director:
|
||||||
rules:
|
rules:
|
||||||
'admin': 'AdminRootController'
|
'admin': 'AdminRootController'
|
||||||
---
|
---
|
||||||
```
|
```
|
||||||
|
|
||||||
You do not have to specify all portions of a reference path. Any portion may be replaced with a wildcard "\*", or left
|
You do not have to specify all portions of a reference path. Any portion may be replaced with a wildcard "\*", or left
|
||||||
@ -320,19 +320,19 @@ For instance, to add a property to "foo" when a module exists, and "bar" otherwi
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
---
|
---
|
||||||
Only:
|
Only:
|
||||||
moduleexists: 'MyFineModule'
|
moduleexists: 'MyFineModule'
|
||||||
---
|
---
|
||||||
MyClass:
|
MyClass:
|
||||||
property: 'foo'
|
property: 'foo'
|
||||||
---
|
---
|
||||||
Except:
|
Except:
|
||||||
moduleexists: 'MyFineModule'
|
moduleexists: 'MyFineModule'
|
||||||
---
|
---
|
||||||
MyClass:
|
MyClass:
|
||||||
property: 'bar'
|
property: 'bar'
|
||||||
---
|
---
|
||||||
```
|
```
|
||||||
|
|
||||||
Multiple conditions of the same type can be declared via array format
|
Multiple conditions of the same type can be declared via array format
|
||||||
@ -341,11 +341,11 @@ Multiple conditions of the same type can be declared via array format
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|
||||||
---
|
---
|
||||||
Only:
|
Only:
|
||||||
moduleexists:
|
moduleexists:
|
||||||
- 'silverstripe/blog'
|
- 'silverstripe/blog'
|
||||||
- 'silverstripe/lumberjack'
|
- 'silverstripe/lumberjack'
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="alert" markdown="1">
|
<div class="alert" markdown="1">
|
||||||
|
@ -13,23 +13,23 @@ throughout the site. Out of the box this includes selecting the current site the
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
$SiteConfig.Title
|
$SiteConfig.Title
|
||||||
$SiteConfig.Tagline
|
$SiteConfig.Tagline
|
||||||
|
|
||||||
<% with $SiteConfig %>
|
<% with $SiteConfig %>
|
||||||
$Title $AnotherField
|
$Title $AnotherField
|
||||||
<% end_with %>
|
<% end_with %>
|
||||||
```
|
```
|
||||||
|
|
||||||
To access variables in the PHP:
|
To access variables in the PHP:
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$config = SiteConfig::current_site_config();
|
$config = SiteConfig::current_site_config();
|
||||||
|
|
||||||
echo $config->Title;
|
echo $config->Title;
|
||||||
|
|
||||||
// returns "Website Name"
|
// returns "Website Name"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Extending SiteConfig
|
## Extending SiteConfig
|
||||||
@ -40,23 +40,23 @@ To extend the options available in the panel, define your own fields via a [Data
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Forms\HTMLEditor\HTMLEditorField;
|
use SilverStripe\Forms\HTMLEditor\HTMLEditorField;
|
||||||
use SilverStripe\ORM\DataExtension;
|
use SilverStripe\ORM\DataExtension;
|
||||||
|
|
||||||
class CustomSiteConfig extends DataExtension
|
class CustomSiteConfig extends DataExtension
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'FooterContent' => 'HTMLText'
|
'FooterContent' => 'HTMLText'
|
||||||
];
|
];
|
||||||
|
|
||||||
public function updateCMSFields(FieldList $fields)
|
public function updateCMSFields(FieldList $fields)
|
||||||
{
|
{
|
||||||
$fields->addFieldToTab("Root.Main",
|
$fields->addFieldToTab("Root.Main",
|
||||||
new HTMLEditorField("FooterContent", "Footer Content")
|
new HTMLEditorField("FooterContent", "Footer Content")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -67,9 +67,9 @@ Then activate the extension.
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
Silverstripe\SiteConfig\SiteConfig:
|
Silverstripe\SiteConfig\SiteConfig:
|
||||||
extensions:
|
extensions:
|
||||||
- CustomSiteConfig
|
- CustomSiteConfig
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="notice" markdown="1">
|
<div class="notice" markdown="1">
|
||||||
|
@ -19,21 +19,21 @@ and `RequestHandler`. You can still apply extensions to descendants of these cla
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataExtension;
|
use SilverStripe\ORM\DataExtension;
|
||||||
|
|
||||||
class MyMemberExtension extends DataExtension
|
class MyMemberExtension extends DataExtension
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'DateOfBirth' => 'SS_Datetime'
|
'DateOfBirth' => 'SS_Datetime'
|
||||||
];
|
];
|
||||||
|
|
||||||
public function SayHi()
|
public function SayHi()
|
||||||
{
|
{
|
||||||
// $this->owner refers to the original instance. In this case a `Member`.
|
// $this->owner refers to the original instance. In this case a `Member`.
|
||||||
return "Hi " . $this->owner->Name;
|
return "Hi " . $this->owner->Name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -49,16 +49,16 @@ we want to add the `MyMemberExtension` too. To activate this extension, add the
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
Member:
|
Member:
|
||||||
extensions:
|
extensions:
|
||||||
- MyMemberExtension
|
- MyMemberExtension
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternatively, we can add extensions through PHP code (in the `_config.php` file).
|
Alternatively, we can add extensions through PHP code (in the `_config.php` file).
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
Member::add_extension('MyMemberExtension');
|
Member::add_extension('MyMemberExtension');
|
||||||
```
|
```
|
||||||
|
|
||||||
This class now defines a `MyMemberExtension` that applies to all `Member` instances on the website. It will have
|
This class now defines a `MyMemberExtension` that applies to all `Member` instances on the website. It will have
|
||||||
@ -82,25 +82,25 @@ $has_one etc.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataExtension;
|
use SilverStripe\ORM\DataExtension;
|
||||||
|
|
||||||
class MyMemberExtension extends DataExtension
|
class MyMemberExtension extends DataExtension
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Position' => 'Varchar',
|
'Position' => 'Varchar',
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $has_one = [
|
private static $has_one = [
|
||||||
'Image' => 'Image',
|
'Image' => 'Image',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function SayHi()
|
public function SayHi()
|
||||||
{
|
{
|
||||||
// $this->owner refers to the original instance. In this case a `Member`.
|
// $this->owner refers to the original instance. In this case a `Member`.
|
||||||
return "Hi " . $this->owner->Name;
|
return "Hi " . $this->owner->Name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -109,8 +109,8 @@ $has_one etc.
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
$CurrentMember.Position
|
$CurrentMember.Position
|
||||||
$CurrentMember.Image
|
$CurrentMember.Image
|
||||||
```
|
```
|
||||||
|
|
||||||
## Adding Methods
|
## Adding Methods
|
||||||
@ -122,18 +122,18 @@ we added a `SayHi` method which is unique to our extension.
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<p>$CurrentMember.SayHi</p>
|
<p>$CurrentMember.SayHi</p>
|
||||||
|
|
||||||
// "Hi Sam"
|
// "Hi Sam"
|
||||||
```
|
```
|
||||||
|
|
||||||
**mysite/code/Page.php**
|
**mysite/code/Page.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$member = Security::getCurrentUser();
|
$member = Security::getCurrentUser();
|
||||||
echo $member->SayHi;
|
echo $member->SayHi;
|
||||||
|
|
||||||
// "Hi Sam"
|
// "Hi Sam"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Modifying Existing Methods
|
## Modifying Existing Methods
|
||||||
@ -146,14 +146,14 @@ through the `extend()` method of the [Extensible](api:SilverStripe\Core\Extensib
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function getValidator()
|
public function getValidator()
|
||||||
{
|
{
|
||||||
// ..
|
// ..
|
||||||
|
|
||||||
$this->extend('updateValidator', $validator);
|
$this->extend('updateValidator', $validator);
|
||||||
|
|
||||||
// ..
|
// ..
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Extension Hooks can be located anywhere in the method and provide a point for any `Extension` instances to modify the
|
Extension Hooks can be located anywhere in the method and provide a point for any `Extension` instances to modify the
|
||||||
@ -165,19 +165,19 @@ validator by defining the `updateValidator` method.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataExtension;
|
use SilverStripe\ORM\DataExtension;
|
||||||
|
|
||||||
class MyMemberExtension extends DataExtension
|
class MyMemberExtension extends DataExtension
|
||||||
{
|
{
|
||||||
|
|
||||||
// ..
|
// ..
|
||||||
|
|
||||||
public function updateValidator($validator)
|
public function updateValidator($validator)
|
||||||
{
|
{
|
||||||
// we want to make date of birth required for each member
|
// we want to make date of birth required for each member
|
||||||
$validator->addRequiredField('DateOfBirth');
|
$validator->addRequiredField('DateOfBirth');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="info" markdown="1">
|
<div class="info" markdown="1">
|
||||||
@ -189,28 +189,28 @@ extension. The `CMS` provides a `updateCMSFields` Extension Hook to tie into.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Forms\TextField;
|
use SilverStripe\Forms\TextField;
|
||||||
use SilverStripe\AssetAdmin\Forms\UploadField;
|
use SilverStripe\AssetAdmin\Forms\UploadField;
|
||||||
use SilverStripe\ORM\DataExtension;
|
use SilverStripe\ORM\DataExtension;
|
||||||
|
|
||||||
class MyMemberExtension extends DataExtension
|
class MyMemberExtension extends DataExtension
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Position' => 'Varchar',
|
'Position' => 'Varchar',
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $has_one = [
|
private static $has_one = [
|
||||||
'Image' => 'Image',
|
'Image' => 'Image',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function updateCMSFields(FieldList $fields)
|
public function updateCMSFields(FieldList $fields)
|
||||||
{
|
{
|
||||||
$fields->push(new TextField('Position'));
|
$fields->push(new TextField('Position'));
|
||||||
$fields->push($upload = new UploadField('Image', 'Profile Image'));
|
$fields->push($upload = new UploadField('Image', 'Profile Image'));
|
||||||
$upload->setAllowedFileCategories('image/supported');
|
$upload->setAllowedFileCategories('image/supported');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -221,14 +221,14 @@ which allows an Extension to modify the results.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function Foo()
|
public function Foo()
|
||||||
{
|
{
|
||||||
$foo = // ..
|
$foo = // ..
|
||||||
|
|
||||||
$this->extend('updateFoo', $foo);
|
$this->extend('updateFoo', $foo);
|
||||||
|
|
||||||
return $foo;
|
return $foo;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The convention for extension hooks is to provide an `update{$Function}` hook at the end before you return the result. If
|
The convention for extension hooks is to provide an `update{$Function}` hook at the end before you return the result. If
|
||||||
@ -241,17 +241,17 @@ In your [Extension](api:SilverStripe\Core\Extension) class you can only refer to
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataExtension;
|
use SilverStripe\ORM\DataExtension;
|
||||||
|
|
||||||
class MyMemberExtension extends DataExtension
|
class MyMemberExtension extends DataExtension
|
||||||
{
|
{
|
||||||
|
|
||||||
public function updateFoo($foo)
|
public function updateFoo($foo)
|
||||||
{
|
{
|
||||||
// outputs the original class
|
// outputs the original class
|
||||||
var_dump($this->owner);
|
var_dump($this->owner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Checking to see if an Object has an Extension
|
## Checking to see if an Object has an Extension
|
||||||
@ -259,13 +259,13 @@ In your [Extension](api:SilverStripe\Core\Extension) class you can only refer to
|
|||||||
To see what extensions are currently enabled on an object, use the [getExtensionInstances()](api:SilverStripe\Core\Extensible::getExtensionInstances()) and
|
To see what extensions are currently enabled on an object, use the [getExtensionInstances()](api:SilverStripe\Core\Extensible::getExtensionInstances()) and
|
||||||
[hasExtension()](api:SilverStripe\Core\Extensible::hasExtension()) methods of the [Extensible](api:SilverStripe\Core\Extensible) trait.
|
[hasExtension()](api:SilverStripe\Core\Extensible::hasExtension()) methods of the [Extensible](api:SilverStripe\Core\Extensible) trait.
|
||||||
```php
|
```php
|
||||||
$member = Security::getCurrentUser();
|
$member = Security::getCurrentUser();
|
||||||
|
|
||||||
print_r($member->getExtensionInstances());
|
print_r($member->getExtensionInstances());
|
||||||
|
|
||||||
if($member->hasExtension('MyCustomMemberExtension')) {
|
if($member->hasExtension('MyCustomMemberExtension')) {
|
||||||
// ..
|
// ..
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Extension injection points
|
## Extension injection points
|
||||||
@ -286,17 +286,17 @@ if not specified in `self::$defaults`, but before extensions have been called:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
function __construct() {
|
function __construct() {
|
||||||
$self = $this;
|
$self = $this;
|
||||||
|
|
||||||
$this->beforeExtending('populateDefaults', function() use ($self) {
|
$this->beforeExtending('populateDefaults', function() use ($self) {
|
||||||
if(empty($self->MyField)) {
|
if(empty($self->MyField)) {
|
||||||
$self->MyField = 'Value we want as a default if not specified in $defaults, but set before extensions';
|
$self->MyField = 'Value we want as a default if not specified in $defaults, but set before extensions';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Example 2: User code can intervene in the process of extending cms fields.
|
Example 2: User code can intervene in the process of extending cms fields.
|
||||||
@ -307,18 +307,18 @@ This method is preferred to disabling, enabling, and calling field extensions ma
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function getCMSFields()
|
public function getCMSFields()
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->beforeUpdateCMSFields(function($fields) {
|
$this->beforeUpdateCMSFields(function($fields) {
|
||||||
// Include field which must be present when updateCMSFields is called on extensions
|
// Include field which must be present when updateCMSFields is called on extensions
|
||||||
$fields->addFieldToTab("Root.Main", new TextField('Detail', 'Details', null, 255));
|
$fields->addFieldToTab("Root.Main", new TextField('Detail', 'Details', null, 255));
|
||||||
});
|
});
|
||||||
|
|
||||||
$fields = parent::getCMSFields();
|
$fields = parent::getCMSFields();
|
||||||
// ... additional fields here
|
// ... additional fields here
|
||||||
return $fields;
|
return $fields;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Related Documentaion
|
## Related Documentaion
|
||||||
|
@ -12,22 +12,22 @@ in their WYSIWYG editor. Shortcodes are a semi-technical solution for this. A go
|
|||||||
viewer or a Google Map at a certain location.
|
viewer or a Google Map at a certain location.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$text = "<h1>My Map</h1>[map]"
|
$text = "<h1>My Map</h1>[map]"
|
||||||
|
|
||||||
// Will output
|
// Will output
|
||||||
// <h1>My Map</h1><iframe ..></iframe>
|
// <h1>My Map</h1><iframe ..></iframe>
|
||||||
```
|
```
|
||||||
|
|
||||||
Here's some syntax variations:
|
Here's some syntax variations:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
[my_shortcode]
|
[my_shortcode]
|
||||||
#
|
#
|
||||||
[my_shortcode /]
|
[my_shortcode /]
|
||||||
#
|
#
|
||||||
[my_shortcode,myparameter="value"]
|
[my_shortcode,myparameter="value"]
|
||||||
#
|
#
|
||||||
[my_shortcode,myparameter="value"]Enclosed Content[/my_shortcode]
|
[my_shortcode,myparameter="value"]Enclosed Content[/my_shortcode]
|
||||||
```
|
```
|
||||||
|
|
||||||
Shortcodes are automatically parsed on any database field which is declared as [HTMLValue](api:SilverStripe\View\Parsers\HTMLValue) or [DBHTMLText](api:SilverStripe\ORM\FieldType\DBHTMLText),
|
Shortcodes are automatically parsed on any database field which is declared as [HTMLValue](api:SilverStripe\View\Parsers\HTMLValue) or [DBHTMLText](api:SilverStripe\ORM\FieldType\DBHTMLText),
|
||||||
@ -38,8 +38,8 @@ Other fields can be manually parsed with shortcodes through the `parse` method.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$text = "My awesome [my_shortcode] is here.";
|
$text = "My awesome [my_shortcode] is here.";
|
||||||
ShortcodeParser::get_active()->parse($text);
|
ShortcodeParser::get_active()->parse($text);
|
||||||
```
|
```
|
||||||
|
|
||||||
## Defining Custom Shortcodes
|
## Defining Custom Shortcodes
|
||||||
@ -50,20 +50,20 @@ First we need to define a callback for the shortcode.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
|
||||||
class Page extends SiteTree
|
class Page extends SiteTree
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $casting = [
|
private static $casting = [
|
||||||
'MyShortCodeMethod' => 'HTMLText'
|
'MyShortCodeMethod' => 'HTMLText'
|
||||||
];
|
];
|
||||||
|
|
||||||
public static function MyShortCodeMethod($arguments, $content = null, $parser = null, $tagName)
|
public static function MyShortCodeMethod($arguments, $content = null, $parser = null, $tagName)
|
||||||
{
|
{
|
||||||
return "<em>" . $tagName . "</em> " . $content . "; " . count($arguments) . " arguments.";
|
return "<em>" . $tagName . "</em> " . $content . "; " . count($arguments) . " arguments.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -85,9 +85,9 @@ To register a shortcode you call the following.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// ShortcodeParser::get('default')->register($shortcode, $callback);
|
// ShortcodeParser::get('default')->register($shortcode, $callback);
|
||||||
|
|
||||||
ShortcodeParser::get('default')->register('my_shortcode', ['Page', 'MyShortCodeMethod']);
|
ShortcodeParser::get('default')->register('my_shortcode', ['Page', 'MyShortCodeMethod']);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -103,14 +103,14 @@ shortcode, which takes an `id` parameter.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
<a href="[sitetree_link,id=99]">
|
<a href="[sitetree_link,id=99]">
|
||||||
```
|
```
|
||||||
|
|
||||||
Links to internal `File` database records work exactly the same, but with the `[file_link]` shortcode.
|
Links to internal `File` database records work exactly the same, but with the `[file_link]` shortcode.
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
<a href="[file_link,id=99]">
|
<a href="[file_link,id=99]">
|
||||||
```
|
```
|
||||||
|
|
||||||
### Images
|
### Images
|
||||||
@ -148,30 +148,30 @@ The first is called "element scope" use, the second "attribute scope"
|
|||||||
You may not use shortcodes in any other location. Specifically, you can not use shortcodes to generate attributes or
|
You may not use shortcodes in any other location. Specifically, you can not use shortcodes to generate attributes or
|
||||||
change the name of a tag. These usages are forbidden:
|
change the name of a tag. These usages are forbidden:
|
||||||
```ss
|
```ss
|
||||||
<[paragraph]>Some test</[paragraph]>
|
<[paragraph]>Some test</[paragraph]>
|
||||||
|
|
||||||
<a [titleattribute]>link</a>
|
<a [titleattribute]>link</a>
|
||||||
```
|
```
|
||||||
You may need to escape text inside attributes `>` becomes `>`, You can include HTML tags inside a shortcode tag, but
|
You may need to escape text inside attributes `>` becomes `>`, You can include HTML tags inside a shortcode tag, but
|
||||||
you need to be careful of nesting to ensure you don't break the output.
|
you need to be careful of nesting to ensure you don't break the output.
|
||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<!-- Good -->
|
<!-- Good -->
|
||||||
<div>
|
<div>
|
||||||
[shortcode]
|
[shortcode]
|
||||||
<p>Caption</p>
|
<p>Caption</p>
|
||||||
[/shortcode]
|
[/shortcode]
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Bad: -->
|
<!-- Bad: -->
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
[shortcode]
|
[shortcode]
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
[/shortcode]
|
[/shortcode]
|
||||||
</p>
|
</p>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Location
|
### Location
|
||||||
@ -179,51 +179,51 @@ you need to be careful of nesting to ensure you don't break the output.
|
|||||||
Element scoped shortcodes have a special ability to move the location they are inserted at to comply with HTML lexical
|
Element scoped shortcodes have a special ability to move the location they are inserted at to comply with HTML lexical
|
||||||
rules. Take for example this basic paragraph tag:
|
rules. Take for example this basic paragraph tag:
|
||||||
```ss
|
```ss
|
||||||
<p><a href="#">Head [figure,src="assets/a.jpg",caption="caption"] Tail</a></p>
|
<p><a href="#">Head [figure,src="assets/a.jpg",caption="caption"] Tail</a></p>
|
||||||
```
|
```
|
||||||
When converted naively would become:
|
When converted naively would become:
|
||||||
```ss
|
```ss
|
||||||
<p><a href="#">Head <figure><img src="assets/a.jpg" /><figcaption>caption</figcaption></figure> Tail</a></p>
|
<p><a href="#">Head <figure><img src="assets/a.jpg" /><figcaption>caption</figcaption></figure> Tail</a></p>
|
||||||
```
|
```
|
||||||
However this is not valid HTML - P elements can not contain other block level elements.
|
However this is not valid HTML - P elements can not contain other block level elements.
|
||||||
|
|
||||||
To fix this you can specify a "location" attribute on a shortcode. When the location attribute is "left" or "right"
|
To fix this you can specify a "location" attribute on a shortcode. When the location attribute is "left" or "right"
|
||||||
the inserted content will be moved to immediately before the block tag. The result is this:
|
the inserted content will be moved to immediately before the block tag. The result is this:
|
||||||
```ss
|
```ss
|
||||||
<figure><img src="assets/a.jpg" /><figcaption>caption</figcaption></figure><p><a href="#">Head Tail</a></p>
|
<figure><img src="assets/a.jpg" /><figcaption>caption</figcaption></figure><p><a href="#">Head Tail</a></p>
|
||||||
```
|
```
|
||||||
When the location attribute is "leftAlone" or "center" then the DOM is split around the element. The result is this:
|
When the location attribute is "leftAlone" or "center" then the DOM is split around the element. The result is this:
|
||||||
```ss
|
```ss
|
||||||
<p><a href="#">Head </a></p><figure><img src="assets/a.jpg" /><figcaption>caption</figcaption></figure><p><a href="#"> Tail</a></p>
|
<p><a href="#">Head </a></p><figure><img src="assets/a.jpg" /><figcaption>caption</figcaption></figure><p><a href="#"> Tail</a></p>
|
||||||
```
|
```
|
||||||
### Parameter values
|
### Parameter values
|
||||||
|
|
||||||
Here is a summary of the callback parameter values based on some example shortcodes.
|
Here is a summary of the callback parameter values based on some example shortcodes.
|
||||||
```php
|
```php
|
||||||
public function MyCustomShortCode($arguments, $content = null, $parser = null, $tagName)
|
public function MyCustomShortCode($arguments, $content = null, $parser = null, $tagName)
|
||||||
{
|
{
|
||||||
// ..
|
// ..
|
||||||
}
|
}
|
||||||
|
|
||||||
[my_shortcode]
|
[my_shortcode]
|
||||||
$attributes => [];
|
$attributes => [];
|
||||||
$content => null;
|
$content => null;
|
||||||
$parser => ShortcodeParser instance,
|
$parser => ShortcodeParser instance,
|
||||||
$tagName => 'my_shortcode')
|
$tagName => 'my_shortcode')
|
||||||
|
|
||||||
[my_shortcode,attribute="foo",other="bar"]
|
[my_shortcode,attribute="foo",other="bar"]
|
||||||
|
|
||||||
$attributes => ['attribute' => 'foo', 'other' => 'bar']
|
$attributes => ['attribute' => 'foo', 'other' => 'bar']
|
||||||
$enclosedContent => null
|
$enclosedContent => null
|
||||||
$parser => ShortcodeParser instance
|
$parser => ShortcodeParser instance
|
||||||
$tagName => 'my_shortcode'
|
$tagName => 'my_shortcode'
|
||||||
|
|
||||||
[my_shortcode,attribute="foo"]content[/my_shortcode]
|
[my_shortcode,attribute="foo"]content[/my_shortcode]
|
||||||
|
|
||||||
$attributes => ['attribute' => 'foo']
|
$attributes => ['attribute' => 'foo']
|
||||||
$enclosedContent => 'content'
|
$enclosedContent => 'content'
|
||||||
$parser => ShortcodeParser instance
|
$parser => ShortcodeParser instance
|
||||||
$tagName => 'my_shortcode'
|
$tagName => 'my_shortcode'
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ The following sums up the simplest usage of the `Injector` it creates a new obje
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$object = Injector::inst()->create('MyClassName');
|
$object = Injector::inst()->create('MyClassName');
|
||||||
```
|
```
|
||||||
|
|
||||||
The benefit of constructing objects through this syntax is `ClassName` can be swapped out using the
|
The benefit of constructing objects through this syntax is `ClassName` can be swapped out using the
|
||||||
@ -30,21 +30,21 @@ The benefit of constructing objects through this syntax is `ClassName` can be sw
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
Injector:
|
Injector:
|
||||||
MyClassName:
|
MyClassName:
|
||||||
class: MyBetterClassName
|
class: MyBetterClassName
|
||||||
```
|
```
|
||||||
|
|
||||||
Repeated calls to `create()` create a new object each time.
|
Repeated calls to `create()` create a new object each time.
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$object = Injector::inst()->create('MyClassName');
|
$object = Injector::inst()->create('MyClassName');
|
||||||
$object2 = Injector::inst()->create('MyClassName');
|
$object2 = Injector::inst()->create('MyClassName');
|
||||||
|
|
||||||
echo $object !== $object2;
|
echo $object !== $object2;
|
||||||
|
|
||||||
// returns true;
|
// returns true;
|
||||||
```
|
```
|
||||||
|
|
||||||
## Singleton Pattern
|
## Singleton Pattern
|
||||||
@ -54,13 +54,13 @@ object instance as the first call.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// sets up MyClassName as a singleton
|
// sets up MyClassName as a singleton
|
||||||
$object = Injector::inst()->get('MyClassName');
|
$object = Injector::inst()->get('MyClassName');
|
||||||
$object2 = Injector::inst()->get('MyClassName');
|
$object2 = Injector::inst()->get('MyClassName');
|
||||||
|
|
||||||
echo ($object === $object2);
|
echo ($object === $object2);
|
||||||
|
|
||||||
// returns true;
|
// returns true;
|
||||||
```
|
```
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
@ -69,23 +69,23 @@ The `Injector` API can be used to define the types of `$dependencies` that an ob
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Control\Controller;
|
use SilverStripe\Control\Controller;
|
||||||
|
|
||||||
class MyController extends Controller
|
class MyController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
// both of these properties will be automatically
|
// both of these properties will be automatically
|
||||||
// set by the injector on object creation
|
// set by the injector on object creation
|
||||||
public $permissions;
|
public $permissions;
|
||||||
public $textProperty;
|
public $textProperty;
|
||||||
|
|
||||||
// we declare the types for each of the properties on the object. Anything we pass in via the Injector API must
|
// we declare the types for each of the properties on the object. Anything we pass in via the Injector API must
|
||||||
// match these data types.
|
// match these data types.
|
||||||
static $dependencies = [
|
static $dependencies = [
|
||||||
'textProperty' => 'a string value',
|
'textProperty' => 'a string value',
|
||||||
'permissions' => '%$PermissionService',
|
'permissions' => '%$PermissionService',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -93,13 +93,13 @@ When creating a new instance of `MyController` the dependencies on that class wi
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$object = Injector::inst()->get('MyController');
|
$object = Injector::inst()->get('MyController');
|
||||||
|
|
||||||
echo ($object->permissions instanceof PermissionService);
|
echo ($object->permissions instanceof PermissionService);
|
||||||
// returns true;
|
// returns true;
|
||||||
|
|
||||||
echo (is_string($object->textProperty));
|
echo (is_string($object->textProperty));
|
||||||
// returns true;
|
// returns true;
|
||||||
```
|
```
|
||||||
|
|
||||||
The [Configuration YAML](../configuration) does the hard work of configuring those `$dependencies` for us.
|
The [Configuration YAML](../configuration) does the hard work of configuring those `$dependencies` for us.
|
||||||
@ -108,25 +108,25 @@ The [Configuration YAML](../configuration) does the hard work of configuring tho
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
Injector:
|
Injector:
|
||||||
PermissionService:
|
PermissionService:
|
||||||
class: MyCustomPermissionService
|
class: MyCustomPermissionService
|
||||||
MyController
|
MyController
|
||||||
properties:
|
properties:
|
||||||
textProperty: 'My Text Value'
|
textProperty: 'My Text Value'
|
||||||
```
|
```
|
||||||
|
|
||||||
Now the dependencies will be replaced with our configuration.
|
Now the dependencies will be replaced with our configuration.
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$object = Injector::inst()->get('MyController');
|
$object = Injector::inst()->get('MyController');
|
||||||
|
|
||||||
echo ($object->permissions instanceof MyCustomPermissionService);
|
echo ($object->permissions instanceof MyCustomPermissionService);
|
||||||
// returns true;
|
// returns true;
|
||||||
|
|
||||||
echo ($object->textProperty == 'My Text Value');
|
echo ($object->textProperty == 'My Text Value');
|
||||||
// returns true;
|
// returns true;
|
||||||
```
|
```
|
||||||
|
|
||||||
As well as properties, method calls can also be specified:
|
As well as properties, method calls can also be specified:
|
||||||
@ -134,11 +134,11 @@ As well as properties, method calls can also be specified:
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
Injector:
|
Injector:
|
||||||
Logger:
|
Logger:
|
||||||
class: Monolog\Logger
|
class: Monolog\Logger
|
||||||
calls:
|
calls:
|
||||||
- [ pushHandler, [ %$DefaultHandler ] ]
|
- [ pushHandler, [ %$DefaultHandler ] ]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Using constants as variables
|
## Using constants as variables
|
||||||
@ -169,26 +169,26 @@ An example using the `MyFactory` service to create instances of the `MyService`
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
Injector:
|
Injector:
|
||||||
MyService:
|
MyService:
|
||||||
factory: MyFactory
|
factory: MyFactory
|
||||||
```
|
```
|
||||||
|
|
||||||
**mysite/code/MyFactory.php**
|
**mysite/code/MyFactory.php**
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
class MyFactory implements SilverStripe\Core\Injector\Factory
|
class MyFactory implements SilverStripe\Core\Injector\Factory
|
||||||
{
|
{
|
||||||
|
|
||||||
public function create($service, array $params = [])
|
public function create($service, array $params = [])
|
||||||
{
|
{
|
||||||
return new MyServiceImplementation();
|
return new MyServiceImplementation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Will use MyFactoryImplementation::create() to create the service instance.
|
// Will use MyFactoryImplementation::create() to create the service instance.
|
||||||
$instance = Injector::inst()->get('MyService');
|
$instance = Injector::inst()->get('MyService');
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -198,10 +198,10 @@ To override the `$dependency` declaration for a class, define the following conf
|
|||||||
|
|
||||||
**mysite/_config/app.yml**
|
**mysite/_config/app.yml**
|
||||||
```yml
|
```yml
|
||||||
MyController:
|
MyController:
|
||||||
dependencies:
|
dependencies:
|
||||||
textProperty: a string value
|
textProperty: a string value
|
||||||
permissions: %$PermissionService
|
permissions: %$PermissionService
|
||||||
```
|
```
|
||||||
## Managed objects
|
## Managed objects
|
||||||
|
|
||||||
@ -215,26 +215,26 @@ Assuming a class structure such as
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
class RestrictivePermissionService
|
class RestrictivePermissionService
|
||||||
{
|
{
|
||||||
private $database;
|
private $database;
|
||||||
|
|
||||||
public function setDatabase($d)
|
public function setDatabase($d)
|
||||||
{
|
{
|
||||||
$this->database = $d;
|
$this->database = $d;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class MySQLDatabase
|
class MySQLDatabase
|
||||||
{
|
{
|
||||||
private $username;
|
private $username;
|
||||||
private $password;
|
private $password;
|
||||||
|
|
||||||
public function __construct($username, $password)
|
public function __construct($username, $password)
|
||||||
{
|
{
|
||||||
$this->username = $username;
|
$this->username = $username;
|
||||||
$this->password = $password;
|
$this->password = $password;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
And the following configuration..
|
And the following configuration..
|
||||||
@ -242,28 +242,28 @@ And the following configuration..
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
name: MyController
|
name: MyController
|
||||||
---
|
---
|
||||||
MyController:
|
MyController:
|
||||||
dependencies:
|
dependencies:
|
||||||
permissions: %$PermissionService
|
permissions: %$PermissionService
|
||||||
Injector:
|
Injector:
|
||||||
PermissionService:
|
PermissionService:
|
||||||
class: RestrictivePermissionService
|
class: RestrictivePermissionService
|
||||||
properties:
|
properties:
|
||||||
database: %$MySQLDatabase
|
database: %$MySQLDatabase
|
||||||
MySQLDatabase
|
MySQLDatabase
|
||||||
constructor:
|
constructor:
|
||||||
0: 'dbusername'
|
0: 'dbusername'
|
||||||
1: 'dbpassword'
|
1: 'dbpassword'
|
||||||
```
|
```
|
||||||
|
|
||||||
Calling..
|
Calling..
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// sets up ClassName as a singleton
|
// sets up ClassName as a singleton
|
||||||
$controller = Injector::inst()->get('MyController');
|
$controller = Injector::inst()->get('MyController');
|
||||||
```
|
```
|
||||||
|
|
||||||
Would setup the following
|
Would setup the following
|
||||||
@ -283,12 +283,12 @@ Thus if you want an object to have the injected dependencies of a service of ano
|
|||||||
assign a reference to that service.
|
assign a reference to that service.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
Injector:
|
Injector:
|
||||||
JSONServiceDefinition:
|
JSONServiceDefinition:
|
||||||
class: JSONServiceImplementor
|
class: JSONServiceImplementor
|
||||||
properties:
|
properties:
|
||||||
Serialiser: JSONSerialiser
|
Serialiser: JSONSerialiser
|
||||||
GZIPJSONProvider: %$JSONServiceDefinition
|
GZIPJSONProvider: %$JSONServiceDefinition
|
||||||
```
|
```
|
||||||
|
|
||||||
`Injector::inst()->get('GZIPJSONProvider')` will then be an instance of `JSONServiceImplementor` with the injected
|
`Injector::inst()->get('GZIPJSONProvider')` will then be an instance of `JSONServiceImplementor` with the injected
|
||||||
@ -300,11 +300,11 @@ If class is not specified, then the class will be inherited from the outer servi
|
|||||||
For example with this config:
|
For example with this config:
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
Injector:
|
Injector:
|
||||||
Connector:
|
Connector:
|
||||||
properties:
|
properties:
|
||||||
AsString: true
|
AsString: true
|
||||||
ServiceConnector: %$Connector
|
ServiceConnector: %$Connector
|
||||||
```
|
```
|
||||||
|
|
||||||
Both `Connector` and `ServiceConnector` will have the `AsString` property set to true, but the resulting
|
Both `Connector` and `ServiceConnector` will have the `AsString` property set to true, but the resulting
|
||||||
@ -319,18 +319,18 @@ This is useful when writing test cases, as certain services may be necessary to
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// Setup default service
|
// Setup default service
|
||||||
Injector::inst()->registerService(new LiveService(), 'ServiceName');
|
Injector::inst()->registerService(new LiveService(), 'ServiceName');
|
||||||
|
|
||||||
// Test substitute service temporarily
|
// Test substitute service temporarily
|
||||||
Injector::nest();
|
Injector::nest();
|
||||||
|
|
||||||
Injector::inst()->registerService(new TestingService(), 'ServiceName');
|
Injector::inst()->registerService(new TestingService(), 'ServiceName');
|
||||||
$service = Injector::inst()->get('ServiceName');
|
$service = Injector::inst()->get('ServiceName');
|
||||||
// ... do something with $service
|
// ... do something with $service
|
||||||
|
|
||||||
// revert changes
|
// revert changes
|
||||||
Injector::unnest();
|
Injector::unnest();
|
||||||
```
|
```
|
||||||
|
|
||||||
## API Documentation
|
## API Documentation
|
||||||
|
@ -47,31 +47,31 @@ used.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
class MySQLWriteDbAspect implements BeforeCallAspect
|
class MySQLWriteDbAspect implements BeforeCallAspect
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var MySQLDatabase
|
* @var MySQLDatabase
|
||||||
*/
|
*/
|
||||||
public $writeDb;
|
public $writeDb;
|
||||||
|
|
||||||
public $writeQueries = [
|
public $writeQueries = [
|
||||||
'insert','update','delete','replace'
|
'insert','update','delete','replace'
|
||||||
];
|
];
|
||||||
|
|
||||||
public function beforeCall($proxied, $method, $args, &$alternateReturn)
|
public function beforeCall($proxied, $method, $args, &$alternateReturn)
|
||||||
{
|
{
|
||||||
if (isset($args[0])) {
|
if (isset($args[0])) {
|
||||||
$sql = $args[0];
|
$sql = $args[0];
|
||||||
$code = isset($args[1]) ? $args[1] : E_USER_ERROR;
|
$code = isset($args[1]) ? $args[1] : E_USER_ERROR;
|
||||||
|
|
||||||
if (in_array(strtolower(substr($sql,0,strpos($sql,' '))), $this->writeQueries)) {
|
if (in_array(strtolower(substr($sql,0,strpos($sql,' '))), $this->writeQueries)) {
|
||||||
$alternateReturn = $this->writeDb->query($sql, $code);
|
$alternateReturn = $this->writeDb->query($sql, $code);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -83,14 +83,14 @@ object that's made use of above.
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
WriteMySQLDatabase:
|
WriteMySQLDatabase:
|
||||||
class: MySQLDatabase
|
class: MySQLDatabase
|
||||||
constructor:
|
constructor:
|
||||||
- type: MySQLDatabase
|
- type: MySQLDatabase
|
||||||
server: write.hostname.db
|
server: write.hostname.db
|
||||||
username: user
|
username: user
|
||||||
password: pass
|
password: pass
|
||||||
database: write_database
|
database: write_database
|
||||||
```
|
```
|
||||||
|
|
||||||
This means that whenever something asks the [Injector](api:SilverStripe\Core\Injector\Injector) for the `WriteMySQLDatabase` object, it'll receive an object
|
This means that whenever something asks the [Injector](api:SilverStripe\Core\Injector\Injector) for the `WriteMySQLDatabase` object, it'll receive an object
|
||||||
@ -103,9 +103,9 @@ Next, this should be bound into an instance of the `Aspect` class
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
MySQLWriteDbAspect:
|
MySQLWriteDbAspect:
|
||||||
properties:
|
properties:
|
||||||
writeDb: %$WriteMySQLDatabase
|
writeDb: %$WriteMySQLDatabase
|
||||||
```
|
```
|
||||||
|
|
||||||
Next, we need to define the database connection that will be used for all non-write queries
|
Next, we need to define the database connection that will be used for all non-write queries
|
||||||
@ -113,14 +113,14 @@ Next, we need to define the database connection that will be used for all non-wr
|
|||||||
**mysite/_config/app.yml**
|
**mysite/_config/app.yml**
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
ReadMySQLDatabase:
|
ReadMySQLDatabase:
|
||||||
class: MySQLDatabase
|
class: MySQLDatabase
|
||||||
constructor:
|
constructor:
|
||||||
- type: MySQLDatabase
|
- type: MySQLDatabase
|
||||||
server: slavecluster.hostname.db
|
server: slavecluster.hostname.db
|
||||||
username: user
|
username: user
|
||||||
password: pass
|
password: pass
|
||||||
database: read_database
|
database: read_database
|
||||||
```
|
```
|
||||||
|
|
||||||
The final piece that ties everything together is the [AopProxyService](api:SilverStripe\Core\Injector\AopProxyService) instance that will be used as the replacement
|
The final piece that ties everything together is the [AopProxyService](api:SilverStripe\Core\Injector\AopProxyService) instance that will be used as the replacement
|
||||||
@ -129,13 +129,13 @@ object when the framework creates the database connection.
|
|||||||
**mysite/_config/app.yml**
|
**mysite/_config/app.yml**
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
MySQLDatabase:
|
MySQLDatabase:
|
||||||
class: AopProxyService
|
class: AopProxyService
|
||||||
properties:
|
properties:
|
||||||
proxied: %$ReadMySQLDatabase
|
proxied: %$ReadMySQLDatabase
|
||||||
beforeCall:
|
beforeCall:
|
||||||
query:
|
query:
|
||||||
- %$MySQLWriteDbAspect
|
- %$MySQLWriteDbAspect
|
||||||
```
|
```
|
||||||
|
|
||||||
The two important parts here are in the `properties` declared for the object.
|
The two important parts here are in the `properties` declared for the object.
|
||||||
@ -149,33 +149,33 @@ Overall configuration for this would look as follows
|
|||||||
**mysite/_config/app.yml**
|
**mysite/_config/app.yml**
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
Injector:
|
Injector:
|
||||||
ReadMySQLDatabase:
|
ReadMySQLDatabase:
|
||||||
class: MySQLDatabase
|
class: MySQLDatabase
|
||||||
constructor:
|
constructor:
|
||||||
- type: MySQLDatabase
|
- type: MySQLDatabase
|
||||||
server: slavecluster.hostname.db
|
server: slavecluster.hostname.db
|
||||||
username: user
|
username: user
|
||||||
password: pass
|
password: pass
|
||||||
database: read_database
|
database: read_database
|
||||||
MySQLWriteDbAspect:
|
MySQLWriteDbAspect:
|
||||||
properties:
|
properties:
|
||||||
writeDb: %$WriteMySQLDatabase
|
writeDb: %$WriteMySQLDatabase
|
||||||
WriteMySQLDatabase:
|
WriteMySQLDatabase:
|
||||||
class: MySQLDatabase
|
class: MySQLDatabase
|
||||||
constructor:
|
constructor:
|
||||||
- type: MySQLDatabase
|
- type: MySQLDatabase
|
||||||
server: write.hostname.db
|
server: write.hostname.db
|
||||||
username: user
|
username: user
|
||||||
password: pass
|
password: pass
|
||||||
database: write_database
|
database: write_database
|
||||||
MySQLDatabase:
|
MySQLDatabase:
|
||||||
class: AopProxyService
|
class: AopProxyService
|
||||||
properties:
|
properties:
|
||||||
proxied: %$ReadMySQLDatabase
|
proxied: %$ReadMySQLDatabase
|
||||||
beforeCall:
|
beforeCall:
|
||||||
query:
|
query:
|
||||||
- %$MySQLWriteDbAspect
|
- %$MySQLWriteDbAspect
|
||||||
```
|
```
|
||||||
|
|
||||||
## Changing what a method returns
|
## Changing what a method returns
|
||||||
@ -184,9 +184,9 @@ One major feature of an `Aspect` is the ability to modify what is returned from
|
|||||||
As seen in the above example, the `beforeCall` method modifies the `&$alternateReturn` variable, and returns `false`
|
As seen in the above example, the `beforeCall` method modifies the `&$alternateReturn` variable, and returns `false`
|
||||||
after doing so.
|
after doing so.
|
||||||
```php
|
```php
|
||||||
$alternateReturn = $this->writeDb->query($sql, $code);
|
$alternateReturn = $this->writeDb->query($sql, $code);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
```
|
```
|
||||||
|
|
||||||
By returning `false` from the `beforeCall()` method, the wrapping proxy class will_not_ call any additional `beforeCall`
|
By returning `false` from the `beforeCall()` method, the wrapping proxy class will_not_ call any additional `beforeCall`
|
||||||
|
@ -19,31 +19,31 @@ this:
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "your-vendor-name/module-name",
|
"name": "your-vendor-name/module-name",
|
||||||
"description": "One-liner describing your module",
|
"description": "One-liner describing your module",
|
||||||
"type": "silverstripe-module",
|
"type": "silverstripe-module",
|
||||||
"homepage": "http://github.com/your-vendor-name/module-name",
|
"homepage": "http://github.com/your-vendor-name/module-name",
|
||||||
"keywords": ["silverstripe", "some-tag", "some-other-tag"],
|
"keywords": ["silverstripe", "some-tag", "some-other-tag"],
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"authors": [
|
"authors": [
|
||||||
{"name": "Your Name","email": "your@email.com"}
|
{"name": "Your Name","email": "your@email.com"}
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "http://github.com/your-vendor-name/module-name/issues"
|
"issues": "http://github.com/your-vendor-name/module-name/issues"
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"silverstripe/cms": "~3.1",
|
"silverstripe/cms": "~3.1",
|
||||||
"silverstripe/framework": "~3.1"
|
"silverstripe/framework": "~3.1"
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"installer-name": "module-name",
|
"installer-name": "module-name",
|
||||||
"screenshots": [
|
"screenshots": [
|
||||||
"relative/path/screenshot1.png",
|
"relative/path/screenshot1.png",
|
||||||
"http://myhost.com/screenshot2.png"
|
"http://myhost.com/screenshot2.png"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Once your module is published online with a service like Github.com or Bitbucket.com, submit the repository to
|
Once your module is published online with a service like Github.com or Bitbucket.com, submit the repository to
|
||||||
|
@ -7,7 +7,7 @@ address. We want our CMS authors to be able to embed the map using the following
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
[googlemap,width=500,height=300]97-99 Courtenay Place, Wellington, New Zealand[/googlemap]
|
[googlemap,width=500,height=300]97-99 Courtenay Place, Wellington, New Zealand[/googlemap]
|
||||||
```
|
```
|
||||||
|
|
||||||
So we've got the address as "content" of our new `googlemap` shortcode tags, plus some `width` and `height` arguments.
|
So we've got the address as "content" of our new `googlemap` shortcode tags, plus some `width` and `height` arguments.
|
||||||
@ -15,22 +15,22 @@ We'll add defaults to those in our shortcode parser so they're optional.
|
|||||||
|
|
||||||
**mysite/_config.php**
|
**mysite/_config.php**
|
||||||
|
|
||||||
```php
|
```php
|
||||||
ShortcodeParser::get('default')->register('googlemap', function($arguments, $address, $parser, $shortcode) {
|
ShortcodeParser::get('default')->register('googlemap', function($arguments, $address, $parser, $shortcode) {
|
||||||
$iframeUrl = sprintf(
|
$iframeUrl = sprintf(
|
||||||
'http://maps.google.com/maps?q=%s&hnear=%s&ie=UTF8&hq=&t=m&z=14&output=embed',
|
'http://maps.google.com/maps?q=%s&hnear=%s&ie=UTF8&hq=&t=m&z=14&output=embed',
|
||||||
urlencode($address),
|
urlencode($address),
|
||||||
urlencode($address)
|
urlencode($address)
|
||||||
);
|
);
|
||||||
|
|
||||||
$width = (isset($arguments['width']) && $arguments['width']) ? $arguments['width'] : 400;
|
$width = (isset($arguments['width']) && $arguments['width']) ? $arguments['width'] : 400;
|
||||||
$height = (isset($arguments['height']) && $arguments['height']) ? $arguments['height'] : 300;
|
$height = (isset($arguments['height']) && $arguments['height']) ? $arguments['height'] : 300;
|
||||||
|
|
||||||
return sprintf(
|
return sprintf(
|
||||||
'<iframe width="%d" height="%d" src="%s" frameborder="0" scrolling="no" marginheight="0" marginwidth="0"></iframe>',
|
'<iframe width="%d" height="%d" src="%s" frameborder="0" scrolling="no" marginheight="0" marginwidth="0"></iframe>',
|
||||||
$width,
|
$width,
|
||||||
$height,
|
$height,
|
||||||
$iframeUrl
|
$iframeUrl
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
```
|
```
|
@ -11,55 +11,55 @@ explicitly logging in or by invoking the "remember me" functionality.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Forms\ReadonlyField;
|
use SilverStripe\Forms\ReadonlyField;
|
||||||
use SilverStripe\Security\Security;
|
use SilverStripe\Security\Security;
|
||||||
use SilverStripe\ORM\DB;
|
use SilverStripe\ORM\DB;
|
||||||
use SilverStripe\ORM\DataExtension;
|
use SilverStripe\ORM\DataExtension;
|
||||||
|
|
||||||
class MyMemberExtension extends DataExtension
|
class MyMemberExtension extends DataExtension
|
||||||
{
|
{
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'LastVisited' => 'Datetime',
|
'LastVisited' => 'Datetime',
|
||||||
'NumVisit' => 'Int',
|
'NumVisit' => 'Int',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function memberLoggedIn()
|
public function memberLoggedIn()
|
||||||
{
|
{
|
||||||
$this->logVisit();
|
$this->logVisit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function memberAutoLoggedIn()
|
public function memberAutoLoggedIn()
|
||||||
{
|
{
|
||||||
$this->logVisit();
|
$this->logVisit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updateCMSFields(FieldList $fields)
|
public function updateCMSFields(FieldList $fields)
|
||||||
{
|
{
|
||||||
$fields->addFieldsToTab('Root.Main', [
|
$fields->addFieldsToTab('Root.Main', [
|
||||||
ReadonlyField::create('LastVisited', 'Last visited'),
|
ReadonlyField::create('LastVisited', 'Last visited'),
|
||||||
ReadonlyField::create('NumVisit', 'Number of visits')
|
ReadonlyField::create('NumVisit', 'Number of visits')
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function logVisit()
|
protected function logVisit()
|
||||||
{
|
{
|
||||||
if(!Security::database_is_ready()) return;
|
if(!Security::database_is_ready()) return;
|
||||||
|
|
||||||
DB::query(sprintf(
|
DB::query(sprintf(
|
||||||
'UPDATE "Member" SET "LastVisited" = %s, "NumVisit" = "NumVisit" + 1 WHERE "ID" = %d',
|
'UPDATE "Member" SET "LastVisited" = %s, "NumVisit" = "NumVisit" + 1 WHERE "ID" = %d',
|
||||||
DB::get_conn()->now(),
|
DB::get_conn()->now(),
|
||||||
$this->owner->ID
|
$this->owner->ID
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Now you just need to apply this extension through your config:
|
Now you just need to apply this extension through your config:
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
Member:
|
Member:
|
||||||
extensions:
|
extensions:
|
||||||
- MyMemberExtension
|
- MyMemberExtension
|
||||||
|
|
||||||
```
|
```
|
@ -10,33 +10,33 @@ to ensure that it works as it should. A simple example would be to test the resu
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
|
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
|
||||||
class Page extends SiteTree
|
class Page extends SiteTree
|
||||||
{
|
{
|
||||||
public static function MyMethod()
|
public static function MyMethod()
|
||||||
{
|
{
|
||||||
return (1 + 1);
|
return (1 + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**mysite/tests/PageTest.php**
|
**mysite/tests/PageTest.php**
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
|
|
||||||
use Page;
|
use Page;
|
||||||
use SilverStripe\Dev\SapphireTest;
|
use SilverStripe\Dev\SapphireTest;
|
||||||
|
|
||||||
class PageTest extends SapphireTest
|
class PageTest extends SapphireTest
|
||||||
{
|
{
|
||||||
public function testMyMethod()
|
public function testMyMethod()
|
||||||
{
|
{
|
||||||
$this->assertEquals(2, Page::MyMethod());
|
$this->assertEquals(2, Page::MyMethod());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="info" markdown="1">
|
<div class="info" markdown="1">
|
||||||
@ -89,23 +89,23 @@ needs.
|
|||||||
|
|
||||||
```xml
|
```xml
|
||||||
|
|
||||||
<phpunit bootstrap="framework/tests/bootstrap.php" colors="true">
|
<phpunit bootstrap="framework/tests/bootstrap.php" colors="true">
|
||||||
<testsuite name="Default">
|
<testsuite name="Default">
|
||||||
<directory>mysite/tests</directory>
|
<directory>mysite/tests</directory>
|
||||||
<directory>cms/tests</directory>
|
<directory>cms/tests</directory>
|
||||||
<directory>framework/tests</directory>
|
<directory>framework/tests</directory>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
|
|
||||||
<listeners>
|
<listeners>
|
||||||
<listener class="SS_TestListener" file="framework/dev/TestListener.php" />
|
<listener class="SS_TestListener" file="framework/dev/TestListener.php" />
|
||||||
</listeners>
|
</listeners>
|
||||||
|
|
||||||
<groups>
|
<groups>
|
||||||
<exclude>
|
<exclude>
|
||||||
<group>sanitychecks</group>
|
<group>sanitychecks</group>
|
||||||
</exclude>
|
</exclude>
|
||||||
</groups>
|
</groups>
|
||||||
</phpunit>
|
</phpunit>
|
||||||
```
|
```
|
||||||
|
|
||||||
### setUp() and tearDown()
|
### setUp() and tearDown()
|
||||||
@ -116,37 +116,37 @@ end of each test.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
|
|
||||||
use SilverStripe\Core\Config\Config;
|
use SilverStripe\Core\Config\Config;
|
||||||
use SilverStripe\Dev\SapphireTest;
|
use SilverStripe\Dev\SapphireTest;
|
||||||
|
|
||||||
class PageTest extends SapphireTest
|
class PageTest extends SapphireTest
|
||||||
{
|
{
|
||||||
function setUp()
|
function setUp()
|
||||||
{
|
{
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
// create 100 pages
|
// create 100 pages
|
||||||
for ($i = 0; $i < 100; $i++) {
|
for ($i = 0; $i < 100; $i++) {
|
||||||
$page = new Page(['Title' => "Page $i"]);
|
$page = new Page(['Title' => "Page $i"]);
|
||||||
$page->write();
|
$page->write();
|
||||||
$page->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
$page->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// set custom configuration for the test.
|
// set custom configuration for the test.
|
||||||
Config::inst()->update('Foo', 'bar', 'Hello!');
|
Config::inst()->update('Foo', 'bar', 'Hello!');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testMyMethod()
|
public function testMyMethod()
|
||||||
{
|
{
|
||||||
// ..
|
// ..
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testMySecondMethod()
|
public function testMySecondMethod()
|
||||||
{
|
{
|
||||||
// ..
|
// ..
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -156,25 +156,25 @@ takes place.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
|
|
||||||
use SilverStripe\Dev\SapphireTest;
|
use SilverStripe\Dev\SapphireTest;
|
||||||
|
|
||||||
class PageTest extends SapphireTest
|
class PageTest extends SapphireTest
|
||||||
{
|
{
|
||||||
public static function setUpBeforeClass()
|
public static function setUpBeforeClass()
|
||||||
{
|
{
|
||||||
parent::setUpBeforeClass();
|
parent::setUpBeforeClass();
|
||||||
|
|
||||||
// ..
|
// ..
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function tearDownAfterClass()
|
public static function tearDownAfterClass()
|
||||||
{
|
{
|
||||||
parent::tearDownAfterClass();
|
parent::tearDownAfterClass();
|
||||||
|
|
||||||
// ..
|
// ..
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Config and Injector Nesting
|
### Config and Injector Nesting
|
||||||
@ -189,23 +189,23 @@ It's important to remember that the `parent::setUp();` functions will need to be
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public static function setUpBeforeClass()
|
public static function setUpBeforeClass()
|
||||||
{
|
{
|
||||||
parent::setUpBeforeClass();
|
parent::setUpBeforeClass();
|
||||||
//this will remain for the whole suite and be removed for any other tests
|
//this will remain for the whole suite and be removed for any other tests
|
||||||
Config::inst()->update('ClassName', 'var_name', 'var_value');
|
Config::inst()->update('ClassName', 'var_name', 'var_value');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFeatureDoesAsExpected()
|
public function testFeatureDoesAsExpected()
|
||||||
{
|
{
|
||||||
//this will be reset to 'var_value' at the end of this test function
|
//this will be reset to 'var_value' at the end of this test function
|
||||||
Config::inst()->update('ClassName', 'var_name', 'new_var_value');
|
Config::inst()->update('ClassName', 'var_name', 'new_var_value');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAnotherFeatureDoesAsExpected()
|
public function testAnotherFeatureDoesAsExpected()
|
||||||
{
|
{
|
||||||
Config::inst()->get('ClassName', 'var_name'); // this will be 'var_value'
|
Config::inst()->get('ClassName', 'var_name'); // this will be 'var_value'
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Related Documentation
|
## Related Documentation
|
||||||
|
@ -9,7 +9,7 @@ creating [HTTPRequest](api:SilverStripe\Control\HTTPRequest), receiving [HTTPRes
|
|||||||
|
|
||||||
## Get
|
## Get
|
||||||
```php
|
```php
|
||||||
$page = $this->get($url);
|
$page = $this->get($url);
|
||||||
```
|
```
|
||||||
|
|
||||||
Performs a GET request on $url and retrieves the [HTTPResponse](api:SilverStripe\Control\HTTPResponse). This also changes the current page to the value
|
Performs a GET request on $url and retrieves the [HTTPResponse](api:SilverStripe\Control\HTTPResponse). This also changes the current page to the value
|
||||||
@ -17,7 +17,7 @@ of the response.
|
|||||||
|
|
||||||
## Post
|
## Post
|
||||||
```php
|
```php
|
||||||
$page = $this->post($url);
|
$page = $this->post($url);
|
||||||
```
|
```
|
||||||
|
|
||||||
Performs a POST request on $url and retrieves the [HTTPResponse](api:SilverStripe\Control\HTTPResponse). This also changes the current page to the value
|
Performs a POST request on $url and retrieves the [HTTPResponse](api:SilverStripe\Control\HTTPResponse). This also changes the current page to the value
|
||||||
@ -27,7 +27,7 @@ of the response.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$submit = $this->submitForm($formID, $button = null, $data = []);
|
$submit = $this->submitForm($formID, $button = null, $data = []);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -37,14 +37,14 @@ Submits the given form (`#ContactForm`) on the current page and returns the [HTT
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$this->logInAs($member);
|
$this->logInAs($member);
|
||||||
```
|
```
|
||||||
|
|
||||||
Logs a given user in, sets the current session. To log all users out pass `null` to the method.
|
Logs a given user in, sets the current session. To log all users out pass `null` to the method.
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$this->logInAs(null);
|
$this->logInAs(null);
|
||||||
```
|
```
|
||||||
|
|
||||||
## Assertions
|
## Assertions
|
||||||
@ -55,9 +55,9 @@ The `FunctionalTest` class also provides additional asserts to validate your tes
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$this->assertPartialMatchBySelector('p.good',[
|
$this->assertPartialMatchBySelector('p.good',[
|
||||||
'Test save was successful'
|
'Test save was successful'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -70,9 +70,9 @@ assertion fails if one of the expectedMatches fails to appear.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$this->assertExactMatchBySelector("#MyForm_ID p.error", [
|
$this->assertExactMatchBySelector("#MyForm_ID p.error", [
|
||||||
"That email address is invalid."
|
"That email address is invalid."
|
||||||
]);
|
]);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -82,9 +82,9 @@ assertion fails if one of the expectedMatches fails to appear.
|
|||||||
|
|
||||||
### assertPartialHTMLMatchBySelector
|
### assertPartialHTMLMatchBySelector
|
||||||
```php
|
```php
|
||||||
$this->assertPartialHTMLMatchBySelector("#MyForm_ID p.error", [
|
$this->assertPartialHTMLMatchBySelector("#MyForm_ID p.error", [
|
||||||
"That email address is invalid."
|
"That email address is invalid."
|
||||||
]);
|
]);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -98,9 +98,9 @@ assertion fails if one of the expectedMatches fails to appear.
|
|||||||
|
|
||||||
### assertExactHTMLMatchBySelector
|
### assertExactHTMLMatchBySelector
|
||||||
```php
|
```php
|
||||||
$this->assertExactHTMLMatchBySelector("#MyForm_ID p.error", [
|
$this->assertExactHTMLMatchBySelector("#MyForm_ID p.error", [
|
||||||
"That email address is invalid."
|
"That email address is invalid."
|
||||||
]);
|
]);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -16,12 +16,12 @@ To include your fixture file in your tests, you should define it as your `$fixtu
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
class MyNewTest extends SapphireTest
|
class MyNewTest extends SapphireTest
|
||||||
{
|
{
|
||||||
|
|
||||||
protected static $fixture_file = 'fixtures.yml';
|
protected static $fixture_file = 'fixtures.yml';
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also use an array of fixture files, if you want to use parts of multiple other tests:
|
You can also use an array of fixture files, if you want to use parts of multiple other tests:
|
||||||
@ -31,15 +31,15 @@ You can also use an array of fixture files, if you want to use parts of multiple
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
class MyNewTest extends SapphireTest
|
class MyNewTest extends SapphireTest
|
||||||
{
|
{
|
||||||
|
|
||||||
protected static $fixture_file = [
|
protected static $fixture_file = [
|
||||||
'fixtures.yml',
|
'fixtures.yml',
|
||||||
'otherfixtures.yml'
|
'otherfixtures.yml'
|
||||||
];
|
];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -50,31 +50,31 @@ ideal for fixture generation. Say we have the following two DataObjects:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
|
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Player extends DataObject
|
class Player extends DataObject
|
||||||
{
|
{
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Name' => 'Varchar(255)'
|
'Name' => 'Varchar(255)'
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $has_one = [
|
private static $has_one = [
|
||||||
'Team' => 'Team'
|
'Team' => 'Team'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
class Team extends DataObject
|
class Team extends DataObject
|
||||||
{
|
{
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Name' => 'Varchar(255)',
|
'Name' => 'Varchar(255)',
|
||||||
'Origin' => 'Varchar(255)'
|
'Origin' => 'Varchar(255)'
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $has_many = [
|
private static $has_many = [
|
||||||
'Players' => 'Player'
|
'Players' => 'Player'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -85,23 +85,23 @@ We can represent multiple instances of them in `YAML` as follows:
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
Team:
|
Team:
|
||||||
hurricanes:
|
hurricanes:
|
||||||
Name: The Hurricanes
|
Name: The Hurricanes
|
||||||
Origin: Wellington
|
Origin: Wellington
|
||||||
crusaders:
|
crusaders:
|
||||||
Name: The Crusaders
|
Name: The Crusaders
|
||||||
Origin: Canterbury
|
Origin: Canterbury
|
||||||
Player:
|
Player:
|
||||||
john:
|
john:
|
||||||
Name: John
|
Name: John
|
||||||
Team: =>Team.hurricanes
|
Team: =>Team.hurricanes
|
||||||
joe:
|
joe:
|
||||||
Name: Joe
|
Name: Joe
|
||||||
Team: =>Team.crusaders
|
Team: =>Team.crusaders
|
||||||
jack:
|
jack:
|
||||||
Name: Jack
|
Name: Jack
|
||||||
Team: =>Team.crusaders
|
Team: =>Team.crusaders
|
||||||
```
|
```
|
||||||
|
|
||||||
This `YAML` is broken up into three levels, signified by the indentation of each line. In the first level of
|
This `YAML` is broken up into three levels, signified by the indentation of each line. In the first level of
|
||||||
@ -112,7 +112,7 @@ represents a new object and can be referenced in the PHP using `objFromFixture`
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$player = $this->objFromFixture('Player', 'jack');
|
$player = $this->objFromFixture('Player', 'jack');
|
||||||
```
|
```
|
||||||
|
|
||||||
The third and final level represents each individual object's fields.
|
The third and final level represents each individual object's fields.
|
||||||
@ -144,22 +144,22 @@ We can also declare the relationships conversely. Another way we could write the
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
Player:
|
Player:
|
||||||
john:
|
john:
|
||||||
Name: John
|
Name: John
|
||||||
joe:
|
joe:
|
||||||
Name: Joe
|
Name: Joe
|
||||||
jack:
|
jack:
|
||||||
Name: Jack
|
Name: Jack
|
||||||
Team:
|
Team:
|
||||||
hurricanes:
|
hurricanes:
|
||||||
Name: Hurricanes
|
Name: Hurricanes
|
||||||
Origin: Wellington
|
Origin: Wellington
|
||||||
Players: =>Player.john
|
Players: =>Player.john
|
||||||
crusaders:
|
crusaders:
|
||||||
Name: Crusaders
|
Name: Crusaders
|
||||||
Origin: Canterbury
|
Origin: Canterbury
|
||||||
Players: =>Player.joe,=>Player.jack
|
Players: =>Player.joe,=>Player.jack
|
||||||
```
|
```
|
||||||
|
|
||||||
The database is populated by instantiating `DataObject` objects and setting the fields declared in the `YAML`, then
|
The database is populated by instantiating `DataObject` objects and setting the fields declared in the `YAML`, then
|
||||||
@ -168,14 +168,14 @@ writing:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$team = new Team([
|
$team = new Team([
|
||||||
'Name' => 'Hurricanes',
|
'Name' => 'Hurricanes',
|
||||||
'Origin' => 'Wellington'
|
'Origin' => 'Wellington'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$team->write();
|
$team->write();
|
||||||
|
|
||||||
$team->Players()->add($john);
|
$team->Players()->add($john);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -215,33 +215,33 @@ declare the role each player has in the team.
|
|||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Player extends DataObject
|
class Player extends DataObject
|
||||||
{
|
{
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Name' => 'Varchar(255)'
|
'Name' => 'Varchar(255)'
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $belongs_many_many = [
|
private static $belongs_many_many = [
|
||||||
'Teams' => 'Team'
|
'Teams' => 'Team'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
class Team extends DataObject
|
class Team extends DataObject
|
||||||
{
|
{
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Name' => 'Varchar(255)'
|
'Name' => 'Varchar(255)'
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $many_many = [
|
private static $many_many = [
|
||||||
'Players' => 'Player'
|
'Players' => 'Player'
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $many_many_extraFields = [
|
private static $many_many_extraFields = [
|
||||||
'Players' => [
|
'Players' => [
|
||||||
'Role' => "Varchar"
|
'Role' => "Varchar"
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -250,27 +250,27 @@ To provide the value for the `many_many_extraField` use the YAML list syntax.
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
Player:
|
Player:
|
||||||
john:
|
john:
|
||||||
Name: John
|
Name: John
|
||||||
joe:
|
joe:
|
||||||
Name: Joe
|
Name: Joe
|
||||||
jack:
|
jack:
|
||||||
Name: Jack
|
Name: Jack
|
||||||
Team:
|
Team:
|
||||||
hurricanes:
|
hurricanes:
|
||||||
Name: The Hurricanes
|
Name: The Hurricanes
|
||||||
Players:
|
Players:
|
||||||
- =>Player.john:
|
- =>Player.john:
|
||||||
Role: Captain
|
Role: Captain
|
||||||
|
|
||||||
crusaders:
|
crusaders:
|
||||||
Name: The Crusaders
|
Name: The Crusaders
|
||||||
Players:
|
Players:
|
||||||
- =>Player.joe:
|
- =>Player.joe:
|
||||||
Role: Captain
|
Role: Captain
|
||||||
- =>Player.jack:
|
- =>Player.jack:
|
||||||
Role: Winger
|
Role: Winger
|
||||||
```
|
```
|
||||||
|
|
||||||
## Fixture Factories
|
## Fixture Factories
|
||||||
@ -293,18 +293,18 @@ using them.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$factory = Injector::inst()->create('FixtureFactory');
|
$factory = Injector::inst()->create('FixtureFactory');
|
||||||
|
|
||||||
$obj = $factory->createObject('Team', 'hurricanes');
|
$obj = $factory->createObject('Team', 'hurricanes');
|
||||||
```
|
```
|
||||||
|
|
||||||
In order to create an object with certain properties, just add a third argument:
|
In order to create an object with certain properties, just add a third argument:
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$obj = $factory->createObject('Team', 'hurricanes', [
|
$obj = $factory->createObject('Team', 'hurricanes', [
|
||||||
'Name' => 'My Value'
|
'Name' => 'My Value'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -317,7 +317,7 @@ After we've created this object in the factory, `getId` is used to retrieve it b
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$databaseId = $factory->getId('Team', 'hurricanes');
|
$databaseId = $factory->getId('Team', 'hurricanes');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Default Properties
|
### Default Properties
|
||||||
@ -327,9 +327,9 @@ name, we can set the default to be `Unknown Team`.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$factory->define('Team', [
|
$factory->define('Team', [
|
||||||
'Name' => 'Unknown Team'
|
'Name' => 'Unknown Team'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -340,16 +340,16 @@ values based on other fixture data.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$factory->define('Member', [
|
$factory->define('Member', [
|
||||||
'Email' => function($obj, $data, $fixtures) {
|
'Email' => function($obj, $data, $fixtures) {
|
||||||
if(isset($data['FirstName']) {
|
if(isset($data['FirstName']) {
|
||||||
$obj->Email = strtolower($data['FirstName']) . '@example.org';
|
$obj->Email = strtolower($data['FirstName']) . '@example.org';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'Score' => function($obj, $data, $fixtures) {
|
'Score' => function($obj, $data, $fixtures) {
|
||||||
$obj->Score = rand(0,10);
|
$obj->Score = rand(0,10);
|
||||||
}
|
}
|
||||||
)];
|
)];
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -360,9 +360,9 @@ Model relations can be expressed through the same notation as in the YAML fixtur
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$obj = $factory->createObject('Team', 'hurricanes', [
|
$obj = $factory->createObject('Team', 'hurricanes', [
|
||||||
'MyHasManyRelation' => '=>Player.john,=>Player.joe'
|
'MyHasManyRelation' => '=>Player.john,=>Player.joe'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -373,13 +373,13 @@ publish a page, which requires a method call.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$blueprint = Injector::inst()->create('FixtureBlueprint', 'Member');
|
$blueprint = Injector::inst()->create('FixtureBlueprint', 'Member');
|
||||||
|
|
||||||
$blueprint->addCallback('afterCreate', function($obj, $identifier, $data, $fixtures) {
|
$blueprint->addCallback('afterCreate', function($obj, $identifier, $data, $fixtures) {
|
||||||
$obj->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
$obj->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
||||||
});
|
});
|
||||||
|
|
||||||
$page = $factory->define('Page', $blueprint);
|
$page = $factory->define('Page', $blueprint);
|
||||||
```
|
```
|
||||||
|
|
||||||
Available callbacks:
|
Available callbacks:
|
||||||
@ -395,20 +395,20 @@ equal the class names they manage.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$memberBlueprint = Injector::inst()->create('FixtureBlueprint', 'Member', 'Member');
|
$memberBlueprint = Injector::inst()->create('FixtureBlueprint', 'Member', 'Member');
|
||||||
|
|
||||||
$adminBlueprint = Injector::inst()->create('FixtureBlueprint', 'AdminMember', 'Member');
|
$adminBlueprint = Injector::inst()->create('FixtureBlueprint', 'AdminMember', 'Member');
|
||||||
|
|
||||||
$adminBlueprint->addCallback('afterCreate', function($obj, $identifier, $data, $fixtures) {
|
$adminBlueprint->addCallback('afterCreate', function($obj, $identifier, $data, $fixtures) {
|
||||||
if(isset($fixtures['Group']['admin'])) {
|
if(isset($fixtures['Group']['admin'])) {
|
||||||
$adminGroup = Group::get()->byId($fixtures['Group']['admin']);
|
$adminGroup = Group::get()->byId($fixtures['Group']['admin']);
|
||||||
$obj->Groups()->add($adminGroup);
|
$obj->Groups()->add($adminGroup);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$member = $factory->createObject('Member'); // not in admin group
|
$member = $factory->createObject('Member'); // not in admin group
|
||||||
|
|
||||||
$admin = $factory->createObject('AdminMember'); // in admin group
|
$admin = $factory->createObject('AdminMember'); // in admin group
|
||||||
```
|
```
|
||||||
|
|
||||||
## Related Documentation
|
## Related Documentation
|
||||||
|
@ -9,41 +9,41 @@ how you can load default records into the test database.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
|
|
||||||
use SilverStripe\Dev\SapphireTest;
|
use SilverStripe\Dev\SapphireTest;
|
||||||
|
|
||||||
class PageTest extends SapphireTest
|
class PageTest extends SapphireTest
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Defines the fixture file to use for this test class
|
* Defines the fixture file to use for this test class
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected static $fixture_file = 'SiteTreeTest.yml';
|
protected static $fixture_file = 'SiteTreeTest.yml';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test generation of the URLSegment values.
|
* Test generation of the URLSegment values.
|
||||||
*
|
*
|
||||||
* Makes sure to:
|
* Makes sure to:
|
||||||
* - Turn things into lowercase-hyphen-format
|
* - Turn things into lowercase-hyphen-format
|
||||||
* - Generates from Title by default, unless URLSegment is explicitly set
|
* - Generates from Title by default, unless URLSegment is explicitly set
|
||||||
* - Resolves duplicates by appending a number
|
* - Resolves duplicates by appending a number
|
||||||
*/
|
*/
|
||||||
public function testURLGeneration()
|
public function testURLGeneration()
|
||||||
{
|
{
|
||||||
$expectedURLs = [
|
$expectedURLs = [
|
||||||
'home' => 'home',
|
'home' => 'home',
|
||||||
'staff' => 'my-staff',
|
'staff' => 'my-staff',
|
||||||
'about' => 'about-us',
|
'about' => 'about-us',
|
||||||
'staffduplicate' => 'my-staff-2'
|
'staffduplicate' => 'my-staff-2'
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach($expectedURLs as $fixture => $urlSegment) {
|
foreach($expectedURLs as $fixture => $urlSegment) {
|
||||||
$obj = $this->objFromFixture('Page', $fixture);
|
$obj = $this->objFromFixture('Page', $fixture);
|
||||||
|
|
||||||
$this->assertEquals($urlSegment, $obj->URLSegment);
|
$this->assertEquals($urlSegment, $obj->URLSegment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -11,43 +11,43 @@ response and modify the session within a test.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Security\Member;
|
use SilverStripe\Security\Member;
|
||||||
|
|
||||||
class HomePageTest extends FunctionalTest
|
class HomePageTest extends FunctionalTest
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test generation of the view
|
* Test generation of the view
|
||||||
*/
|
*/
|
||||||
public function testViewHomePage()
|
public function testViewHomePage()
|
||||||
{
|
{
|
||||||
$page = $this->get('home/');
|
$page = $this->get('home/');
|
||||||
|
|
||||||
// Home page should load..
|
// Home page should load..
|
||||||
$this->assertEquals(200, $page->getStatusCode());
|
$this->assertEquals(200, $page->getStatusCode());
|
||||||
|
|
||||||
// We should see a login form
|
// We should see a login form
|
||||||
$login = $this->submitForm("LoginFormID", null, [
|
$login = $this->submitForm("LoginFormID", null, [
|
||||||
'Email' => 'test@test.com',
|
'Email' => 'test@test.com',
|
||||||
'Password' => 'wrongpassword'
|
'Password' => 'wrongpassword'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// wrong details, should now see an error message
|
// wrong details, should now see an error message
|
||||||
$this->assertExactHTMLMatchBySelector("#LoginForm p.error", [
|
$this->assertExactHTMLMatchBySelector("#LoginForm p.error", [
|
||||||
"That email address is invalid."
|
"That email address is invalid."
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// If we login as a user we should see a welcome message
|
// If we login as a user we should see a welcome message
|
||||||
$me = Member::get()->first();
|
$me = Member::get()->first();
|
||||||
|
|
||||||
$this->logInAs($me);
|
$this->logInAs($me);
|
||||||
$page = $this->get('home/');
|
$page = $this->get('home/');
|
||||||
|
|
||||||
$this->assertExactHTMLMatchBySelector("#Welcome", [
|
$this->assertExactHTMLMatchBySelector("#Welcome", [
|
||||||
'Welcome Back'
|
'Welcome Back'
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -10,39 +10,39 @@ with information that we need.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Core\Injector\Injector;
|
use SilverStripe\Core\Injector\Injector;
|
||||||
|
|
||||||
class MyObjectTest extends SapphireTest
|
class MyObjectTest extends SapphireTest
|
||||||
{
|
{
|
||||||
|
|
||||||
protected $factory;
|
protected $factory;
|
||||||
|
|
||||||
function __construct() {
|
function __construct() {
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
|
|
||||||
$factory = Injector::inst()->create('FixtureFactory');
|
$factory = Injector::inst()->create('FixtureFactory');
|
||||||
|
|
||||||
// Defines a "blueprint" for new objects
|
// Defines a "blueprint" for new objects
|
||||||
$factory->define('MyObject', [
|
$factory->define('MyObject', [
|
||||||
'MyProperty' => 'My Default Value'
|
'MyProperty' => 'My Default Value'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->factory = $factory;
|
$this->factory = $factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
function testSomething() {
|
function testSomething() {
|
||||||
$MyObjectObj = $this->factory->createObject(
|
$MyObjectObj = $this->factory->createObject(
|
||||||
'MyObject',
|
'MyObject',
|
||||||
['MyOtherProperty' => 'My Custom Value']
|
['MyOtherProperty' => 'My Custom Value']
|
||||||
);
|
);
|
||||||
|
|
||||||
echo $MyObjectObj->MyProperty;
|
echo $MyObjectObj->MyProperty;
|
||||||
// returns "My Default Value"
|
// returns "My Default Value"
|
||||||
|
|
||||||
echo $myPageObj->MyOtherProperty;
|
echo $myPageObj->MyOtherProperty;
|
||||||
// returns "My Custom Value"
|
// returns "My Custom Value"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -8,24 +8,24 @@ email was sent using this method.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function MyMethod()
|
public function MyMethod()
|
||||||
{
|
{
|
||||||
$e = new Email();
|
$e = new Email();
|
||||||
$e->To = "someone@example.com";
|
$e->To = "someone@example.com";
|
||||||
$e->Subject = "Hi there";
|
$e->Subject = "Hi there";
|
||||||
$e->Body = "I just really wanted to email you and say hi.";
|
$e->Body = "I just really wanted to email you and say hi.";
|
||||||
$e->send();
|
$e->send();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
To test that `MyMethod` sends the correct email, use the [SapphireTest::assertEmailSent()](api:SilverStripe\Dev\SapphireTest::assertEmailSent()) method.
|
To test that `MyMethod` sends the correct email, use the [SapphireTest::assertEmailSent()](api:SilverStripe\Dev\SapphireTest::assertEmailSent()) method.
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$this->assertEmailSent($to, $from, $subject, $body);
|
$this->assertEmailSent($to, $from, $subject, $body);
|
||||||
|
|
||||||
// to assert that the email is sent to the correct person
|
// to assert that the email is sent to the correct person
|
||||||
$this->assertEmailSent("someone@example.com", null, "/th.*e$/");
|
$this->assertEmailSent("someone@example.com", null, "/th.*e$/");
|
||||||
```
|
```
|
||||||
|
|
||||||
Each of the arguments (`$to`, `$from`, `$subject` and `$body`) can be either one of the following.
|
Each of the arguments (`$to`, `$from`, `$subject` and `$body`) can be either one of the following.
|
||||||
|
@ -12,13 +12,13 @@ The definition of setting an environment type in a `mysite/_config/app.yml` look
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
SilverStripe\Control\Director:
|
SilverStripe\Control\Director:
|
||||||
environment_type: 'dev'
|
environment_type: 'dev'
|
||||||
```
|
```
|
||||||
|
|
||||||
The definition of setting an environment type in a `.env` file looks like
|
The definition of setting an environment type in a `.env` file looks like
|
||||||
```
|
```
|
||||||
SS_ENVIRONMENT_TYPE="dev"
|
SS_ENVIRONMENT_TYPE="dev"
|
||||||
```
|
```
|
||||||
The three environment types you can set are `dev`, `test` and `live`.
|
The three environment types you can set are `dev`, `test` and `live`.
|
||||||
|
|
||||||
@ -44,12 +44,12 @@ want to password protect the site. You can enable that by adding this to your `m
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
---
|
---
|
||||||
Only:
|
Only:
|
||||||
environment: 'test'
|
environment: 'test'
|
||||||
---
|
---
|
||||||
SilverStripe\Security\BasicAuth:
|
SilverStripe\Security\BasicAuth:
|
||||||
entire_site_protected: true
|
entire_site_protected: true
|
||||||
```
|
```
|
||||||
|
|
||||||
### Live Mode
|
### Live Mode
|
||||||
@ -67,29 +67,29 @@ You can check for the current environment type in [config files](../configuratio
|
|||||||
|
|
||||||
**mysite/_config/app.yml**
|
**mysite/_config/app.yml**
|
||||||
```yml
|
```yml
|
||||||
---
|
---
|
||||||
Only:
|
Only:
|
||||||
environment: 'live'
|
environment: 'live'
|
||||||
---
|
---
|
||||||
MyClass:
|
MyClass:
|
||||||
myvar: live_value
|
myvar: live_value
|
||||||
---
|
---
|
||||||
Only:
|
Only:
|
||||||
environment: 'test'
|
environment: 'test'
|
||||||
---
|
---
|
||||||
MyClass:
|
MyClass:
|
||||||
myvar: test_value
|
myvar: test_value
|
||||||
```
|
```
|
||||||
Checking for what environment you're running in can also be done in PHP. Your application code may disable or enable
|
Checking for what environment you're running in can also be done in PHP. Your application code may disable or enable
|
||||||
certain functionality depending on the environment type.
|
certain functionality depending on the environment type.
|
||||||
```php
|
```php
|
||||||
if (Director::isLive()) {
|
if (Director::isLive()) {
|
||||||
// is in live
|
// is in live
|
||||||
} elseif (Director::isTest()) {
|
} elseif (Director::isTest()) {
|
||||||
// is in test mode
|
// is in test mode
|
||||||
} elseif (Director::isDev()) {
|
} elseif (Director::isDev()) {
|
||||||
// is in dev mode
|
// is in dev mode
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,10 +11,10 @@ source code comments indicating which template is responsible for rendering each
|
|||||||
block of html on your page.
|
block of html on your page.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
---
|
---
|
||||||
Only:
|
Only:
|
||||||
environment: 'dev'
|
environment: 'dev'
|
||||||
---
|
---
|
||||||
SSViewer:
|
SSViewer:
|
||||||
source_file_comments: true
|
source_file_comments: true
|
||||||
```
|
```
|
@ -17,7 +17,7 @@ bottle-necks and identify slow moving parts of your application chain.
|
|||||||
|
|
||||||
The [Debug](api:SilverStripe\Dev\Debug) class contains a number of static utility methods for more advanced debugging.
|
The [Debug](api:SilverStripe\Dev\Debug) class contains a number of static utility methods for more advanced debugging.
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
Debug::show($myVariable);
|
Debug::show($myVariable);
|
||||||
// similar to print_r($myVariable) but shows it in a more useful format.
|
// similar to print_r($myVariable) but shows it in a more useful format.
|
||||||
|
|
||||||
@ -26,6 +26,7 @@ The [Debug](api:SilverStripe\Dev\Debug) class contains a number of static utilit
|
|||||||
|
|
||||||
Backtrace::backtrace();
|
Backtrace::backtrace();
|
||||||
// prints a calls-stack
|
// prints a calls-stack
|
||||||
|
```
|
||||||
|
|
||||||
## API Documentation
|
## API Documentation
|
||||||
|
|
||||||
|
@ -6,10 +6,10 @@ summary: Cache SilverStripe templates to reduce database queries.
|
|||||||
Partial caching is a feature that allows the caching of just a portion of a page.
|
Partial caching is a feature that allows the caching of just a portion of a page.
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% cached 'CacheKey' %>
|
<% cached 'CacheKey' %>
|
||||||
$DataTable
|
$DataTable
|
||||||
...
|
...
|
||||||
<% end_cached %>
|
<% end_cached %>
|
||||||
```
|
```
|
||||||
|
|
||||||
Each cache block has a cache key. A cache key is an unlimited number of comma separated variables and quoted strings.
|
Each cache block has a cache key. A cache key is an unlimited number of comma separated variables and quoted strings.
|
||||||
@ -24,17 +24,17 @@ Here are some more complex examples:
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% cached 'database', $LastEdited %>
|
<% cached 'database', $LastEdited %>
|
||||||
<!-- that updates every time the record changes. -->
|
<!-- that updates every time the record changes. -->
|
||||||
<% end_cached %>
|
<% end_cached %>
|
||||||
|
|
||||||
<% cached 'loginblock', $CurrentMember.ID %>
|
<% cached 'loginblock', $CurrentMember.ID %>
|
||||||
<!-- cached unique to the user. i.e for user 2, they will see a different cache to user 1 -->
|
<!-- cached unique to the user. i.e for user 2, they will see a different cache to user 1 -->
|
||||||
<% end_cached %>
|
<% end_cached %>
|
||||||
|
|
||||||
<% cached 'loginblock', $LastEdited, $CurrentMember.isAdmin %>
|
<% cached 'loginblock', $LastEdited, $CurrentMember.isAdmin %>
|
||||||
<!-- recached when block object changes, and if the user is admin -->
|
<!-- recached when block object changes, and if the user is admin -->
|
||||||
<% end_cached %>
|
<% end_cached %>
|
||||||
```
|
```
|
||||||
|
|
||||||
An additional global key is incorporated in the cache lookup. The default value for this is
|
An additional global key is incorporated in the cache lookup. The default value for this is
|
||||||
@ -50,8 +50,8 @@ user does not influence your template content, you can update this key as below;
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|
||||||
SilverStripe\View\SSViewer:
|
SilverStripe\View\SSViewer:
|
||||||
global_key: '$CurrentReadingMode, $Locale'
|
global_key: '$CurrentReadingMode, $Locale'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Aggregates
|
## Aggregates
|
||||||
@ -66,7 +66,7 @@ otherwise. By using aggregates, we do that like this:
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% cached 'navigation', $List('SiteTree').max('LastEdited'), $List('SiteTree').count() %>
|
<% cached 'navigation', $List('SiteTree').max('LastEdited'), $List('SiteTree').count() %>
|
||||||
```
|
```
|
||||||
|
|
||||||
The cache for this will update whenever a page is added, removed or edited.
|
The cache for this will update whenever a page is added, removed or edited.
|
||||||
@ -77,7 +77,7 @@ or edited
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% cached 'categorylist', $List('Category').max('LastEdited'), $List('Category').count() %>
|
<% cached 'categorylist', $List('Category').max('LastEdited'), $List('Category').count() %>
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="notice" markdown="1">
|
<div class="notice" markdown="1">
|
||||||
@ -98,17 +98,17 @@ For example, a block that shows a collection of rotating slides needs to update
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function SliderCacheKey()
|
public function SliderCacheKey()
|
||||||
{
|
{
|
||||||
$fragments = [
|
$fragments = [
|
||||||
'Page-Slides',
|
'Page-Slides',
|
||||||
$this->ID,
|
$this->ID,
|
||||||
// identify which objects are in the list and their sort order
|
// identify which objects are in the list and their sort order
|
||||||
implode('-', $this->Slides()->Column('ID')),
|
implode('-', $this->Slides()->Column('ID')),
|
||||||
$this->Slides()->max('LastEdited')
|
$this->Slides()->max('LastEdited')
|
||||||
];
|
];
|
||||||
return implode('-_-', $fragments);
|
return implode('-_-', $fragments);
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -117,7 +117,7 @@ Then reference that function in the cache key:
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% cached $SliderCacheKey %>
|
<% cached $SliderCacheKey %>
|
||||||
```
|
```
|
||||||
|
|
||||||
The example above would work for both a has_many and many_many relationship.
|
The example above would work for both a has_many and many_many relationship.
|
||||||
@ -139,7 +139,7 @@ For instance, if we show some blog statistics, but are happy having them be slig
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% cached 'blogstatistics', $Blog.ID %>
|
<% cached 'blogstatistics', $Blog.ID %>
|
||||||
```
|
```
|
||||||
|
|
||||||
which will invalidate after the cache lifetime expires. If you need more control than that (cache lifetime is
|
which will invalidate after the cache lifetime expires. If you need more control than that (cache lifetime is
|
||||||
@ -147,10 +147,10 @@ configurable only on a site-wide basis), you could add a special function to you
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function BlogStatisticsCounter()
|
public function BlogStatisticsCounter()
|
||||||
{
|
{
|
||||||
return (int)(time() / 60 / 5); // Returns a new number every five minutes
|
return (int)(time() / 60 / 5); // Returns a new number every five minutes
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -159,7 +159,7 @@ and then use it in the cache key
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% cached 'blogstatistics', $Blog.ID, $BlogStatisticsCounter %>
|
<% cached 'blogstatistics', $Blog.ID, $BlogStatisticsCounter %>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Cache block conditionals
|
## Cache block conditionals
|
||||||
@ -174,7 +174,7 @@ heavy load:
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% cached 'blogstatistics', $Blog.ID if $HighLoad %>
|
<% cached 'blogstatistics', $Blog.ID if $HighLoad %>
|
||||||
```
|
```
|
||||||
|
|
||||||
By adding a `HighLoad` function to your `PageController`, you could enable or disable caching dynamically.
|
By adding a `HighLoad` function to your `PageController`, you could enable or disable caching dynamically.
|
||||||
@ -185,7 +185,7 @@ To cache the contents of a page for all anonymous users, but dynamically calcula
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% cached unless $CurrentUser %>
|
<% cached unless $CurrentUser %>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Uncached
|
## Uncached
|
||||||
@ -197,7 +197,7 @@ particular cache block by changing just the tag, leaving the key and conditional
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% uncached %>
|
<% uncached %>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Nested cache blocks
|
## Nested cache blocks
|
||||||
@ -213,15 +213,15 @@ An example:
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% cached $LastEdited %>
|
<% cached $LastEdited %>
|
||||||
Our wonderful site
|
Our wonderful site
|
||||||
|
|
||||||
<% cached $Member.ID %>
|
<% cached $Member.ID %>
|
||||||
Welcome $Member.Name
|
Welcome $Member.Name
|
||||||
<% end_cached %>
|
<% end_cached %>
|
||||||
|
|
||||||
$ASlowCalculation
|
$ASlowCalculation
|
||||||
<% end_cached %>
|
<% end_cached %>
|
||||||
```
|
```
|
||||||
|
|
||||||
This will cache the entire outer section until the next time the page is edited, but will display a different welcome
|
This will cache the entire outer section until the next time the page is edited, but will display a different welcome
|
||||||
@ -233,15 +233,15 @@ could also write the last example as:
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% cached $LastEdited %>
|
<% cached $LastEdited %>
|
||||||
Our wonderful site
|
Our wonderful site
|
||||||
|
|
||||||
<% uncached %>
|
<% uncached %>
|
||||||
Welcome $Member.Name
|
Welcome $Member.Name
|
||||||
<% end_uncached %>
|
<% end_uncached %>
|
||||||
|
|
||||||
$ASlowCalculation
|
$ASlowCalculation
|
||||||
<% end_cached %>
|
<% end_cached %>
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="warning" markdown="1">
|
<div class="warning" markdown="1">
|
||||||
@ -254,15 +254,15 @@ Failing example:
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% cached $LastEdited %>
|
<% cached $LastEdited %>
|
||||||
|
|
||||||
<% loop $Children %>
|
<% loop $Children %>
|
||||||
<% cached $LastEdited %>
|
<% cached $LastEdited %>
|
||||||
$Name
|
$Name
|
||||||
<% end_cached %>
|
<% end_cached %>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
|
|
||||||
<% end_cached %>
|
<% end_cached %>
|
||||||
```
|
```
|
||||||
|
|
||||||
Can be re-written as:
|
Can be re-written as:
|
||||||
@ -270,15 +270,15 @@ Can be re-written as:
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% cached $LastEdited %>
|
<% cached $LastEdited %>
|
||||||
|
|
||||||
<% cached $AllChildren.max('LastEdited') %>
|
<% cached $AllChildren.max('LastEdited') %>
|
||||||
<% loop $Children %>
|
<% loop $Children %>
|
||||||
$Name
|
$Name
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
<% end_cached %>
|
<% end_cached %>
|
||||||
|
|
||||||
<% end_cached %>
|
<% end_cached %>
|
||||||
```
|
```
|
||||||
|
|
||||||
Or:
|
Or:
|
||||||
@ -286,13 +286,13 @@ Or:
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% cached $LastEdited %>
|
<% cached $LastEdited %>
|
||||||
(other code)
|
(other code)
|
||||||
<% end_cached %>
|
<% end_cached %>
|
||||||
|
|
||||||
<% loop $Children %>
|
<% loop $Children %>
|
||||||
<% cached $LastEdited %>
|
<% cached $LastEdited %>
|
||||||
$Name
|
$Name
|
||||||
<% end_cached %>
|
<% end_cached %>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
```
|
```
|
||||||
|
@ -65,7 +65,7 @@ Cache objects follow the [PSR-16](http://www.php-fig.org/psr/psr-16/) class inte
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use Psr\SimpleCache\CacheInterface
|
use Psr\SimpleCache\CacheInterface
|
||||||
$cache = Injector::inst()->get(CacheInterface::class . '.myCache');
|
$cache = Injector::inst()->get(CacheInterface::class . '.myCache');
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ headers:
|
|||||||
|
|
||||||
### HTTP::set_cache_age
|
### HTTP::set_cache_age
|
||||||
```php
|
```php
|
||||||
HTTP::set_cache_age(0);
|
HTTP::set_cache_age(0);
|
||||||
```
|
```
|
||||||
|
|
||||||
Used to set the max-age component of the cache-control line, in seconds. Set it to 0 to disable caching; the "no-cache"
|
Used to set the max-age component of the cache-control line, in seconds. Set it to 0 to disable caching; the "no-cache"
|
||||||
@ -28,7 +28,7 @@ clause in `Cache-Control` and `Pragma` will be included.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
HTTP::register_modification_date('2014-10-10');
|
HTTP::register_modification_date('2014-10-10');
|
||||||
```
|
```
|
||||||
|
|
||||||
Used to set the modification date to something more recent than the default. [DataObject::__construct](api:SilverStripe\ORM\DataObject::__construct) calls
|
Used to set the modification date to something more recent than the default. [DataObject::__construct](api:SilverStripe\ORM\DataObject::__construct) calls
|
||||||
|
@ -24,8 +24,8 @@ SilverStripe can request more resources through `Environment::increaseMemoryLimi
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function myBigFunction()
|
public function myBigFunction()
|
||||||
{
|
{
|
||||||
Environment::increaseTimeLimitTo(400);
|
Environment::increaseTimeLimitTo(400);
|
||||||
}
|
}
|
||||||
```
|
```
|
@ -14,11 +14,11 @@ Returns the full *Member* Object for the current user, returns *null* if user is
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
if( $member = Security::getCurrentUser() ) {
|
if( $member = Security::getCurrentUser() ) {
|
||||||
// Work with $member
|
// Work with $member
|
||||||
} else {
|
} else {
|
||||||
// Do non-member stuff
|
// Do non-member stuff
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Subclassing
|
## Subclassing
|
||||||
@ -32,15 +32,15 @@ You can define subclasses of [Member](api:SilverStripe\Security\Member) to add e
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Security\Member;
|
use SilverStripe\Security\Member;
|
||||||
|
|
||||||
class MyMember extends Member
|
class MyMember extends Member
|
||||||
{
|
{
|
||||||
private static $db = [
|
private static $db = [
|
||||||
"Age" => "Int",
|
"Age" => "Int",
|
||||||
"Address" => "Text",
|
"Address" => "Text",
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -61,14 +61,14 @@ parent::getCMSFields() and manipulate the [FieldList](api:SilverStripe\Forms\Fie
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function getCMSFields()
|
public function getCMSFields()
|
||||||
{
|
{
|
||||||
$fields = parent::getCMSFields();
|
$fields = parent::getCMSFields();
|
||||||
$fields->insertBefore("HTMLEmail", new TextField("Age"));
|
$fields->insertBefore("HTMLEmail", new TextField("Age"));
|
||||||
$fields->removeByName("JobTitle");
|
$fields->removeByName("JobTitle");
|
||||||
$fields->removeByName("Organisation");
|
$fields->removeByName("Organisation");
|
||||||
return $fields;
|
return $fields;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Extending Member or DataObject?
|
## Extending Member or DataObject?
|
||||||
@ -89,9 +89,9 @@ class. A better way is to use role extensions to add this behaviour. Add the fol
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
Member:
|
Member:
|
||||||
extensions:
|
extensions:
|
||||||
- MyMemberExtension
|
- MyMemberExtension
|
||||||
```
|
```
|
||||||
|
|
||||||
A role extension is simply a subclass of [DataExtension](api:SilverStripe\ORM\DataExtension) that is designed to be used to add behaviour to [Member](api:SilverStripe\Security\Member).
|
A role extension is simply a subclass of [DataExtension](api:SilverStripe\ORM\DataExtension) that is designed to be used to add behaviour to [Member](api:SilverStripe\Security\Member).
|
||||||
@ -100,35 +100,35 @@ things, you should add appropriate [Permission::checkMember()](api:SilverStripe\
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Security\Permission;
|
use SilverStripe\Security\Permission;
|
||||||
use SilverStripe\ORM\DataExtension;
|
use SilverStripe\ORM\DataExtension;
|
||||||
|
|
||||||
class MyMemberExtension extends DataExtension
|
class MyMemberExtension extends DataExtension
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
||||||
* Modify the field set to be displayed in the CMS detail pop-up
|
* Modify the field set to be displayed in the CMS detail pop-up
|
||||||
*/
|
*/
|
||||||
public function updateCMSFields(FieldList $currentFields)
|
public function updateCMSFields(FieldList $currentFields)
|
||||||
{
|
{
|
||||||
// Only show the additional fields on an appropriate kind of use
|
// Only show the additional fields on an appropriate kind of use
|
||||||
if(Permission::checkMember($this->owner->ID, "VIEW_FORUM")) {
|
if(Permission::checkMember($this->owner->ID, "VIEW_FORUM")) {
|
||||||
// Edit the FieldList passed, adding or removing fields as necessary
|
// Edit the FieldList passed, adding or removing fields as necessary
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// define additional properties
|
// define additional properties
|
||||||
private static $db = [];
|
private static $db = [];
|
||||||
private static $has_one = [];
|
private static $has_one = [];
|
||||||
private static $has_many = [];
|
private static $has_many = [];
|
||||||
private static $many_many = [];
|
private static $many_many = [];
|
||||||
private static $belongs_many_many = [];
|
private static $belongs_many_many = [];
|
||||||
|
|
||||||
public function somethingElse()
|
public function somethingElse()
|
||||||
{
|
{
|
||||||
// You can add any other methods you like, which you can call directly on the member object.
|
// You can add any other methods you like, which you can call directly on the member object.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -39,9 +39,9 @@ and password to be configured for a single special user outside of the normal me
|
|||||||
|
|
||||||
It is advisable to configure this user in your `.env` file inside of the web root, as below:
|
It is advisable to configure this user in your `.env` file inside of the web root, as below:
|
||||||
```
|
```
|
||||||
# Configure a default username and password to access the CMS on all sites in this environment.
|
# Configure a default username and password to access the CMS on all sites in this environment.
|
||||||
SS_DEFAULT_ADMIN_USERNAME="admin"
|
SS_DEFAULT_ADMIN_USERNAME="admin"
|
||||||
SS_DEFAULT_ADMIN_PASSWORD="password"
|
SS_DEFAULT_ADMIN_PASSWORD="password"
|
||||||
```
|
```
|
||||||
When a user logs in with these credentials, then a [Member](api:SilverStripe\Security\Member) with the Email 'admin' will be generated in
|
When a user logs in with these credentials, then a [Member](api:SilverStripe\Security\Member) with the Email 'admin' will be generated in
|
||||||
the database, but without any password information. This means that the password can be reset or changed by simply
|
the database, but without any password information. This means that the password can be reset or changed by simply
|
||||||
|
@ -26,13 +26,13 @@ come from user input.
|
|||||||
|
|
||||||
Example:
|
Example:
|
||||||
```php
|
```php
|
||||||
$records = DB::prepared_query('SELECT * FROM "MyClass" WHERE "ID" = ?', [3]);
|
$records = DB::prepared_query('SELECT * FROM "MyClass" WHERE "ID" = ?', [3]);
|
||||||
$records = MyClass::get()->where(['"ID" = ?' => 3]);
|
$records = MyClass::get()->where(['"ID" = ?' => 3]);
|
||||||
$records = MyClass::get()->where(['"ID"' => 3]);
|
$records = MyClass::get()->where(['"ID"' => 3]);
|
||||||
$records = DataObject::get_by_id('MyClass', 3);
|
$records = DataObject::get_by_id('MyClass', 3);
|
||||||
$records = DataObject::get_one('MyClass', ['"ID" = ?' => 3]);
|
$records = DataObject::get_one('MyClass', ['"ID" = ?' => 3]);
|
||||||
$records = MyClass::get()->byID(3);
|
$records = MyClass::get()->byID(3);
|
||||||
$records = SQLSelect::create()->addWhere(['"ID"' => 3])->execute();
|
$records = SQLSelect::create()->addWhere(['"ID"' => 3])->execute();
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -40,20 +40,20 @@ Parameterised updates and inserts are also supported, but the syntax is a little
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
SQLInsert::create('"MyClass"')
|
SQLInsert::create('"MyClass"')
|
||||||
->assign('"Name"', 'Daniel')
|
->assign('"Name"', 'Daniel')
|
||||||
->addAssignments([
|
->addAssignments([
|
||||||
'"Position"' => 'Accountant',
|
'"Position"' => 'Accountant',
|
||||||
'"Age"' => [
|
'"Age"' => [
|
||||||
'GREATEST(0,?,?)' => [24, 28]
|
'GREATEST(0,?,?)' => [24, 28]
|
||||||
]
|
]
|
||||||
])
|
])
|
||||||
->assignSQL('"Created"', 'NOW()')
|
->assignSQL('"Created"', 'NOW()')
|
||||||
->execute();
|
->execute();
|
||||||
DB::prepared_query(
|
DB::prepared_query(
|
||||||
'INSERT INTO "MyClass" ("Name", "Position", "Age", "Created") VALUES(?, ?, GREATEST(0,?,?), NOW())'
|
'INSERT INTO "MyClass" ("Name", "Position", "Age", "Created") VALUES(?, ?, GREATEST(0,?,?), NOW())'
|
||||||
['Daniel', 'Accountant', 24, 28]
|
['Daniel', 'Accountant', 24, 28]
|
||||||
);
|
);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -80,14 +80,14 @@ handled via prepared statements.
|
|||||||
|
|
||||||
Example:
|
Example:
|
||||||
```php
|
```php
|
||||||
// automatically escaped/quoted
|
// automatically escaped/quoted
|
||||||
$members = Member::get()->filter('Name', $_GET['name']);
|
$members = Member::get()->filter('Name', $_GET['name']);
|
||||||
// automatically escaped/quoted
|
// automatically escaped/quoted
|
||||||
$members = Member::get()->filter(['Name' => $_GET['name']]);
|
$members = Member::get()->filter(['Name' => $_GET['name']]);
|
||||||
// parameterised condition
|
// parameterised condition
|
||||||
$members = Member::get()->where(['"Name" = ?' => $_GET['name']]);
|
$members = Member::get()->where(['"Name" = ?' => $_GET['name']]);
|
||||||
// needs to be escaped and quoted manually (note raw2sql called with the $quote parameter set to true)
|
// needs to be escaped and quoted manually (note raw2sql called with the $quote parameter set to true)
|
||||||
$members = Member::get()->where(sprintf('"Name" = %s', Convert::raw2sql($_GET['name'], true)));
|
$members = Member::get()->where(sprintf('"Name" = %s', Convert::raw2sql($_GET['name'], true)));
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -114,19 +114,19 @@ Example:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Core\Convert;
|
use SilverStripe\Core\Convert;
|
||||||
use SilverStripe\Forms\Form;
|
use SilverStripe\Forms\Form;
|
||||||
|
|
||||||
class MyForm extends Form
|
class MyForm extends Form
|
||||||
{
|
{
|
||||||
public function save($RAW_data, $form)
|
public function save($RAW_data, $form)
|
||||||
{
|
{
|
||||||
// Pass true as the second parameter of raw2sql to quote the value safely
|
// Pass true as the second parameter of raw2sql to quote the value safely
|
||||||
$SQL_data = Convert::raw2sql($RAW_data, true); // works recursively on an array
|
$SQL_data = Convert::raw2sql($RAW_data, true); // works recursively on an array
|
||||||
$objs = Player::get()->where("Name = " . $SQL_data['name']);
|
$objs = Player::get()->where("Name = " . $SQL_data['name']);
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -137,20 +137,20 @@ Example:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Core\Convert;
|
use SilverStripe\Core\Convert;
|
||||||
use SilverStripe\Control\Controller;
|
use SilverStripe\Control\Controller;
|
||||||
|
|
||||||
class MyController extends Controller
|
class MyController extends Controller
|
||||||
{
|
{
|
||||||
private static $allowed_actions = ['myurlaction'];
|
private static $allowed_actions = ['myurlaction'];
|
||||||
public function myurlaction($RAW_urlParams)
|
public function myurlaction($RAW_urlParams)
|
||||||
{
|
{
|
||||||
// Pass true as the second parameter of raw2sql to quote the value safely
|
// Pass true as the second parameter of raw2sql to quote the value safely
|
||||||
$SQL_urlParams = Convert::raw2sql($RAW_urlParams, true); // works recursively on an array
|
$SQL_urlParams = Convert::raw2sql($RAW_urlParams, true); // works recursively on an array
|
||||||
$objs = Player::get()->where("Name = " . $SQL_data['OtherID']);
|
$objs = Player::get()->where("Name = " . $SQL_data['OtherID']);
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -160,27 +160,27 @@ passing data through, escaping should happen at the end of the chain.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Core\Convert;
|
use SilverStripe\Core\Convert;
|
||||||
use SilverStripe\ORM\DB;
|
use SilverStripe\ORM\DB;
|
||||||
use SilverStripe\Control\Controller;
|
use SilverStripe\Control\Controller;
|
||||||
|
|
||||||
class MyController extends Controller
|
class MyController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @param array $RAW_data All names in an indexed array (not SQL-safe)
|
* @param array $RAW_data All names in an indexed array (not SQL-safe)
|
||||||
*/
|
*/
|
||||||
public function saveAllNames($RAW_data)
|
public function saveAllNames($RAW_data)
|
||||||
{
|
{
|
||||||
// $SQL_data = Convert::raw2sql($RAW_data); // premature escaping
|
// $SQL_data = Convert::raw2sql($RAW_data); // premature escaping
|
||||||
foreach($RAW_data as $item) $this->saveName($item);
|
foreach($RAW_data as $item) $this->saveName($item);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function saveName($RAW_name)
|
public function saveName($RAW_name)
|
||||||
{
|
{
|
||||||
$SQL_name = Convert::raw2sql($RAW_name, true);
|
$SQL_name = Convert::raw2sql($RAW_name, true);
|
||||||
DB::query("UPDATE Player SET Name = {$SQL_name}");
|
DB::query("UPDATE Player SET Name = {$SQL_name}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -217,7 +217,7 @@ stripped out
|
|||||||
To enable filtering, set the HtmlEditorField::$sanitise_server_side [configuration](/developer_guides/configuration/configuration) property to
|
To enable filtering, set the HtmlEditorField::$sanitise_server_side [configuration](/developer_guides/configuration/configuration) property to
|
||||||
true, e.g.
|
true, e.g.
|
||||||
```
|
```
|
||||||
HtmlEditorField::config()->sanitise_server_side = true
|
HtmlEditorField::config()->sanitise_server_side = true
|
||||||
```
|
```
|
||||||
The built in sanitiser enforces the TinyMCE whitelist rules on the server side, and is sufficient to eliminate the
|
The built in sanitiser enforces the TinyMCE whitelist rules on the server side, and is sufficient to eliminate the
|
||||||
most common XSS vectors.
|
most common XSS vectors.
|
||||||
@ -248,15 +248,15 @@ PHP:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class MyObject extends DataObject
|
class MyObject extends DataObject
|
||||||
{
|
{
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'MyEscapedValue' => 'Text', // Example value: <b>not bold</b>
|
'MyEscapedValue' => 'Text', // Example value: <b>not bold</b>
|
||||||
'MyUnescapedValue' => 'HTMLText' // Example value: <b>bold</b>
|
'MyUnescapedValue' => 'HTMLText' // Example value: <b>bold</b>
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -264,10 +264,10 @@ Template:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
<ul>
|
<ul>
|
||||||
<li>$MyEscapedValue</li> // output: <b>not bold<b>
|
<li>$MyEscapedValue</li> // output: <b>not bold<b>
|
||||||
<li>$MyUnescapedValue</li> // output: <b>bold</b>
|
<li>$MyUnescapedValue</li> // output: <b>bold</b>
|
||||||
</ul>
|
</ul>
|
||||||
```
|
```
|
||||||
|
|
||||||
The example below assumes that data wasn't properly filtered when saving to the database, but are escaped before
|
The example below assumes that data wasn't properly filtered when saving to the database, but are escaped before
|
||||||
@ -282,13 +282,13 @@ Template (see above):
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
<ul>
|
<ul>
|
||||||
// output: <a href="#" title="foo & &#quot;bar"">foo & "bar"</a>
|
// output: <a href="#" title="foo & &#quot;bar"">foo & "bar"</a>
|
||||||
<li><a href="#" title="$Title.ATT">$Title</a></li>
|
<li><a href="#" title="$Title.ATT">$Title</a></li>
|
||||||
<li>$MyEscapedValue</li> // output: <b>not bold<b>
|
<li>$MyEscapedValue</li> // output: <b>not bold<b>
|
||||||
<li>$MyUnescapedValue</li> // output: <b>bold</b>
|
<li>$MyUnescapedValue</li> // output: <b>bold</b>
|
||||||
<li>$MyUnescapedValue.XML</li> // output: <b>bold<b>
|
<li>$MyUnescapedValue.XML</li> // output: <b>bold<b>
|
||||||
</ul>
|
</ul>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Escaping custom attributes and getters
|
### Escaping custom attributes and getters
|
||||||
@ -300,23 +300,23 @@ PHP:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class MyObject extends DataObject
|
class MyObject extends DataObject
|
||||||
{
|
{
|
||||||
public $Title = '<b>not bold</b>'; // will be escaped due to Text casting
|
public $Title = '<b>not bold</b>'; // will be escaped due to Text casting
|
||||||
|
|
||||||
$casting = [
|
$casting = [
|
||||||
"Title" => "Text", // forcing a casting
|
"Title" => "Text", // forcing a casting
|
||||||
'TitleWithHTMLSuffix' => 'HTMLText' // optional, as HTMLText is the default casting
|
'TitleWithHTMLSuffix' => 'HTMLText' // optional, as HTMLText is the default casting
|
||||||
];
|
];
|
||||||
|
|
||||||
public function TitleWithHTMLSuffix($suffix)
|
public function TitleWithHTMLSuffix($suffix)
|
||||||
{
|
{
|
||||||
// $this->Title is not casted in PHP
|
// $this->Title is not casted in PHP
|
||||||
return $this->Title . '<small>(' . $suffix. ')</small>';
|
return $this->Title . '<small>(' . $suffix. ')</small>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -324,11 +324,11 @@ Template:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
<ul>
|
<ul>
|
||||||
<li>$Title</li> // output: <b>not bold<b>
|
<li>$Title</li> // output: <b>not bold<b>
|
||||||
<li>$Title.RAW</li> // output: <b>not bold</b>
|
<li>$Title.RAW</li> // output: <b>not bold</b>
|
||||||
<li>$TitleWithHTMLSuffix</li> // output: <b>not bold</b>: <small>(...)</small>
|
<li>$TitleWithHTMLSuffix</li> // output: <b>not bold</b>: <small>(...)</small>
|
||||||
</ul>
|
</ul>
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: Avoid generating HTML by string concatenation in PHP wherever possible to minimize risk and separate your
|
Note: Avoid generating HTML by string concatenation in PHP wherever possible to minimize risk and separate your
|
||||||
@ -346,23 +346,23 @@ PHP:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Core\Convert;
|
use SilverStripe\Core\Convert;
|
||||||
use SilverStripe\Control\Controller;
|
use SilverStripe\Control\Controller;
|
||||||
use SilverStripe\ORM\FieldType\DBText;
|
use SilverStripe\ORM\FieldType\DBText;
|
||||||
use SilverStripe\ORM\FieldType\DBHTMLText;
|
use SilverStripe\ORM\FieldType\DBHTMLText;
|
||||||
|
|
||||||
class MyController extends Controller
|
class MyController extends Controller
|
||||||
{
|
{
|
||||||
private static $allowed_actions = ['search'];
|
private static $allowed_actions = ['search'];
|
||||||
public function search($request)
|
public function search($request)
|
||||||
{
|
{
|
||||||
$htmlTitle = '<p>Your results for:' . Convert::raw2xml($request->getVar('Query')) . '</p>';
|
$htmlTitle = '<p>Your results for:' . Convert::raw2xml($request->getVar('Query')) . '</p>';
|
||||||
return $this->customise([
|
return $this->customise([
|
||||||
'Query' => DBText::create($request->getVar('Query')),
|
'Query' => DBText::create($request->getVar('Query')),
|
||||||
'HTMLTitle' => DBHTMLText::create($htmlTitle)
|
'HTMLTitle' => DBHTMLText::create($htmlTitle)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -370,7 +370,7 @@ Template:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
<h2 title="Searching for $Query.ATT">$HTMLTitle</h2>
|
<h2 title="Searching for $Query.ATT">$HTMLTitle</h2>
|
||||||
```
|
```
|
||||||
|
|
||||||
Whenever you insert a variable into an HTML attribute within a template, use $VarName.ATT, no not $VarName.
|
Whenever you insert a variable into an HTML attribute within a template, use $VarName.ATT, no not $VarName.
|
||||||
@ -386,21 +386,21 @@ PHP:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Control\Controller;
|
use SilverStripe\Control\Controller;
|
||||||
use SilverStripe\ORM\FieldType\DBText;
|
use SilverStripe\ORM\FieldType\DBText;
|
||||||
|
|
||||||
class MyController extends Controller
|
class MyController extends Controller
|
||||||
{
|
{
|
||||||
private static $allowed_actions = ['search'];
|
private static $allowed_actions = ['search'];
|
||||||
public function search($request)
|
public function search($request)
|
||||||
{
|
{
|
||||||
$rssRelativeLink = "/rss?Query=" . urlencode($_REQUEST['query']) . "&sortOrder=asc";
|
$rssRelativeLink = "/rss?Query=" . urlencode($_REQUEST['query']) . "&sortOrder=asc";
|
||||||
$rssLink = Controller::join_links($this->Link(), $rssRelativeLink);
|
$rssLink = Controller::join_links($this->Link(), $rssRelativeLink);
|
||||||
return $this->customise([
|
return $this->customise([
|
||||||
"RSSLink" => DBText::create($rssLink),
|
"RSSLink" => DBText::create($rssLink),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -408,7 +408,7 @@ Template:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
<a href="$RSSLink.ATT">RSS feed</a>
|
<a href="$RSSLink.ATT">RSS feed</a>
|
||||||
```
|
```
|
||||||
|
|
||||||
Some rules of thumb:
|
Some rules of thumb:
|
||||||
@ -457,21 +457,21 @@ Below is an example with different ways you would use this casting technique:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function CaseStudies()
|
public 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::urlParam['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;
|
||||||
|
|
||||||
// cast the 'category' GET variable as an integer
|
// cast the 'category' GET variable as an integer
|
||||||
$categoryID = (int)$_GET['category'];
|
$categoryID = (int)$_GET['category'];
|
||||||
|
|
||||||
// perform a byID(), which ensures the ID is an integer before querying
|
// perform a byID(), which ensures the ID is an integer before querying
|
||||||
return CaseStudy::get()->byID($categoryID);
|
return CaseStudy::get()->byID($categoryID);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The same technique can be employed anywhere in your PHP code you know something must be of a certain type. A list of PHP
|
The same technique can be employed anywhere in your PHP code you know something must be of a certain type. A list of PHP
|
||||||
@ -504,10 +504,10 @@ If you need users to access files with this extension,
|
|||||||
you can bypass the rules for a specific directory.
|
you can bypass the rules for a specific directory.
|
||||||
Here's an example for a `.htaccess` file used by the Apache web server:
|
Here's an example for a `.htaccess` file used by the Apache web server:
|
||||||
```
|
```
|
||||||
<Files *.yml>
|
<Files *.yml>
|
||||||
Order allow,deny
|
Order allow,deny
|
||||||
Allow from all
|
Allow from all
|
||||||
</Files>
|
</Files>
|
||||||
|
|
||||||
```
|
```
|
||||||
### User uploaded files
|
### User uploaded files
|
||||||
@ -555,11 +555,11 @@ a [PasswordValidator](api:SilverStripe\Security\PasswordValidator):
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$validator = new PasswordValidator();
|
$validator = new PasswordValidator();
|
||||||
$validator->minLength(7);
|
$validator->minLength(7);
|
||||||
$validator->checkHistoricalPasswords(6);
|
$validator->checkHistoricalPasswords(6);
|
||||||
$validator->characterStrength(3, ["lowercase", "uppercase", "digits", "punctuation"]);
|
$validator->characterStrength(3, ["lowercase", "uppercase", "digits", "punctuation"]);
|
||||||
Member::set_password_validator($validator);
|
Member::set_password_validator($validator);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -585,16 +585,16 @@ controller's `init()` method:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Control\Controller;
|
use SilverStripe\Control\Controller;
|
||||||
|
|
||||||
class MyController extends Controller
|
class MyController extends Controller
|
||||||
{
|
{
|
||||||
public function init()
|
public function init()
|
||||||
{
|
{
|
||||||
parent::init();
|
parent::init();
|
||||||
$this->getResponse()->addHeader('X-Frame-Options', 'SAMEORIGIN');
|
$this->getResponse()->addHeader('X-Frame-Options', 'SAMEORIGIN');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
This is a recommended option to secure any controller which displays
|
This is a recommended option to secure any controller which displays
|
||||||
@ -608,7 +608,7 @@ allows the configure of a whitelist of hosts that are allowed to access the syst
|
|||||||
this whitelist in your `.env` file, any request presenting a `Host` header that is
|
this whitelist in your `.env` file, any request presenting a `Host` header that is
|
||||||
_not_ in this list will be blocked with a HTTP 400 error:
|
_not_ in this list will be blocked with a HTTP 400 error:
|
||||||
```
|
```
|
||||||
SS_ALLOWED_HOSTS="www.mysite.com,mysite.com,subdomain.mysite.com"
|
SS_ALLOWED_HOSTS="www.mysite.com,mysite.com,subdomain.mysite.com"
|
||||||
```
|
```
|
||||||
Please note that if this configuration is defined, you _must_ include _all_ subdomains (eg www.)
|
Please note that if this configuration is defined, you _must_ include _all_ subdomains (eg www.)
|
||||||
that will be accessing the site.
|
that will be accessing the site.
|
||||||
@ -625,7 +625,7 @@ into visiting external sites.
|
|||||||
In order to prevent this kind of attack, it's necessary to whitelist trusted proxy
|
In order to prevent this kind of attack, it's necessary to whitelist trusted proxy
|
||||||
server IPs using the SS_TRUSTED_PROXY_IPS define in your `.env`.
|
server IPs using the SS_TRUSTED_PROXY_IPS define in your `.env`.
|
||||||
```
|
```
|
||||||
SS_TRUSTED_PROXY_IPS="127.0.0.1,192.168.0.1"
|
SS_TRUSTED_PROXY_IPS="127.0.0.1,192.168.0.1"
|
||||||
```
|
```
|
||||||
If you wish to change the headers that are used to find the proxy information, you should reconfigure the
|
If you wish to change the headers that are used to find the proxy information, you should reconfigure the
|
||||||
TrustedProxyMiddleware service:
|
TrustedProxyMiddleware service:
|
||||||
@ -639,9 +639,9 @@ TrustedProxyMiddleware service:
|
|||||||
ProxySchemeHeaders: X-Forwarded-Protocol
|
ProxySchemeHeaders: X-Forwarded-Protocol
|
||||||
ProxyIPHeaders: X-Forwarded-Ip
|
ProxyIPHeaders: X-Forwarded-Ip
|
||||||
|
|
||||||
SS_TRUSTED_PROXY_HOST_HEADER="HTTP_X_FORWARDED_HOST"
|
SS_TRUSTED_PROXY_HOST_HEADER="HTTP_X_FORWARDED_HOST"
|
||||||
SS_TRUSTED_PROXY_IP_HEADER="HTTP_X_FORWARDED_FOR"
|
SS_TRUSTED_PROXY_IP_HEADER="HTTP_X_FORWARDED_FOR"
|
||||||
SS_TRUSTED_PROXY_PROTOCOL_HEADER="HTTP_X_FORWARDED_PROTOCOL"
|
SS_TRUSTED_PROXY_PROTOCOL_HEADER="HTTP_X_FORWARDED_PROTOCOL"
|
||||||
```
|
```
|
||||||
|
|
||||||
At the same time, you'll also need to define which headers you trust from these proxy IPs. Since there are multiple ways through which proxies can pass through HTTP information on the original hostname, IP and protocol, these values need to be adjusted for your specific proxy. The header names match their equivalent `$_SERVER` values.
|
At the same time, you'll also need to define which headers you trust from these proxy IPs. Since there are multiple ways through which proxies can pass through HTTP information on the original hostname, IP and protocol, these values need to be adjusted for your specific proxy. The header names match their equivalent `$_SERVER` values.
|
||||||
@ -655,12 +655,12 @@ This behaviour is enabled whenever `SS_TRUSTED_PROXY_IPS` is defined, or if the
|
|||||||
following in your .htaccess to ensure this behaviour is activated.
|
following in your .htaccess to ensure this behaviour is activated.
|
||||||
|
|
||||||
```
|
```
|
||||||
<IfModule mod_env.c>
|
<IfModule mod_env.c>
|
||||||
# Ensure that X-Forwarded-Host is only allowed to determine the request
|
# Ensure that X-Forwarded-Host is only allowed to determine the request
|
||||||
# hostname for servers ips defined by SS_TRUSTED_PROXY_IPS in your .env
|
# hostname for servers ips defined by SS_TRUSTED_PROXY_IPS in your .env
|
||||||
# Note that in a future release this setting will be always on.
|
# Note that in a future release this setting will be always on.
|
||||||
SetEnv BlockUntrustedIPs true
|
SetEnv BlockUntrustedIPs true
|
||||||
</IfModule>
|
</IfModule>
|
||||||
```
|
```
|
||||||
|
|
||||||
In a future release this behaviour will be changed to be on by default, and this environment
|
In a future release this behaviour will be changed to be on by default, and this environment
|
||||||
@ -672,9 +672,9 @@ variable will be no longer necessary, thus it will be necessary to always set
|
|||||||
SilverStripe recommends the use of TLS(HTTPS) for your application, and you can easily force the use through the
|
SilverStripe recommends the use of TLS(HTTPS) for your application, and you can easily force the use through the
|
||||||
director function `forceSSL()`
|
director function `forceSSL()`
|
||||||
```php
|
```php
|
||||||
if (!Director::isDev()) {
|
if (!Director::isDev()) {
|
||||||
Director::forceSSL();
|
Director::forceSSL();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Forcing HTTPS so requires a certificate to be purchased or obtained through a vendor such as
|
Forcing HTTPS so requires a certificate to be purchased or obtained through a vendor such as
|
||||||
@ -685,8 +685,8 @@ use a [secure session](https://docs.silverstripe.org/en/3/developer_guides/cooki
|
|||||||
To do this, you may set the `cookie_secure` parameter to `true` in your `config.yml` for `Session`
|
To do this, you may set the `cookie_secure` parameter to `true` in your `config.yml` for `Session`
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
Session:
|
Session:
|
||||||
cookie_secure: true
|
cookie_secure: true
|
||||||
```
|
```
|
||||||
|
|
||||||
For other cookies set by your application we should also ensure the users are provided with secure cookies by setting
|
For other cookies set by your application we should also ensure the users are provided with secure cookies by setting
|
||||||
@ -700,10 +700,10 @@ clear text and can be intercepted and stolen by an attacker who is listening on
|
|||||||
code. It is best practice to set this flag unless the application is known to use JavaScript to access these cookies
|
code. It is best practice to set this flag unless the application is known to use JavaScript to access these cookies
|
||||||
as this prevents an attacker who achieves cross-site scripting from accessing these cookies.
|
as this prevents an attacker who achieves cross-site scripting from accessing these cookies.
|
||||||
```php
|
```php
|
||||||
|
|
||||||
Cookie::set('cookie-name', 'chocolate-chip', $expiry = 30, $path = null, $domain = null, $secure = true,
|
Cookie::set('cookie-name', 'chocolate-chip', $expiry = 30, $path = null, $domain = null, $secure = true,
|
||||||
$httpOnly = false
|
$httpOnly = false
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
## Security Headers
|
## Security Headers
|
||||||
@ -729,27 +729,27 @@ unauthorised local persons. SilverStripe adds the current date for every request
|
|||||||
headers to the request for our secure controllers:
|
headers to the request for our secure controllers:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Control\HTTP;
|
use SilverStripe\Control\HTTP;
|
||||||
use SilverStripe\Control\Controller;
|
use SilverStripe\Control\Controller;
|
||||||
|
|
||||||
class MySecureController extends Controller
|
class MySecureController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
public function init()
|
public function init()
|
||||||
{
|
{
|
||||||
parent::init();
|
parent::init();
|
||||||
|
|
||||||
// Add cache headers to ensure sensitive content isn't cached.
|
// Add cache headers to ensure sensitive content isn't cached.
|
||||||
$this->response->addHeader('Cache-Control', 'max-age=0, must-revalidate, no-transform');
|
$this->response->addHeader('Cache-Control', 'max-age=0, must-revalidate, no-transform');
|
||||||
$this->response->addHeader('Pragma', 'no-cache'); // for HTTP 1.0 support
|
$this->response->addHeader('Pragma', 'no-cache'); // for HTTP 1.0 support
|
||||||
|
|
||||||
HTTP::set_cache_age(0);
|
HTTP::set_cache_age(0);
|
||||||
HTTP::add_cache_headers($this->response);
|
HTTP::add_cache_headers($this->response);
|
||||||
|
|
||||||
// Add HSTS header to force TLS for document content
|
// Add HSTS header to force TLS for document content
|
||||||
$this->response->addHeader('Strict-Transport-Security', 'max-age=86400; includeSubDomains');
|
$this->response->addHeader('Strict-Transport-Security', 'max-age=86400; includeSubDomains');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Related
|
## Related
|
||||||
|
@ -24,8 +24,8 @@ SilverStripe\Core\Injector\Injector:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$email = new Email($from, $to, $subject, $body);
|
$email = new Email($from, $to, $subject, $body);
|
||||||
$email->sendPlain();
|
$email->sendPlain();
|
||||||
```
|
```
|
||||||
|
|
||||||
### Sending combined HTML and plain text
|
### Sending combined HTML and plain text
|
||||||
@ -36,8 +36,8 @@ to `*text*`).
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$email = new Email($from, $to, $subject, $body);
|
$email = new Email($from, $to, $subject, $body);
|
||||||
$email->send();
|
$email->send();
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="info" markdown="1">
|
<div class="info" markdown="1">
|
||||||
@ -57,8 +57,8 @@ email object additional information using the `setData` and `addData` methods.
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<h1>Hi $Member.FirstName</h1>
|
<h1>Hi $Member.FirstName</h1>
|
||||||
<p>You can go to $Link.</p>
|
<p>You can go to $Link.</p>
|
||||||
```
|
```
|
||||||
|
|
||||||
The PHP Logic..
|
The PHP Logic..
|
||||||
@ -108,8 +108,8 @@ You can set the default sender address of emails through the `Email.admin_email`
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|
||||||
SilverStripe\Control\Email\Email:
|
SilverStripe\Control\Email\Email:
|
||||||
admin_email: support@silverstripe.org
|
admin_email: support@silverstripe.org
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -135,11 +135,11 @@ Configuration of those properties looks like the following:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
if(Director::isLive()) {
|
if(Director::isLive()) {
|
||||||
Config::inst()->update('Email', 'bcc_all_emails_to', "client@example.com");
|
Config::inst()->update('Email', 'bcc_all_emails_to', "client@example.com");
|
||||||
} else {
|
} else {
|
||||||
Config::inst()->update('Email', 'send_all_emails_to', "developer@example.com");
|
Config::inst()->update('Email', 'send_all_emails_to', "developer@example.com");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Setting custom "Reply To" email address.
|
### Setting custom "Reply To" email address.
|
||||||
@ -149,8 +149,8 @@ email, do the following. This is encouraged especially when the domain responsib
|
|||||||
necessarily the same which should be used for return correspondence and should help prevent your message from being
|
necessarily the same which should be used for return correspondence and should help prevent your message from being
|
||||||
marked as spam.
|
marked as spam.
|
||||||
```php
|
```php
|
||||||
$email = new Email(..);
|
$email = new Email(..);
|
||||||
$email->setReplyTo('me@address.com');
|
$email->setReplyTo('me@address.com');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Setting Custom Headers
|
### Setting Custom Headers
|
||||||
@ -160,9 +160,9 @@ For email headers which do not have getters or setters (like setTo(), setFrom())
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$email = new Email(...);
|
$email = new Email(...);
|
||||||
$email->getSwiftMessage()->getHeaders()->addTextHeader('HeaderName', 'HeaderValue');
|
$email->getSwiftMessage()->getHeaders()->addTextHeader('HeaderName', 'HeaderValue');
|
||||||
..
|
..
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="info" markdown="1">
|
<div class="info" markdown="1">
|
||||||
|
@ -28,16 +28,16 @@ You can use the CsvBulkLoader without subclassing or other customizations, if th
|
|||||||
in your CSV file match `$db` properties in your dataobject. E.g. a simple import for the
|
in your CSV file match `$db` properties in your dataobject. E.g. a simple import for the
|
||||||
[Member](api:SilverStripe\Security\Member) class could have this data in a file:
|
[Member](api:SilverStripe\Security\Member) class could have this data in a file:
|
||||||
```
|
```
|
||||||
FirstName,LastName,Email
|
FirstName,LastName,Email
|
||||||
Donald,Duck,donald@disney.com
|
Donald,Duck,donald@disney.com
|
||||||
Daisy,Duck,daisy@disney.com
|
Daisy,Duck,daisy@disney.com
|
||||||
```
|
```
|
||||||
The loader would be triggered through the `load()` method:
|
The loader would be triggered through the `load()` method:
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$loader = new CsvBulkLoader('Member');
|
$loader = new CsvBulkLoader('Member');
|
||||||
$result = $loader->load('<my-file-path>');
|
$result = $loader->load('<my-file-path>');
|
||||||
```
|
```
|
||||||
|
|
||||||
By the way, you can import [Member](api:SilverStripe\Security\Member) and [Group](api:SilverStripe\Security\Group) data through `http://localhost/admin/security`
|
By the way, you can import [Member](api:SilverStripe\Security\Member) and [Group](api:SilverStripe\Security\Group) data through `http://localhost/admin/security`
|
||||||
@ -49,19 +49,19 @@ The simplest way to use [CsvBulkLoader](api:SilverStripe\Dev\CsvBulkLoader) is t
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Admin\ModelAdmin;
|
use SilverStripe\Admin\ModelAdmin;
|
||||||
|
|
||||||
class PlayerAdmin extends ModelAdmin
|
class PlayerAdmin extends ModelAdmin
|
||||||
{
|
{
|
||||||
private static $managed_models = [
|
private static $managed_models = [
|
||||||
'Player'
|
'Player'
|
||||||
];
|
];
|
||||||
private static $model_importers = [
|
private static $model_importers = [
|
||||||
'Player' => 'CsvBulkLoader',
|
'Player' => 'CsvBulkLoader',
|
||||||
];
|
];
|
||||||
private static $url_segment = 'players';
|
private static $url_segment = 'players';
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -77,56 +77,56 @@ You'll need to add a route to your controller to make it accessible via URL
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Forms\Form;
|
use SilverStripe\Forms\Form;
|
||||||
use SilverStripe\Forms\FieldList;
|
use SilverStripe\Forms\FieldList;
|
||||||
use SilverStripe\Forms\FileField;
|
use SilverStripe\Forms\FileField;
|
||||||
use SilverStripe\Forms\FormAction;
|
use SilverStripe\Forms\FormAction;
|
||||||
use SilverStripe\Forms\RequiredFields;
|
use SilverStripe\Forms\RequiredFields;
|
||||||
use SilverStripe\Dev\CsvBulkLoader;
|
use SilverStripe\Dev\CsvBulkLoader;
|
||||||
use SilverStripe\Control\Controller;
|
use SilverStripe\Control\Controller;
|
||||||
|
|
||||||
class MyController extends Controller
|
class MyController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $allowed_actions = ['Form'];
|
private static $allowed_actions = ['Form'];
|
||||||
|
|
||||||
protected $template = "BlankPage";
|
protected $template = "BlankPage";
|
||||||
|
|
||||||
public function Link($action = null)
|
public function Link($action = null)
|
||||||
{
|
{
|
||||||
return Controller::join_links('MyController', $action);
|
return Controller::join_links('MyController', $action);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function Form()
|
public function Form()
|
||||||
{
|
{
|
||||||
$form = new Form(
|
$form = new Form(
|
||||||
$this,
|
$this,
|
||||||
'Form',
|
'Form',
|
||||||
new FieldList(
|
new FieldList(
|
||||||
new FileField('CsvFile', false)
|
new FileField('CsvFile', false)
|
||||||
),
|
),
|
||||||
new FieldList(
|
new FieldList(
|
||||||
new FormAction('doUpload', 'Upload')
|
new FormAction('doUpload', 'Upload')
|
||||||
),
|
),
|
||||||
new RequiredFields()
|
new RequiredFields()
|
||||||
);
|
);
|
||||||
return $form;
|
return $form;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function doUpload($data, $form)
|
public function doUpload($data, $form)
|
||||||
{
|
{
|
||||||
$loader = new CsvBulkLoader('MyDataObject');
|
$loader = new CsvBulkLoader('MyDataObject');
|
||||||
$results = $loader->load($_FILES['CsvFile']['tmp_name']);
|
$results = $loader->load($_FILES['CsvFile']['tmp_name']);
|
||||||
$messages = [];
|
$messages = [];
|
||||||
if($results->CreatedCount()) $messages[] = sprintf('Imported %d items', $results->CreatedCount());
|
if($results->CreatedCount()) $messages[] = sprintf('Imported %d items', $results->CreatedCount());
|
||||||
if($results->UpdatedCount()) $messages[] = sprintf('Updated %d items', $results->UpdatedCount());
|
if($results->UpdatedCount()) $messages[] = sprintf('Updated %d items', $results->UpdatedCount());
|
||||||
if($results->DeletedCount()) $messages[] = sprintf('Deleted %d items', $results->DeletedCount());
|
if($results->DeletedCount()) $messages[] = sprintf('Deleted %d items', $results->DeletedCount());
|
||||||
if(!$messages) $messages[] = 'No changes';
|
if(!$messages) $messages[] = 'No changes';
|
||||||
$form->sessionMessage(implode(', ', $messages), 'good');
|
$form->sessionMessage(implode(', ', $messages), 'good');
|
||||||
|
|
||||||
return $this->redirectBack();
|
return $this->redirectBack();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -139,31 +139,31 @@ We're going to use our knowledge from the previous example to import a more soph
|
|||||||
|
|
||||||
Sample CSV Content
|
Sample CSV Content
|
||||||
```
|
```
|
||||||
"Number","Name","Birthday","Team"
|
"Number","Name","Birthday","Team"
|
||||||
11,"John Doe",1982-05-12,"FC Bayern"
|
11,"John Doe",1982-05-12,"FC Bayern"
|
||||||
12,"Jane Johnson", 1982-05-12,"FC Bayern"
|
12,"Jane Johnson", 1982-05-12,"FC Bayern"
|
||||||
13,"Jimmy Dole",,"Schalke 04"
|
13,"Jimmy Dole",,"Schalke 04"
|
||||||
```
|
```
|
||||||
|
|
||||||
Datamodel for Player
|
Datamodel for Player
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Player extends DataObject
|
class Player extends DataObject
|
||||||
{
|
{
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'PlayerNumber' => 'Int',
|
'PlayerNumber' => 'Int',
|
||||||
'FirstName' => 'Text',
|
'FirstName' => 'Text',
|
||||||
'LastName' => 'Text',
|
'LastName' => 'Text',
|
||||||
'Birthday' => 'Date',
|
'Birthday' => 'Date',
|
||||||
];
|
];
|
||||||
private static $has_one = [
|
private static $has_one = [
|
||||||
'Team' => 'FootballTeam'
|
'Team' => 'FootballTeam'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -171,18 +171,18 @@ Datamodel for FootballTeam:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class FootballTeam extends DataObject
|
class FootballTeam extends DataObject
|
||||||
{
|
{
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Title' => 'Text',
|
'Title' => 'Text',
|
||||||
];
|
];
|
||||||
private static $has_many = [
|
private static $has_many = [
|
||||||
'Players' => 'Player'
|
'Players' => 'Player'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -193,38 +193,38 @@ Sample implementation of a custom loader. Assumes a CSV-file in a certain format
|
|||||||
* Avoids duplicate imports by a custom `$duplicateChecks` definition
|
* Avoids duplicate imports by a custom `$duplicateChecks` definition
|
||||||
* Creates `Team` relations automatically based on the `Gruppe` column in the CSV data
|
* Creates `Team` relations automatically based on the `Gruppe` column in the CSV data
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Dev\CsvBulkLoader;
|
use SilverStripe\Dev\CsvBulkLoader;
|
||||||
|
|
||||||
class PlayerCsvBulkLoader extends CsvBulkLoader
|
class PlayerCsvBulkLoader extends CsvBulkLoader
|
||||||
{
|
{
|
||||||
public $columnMap = [
|
public $columnMap = [
|
||||||
'Number' => 'PlayerNumber',
|
'Number' => 'PlayerNumber',
|
||||||
'Name' => '->importFirstAndLastName',
|
'Name' => '->importFirstAndLastName',
|
||||||
'Birthday' => 'Birthday',
|
'Birthday' => 'Birthday',
|
||||||
'Team' => 'Team.Title',
|
'Team' => 'Team.Title',
|
||||||
];
|
];
|
||||||
public $duplicateChecks = [
|
public $duplicateChecks = [
|
||||||
'Number' => 'PlayerNumber'
|
'Number' => 'PlayerNumber'
|
||||||
];
|
];
|
||||||
public $relationCallbacks = [
|
public $relationCallbacks = [
|
||||||
'Team.Title' => [
|
'Team.Title' => [
|
||||||
'relationname' => 'Team',
|
'relationname' => 'Team',
|
||||||
'callback' => 'getTeamByTitle'
|
'callback' => 'getTeamByTitle'
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
public static function importFirstAndLastName(&$obj, $val, $record)
|
public static function importFirstAndLastName(&$obj, $val, $record)
|
||||||
{
|
{
|
||||||
$parts = explode(' ', $val);
|
$parts = explode(' ', $val);
|
||||||
if(count($parts) != 2) return false;
|
if(count($parts) != 2) return false;
|
||||||
$obj->FirstName = $parts[0];
|
$obj->FirstName = $parts[0];
|
||||||
$obj->LastName = $parts[1];
|
$obj->LastName = $parts[1];
|
||||||
}
|
}
|
||||||
public static function getTeamByTitle(&$obj, $val, $record)
|
public static function getTeamByTitle(&$obj, $val, $record)
|
||||||
{
|
{
|
||||||
return FootballTeam::get()->filter('Title', $val)->First();
|
return FootballTeam::get()->filter('Title', $val)->First();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -232,19 +232,19 @@ Building off of the ModelAdmin example up top, use a custom loader instead of th
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Admin\ModelAdmin;
|
use SilverStripe\Admin\ModelAdmin;
|
||||||
|
|
||||||
class PlayerAdmin extends ModelAdmin
|
class PlayerAdmin extends ModelAdmin
|
||||||
{
|
{
|
||||||
private static $managed_models = [
|
private static $managed_models = [
|
||||||
'Player'
|
'Player'
|
||||||
];
|
];
|
||||||
private static $model_importers = [
|
private static $model_importers = [
|
||||||
'Player' => 'PlayerCsvBulkLoader',
|
'Player' => 'PlayerCsvBulkLoader',
|
||||||
];
|
];
|
||||||
private static $url_segment = 'players';
|
private static $url_segment = 'players';
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -24,25 +24,25 @@ An outline of step one looks like:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$feed = new RSSFeed(
|
$feed = new RSSFeed(
|
||||||
$list,
|
$list,
|
||||||
$link,
|
$link,
|
||||||
$title,
|
$title,
|
||||||
$description,
|
$description,
|
||||||
$titleField,
|
$titleField,
|
||||||
$descriptionField,
|
$descriptionField,
|
||||||
$authorField,
|
$authorField,
|
||||||
$lastModifiedTime,
|
$lastModifiedTime,
|
||||||
$etag
|
$etag
|
||||||
);
|
);
|
||||||
|
|
||||||
$feed->outputToBrowser();
|
$feed->outputToBrowser();
|
||||||
```
|
```
|
||||||
|
|
||||||
To achieve step two include the following code where ever you want to include the `<link>` tag to the RSS Feed. This
|
To achieve step two include the following code where ever you want to include the `<link>` tag to the RSS Feed. This
|
||||||
will normally go in your `Controllers` `init` method.
|
will normally go in your `Controllers` `init` method.
|
||||||
```php
|
```php
|
||||||
RSSFeed::linkToFeed($link, $title);
|
RSSFeed::linkToFeed($link, $title);
|
||||||
```
|
```
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
@ -56,43 +56,43 @@ You can use [RSSFeed](api:SilverStripe\Control\RSS\RSSFeed) to easily create a f
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Control\RSS\RSSFeed;
|
use SilverStripe\Control\RSS\RSSFeed;
|
||||||
use Page;
|
use Page;
|
||||||
use SilverStripe\CMS\Controllers\ContentController;
|
use SilverStripe\CMS\Controllers\ContentController;
|
||||||
|
|
||||||
|
|
||||||
..
|
..
|
||||||
class PageController extends ContentController
|
class PageController extends ContentController
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $allowed_actions = [
|
private static $allowed_actions = [
|
||||||
'rss'
|
'rss'
|
||||||
];
|
];
|
||||||
|
|
||||||
public function init()
|
public function init()
|
||||||
{
|
{
|
||||||
parent::init();
|
parent::init();
|
||||||
|
|
||||||
RSSFeed::linkToFeed($this->Link() . "rss", "10 Most Recently Updated Pages");
|
RSSFeed::linkToFeed($this->Link() . "rss", "10 Most Recently Updated Pages");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function rss()
|
public function rss()
|
||||||
{
|
{
|
||||||
$rss = new RSSFeed(
|
$rss = new RSSFeed(
|
||||||
$this->LatestUpdates(),
|
$this->LatestUpdates(),
|
||||||
$this->Link(),
|
$this->Link(),
|
||||||
"10 Most Recently Updated Pages",
|
"10 Most Recently Updated Pages",
|
||||||
"Shows a list of the 10 most recently updated pages."
|
"Shows a list of the 10 most recently updated pages."
|
||||||
);
|
);
|
||||||
|
|
||||||
return $rss->outputToBrowser();
|
return $rss->outputToBrowser();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function LatestUpdates()
|
public function LatestUpdates()
|
||||||
{
|
{
|
||||||
return Page::get()->sort("LastEdited", "DESC")->limit(10);
|
return Page::get()->sort("LastEdited", "DESC")->limit(10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -110,58 +110,58 @@ method is defined and returns a string to the full website URL.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Control\Controller;
|
use SilverStripe\Control\Controller;
|
||||||
use SilverStripe\Control\Director;
|
use SilverStripe\Control\Director;
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Player extends DataObject
|
class Player extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
public function AbsoluteLink()
|
public function AbsoluteLink()
|
||||||
{
|
{
|
||||||
// assumes players can be accessed at yoursite.com/players/2
|
// assumes players can be accessed at yoursite.com/players/2
|
||||||
|
|
||||||
return Controller::join_links(
|
return Controller::join_links(
|
||||||
Director::absoluteBaseUrl(),
|
Director::absoluteBaseUrl(),
|
||||||
'players',
|
'players',
|
||||||
$this->ID
|
$this->ID
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Then in our controller, we add a new action which returns a the XML list of `Players`.
|
Then in our controller, we add a new action which returns a the XML list of `Players`.
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Control\RSS\RSSFeed;
|
use SilverStripe\Control\RSS\RSSFeed;
|
||||||
use SilverStripe\CMS\Controllers\ContentController;
|
use SilverStripe\CMS\Controllers\ContentController;
|
||||||
|
|
||||||
class PageController extends ContentController
|
class PageController extends ContentController
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $allowed_actions = [
|
private static $allowed_actions = [
|
||||||
'players'
|
'players'
|
||||||
];
|
];
|
||||||
|
|
||||||
public function init()
|
public function init()
|
||||||
{
|
{
|
||||||
parent::init();
|
parent::init();
|
||||||
|
|
||||||
RSSFeed::linkToFeed($this->Link("players"), "Players");
|
RSSFeed::linkToFeed($this->Link("players"), "Players");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function players()
|
public function players()
|
||||||
{
|
{
|
||||||
$rss = new RSSFeed(
|
$rss = new RSSFeed(
|
||||||
Player::get(),
|
Player::get(),
|
||||||
$this->Link("players"),
|
$this->Link("players"),
|
||||||
"Players"
|
"Players"
|
||||||
);
|
);
|
||||||
|
|
||||||
return $rss->outputToBrowser();
|
return $rss->outputToBrowser();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -177,22 +177,22 @@ Say from that last example we want to include the Players Team in the XML feed w
|
|||||||
|
|
||||||
```xml
|
```xml
|
||||||
|
|
||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom">
|
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||||
<channel>
|
<channel>
|
||||||
<title>$Title</title>
|
<title>$Title</title>
|
||||||
<link>$Link</link>
|
<link>$Link</link>
|
||||||
<atom:link href="$Link" rel="self" type="application/rss+xml" />
|
<atom:link href="$Link" rel="self" type="application/rss+xml" />
|
||||||
<description>$Description.XML</description>
|
<description>$Description.XML</description>
|
||||||
|
|
||||||
<% loop $Entries %>
|
<% loop $Entries %>
|
||||||
<item>
|
<item>
|
||||||
<title>$Title.XML</title>
|
<title>$Title.XML</title>
|
||||||
<team>$Team.Title</team>
|
<team>$Team.Title</team>
|
||||||
</item>
|
</item>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</channel>
|
</channel>
|
||||||
</rss>
|
</rss>
|
||||||
```
|
```
|
||||||
|
|
||||||
`setTemplate` can then be used to tell RSSFeed to use that new template.
|
`setTemplate` can then be used to tell RSSFeed to use that new template.
|
||||||
@ -201,18 +201,18 @@ Say from that last example we want to include the Players Team in the XML feed w
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function players()
|
public function players()
|
||||||
{
|
{
|
||||||
$rss = new RSSFeed(
|
$rss = new RSSFeed(
|
||||||
Player::get(),
|
Player::get(),
|
||||||
$this->Link("players"),
|
$this->Link("players"),
|
||||||
"Players"
|
"Players"
|
||||||
);
|
);
|
||||||
|
|
||||||
$rss->setTemplate('PlayersRss');
|
$rss->setTemplate('PlayersRss');
|
||||||
|
|
||||||
return $rss->outputToBrowser();
|
return $rss->outputToBrowser();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="warning">
|
<div class="warning">
|
||||||
|
@ -8,71 +8,71 @@ form (which is used for `MyDataObject` instances). You can access it through
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Forms\Form;
|
use SilverStripe\Forms\Form;
|
||||||
use SilverStripe\Forms\FieldList;
|
use SilverStripe\Forms\FieldList;
|
||||||
use SilverStripe\Forms\FileField;
|
use SilverStripe\Forms\FileField;
|
||||||
use SilverStripe\Forms\FormAction;
|
use SilverStripe\Forms\FormAction;
|
||||||
use SilverStripe\Forms\RequiredFields;
|
use SilverStripe\Forms\RequiredFields;
|
||||||
use SilverStripe\Dev\CsvBulkLoader;
|
use SilverStripe\Dev\CsvBulkLoader;
|
||||||
use SilverStripe\Control\Controller;
|
use SilverStripe\Control\Controller;
|
||||||
|
|
||||||
class MyController extends Controller
|
class MyController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $allowed_actions = [
|
private static $allowed_actions = [
|
||||||
'Form'
|
'Form'
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $template = "BlankPage";
|
protected $template = "BlankPage";
|
||||||
|
|
||||||
public function Link($action = null)
|
public function Link($action = null)
|
||||||
{
|
{
|
||||||
return Controller::join_links('MyController', $action);
|
return Controller::join_links('MyController', $action);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function Form()
|
public function Form()
|
||||||
{
|
{
|
||||||
$form = new Form(
|
$form = new Form(
|
||||||
$this,
|
$this,
|
||||||
'Form',
|
'Form',
|
||||||
new FieldList(
|
new FieldList(
|
||||||
new FileField('CsvFile', false)
|
new FileField('CsvFile', false)
|
||||||
),
|
),
|
||||||
new FieldList(
|
new FieldList(
|
||||||
new FormAction('doUpload', 'Upload')
|
new FormAction('doUpload', 'Upload')
|
||||||
),
|
),
|
||||||
new RequiredFields()
|
new RequiredFields()
|
||||||
);
|
);
|
||||||
return $form;
|
return $form;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function doUpload($data, $form)
|
public function doUpload($data, $form)
|
||||||
{
|
{
|
||||||
$loader = new CsvBulkLoader('MyDataObject');
|
$loader = new CsvBulkLoader('MyDataObject');
|
||||||
$results = $loader->load($_FILES['CsvFile']['tmp_name']);
|
$results = $loader->load($_FILES['CsvFile']['tmp_name']);
|
||||||
$messages = [];
|
$messages = [];
|
||||||
|
|
||||||
if($results->CreatedCount()) {
|
if($results->CreatedCount()) {
|
||||||
$messages[] = sprintf('Imported %d items', $results->CreatedCount());
|
$messages[] = sprintf('Imported %d items', $results->CreatedCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
if($results->UpdatedCount()) {
|
if($results->UpdatedCount()) {
|
||||||
$messages[] = sprintf('Updated %d items', $results->UpdatedCount());
|
$messages[] = sprintf('Updated %d items', $results->UpdatedCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
if($results->DeletedCount()) {
|
if($results->DeletedCount()) {
|
||||||
$messages[] = sprintf('Deleted %d items', $results->DeletedCount());
|
$messages[] = sprintf('Deleted %d items', $results->DeletedCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!$messages) {
|
if(!$messages) {
|
||||||
$messages[] = 'No changes';
|
$messages[] = 'No changes';
|
||||||
}
|
}
|
||||||
|
|
||||||
$form->sessionMessage(implode(', ', $messages), 'good');
|
$form->sessionMessage(implode(', ', $messages), 'good');
|
||||||
|
|
||||||
return $this->redirectBack();
|
return $this->redirectBack();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -5,10 +5,10 @@ title: A custom CSVBulkLoader instance
|
|||||||
A an implementation of a custom `CSVBulkLoader` loader. In this example. we're provided with a unique CSV file
|
A an implementation of a custom `CSVBulkLoader` loader. In this example. we're provided with a unique CSV file
|
||||||
containing a list of football players and the team they play for. The file we have is in the format like below.
|
containing a list of football players and the team they play for. The file we have is in the format like below.
|
||||||
```
|
```
|
||||||
"SpielerNummer", "Name", "Geburtsdatum", "Gruppe"
|
"SpielerNummer", "Name", "Geburtsdatum", "Gruppe"
|
||||||
11, "John Doe", 1982-05-12,"FC Bayern"
|
11, "John Doe", 1982-05-12,"FC Bayern"
|
||||||
12, "Jane Johnson", 1982-05-12,"FC Bayern"
|
12, "Jane Johnson", 1982-05-12,"FC Bayern"
|
||||||
13, "Jimmy Dole",,"Schalke 04"
|
13, "Jimmy Dole",,"Schalke 04"
|
||||||
```
|
```
|
||||||
This data needs to be imported into our application. For this, we have two `DataObjects` setup. `Player` contains
|
This data needs to be imported into our application. For this, we have two `DataObjects` setup. `Player` contains
|
||||||
information about the individual player and a relation set up for managing the `Team`.
|
information about the individual player and a relation set up for managing the `Team`.
|
||||||
@ -17,22 +17,22 @@ information about the individual player and a relation set up for managing the `
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Player extends DataObject
|
class Player extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'PlayerNumber' => 'Int',
|
'PlayerNumber' => 'Int',
|
||||||
'FirstName' => 'Text',
|
'FirstName' => 'Text',
|
||||||
'LastName' => 'Text',
|
'LastName' => 'Text',
|
||||||
'Birthday' => 'Date'
|
'Birthday' => 'Date'
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $has_one = [
|
private static $has_one = [
|
||||||
'Team' => 'FootballTeam'
|
'Team' => 'FootballTeam'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -40,19 +40,19 @@ information about the individual player and a relation set up for managing the `
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class FootballTeam extends DataObject
|
class FootballTeam extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Title' => 'Text'
|
'Title' => 'Text'
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $has_many = [
|
private static $has_many = [
|
||||||
'Players' => 'Player'
|
'Players' => 'Player'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -72,42 +72,42 @@ Our final import looks like this.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Dev\CsvBulkLoader;
|
use SilverStripe\Dev\CsvBulkLoader;
|
||||||
|
|
||||||
class PlayerCsvBulkLoader extends CsvBulkLoader
|
class PlayerCsvBulkLoader extends CsvBulkLoader
|
||||||
{
|
{
|
||||||
|
|
||||||
public $columnMap = [
|
public $columnMap = [
|
||||||
'Number' => 'PlayerNumber',
|
'Number' => 'PlayerNumber',
|
||||||
'Name' => '->importFirstAndLastName',
|
'Name' => '->importFirstAndLastName',
|
||||||
'Geburtsdatum' => 'Birthday',
|
'Geburtsdatum' => 'Birthday',
|
||||||
'Gruppe' => 'Team.Title',
|
'Gruppe' => 'Team.Title',
|
||||||
];
|
];
|
||||||
|
|
||||||
public $duplicateChecks = [
|
public $duplicateChecks = [
|
||||||
'SpielerNummer' => 'PlayerNumber'
|
'SpielerNummer' => 'PlayerNumber'
|
||||||
];
|
];
|
||||||
|
|
||||||
public $relationCallbacks = [
|
public $relationCallbacks = [
|
||||||
'Team.Title' => [
|
'Team.Title' => [
|
||||||
'relationname' => 'Team',
|
'relationname' => 'Team',
|
||||||
'callback' => 'getTeamByTitle'
|
'callback' => 'getTeamByTitle'
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
public static function importFirstAndLastName(&$obj, $val, $record)
|
public static function importFirstAndLastName(&$obj, $val, $record)
|
||||||
{
|
{
|
||||||
$parts = explode(' ', $val);
|
$parts = explode(' ', $val);
|
||||||
if(count($parts) != 2) return false;
|
if(count($parts) != 2) return false;
|
||||||
$obj->FirstName = $parts[0];
|
$obj->FirstName = $parts[0];
|
||||||
$obj->LastName = $parts[1];
|
$obj->LastName = $parts[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getTeamByTitle(&$obj, $val, $record)
|
public static function getTeamByTitle(&$obj, $val, $record)
|
||||||
{
|
{
|
||||||
return FootballTeam::get()->filter('Title', $val)->First();
|
return FootballTeam::get()->filter('Title', $val)->First();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -11,35 +11,35 @@ First, we write the code to query the API feed.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function getWellingtonWeather()
|
public function getWellingtonWeather()
|
||||||
{
|
{
|
||||||
$fetch = new RestfulService(
|
$fetch = new RestfulService(
|
||||||
'https://query.yahooapis.com/v1/public/yql'
|
'https://query.yahooapis.com/v1/public/yql'
|
||||||
);
|
);
|
||||||
|
|
||||||
$fetch->setQueryString([
|
$fetch->setQueryString([
|
||||||
'q' => 'select * from weather.forecast where woeid in (select woeid from geo.places(1) where text="Wellington, NZ")'
|
'q' => 'select * from weather.forecast where woeid in (select woeid from geo.places(1) where text="Wellington, NZ")'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// perform the query
|
// perform the query
|
||||||
$conn = $fetch->request();
|
$conn = $fetch->request();
|
||||||
|
|
||||||
// parse the XML body
|
// parse the XML body
|
||||||
$msgs = $fetch->getValues($conn->getBody(), "results");
|
$msgs = $fetch->getValues($conn->getBody(), "results");
|
||||||
|
|
||||||
// generate an object our templates can read
|
// generate an object our templates can read
|
||||||
$output = new ArrayList();
|
$output = new ArrayList();
|
||||||
|
|
||||||
if($msgs) {
|
if($msgs) {
|
||||||
foreach($msgs as $msg) {
|
foreach($msgs as $msg) {
|
||||||
$output->push(new ArrayData([
|
$output->push(new ArrayData([
|
||||||
'Description' => Convert::xml2raw($msg->channel_item_description)
|
'Description' => Convert::xml2raw($msg->channel_item_description)
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -51,11 +51,11 @@ single field `Description`.
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% if WellingtonWeather %>
|
<% if WellingtonWeather %>
|
||||||
<% loop WellingtonWeather %>
|
<% loop WellingtonWeather %>
|
||||||
$Description
|
$Description
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Related
|
## Related
|
||||||
|
@ -20,16 +20,16 @@ Defining search-able fields on your DataObject.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class MyDataObject extends DataObject
|
class MyDataObject extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $searchable_fields = [
|
private static $searchable_fields = [
|
||||||
'Name',
|
'Name',
|
||||||
'ProductCode'
|
'ProductCode'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -41,38 +41,38 @@ and `MyDate`. The attribute `HiddenProperty` should not be searchable, and `MyDa
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\Filters\PartialMatchFilter;
|
use SilverStripe\ORM\Filters\PartialMatchFilter;
|
||||||
use SilverStripe\ORM\Filters\GreaterThanFilter;
|
use SilverStripe\ORM\Filters\GreaterThanFilter;
|
||||||
use SilverStripe\ORM\Search\SearchContext;
|
use SilverStripe\ORM\Search\SearchContext;
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class MyDataObject extends DataObject
|
class MyDataObject extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'PublicProperty' => 'Text'
|
'PublicProperty' => 'Text'
|
||||||
'HiddenProperty' => 'Text',
|
'HiddenProperty' => 'Text',
|
||||||
'MyDate' => 'Date'
|
'MyDate' => 'Date'
|
||||||
];
|
];
|
||||||
|
|
||||||
public function getDefaultSearchContext()
|
public function getDefaultSearchContext()
|
||||||
{
|
{
|
||||||
$fields = $this->scaffoldSearchFields([
|
$fields = $this->scaffoldSearchFields([
|
||||||
'restrictFields' => ['PublicProperty','MyDate']
|
'restrictFields' => ['PublicProperty','MyDate']
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$filters = [
|
$filters = [
|
||||||
'PublicProperty' => new PartialMatchFilter('PublicProperty'),
|
'PublicProperty' => new PartialMatchFilter('PublicProperty'),
|
||||||
'MyDate' => new GreaterThanFilter('MyDate')
|
'MyDate' => new GreaterThanFilter('MyDate')
|
||||||
];
|
];
|
||||||
|
|
||||||
return new SearchContext(
|
return new SearchContext(
|
||||||
$this->class,
|
$this->class,
|
||||||
$fields,
|
$fields,
|
||||||
$filters
|
$filters
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -90,41 +90,41 @@ the `$fields` constructor parameter.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Forms\Form;
|
use SilverStripe\Forms\Form;
|
||||||
use SilverStripe\Forms\FieldList;
|
use SilverStripe\Forms\FieldList;
|
||||||
use SilverStripe\Forms\FormAction;
|
use SilverStripe\Forms\FormAction;
|
||||||
use SilverStripe\CMS\Controllers\ContentController;
|
use SilverStripe\CMS\Controllers\ContentController;
|
||||||
|
|
||||||
|
|
||||||
// ..
|
// ..
|
||||||
class PageController extends ContentController
|
class PageController extends ContentController
|
||||||
{
|
{
|
||||||
|
|
||||||
public function SearchForm()
|
public function SearchForm()
|
||||||
{
|
{
|
||||||
$context = singleton('MyDataObject')->getCustomSearchContext();
|
$context = singleton('MyDataObject')->getCustomSearchContext();
|
||||||
$fields = $context->getSearchFields();
|
$fields = $context->getSearchFields();
|
||||||
|
|
||||||
$form = new Form($this, "SearchForm",
|
$form = new Form($this, "SearchForm",
|
||||||
$fields,
|
$fields,
|
||||||
new FieldList(
|
new FieldList(
|
||||||
new FormAction('doSearch')
|
new FormAction('doSearch')
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
return $form;
|
return $form;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function doSearch($data, $form)
|
public function doSearch($data, $form)
|
||||||
{
|
{
|
||||||
$context = singleton('MyDataObject')->getCustomSearchContext();
|
$context = singleton('MyDataObject')->getCustomSearchContext();
|
||||||
$results = $context->getResults($data);
|
$results = $context->getResults($data);
|
||||||
|
|
||||||
return $this->customise([
|
return $this->customise([
|
||||||
'Results' => $results
|
'Results' => $results
|
||||||
])->renderWith('Page_results');
|
])->renderWith('Page_results');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -137,24 +137,24 @@ in order to read page limit information. It is also passed the current
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function getResults($searchCriteria = [])
|
public function getResults($searchCriteria = [])
|
||||||
{
|
{
|
||||||
$start = ($this->getRequest()->getVar('start')) ? (int)$this->getRequest()->getVar('start') : 0;
|
$start = ($this->getRequest()->getVar('start')) ? (int)$this->getRequest()->getVar('start') : 0;
|
||||||
$limit = 10;
|
$limit = 10;
|
||||||
|
|
||||||
$context = singleton('MyDataObject')->getCustomSearchContext();
|
$context = singleton('MyDataObject')->getCustomSearchContext();
|
||||||
$query = $context->getQuery($searchCriteria, null, ['start'=>$start,'limit'=>$limit]);
|
$query = $context->getQuery($searchCriteria, null, ['start'=>$start,'limit'=>$limit]);
|
||||||
$records = $context->getResults($searchCriteria, null, ['start'=>$start,'limit'=>$limit]);
|
$records = $context->getResults($searchCriteria, null, ['start'=>$start,'limit'=>$limit]);
|
||||||
|
|
||||||
if($records) {
|
if($records) {
|
||||||
$records = new PaginatedList($records, $this->getRequest());
|
$records = new PaginatedList($records, $this->getRequest());
|
||||||
$records->setPageStart($start);
|
$records->setPageStart($start);
|
||||||
$records->setPageLength($limit);
|
$records->setPageLength($limit);
|
||||||
$records->setTotalItems($query->unlimitedRowCount());
|
$records->setTotalItems($query->unlimitedRowCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
return $records;
|
return $records;
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -162,14 +162,14 @@ notice that if you want to use this getResults function, you need to change the
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function doSearch($data, $form)
|
public function doSearch($data, $form)
|
||||||
{
|
{
|
||||||
$context = singleton('MyDataObject')->getCustomSearchContext();
|
$context = singleton('MyDataObject')->getCustomSearchContext();
|
||||||
$results = $this->getResults($data);
|
$results = $this->getResults($data);
|
||||||
return $this->customise([
|
return $this->customise([
|
||||||
'Results' => $results
|
'Results' => $results
|
||||||
])->renderWith(['Catalogo_results', 'Page']);
|
])->renderWith(['Catalogo_results', 'Page']);
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -189,43 +189,43 @@ Results.PaginationSummary(4) defines how many pages the search will show in the
|
|||||||
**Next 1 2 *3* 4 5 … 558**
|
**Next 1 2 *3* 4 5 … 558**
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% if $Results %>
|
<% if $Results %>
|
||||||
<ul>
|
<ul>
|
||||||
<% loop $Results %>
|
<% loop $Results %>
|
||||||
<li>$Title, $Autor</li>
|
<li>$Title, $Autor</li>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</ul>
|
</ul>
|
||||||
<% else %>
|
<% else %>
|
||||||
<p>Sorry, your search query did not return any results.</p>
|
<p>Sorry, your search query did not return any results.</p>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
|
|
||||||
<% if $Results.MoreThanOnePage %>
|
<% if $Results.MoreThanOnePage %>
|
||||||
<div id="PageNumbers">
|
<div id="PageNumbers">
|
||||||
<p>
|
<p>
|
||||||
<% if $Results.NotFirstPage %>
|
<% if $Results.NotFirstPage %>
|
||||||
<a class="prev" href="$Results.PrevLink" title="View the previous page">Prev</a>
|
<a class="prev" href="$Results.PrevLink" title="View the previous page">Prev</a>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
|
|
||||||
<span>
|
<span>
|
||||||
<% loop $Results.PaginationSummary(4) %>
|
<% loop $Results.PaginationSummary(4) %>
|
||||||
<% if $CurrentBool %>
|
<% if $CurrentBool %>
|
||||||
$PageNum
|
$PageNum
|
||||||
<% else %>
|
<% else %>
|
||||||
<% if $Link %>
|
<% if $Link %>
|
||||||
<a href="$Link" title="View page number $PageNum">$PageNum</a>
|
<a href="$Link" title="View page number $PageNum">$PageNum</a>
|
||||||
<% else %>
|
<% else %>
|
||||||
…
|
…
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<% if $Results.NotLastPage %>
|
<% if $Results.NotLastPage %>
|
||||||
<a class="next" href="$Results.NextLink" title="View the next page">Next</a>
|
<a class="next" href="$Results.NextLink" title="View the next page">Next</a>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Available SearchFilters
|
## Available SearchFilters
|
||||||
|
@ -23,15 +23,15 @@ You can do so by adding this static variable to your class definition:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class MyDataObject extends DataObject
|
class MyDataObject extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $create_table_options = [
|
private static $create_table_options = [
|
||||||
'MySQLDatabase' => 'ENGINE=MyISAM'
|
'MySQLDatabase' => 'ENGINE=MyISAM'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -52,28 +52,28 @@ Example DataObject:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class SearchableDataObject extends DataObject
|
class SearchableDataObject extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
"Title" => "Varchar(255)",
|
"Title" => "Varchar(255)",
|
||||||
"Content" => "HTMLText",
|
"Content" => "HTMLText",
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $indexes = [
|
private static $indexes = [
|
||||||
'SearchFields' => [
|
'SearchFields' => [
|
||||||
'type' => 'fulltext',
|
'type' => 'fulltext',
|
||||||
'columns' => ['Title', 'Content'],
|
'columns' => ['Title', 'Content'],
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $create_table_options = [
|
private static $create_table_options = [
|
||||||
'MySQLDatabase' => 'ENGINE=MyISAM'
|
'MySQLDatabase' => 'ENGINE=MyISAM'
|
||||||
];
|
];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ Performing the search:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
SearchableDataObject::get()->filter('SearchFields:Fulltext', 'search term');
|
SearchableDataObject::get()->filter('SearchFields:Fulltext', 'search term');
|
||||||
```
|
```
|
||||||
|
|
||||||
If your search index is a single field size, then you may also specify the search filter by the name of the
|
If your search index is a single field size, then you may also specify the search filter by the name of the
|
||||||
|
@ -29,9 +29,9 @@ you want to set.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// mysite/_config.php
|
// mysite/_config.php
|
||||||
i18n::set_locale('de_DE'); // Setting the locale to German (Germany)
|
i18n::set_locale('de_DE'); // Setting the locale to German (Germany)
|
||||||
i18n::set_locale('ca_AD'); // Setting to Catalan (Andorra)
|
i18n::set_locale('ca_AD'); // Setting to Catalan (Andorra)
|
||||||
```
|
```
|
||||||
|
|
||||||
Once we set a locale, all the calls to the translator function will return strings according to the set locale value, if
|
Once we set a locale, all the calls to the translator function will return strings according to the set locale value, if
|
||||||
@ -55,11 +55,11 @@ To let browsers know which language they're displaying a document in, you can de
|
|||||||
|
|
||||||
```html
|
```html
|
||||||
|
|
||||||
//'Page.ss' (HTML)
|
//'Page.ss' (HTML)
|
||||||
<html lang="$ContentLocale">
|
<html lang="$ContentLocale">
|
||||||
|
|
||||||
//'Page.ss' (XHTML)
|
//'Page.ss' (XHTML)
|
||||||
<html lang="$ContentLocale" xml:lang="$ContentLocale" xmlns="http://www.w3.org/1999/xhtml">
|
<html lang="$ContentLocale" xml:lang="$ContentLocale" xmlns="http://www.w3.org/1999/xhtml">
|
||||||
```
|
```
|
||||||
|
|
||||||
Setting the `<html>` attribute is the most commonly used technique. There are other ways to specify content languages
|
Setting the `<html>` attribute is the most commonly used technique. There are other ways to specify content languages
|
||||||
@ -72,7 +72,7 @@ and default alignment of paragraphs and tables to browsers.
|
|||||||
|
|
||||||
```html
|
```html
|
||||||
|
|
||||||
<html lang="$ContentLocale" dir="$i18nScriptDirection">
|
<html lang="$ContentLocale" dir="$i18nScriptDirection">
|
||||||
```
|
```
|
||||||
|
|
||||||
### Date and time formats
|
### Date and time formats
|
||||||
@ -82,8 +82,8 @@ You can use these settings for your own view logic.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
Config::inst()->update('i18n', 'date_format', 'dd.MM.yyyy');
|
Config::inst()->update('i18n', 'date_format', 'dd.MM.yyyy');
|
||||||
Config::inst()->update('i18n', 'time_format', 'HH:mm');
|
Config::inst()->update('i18n', 'time_format', 'HH:mm');
|
||||||
```
|
```
|
||||||
|
|
||||||
Localization in SilverStripe uses PHP's [intl extension](http://php.net/intl).
|
Localization in SilverStripe uses PHP's [intl extension](http://php.net/intl).
|
||||||
@ -107,11 +107,11 @@ In order to add a value, add the following to your `config.yml`:
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
i18n:
|
i18n:
|
||||||
common_locales:
|
common_locales:
|
||||||
de_CGN:
|
de_CGN:
|
||||||
name: German (Cologne)
|
name: German (Cologne)
|
||||||
native: Kölsch
|
native: Kölsch
|
||||||
```
|
```
|
||||||
|
|
||||||
Similarly, to change an existing language label, you can overwrite one of these keys:
|
Similarly, to change an existing language label, you can overwrite one of these keys:
|
||||||
@ -119,10 +119,10 @@ Similarly, to change an existing language label, you can overwrite one of these
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
i18n:
|
i18n:
|
||||||
common_locales:
|
common_locales:
|
||||||
en_NZ:
|
en_NZ:
|
||||||
native: Niu Zillund
|
native: Niu Zillund
|
||||||
```
|
```
|
||||||
|
|
||||||
### i18n in URLs
|
### i18n in URLs
|
||||||
@ -155,9 +155,9 @@ followed by `setLocale()` or `setDateFormat()`/`setTimeFormat()`.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$field = new DateField();
|
$field = new DateField();
|
||||||
$field->setLocale('de_AT'); // set Austrian/German locale, defaulting format to dd.MM.y
|
$field->setLocale('de_AT'); // set Austrian/German locale, defaulting format to dd.MM.y
|
||||||
$field->setDateFormat('d.M.y'); // set a more specific date format (single digit day/month)
|
$field->setDateFormat('d.M.y'); // set a more specific date format (single digit day/month)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Translating text
|
## Translating text
|
||||||
@ -167,10 +167,10 @@ language-dependent and use a translator function call instead.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// without i18n
|
// without i18n
|
||||||
echo "This is a string";
|
echo "This is a string";
|
||||||
// with i18n
|
// with i18n
|
||||||
echo _t("Namespace.Entity","This is a string");
|
echo _t("Namespace.Entity","This is a string");
|
||||||
```
|
```
|
||||||
|
|
||||||
All strings passed through the `_t()` function will be collected in a separate language table (see [Collecting text](#collecting-text)), which is the starting point for translations.
|
All strings passed through the `_t()` function will be collected in a separate language table (see [Collecting text](#collecting-text)), which is the starting point for translations.
|
||||||
@ -252,17 +252,17 @@ Please ensure that any required plurals are exposed via provideI18nEntities.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// Simple string translation
|
// Simple string translation
|
||||||
_t('LeftAndMain.FILESIMAGES','Files & Images');
|
_t('LeftAndMain.FILESIMAGES','Files & Images');
|
||||||
|
|
||||||
// Using injection to add variables into the translated strings.
|
// Using injection to add variables into the translated strings.
|
||||||
_t('CMSMain.RESTORED',
|
_t('CMSMain.RESTORED',
|
||||||
"Restored {value} successfully",
|
"Restored {value} successfully",
|
||||||
['value' => $itemRestored]
|
['value' => $itemRestored]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Plurals are invoked via a `|` pipe-delimeter with a {count} argument
|
// Plurals are invoked via a `|` pipe-delimeter with a {count} argument
|
||||||
_t('MyObject.PLURALS', 'An object|{count} objects', [ 'count' => '$count ]);
|
_t('MyObject.PLURALS', 'An object|{count} objects', [ 'count' => '$count ]);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -282,13 +282,13 @@ the PHP version of the function.
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
// Simple string translation
|
// Simple string translation
|
||||||
<%t Namespace.Entity "String to translate" %>
|
<%t Namespace.Entity "String to translate" %>
|
||||||
|
|
||||||
// Using injection to add variables into the translated strings (note that $Name and $Greeting must be available in the current template scope).
|
// Using injection to add variables into the translated strings (note that $Name and $Greeting must be available in the current template scope).
|
||||||
<%t Header.Greeting "Hello {name} {greeting}" name=$Name greeting=$Greeting %>
|
<%t Header.Greeting "Hello {name} {greeting}" name=$Name greeting=$Greeting %>
|
||||||
|
|
||||||
// Plurals follow the same convention, required a `|` and `{count}` in the default string
|
// Plurals follow the same convention, required a `|` and `{count}` in the default string
|
||||||
<%t MyObject.PLURALS 'An item|{count} items' count=$Count %>
|
<%t MyObject.PLURALS 'An item|{count} items' count=$Count %>
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -300,11 +300,11 @@ otherwise it won't pick up locale changes.
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<% cached 'MyIdentifier', $CurrentLocale %>
|
<% cached 'MyIdentifier', $CurrentLocale %>
|
||||||
<% loop $Students %>
|
<% loop $Students %>
|
||||||
$Name
|
$Name
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
<% end_cached %>
|
<% end_cached %>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Collecting text
|
## Collecting text
|
||||||
@ -337,15 +337,15 @@ This default order is configured in `framework/_config/i18n.yml`. This file spe
|
|||||||
|
|
||||||
To create a custom module order, you need to specify a config fragment that inserts itself either after or before those items. For example, you may have a number of modules that have to come after the framework/admin, but before anyhting else. To do that, you would use this
|
To create a custom module order, you need to specify a config fragment that inserts itself either after or before those items. For example, you may have a number of modules that have to come after the framework/admin, but before anyhting else. To do that, you would use this
|
||||||
```yml
|
```yml
|
||||||
---
|
---
|
||||||
Name: customi18n
|
Name: customi18n
|
||||||
Before: 'defaulti18n'
|
Before: 'defaulti18n'
|
||||||
---
|
---
|
||||||
i18n:
|
i18n:
|
||||||
module_priority:
|
module_priority:
|
||||||
- module1
|
- module1
|
||||||
- module2
|
- module2
|
||||||
- module3
|
- module3
|
||||||
```
|
```
|
||||||
The config option being set is `i18n.module_priority`, and it is a list of module names.
|
The config option being set is `i18n.module_priority`, and it is a list of module names.
|
||||||
|
|
||||||
@ -366,19 +366,19 @@ By default, SilverStripe uses a YAML format which is loaded via the
|
|||||||
|
|
||||||
Example: framework/lang/en.yml (extract)
|
Example: framework/lang/en.yml (extract)
|
||||||
```yml
|
```yml
|
||||||
en:
|
en:
|
||||||
ImageUploader:
|
ImageUploader:
|
||||||
Attach: 'Attach {title}'
|
Attach: 'Attach {title}'
|
||||||
UploadField:
|
UploadField:
|
||||||
NOTEADDFILES: 'You can add files once you have saved for the first time.'
|
NOTEADDFILES: 'You can add files once you have saved for the first time.'
|
||||||
```
|
```
|
||||||
Translation table: framework/lang/de.yml (extract)
|
Translation table: framework/lang/de.yml (extract)
|
||||||
```yml
|
```yml
|
||||||
de:
|
de:
|
||||||
ImageUploader:
|
ImageUploader:
|
||||||
ATTACH: '{title} anhängen'
|
ATTACH: '{title} anhängen'
|
||||||
UploadField:
|
UploadField:
|
||||||
NOTEADDFILES: 'Sie können Dateien hinzufügen sobald Sie das erste mal gespeichert haben'
|
NOTEADDFILES: 'Sie können Dateien hinzufügen sobald Sie das erste mal gespeichert haben'
|
||||||
```
|
```
|
||||||
Note that translations are cached across requests.
|
Note that translations are cached across requests.
|
||||||
The cache can be cleared through the `?flush=1` query parameter,
|
The cache can be cleared through the `?flush=1` query parameter,
|
||||||
@ -400,7 +400,7 @@ Just point it to a directory instead of a file, and the class will figure out th
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
Requirements::add_i18n_javascript('<my-module-dir>/javascript/lang');
|
Requirements::add_i18n_javascript('<my-module-dir>/javascript/lang');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Translation Tables in JavaScript
|
### Translation Tables in JavaScript
|
||||||
@ -413,13 +413,13 @@ Master Table (`<my-module-dir>/javascript/lang/en.js`)
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
if(typeof(ss) == 'undefined' || typeof(ss.i18n) == 'undefined') {
|
if(typeof(ss) == 'undefined' || typeof(ss.i18n) == 'undefined') {
|
||||||
console.error('Class ss.i18n not defined');
|
console.error('Class ss.i18n not defined');
|
||||||
} else {
|
} else {
|
||||||
ss.i18n.addDictionary('en', {
|
ss.i18n.addDictionary('en', {
|
||||||
'MYMODULE.MYENTITY' : "Really delete these articles?"
|
'MYMODULE.MYENTITY' : "Really delete these articles?"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Example Translation Table (`<my-module-dir>/javascript/lang/de.js`)
|
Example Translation Table (`<my-module-dir>/javascript/lang/de.js`)
|
||||||
@ -427,9 +427,9 @@ Example Translation Table (`<my-module-dir>/javascript/lang/de.js`)
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
ss.i18n.addDictionary('de', {
|
ss.i18n.addDictionary('de', {
|
||||||
'MYMODULE.MYENTITY' : "Artikel wirklich löschen?"
|
'MYMODULE.MYENTITY' : "Artikel wirklich löschen?"
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
For most core modules, these files are generated by a
|
For most core modules, these files are generated by a
|
||||||
@ -442,7 +442,7 @@ format which can be processed more easily by external translation providers (see
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
alert(ss.i18n._t('MYMODULE.MYENTITY'));
|
alert(ss.i18n._t('MYMODULE.MYENTITY'));
|
||||||
```
|
```
|
||||||
|
|
||||||
### Advanced Use
|
### Advanced Use
|
||||||
@ -456,13 +456,13 @@ The `ss.i18n` object contain a couple functions to help and replace dynamic vari
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
// MYMODULE.MYENTITY contains "Really delete %s articles by %s?"
|
// MYMODULE.MYENTITY contains "Really delete %s articles by %s?"
|
||||||
alert(ss.i18n.sprintf(
|
alert(ss.i18n.sprintf(
|
||||||
ss.i18n._t('MYMODULE.MYENTITY'),
|
ss.i18n._t('MYMODULE.MYENTITY'),
|
||||||
42,
|
42,
|
||||||
'Douglas Adams'
|
'Douglas Adams'
|
||||||
));
|
));
|
||||||
// Displays: "Really delete 42 articles by Douglas Adams?"
|
// Displays: "Really delete 42 articles by Douglas Adams?"
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Variable injection with inject()
|
#### Variable injection with inject()
|
||||||
@ -472,12 +472,12 @@ The `ss.i18n` object contain a couple functions to help and replace dynamic vari
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
// MYMODULE.MYENTITY contains "Really delete {count} articles by {author}?"
|
// MYMODULE.MYENTITY contains "Really delete {count} articles by {author}?"
|
||||||
alert(ss.i18n.inject(
|
alert(ss.i18n.inject(
|
||||||
ss.i18n._t('MYMODULE.MYENTITY'),
|
ss.i18n._t('MYMODULE.MYENTITY'),
|
||||||
{count: 42, author: 'Douglas Adams'}
|
{count: 42, author: 'Douglas Adams'}
|
||||||
));
|
));
|
||||||
// Displays: "Really delete 42 articles by Douglas Adams?"
|
// Displays: "Really delete 42 articles by Douglas Adams?"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Limitations
|
## Limitations
|
||||||
|
@ -41,8 +41,8 @@ Note that this will not allow you to utilise certain file versioning features in
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|
||||||
\SilverStripe\Assets\Flysystem\FlysystemAssetStore:
|
\SilverStripe\Assets\Flysystem\FlysystemAssetStore:
|
||||||
legacy_filenames: true
|
legacy_filenames: true
|
||||||
```
|
```
|
||||||
|
|
||||||
## Loading content into `DBFile`
|
## Loading content into `DBFile`
|
||||||
@ -52,19 +52,19 @@ within the assets folder).
|
|||||||
|
|
||||||
For example, to load a temporary file into a DataObject you could use the below:
|
For example, to load a temporary file into a DataObject you could use the below:
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
<?
|
<?
|
||||||
class Banner extends DataObject
|
class Banner extends DataObject
|
||||||
{
|
{
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Image' => 'DBFile'
|
'Image' => 'DBFile'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Image could be assigned in other parts of the code using the below
|
// Image could be assigned in other parts of the code using the below
|
||||||
$banner = new Banner();
|
$banner = new Banner();
|
||||||
$banner->Image->setFromLocalFile($tempfile['path'], 'uploads/banner-file.jpg');
|
$banner->Image->setFromLocalFile($tempfile['path'], 'uploads/banner-file.jpg');
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -38,36 +38,36 @@ Here are some examples, assuming the `$Image` object has dimensions of 200x100px
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
// Scaling functions
|
// Scaling functions
|
||||||
$Image.ScaleWidth(150) // Returns a 150x75px image
|
$Image.ScaleWidth(150) // Returns a 150x75px image
|
||||||
$Image.ScaleMaxWidth(100) // Returns a 100x50px image (like ScaleWidth but prevents up-sampling)
|
$Image.ScaleMaxWidth(100) // Returns a 100x50px image (like ScaleWidth but prevents up-sampling)
|
||||||
$Image.ScaleHeight(150) // Returns a 300x150px image (up-sampled. Try to avoid doing this)
|
$Image.ScaleHeight(150) // Returns a 300x150px image (up-sampled. Try to avoid doing this)
|
||||||
$Image.ScaleMaxHeight(150) // Returns a 200x100px image (like ScaleHeight but prevents up-sampling)
|
$Image.ScaleMaxHeight(150) // Returns a 200x100px image (like ScaleHeight but prevents up-sampling)
|
||||||
$Image.Fit(300,300) // Returns an image that fits within a 300x300px boundary, resulting in a 300x150px image (up-sampled)
|
$Image.Fit(300,300) // Returns an image that fits within a 300x300px boundary, resulting in a 300x150px image (up-sampled)
|
||||||
$Image.FitMax(300,300) // Returns a 200x100px image (like Fit but prevents up-sampling)
|
$Image.FitMax(300,300) // Returns a 200x100px image (like Fit but prevents up-sampling)
|
||||||
|
|
||||||
// Warning: This method can distort images that are not the correct aspect ratio
|
// Warning: This method can distort images that are not the correct aspect ratio
|
||||||
$Image.ResizedImage(200, 300) // Forces dimensions of this image to the given values.
|
$Image.ResizedImage(200, 300) // Forces dimensions of this image to the given values.
|
||||||
|
|
||||||
// Cropping functions
|
// Cropping functions
|
||||||
$Image.Fill(150,150) // Returns a 150x150px image resized and cropped to fill specified dimensions (up-sampled)
|
$Image.Fill(150,150) // Returns a 150x150px image resized and cropped to fill specified dimensions (up-sampled)
|
||||||
$Image.FillMax(150,150) // Returns a 100x100px image (like Fill but prevents up-sampling)
|
$Image.FillMax(150,150) // Returns a 100x100px image (like Fill but prevents up-sampling)
|
||||||
$Image.CropWidth(150) // Returns a 150x100px image (trims excess pixels off the x axis from the center)
|
$Image.CropWidth(150) // Returns a 150x100px image (trims excess pixels off the x axis from the center)
|
||||||
$Image.CropHeight(50) // Returns a 200x50px image (trims excess pixels off the y axis from the center)
|
$Image.CropHeight(50) // Returns a 200x50px image (trims excess pixels off the y axis from the center)
|
||||||
|
|
||||||
// Padding functions (add space around an image)
|
// Padding functions (add space around an image)
|
||||||
$Image.Pad(100,100) // Returns a 100x100px padded image, with white bars added at the top and bottom
|
$Image.Pad(100,100) // Returns a 100x100px padded image, with white bars added at the top and bottom
|
||||||
$Image.Pad(100, 100, CCCCCC) // Same as above but with a grey background
|
$Image.Pad(100, 100, CCCCCC) // Same as above but with a grey background
|
||||||
|
|
||||||
// Metadata
|
// Metadata
|
||||||
$Image.Width // Returns width of image
|
$Image.Width // Returns width of image
|
||||||
$Image.Height // Returns height of image
|
$Image.Height // Returns height of image
|
||||||
$Image.Orientation // Returns Orientation
|
$Image.Orientation // Returns Orientation
|
||||||
$Image.Title // Returns the friendly file name
|
$Image.Title // Returns the friendly file name
|
||||||
$Image.Name // Returns the actual file name
|
$Image.Name // Returns the actual file name
|
||||||
$Image.FileName // Returns the actual file name including directory path from web root
|
$Image.FileName // Returns the actual file name including directory path from web root
|
||||||
$Image.Link // Returns relative URL path to image
|
$Image.Link // Returns relative URL path to image
|
||||||
$Image.AbsoluteLink // Returns absolute URL path to image
|
$Image.AbsoluteLink // Returns absolute URL path to image
|
||||||
```
|
```
|
||||||
|
|
||||||
Image methods are chainable. Example:
|
Image methods are chainable. Example:
|
||||||
@ -75,7 +75,7 @@ Image methods are chainable. Example:
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<body style="background-image:url($Image.ScaleWidth(800).CropHeight(800).Link)">
|
<body style="background-image:url($Image.ScaleWidth(800).CropHeight(800).Link)">
|
||||||
```
|
```
|
||||||
|
|
||||||
### Padded Image Resize
|
### Padded Image Resize
|
||||||
@ -88,9 +88,9 @@ png images.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$Image.Pad(80, 80, FFFFFF, 50) // white padding with 50% transparency
|
$Image.Pad(80, 80, FFFFFF, 50) // white padding with 50% transparency
|
||||||
$Image.Pad(80, 80, FFFFFF, 100) // white padding with 100% transparency
|
$Image.Pad(80, 80, FFFFFF, 100) // white padding with 100% transparency
|
||||||
$Image.Pad(80, 80, FFFFFF) // white padding with no transparency
|
$Image.Pad(80, 80, FFFFFF) // white padding with no transparency
|
||||||
```
|
```
|
||||||
|
|
||||||
### Manipulating images in PHP
|
### Manipulating images in PHP
|
||||||
@ -107,7 +107,7 @@ You can also create your own functions by decorating the `Image` class.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
class ImageExtension extends \SilverStripe\Core\Extension
|
class ImageExtension extends \SilverStripe\Core\Extension
|
||||||
{
|
{
|
||||||
|
|
||||||
public function Square($width)
|
public function Square($width)
|
||||||
@ -136,13 +136,13 @@ You can also create your own functions by decorating the `Image` class.
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
:::yml
|
:::yml
|
||||||
SilverStripe\Assets\Image:
|
SilverStripe\Assets\Image:
|
||||||
extensions:
|
extensions:
|
||||||
- ImageExtension
|
- ImageExtension
|
||||||
SilverStripe\Filesystem\Storage\DBFile:
|
SilverStripe\Filesystem\Storage\DBFile:
|
||||||
extensions:
|
extensions:
|
||||||
- ImageExtension
|
- ImageExtension
|
||||||
```
|
```
|
||||||
|
|
||||||
### Form Upload
|
### Form Upload
|
||||||
@ -177,12 +177,12 @@ necessary, you can add this to your mysite/config/config.yml file:
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
# Configure resampling for File dataobject
|
# Configure resampling for File dataobject
|
||||||
File:
|
File:
|
||||||
force_resample: false
|
force_resample: false
|
||||||
# DBFile can be configured independently
|
# DBFile can be configured independently
|
||||||
SilverStripe\Filesystem\Storage\DBFile:
|
SilverStripe\Filesystem\Storage\DBFile:
|
||||||
force_resample: false
|
force_resample: false
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Resampled image quality
|
#### Resampled image quality
|
||||||
|
@ -265,10 +265,10 @@ appropriate file extensions for your instance. This can be done by setting the `
|
|||||||
config.
|
config.
|
||||||
```yaml
|
```yaml
|
||||||
|
|
||||||
File:
|
File:
|
||||||
allowed_extensions:
|
allowed_extensions:
|
||||||
- 7zip
|
- 7zip
|
||||||
- xzip
|
- xzip
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="warning" markdown="1">
|
<div class="warning" markdown="1">
|
||||||
|
@ -21,21 +21,21 @@ a category.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Product extends DataObject
|
class Product extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Name' => 'Varchar',
|
'Name' => 'Varchar',
|
||||||
'ProductCode' => 'Varchar',
|
'ProductCode' => 'Varchar',
|
||||||
'Price' => 'Currency'
|
'Price' => 'Currency'
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $has_one = [
|
private static $has_one = [
|
||||||
'Category' => 'Category'
|
'Category' => 'Category'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -43,19 +43,19 @@ a category.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Category extends DataObject
|
class Category extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'Title' => 'Text'
|
'Title' => 'Text'
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $has_many = [
|
private static $has_many = [
|
||||||
'Products' => 'Product'
|
'Products' => 'Product'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -68,20 +68,20 @@ We'll name it `MyAdmin`, but the class name can be anything you want.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Admin\ModelAdmin;
|
use SilverStripe\Admin\ModelAdmin;
|
||||||
|
|
||||||
class MyAdmin extends ModelAdmin
|
class MyAdmin extends ModelAdmin
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $managed_models = [
|
private static $managed_models = [
|
||||||
'Product',
|
'Product',
|
||||||
'Category'
|
'Category'
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $url_segment = 'products';
|
private static $url_segment = 'products';
|
||||||
|
|
||||||
private static $menu_title = 'My Product Admin';
|
private static $menu_title = 'My Product Admin';
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -110,31 +110,31 @@ permissions by default. For most cases, less restrictive checks make sense, e.g.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Security\Permission;
|
use SilverStripe\Security\Permission;
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Category extends DataObject
|
class Category extends DataObject
|
||||||
{
|
{
|
||||||
// ...
|
// ...
|
||||||
public function canView($member = null)
|
public function canView($member = null)
|
||||||
{
|
{
|
||||||
return Permission::check('CMS_ACCESS_MyAdmin', 'any', $member);
|
return Permission::check('CMS_ACCESS_MyAdmin', 'any', $member);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function canEdit($member = null)
|
public function canEdit($member = null)
|
||||||
{
|
{
|
||||||
return Permission::check('CMS_ACCESS_MyAdmin', 'any', $member);
|
return Permission::check('CMS_ACCESS_MyAdmin', 'any', $member);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function canDelete($member = null)
|
public function canDelete($member = null)
|
||||||
{
|
{
|
||||||
return Permission::check('CMS_ACCESS_MyAdmin', 'any', $member);
|
return Permission::check('CMS_ACCESS_MyAdmin', 'any', $member);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function canCreate($member = null)
|
public function canCreate($member = null)
|
||||||
{
|
{
|
||||||
return Permission::check('CMS_ACCESS_MyAdmin', 'any', $member);
|
return Permission::check('CMS_ACCESS_MyAdmin', 'any', $member);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Searching Records
|
## Searching Records
|
||||||
@ -151,16 +151,16 @@ class (see [SearchContext](../search/searchcontext) docs for details).
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Product extends DataObject
|
class Product extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $searchable_fields = [
|
private static $searchable_fields = [
|
||||||
'Name',
|
'Name',
|
||||||
'ProductCode'
|
'ProductCode'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -178,20 +178,20 @@ model class, where you can add or remove columns. To change the title, use [Data
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
class Product extends DataObject
|
class Product extends DataObject
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $field_labels = [
|
private static $field_labels = [
|
||||||
'Price' => 'Cost' // renames the column to "Cost"
|
'Price' => 'Cost' // renames the column to "Cost"
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $summary_fields = [
|
private static $summary_fields = [
|
||||||
'Name',
|
'Name',
|
||||||
'Price'
|
'Price'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -205,23 +205,23 @@ For example, we might want to exclude all products without prices in our sample
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Admin\ModelAdmin;
|
use SilverStripe\Admin\ModelAdmin;
|
||||||
|
|
||||||
class MyAdmin extends ModelAdmin
|
class MyAdmin extends ModelAdmin
|
||||||
{
|
{
|
||||||
|
|
||||||
public function getList()
|
public function getList()
|
||||||
{
|
{
|
||||||
$list = parent::getList();
|
$list = parent::getList();
|
||||||
|
|
||||||
// Always limit by model class, in case you're managing multiple
|
// Always limit by model class, in case you're managing multiple
|
||||||
if($this->modelClass == 'Product') {
|
if($this->modelClass == 'Product') {
|
||||||
$list = $list->exclude('Price', '0');
|
$list = $list->exclude('Price', '0');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $list;
|
return $list;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also customize the search behavior directly on your `ModelAdmin` instance. For example, we might want to have a
|
You can also customize the search behavior directly on your `ModelAdmin` instance. For example, we might want to have a
|
||||||
@ -231,36 +231,36 @@ checkbox which limits search results to expensive products (over $100).
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Forms\CheckboxField;
|
use SilverStripe\Forms\CheckboxField;
|
||||||
use SilverStripe\Admin\ModelAdmin;
|
use SilverStripe\Admin\ModelAdmin;
|
||||||
|
|
||||||
class MyAdmin extends ModelAdmin
|
class MyAdmin extends ModelAdmin
|
||||||
{
|
{
|
||||||
|
|
||||||
public function getSearchContext()
|
public function getSearchContext()
|
||||||
{
|
{
|
||||||
$context = parent::getSearchContext();
|
$context = parent::getSearchContext();
|
||||||
|
|
||||||
if($this->modelClass == 'Product') {
|
if($this->modelClass == 'Product') {
|
||||||
$context->getFields()->push(new CheckboxField('q[ExpensiveOnly]', 'Only expensive stuff'));
|
$context->getFields()->push(new CheckboxField('q[ExpensiveOnly]', 'Only expensive stuff'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $context;
|
return $context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getList()
|
public function getList()
|
||||||
{
|
{
|
||||||
$list = parent::getList();
|
$list = parent::getList();
|
||||||
|
|
||||||
$params = $this->getRequest()->requestVar('q'); // use this to access search parameters
|
$params = $this->getRequest()->requestVar('q'); // use this to access search parameters
|
||||||
|
|
||||||
if($this->modelClass == 'Product' && isset($params['ExpensiveOnly']) && $params['ExpensiveOnly']) {
|
if($this->modelClass == 'Product' && isset($params['ExpensiveOnly']) && $params['ExpensiveOnly']) {
|
||||||
$list = $list->exclude('Price:LessThan', '100');
|
$list = $list->exclude('Price:LessThan', '100');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $list;
|
return $list;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
To alter how the results are displayed (via [GridField](api:SilverStripe\Forms\GridField\GridField)), you can also overload the `getEditForm()` method. For
|
To alter how the results are displayed (via [GridField](api:SilverStripe\Forms\GridField\GridField)), you can also overload the `getEditForm()` method. For
|
||||||
@ -270,34 +270,34 @@ example, to add a new component.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Forms\GridField\GridFieldFilterHeader;
|
use SilverStripe\Forms\GridField\GridFieldFilterHeader;
|
||||||
use SilverStripe\Admin\ModelAdmin;
|
use SilverStripe\Admin\ModelAdmin;
|
||||||
|
|
||||||
class MyAdmin extends ModelAdmin
|
class MyAdmin extends ModelAdmin
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $managed_models = [
|
private static $managed_models = [
|
||||||
'Product',
|
'Product',
|
||||||
'Category'
|
'Category'
|
||||||
];
|
];
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
public function getEditForm($id = null, $fields = null)
|
public function getEditForm($id = null, $fields = null)
|
||||||
{
|
{
|
||||||
$form = parent::getEditForm($id, $fields);
|
$form = parent::getEditForm($id, $fields);
|
||||||
|
|
||||||
// $gridFieldName is generated from the ModelClass, eg if the Class 'Product'
|
// $gridFieldName is generated from the ModelClass, eg if the Class 'Product'
|
||||||
// is managed by this ModelAdmin, the GridField for it will also be named 'Product'
|
// is managed by this ModelAdmin, the GridField for it will also be named 'Product'
|
||||||
|
|
||||||
$gridFieldName = $this->sanitiseClassName($this->modelClass);
|
$gridFieldName = $this->sanitiseClassName($this->modelClass);
|
||||||
$gridField = $form->Fields()->fieldByName($gridFieldName);
|
$gridField = $form->Fields()->fieldByName($gridFieldName);
|
||||||
|
|
||||||
// modify the list view.
|
// modify the list view.
|
||||||
$gridField->getConfig()->addComponent(new GridFieldFilterHeader());
|
$gridField->getConfig()->addComponent(new GridFieldFilterHeader());
|
||||||
|
|
||||||
return $form;
|
return $form;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -308,31 +308,31 @@ to only one specific `GridField`:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Forms\GridField\GridFieldFilterHeader;
|
use SilverStripe\Forms\GridField\GridFieldFilterHeader;
|
||||||
use SilverStripe\Admin\ModelAdmin;
|
use SilverStripe\Admin\ModelAdmin;
|
||||||
|
|
||||||
class MyAdmin extends ModelAdmin
|
class MyAdmin extends ModelAdmin
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $managed_models = [
|
private static $managed_models = [
|
||||||
'Product',
|
'Product',
|
||||||
'Category'
|
'Category'
|
||||||
];
|
];
|
||||||
|
|
||||||
public function getEditForm($id = null, $fields = null)
|
public function getEditForm($id = null, $fields = null)
|
||||||
{
|
{
|
||||||
$form = parent::getEditForm($id, $fields);
|
$form = parent::getEditForm($id, $fields);
|
||||||
|
|
||||||
$gridFieldName = 'Product';
|
$gridFieldName = 'Product';
|
||||||
$gridField = $form->Fields()->fieldByName($gridFieldName);
|
$gridField = $form->Fields()->fieldByName($gridFieldName);
|
||||||
|
|
||||||
if ($gridField) {
|
if ($gridField) {
|
||||||
$gridField->getConfig()->addComponent(new GridFieldFilterHeader());
|
$gridField->getConfig()->addComponent(new GridFieldFilterHeader());
|
||||||
}
|
}
|
||||||
|
|
||||||
return $form;
|
return $form;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -354,21 +354,21 @@ To customize the exported columns, create a new method called `getExportFields`
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Admin\ModelAdmin;
|
use SilverStripe\Admin\ModelAdmin;
|
||||||
|
|
||||||
class MyAdmin extends ModelAdmin
|
class MyAdmin extends ModelAdmin
|
||||||
{
|
{
|
||||||
// ...
|
// ...
|
||||||
|
|
||||||
public function getExportFields()
|
public function getExportFields()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'Name' => 'Name',
|
'Name' => 'Name',
|
||||||
'ProductCode' => 'Product Code',
|
'ProductCode' => 'Product Code',
|
||||||
'Category.Title' => 'Category'
|
'Category.Title' => 'Category'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -57,16 +57,16 @@ The CMS interface can be accessed by default through the `admin/` URL. You can c
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
---
|
---
|
||||||
Name: myadmin
|
Name: myadmin
|
||||||
After:
|
After:
|
||||||
- '#adminroutes'
|
- '#adminroutes'
|
||||||
---
|
---
|
||||||
Director:
|
Director:
|
||||||
rules:
|
rules:
|
||||||
'admin': ''
|
'admin': ''
|
||||||
'newAdmin': 'AdminRootController'
|
'newAdmin': 'AdminRootController'
|
||||||
---
|
---
|
||||||
```
|
```
|
||||||
|
|
||||||
When extending the CMS or creating modules, you can take advantage of various functions that will return the configured admin URL (by default 'admin/' is returned):
|
When extending the CMS or creating modules, you can take advantage of various functions that will return the configured admin URL (by default 'admin/' is returned):
|
||||||
@ -75,7 +75,7 @@ In PHP you should use:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
AdminRootController::admin_url()
|
AdminRootController::admin_url()
|
||||||
```
|
```
|
||||||
|
|
||||||
When writing templates use:
|
When writing templates use:
|
||||||
@ -83,7 +83,7 @@ When writing templates use:
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
$AdminURL
|
$AdminURL
|
||||||
```
|
```
|
||||||
|
|
||||||
And in JavaScript, this is avaible through the `ss` namespace
|
And in JavaScript, this is avaible through the `ss` namespace
|
||||||
@ -91,7 +91,7 @@ And in JavaScript, this is avaible through the `ss` namespace
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
ss.config.adminUrl
|
ss.config.adminUrl
|
||||||
```
|
```
|
||||||
|
|
||||||
### Multiple Admin URL and overrides
|
### Multiple Admin URL and overrides
|
||||||
@ -162,45 +162,45 @@ Basic example form in a CMS controller subclass:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Forms\TabSet;
|
use SilverStripe\Forms\TabSet;
|
||||||
use SilverStripe\Forms\FieldList;
|
use SilverStripe\Forms\FieldList;
|
||||||
use SilverStripe\Forms\Tab;
|
use SilverStripe\Forms\Tab;
|
||||||
use SilverStripe\Forms\TextField;
|
use SilverStripe\Forms\TextField;
|
||||||
use SilverStripe\Forms\FormAction;
|
use SilverStripe\Forms\FormAction;
|
||||||
use SilverStripe\Admin\LeftAndMain;
|
use SilverStripe\Admin\LeftAndMain;
|
||||||
use SilverStripe\Admin\LeftAndMainFormRequestHandler;
|
use SilverStripe\Admin\LeftAndMainFormRequestHandler;
|
||||||
|
|
||||||
class MyAdmin extends LeftAndMain
|
class MyAdmin extends LeftAndMain
|
||||||
{
|
{
|
||||||
function getEditForm() {
|
function getEditForm() {
|
||||||
return Form::create(
|
return Form::create(
|
||||||
$this,
|
$this,
|
||||||
'EditForm',
|
'EditForm',
|
||||||
new FieldList(
|
new FieldList(
|
||||||
TabSet::create(
|
TabSet::create(
|
||||||
'Root',
|
'Root',
|
||||||
Tab::create('Main',
|
Tab::create('Main',
|
||||||
TextField::create('MyText')
|
TextField::create('MyText')
|
||||||
)
|
)
|
||||||
)->setTemplate('CMSTabset')
|
)->setTemplate('CMSTabset')
|
||||||
),
|
),
|
||||||
new FieldList(
|
new FieldList(
|
||||||
FormAction::create('doSubmit')
|
FormAction::create('doSubmit')
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
// Use a custom request handler
|
// Use a custom request handler
|
||||||
->setRequestHandler(
|
->setRequestHandler(
|
||||||
LeftAndMainFormRequestHandler::create($form)
|
LeftAndMainFormRequestHandler::create($form)
|
||||||
)
|
)
|
||||||
// JS and CSS use this identifier
|
// JS and CSS use this identifier
|
||||||
->setHTMLID('Form_EditForm')
|
->setHTMLID('Form_EditForm')
|
||||||
// Render correct responses on validation errors
|
// Render correct responses on validation errors
|
||||||
->setResponseNegotiator($this->getResponseNegotiator());
|
->setResponseNegotiator($this->getResponseNegotiator());
|
||||||
// Required for correct CMS layout
|
// Required for correct CMS layout
|
||||||
->addExtraClass('cms-edit-form')
|
->addExtraClass('cms-edit-form')
|
||||||
->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
|
->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: Usually you don't need to worry about these settings,
|
Note: Usually you don't need to worry about these settings,
|
||||||
@ -307,12 +307,12 @@ Firstly, `reactRouter` must be passed as a boolean flag to indicate that this se
|
|||||||
controlled by the react section, and thus should suppress registration of a page.js route
|
controlled by the react section, and thus should suppress registration of a page.js route
|
||||||
for this section.
|
for this section.
|
||||||
```php
|
```php
|
||||||
public function getClientConfig()
|
public function getClientConfig()
|
||||||
{
|
{
|
||||||
return array_merge(parent::getClientConfig(), [
|
return array_merge(parent::getClientConfig(), [
|
||||||
'reactRouter' => true
|
'reactRouter' => true
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -321,33 +321,33 @@ with the reactRouteRegister component. This will need to be done on the `DOMCont
|
|||||||
to ensure routes are registered before window.load is invoked.
|
to ensure routes are registered before window.load is invoked.
|
||||||
```js
|
```js
|
||||||
|
|
||||||
import { withRouter } from 'react-router';
|
import { withRouter } from 'react-router';
|
||||||
import ConfigHelpers from 'lib/Config';
|
import ConfigHelpers from 'lib/Config';
|
||||||
import reactRouteRegister from 'lib/ReactRouteRegister';
|
import reactRouteRegister from 'lib/ReactRouteRegister';
|
||||||
import MyAdmin from './MyAdmin';
|
import MyAdmin from './MyAdmin';
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
const sectionConfig = ConfigHelpers.getSection('MyAdmin');
|
const sectionConfig = ConfigHelpers.getSection('MyAdmin');
|
||||||
|
|
||||||
reactRouteRegister.add({
|
reactRouteRegister.add({
|
||||||
path: sectionConfig.url,
|
path: sectionConfig.url,
|
||||||
component: withRouter(MyAdminComponent),
|
component: withRouter(MyAdminComponent),
|
||||||
childRoutes: [
|
childRoutes: [
|
||||||
{ path: 'form/:id/:view', component: MyAdminComponent },
|
{ path: 'form/:id/:view', component: MyAdminComponent },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Child routes can be registered post-boot by using `ReactRouteRegister` in the same way.
|
Child routes can be registered post-boot by using `ReactRouteRegister` in the same way.
|
||||||
```js
|
```js
|
||||||
|
|
||||||
// Register a nested url under `sectionConfig.url`
|
// Register a nested url under `sectionConfig.url`
|
||||||
const sectionConfig = ConfigHelpers.getSection('MyAdmin');
|
const sectionConfig = ConfigHelpers.getSection('MyAdmin');
|
||||||
reactRouteRegister.add({
|
reactRouteRegister.add({
|
||||||
path: 'nested',
|
path: 'nested',
|
||||||
component: NestedComponent,
|
component: NestedComponent,
|
||||||
}, [ sectionConfig.url ]);
|
}, [ sectionConfig.url ]);
|
||||||
```
|
```
|
||||||
|
|
||||||
## PJAX: Partial template replacement through Ajax
|
## PJAX: Partial template replacement through Ajax
|
||||||
@ -375,54 +375,57 @@ in a single Ajax request.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Admin\LeftAndMain;
|
use SilverStripe\Admin\LeftAndMain;
|
||||||
|
|
||||||
// mysite/code/MyAdmin.php
|
// mysite/code/MyAdmin.php
|
||||||
class MyAdmin extends LeftAndMain
|
class MyAdmin extends LeftAndMain
|
||||||
{
|
{
|
||||||
private static $url_segment = 'myadmin';
|
private static $url_segment = 'myadmin';
|
||||||
public function getResponseNegotiator()
|
public function getResponseNegotiator()
|
||||||
{
|
{
|
||||||
$negotiator = parent::getResponseNegotiator();
|
$negotiator = parent::getResponseNegotiator();
|
||||||
$controller = $this;
|
$controller = $this;
|
||||||
// Register a new callback
|
// Register a new callback
|
||||||
$negotiator->setCallback('MyRecordInfo', function() use(&$controller) {
|
$negotiator->setCallback('MyRecordInfo', function() use(&$controller) {
|
||||||
return $controller->MyRecordInfo();
|
return $controller->MyRecordInfo();
|
||||||
});
|
});
|
||||||
return $negotiator;
|
return $negotiator;
|
||||||
}
|
}
|
||||||
public function MyRecordInfo()
|
public function MyRecordInfo()
|
||||||
{
|
{
|
||||||
return $this->renderWith('MyRecordInfo');
|
return $this->renderWith('MyRecordInfo');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// MyAdmin.ss
|
// MyAdmin.ss
|
||||||
<% include SilverStripe\\Admin\\CMSBreadcrumbs %>
|
<% include SilverStripe\\Admin\\CMSBreadcrumbs %>
|
||||||
<div>Static content (not affected by update)</div>
|
<div>Static content (not affected by update)</div>
|
||||||
<% include MyRecordInfo %>
|
<% include MyRecordInfo %>
|
||||||
<a href="{$AdminURL}myadmin" class="cms-panel-link" data-pjax-target="MyRecordInfo,Breadcrumbs">
|
<a href="{$AdminURL}myadmin" class="cms-panel-link" data-pjax-target="MyRecordInfo,Breadcrumbs">
|
||||||
Update record info
|
Update record info
|
||||||
</a>
|
</a>
|
||||||
|
```
|
||||||
|
|
||||||
:::ss
|
```ss
|
||||||
// MyRecordInfo.ss
|
// MyRecordInfo.ss
|
||||||
<div data-pjax-fragment="MyRecordInfo">
|
<div data-pjax-fragment="MyRecordInfo">
|
||||||
Current Record: $currentPage.Title
|
Current Record: $currentPage.Title
|
||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
A click on the link will cause the following (abbreviated) ajax HTTP request:
|
A click on the link will cause the following (abbreviated) ajax HTTP request:
|
||||||
|
|
||||||
```
|
```
|
||||||
GET /admin/myadmin HTTP/1.1
|
GET /admin/myadmin HTTP/1.1
|
||||||
X-Pjax:MyRecordInfo,Breadcrumbs
|
X-Pjax:MyRecordInfo,Breadcrumbs
|
||||||
X-Requested-With:XMLHttpRequest
|
X-Requested-With:XMLHttpRequest
|
||||||
```
|
```
|
||||||
... and result in the following response:
|
... and result in the following response:
|
||||||
|
|
||||||
```
|
```
|
||||||
{"MyRecordInfo": "<div...", "CMSBreadcrumbs": "<div..."}
|
{"MyRecordInfo": "<div...", "CMSBreadcrumbs": "<div..."}
|
||||||
```
|
```
|
||||||
Keep in mind that the returned view isn't always decided upon when the Ajax request
|
Keep in mind that the returned view isn't always decided upon when the Ajax request
|
||||||
is fired, so the server might decide to change it based on its own logic,
|
is fired, so the server might decide to change it based on its own logic,
|
||||||
@ -432,7 +435,7 @@ On the client, you can set your preference through the `data-pjax-target` attrib
|
|||||||
on links or through the `X-Pjax` header. For firing off an Ajax request that is
|
on links or through the `X-Pjax` header. For firing off an Ajax request that is
|
||||||
tracked in the browser history, use the `pjax` attribute on the state data.
|
tracked in the browser history, use the `pjax` attribute on the state data.
|
||||||
```js
|
```js
|
||||||
$('.cms-container').loadPanel(ss.config.adminUrl+'pages', null, {pjax: 'Content'});
|
$('.cms-container').loadPanel(ss.config.adminUrl+'pages', null, {pjax: 'Content'});
|
||||||
```
|
```
|
||||||
## Loading lightweight PJAX fragments
|
## Loading lightweight PJAX fragments
|
||||||
|
|
||||||
@ -448,9 +451,9 @@ unrelated to the main flow.
|
|||||||
In this case you can use the `loadFragment` call supplied by `LeftAndMain.js`. You can trigger as many of these in
|
In this case you can use the `loadFragment` call supplied by `LeftAndMain.js`. You can trigger as many of these in
|
||||||
parallel as you want. This will not disturb the main navigation.
|
parallel as you want. This will not disturb the main navigation.
|
||||||
```js
|
```js
|
||||||
$('.cms-container').loadFragment(ss.config.adminUrl+'foobar/', 'Fragment1');
|
$('.cms-container').loadFragment(ss.config.adminUrl+'foobar/', 'Fragment1');
|
||||||
$('.cms-container').loadFragment(ss.config.adminUrl+'foobar/', 'Fragment2');
|
$('.cms-container').loadFragment(ss.config.adminUrl+'foobar/', 'Fragment2');
|
||||||
$('.cms-container').loadFragment(ss.config.adminUrl+'foobar/', 'Fragment3');
|
$('.cms-container').loadFragment(ss.config.adminUrl+'foobar/', 'Fragment3');
|
||||||
```
|
```
|
||||||
The ongoing requests are tracked by the PJAX fragment name (Fragment1, 2, and 3 above) - resubmission will
|
The ongoing requests are tracked by the PJAX fragment name (Fragment1, 2, and 3 above) - resubmission will
|
||||||
result in the prior request for this fragment to be aborted. Other parallel requests will continue undisturbed.
|
result in the prior request for this fragment to be aborted. Other parallel requests will continue undisturbed.
|
||||||
@ -468,21 +471,21 @@ will be triggered. In case of a request error a `loadfragmenterror` will be rais
|
|||||||
|
|
||||||
You can hook up a response handler that obtains all the details of the XHR request via Entwine handler:
|
You can hook up a response handler that obtains all the details of the XHR request via Entwine handler:
|
||||||
```js
|
```js
|
||||||
'from .cms-container': {
|
'from .cms-container': {
|
||||||
onafterloadfragment: function(e, data) {
|
onafterloadfragment: function(e, data) {
|
||||||
// Say 'success'!
|
// Say 'success'!
|
||||||
alert(data.status);
|
alert(data.status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Alternatively you can use the jQuery deferred API:
|
Alternatively you can use the jQuery deferred API:
|
||||||
```js
|
```js
|
||||||
$('.cms-container')
|
$('.cms-container')
|
||||||
.loadFragment(ss.config.adminUrl+'foobar/', 'Fragment1')
|
.loadFragment(ss.config.adminUrl+'foobar/', 'Fragment1')
|
||||||
.success(function(data, status, xhr) {
|
.success(function(data, status, xhr) {
|
||||||
// Say 'success'!
|
// Say 'success'!
|
||||||
alert(status);
|
alert(status);
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
## Ajax Redirects
|
## Ajax Redirects
|
||||||
|
|
||||||
@ -505,17 +508,17 @@ without affecting the response body.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Admin\LeftAndMain;
|
use SilverStripe\Admin\LeftAndMain;
|
||||||
|
|
||||||
class MyController extends LeftAndMain
|
class MyController extends LeftAndMain
|
||||||
{
|
{
|
||||||
class myaction()
|
class myaction()
|
||||||
{
|
{
|
||||||
// ...
|
// ...
|
||||||
$this->getResponse()->addHeader('X-Controller', 'MyOtherController');
|
$this->getResponse()->addHeader('X-Controller', 'MyOtherController');
|
||||||
return $html;
|
return $html;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Built-in headers are:
|
Built-in headers are:
|
||||||
@ -569,12 +572,12 @@ which is picked up by the menu:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function mycontrollermethod()
|
public function mycontrollermethod()
|
||||||
{
|
{
|
||||||
// .. logic here
|
// .. logic here
|
||||||
$this->getResponse()->addHeader('X-Controller', 'AssetAdmin');
|
$this->getResponse()->addHeader('X-Controller', 'AssetAdmin');
|
||||||
return 'my response';
|
return 'my response';
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
This is usually handled by the existing [LeftAndMain](api:SilverStripe\Admin\LeftAndMain) logic,
|
This is usually handled by the existing [LeftAndMain](api:SilverStripe\Admin\LeftAndMain) logic,
|
||||||
@ -623,29 +626,29 @@ Form template with custom tab navigation (trimmed down):
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<form $FormAttributes data-layout-type="border">
|
<form $FormAttributes data-layout-type="border">
|
||||||
|
|
||||||
<div class="cms-content-header north">
|
<div class="cms-content-header north">
|
||||||
<% if Fields.hasTabset %>
|
<% if Fields.hasTabset %>
|
||||||
<% with Fields.fieldByName('Root') %>
|
<% with Fields.fieldByName('Root') %>
|
||||||
<div class="cms-content-header-tabs">
|
<div class="cms-content-header-tabs">
|
||||||
<ul>
|
<ul>
|
||||||
<% loop Tabs %>
|
<% loop Tabs %>
|
||||||
<li><a href="#$id">$Title</a></li>
|
<li><a href="#$id">$Title</a></li>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<% end_with %>
|
<% end_with %>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="cms-content-fields center">
|
<div class="cms-content-fields center">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<% loop Fields %>$FieldHolder<% end_loop %>
|
<% loop Fields %>$FieldHolder<% end_loop %>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
```
|
```
|
||||||
|
|
||||||
Tabset template without tab navigation (e.g. `CMSTabset.ss`)
|
Tabset template without tab navigation (e.g. `CMSTabset.ss`)
|
||||||
@ -653,19 +656,19 @@ Tabset template without tab navigation (e.g. `CMSTabset.ss`)
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<div $AttributesHTML>
|
<div $AttributesHTML>
|
||||||
<% loop Tabs %>
|
<% loop Tabs %>
|
||||||
<% if Tabs %>
|
<% if Tabs %>
|
||||||
$FieldHolder
|
$FieldHolder
|
||||||
<% else %>
|
<% else %>
|
||||||
<div $AttributesHTML>
|
<div $AttributesHTML>
|
||||||
<% loop Fields %>
|
<% loop Fields %>
|
||||||
$FieldHolder
|
$FieldHolder
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</div>
|
</div>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
Lazy loading works based on the `href` attribute of the tab navigation.
|
Lazy loading works based on the `href` attribute of the tab navigation.
|
||||||
@ -679,20 +682,20 @@ and load the HTML content into the main view. Example:
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<div id="my-tab-id" class="cms-tabset" data-ignore-tab-state="true">
|
<div id="my-tab-id" class="cms-tabset" data-ignore-tab-state="true">
|
||||||
<ul>
|
<ul>
|
||||||
<li class="<% if MyActiveCondition %> ui-tabs-active<% end_if %>">
|
<li class="<% if MyActiveCondition %> ui-tabs-active<% end_if %>">
|
||||||
<a href="{$AdminURL}mytabs/tab1" class="cms-panel-link">
|
<a href="{$AdminURL}mytabs/tab1" class="cms-panel-link">
|
||||||
Tab1
|
Tab1
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="<% if MyActiveCondition %> ui-tabs-active<% end_if %>">
|
<li class="<% if MyActiveCondition %> ui-tabs-active<% end_if %>">
|
||||||
<a href="{$AdminURL}mytabs/tab2" class="cms-panel-link">
|
<a href="{$AdminURL}mytabs/tab2" class="cms-panel-link">
|
||||||
Tab2
|
Tab2
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
The URL endpoints `{$AdminURL}mytabs/tab1` and `{$AdminURL}mytabs/tab2`
|
The URL endpoints `{$AdminURL}mytabs/tab1` and `{$AdminURL}mytabs/tab2`
|
||||||
|
@ -24,7 +24,7 @@ The easiest way to update the layout of the CMS is to call `redraw` on the top-l
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
$('.cms-container').redraw();
|
$('.cms-container').redraw();
|
||||||
```
|
```
|
||||||
|
|
||||||
This causes the framework to:
|
This causes the framework to:
|
||||||
@ -66,12 +66,12 @@ Layout manager will automatically apply algorithms to the children of `.cms-cont
|
|||||||
|
|
||||||
```html
|
```html
|
||||||
|
|
||||||
<div class="cms-content-tools west cms-panel cms-panel-layout"
|
<div class="cms-content-tools west cms-panel cms-panel-layout"
|
||||||
data-expandOnClick="true"
|
data-expandOnClick="true"
|
||||||
data-layout-type="border"
|
data-layout-type="border"
|
||||||
id="cms-content-tools-CMSMain">
|
id="cms-content-tools-CMSMain">
|
||||||
<%-- content utilising border's north, south, east, west and center classes --%>
|
<%-- content utilising border's north, south, east, west and center classes --%>
|
||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
For detailed discussion on available algorithms refer to
|
For detailed discussion on available algorithms refer to
|
||||||
@ -113,7 +113,7 @@ Use provided factory method to generate algorithm instances.
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
jLayout.threeColumnCompressor(<column-spec-object>, <options-object>);
|
jLayout.threeColumnCompressor(<column-spec-object>, <options-object>);
|
||||||
```
|
```
|
||||||
|
|
||||||
The parameters are as follows:
|
The parameters are as follows:
|
||||||
|
@ -53,27 +53,27 @@ Note how the configuration happens in different entwine namespaces
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
(function($) {
|
(function($) {
|
||||||
$.entwine('ss.preview', function($){
|
$.entwine('ss.preview', function($){
|
||||||
$('.cms-preview').entwine({
|
$('.cms-preview').entwine({
|
||||||
DefaultMode: 'content',
|
DefaultMode: 'content',
|
||||||
getSizes: function() {
|
getSizes: function() {
|
||||||
var sizes = this._super();
|
var sizes = this._super();
|
||||||
sizes.mobile.width = '400px';
|
sizes.mobile.width = '400px';
|
||||||
return sizes;
|
return sizes;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
$.entwine('ss', function($){
|
$.entwine('ss', function($){
|
||||||
$('.cms-container').entwine({
|
$('.cms-container').entwine({
|
||||||
getLayoutOptions: function() {
|
getLayoutOptions: function() {
|
||||||
var opts = this._super();
|
var opts = this._super();
|
||||||
opts.minPreviewWidth = 600;
|
opts.minPreviewWidth = 600;
|
||||||
return opts;
|
return opts;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}(jQuery));
|
}(jQuery));
|
||||||
```
|
```
|
||||||
|
|
||||||
Load the file in the CMS via setting adding 'mysite/javascript/MyLeftAndMain.Preview.js'
|
Load the file in the CMS via setting adding 'mysite/javascript/MyLeftAndMain.Preview.js'
|
||||||
@ -82,9 +82,9 @@ to the `LeftAndMain.extra_requirements_javascript` [configuration value](../conf
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
LeftAndMain:
|
LeftAndMain:
|
||||||
extra_requirements_javascript:
|
extra_requirements_javascript:
|
||||||
- mysite/javascript/MyLeftAndMain.Preview.js
|
- mysite/javascript/MyLeftAndMain.Preview.js
|
||||||
```
|
```
|
||||||
|
|
||||||
In order to find out which configuration values are available, the source code
|
In order to find out which configuration values are available, the source code
|
||||||
@ -115,8 +115,8 @@ States are the site stages: _live_, _stage_ etc. Preview states are picked up
|
|||||||
from the `SilverStripeNavigator`. You can invoke the state change by calling:
|
from the `SilverStripeNavigator`. You can invoke the state change by calling:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
$('.cms-preview').entwine('.ss.preview').changeState('StageLink');
|
$('.cms-preview').entwine('.ss.preview').changeState('StageLink');
|
||||||
```
|
```
|
||||||
|
|
||||||
Note the state names come from `SilverStripeNavigatorItems` class names - thus
|
Note the state names come from `SilverStripeNavigatorItems` class names - thus
|
||||||
the _Link_ in their names. This call will also redraw the state selector to fit
|
the _Link_ in their names. This call will also redraw the state selector to fit
|
||||||
@ -126,8 +126,8 @@ list of supported states.
|
|||||||
You can get the current state by calling:
|
You can get the current state by calling:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
$('.cms-preview').entwine('.ss.preview').getCurrentStateName();
|
$('.cms-preview').entwine('.ss.preview').getCurrentStateName();
|
||||||
```
|
```
|
||||||
|
|
||||||
## Preview sizes
|
## Preview sizes
|
||||||
|
|
||||||
@ -145,14 +145,14 @@ has the benefit of redrawing the related selector and maintaining a consistent
|
|||||||
internal state:
|
internal state:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
$('.cms-preview').entwine('.ss.preview').changeSize('auto');
|
$('.cms-preview').entwine('.ss.preview').changeSize('auto');
|
||||||
```
|
```
|
||||||
|
|
||||||
You can find out current size by calling:
|
You can find out current size by calling:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
$('.cms-preview').entwine('.ss.preview').getCurrentSizeName();
|
$('.cms-preview').entwine('.ss.preview').getCurrentSizeName();
|
||||||
```
|
```
|
||||||
|
|
||||||
## Preview modes
|
## Preview modes
|
||||||
|
|
||||||
@ -161,15 +161,15 @@ algorithm, see [layout reference](cms_layout) for more details. You
|
|||||||
can change modes by calling:
|
can change modes by calling:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
$('.cms-preview').entwine('.ss.preview').changeMode('preview');
|
$('.cms-preview').entwine('.ss.preview').changeMode('preview');
|
||||||
```
|
```
|
||||||
|
|
||||||
Currently active mode is stored on the `.cms-container` along with related
|
Currently active mode is stored on the `.cms-container` along with related
|
||||||
internal states of the layout. You can reach it by calling:
|
internal states of the layout. You can reach it by calling:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
$('.cms-container').entwine('.ss').getLayoutOptions().mode;
|
$('.cms-container').entwine('.ss').getLayoutOptions().mode;
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="notice" markdown='1'>
|
<div class="notice" markdown='1'>
|
||||||
Caveat: the `.preview-mode-selector` appears twice, once in the preview and
|
Caveat: the `.preview-mode-selector` appears twice, once in the preview and
|
||||||
|
@ -8,7 +8,7 @@ SilverStripe lets you customise the style of content in the CMS. This is done by
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
HtmlEditorConfig::get('cms')->setOption('content_css', project() . '/css/editor.css');
|
HtmlEditorConfig::get('cms')->setOption('content_css', project() . '/css/editor.css');
|
||||||
```
|
```
|
||||||
|
|
||||||
Will load the `mysite/css/editor.css` file.
|
Will load the `mysite/css/editor.css` file.
|
||||||
@ -17,7 +17,7 @@ If using this config option in `mysite/_config.php`, you will have to instead ca
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
HtmlEditorConfig::get('cms')->setOption('content_css', project() . '/css/editor.css');
|
HtmlEditorConfig::get('cms')->setOption('content_css', project() . '/css/editor.css');
|
||||||
```
|
```
|
||||||
|
|
||||||
Any CSS classes within this file will be automatically added to the `WYSIWYG` editors 'style' dropdown. For instance, to
|
Any CSS classes within this file will be automatically added to the `WYSIWYG` editors 'style' dropdown. For instance, to
|
||||||
@ -26,9 +26,9 @@ add the color 'red' as an option within the `WYSIWYG` add the following to the `
|
|||||||
|
|
||||||
```css
|
```css
|
||||||
|
|
||||||
.red {
|
.red {
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="notice" markdown="1">
|
<div class="notice" markdown="1">
|
||||||
|
@ -47,11 +47,11 @@ You should write all your custom jQuery code in a closure.
|
|||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
(function($) {
|
(function($) {
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
// your code here.
|
// your code here.
|
||||||
})
|
})
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
```
|
```
|
||||||
|
|
||||||
## jQuery Plugins
|
## jQuery Plugins
|
||||||
@ -76,31 +76,31 @@ Example: A plugin to highlight a collection of elements with a configurable fore
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
// create closure
|
// create closure
|
||||||
(function($) {
|
(function($) {
|
||||||
// plugin definition
|
// plugin definition
|
||||||
$.fn.hilight = function(options) {
|
$.fn.hilight = function(options) {
|
||||||
// build main options before element iteration
|
// build main options before element iteration
|
||||||
var opts = $.extend({}, $.fn.hilight.defaults, options);
|
var opts = $.extend({}, $.fn.hilight.defaults, options);
|
||||||
// iterate and reformat each matched element
|
// iterate and reformat each matched element
|
||||||
return this.each(function() {
|
return this.each(function() {
|
||||||
$this = $(this);
|
$this = $(this);
|
||||||
// build element specific options
|
// build element specific options
|
||||||
var o = $.meta ? $.extend({}, opts, $this.data()) : opts;
|
var o = $.meta ? $.extend({}, opts, $this.data()) : opts;
|
||||||
// update element styles
|
// update element styles
|
||||||
$this.css({
|
$this.css({
|
||||||
backgroundColor: o.background,
|
backgroundColor: o.background,
|
||||||
color: o.foreground
|
color: o.foreground
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
// plugin defaults
|
// plugin defaults
|
||||||
$.fn.hilight.defaults = {
|
$.fn.hilight.defaults = {
|
||||||
foreground: "red",
|
foreground: "red",
|
||||||
background: "yellow"
|
background: "yellow"
|
||||||
};
|
};
|
||||||
// end of closure
|
// end of closure
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
```
|
```
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
@ -108,16 +108,16 @@ Usage:
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
(function($) {
|
(function($) {
|
||||||
// Highlight all buttons with default colours
|
// Highlight all buttons with default colours
|
||||||
jQuery(':button').highlight();
|
jQuery(':button').highlight();
|
||||||
|
|
||||||
// Highlight all buttons with green background
|
// Highlight all buttons with green background
|
||||||
jQuery(':button').highlight({background: "green"});
|
jQuery(':button').highlight({background: "green"});
|
||||||
|
|
||||||
// Set all further highlight() calls to have a green background
|
// Set all further highlight() calls to have a green background
|
||||||
$.fn.hilight.defaults.background = "green";
|
$.fn.hilight.defaults.background = "green";
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
```
|
```
|
||||||
|
|
||||||
## jQuery UI Widgets
|
## jQuery UI Widgets
|
||||||
@ -140,31 +140,31 @@ Example: Highlighter
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
(function($) {
|
(function($) {
|
||||||
$.widget("ui.myHighlight", {
|
$.widget("ui.myHighlight", {
|
||||||
getBlink: function () {
|
getBlink: function () {
|
||||||
return this._getData('blink');
|
return this._getData('blink');
|
||||||
},
|
},
|
||||||
setBlink: function (blink) {
|
setBlink: function (blink) {
|
||||||
this._setData('blink', blink);
|
this._setData('blink', blink);
|
||||||
if(blink) this.element.wrapInner('<blink></blink>');
|
if(blink) this.element.wrapInner('<blink></blink>');
|
||||||
else this.element.html(this.element.children().html());
|
else this.element.html(this.element.children().html());
|
||||||
},
|
},
|
||||||
_init: function() {
|
_init: function() {
|
||||||
// grab the default value and use it
|
// grab the default value and use it
|
||||||
this.element.css('background',this.options.background);
|
this.element.css('background',this.options.background);
|
||||||
this.element.css('color',this.options.foreground);
|
this.element.css('color',this.options.foreground);
|
||||||
this.setBlink(this.options.blink);
|
this.setBlink(this.options.blink);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// For demonstration purposes, this is also possible with jQuery.css()
|
// For demonstration purposes, this is also possible with jQuery.css()
|
||||||
$.ui.myHighlight.getter = "getBlink";
|
$.ui.myHighlight.getter = "getBlink";
|
||||||
$.ui.myHighlight.defaults = {
|
$.ui.myHighlight.defaults = {
|
||||||
foreground: "red",
|
foreground: "red",
|
||||||
background: "yellow",
|
background: "yellow",
|
||||||
blink: false
|
blink: false
|
||||||
};
|
};
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
```
|
```
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
@ -172,22 +172,22 @@ Usage:
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
(function($) {
|
(function($) {
|
||||||
// call with default options
|
// call with default options
|
||||||
$(':button').myHighlight();
|
$(':button').myHighlight();
|
||||||
|
|
||||||
// call with custom options
|
// call with custom options
|
||||||
$(':button').myHighlight({background: "green"});
|
$(':button').myHighlight({background: "green"});
|
||||||
|
|
||||||
// set defaults for all future instances
|
// set defaults for all future instances
|
||||||
$.ui.myHighlight.defaults.background = "green";
|
$.ui.myHighlight.defaults.background = "green";
|
||||||
|
|
||||||
// Adjust property after initialization
|
// Adjust property after initialization
|
||||||
$(':button').myHighlight('setBlink', true);
|
$(':button').myHighlight('setBlink', true);
|
||||||
|
|
||||||
// Get property
|
// Get property
|
||||||
$(':button').myHighlight('getBlink');
|
$(':button').myHighlight('getBlink');
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
```
|
```
|
||||||
|
|
||||||
### jQuery.Entwine
|
### jQuery.Entwine
|
||||||
@ -206,16 +206,16 @@ Example: Highlighter
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
(function($) {
|
(function($) {
|
||||||
$(':button').entwine({
|
$(':button').entwine({
|
||||||
Foreground: 'red',
|
Foreground: 'red',
|
||||||
Background: 'yellow',
|
Background: 'yellow',
|
||||||
highlight: function() {
|
highlight: function() {
|
||||||
this.css('background', this.getBackground());
|
this.css('background', this.getBackground());
|
||||||
this.css('color', this.getForeground());
|
this.css('color', this.getForeground());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
```
|
```
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
@ -223,16 +223,16 @@ Usage:
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
(function($) {
|
(function($) {
|
||||||
// call with default options
|
// call with default options
|
||||||
$(':button').entwine().highlight();
|
$(':button').entwine().highlight();
|
||||||
|
|
||||||
// set options for existing and new instances
|
// set options for existing and new instances
|
||||||
$(':button').entwine().setBackground('green');
|
$(':button').entwine().setBackground('green');
|
||||||
|
|
||||||
// get property
|
// get property
|
||||||
$(':button').entwine().getBackground();
|
$(':button').entwine().getBackground();
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
```
|
```
|
||||||
|
|
||||||
This is a deliberately simple example, the strength of jQuery.entwine over simple jQuery plugins lies in its public
|
This is a deliberately simple example, the strength of jQuery.entwine over simple jQuery plugins lies in its public
|
||||||
@ -256,11 +256,11 @@ Global properties are evil. They are accessible by other scripts, might be overw
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
// you can't rely on '$' being defined outside of the closure
|
// you can't rely on '$' being defined outside of the closure
|
||||||
(function($) {
|
(function($) {
|
||||||
var myPrivateVar; // only available inside the closure
|
var myPrivateVar; // only available inside the closure
|
||||||
// inside here you can use the 'jQuery' object as '$'
|
// inside here you can use the 'jQuery' object as '$'
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
```
|
```
|
||||||
|
|
||||||
You can run `[jQuery.noConflict()](http://docs.jquery.com/Core/jQuery.noConflict)` to avoid namespace clashes.
|
You can run `[jQuery.noConflict()](http://docs.jquery.com/Core/jQuery.noConflict)` to avoid namespace clashes.
|
||||||
@ -274,10 +274,10 @@ the `window.onload` and `document.ready` events.
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
// DOM elements might not be available here
|
// DOM elements might not be available here
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
// The DOM is fully loaded here
|
// The DOM is fully loaded here
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
See [jQuery FAQ: Launching Code on Document
|
See [jQuery FAQ: Launching Code on Document
|
||||||
@ -295,15 +295,15 @@ Example: Add a 'loading' classname to all pressed buttons
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
// manual binding, only applies to existing elements
|
// manual binding, only applies to existing elements
|
||||||
$('input[[type=submit]]').on('click', function() {
|
$('input[[type=submit]]').on('click', function() {
|
||||||
$(this).addClass('loading');
|
$(this).addClass('loading');
|
||||||
});
|
});
|
||||||
|
|
||||||
// binding, applies to any inserted elements as well
|
// binding, applies to any inserted elements as well
|
||||||
$('.cms-container').on('click', 'input[[type=submit]]', function() {
|
$('.cms-container').on('click', 'input[[type=submit]]', function() {
|
||||||
$(this).addClass('loading');
|
$(this).addClass('loading');
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
### Assume Element Collections
|
### Assume Element Collections
|
||||||
@ -314,12 +314,12 @@ makes sense). Encapsulate your code by nesting your jQuery commands inside a `jQ
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
$('div.MyGridField').each(function() {
|
$('div.MyGridField').each(function() {
|
||||||
// This is the over code for the tr elements inside a GridField.
|
// This is the over code for the tr elements inside a GridField.
|
||||||
$(this).find('tr').hover(
|
$(this).find('tr').hover(
|
||||||
// ...
|
// ...
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
### Use plain HTML and jQuery.data() to store data
|
### Use plain HTML and jQuery.data() to store data
|
||||||
@ -334,12 +334,12 @@ Through CSS properties
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
$('form :input').bind('change', function(e) {
|
$('form :input').bind('change', function(e) {
|
||||||
$(this.form).addClass('isChanged');
|
$(this.form).addClass('isChanged');
|
||||||
});
|
});
|
||||||
$('form').bind('submit', function(e) {
|
$('form').bind('submit', function(e) {
|
||||||
if($(this).hasClass('isChanged')) return false;
|
if($(this).hasClass('isChanged')) return false;
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Through jQuery.data()
|
Through jQuery.data()
|
||||||
@ -347,13 +347,13 @@ Through jQuery.data()
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
$('form :input').bind('change', function(e) {
|
$('form :input').bind('change', function(e) {
|
||||||
$(this.form).data('isChanged', true);
|
$(this.form).data('isChanged', true);
|
||||||
});
|
});
|
||||||
$('form').bind('submit', function(e) {
|
$('form').bind('submit', function(e) {
|
||||||
alert($(this).data('isChanged'));
|
alert($(this).data('isChanged'));
|
||||||
if($(this).data('isChanged')) return false;
|
if($(this).data('isChanged')) return false;
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
See [interactive example on jsbin.com](http://jsbin.com/opuva)
|
See [interactive example on jsbin.com](http://jsbin.com/opuva)
|
||||||
@ -367,22 +367,22 @@ Example: Restricted numeric value field
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<input type="text" class="restricted-text {min:4,max:10}" />
|
<input type="text" class="restricted-text {min:4,max:10}" />
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
$('.restricted-text').bind('change', function(e) {
|
$('.restricted-text').bind('change', function(e) {
|
||||||
if(
|
if(
|
||||||
e.target.value < $(this).metadata().min
|
e.target.value < $(this).metadata().min
|
||||||
|| e.target.value > $(this).metadata().max
|
|| e.target.value > $(this).metadata().max
|
||||||
) {
|
) {
|
||||||
alert('Invalid value');
|
alert('Invalid value');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
See [interactive example on jsbin.com](http://jsbin.com/axafa)
|
See [interactive example on jsbin.com](http://jsbin.com/axafa)
|
||||||
@ -408,34 +408,34 @@ Template:
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<% loop $Results %>
|
<% loop $Results %>
|
||||||
<li id="Result-$ID">$Title</li>
|
<li id="Result-$ID">$Title</li>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</ul>
|
</ul>
|
||||||
```
|
```
|
||||||
|
|
||||||
PHP:
|
PHP:
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
class MyController
|
class MyController
|
||||||
{
|
{
|
||||||
public function autocomplete($request)
|
public function autocomplete($request)
|
||||||
{
|
{
|
||||||
$results = Page::get()->filter("Title", $request->getVar('title'));
|
$results = Page::get()->filter("Title", $request->getVar('title'));
|
||||||
if(!$results) return new HTTPResponse("Not found", 404);
|
if(!$results) return new HTTPResponse("Not found", 404);
|
||||||
|
|
||||||
// Use HTTPResponse to pass custom status messages
|
// Use HTTPResponse to pass custom status messages
|
||||||
$this->getResponse()->setStatusCode(200, "Found " . $results->Count() . " elements");
|
$this->getResponse()->setStatusCode(200, "Found " . $results->Count() . " elements");
|
||||||
|
|
||||||
// render all results with a custom template
|
// render all results with a custom template
|
||||||
$vd = new ViewableData();
|
$vd = new ViewableData();
|
||||||
return $vd->customise([
|
return $vd->customise([
|
||||||
"Results" => $results
|
"Results" => $results
|
||||||
])->renderWith('AutoComplete');
|
])->renderWith('AutoComplete');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -444,13 +444,13 @@ HTML
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<form action"#">
|
<form action"#">
|
||||||
<div class="autocomplete {url:'MyController/autocomplete'}">
|
<div class="autocomplete {url:'MyController/autocomplete'}">
|
||||||
<input type="text" name="title" />
|
<input type="text" name="title" />
|
||||||
<div class="results" style="display: none;">
|
<div class="results" style="display: none;">
|
||||||
</div>
|
</div>
|
||||||
<input type="submit" value="action_autocomplete" />
|
<input type="submit" value="action_autocomplete" />
|
||||||
</form>
|
</form>
|
||||||
```
|
```
|
||||||
|
|
||||||
JavaScript:
|
JavaScript:
|
||||||
@ -458,23 +458,23 @@ JavaScript:
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
$('.autocomplete input').on('change', function() {
|
$('.autocomplete input').on('change', function() {
|
||||||
var resultsEl = $(this).siblings('.results');
|
var resultsEl = $(this).siblings('.results');
|
||||||
resultsEl.load(
|
resultsEl.load(
|
||||||
// get form action, using the jQuery.metadata plugin
|
// get form action, using the jQuery.metadata plugin
|
||||||
$(this).parent().metadata().url,
|
$(this).parent().metadata().url,
|
||||||
// submit all form values
|
// submit all form values
|
||||||
$(this.form).serialize(),
|
$(this.form).serialize(),
|
||||||
// callback after data is loaded
|
// callback after data is loaded
|
||||||
function(data, status) {
|
function(data, status) {
|
||||||
resultsEl.show();
|
resultsEl.show();
|
||||||
// get all record IDs from the new HTML
|
// get all record IDs from the new HTML
|
||||||
var ids = jQuery('.results').find('li').map(function() {
|
var ids = jQuery('.results').find('li').map(function() {
|
||||||
return $(this).attr('id').replace(/Record\-/,'');
|
return $(this).attr('id').replace(/Record\-/,'');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Although they are the minority of cases, there are times when a simple HTML fragment isn't enough. For example, if you
|
Although they are the minority of cases, there are times when a simple HTML fragment isn't enough. For example, if you
|
||||||
@ -499,20 +499,20 @@ Example: Trigger custom 'validationfailed' event on form submission for each emp
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
$('form').bind('submit', function(e) {
|
$('form').bind('submit', function(e) {
|
||||||
// $(this) refers to form
|
// $(this) refers to form
|
||||||
$(this).find(':input').each(function() {
|
$(this).find(':input').each(function() {
|
||||||
// $(this) in here refers to input field
|
// $(this) in here refers to input field
|
||||||
if(!$(this).val()) $(this).trigger('validationfailed');
|
if(!$(this).val()) $(this).trigger('validationfailed');
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
// listen to custom event on each <input> field
|
// listen to custom event on each <input> field
|
||||||
$('form :input').bind('validationfailed',function(e) {
|
$('form :input').bind('validationfailed',function(e) {
|
||||||
// $(this) refers to input field
|
// $(this) refers to input field
|
||||||
alert($(this).attr('name'));
|
alert($(this).attr('name'));
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
See [interactive example on jsbin.com](http://jsbin.com/ipeca).
|
See [interactive example on jsbin.com](http://jsbin.com/ipeca).
|
||||||
@ -558,52 +558,52 @@ Example: jQuery.entwine
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
||||||
* Available Custom Events:
|
* Available Custom Events:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>ajaxsubmit</li>
|
* <li>ajaxsubmit</li>
|
||||||
* <li>validate</li>
|
* <li>validate</li>
|
||||||
* <li>reloadeditform</li>
|
* <li>reloadeditform</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @class Main LeftAndMain interface with some control panel and an edit form.
|
* @class Main LeftAndMain interface with some control panel and an edit form.
|
||||||
* @name ss.LeftAndMain
|
* @name ss.LeftAndMain
|
||||||
*/
|
*/
|
||||||
$('.LeftAndMain').entwine('ss', function($){
|
$('.LeftAndMain').entwine('ss', function($){
|
||||||
return/** @lends ss.LeftAndMain */ {
|
return/** @lends ss.LeftAndMain */ {
|
||||||
/**
|
/**
|
||||||
|
|
||||||
* Reference to some property
|
* Reference to some property
|
||||||
* @type Number
|
* @type Number
|
||||||
*/
|
*/
|
||||||
MyProperty: 123,
|
MyProperty: 123,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
||||||
* Renders the provided data into an unordered list.
|
* Renders the provided data into an unordered list.
|
||||||
*
|
*
|
||||||
* @param {Object} data
|
* @param {Object} data
|
||||||
* @param {String} status
|
* @param {String} status
|
||||||
* @return {String} HTML unordered list
|
* @return {String} HTML unordered list
|
||||||
*/
|
*/
|
||||||
publicMethod: function(data, status) {
|
publicMethod: function(data, status) {
|
||||||
return '<ul>'
|
return '<ul>'
|
||||||
+ /...
|
+ /...
|
||||||
+ '</ul>';
|
+ '</ul>';
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
||||||
* Won't show in documentation, but still worth documenting.
|
* Won't show in documentation, but still worth documenting.
|
||||||
*
|
*
|
||||||
* @return {String} Something else.
|
* @return {String} Something else.
|
||||||
*/
|
*/
|
||||||
_privateMethod: function() {
|
_privateMethod: function() {
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
]]);
|
]]);
|
||||||
```
|
```
|
||||||
|
|
||||||
### Unit Testing
|
### Unit Testing
|
||||||
@ -619,27 +619,27 @@ Example: QUnit test (from [jquery.com](http://docs.jquery.com/QUnit#Using_QUnit)
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
test("a basic test example", function() {
|
test("a basic test example", function() {
|
||||||
ok( true, "this test is fine" );
|
ok( true, "this test is fine" );
|
||||||
var value = "hello";
|
var value = "hello";
|
||||||
equals( "hello", value, "We expect value to be hello" );
|
equals( "hello", value, "We expect value to be hello" );
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Example: JSpec Shopping cart test (from [visionmedia.github.com](http://visionmedia.github.com/jspec/))
|
Example: JSpec Shopping cart test (from [visionmedia.github.com](http://visionmedia.github.com/jspec/))
|
||||||
```
|
```
|
||||||
describe 'ShoppingCart'
|
describe 'ShoppingCart'
|
||||||
before_each
|
before_each
|
||||||
cart = new ShoppingCart
|
cart = new ShoppingCart
|
||||||
end
|
end
|
||||||
describe 'addProduct'
|
describe 'addProduct'
|
||||||
it 'should add a product'
|
it 'should add a product'
|
||||||
cart.addProduct('cookie')
|
cart.addProduct('cookie')
|
||||||
cart.addProduct('icecream')
|
cart.addProduct('icecream')
|
||||||
cart.should.have 2, 'products'
|
cart.should.have 2, 'products'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
## Related
|
## Related
|
||||||
|
|
||||||
|
@ -23,19 +23,19 @@ React's job is to render UI. Its UI elements are known as "components" and repre
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
<PhotoItem size={200} caption="Angkor Wat" onSelect={openLightbox}>
|
<PhotoItem size={200} caption="Angkor Wat" onSelect={openLightbox}>
|
||||||
<img src="path/to/image.jpg" />
|
<img src="path/to/image.jpg" />
|
||||||
</PhotoItem>
|
</PhotoItem>
|
||||||
```
|
```
|
||||||
|
|
||||||
Might actually render HTML that looks like this:
|
Might actually render HTML that looks like this:
|
||||||
```html
|
```html
|
||||||
<div class="photo-item">
|
<div class="photo-item">
|
||||||
<div class="photo" style="width:200px;height:200px;">
|
<div class="photo" style="width:200px;height:200px;">
|
||||||
<img src="path/to/image.jpg">
|
<img src="path/to/image.jpg">
|
||||||
</div>
|
</div>
|
||||||
<div class="photo-caption">
|
<div class="photo-caption">
|
||||||
<h3><a>Angkor Wat/a></h3>
|
<h3><a>Angkor Wat/a></h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -91,18 +91,18 @@ engine.
|
|||||||
|
|
||||||
```
|
```
|
||||||
query GetUser($ID: Int!) {
|
query GetUser($ID: Int!) {
|
||||||
user {
|
user {
|
||||||
name
|
name
|
||||||
email
|
email
|
||||||
blogPosts {
|
blogPosts {
|
||||||
title
|
title
|
||||||
comments(Limit: 5) {
|
comments(Limit: 5) {
|
||||||
author
|
author
|
||||||
comment
|
comment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -110,21 +110,21 @@ The above query is almost self-descriptive. It gets a user by ID, returns his or
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
{
|
{
|
||||||
"user": {
|
"user": {
|
||||||
"name": "Test user",
|
"name": "Test user",
|
||||||
"email": "me@example.com",
|
"email": "me@example.com",
|
||||||
"blogPosts": [
|
"blogPosts": [
|
||||||
{
|
{
|
||||||
"title": "How to be awesome at GraphQL",
|
"title": "How to be awesome at GraphQL",
|
||||||
"comments": [
|
"comments": [
|
||||||
{
|
{
|
||||||
"author": "Uncle Cheese",
|
"author": "Uncle Cheese",
|
||||||
"comment": "Nice stuff, bro"
|
"comment": "Nice stuff, bro"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -176,10 +176,10 @@ Where `next()` is the next customisation in the "chain" of middleware. Before in
|
|||||||
```js
|
```js
|
||||||
import thirdPartyLogger from 'third-party-logger';
|
import thirdPartyLogger from 'third-party-logger';
|
||||||
const addLoggingMiddleware = (next) => (error) => {
|
const addLoggingMiddleware = (next) => (error) => {
|
||||||
if (error.type === LoggingService.CRITICAL) {
|
if (error.type === LoggingService.CRITICAL) {
|
||||||
thirdpartyLogger.send(error.message);
|
thirdpartyLogger.send(error.message);
|
||||||
}
|
}
|
||||||
return next(error);
|
return next(error);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -198,18 +198,18 @@ import LoggingService from './LoggingService';
|
|||||||
import thirdPartyLogger from 'third-party-logger';
|
import thirdPartyLogger from 'third-party-logger';
|
||||||
|
|
||||||
const addLoggingMiddleware = (next) => (error) => {
|
const addLoggingMiddleware = (next) => (error) => {
|
||||||
// Critical errors go to a thirdparty service
|
// Critical errors go to a thirdparty service
|
||||||
if (error.type === LoggingService.CRITICAL) {
|
if (error.type === LoggingService.CRITICAL) {
|
||||||
thirdPartyLogger.send(error.message);
|
thirdPartyLogger.send(error.message);
|
||||||
}
|
}
|
||||||
// Other errors get logged, but not to our thirdparty
|
// Other errors get logged, but not to our thirdparty
|
||||||
else if (error.type === LoggingService.ERROR) {
|
else if (error.type === LoggingService.ERROR) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
// Minor errors are ignored
|
// Minor errors are ignored
|
||||||
else {
|
else {
|
||||||
// Do nothing!
|
// Do nothing!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -253,12 +253,12 @@ __someone-elses-module/js/main.js__
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
Injector.transform(
|
Injector.transform(
|
||||||
'my-transformation',
|
'my-transformation',
|
||||||
(updater) => {
|
(updater) => {
|
||||||
updater.component('MyComponent', MyCustomComponent);
|
updater.component('MyComponent', MyCustomComponent);
|
||||||
updater.reducer('myCustom', MyCustomReducer);
|
updater.reducer('myCustom', MyCustomReducer);
|
||||||
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -296,13 +296,13 @@ __my-module/js/main.js__
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
Injector.transform(
|
Injector.transform(
|
||||||
'my-transformation',
|
'my-transformation',
|
||||||
(updater) => {
|
(updater) => {
|
||||||
updater.component('MyComponent', MyCustomComponent);
|
updater.component('MyComponent', MyCustomComponent);
|
||||||
updater.reducer('myCustom', MyCustomReducer);
|
updater.reducer('myCustom', MyCustomReducer);
|
||||||
|
|
||||||
},
|
},
|
||||||
{ after: 'another-module' }
|
{ after: 'another-module' }
|
||||||
);
|
);
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -349,17 +349,17 @@ Likewise, services can be applied for specific contexts.
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
Injector.transform('my-transform', (updater) => {
|
Injector.transform('my-transform', (updater) => {
|
||||||
// Applies to all text fields in AssetAdmin
|
// Applies to all text fields in AssetAdmin
|
||||||
updater.component('TextField.AssetAdmin', MyTextField);
|
updater.component('TextField.AssetAdmin', MyTextField);
|
||||||
|
|
||||||
// Applies to all text fields in AssetAdmin editform
|
// Applies to all text fields in AssetAdmin editform
|
||||||
updater.component('TextField.AssetAdmin.FileEditForm', MyTextField);
|
updater.component('TextField.AssetAdmin.FileEditForm', MyTextField);
|
||||||
|
|
||||||
// Applies to any textfield named "Title" in AssetAdmin
|
// Applies to any textfield named "Title" in AssetAdmin
|
||||||
updater.component('TextField.AssetAdmin.*.Title', MyTextField);
|
updater.component('TextField.AssetAdmin.*.Title', MyTextField);
|
||||||
|
|
||||||
// Applies to any textfield named "Title" in any admin
|
// Applies to any textfield named "Title" in any admin
|
||||||
updater.component('TextField.*.*.Title', MyTextField);
|
updater.component('TextField.*.*.Title', MyTextField);
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -379,16 +379,16 @@ Using the `PhotoItem` example above, let's create a customised `PhotoItem` that
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
const enhancedPhoto = (PhotoItem) => (props) => {
|
const enhancedPhoto = (PhotoItem) => (props) => {
|
||||||
const badge = props.isNew ?
|
const badge = props.isNew ?
|
||||||
<div className="badge">New!</div> :
|
<div className="badge">New!</div> :
|
||||||
null;
|
null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{badge}
|
{badge}
|
||||||
<PhotoItem {...props} />
|
<PhotoItem {...props} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const EnhancedPhotoItem = enhancedPhoto(PhotoItem);
|
const EnhancedPhotoItem = enhancedPhoto(PhotoItem);
|
||||||
@ -401,21 +401,21 @@ function.
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
const enhancedPhoto = (PhotoItem) => {
|
const enhancedPhoto = (PhotoItem) => {
|
||||||
return class EnhancedPhotoItem extends React.Component {
|
return class EnhancedPhotoItem extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
const badge = this.props.isNew ?
|
const badge = this.props.isNew ?
|
||||||
<div className="badge">New!</div> :
|
<div className="badge">New!</div> :
|
||||||
null;
|
null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{badge}
|
{badge}
|
||||||
<PhotoItem {...this.props} />
|
<PhotoItem {...this.props} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -509,17 +509,17 @@ Most behavioural and aesthetic customisations will happen via a mutation of the
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
Injector.transform(
|
Injector.transform(
|
||||||
'my-custom-form',
|
'my-custom-form',
|
||||||
(updater) => {
|
(updater) => {
|
||||||
updater.form.alterSchema(
|
updater.form.alterSchema(
|
||||||
'AssetAdmin.*',
|
'AssetAdmin.*',
|
||||||
(form) =>
|
(form) =>
|
||||||
form.updateField('Title', {
|
form.updateField('Title', {
|
||||||
myCustomProp: true
|
myCustomProp: true
|
||||||
})
|
})
|
||||||
.getState()
|
.getState()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -28,21 +28,21 @@ Here is the configuration code for the button:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function getCMSActions()
|
public function getCMSActions()
|
||||||
{
|
{
|
||||||
$fields = parent::getCMSActions();
|
$fields = parent::getCMSActions();
|
||||||
|
|
||||||
$fields->fieldByName('MajorActions')->push(
|
$fields->fieldByName('MajorActions')->push(
|
||||||
$cleanupAction = FormAction::create('cleanup', 'Cleaned')
|
$cleanupAction = FormAction::create('cleanup', 'Cleaned')
|
||||||
// Set up an icon for the neutral state that will use the default text.
|
// Set up an icon for the neutral state that will use the default text.
|
||||||
->setAttribute('data-icon', 'accept')
|
->setAttribute('data-icon', 'accept')
|
||||||
// Initialise the alternate constructive state.
|
// Initialise the alternate constructive state.
|
||||||
->setAttribute('data-icon-alternate', 'addpage')
|
->setAttribute('data-icon-alternate', 'addpage')
|
||||||
->setAttribute('data-text-alternate', 'Clean-up now')
|
->setAttribute('data-text-alternate', 'Clean-up now')
|
||||||
);
|
);
|
||||||
|
|
||||||
return $fields;
|
return $fields;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
You can control the state of the button from the backend by applying `ss-ui-alternate` class to the `FormAction`. To
|
You can control the state of the button from the backend by applying `ss-ui-alternate` class to the `FormAction`. To
|
||||||
@ -55,15 +55,15 @@ Here we initialise the button based on the backend check, and assume that the bu
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
public function getCMSActions()
|
public function getCMSActions()
|
||||||
{
|
{
|
||||||
// ...
|
// ...
|
||||||
if ($this->needsCleaning()) {
|
if ($this->needsCleaning()) {
|
||||||
// Will initialise the button into alternate state.
|
// Will initialise the button into alternate state.
|
||||||
$cleanupAction->addExtraClass('ss-ui-alternate');
|
$cleanupAction->addExtraClass('ss-ui-alternate');
|
||||||
}
|
}
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Frontend support
|
## Frontend support
|
||||||
@ -81,7 +81,7 @@ First of all, you can toggle the state of the button - execute this code in the
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
jQuery('.cms-edit-form .btn-toolbar #Form_EditForm_action_cleanup').button('toggleAlternate');
|
jQuery('.cms-edit-form .btn-toolbar #Form_EditForm_action_cleanup').button('toggleAlternate');
|
||||||
```
|
```
|
||||||
|
|
||||||
Another, more useful, scenario is to check the current state.
|
Another, more useful, scenario is to check the current state.
|
||||||
@ -89,7 +89,7 @@ Another, more useful, scenario is to check the current state.
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
jQuery('.cms-edit-form .btn-toolbar #Form_EditForm_action_cleanup').button('option', 'showingAlternate');
|
jQuery('.cms-edit-form .btn-toolbar #Form_EditForm_action_cleanup').button('option', 'showingAlternate');
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also force the button into a specific state by using UI options.
|
You can also force the button into a specific state by using UI options.
|
||||||
@ -97,7 +97,7 @@ You can also force the button into a specific state by using UI options.
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
jQuery('.cms-edit-form .btn-toolbar #Form_EditForm_action_cleanup').button({showingAlternate: true});
|
jQuery('.cms-edit-form .btn-toolbar #Form_EditForm_action_cleanup').button({showingAlternate: true});
|
||||||
```
|
```
|
||||||
|
|
||||||
This will allow you to react to user actions in the CMS and give immediate feedback. Here is an example taken from the
|
This will allow you to react to user actions in the CMS and give immediate feedback. Here is an example taken from the
|
||||||
@ -107,23 +107,23 @@ CMS core that tracks the changes to the input fields and reacts by enabling the
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable save buttons upon detecting changes to content.
|
* Enable save buttons upon detecting changes to content.
|
||||||
* "changed" class is added by jQuery.changetracker.
|
* "changed" class is added by jQuery.changetracker.
|
||||||
*/
|
*/
|
||||||
$('.cms-edit-form .changed').entwine({
|
$('.cms-edit-form .changed').entwine({
|
||||||
// This will execute when the class is added to the element.
|
// This will execute when the class is added to the element.
|
||||||
onmatch: function(e) {
|
onmatch: function(e) {
|
||||||
var form = this.closest('.cms-edit-form');
|
var form = this.closest('.cms-edit-form');
|
||||||
form.find('#Form_EditForm_action_save').button({showingAlternate: true});
|
form.find('#Form_EditForm_action_save').button({showingAlternate: true});
|
||||||
form.find('#Form_EditForm_action_publish').button({showingAlternate: true});
|
form.find('#Form_EditForm_action_publish').button({showingAlternate: true});
|
||||||
this._super(e);
|
this._super(e);
|
||||||
},
|
},
|
||||||
// Entwine requires us to define this, even if we don't use it.
|
// Entwine requires us to define this, even if we don't use it.
|
||||||
onunmatch: function(e) {
|
onunmatch: function(e) {
|
||||||
this._super(e);
|
this._super(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
## Frontend hooks
|
## Frontend hooks
|
||||||
@ -157,26 +157,26 @@ cases.
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
(function($) {
|
(function($) {
|
||||||
|
|
||||||
$.entwine('mysite', function($){
|
$.entwine('mysite', function($){
|
||||||
$('.cms-edit-form .btn-toolbar #Form_EditForm_action_cleanup').entwine({
|
$('.cms-edit-form .btn-toolbar #Form_EditForm_action_cleanup').entwine({
|
||||||
/**
|
/**
|
||||||
* onafterrefreshalternate is SS-specific jQuery UI hook that is executed
|
* onafterrefreshalternate is SS-specific jQuery UI hook that is executed
|
||||||
* every time the button is rendered (including on initialisation).
|
* every time the button is rendered (including on initialisation).
|
||||||
*/
|
*/
|
||||||
onbuttonafterrefreshalternate: function() {
|
onbuttonafterrefreshalternate: function() {
|
||||||
if (this.button('option', 'showingAlternate')) {
|
if (this.button('option', 'showingAlternate')) {
|
||||||
this.addClass('ss-ui-action-constructive');
|
this.addClass('ss-ui-action-constructive');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.removeClass('ss-ui-action-constructive');
|
this.removeClass('ss-ui-action-constructive');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
}(jQuery));
|
}(jQuery));
|
||||||
```
|
```
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
@ -11,8 +11,8 @@ at the last position within the field, and expects unescaped HTML content.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
TextField::create('MyText', 'My Text Label')
|
TextField::create('MyText', 'My Text Label')
|
||||||
->setDescription('More <strong>detailed</strong> help');
|
->setDescription('More <strong>detailed</strong> help');
|
||||||
```
|
```
|
||||||
|
|
||||||
To show the help text as a tooltip instead of inline,
|
To show the help text as a tooltip instead of inline,
|
||||||
@ -20,9 +20,9 @@ add a `.cms-description-tooltip` class.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
TextField::create('MyText', 'My Text Label')
|
TextField::create('MyText', 'My Text Label')
|
||||||
->setDescription('More <strong>detailed</strong> help')
|
->setDescription('More <strong>detailed</strong> help')
|
||||||
->addExtraClass('cms-description-tooltip');
|
->addExtraClass('cms-description-tooltip');
|
||||||
```
|
```
|
||||||
|
|
||||||
Tooltips are only supported
|
Tooltips are only supported
|
||||||
@ -40,9 +40,9 @@ by clicking the 'info' icon displayed alongside the field.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
TextField::create('MyText', 'My Text Label')
|
TextField::create('MyText', 'My Text Label')
|
||||||
->setDescription('More <strong>detailed</strong> help')
|
->setDescription('More <strong>detailed</strong> help')
|
||||||
->addExtraClass('cms-description-toggle');
|
->addExtraClass('cms-description-toggle');
|
||||||
```
|
```
|
||||||
|
|
||||||
If you want to provide a custom icon for toggling the description, you can do that
|
If you want to provide a custom icon for toggling the description, you can do that
|
||||||
@ -50,10 +50,10 @@ by setting an additional `RightTitle`.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
TextField::create('MyText', 'My Text Label')
|
TextField::create('MyText', 'My Text Label')
|
||||||
->setDescription('More <strong>detailed</strong> help')
|
->setDescription('More <strong>detailed</strong> help')
|
||||||
->addExtraClass('cms-description-toggle')
|
->addExtraClass('cms-description-toggle')
|
||||||
->setRightTitle('<a class="cms-description-trigger">My custom icon</a>');
|
->setRightTitle('<a class="cms-description-trigger">My custom icon</a>');
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: For more advanced help text we recommend using
|
Note: For more advanced help text we recommend using
|
||||||
|
@ -22,13 +22,13 @@ black-and-transparent PNG graphics. In this case we'll place the icon in
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Admin\ModelAdmin;
|
use SilverStripe\Admin\ModelAdmin;
|
||||||
|
|
||||||
class ProductAdmin extends ModelAdmin
|
class ProductAdmin extends ModelAdmin
|
||||||
{
|
{
|
||||||
// ...
|
// ...
|
||||||
private static $menu_icon = 'mysite/images/product-icon.png';
|
private static $menu_icon = 'mysite/images/product-icon.png';
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Defining a Custom Title
|
### Defining a Custom Title
|
||||||
@ -39,13 +39,13 @@ controller, removing the "Admin" bit at the end.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Admin\ModelAdmin;
|
use SilverStripe\Admin\ModelAdmin;
|
||||||
|
|
||||||
class ProductAdmin extends ModelAdmin
|
class ProductAdmin extends ModelAdmin
|
||||||
{
|
{
|
||||||
// ...
|
// ...
|
||||||
private static $menu_title = 'My Custom Admin';
|
private static $menu_title = 'My Custom Admin';
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
In order to localize the menu title in different languages, use the
|
In order to localize the menu title in different languages, use the
|
||||||
@ -66,36 +66,36 @@ button configuration.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Admin\CMSMenu;
|
use SilverStripe\Admin\CMSMenu;
|
||||||
use SilverStripe\Admin\LeftAndMainExtension;
|
use SilverStripe\Admin\LeftAndMainExtension;
|
||||||
|
|
||||||
class CustomLeftAndMain extends LeftAndMainExtension
|
class CustomLeftAndMain extends LeftAndMainExtension
|
||||||
{
|
{
|
||||||
|
|
||||||
public function init()
|
public function init()
|
||||||
{
|
{
|
||||||
// unique identifier for this item. Will have an ID of Menu-$ID
|
// unique identifier for this item. Will have an ID of Menu-$ID
|
||||||
$id = 'LinkToGoogle';
|
$id = 'LinkToGoogle';
|
||||||
|
|
||||||
// your 'nice' title
|
// your 'nice' title
|
||||||
$title = 'Google';
|
$title = 'Google';
|
||||||
|
|
||||||
// the link you want to item to go to
|
// the link you want to item to go to
|
||||||
$link = 'http://google.com';
|
$link = 'http://google.com';
|
||||||
|
|
||||||
// priority controls the ordering of the link in the stack. The
|
// priority controls the ordering of the link in the stack. The
|
||||||
// lower the number, the lower in the list
|
// lower the number, the lower in the list
|
||||||
$priority = -2;
|
$priority = -2;
|
||||||
|
|
||||||
// Add your own attributes onto the link. In our case, we want to
|
// Add your own attributes onto the link. In our case, we want to
|
||||||
// open the link in a new window (not the original)
|
// open the link in a new window (not the original)
|
||||||
$attributes = [
|
$attributes = [
|
||||||
'target' => '_blank'
|
'target' => '_blank'
|
||||||
];
|
];
|
||||||
|
|
||||||
CMSMenu::add_link($id, $title, $link, $priority, $attributes);
|
CMSMenu::add_link($id, $title, $link, $priority, $attributes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -105,7 +105,7 @@ class. For more information about configuring extensions see the
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
LeftAndMain::add_extension('CustomLeftAndMain')
|
LeftAndMain::add_extension('CustomLeftAndMain')
|
||||||
```
|
```
|
||||||
|
|
||||||
## Related
|
## Related
|
||||||
|
@ -19,21 +19,21 @@ hypothetical `NewsPageHolder` type, which contains `NewsPage` children.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use Page;
|
use Page;
|
||||||
|
|
||||||
// mysite/code/NewsPageHolder.php
|
// mysite/code/NewsPageHolder.php
|
||||||
class NewsPageHolder extends Page
|
class NewsPageHolder extends Page
|
||||||
{
|
{
|
||||||
private static $allowed_children = ['NewsPage'];
|
private static $allowed_children = ['NewsPage'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// mysite/code/NewsPage.php
|
// mysite/code/NewsPage.php
|
||||||
class NewsPage extends Page
|
class NewsPage extends Page
|
||||||
{
|
{
|
||||||
private static $has_one = [
|
private static $has_one = [
|
||||||
'Author' => 'Member',
|
'Author' => 'Member',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -45,41 +45,41 @@ or across page types with common characteristics.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use Page;
|
use Page;
|
||||||
use SilverStripe\Core\Extension;
|
use SilverStripe\Core\Extension;
|
||||||
|
|
||||||
// mysite/code/NewsPageHolderCMSMainExtension.php
|
// mysite/code/NewsPageHolderCMSMainExtension.php
|
||||||
class NewsPageHolderCMSMainExtension extends Extension
|
class NewsPageHolderCMSMainExtension extends Extension
|
||||||
{
|
{
|
||||||
function updateListView($listView) {
|
function updateListView($listView) {
|
||||||
$parentId = $listView->getController()->getRequest()->requestVar('ParentID');
|
$parentId = $listView->getController()->getRequest()->requestVar('ParentID');
|
||||||
$parent = ($parentId) ? Page::get()->byId($parentId) : new Page();
|
$parent = ($parentId) ? Page::get()->byId($parentId) : new Page();
|
||||||
|
|
||||||
// Only apply logic for this page type
|
// Only apply logic for this page type
|
||||||
if($parent && $parent instanceof NewsPageHolder) {
|
if($parent && $parent instanceof NewsPageHolder) {
|
||||||
$gridField = $listView->Fields()->dataFieldByName('Page');
|
$gridField = $listView->Fields()->dataFieldByName('Page');
|
||||||
if($gridField) {
|
if($gridField) {
|
||||||
// Sort by created
|
// Sort by created
|
||||||
$list = $gridField->getList();
|
$list = $gridField->getList();
|
||||||
$gridField->setList($list->sort('Created', 'DESC'));
|
$gridField->setList($list->sort('Created', 'DESC'));
|
||||||
// Add author to columns
|
// Add author to columns
|
||||||
$cols = $gridField->getConfig()->getComponentByType('GridFieldDataColumns');
|
$cols = $gridField->getConfig()->getComponentByType('GridFieldDataColumns');
|
||||||
if($cols) {
|
if($cols) {
|
||||||
$fields = $cols->getDisplayFields($gridField);
|
$fields = $cols->getDisplayFields($gridField);
|
||||||
$fields['Author.Title'] = 'Author';
|
$fields['Author.Title'] = 'Author';
|
||||||
$cols->setDisplayFields($fields);
|
$cols->setDisplayFields($fields);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Now you just need to enable the extension in your [configuration file](../../configuration).
|
Now you just need to enable the extension in your [configuration file](../../configuration).
|
||||||
```yml
|
```yml
|
||||||
// mysite/_config/config.yml
|
// mysite/_config/config.yml
|
||||||
LeftAndMain:
|
LeftAndMain:
|
||||||
extensions:
|
extensions:
|
||||||
- NewsPageHolderCMSMainExtension
|
- NewsPageHolderCMSMainExtension
|
||||||
```
|
```
|
||||||
You're all set! Don't forget to flush the caches by appending `?flush=all` to the URL.
|
You're all set! Don't forget to flush the caches by appending `?flush=all` to the URL.
|
||||||
|
@ -23,24 +23,24 @@ code like this:
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
...
|
...
|
||||||
<ul>
|
<ul>
|
||||||
...
|
...
|
||||||
<li id="record-15" class="class-Page closed jstree-leaf jstree-unchecked" data-id="15">
|
<li id="record-15" class="class-Page closed jstree-leaf jstree-unchecked" data-id="15">
|
||||||
<ins class="jstree-icon"> </ins>
|
<ins class="jstree-icon"> </ins>
|
||||||
<a class="" title="Page type: Page" href="{$AdminURL}page/edit/show/15">
|
<a class="" title="Page type: Page" href="{$AdminURL}page/edit/show/15">
|
||||||
<ins class="jstree-checkbox"> </ins>
|
<ins class="jstree-checkbox"> </ins>
|
||||||
<ins class="jstree-icon"> </ins>
|
<ins class="jstree-icon"> </ins>
|
||||||
<span class="text">
|
<span class="text">
|
||||||
<span class="jstree-pageicon"></span>
|
<span class="jstree-pageicon"></span>
|
||||||
<span class="item" title="Deleted">New Page</span>
|
<span class="item" title="Deleted">New Page</span>
|
||||||
<span class="badge deletedonlive">Deleted</span>
|
<span class="badge deletedonlive">Deleted</span>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
...
|
...
|
||||||
</ul>
|
</ul>
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
By applying the proper style sheet, the snippet html above could produce the look of:
|
By applying the proper style sheet, the snippet html above could produce the look of:
|
||||||
@ -66,22 +66,22 @@ __Example: using a subclass__
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
|
||||||
class Page extends SiteTree
|
class Page extends SiteTree
|
||||||
{
|
{
|
||||||
public function getScheduledToPublish()
|
public function getScheduledToPublish()
|
||||||
{
|
{
|
||||||
// return either true or false
|
// return either true or false
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getStatusFlags($cached = true)
|
public function getStatusFlags($cached = true)
|
||||||
{
|
{
|
||||||
$flags = parent::getStatusFlags($cached);
|
$flags = parent::getStatusFlags($cached);
|
||||||
$flags['scheduledtopublish'] = "Scheduled To Publish";
|
$flags['scheduledtopublish'] = "Scheduled To Publish";
|
||||||
return $flags;
|
return $flags;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The above subclass of [SiteTree](api:SilverStripe\CMS\Model\SiteTree) will add a new flag for indicating its
|
The above subclass of [SiteTree](api:SilverStripe\CMS\Model\SiteTree) will add a new flag for indicating its
|
||||||
|
@ -123,7 +123,7 @@ const CharacterCounter = (TextField) => (props) => {
|
|||||||
<div>
|
<div>
|
||||||
<TextField {...props} />
|
<TextField {...props} />
|
||||||
{showWarning &&
|
{showWarning &&
|
||||||
<small>Characters remaining: {remainingChars}</small>
|
<small>Characters remaining: {remainingChars}</small>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -34,34 +34,34 @@ The following example will create a report to list every page on the current sit
|
|||||||
|
|
||||||
###CustomSideReport.php
|
###CustomSideReport.php
|
||||||
```php
|
```php
|
||||||
use Page;
|
use Page;
|
||||||
use SilverStripe\Reports\Report;
|
use SilverStripe\Reports\Report;
|
||||||
|
|
||||||
class CustomSideReport_NameOfReport extends Report
|
class CustomSideReport_NameOfReport extends Report
|
||||||
{
|
{
|
||||||
|
|
||||||
// the name of the report
|
// the name of the report
|
||||||
public function title()
|
public function title()
|
||||||
{
|
{
|
||||||
return 'All Pages';
|
return 'All Pages';
|
||||||
}
|
}
|
||||||
|
|
||||||
// what we want the report to return
|
// what we want the report to return
|
||||||
public function sourceRecords($params = null)
|
public function sourceRecords($params = null)
|
||||||
{
|
{
|
||||||
return Page::get()->sort('Title');
|
return Page::get()->sort('Title');
|
||||||
}
|
}
|
||||||
|
|
||||||
// which fields on that object we want to show
|
// which fields on that object we want to show
|
||||||
public function columns()
|
public function columns()
|
||||||
{
|
{
|
||||||
$fields = [
|
$fields = [
|
||||||
'Title' => 'Title'
|
'Title' => 'Title'
|
||||||
];
|
];
|
||||||
|
|
||||||
return $fields;
|
return $fields;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -31,17 +31,17 @@ the CMS logic. Add a new section into the `<ul class="cms-menu-list">`
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
...
|
...
|
||||||
<ul class="cms-menu-list">
|
<ul class="cms-menu-list">
|
||||||
<!-- ... -->
|
<!-- ... -->
|
||||||
<li class="bookmarked-link first">
|
<li class="bookmarked-link first">
|
||||||
<a href="{$AdminURL}pages/edit/show/1">Edit "My popular page"</a>
|
<a href="{$AdminURL}pages/edit/show/1">Edit "My popular page"</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="bookmarked-link last">
|
<li class="bookmarked-link last">
|
||||||
<a href="{$AdminURL}pages/edit/show/99">Edit "My other page"</a>
|
<a href="{$AdminURL}pages/edit/show/99">Edit "My other page"</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
Refresh the CMS interface with `admin/?flush=all`, and you should see those
|
Refresh the CMS interface with `admin/?flush=all`, and you should see those
|
||||||
@ -57,7 +57,7 @@ with the CMS interface. Paste the following content into a new file called
|
|||||||
|
|
||||||
```css
|
```css
|
||||||
|
|
||||||
.bookmarked-link.first {margin-top: 1em;}
|
.bookmarked-link.first {margin-top: 1em;}
|
||||||
```
|
```
|
||||||
|
|
||||||
Load the new CSS file into the CMS, by setting the `LeftAndMain.extra_requirements_css`
|
Load the new CSS file into the CMS, by setting the `LeftAndMain.extra_requirements_css`
|
||||||
@ -66,9 +66,9 @@ Load the new CSS file into the CMS, by setting the `LeftAndMain.extra_requiremen
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
LeftAndMain:
|
LeftAndMain:
|
||||||
extra_requirements_css:
|
extra_requirements_css:
|
||||||
- mysite/css/BookmarkedPages.css
|
- mysite/css/BookmarkedPages.css
|
||||||
```
|
```
|
||||||
|
|
||||||
## Create a "bookmark" flag on pages
|
## Create a "bookmark" flag on pages
|
||||||
@ -80,23 +80,23 @@ and insert the following code.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Forms\CheckboxField;
|
use SilverStripe\Forms\CheckboxField;
|
||||||
use SilverStripe\ORM\DataExtension;
|
use SilverStripe\ORM\DataExtension;
|
||||||
|
|
||||||
class BookmarkedPageExtension extends DataExtension
|
class BookmarkedPageExtension extends DataExtension
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $db = [
|
private static $db = [
|
||||||
'IsBookmarked' => 'Boolean'
|
'IsBookmarked' => 'Boolean'
|
||||||
];
|
];
|
||||||
|
|
||||||
public function updateCMSFields(FieldList $fields)
|
public function updateCMSFields(FieldList $fields)
|
||||||
{
|
{
|
||||||
$fields->addFieldToTab('Root.Main',
|
$fields->addFieldToTab('Root.Main',
|
||||||
new CheckboxField('IsBookmarked', "Show in CMS bookmarks?")
|
new CheckboxField('IsBookmarked', "Show in CMS bookmarks?")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -105,9 +105,9 @@ Enable the extension in your [configuration file](../../configuration)
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
SiteTree:
|
SiteTree:
|
||||||
extensions:
|
extensions:
|
||||||
- BookmarkedPageExtension
|
- BookmarkedPageExtension
|
||||||
```
|
```
|
||||||
|
|
||||||
In order to add the field to the database, run a `dev/build/?flush=all`.
|
In order to add the field to the database, run a `dev/build/?flush=all`.
|
||||||
@ -124,17 +124,17 @@ Add the following code to a new file `mysite/code/BookmarkedLeftAndMainExtension
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use Page;
|
use Page;
|
||||||
use SilverStripe\Admin\LeftAndMainExtension;
|
use SilverStripe\Admin\LeftAndMainExtension;
|
||||||
|
|
||||||
class BookmarkedPagesLeftAndMainExtension extends LeftAndMainExtension
|
class BookmarkedPagesLeftAndMainExtension extends LeftAndMainExtension
|
||||||
{
|
{
|
||||||
|
|
||||||
public function BookmarkedPages()
|
public function BookmarkedPages()
|
||||||
{
|
{
|
||||||
return Page::get()->filter("IsBookmarked", 1);
|
return Page::get()->filter("IsBookmarked", 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Enable the extension in your [configuration file](../../configuration)
|
Enable the extension in your [configuration file](../../configuration)
|
||||||
@ -142,9 +142,9 @@ Enable the extension in your [configuration file](../../configuration)
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
LeftAndMain:
|
LeftAndMain:
|
||||||
extensions:
|
extensions:
|
||||||
- BookmarkedPagesLeftAndMainExtension
|
- BookmarkedPagesLeftAndMainExtension
|
||||||
```
|
```
|
||||||
|
|
||||||
As the last step, replace the hardcoded links with our list from the database.
|
As the last step, replace the hardcoded links with our list from the database.
|
||||||
@ -154,14 +154,14 @@ and replace it with the following:
|
|||||||
|
|
||||||
```ss
|
```ss
|
||||||
|
|
||||||
<ul class="cms-menu-list">
|
<ul class="cms-menu-list">
|
||||||
<!-- ... -->
|
<!-- ... -->
|
||||||
<% loop $BookmarkedPages %>
|
<% loop $BookmarkedPages %>
|
||||||
<li class="bookmarked-link $FirstLast">
|
<li class="bookmarked-link $FirstLast">
|
||||||
<li><a href="{$AdminURL}pages/edit/show/$ID">Edit "$Title"</a></li>
|
<li><a href="{$AdminURL}pages/edit/show/$ID">Edit "$Title"</a></li>
|
||||||
</li>
|
</li>
|
||||||
<% end_loop %>
|
<% end_loop %>
|
||||||
</ul>
|
</ul>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Extending the CMS actions
|
## Extending the CMS actions
|
||||||
@ -196,7 +196,7 @@ button group (`CompositeField`) in a similar fashion.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$fields->unshift(FormAction::create('normal', 'Normal button'));
|
$fields->unshift(FormAction::create('normal', 'Normal button'));
|
||||||
```
|
```
|
||||||
|
|
||||||
We can affect the existing button group by manipulating the `CompositeField`
|
We can affect the existing button group by manipulating the `CompositeField`
|
||||||
@ -204,7 +204,7 @@ already present in the `FieldList`.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$fields->fieldByName('MajorActions')->push(FormAction::create('grouped', 'New group button'));
|
$fields->fieldByName('MajorActions')->push(FormAction::create('grouped', 'New group button'));
|
||||||
```
|
```
|
||||||
|
|
||||||
Another option is adding actions into the drop-up - best place for placing
|
Another option is adding actions into the drop-up - best place for placing
|
||||||
@ -212,7 +212,7 @@ infrequently used minor actions.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$fields->addFieldToTab('ActionMenus.MoreOptions', FormAction::create('minor', 'Minor action'));
|
$fields->addFieldToTab('ActionMenus.MoreOptions', FormAction::create('minor', 'Minor action'));
|
||||||
```
|
```
|
||||||
|
|
||||||
We can also easily create new drop-up menus by defining new tabs within the
|
We can also easily create new drop-up menus by defining new tabs within the
|
||||||
@ -220,7 +220,7 @@ We can also easily create new drop-up menus by defining new tabs within the
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$fields->addFieldToTab('ActionMenus.MyDropUp', FormAction::create('minor', 'Minor action in a new drop-up'));
|
$fields->addFieldToTab('ActionMenus.MyDropUp', FormAction::create('minor', 'Minor action in a new drop-up'));
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="hint" markdown='1'>
|
<div class="hint" markdown='1'>
|
||||||
@ -244,20 +244,20 @@ applicable controller actions to it:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Admin\LeftAndMainExtension;
|
use SilverStripe\Admin\LeftAndMainExtension;
|
||||||
|
|
||||||
class CustomActionsExtension extends LeftAndMainExtension
|
class CustomActionsExtension extends LeftAndMainExtension
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $allowed_actions = [
|
private static $allowed_actions = [
|
||||||
'sampleAction'
|
'sampleAction'
|
||||||
];
|
];
|
||||||
|
|
||||||
public function sampleAction()
|
public function sampleAction()
|
||||||
{
|
{
|
||||||
// Create the web
|
// Create the web
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -267,16 +267,16 @@ The extension then needs to be registered:
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|
||||||
LeftAndMain:
|
LeftAndMain:
|
||||||
extensions:
|
extensions:
|
||||||
- CustomActionsExtension
|
- CustomActionsExtension
|
||||||
```
|
```
|
||||||
|
|
||||||
You can now use these handlers with your buttons:
|
You can now use these handlers with your buttons:
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$fields->push(FormAction::create('sampleAction', 'Perform Sample Action'));
|
$fields->push(FormAction::create('sampleAction', 'Perform Sample Action'));
|
||||||
```
|
```
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
@ -5,16 +5,16 @@ also another tool at your disposal: The [Extension](api:SilverStripe\Core\Extens
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Core\Extension;
|
use SilverStripe\Core\Extension;
|
||||||
|
|
||||||
class MyAdminExtension extends Extension
|
class MyAdminExtension extends Extension
|
||||||
{
|
{
|
||||||
// ...
|
// ...
|
||||||
public function updateEditForm(&$form)
|
public function updateEditForm(&$form)
|
||||||
{
|
{
|
||||||
$form->Fields()->push(/* ... */)
|
$form->Fields()->push(/* ... */)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Now enable this extension through your `[config.yml](/topics/configuration)` file.
|
Now enable this extension through your `[config.yml](/topics/configuration)` file.
|
||||||
@ -22,9 +22,9 @@ Now enable this extension through your `[config.yml](/topics/configuration)` fil
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
MyAdmin:
|
MyAdmin:
|
||||||
extensions:
|
extensions:
|
||||||
- MyAdminExtension
|
- MyAdminExtension
|
||||||
```
|
```
|
||||||
|
|
||||||
The following extension points are available: `updateEditForm()`, `updateSearchContext()`,
|
The following extension points are available: `updateEditForm()`, `updateSearchContext()`,
|
||||||
|
@ -20,31 +20,31 @@ This example uses [Cache](api:Cache) in some custom code, and the same cache is
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
use SilverStripe\Core\Injector\Injector;
|
use SilverStripe\Core\Injector\Injector;
|
||||||
use SilverStripe\Core\Flushable;
|
use SilverStripe\Core\Flushable;
|
||||||
use Psr\SimpleCache\CacheInterface;
|
use Psr\SimpleCache\CacheInterface;
|
||||||
|
|
||||||
class MyClass extends DataObject implements Flushable
|
class MyClass extends DataObject implements Flushable
|
||||||
{
|
{
|
||||||
|
|
||||||
public static function flush()
|
public static function flush()
|
||||||
{
|
{
|
||||||
Injector::inst()->get(CacheInterface::class . '.mycache')->clear();
|
Injector::inst()->get(CacheInterface::class . '.mycache')->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function MyCachedContent()
|
public function MyCachedContent()
|
||||||
{
|
{
|
||||||
$cache = Injector::inst()->get(CacheInterface::class . '.mycache')
|
$cache = Injector::inst()->get(CacheInterface::class . '.mycache')
|
||||||
$something = $cache->get('mykey');
|
$something = $cache->get('mykey');
|
||||||
if(!$something) {
|
if(!$something) {
|
||||||
$something = 'value to be cached';
|
$something = 'value to be cached';
|
||||||
$cache->set('mykey', $something);
|
$cache->set('mykey', $something);
|
||||||
}
|
}
|
||||||
return $something;
|
return $something;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Using with filesystem
|
### Using with filesystem
|
||||||
@ -54,18 +54,18 @@ useful in an example like `GD` or `Imagick` generating resampled images, but we
|
|||||||
flush so they are re-created on demand.
|
flush so they are re-created on demand.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
use SilverStripe\Core\Flushable;
|
use SilverStripe\Core\Flushable;
|
||||||
|
|
||||||
class MyClass extends DataObject implements Flushable
|
class MyClass extends DataObject implements Flushable
|
||||||
{
|
{
|
||||||
|
|
||||||
public static function flush()
|
public static function flush()
|
||||||
{
|
{
|
||||||
foreach(glob(ASSETS_PATH . '/_tempfiles/*.jpg') as $file) {
|
foreach(glob(ASSETS_PATH . '/_tempfiles/*.jpg') as $file) {
|
||||||
unlink($file);
|
unlink($file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
@ -10,8 +10,8 @@ The main entry point for any command line execution is `framework/cli-script.php
|
|||||||
rebuild from the command line, use this command:
|
rebuild from the command line, use this command:
|
||||||
```bash
|
```bash
|
||||||
|
|
||||||
cd your-webroot/
|
cd your-webroot/
|
||||||
php framework/cli-script.php dev/build
|
php framework/cli-script.php dev/build
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="notice">
|
<div class="notice">
|
||||||
@ -34,8 +34,8 @@ when running the command php -v, then you may not have php-cli installed so sake
|
|||||||
|
|
||||||
`sake` can be invoked using `./framework/sake`. For easier access, copy the `sake` file into `/usr/bin/sake`.
|
`sake` can be invoked using `./framework/sake`. For easier access, copy the `sake` file into `/usr/bin/sake`.
|
||||||
```
|
```
|
||||||
cd your-webroot/
|
cd your-webroot/
|
||||||
sudo ./framework/sake installsake
|
sudo ./framework/sake installsake
|
||||||
```
|
```
|
||||||
<div class="warning">
|
<div class="warning">
|
||||||
This currently only works on UNIX like systems, not on Windows.
|
This currently only works on UNIX like systems, not on Windows.
|
||||||
@ -60,23 +60,23 @@ Sake can run any controller by passing the relative URL to that controller.
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
||||||
sake /
|
sake /
|
||||||
# returns the homepage
|
# returns the homepage
|
||||||
|
|
||||||
sake dev/
|
sake dev/
|
||||||
# shows a list of development operations
|
# shows a list of development operations
|
||||||
```
|
```
|
||||||
|
|
||||||
Sake is particularly useful for running build tasks.
|
Sake is particularly useful for running build tasks.
|
||||||
```bash
|
```bash
|
||||||
|
|
||||||
sake dev/build "flush=1"
|
sake dev/build "flush=1"
|
||||||
```
|
```
|
||||||
|
|
||||||
It can also be handy if you have a long running script..
|
It can also be handy if you have a long running script..
|
||||||
```bash
|
```bash
|
||||||
|
|
||||||
sake dev/tasks/MyReallyLongTask
|
sake dev/tasks/MyReallyLongTask
|
||||||
```
|
```
|
||||||
|
|
||||||
### Running processes
|
### Running processes
|
||||||
@ -93,28 +93,28 @@ This code provides a good template:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
use SilverStripe\Control\Controller;
|
use SilverStripe\Control\Controller;
|
||||||
|
|
||||||
class MyProcess extends Controller
|
class MyProcess extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $allowed_actions = [
|
private static $allowed_actions = [
|
||||||
'index'
|
'index'
|
||||||
];
|
];
|
||||||
|
|
||||||
function index() {
|
function index() {
|
||||||
set_time_limit(0);
|
set_time_limit(0);
|
||||||
|
|
||||||
while(memory_get_usage() < 32*1024*1024) {
|
while(memory_get_usage() < 32*1024*1024) {
|
||||||
if($this->somethingToDo()) {
|
if($this->somethingToDo()) {
|
||||||
$this->doSomething();
|
$this->doSomething();
|
||||||
sleep(1)
|
sleep(1)
|
||||||
} else {
|
} else {
|
||||||
sleep(300);
|
sleep(300);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -123,8 +123,8 @@ Then the process can be managed through `sake`
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
||||||
sake -start MyProcess
|
sake -start MyProcess
|
||||||
sake -stop MyProcess
|
sake -stop MyProcess
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="notice">
|
<div class="notice">
|
||||||
@ -138,8 +138,8 @@ Parameters can be added to the command. All parameters will be available in `$_G
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
||||||
cd your-webroot/
|
cd your-webroot/
|
||||||
php framework/cli-script.php myurl myparam=1 myotherparam=2
|
php framework/cli-script.php myurl myparam=1 myotherparam=2
|
||||||
```
|
```
|
||||||
|
|
||||||
Or if you're using `sake`
|
Or if you're using `sake`
|
||||||
@ -147,7 +147,7 @@ Or if you're using `sake`
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
||||||
sake myurl "myparam=1&myotherparam=2"
|
sake myurl "myparam=1&myotherparam=2"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Running Regular Tasks With Cron
|
## Running Regular Tasks With Cron
|
||||||
@ -158,5 +158,5 @@ On a UNIX machine, you can typically run a scheduled task with a [cron job](http
|
|||||||
The following will run `MyTask` every minute.
|
The following will run `MyTask` every minute.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
* * * * * /your/site/folder/sake dev/tasks/MyTask
|
* * * * * /your/site/folder/sake dev/tasks/MyTask
|
||||||
```
|
```
|
@ -15,9 +15,9 @@ Sets the value of cookie with configuration.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
Cookie::set($name, $value, $expiry = 90, $path = null, $domain = null, $secure = false, $httpOnly = false);
|
Cookie::set($name, $value, $expiry = 90, $path = null, $domain = null, $secure = false, $httpOnly = false);
|
||||||
|
|
||||||
// Cookie::set('MyApplicationPreference', 'Yes');
|
// Cookie::set('MyApplicationPreference', 'Yes');
|
||||||
```
|
```
|
||||||
|
|
||||||
### get
|
### get
|
||||||
@ -26,10 +26,10 @@ Returns the value of cookie.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
Cookie::get($name);
|
Cookie::get($name);
|
||||||
|
|
||||||
// Cookie::get('MyApplicationPreference');
|
// Cookie::get('MyApplicationPreference');
|
||||||
// returns 'Yes'
|
// returns 'Yes'
|
||||||
```
|
```
|
||||||
|
|
||||||
### force_expiry
|
### force_expiry
|
||||||
@ -38,9 +38,9 @@ Clears a given cookie.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
Cookie::force_expiry($name, $path = null, $domain = null);
|
Cookie::force_expiry($name, $path = null, $domain = null);
|
||||||
|
|
||||||
// Cookie::force_expiry('MyApplicationPreference')
|
// Cookie::force_expiry('MyApplicationPreference')
|
||||||
```
|
```
|
||||||
|
|
||||||
## Cookie_Backend
|
## Cookie_Backend
|
||||||
@ -54,15 +54,15 @@ from the browser.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$myCookies = [
|
$myCookies = [
|
||||||
'cookie1' => 'value1',
|
'cookie1' => 'value1',
|
||||||
];
|
];
|
||||||
|
|
||||||
$newBackend = new CookieJar($myCookies);
|
$newBackend = new CookieJar($myCookies);
|
||||||
|
|
||||||
Injector::inst()->registerService($newBackend, 'Cookie_Backend');
|
Injector::inst()->registerService($newBackend, 'Cookie_Backend');
|
||||||
|
|
||||||
Cookie::get('cookie1');
|
Cookie::get('cookie1');
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -74,9 +74,9 @@ create a new service for you using the `$_COOKIE` superglobal.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
Injector::inst()->unregisterNamedObject('Cookie_Backend');
|
Injector::inst()->unregisterNamedObject('Cookie_Backend');
|
||||||
|
|
||||||
Cookie::get('cookiename'); // will return $_COOKIE['cookiename'] if set
|
Cookie::get('cookiename'); // will return $_COOKIE['cookiename'] if set
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternatively, if you know that the superglobal has been changed (or you aren't sure it hasn't) you can attempt to use
|
Alternatively, if you know that the superglobal has been changed (or you aren't sure it hasn't) you can attempt to use
|
||||||
@ -84,11 +84,11 @@ the current `CookieJar` service to tell you what it was like when it was registe
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
//store the cookies that were loaded into the `CookieJar`
|
//store the cookies that were loaded into the `CookieJar`
|
||||||
$recievedCookie = Cookie::get_inst()->getAll(false);
|
$recievedCookie = Cookie::get_inst()->getAll(false);
|
||||||
|
|
||||||
//set a new `CookieJar`
|
//set a new `CookieJar`
|
||||||
Injector::inst()->registerService(new CookieJar($recievedCookie), 'CookieJar');
|
Injector::inst()->registerService(new CookieJar($recievedCookie), 'CookieJar');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Using your own Cookie_Backend
|
### Using your own Cookie_Backend
|
||||||
@ -98,13 +98,13 @@ If you need to implement your own Cookie_Backend you can use the injector system
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
|
||||||
---
|
---
|
||||||
Name: mycookie
|
Name: mycookie
|
||||||
After: '#cookie'
|
After: '#cookie'
|
||||||
---
|
---
|
||||||
Injector:
|
Injector:
|
||||||
Cookie_Backend:
|
Cookie_Backend:
|
||||||
class: MyCookieJar
|
class: MyCookieJar
|
||||||
```
|
```
|
||||||
|
|
||||||
To be a valid backend your class must implement the [Cookie_Backend](api:SilverStripe\Control\Cookie_Backend) interface.
|
To be a valid backend your class must implement the [Cookie_Backend](api:SilverStripe\Control\Cookie_Backend) interface.
|
||||||
@ -120,12 +120,12 @@ Using the `Cookie_Backend` we can do this like such:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
Cookie::set('CookieName', 'CookieVal');
|
Cookie::set('CookieName', 'CookieVal');
|
||||||
|
|
||||||
Cookie::get('CookieName'); //gets the cookie as we set it
|
Cookie::get('CookieName'); //gets the cookie as we set it
|
||||||
|
|
||||||
//will return the cookie as it was when it was sent in the request
|
//will return the cookie as it was when it was sent in the request
|
||||||
Cookie::get('CookieName', false);
|
Cookie::get('CookieName', false);
|
||||||
```
|
```
|
||||||
|
|
||||||
### Accessing all the cookies at once
|
### Accessing all the cookies at once
|
||||||
@ -134,9 +134,9 @@ One can also access all of the cookies in one go using the `Cookie_Backend`
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
Cookie::get_inst()->getAll(); //returns all the cookies including ones set during the current process
|
Cookie::get_inst()->getAll(); //returns all the cookies including ones set during the current process
|
||||||
|
|
||||||
Cookie::get_inst()->getAll(false); //returns all the cookies in the request
|
Cookie::get_inst()->getAll(false); //returns all the cookies in the request
|
||||||
```
|
```
|
||||||
|
|
||||||
## API Documentation
|
## API Documentation
|
||||||
|
@ -19,10 +19,10 @@ use SilverStripe\Control\Controller;
|
|||||||
|
|
||||||
class MyController extends Controller
|
class MyController extends Controller
|
||||||
{
|
{
|
||||||
public function MySession()
|
public function MySession()
|
||||||
{
|
{
|
||||||
return $this->getRequest()->getSession();
|
return $this->getRequest()->getSession();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ $session = $request->getSession();
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$session->set('MyValue', 6);
|
$session->set('MyValue', 6);
|
||||||
```
|
```
|
||||||
|
|
||||||
Saves the value of to session data. You can also save arrays or serialized objects in session (but note there may be
|
Saves the value of to session data. You can also save arrays or serialized objects in session (but note there may be
|
||||||
@ -45,12 +45,12 @@ size restrictions as to how much you can save).
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// saves an array
|
// saves an array
|
||||||
$session->set('MyArrayOfValues', ['1','2','3']);
|
$session->set('MyArrayOfValues', ['1','2','3']);
|
||||||
|
|
||||||
// saves an object (you'll have to unserialize it back)
|
// saves an object (you'll have to unserialize it back)
|
||||||
$object = new Object();
|
$object = new Object();
|
||||||
$session->set('MyObject', serialize($object));
|
$session->set('MyObject', serialize($object));
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -62,14 +62,14 @@ can use this anywhere in your PHP files.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
echo $session->get('MyValue');
|
echo $session->get('MyValue');
|
||||||
// returns 6
|
// returns 6
|
||||||
|
|
||||||
$data = $session->get('MyArrayOfValues');
|
$data = $session->get('MyArrayOfValues');
|
||||||
// $data = array(1,2,3)
|
// $data = array(1,2,3)
|
||||||
|
|
||||||
$object = unserialize($session->get('MyObject', $object));
|
$object = unserialize($session->get('MyObject', $object));
|
||||||
// $object = Object()
|
// $object = Object()
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -77,8 +77,8 @@ can use this anywhere in your PHP files.
|
|||||||
|
|
||||||
You can also get all the values in the session at once. This is useful for debugging.
|
You can also get all the values in the session at once. This is useful for debugging.
|
||||||
```php
|
```php
|
||||||
$session->getAll();
|
$session->getAll();
|
||||||
// returns an array of all the session values.
|
// returns an array of all the session values.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -87,13 +87,13 @@ You can also get all the values in the session at once. This is useful for debug
|
|||||||
Once you have accessed a value from the Session it doesn't automatically wipe the value from the Session, you have
|
Once you have accessed a value from the Session it doesn't automatically wipe the value from the Session, you have
|
||||||
to specifically remove it.
|
to specifically remove it.
|
||||||
```php
|
```php
|
||||||
$session->clear('MyValue');
|
$session->clear('MyValue');
|
||||||
```
|
```
|
||||||
|
|
||||||
Or you can clear every single value in the session at once. Note SilverStripe stores some of its own session data
|
Or you can clear every single value in the session at once. Note SilverStripe stores some of its own session data
|
||||||
including form and page comment information. None of this is vital but `clear_all` will clear everything.
|
including form and page comment information. None of this is vital but `clear_all` will clear everything.
|
||||||
```php
|
```php
|
||||||
$session->clearAll();
|
$session->clearAll();
|
||||||
```
|
```
|
||||||
|
|
||||||
## Secure Session Cookie
|
## Secure Session Cookie
|
||||||
|
@ -15,8 +15,8 @@ For projects managed through Composer, update the version number of `framework`
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
"require": {
|
"require": {
|
||||||
"silverstripe/framework": "^4.0",
|
"silverstripe/framework": "^4.0",
|
||||||
"silverstripe/cms": "^4.0"
|
"silverstripe/cms": "^4.0"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -114,19 +114,19 @@ specifies "these are arguments to the controller".
|
|||||||
|
|
||||||
In other words, change this:
|
In other words, change this:
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
Director::addRules(50, array(
|
Director::addRules(50, array(
|
||||||
'admin/ImageEditor/$Action' => 'ImageEditor',
|
'admin/ImageEditor/$Action' => 'ImageEditor',
|
||||||
));
|
));
|
||||||
|
```
|
||||||
|
|
||||||
To this:
|
To this:
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
Director::addRules(50, array(
|
Director::addRules(50, array(
|
||||||
'admin/ImageEditor//$Action' => 'ImageEditor',
|
'admin/ImageEditor//$Action' => 'ImageEditor',
|
||||||
));
|
));
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### Decorators
|
### Decorators
|
||||||
@ -213,8 +213,9 @@ In most cases this won't have any effect on existing forms, although you might g
|
|||||||
like Validator.js and behaviour.js. If you want to disable JavaScript validation across forms, add the following to your
|
like Validator.js and behaviour.js. If you want to disable JavaScript validation across forms, add the following to your
|
||||||
_config.php:
|
_config.php:
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
Validator::set_javascript_validation_handler('none');
|
Validator::set_javascript_validation_handler('none');
|
||||||
|
```
|
||||||
|
|
||||||
See http://open.silverstripe.com/changeset/69688
|
See http://open.silverstripe.com/changeset/69688
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ can access them. In particular the following static variables have been changed
|
|||||||
Because of this, you will need to change the static definitions in your ModelAdmin subclasses. For example, you should
|
Because of this, you will need to change the static definitions in your ModelAdmin subclasses. For example, you should
|
||||||
change this:
|
change this:
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
class MyCatalogAdmin extends ModelAdmin
|
class MyCatalogAdmin extends ModelAdmin
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -84,11 +84,11 @@ change this:
|
|||||||
|
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
To this:
|
To this:
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
class MyCatalogAdmin extends ModelAdmin
|
class MyCatalogAdmin extends ModelAdmin
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -99,7 +99,7 @@ To this:
|
|||||||
|
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
#### Deprecated Translatable::enable()
|
#### Deprecated Translatable::enable()
|
||||||
|
@ -129,9 +129,9 @@ post](http://www.silverstripe.org/preview-of-silverstripe-2-4-hierarchical-urls-
|
|||||||
You can enable it manually for existing websites. Existing URLs will automatically change to the nested format without
|
You can enable it manually for existing websites. Existing URLs will automatically change to the nested format without
|
||||||
republication (your old URLs should redirect automatically).
|
republication (your old URLs should redirect automatically).
|
||||||
|
|
||||||
:::php
|
```php
|
||||||
SiteTree::enable_nested_urls();
|
SiteTree::enable_nested_urls();
|
||||||
|
```
|
||||||
|
|
||||||
### SiteTree->Link() instead of SiteTree->URLSegment
|
### SiteTree->Link() instead of SiteTree->URLSegment
|
||||||
|
|
||||||
|
@ -13,12 +13,12 @@ use SilverStripe\Security\MemberAuthenticator\MemberLoginForm;
|
|||||||
class CustomLoginForm extends MemberLoginForm
|
class CustomLoginForm extends MemberLoginForm
|
||||||
{
|
{
|
||||||
|
|
||||||
public function __construct($controller, $name, $fields = null, $actions = null, $checkCurrentUser = true)
|
public function __construct($controller, $name, $fields = null, $actions = null, $checkCurrentUser = true)
|
||||||
{
|
{
|
||||||
parent::__construct($controller, $name, $fields, $actions, $checkCurrentUser);
|
parent::__construct($controller, $name, $fields, $actions, $checkCurrentUser);
|
||||||
|
|
||||||
$this->disableSecurityToken();
|
$this->disableSecurityToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -13,12 +13,12 @@ use SilverStripe\Security\MemberAuthenticator\MemberLoginForm;
|
|||||||
class CustomLoginForm extends MemberLoginForm
|
class CustomLoginForm extends MemberLoginForm
|
||||||
{
|
{
|
||||||
|
|
||||||
public function __construct($controller, $name, $fields = null, $actions = null, $checkCurrentUser = true)
|
public function __construct($controller, $name, $fields = null, $actions = null, $checkCurrentUser = true)
|
||||||
{
|
{
|
||||||
parent::__construct($controller, $name, $fields, $actions, $checkCurrentUser);
|
parent::__construct($controller, $name, $fields, $actions, $checkCurrentUser);
|
||||||
|
|
||||||
$this->disableSecurityToken();
|
$this->disableSecurityToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -13,12 +13,12 @@ use SilverStripe\Security\MemberAuthenticator\MemberLoginForm;
|
|||||||
class CustomLoginForm extends MemberLoginForm
|
class CustomLoginForm extends MemberLoginForm
|
||||||
{
|
{
|
||||||
|
|
||||||
public function __construct($controller, $name, $fields = null, $actions = null, $checkCurrentUser = true)
|
public function __construct($controller, $name, $fields = null, $actions = null, $checkCurrentUser = true)
|
||||||
{
|
{
|
||||||
parent::__construct($controller, $name, $fields, $actions, $checkCurrentUser);
|
parent::__construct($controller, $name, $fields, $actions, $checkCurrentUser);
|
||||||
|
|
||||||
$this->disableSecurityToken();
|
$this->disableSecurityToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -116,7 +116,7 @@ For example, the below shows how you would update a query with a hard-coded tabl
|
|||||||
```diff
|
```diff
|
||||||
public function countDuplicates($model, $fieldToCheck)
|
public function countDuplicates($model, $fieldToCheck)
|
||||||
{
|
{
|
||||||
$query = new SilverStripe\ORM\Queries\SQLSelect();
|
$query = new SilverStripe\ORM\Queries\SQLSelect();
|
||||||
+ $table = SilverStripe\ORM\DataObject::getSchema()->tableForField($model, $field);
|
+ $table = SilverStripe\ORM\DataObject::getSchema()->tableForField($model, $field);
|
||||||
- $query->setFrom("\"{$model}\"");
|
- $query->setFrom("\"{$model}\"");
|
||||||
+ $query->setFrom("\"{$table}\"");
|
+ $query->setFrom("\"{$table}\"");
|
||||||
@ -605,11 +605,11 @@ use SilverStripe\Core\Convert;
|
|||||||
|
|
||||||
class MyObject extends ViewableData
|
class MyObject extends ViewableData
|
||||||
{
|
{
|
||||||
public function getSomeHTML
|
public function getSomeHTML
|
||||||
{
|
{
|
||||||
$title = Convert::raw2xml($this->Title);
|
$title = Convert::raw2xml($this->Title);
|
||||||
return "<h1>{$title}</h1>";
|
return "<h1>{$title}</h1>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -618,15 +618,15 @@ After:
|
|||||||
```php
|
```php
|
||||||
class MyObject extends SilverStripe\View\ViewableData
|
class MyObject extends SilverStripe\View\ViewableData
|
||||||
{
|
{
|
||||||
private static $casting = [
|
private static $casting = [
|
||||||
'SomeHTML' => 'HTMLText'
|
'SomeHTML' => 'HTMLText'
|
||||||
];
|
];
|
||||||
|
|
||||||
public function getSomeHTML
|
public function getSomeHTML
|
||||||
{
|
{
|
||||||
$title = SilverStripe\Core\Convert::raw2xml($this->Title);
|
$title = SilverStripe\Core\Convert::raw2xml($this->Title);
|
||||||
return "<h1>{$title}</h1>";
|
return "<h1>{$title}</h1>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -852,12 +852,12 @@ is intended to upload images for manipulation.
|
|||||||
|
|
||||||
```diff
|
```diff
|
||||||
-if($file instanceof Image) {
|
-if($file instanceof Image) {
|
||||||
- $upload = new UploadField();
|
- $upload = new UploadField();
|
||||||
- $upload->setAllowedFileCategories('image');
|
- $upload->setAllowedFileCategories('image');
|
||||||
-}
|
-}
|
||||||
+if ($file->getIsImage()) {
|
+if ($file->getIsImage()) {
|
||||||
+ $upload = new UploadField();
|
+ $upload = new UploadField();
|
||||||
+ $upload->setAllowedFileCategories('image/supported');
|
+ $upload->setAllowedFileCategories('image/supported');
|
||||||
+}
|
+}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -867,12 +867,12 @@ an `Image` datatype, or refer to `DBFile('image/supported')`.
|
|||||||
```php
|
```php
|
||||||
class MyObject extends SilverStripe\ORM\DataObject
|
class MyObject extends SilverStripe\ORM\DataObject
|
||||||
{
|
{
|
||||||
private static $has_one = [
|
private static $has_one = [
|
||||||
"ImageObject" => "Image"
|
"ImageObject" => "Image"
|
||||||
];
|
];
|
||||||
private static $db = [
|
private static $db = [
|
||||||
"ImageField" => "DBFile('image/supported')"
|
"ImageField" => "DBFile('image/supported')"
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -889,18 +889,18 @@ You would need to upgrade your code as below.
|
|||||||
```diff
|
```diff
|
||||||
-function importTempFile($tmp)
|
-function importTempFile($tmp)
|
||||||
-{
|
-{
|
||||||
- copy($tmp, ASSETS_PATH . '/imported/' . basename($tmp));
|
- copy($tmp, ASSETS_PATH . '/imported/' . basename($tmp));
|
||||||
- $file = new File();
|
- $file = new File();
|
||||||
- $file->setFilename('assets/imported/'.basename($tmp));
|
- $file->setFilename('assets/imported/'.basename($tmp));
|
||||||
- $file->write();
|
- $file->write();
|
||||||
-}
|
-}
|
||||||
+public function importTempFile($tmp)
|
+public function importTempFile($tmp)
|
||||||
+{
|
+{
|
||||||
+ Versioned::reading_stage('Stage');
|
+ Versioned::reading_stage('Stage');
|
||||||
+ $file = new File();
|
+ $file = new File();
|
||||||
+ $file->setFromLocalFile($tmp, 'imported/' . basename($tmp));
|
+ $file->setFromLocalFile($tmp, 'imported/' . basename($tmp));
|
||||||
+ $file->write();
|
+ $file->write();
|
||||||
+ $file->doPublish();
|
+ $file->doPublish();
|
||||||
+}
|
+}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -939,15 +939,15 @@ Before:
|
|||||||
// in MyImageExtension.php
|
// in MyImageExtension.php
|
||||||
class MyImageExtension extends SilverStripe\ORM\DataExtension
|
class MyImageExtension extends SilverStripe\ORM\DataExtension
|
||||||
{
|
{
|
||||||
public function GalleryThumbnail($height)
|
public function GalleryThumbnail($height)
|
||||||
{
|
{
|
||||||
return $this->getFormattedImage('GalleryThumbnail', $height);
|
return $this->getFormattedImage('GalleryThumbnail', $height);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function generateGalleryThumbnail(Image_Backend $backend, $height)
|
public function generateGalleryThumbnail(Image_Backend $backend, $height)
|
||||||
{
|
{
|
||||||
return $backend->paddedResize(300, $height);
|
return $backend->paddedResize(300, $height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// in _config.php
|
// in _config.php
|
||||||
@ -962,17 +962,17 @@ use SilverStripe\Assets\File;
|
|||||||
// in MyImageExtension.php
|
// in MyImageExtension.php
|
||||||
class MyImageExtension extends SilverStripe\Core\Extension
|
class MyImageExtension extends SilverStripe\Core\Extension
|
||||||
{
|
{
|
||||||
public function GalleryThumbnail($height)
|
public function GalleryThumbnail($height)
|
||||||
{
|
{
|
||||||
// Generates the manipulation key
|
// Generates the manipulation key
|
||||||
$variant = $this->owner->variantName(__FUNCTION__, $height);
|
$variant = $this->owner->variantName(__FUNCTION__, $height);
|
||||||
|
|
||||||
// Instruct the backend to search for an existing variant with this key,
|
// Instruct the backend to search for an existing variant with this key,
|
||||||
// and include a callback used to generate this image if it doesn't exist
|
// and include a callback used to generate this image if it doesn't exist
|
||||||
return $this->owner->manipulateImage($variant, function (Image_Backend $backend) use ($height) {
|
return $this->owner->manipulateImage($variant, function (Image_Backend $backend) use ($height) {
|
||||||
return $backend->paddedResize(300, $height);
|
return $backend->paddedResize(300, $height);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// in _config.php
|
// in _config.php
|
||||||
@ -1007,7 +1007,7 @@ class MyShortcodeUser extends Object
|
|||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
private $content;
|
private $content;
|
||||||
|
|
||||||
public function Content($arguments, $parser, $shortcode)
|
public function Content($arguments, $parser, $shortcode)
|
||||||
{
|
{
|
||||||
@ -1027,7 +1027,7 @@ class MyShortcodeUser extends Object
|
|||||||
|
|
||||||
{
|
{
|
||||||
|
|
||||||
private $content;
|
private $content;
|
||||||
|
|
||||||
public function Content($arguments, $parser, $shortcode)
|
public function Content($arguments, $parser, $shortcode)
|
||||||
{
|
{
|
||||||
@ -1048,17 +1048,17 @@ use SilverStripe\ORM\FieldType\DBComposite;
|
|||||||
|
|
||||||
class MyAddressField extends DBComposite
|
class MyAddressField extends DBComposite
|
||||||
{
|
{
|
||||||
private static $composite_db = [
|
private static $composite_db = [
|
||||||
'Street' => 'Varchar(200)',
|
'Street' => 'Varchar(200)',
|
||||||
'Suburb' => 'Varchar(100)',
|
'Suburb' => 'Varchar(100)',
|
||||||
'City' => 'Varchar(100)',
|
'City' => 'Varchar(100)',
|
||||||
'Country' => 'Varchar(100)'
|
'Country' => 'Varchar(100)'
|
||||||
];
|
];
|
||||||
|
|
||||||
public function scaffoldFormField($title = null, $params = null)
|
public function scaffoldFormField($title = null, $params = null)
|
||||||
{
|
{
|
||||||
new SilverStripe\Forms\TextField($this->getName(), $title);
|
new SilverStripe\Forms\TextField($this->getName(), $title);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -1114,11 +1114,11 @@ Before:
|
|||||||
```php
|
```php
|
||||||
function augmentSQL(SQLQuery &$query, DataQuery &$dataQuery = null)
|
function augmentSQL(SQLQuery &$query, DataQuery &$dataQuery = null)
|
||||||
{
|
{
|
||||||
$locale = Translatable::get_current_locale();
|
$locale = Translatable::get_current_locale();
|
||||||
if(!preg_match('/("|\'|`)Locale("|\'|`)/', implode(' ', $query->getWhere()))) {
|
if(!preg_match('/("|\'|`)Locale("|\'|`)/', implode(' ', $query->getWhere()))) {
|
||||||
$qry = sprintf('"Locale" = \'%s\'', Convert::raw2sql($locale));
|
$qry = sprintf('"Locale" = \'%s\'', Convert::raw2sql($locale));
|
||||||
$query->addWhere($qry);
|
$query->addWhere($qry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -1127,12 +1127,12 @@ After:
|
|||||||
```php
|
```php
|
||||||
public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null)
|
public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null)
|
||||||
{
|
{
|
||||||
$locale = Translatable::get_current_locale();
|
$locale = Translatable::get_current_locale();
|
||||||
if (!preg_match('/("|\'|`)Locale("|\'|`)/', implode(' ', $query->getWhereParameterised($parameters)))) {
|
if (!preg_match('/("|\'|`)Locale("|\'|`)/', implode(' ', $query->getWhereParameterised($parameters)))) {
|
||||||
$query->addWhere([
|
$query->addWhere([
|
||||||
'"Locale"' => $locale
|
'"Locale"' => $locale
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -1242,9 +1242,9 @@ use SilverStripe\ORM\DataObject;
|
|||||||
*/
|
*/
|
||||||
class MyStagedModel extends SilverStripe\ORM\DataObject
|
class MyStagedModel extends SilverStripe\ORM\DataObject
|
||||||
{
|
{
|
||||||
private staic $extensions = [
|
private staic $extensions = [
|
||||||
"SilverStripe\\ORM\\Versioning\\Versioned('StagedVersioned')"
|
"SilverStripe\\ORM\\Versioning\\Versioned('StagedVersioned')"
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1252,9 +1252,9 @@ class MyStagedModel extends SilverStripe\ORM\DataObject
|
|||||||
*/
|
*/
|
||||||
class MyVersionedModel extends DataObject
|
class MyVersionedModel extends DataObject
|
||||||
{
|
{
|
||||||
private static $extensions = [
|
private static $extensions = [
|
||||||
"SilverStripe\\ORM\\Versioning\\Versioned('Versioned')"
|
"SilverStripe\\ORM\\Versioning\\Versioned('Versioned')"
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -213,14 +213,14 @@ Further guidelines:
|
|||||||
|
|
||||||
Example: Bad commit message
|
Example: Bad commit message
|
||||||
```
|
```
|
||||||
finally fixed this dumb rendering bug that Joe talked about ... LOL
|
finally fixed this dumb rendering bug that Joe talked about ... LOL
|
||||||
also added another form field for password validation
|
also added another form field for password validation
|
||||||
```
|
```
|
||||||
Example: Good commit message
|
Example: Good commit message
|
||||||
```
|
```
|
||||||
BUG Formatting through prepValueForDB()
|
BUG Formatting through prepValueForDB()
|
||||||
|
|
||||||
Added prepValueForDB() which is called on DBField->writeToManipulation()
|
Added prepValueForDB() which is called on DBField->writeToManipulation()
|
||||||
to ensure formatting of value before insertion to DB on a per-DBField type basis (fixes #1234).
|
to ensure formatting of value before insertion to DB on a per-DBField type basis (fixes #1234).
|
||||||
Added documentation for DBField->writeToManipulation() (related to a4bd42fd).
|
Added documentation for DBField->writeToManipulation() (related to a4bd42fd).
|
||||||
```
|
```
|
||||||
|
@ -63,15 +63,15 @@ Here's an example for replacing `Director::isDev()` with a (theoretical) `Env::i
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
/**
|
/**
|
||||||
* Returns true if your are in development mode
|
* Returns true if your are in development mode
|
||||||
* @deprecated 4.0 Use {@link Env::is_dev()} instead.
|
* @deprecated 4.0 Use {@link Env::is_dev()} instead.
|
||||||
*/
|
*/
|
||||||
public function isDev()
|
public function isDev()
|
||||||
{
|
{
|
||||||
Deprecation::notice('4.0', 'Use Env::is_dev() instead');
|
Deprecation::notice('4.0', 'Use Env::is_dev() instead');
|
||||||
return Env::is_dev();
|
return Env::is_dev();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
This change could be committed to a minor release like *3.2.0*, and remains deprecated in all subsequent minor releases
|
This change could be committed to a minor release like *3.2.0*, and remains deprecated in all subsequent minor releases
|
||||||
|
@ -91,9 +91,9 @@ sparingly.
|
|||||||
|
|
||||||
Code for a Tip box:
|
Code for a Tip box:
|
||||||
```
|
```
|
||||||
<div class="hint" markdown='1'>
|
<div class="hint" markdown='1'>
|
||||||
...
|
...
|
||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
<div class="notice" markdown='1'>
|
<div class="notice" markdown='1'>
|
||||||
"Notification box": A notification box is good for technical notifications relating to the main text. For example, notifying users about a deprecated feature.
|
"Notification box": A notification box is good for technical notifications relating to the main text. For example, notifying users about a deprecated feature.
|
||||||
@ -101,9 +101,9 @@ Code for a Tip box:
|
|||||||
|
|
||||||
Code for a Notification box:
|
Code for a Notification box:
|
||||||
```
|
```
|
||||||
<div class="notice" markdown='1'>
|
<div class="notice" markdown='1'>
|
||||||
...
|
...
|
||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
<div class="warning" markdown='1'>
|
<div class="warning" markdown='1'>
|
||||||
"Warning box": A warning box is useful for highlighting a severe bug or a technical issue requiring a user's attention. For example, suppose a rare edge case sometimes leads to a variable being overwritten incorrectly. A warning box can be used to alert the user to this case so they can write their own code to handle it.
|
"Warning box": A warning box is useful for highlighting a severe bug or a technical issue requiring a user's attention. For example, suppose a rare edge case sometimes leads to a variable being overwritten incorrectly. A warning box can be used to alert the user to this case so they can write their own code to handle it.
|
||||||
@ -111,9 +111,9 @@ Code for a Notification box:
|
|||||||
|
|
||||||
Code for a Warning box:
|
Code for a Warning box:
|
||||||
```
|
```
|
||||||
<div class="warning" markdown='1'>
|
<div class="warning" markdown='1'>
|
||||||
...
|
...
|
||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
See [markdown extra documentation](http://michelf.com/projects/php-markdown/extra/#html) for more restrictions
|
See [markdown extra documentation](http://michelf.com/projects/php-markdown/extra/#html) for more restrictions
|
||||||
on placing HTML blocks inside Markdown.
|
on placing HTML blocks inside Markdown.
|
||||||
|
@ -72,12 +72,12 @@ Translations need to be reviewed before being committed, which is a process that
|
|||||||
merging back translations into all supported release branches as well as the `master` branch. The following script
|
merging back translations into all supported release branches as well as the `master` branch. The following script
|
||||||
should be applied to the oldest release branch, and then merged forward into newer branches:
|
should be applied to the oldest release branch, and then merged forward into newer branches:
|
||||||
```bash
|
```bash
|
||||||
|
|
||||||
tx pull
|
tx pull
|
||||||
|
|
||||||
# Manually review changes through git diff, then commit
|
# Manually review changes through git diff, then commit
|
||||||
git add lang/*
|
git add lang/*
|
||||||
git commit -m "Updated translations"
|
git commit -m "Updated translations"
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="notice" markdown="1">
|
<div class="notice" markdown="1">
|
||||||
@ -91,7 +91,7 @@ conversion step involved in order to get those translations syncing with Transif
|
|||||||
`mymodule/javascript/lang/*.js` call `ss.i18n.addDictionary()` to add files.
|
`mymodule/javascript/lang/*.js` call `ss.i18n.addDictionary()` to add files.
|
||||||
```js
|
```js
|
||||||
|
|
||||||
ss.i18n.addDictionary('de', {'MyNamespace.MyKey': 'My Translation'});
|
ss.i18n.addDictionary('de', {'MyNamespace.MyKey': 'My Translation'});
|
||||||
```
|
```
|
||||||
|
|
||||||
But Transifex only accepts structured formats like JSON.
|
But Transifex only accepts structured formats like JSON.
|
||||||
@ -103,30 +103,30 @@ But Transifex only accepts structured formats like JSON.
|
|||||||
First of all, you need to create those source files in JSON, and store them in `mymodule/javascript/lang/src/*.js`. In your `.tx/config` you can configure this path as a separate master location.
|
First of all, you need to create those source files in JSON, and store them in `mymodule/javascript/lang/src/*.js`. In your `.tx/config` you can configure this path as a separate master location.
|
||||||
```ruby
|
```ruby
|
||||||
|
|
||||||
[main]
|
[main]
|
||||||
host = https://www.transifex.com
|
host = https://www.transifex.com
|
||||||
|
|
||||||
[silverstripe-mymodule.master]
|
[silverstripe-mymodule.master]
|
||||||
file_filter = lang/<lang>.yml
|
file_filter = lang/<lang>.yml
|
||||||
source_file = lang/en.yml
|
source_file = lang/en.yml
|
||||||
source_lang = en
|
source_lang = en
|
||||||
type = YML
|
type = YML
|
||||||
|
|
||||||
[silverstripe-mymodule.master-js]
|
[silverstripe-mymodule.master-js]
|
||||||
file_filter = javascript/lang/src/<lang>.js
|
file_filter = javascript/lang/src/<lang>.js
|
||||||
source_file = javascript/lang/src/en.js
|
source_file = javascript/lang/src/en.js
|
||||||
source_lang = en
|
source_lang = en
|
||||||
type = KEYVALUEJSON
|
type = KEYVALUEJSON
|
||||||
```
|
```
|
||||||
|
|
||||||
Then you can upload the source files via a normal `tx push`. Once translations come in, you need to convert the source
|
Then you can upload the source files via a normal `tx push`. Once translations come in, you need to convert the source
|
||||||
files back into the JS files SilverStripe can actually read. This requires an installation of our
|
files back into the JS files SilverStripe can actually read. This requires an installation of our
|
||||||
[buildtools](https://github.com/silverstripe/silverstripe-buildtools).
|
[buildtools](https://github.com/silverstripe/silverstripe-buildtools).
|
||||||
```
|
```
|
||||||
tx pull
|
tx pull
|
||||||
(cd .. && phing -Dmodule=mymodule translation-generate-javascript-for-module)
|
(cd .. && phing -Dmodule=mymodule translation-generate-javascript-for-module)
|
||||||
git add javascript/lang/*
|
git add javascript/lang/*
|
||||||
git commit -m "Updated javascript translations"
|
git commit -m "Updated javascript translations"
|
||||||
```
|
```
|
||||||
# Related
|
# Related
|
||||||
|
|
||||||
|
@ -27,10 +27,10 @@ As opposed to other variables, these should be declared as lower case with under
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
class MyClass
|
class MyClass
|
||||||
{
|
{
|
||||||
private static $my_config_variable = 'foo';
|
private static $my_config_variable = 'foo';
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Prefer identical (===) comparisons over equality (==)
|
## Prefer identical (===) comparisons over equality (==)
|
||||||
@ -40,15 +40,15 @@ Read more in the PHP documentation for [comparison operators](http://php.net/man
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// good - only need to cast to (int) if $a might not already be an int
|
// good - only need to cast to (int) if $a might not already be an int
|
||||||
if ((int)$a === 100) {
|
if ((int)$a === 100) {
|
||||||
doThis();
|
doThis();
|
||||||
}
|
}
|
||||||
|
|
||||||
// bad
|
// bad
|
||||||
if ($a == 100) {
|
if ($a == 100) {
|
||||||
doThis();
|
doThis();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Separation of Logic and Presentation
|
## Separation of Logic and Presentation
|
||||||
@ -57,28 +57,28 @@ Try to avoid using PHP's ability to mix HTML into the code.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// PHP code
|
// PHP code
|
||||||
public function getTitle()
|
public function getTitle()
|
||||||
{
|
{
|
||||||
return "<h2>Bad Example</h2>";
|
return "<h2>Bad Example</h2>";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Template code
|
// Template code
|
||||||
$Title
|
$Title
|
||||||
```
|
```
|
||||||
|
|
||||||
Better: Keep HTML in template files:
|
Better: Keep HTML in template files:
|
||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// PHP code
|
// PHP code
|
||||||
public function getTitle()
|
public function getTitle()
|
||||||
{
|
{
|
||||||
return "Better Example";
|
return "Better Example";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Template code
|
// Template code
|
||||||
<h2>$Title</h2>
|
<h2>$Title</h2>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Comments
|
## Comments
|
||||||
@ -97,21 +97,21 @@ Example:
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
/**
|
/**
|
||||||
* My short description for this class.
|
* My short description for this class.
|
||||||
* My longer description with
|
* My longer description with
|
||||||
* multiple lines and richer formatting.
|
* multiple lines and richer formatting.
|
||||||
*
|
*
|
||||||
* Usage:
|
* Usage:
|
||||||
* <code>
|
* <code>
|
||||||
* $c = new MyClass();
|
* $c = new MyClass();
|
||||||
* $c->myMethod();
|
* $c->myMethod();
|
||||||
* </code>
|
* </code>
|
||||||
*
|
*
|
||||||
* @package custom
|
* @package custom
|
||||||
*/
|
*/
|
||||||
class MyClass extends Class
|
class MyClass extends Class
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* My Method.
|
* My Method.
|
||||||
* This method returns something cool. {@link MyParentMethod} has other cool stuff in it.
|
* This method returns something cool. {@link MyParentMethod} has other cool stuff in it.
|
||||||
@ -124,7 +124,7 @@ Example:
|
|||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Class Member Ordering
|
## Class Member Ordering
|
||||||
@ -148,7 +148,7 @@ with the column or table name escaped with double quotes as below.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
MyClass::get()->where(["\"Score\" > ?" => 50]);
|
MyClass::get()->where(["\"Score\" > ?" => 50]);
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -159,7 +159,7 @@ are single quoted.
|
|||||||
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
MyClass::get()->where("\"Title\" = 'my title'");
|
MyClass::get()->where("\"Title\" = 'my title'");
|
||||||
```
|
```
|
||||||
|
|
||||||
Use [ANSI SQL](http://en.wikipedia.org/wiki/SQL#Standardization) format where possible.
|
Use [ANSI SQL](http://en.wikipedia.org/wiki/SQL#Standardization) format where possible.
|
||||||
|
Loading…
Reference in New Issue
Block a user