mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge remote-tracking branch 'origin/3.2' into 3.3
# Conflicts: # dev/DevelopmentAdmin.php # docs/en/02_Developer_Guides/08_Performance/02_HTTP_Cache_Headers.md # lang/cs.yml # lang/lt.yml
This commit is contained in:
commit
5f2d3f31d7
@ -204,6 +204,7 @@
|
||||
fromContainingPanel: {
|
||||
ontoggle: function(e){
|
||||
this.toggleClass('collapsed', $(e.target).hasClass('collapsed'));
|
||||
$(window).resize(); //Trigger jLayout
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -35,7 +35,7 @@ class DevelopmentAdmin extends Controller {
|
||||
// Special case for dev/build: Defer permission checks to DatabaseAdmin->init() (see #4957)
|
||||
$requestedDevBuild = (stripos($this->getRequest()->getURL(), 'dev/build') === 0)
|
||||
&& (stripos($this->getRequest()->getURL(), 'dev/build/defaults') === false);
|
||||
|
||||
|
||||
// We allow access to this controller regardless of live-status or ADMIN permission only
|
||||
// if on CLI. Access to this controller is always allowed in "dev-mode", or of the user is ADMIN.
|
||||
$canAccess = (
|
||||
|
@ -19,7 +19,7 @@
|
||||
<div class="left">
|
||||
<h2>CMS / Framework Installation <?php if($silverstripe_version) echo "<small>Version $silverstripe_version</small>"; ?></h2>
|
||||
<p>Thanks for choosing to use SilverStripe! Please follow the instructions below and you should be up in running in no time.<br>
|
||||
If you get stuck, head over to the <a href="http://silverstripe.org/installing-silverstripe" target="_blank">installation forum</a>, or check out our list of <a href="http://doc.silverstripe.org/doku.php?id=suggested-web-hosts" target="_blank">suggested web hosts</a> known to work with SilverStripe.
|
||||
If you get stuck, head over to the <a href="http://silverstripe.org/community/forums/installing-silverstripe" target="_blank">installation forum</a>, or check out our page of <a href="http://www.silverstripe.org/hosting" target="_blank">suggested web hosting options</a> known to work with SilverStripe.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -32,7 +32,7 @@
|
||||
<?php if(isset($hasErrorOtherThanDatabase)) { ?>
|
||||
<p class="message error">
|
||||
You aren't currently able to install the software. Please <a href="#requirements">see below</a> for details.<br>
|
||||
If you are having problems meeting the requirements, see the <a href="http://doc.silverstripe.org/framework/en/installation/server-requirements">server requirements</a>.
|
||||
If you are having problems meeting the requirements, see the <a href="http://doc.silverstripe.org/framework/en/installation/server-requirements" target="_blank">server requirements</a>.
|
||||
</p>
|
||||
<?php if (isset($phpIniLocation)) { ?>
|
||||
<p>Your php.ini file is located at <?php echo $phpIniLocation; ?></p>
|
||||
@ -46,7 +46,7 @@
|
||||
<?php } else if($req->hasWarnings()) { ?>
|
||||
<div class="message warning">
|
||||
<p>There are some issues that we recommend you look at before installing, however, you are still able to install the software.
|
||||
<br>Please see below for details. If you are having problems meeting the requirements, see the <a href="http://doc.silverstripe.org/framework/en/installation/server-requirements">server requirements</a>.</p>
|
||||
<br>Please see below for details. If you are having problems meeting the requirements, see the <a href="http://doc.silverstripe.org/framework/en/installation/server-requirements" target="_blank">server requirements</a>.</p>
|
||||
</div>
|
||||
<?php } else if(!$dbReq->hasErrors() && !$adminReq->hasErrors()) { ?>
|
||||
<div class="message goodInstall"><p>You're ready to install! Please confirm the configuration options below. <a href="#install">Install SilverStripe</a></p>
|
||||
@ -243,7 +243,7 @@
|
||||
<h3 class="sectionHeading">Theme selection <small>Step 4 of 5</small></h3>
|
||||
<p class="helpText">You can change the theme or <a href="http://addons.silverstripe.org/add-ons?type=theme">download</a> another from the SilverStripe website after installation.</p>
|
||||
<ul id="Themes">
|
||||
<li><input type="radio" name="template" value="simple" id="Simple" <?php if(!isset($_POST['template']) || $_POST['template'] == 'simple') {?>checked="checked"<?php }?>><label for="Simple"><a href="https://github.com/silverstripe-themes/silverstripe-simple">Simple</a> - our default theme ready to use, or begin the <a href="http://doc.silverstripe.org/framework/en/tutorials" target="_blank">tutorial</a>.</label></li>
|
||||
<li><input type="radio" name="template" value="simple" id="Simple" <?php if(!isset($_POST['template']) || $_POST['template'] == 'simple') {?>checked="checked"<?php }?>><label for="Simple"><a href="https://github.com/silverstripe-themes/silverstripe-simple">Simple</a> - our default theme ready to use, or begin the <a href="http://www.silverstripe.org/learn/lessons" target="_blank">lessons</a>.</label></li>
|
||||
<li><input type="radio" name="template" value="tutorial" id="EmptyTemplate" <?php if(isset($_POST['template']) && $_POST['template'] == 'tutorial') {?>checked="checked"<?php }?>><label for="EmptyTemplate">Empty template</label></li>
|
||||
</ul>
|
||||
<h3 class="sectionHeading" id="install">Confirm Install <small>Step 5 of 5</small></h3>
|
||||
|
@ -23,7 +23,7 @@ Our web-based [PHP installer](installation/) can check if you meet the requireme
|
||||
* MySQL 5.0+
|
||||
* PostgreSQL 8.3+ (requires ["postgresql" module](http://silverstripe.org/postgresql-module))
|
||||
* SQL Server 2008+ (requires ["mssql" module](http://silverstripe.org/microsoft-sql-server-database/))
|
||||
* Support for [Oracle](http://www.silverstripe.org/oracle-database-module/) and [SQLite](http://silverstripe.org/sqlite-database/) is not commercially supported, but is under development by our open source community.
|
||||
* Support for `[Oracle](http://www.silverstripe.org/oracle-database-module/)` and [SQLite](http://silverstripe.org/sqlite-database/) is not commercially supported, but is under development by our open source community.
|
||||
* One of the following web server products:
|
||||
* Apache 2.0+ with mod_rewrite and "AllowOverride All" set
|
||||
* IIS 7+
|
||||
|
@ -71,7 +71,7 @@ every page on the site, if that's easier.
|
||||
|
||||
## I can see unparsed PHP output in my browser
|
||||
|
||||
Please make sure all code inside `*.php` files is wrapped in classes. Due to the way `[api:ManifestBuilder]`
|
||||
Please make sure all code inside `*.php` files is wrapped in classes. Due to the way [api:ManifestBuilder]
|
||||
includes all files with this extension, any **procedural code will be executed on every call**. The most common error here
|
||||
is putting a test.php/phpinfo.php file in the document root. See [datamodel](/developer_guides/model/data_model_and_orm) and [controllers](/developer_guides/controllers)
|
||||
for ways how to structure your code.
|
||||
|
@ -36,11 +36,11 @@ final page. Lets look at each one individually:
|
||||
|
||||
### Model
|
||||
|
||||
All content on our site is stored in a database. Each class that is a child of the `[api:DataObject]` class will have its own table in our database.
|
||||
All content on our site is stored in a database. Each class that is a child of the [api:DataObject] class will have its own table in our database.
|
||||
|
||||
Every object of such a class will correspond to a row in that table -
|
||||
this is our "data object", the **"model"** of Model-View-Controller. A page type has a data object that represents all the data for our page. Rather than inheriting
|
||||
directly from `[api:DataObject]`, it inherits from `[api:SiteTree]`. We generally create a "Page" data object, and subclass this for all other page types. This allows us to define behavior that is consistent across all pages in our site.
|
||||
directly from [api:DataObject], it inherits from [api:SiteTree]. We generally create a "Page" data object, and subclass this for all other page types. This allows us to define behavior that is consistent across all pages in our site.
|
||||
|
||||
### View
|
||||
|
||||
@ -49,7 +49,7 @@ presentation of our website.
|
||||
|
||||
### Controller
|
||||
|
||||
Each page type also has a **"controller"**. The controller contains all the code used to manipulate our data before it is rendered. For example, suppose we were making an auction site, and we only wanted to display the auctions closing in the next ten minutes. We would implement this logic in the controller. The controller for a page should inherit from `[api:ContentController]`. Just as we create a "Page" data object and subclass it for the rest of the site, we also create a "Page_Controller" that is subclassed.
|
||||
Each page type also has a **"controller"**. The controller contains all the code used to manipulate our data before it is rendered. For example, suppose we were making an auction site, and we only wanted to display the auctions closing in the next ten minutes. We would implement this logic in the controller. The controller for a page should inherit from [api:ContentController]. Just as we create a "Page" data object and subclass it for the rest of the site, we also create a "Page_Controller" that is subclassed.
|
||||
|
||||
|
||||
Creating a new page type requires creating each of these three elements. We will then have full control over presentation, the database, and editable CMS fields.
|
||||
@ -95,7 +95,7 @@ Let's create the *ArticleHolder* page type.
|
||||
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
|
||||
to be children of the page in the site tree. As we only want **news articles** in the news section, we only want pages of the type *ArticlePage* as children. We can enforce this in the CMS by setting the *$allowed_children* field within this class.
|
||||
|
||||
We will be introduced to other fields like this as we progress; there is a full list in the documentation for `[api:SiteTree]`.
|
||||
We will be introduced to other fields like this as we progress; there is a full list in the documentation for [api:SiteTree].
|
||||
|
||||
Now that we have created our page types, we need to let SilverStripe rebuild the database: [http://localhost/your_site_name/dev/build](http://localhost/your_site_name/dev/build). SilverStripe should detect that there are two new page types, and add them to the list of page types in the database.
|
||||
|
||||
@ -162,7 +162,7 @@ Let's walk through this method.
|
||||
|
||||
|
||||
Firstly, we get the fields from the parent class; we want to add fields, not replace them. The *$fields* variable
|
||||
returned is a `[api:FieldList]` object.
|
||||
returned is a [api:FieldList] object.
|
||||
|
||||
:::php
|
||||
$fields->addFieldToTab('Root.Main', new TextField('Author'), 'Content');
|
||||
@ -170,14 +170,14 @@ returned is a `[api:FieldList]` object.
|
||||
|
||||
|
||||
We can then add our new fields with *addFieldToTab*. The first argument is the tab on which we want to add the field to:
|
||||
"Root.Main" is the tab which the content editor is on. The second argument is the field to add; this is not a database field, but a `[api:FormField]` - see the documentation for more details.
|
||||
"Root.Main" is the tab which the content editor is on. The second argument is the field to add; this is not a database field, but a [api:FormField] - see the documentation for more details.
|
||||
|
||||
<div class="hint" markdown="1">
|
||||
Note: By default, the CMS only has one tab. Creating new tabs is much like adding to existing tabs. For instance: `$fields->addFieldToTab('Root.NewTab', new TextField('Author'));`
|
||||
would create a new tab called "New Tab", and a single "Author" textfield inside.
|
||||
</div>
|
||||
|
||||
We have added two fields: A simple `[api:TextField]` and a `[api:DateField]`.
|
||||
We have added two fields: A simple [api:TextField]` and a [api:DateField].
|
||||
There are many more fields available in the default installation, listed in ["form field types"](/developer_guides/forms/field_types/common_subclasses).
|
||||
|
||||
:::php
|
||||
@ -232,7 +232,7 @@ By enabling *showCalendar* you show a calendar overlay when clicking on the fiel
|
||||
:::php
|
||||
$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 `[api:DateField]` 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 [api:DateField] documentation for more configuration options.
|
||||
|
||||
:::php
|
||||
$fields->addFieldToTab('Root.Main', new TextField('Author', 'Author Name'), 'Content');
|
||||
@ -269,13 +269,13 @@ First, the template for displaying a single 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.
|
||||
|
||||
To access the new fields, we use *$Date* and *$Author*. In fact, all template variables and page controls come from either the data object or the controller for the page being displayed. The *$Title* variable comes from the *Title* field of the `[api:SiteTree]` class. *$Date* and *$Author* come from the *ArticlePage* table through your custom Page. *$Content* comes from the *SiteTree* table through the same data object. The data for your page is
|
||||
To access the new fields, we use *$Date* and *$Author*. In fact, all template variables and page controls come from either the data object or the controller for the page being displayed. The *$Title* variable comes from the *Title* field of the [api:SiteTree] class. *$Date* and *$Author* come from the *ArticlePage* table through your custom Page. *$Content* comes from the *SiteTree* table through the same data object. The data for your page is
|
||||
spread across several tables in the database matched by id - e.g. *Content* is in the *SiteTree* table, and *Date* and
|
||||
*Author* are in the *ArticlePage* table. SilverStripe matches this data, and collates it into a single data object.
|
||||
|
||||
![](../_images/tutorial2_data-collation.jpg)
|
||||
|
||||
Rather than using *$Date* directly, we use *$Date.Nice*. If we look in the `[api:Date]` documentation, we can see
|
||||
Rather than using *$Date* directly, we use *$Date.Nice*. If we look in the [api:Date] documentation, we can see
|
||||
that the *Nice* function returns the date in *dd/mm/yyyy* format, rather than the *yyyy-mm-dd* format stored in the
|
||||
database.
|
||||
|
||||
@ -305,7 +305,7 @@ We'll now create a template for the article holder. We want our news section to
|
||||
</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 `[api:HTMLText]` 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 [api:HTMLText] field gives us a nice summary of the article. The function strips all tags from the paragraph extracted.
|
||||
|
||||
![](../_images/tutorial2_articleholder.jpg)
|
||||
|
||||
@ -398,7 +398,7 @@ The controller for a page is only created when page is actually visited, while t
|
||||
|
||||
## Creating a RSS feed
|
||||
|
||||
An RSS feed is something that no news section should be without. SilverStripe makes it easy to create RSS feeds by providing an `[api:RSSFeed]` class to do all the hard work for us. Add the following in the *ArticleHolder_Controller* class:
|
||||
An RSS feed is something that no news section should be without. SilverStripe makes it easy to create RSS feeds by providing an [api:RSSFeed] class to do all the hard work for us. Add the following in the *ArticleHolder_Controller* class:
|
||||
|
||||
**mysite/code/ArticleHolder.php**
|
||||
|
||||
@ -418,7 +418,7 @@ Ensure that when you have input the code to implement an RSS feed; flush the web
|
||||
|
||||
This function creates an RSS feed of all the news articles, and outputs it to the browser. If we go to [http://localhost/your_site_name/news/rss](http://localhost/your_site_name/news/rss) we should see our RSS feed. When there is more to a URL after a page's base URL, "rss" in this case, SilverStripe will call the function with that name on the controller if it exists.
|
||||
|
||||
Depending on your browser, you should see something like the picture below. If your browser doesn't support RSS, you will most likely see the XML output instead. For more on RSS, see `[api:RSSFeed]`
|
||||
Depending on your browser, you should see something like the picture below. If your browser doesn't support RSS, you will most likely see the XML output instead. For more on RSS, see [api:RSSFeed]
|
||||
|
||||
![](../_images/tutorial2_rss-feed.jpg)
|
||||
|
||||
@ -483,7 +483,7 @@ Nothing here should be new. The *StaffPage* page type is more interesting though
|
||||
|
||||
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.
|
||||
|
||||
We then add an `[api:UploadField]` in the *getCMSFields* function to the tab "Root.Images". Since this tab doesn't exist,
|
||||
We then add an [api:UploadField] in the *getCMSFields* function to the tab "Root.Images". Since this tab doesn't exist,
|
||||
the *addFieldToTab* function will create it for us. The *UploadField* allows us to select an image or upload a new one in
|
||||
the CMS.
|
||||
|
||||
@ -497,7 +497,7 @@ a new *StaffHolder* called "Staff", and create some *StaffPage*s in it.
|
||||
|
||||
### Creating the staff section templates
|
||||
|
||||
The staff section templates aren't too difficult to create, thanks to the utility methods provided by the `[api:Image]` class.
|
||||
The staff section templates aren't too difficult to create, thanks to the utility methods provided by the [api:Image] class.
|
||||
|
||||
**themes/simple/templates/Layout/StaffHolder.ss**
|
||||
|
||||
@ -521,7 +521,7 @@ The staff section templates aren't too difficult to create, thanks to the utilit
|
||||
</div>
|
||||
|
||||
|
||||
This template is very similar to the *ArticleHolder* template. The *ScaleWidth* method of the `[api:Image]` class
|
||||
This template is very similar to the *ArticleHolder* template. The *ScaleWidth* method of the [api:Image] class
|
||||
will resize the image before sending it to the browser. The resized image is cached, so the server doesn't have to
|
||||
resize the image every time the page is viewed.
|
||||
|
||||
|
@ -74,11 +74,11 @@ Let's step through this code.
|
||||
```
|
||||
|
||||
First we create our form fields.
|
||||
We do this by creating a `[api:FieldList]` and passing our fields as arguments.
|
||||
The first field is a `[api:TextField]` with the name 'Name'.
|
||||
We do this by creating a [api:FieldList] and passing our fields as arguments.
|
||||
The first field is a [api:TextField] with the name 'Name'.
|
||||
There is a second argument when creating a field which specifies the text on the label of the field. If no second
|
||||
argument is passed, as in this case, it is assumed the label is the same as the name of the field.
|
||||
The second field we create is an `[api:OptionsetField]`. This is a dropdown, and takes a third argument - an
|
||||
The second field we create is an [api:OptionsetField]. This is a dropdown, and takes a third argument - an
|
||||
array mapping the values to the options listed in the dropdown.
|
||||
|
||||
```php
|
||||
@ -90,14 +90,14 @@ array mapping the values to the options listed in the dropdown.
|
||||
After creating the fields, we create the form actions. Form actions appear as buttons at the bottom of the form.
|
||||
The first argument is the name of the function to call when the button is pressed, and the second is the label of the button.
|
||||
Here we create a 'Submit' button which calls the 'doBrowserPoll' method, which we will create later.
|
||||
All the form actions (in this case only one) are collected into a `[api:FieldList]` object the same way we did with
|
||||
All the form actions (in this case only one) are collected into a [api:FieldList] object the same way we did with
|
||||
the fields.
|
||||
|
||||
```php
|
||||
return new Form($this, 'BrowserPollForm', $fields, $actions);
|
||||
```
|
||||
|
||||
Finally we create the `[api:Form]` object and return it.
|
||||
Finally we create the [api:Form] object and return it.
|
||||
The first argument is the controller that contains the form, in most cases '$this'. The second is the name of the method
|
||||
that returns the form, which is 'BrowserPollForm' in our case. The third and fourth arguments are the
|
||||
FieldLists containing the fields and form actions respectively.
|
||||
@ -178,8 +178,8 @@ All going according to plan, if you visit [http://localhost/your_site_name/home/
|
||||
|
||||
Great! We now have a browser poll form, but it doesn't actually do anything. In order to make the form work, we have to implement the 'doBrowserPoll()' method that we told it about.
|
||||
|
||||
First, we need some way of saving the poll submissions to the database, so we can retrieve the results later. We can do this by creating a new object that extends from `[api:DataObject]`.
|
||||
If you recall, in the [second tutorial](/tutorials/extending_a_basic_site) we said that all objects that inherit from DataObject and have their own fields are stored in tables the database. Also recall that all pages extend DataObject indirectly through `[api:SiteTree]`. Here instead of extending SiteTree (or `[api:Page]`) to create a page type, we will extend `[api:DataObject]` directly:
|
||||
First, we need some way of saving the poll submissions to the database, so we can retrieve the results later. We can do this by creating a new object that extends from [api:DataObject].
|
||||
If you recall, in the [second tutorial](/tutorials/extending_a_basic_site) we said that all objects that inherit from DataObject and have their own fields are stored in tables the database. Also recall that all pages extend DataObject indirectly through [api:SiteTree]. Here instead of extending SiteTree (or [api:Page]) to create a page type, we will extend [api:DataObject] directly:
|
||||
|
||||
**mysite/code/BrowserPollSubmission.php**
|
||||
|
||||
@ -208,7 +208,7 @@ If we then rebuild the database ([http://localhost/your_site_name/dev/build](htt
|
||||
}
|
||||
```
|
||||
|
||||
A function that processes a form submission takes two arguments - the first is the data in the form, the second is the `[api:Form]` object.
|
||||
A function that processes a form submission takes two arguments - the first is the data in the form, the second is the [api:Form] object.
|
||||
In our function we create a new *BrowserPollSubmission* object. Since the name of our form fields, and the name of the database fields, are the same we can save the form directly into the data object.
|
||||
We call the 'write' method to write our data to the database, and '$this->redirectBack()' will redirect the user back to the home page.
|
||||
|
||||
@ -240,7 +240,7 @@ If we then open the homepage and attempt to submit the form without filling in t
|
||||
Now that we have a working form, we need some way of showing the results.
|
||||
The first thing to do is make it so a user can only vote once per session. If the user hasn't voted, show the form, otherwise show the results.
|
||||
|
||||
We can do this using a session variable. The `[api:Session]` class handles all session variables in SilverStripe. First modify the 'doBrowserPoll' to set the session variable 'BrowserPollVoted' when a user votes.
|
||||
We can do this using a session variable. The [api:Session] class handles all session variables in SilverStripe. First modify the 'doBrowserPoll' to set the session variable 'BrowserPollVoted' when a user votes.
|
||||
|
||||
**mysite/code/HomePage.php**
|
||||
|
||||
@ -279,7 +279,7 @@ Although the form is not shown, you'll still see the 'Browser Poll' heading. We'
|
||||
|
||||
Now that we're collecting data, it would be nice to show the results on the website as well. We could simply output every vote, but that's boring. Let's group the results by browser, through the SilverStripe data model.
|
||||
|
||||
In the [second tutorial](/tutorials/extending_a_basic_site), we got a collection of news articles for the home page by using the 'ArticleHolder::get()' function, which returns a `[api:DataList]`. We can get all submissions in the same fashion, through `BrowserPollSubmission::get()`. This list will be the starting point for our result aggregation.
|
||||
In the [second tutorial](/tutorials/extending_a_basic_site), we got a collection of news articles for the home page by using the 'ArticleHolder::get()' function, which returns a [api:DataList]. We can get all submissions in the same fashion, through `BrowserPollSubmission::get()`. This list will be the starting point for our result aggregation.
|
||||
|
||||
Create the function 'BrowserPollResults' on the *HomePage_Controller* class.
|
||||
|
||||
@ -306,7 +306,7 @@ This code introduces a few new concepts, so let's step through it.
|
||||
```php
|
||||
$submissions = new GroupedList(BrowserPollSubmission::get());
|
||||
```
|
||||
First we get all of the `BrowserPollSubmission` records from the database. This returns the submissions as a `[api:DataList]`.Then we wrap it inside a `[api:GroupedList]`, which adds the ability to group those records. The resulting object will behave just like the original `DataList`, though (with the addition of a `groupBy()` method).
|
||||
First we get all of the `BrowserPollSubmission` records from the database. This returns the submissions as a [api:DataList]. Then we wrap it inside a [api:GroupedList], which adds the ability to group those records. The resulting object will behave just like the original `DataList`, though (with the addition of a `groupBy()` method).
|
||||
|
||||
```php
|
||||
$total = $submissions->Count();
|
||||
@ -324,9 +324,9 @@ We get the total number of submissions, which is needed to calculate the percent
|
||||
}
|
||||
```
|
||||
|
||||
Now we create an empty `[api:ArrayList]` to hold the data we'll pass to the template. Its similar to `[api:DataList]`, but can hold arbitrary objects rather than just DataObject` instances. Then we iterate over the 'Browser' submissions field.
|
||||
Now we create an empty [api:ArrayList] to hold the data we'll pass to the template. Its similar to [api:DataList], but can hold arbitrary objects rather than just DataObject` instances. Then we iterate over the 'Browser' submissions field.
|
||||
|
||||
The `groupBy()` method splits our list by the 'Browser' field passed to it, creating new lists with submissions just for a specific browser. Each of those lists is keyed by the browser name. The aggregated result is then contained in an `[api:ArrayData]` object, which behaves much like a standard PHP array, but allows us to use it in SilverStripe templates.
|
||||
The `groupBy()` method splits our list by the 'Browser' field passed to it, creating new lists with submissions just for a specific browser. Each of those lists is keyed by the browser name. The aggregated result is then contained in an [api:ArrayData] object, which behaves much like a standard PHP array, but allows us to use it in SilverStripe templates.
|
||||
|
||||
|
||||
The final step is to create the template to display our data. Change the 'BrowserPoll' div to the below.
|
||||
|
@ -92,7 +92,7 @@ function, and then attempt to render it with *Page_results.ss*, falling back to
|
||||
## Creating the template
|
||||
|
||||
Lastly we need the template for the search page. This template uses all the same techniques used in previous
|
||||
tutorials. It also uses a number of pagination variables, which are provided by the `[api:PaginatedList]`
|
||||
tutorials. It also uses a number of pagination variables, which are provided by the [api:PaginatedList]
|
||||
class.
|
||||
|
||||
*themes/simple/templates/Layout/Page_results.ss*
|
||||
|
@ -160,7 +160,7 @@ It's empty by default, but you can add new students as required,
|
||||
or relate them to the project by typing in the box above the table.
|
||||
|
||||
In our case, we want to manage those records, edit their details, and add new ones.
|
||||
To accomplish this, we have added a specific `[api:GridFieldConfig]`.
|
||||
To accomplish this, we have added a specific [api:GridFieldConfig].
|
||||
While we could've built the config from scratch, there's several
|
||||
preconfigured instances. The `GridFieldConfig_RecordEditor` default configures
|
||||
the field to edit records, rather than just viewing them.
|
||||
@ -427,4 +427,4 @@ we suggest some excercises to make the solution more flexible:
|
||||
and avoid any duplication between the two subclasses.
|
||||
* Render mentor details in their own template
|
||||
* Change the `GridField` to list only five records per page (the default is 20).
|
||||
This configuration is stored in the `[api:GridFieldPaginator]` component
|
||||
This configuration is stored in the [api:GridFieldPaginator] component
|
||||
|
@ -461,7 +461,7 @@ Occasionally, the system described above won't let you do exactly what you need
|
||||
methods that manipulate the SQL query at a lower level. When using these, please ensure that all table and field names
|
||||
are escaped with double quotes, otherwise some DB backends (e.g. PostgreSQL) won't work.
|
||||
|
||||
Under the hood, query generation is handled by the `[api:DataQuery]` class. This class does provide more direct access
|
||||
Under the hood, query generation is handled by the [api:DataQuery] class. This class does provide more direct access
|
||||
to certain SQL features that `DataList` abstracts away from you.
|
||||
|
||||
In general, we advise against using these methods unless it's absolutely necessary. If the ORM doesn't do quite what
|
||||
@ -573,7 +573,7 @@ sub-classes of the base class (including the base class itself).
|
||||
example above, NewsSection didn't have its own data, so an extra table would be redundant.
|
||||
|
||||
* In all the tables, ID is the primary key. A matching ID number is used for all parts of a particular record:
|
||||
record #2 in Page refers to the same object as record #2 in `[api:SiteTree]`.
|
||||
record #2 in Page refers to the same object as record #2 in [api:SiteTree].
|
||||
|
||||
To retrieve a news article, SilverStripe joins the [api:SiteTree], [api:Page] and NewsPage tables by their ID fields.
|
||||
|
||||
|
@ -295,7 +295,7 @@ and `remove()` method.
|
||||
You can use the ORM to get a filtered result list without writing any SQL. For example, this snippet gets you the
|
||||
"Players"-relation on a team, but only containing active players.
|
||||
|
||||
See `[api:DataObject::$has_many]` for more info on the described relations.
|
||||
See [api:DataObject::$has_many] for more info on the described relations.
|
||||
|
||||
:::php
|
||||
<?php
|
||||
|
@ -82,7 +82,7 @@ E.g.
|
||||
echo $row['BirthYear'];
|
||||
}
|
||||
|
||||
The result of `SQLSelect::execute()` is an array lightly wrapped in a database-specific subclass of `[api:SS_Query]`.
|
||||
The result of `SQLSelect::execute()` is an array lightly wrapped in a database-specific subclass of [api:SS_Query].
|
||||
This class implements the *Iterator*-interface, and provides convenience-methods for accessing the data.
|
||||
|
||||
### DELETE
|
||||
@ -165,7 +165,7 @@ E.g.
|
||||
|
||||
:::php
|
||||
<?php
|
||||
$update = SQLUpdate::create('"SiteTree"')->where(array('ID' => 3));
|
||||
$update = SQLUpdate::create('"SiteTree"')->addWhere(array('ID' => 3));
|
||||
|
||||
// assigning a list of items
|
||||
$update->addAssignments(array(
|
||||
@ -279,4 +279,4 @@ An alternative approach would be a custom getter in the object definition.
|
||||
* [api:SQLSelect]
|
||||
* [api:DB]
|
||||
* [api:Query]
|
||||
* [api:Database]
|
||||
* [api:Database]
|
||||
|
@ -9,7 +9,7 @@ While this is a useful approach, it can lead to data inconsistencies if the reco
|
||||
controller and form context.
|
||||
|
||||
Most validation constraints are actually data constraints which belong on the model. SilverStripe provides the
|
||||
[api:DataObject->validate] method for this purpose.
|
||||
[api:DataObject::validate()] method for this purpose.
|
||||
|
||||
By default, there is no validation - objects are always valid! However, you can overload this method in your DataObject
|
||||
sub-classes to specify custom validation, or use the `validate` hook through a [api:DataExtension].
|
||||
|
@ -10,7 +10,7 @@ It is most commonly applied to pages in the CMS (the `SiteTree` class). Draft co
|
||||
from published content shown to your website visitors.
|
||||
|
||||
Versioning in SilverStripe is handled through the [api:Versioned] class. As a [api:DataExtension] it is possible to
|
||||
be applied to any `[api:DataObject]` subclass. The extension class will automatically update read and write operations
|
||||
be applied to any [api:DataObject]` subclass. The extension class will automatically update read and write operations
|
||||
done via the ORM via the `augmentSQL` database hook.
|
||||
|
||||
Adding Versioned to your `DataObject` subclass works the same as any other extension. It accepts two or more arguments
|
||||
@ -82,7 +82,7 @@ The record is retrieved as a `DataObject`, but saving back modifications via `wr
|
||||
rather than modifying the existing one.
|
||||
</div>
|
||||
|
||||
In order to get a list of all versions for a specific record, we need to generate specialized `[api:Versioned_Version]`
|
||||
In order to get a list of all versions for a specific record, we need to generate specialized [api:Versioned_Version]
|
||||
objects, which expose the same database information as a `DataObject`, but also include information about when and how
|
||||
a record was published.
|
||||
|
||||
@ -95,9 +95,9 @@ a record was published.
|
||||
|
||||
The usual call to `DataObject->write()` will write to whatever stage is currently active, as defined by the
|
||||
`Versioned::current_stage()` global setting. Each call will automatically create a new version in the
|
||||
`<class>_versions` table. To avoid this, use `[writeWithoutVersion()](api:Versioned->writeWithoutVersion())` instead.
|
||||
`<class>_versions` table. To avoid this, use [writeWithoutVersion()](api:Versioned::writeWithoutVersion()) instead.
|
||||
|
||||
To move a saved version from one stage to another, call `[writeToStage(<stage>)](api:Versioned->writeToStage())` on the
|
||||
To move a saved version from one stage to another, call [writeToStage(<stage>)](api:Versioned::writeToStage()) on the
|
||||
object. The process of moving a version to a different stage is also called "publishing", so we've created a shortcut
|
||||
for this: `publish(<from-stage>, <to-stage>)`.
|
||||
|
||||
@ -132,7 +132,7 @@ is initialized. But it can also be set and reset temporarily to force a specific
|
||||
### Custom SQL
|
||||
|
||||
We generally discourage writing `Versioned` queries from scratch, due to the complexities involved through joining
|
||||
multiple tables across an inherited table scheme (see `[api:Versioned->augmentSQL()]`). If possible, try to stick to
|
||||
multiple tables across an inherited table scheme (see [api:Versioned::augmentSQL()]). If possible, try to stick to
|
||||
smaller modifications of the generated `DataList` objects.
|
||||
|
||||
Example: Get the first 10 live records, filtered by creation date:
|
||||
|
@ -146,7 +146,7 @@ To include relations (`$has_one`, `$has_many` and `$many_many`) in your search,
|
||||
### Summary Fields
|
||||
|
||||
Summary fields can be used to show a quick overview of the data for a specific [api:DataObject] record. The most common use
|
||||
is their display as table columns, e.g. in the search results of a `[api:ModelAdmin]` CMS interface.
|
||||
is their display as table columns, e.g. in the search results of a [api:ModelAdmin] CMS interface.
|
||||
|
||||
:::php
|
||||
<?php
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
The [api:DataObject::$defaults] array allows you to specify simple static values to be the default values when a
|
||||
record is created, but in many situations default values need to be dynamically calculated. In order to do this, the
|
||||
[api:DataObject->populateDefaults()] method will need to be overloaded.
|
||||
[api:DataObject::populateDefaults()] method will need to be overloaded.
|
||||
|
||||
This method is called whenever a new record is instantiated, and you must be sure to call the method on the parent
|
||||
object!
|
||||
|
@ -6,8 +6,8 @@ These lists can get quite long, and hard to present on a single list.
|
||||
by splitting up the list into multiple pages.
|
||||
|
||||
In this howto, we present an alternative to pagination:
|
||||
Grouping a list by various criteria, through the `[api:GroupedList]` class.
|
||||
This class is a `[api:SS_ListDecorator]`, which means it wraps around a list,
|
||||
Grouping a list by various criteria, through the [api:GroupedList] class.
|
||||
This class is a [api:SS_ListDecorator], which means it wraps around a list,
|
||||
adding new functionality.
|
||||
|
||||
It provides a `groupBy()` method, which takes a field name, and breaks up the managed list
|
||||
@ -88,7 +88,7 @@ In this case, the `getTitleFirstLetter()` method defined earlier is used to brea
|
||||
Grouping a set by month is a very similar process.
|
||||
The only difference would be to sort the records by month name, and
|
||||
then create a method on the DataObject that returns the month name,
|
||||
and pass that to the [api:GroupedList->GroupedBy()] call.
|
||||
and pass that to the [api:GroupedList::GroupedBy()] call.
|
||||
|
||||
We're reusing our example `Module` object,
|
||||
but grouping by its built-in `Created` property instead,
|
||||
@ -128,7 +128,7 @@ sorted by month name from January to December. This can be accomplshed by sortin
|
||||
|
||||
}
|
||||
|
||||
The final step is the render this into the template using the [api:GroupedList->GroupedBy()] method.
|
||||
The final step is the render this into the template using the [api:GroupedList::GroupedBy()] method.
|
||||
|
||||
:::ss
|
||||
// Modules list grouped by the Month Posted
|
||||
|
@ -403,7 +403,7 @@ You can add your own forms by implementing new form instances (see the [Forms tu
|
||||
|
||||
## API Documentation
|
||||
|
||||
* `[api:ContentController]`: The main controller responsible for handling pages.
|
||||
* `[api:Controller]`: Generic controller (not specific to pages.)
|
||||
* `[api:DataObject]`: Underlying model class for page objects.
|
||||
* `[api:ViewableData]`: Underlying object class for pretty much anything displayable.
|
||||
* [api:ContentController]: The main controller responsible for handling pages.
|
||||
* [api:Controller]: Generic controller (not specific to pages.)
|
||||
* [api:DataObject]: Underlying model class for page objects.
|
||||
* [api:ViewableData]: Underlying object class for pretty much anything displayable.
|
||||
|
@ -23,7 +23,7 @@ Requiring assets from the template is restricted compared to the PHP API.
|
||||
## PHP Requirements API
|
||||
|
||||
It is common practice to include most Requirements either in the *init()*-method of your [controller](../controllers/), or
|
||||
as close to rendering as possible (e.g. in `[api:FormField]`.
|
||||
as close to rendering as possible (e.g. in [api:FormField]).
|
||||
|
||||
:::php
|
||||
<?php
|
||||
|
@ -8,7 +8,7 @@ All objects that are being rendered in a template should be a [api:ViewableData]
|
||||
[scope](syntax#scope).
|
||||
|
||||
For instance, if we provide a [api:HtmlText] instance to the template we can call the `FirstParagraph` method. This will
|
||||
output the result of the [api:HtmlText::FirstParagraph] method to the template.
|
||||
output the result of the [api:HtmlText::FirstParagraph()] method to the template.
|
||||
|
||||
**mysite/code/Page.ss**
|
||||
|
||||
@ -98,7 +98,7 @@ this purpose.
|
||||
There's some exceptions to this rule, see the ["security" guide](../security).
|
||||
</div>
|
||||
|
||||
In case you want to explicitly allow un-escaped HTML input, the property can be cast as `[api:HTMLText]`. The following
|
||||
In case you want to explicitly allow un-escaped HTML input, the property can be cast as [api:HTMLText]. The following
|
||||
example takes the `Content` field in a `SiteTree` class, which is of this type. It forces the content into an explicitly
|
||||
escaped format.
|
||||
|
||||
|
@ -25,8 +25,8 @@ Note that the concept of "pages" used in pagination does not necessarily mean th
|
||||
it's just a term to describe a sub-collection of the list.
|
||||
</div>
|
||||
|
||||
There are two ways to generate pagination controls: [api:PaginatedList->Pages] and
|
||||
[api:PaginatedList->PaginationSummary]. In this example we will use `PaginationSummary()`.
|
||||
There are two ways to generate pagination controls: [api:PaginatedList::Pages()] and
|
||||
[api:PaginatedList::PaginationSummary()]. In this example we will use `PaginationSummary()`.
|
||||
|
||||
The first step is to simply list the objects in the template:
|
||||
|
||||
@ -72,7 +72,7 @@ If there is more than one page, this block will render a set of pagination contr
|
||||
|
||||
In some situations where you are generating the list yourself, the underlying list will already contain only the items
|
||||
that you wish to display on the current page. In this situation the automatic limiting done by [api:PaginatedList]
|
||||
will break the pagination. You can disable automatic limiting using the [api:PaginatedList->setLimitItems] method
|
||||
will break the pagination. You can disable automatic limiting using the [api:PaginatedList::setLimitItems()] method
|
||||
when using custom lists.
|
||||
|
||||
:::php
|
||||
|
@ -159,7 +159,7 @@ Each controller should define a `Link()` method. This should be used to avoid ha
|
||||
}
|
||||
|
||||
<div class="info" markdown="1">
|
||||
The [api:Controller::join_links] is optional, but makes `Link()` more flexible by allowing an `$action` argument, and concatenates the path segments with slashes. The action should map to a method on your controller.
|
||||
The [api:Controller::join_links()] is optional, but makes `Link()` more flexible by allowing an `$action` argument, and concatenates the path segments with slashes. The action should map to a method on your controller.
|
||||
</div>
|
||||
|
||||
## Related Documentation
|
||||
|
@ -95,7 +95,7 @@ You can also fetch one parameter at a time.
|
||||
|
||||
## URL Patterns
|
||||
|
||||
The `[api:RequestHandler]` class will parse all rules you specify against the following patterns. The most specific rule
|
||||
The [api:RequestHandler] class will parse all rules you specify against the following patterns. The most specific rule
|
||||
will be the one followed for the response.
|
||||
|
||||
<div class="alert">
|
||||
|
@ -5,7 +5,7 @@ summary: Validate form data through the server side validation API.
|
||||
|
||||
SilverStripe provides server-side form validation out of the box through the [api:Validator] class and its' child class
|
||||
[api:RequiredFields]. A single `Validator` instance is set on each `Form`. Validators are implemented as an argument to
|
||||
the `[api:Form]` constructor or through the function `setValidator`.
|
||||
the [api:Form] constructor or through the function `setValidator`.
|
||||
|
||||
:::php
|
||||
<?php
|
||||
@ -50,7 +50,7 @@ In this example we will be required to input a value for `Name` and a valid emai
|
||||
|
||||
<div class="info" markdown="1">
|
||||
Each individual [api:FormField] instance is responsible for validating the submitted content through the
|
||||
[api:FormField::validate] method. By default, this just checks the value exists. Fields like `EmailField` override
|
||||
[api:FormField::validate()] method. By default, this just checks the value exists. Fields like `EmailField` override
|
||||
`validate` to check for a specific format.
|
||||
</div>
|
||||
|
||||
@ -192,7 +192,7 @@ classes added to each input. For Parsley we can structure the form like.
|
||||
## Model Validation
|
||||
|
||||
An alternative (or additional) approach to validation is to place it directly on the database model. SilverStripe
|
||||
provides a `[api:DataObject->validate]` method to validate data at the model level. See
|
||||
provides a [api:DataObject::validate()] method to validate data at the model level. See
|
||||
[Data Model Validation](../model/validation).
|
||||
|
||||
### Validation in the CMS
|
||||
|
@ -3,73 +3,73 @@ summary: A table containing a list of the common FormField subclasses.
|
||||
|
||||
# Common FormField type subclasses
|
||||
|
||||
This is a high level overview of available `[api:FormField]` subclasses. An automatically generated list is available
|
||||
This is a high level overview of available [api:FormField] subclasses. An automatically generated list is available
|
||||
on the SilverStripe API documentation.
|
||||
|
||||
## Basic
|
||||
|
||||
* `[api:CheckboxField]`: Single checkbox field.
|
||||
* `[api:DropdownField]`: A `<select>` tag. Can optionally save into has-one relationships.
|
||||
* `[api:ReadonlyField]`: Read-only field to display a non-editable value with a label.
|
||||
* `[api:TextareaField]`: Multi-line text field.
|
||||
* `[api:TextField]`: Single-line text field.
|
||||
* `[api:PasswordField]`: Masked input field.
|
||||
* [api:CheckboxField]: Single checkbox field.
|
||||
* [api:DropdownField]: A `<select>` tag. Can optionally save into has-one relationships.
|
||||
* [api:ReadonlyField]: Read-only field to display a non-editable value with a label.
|
||||
* [api:TextareaField]: Multi-line text field.
|
||||
* [api:TextField]: Single-line text field.
|
||||
* [api:PasswordField]: Masked input field.
|
||||
|
||||
## Actions
|
||||
|
||||
* `[api:FormAction]`: Button element for forms, both for `<input type="submit">` and `<button>`.
|
||||
* `[api:ResetFormAction]`: Action that clears all fields on a form.
|
||||
* [api:FormAction]: Button element for forms, both for `<input type="submit">` and `<button>`.
|
||||
* [api:ResetFormAction]: Action that clears all fields on a form.
|
||||
|
||||
## Formatted input
|
||||
|
||||
* `[api:AjaxUniqueTextField]`: Text field that automatically checks that the value entered is unique for the given set of fields in a given set of tables.
|
||||
* `[api:ConfirmedPasswordField]`: Two masked input fields, checks for matching passwords.
|
||||
* `[api:CountryDropdownField]`: A simple extension to dropdown field, pre-configured to list countries.
|
||||
* `[api:CreditCardField]`: Allows input of credit card numbers via four separate form fields, including generic validation of its numeric values.
|
||||
* `[api:CurrencyField]`: Text field, validating its input as a currency. Limited to US-centric formats, including a hardcoded currency symbol and decimal separators.
|
||||
See `[api:MoneyField]` for a more flexible implementation.
|
||||
* `[api:DateField]`: Represents a date in a single input field, or separated into day, month, and year. Can optionally use a calendar popup.
|
||||
* `[api:DatetimeField]`: Combined date- and time field.
|
||||
* `[api:EmailField]`: Text input field with validation for correct email format according to RFC 2822.
|
||||
* `[api:GroupedDropdownField]`: Grouped dropdown, using <optgroup> tags.
|
||||
* `[api:HtmlEditorField]`: A WYSIWYG editor interface.
|
||||
* `[api:MoneyField]`: A form field that can save into a `[api:Money]` database field.
|
||||
* `[api:NumericField]`: Text input field with validation for numeric values.
|
||||
* `[api:OptionsetField]`: Set of radio buttons designed to emulate a dropdown.
|
||||
* `[api:PhoneNumberField]`: Field for displaying phone numbers. It separates the number, the area code and optionally the country code and extension.
|
||||
* `[api:SelectionGroup]`: SelectionGroup represents a number of fields which are selectable by a radio button that appears at the beginning of each item.
|
||||
* `[api:TimeField]`: Input field with time-specific, localised validation.
|
||||
* [api:AjaxUniqueTextField]: Text field that automatically checks that the value entered is unique for the given set of fields in a given set of tables.
|
||||
* [api:ConfirmedPasswordField]: Two masked input fields, checks for matching passwords.
|
||||
* [api:CountryDropdownField]: A simple extension to dropdown field, pre-configured to list countries.
|
||||
* [api:CreditCardField]: Allows input of credit card numbers via four separate form fields, including generic validation of its numeric values.
|
||||
* [api:CurrencyField]: Text field, validating its input as a currency. Limited to US-centric formats, including a hardcoded currency symbol and decimal separators.
|
||||
See [api:MoneyField] for a more flexible implementation.
|
||||
* [api:DateField]: Represents a date in a single input field, or separated into day, month, and year. Can optionally use a calendar popup.
|
||||
* [api:DatetimeField]: Combined date- and time field.
|
||||
* [api:EmailField]: Text input field with validation for correct email format according to RFC 2822.
|
||||
* [api:GroupedDropdownField]: Grouped dropdown, using <optgroup> tags.
|
||||
* [api:HtmlEditorField]: A WYSIWYG editor interface.
|
||||
* [api:MoneyField]: A form field that can save into a [api:Money] database field.
|
||||
* [api:NumericField]: Text input field with validation for numeric values.
|
||||
* [api:OptionsetField]: Set of radio buttons designed to emulate a dropdown.
|
||||
* [api:PhoneNumberField]: Field for displaying phone numbers. It separates the number, the area code and optionally the country code and extension.
|
||||
* [api:SelectionGroup]: SelectionGroup represents a number of fields which are selectable by a radio button that appears at the beginning of each item.
|
||||
* [api:TimeField]: Input field with time-specific, localised validation.
|
||||
|
||||
## Structure
|
||||
|
||||
* `[api:CompositeField]`: Base class for all fields that contain other fields. Uses `<div>` in template, but
|
||||
* [api:CompositeField]: Base class for all fields that contain other fields. Uses `<div>` in template, but
|
||||
doesn't necessarily have any visible styling.
|
||||
* `[api:FieldGroup] attached in CMS-context.
|
||||
* `[api:FieldList]`: Basic container for sequential fields, or nested fields through CompositeField.
|
||||
* `[api:TabSet]`: Collection of fields which is rendered as separate tabs. Can be nested.
|
||||
* `[api:Tab]`: A single tab inside a `TabSet`.
|
||||
* `[api:ToggleCompositeField]`: Allows visibility of a group of fields to be toggled.
|
||||
* [api:FieldGroup] attached in CMS-context.
|
||||
* [api:FieldList]: Basic container for sequential fields, or nested fields through CompositeField.
|
||||
* [api:TabSet]: Collection of fields which is rendered as separate tabs. Can be nested.
|
||||
* [api:Tab]: A single tab inside a `TabSet`.
|
||||
* [api:ToggleCompositeField]: Allows visibility of a group of fields to be toggled.
|
||||
|
||||
## Files
|
||||
|
||||
* `[api:FileField]`: Simple file upload dialog.
|
||||
* `[api:UploadField]`: File uploads through HTML5 features, including upload progress, preview and relationship management.
|
||||
* [api:FileField]: Simple file upload dialog.
|
||||
* [api:UploadField]: File uploads through HTML5 features, including upload progress, preview and relationship management.
|
||||
|
||||
## Relations
|
||||
|
||||
* `[api:CheckboxSetField]`: Displays a set of checkboxes as a logical group.
|
||||
* `[api:TableField]`: In-place editing of tabular data.
|
||||
* `[api:TreeDropdownField]`: Dropdown-like field that allows you to select an item from a hierarchical AJAX-expandable tree.
|
||||
* `[api:TreeMultiselectField]`: Represents many-many joins using a tree selector shown in a dropdown-like element
|
||||
* `[api:GridField]`: Displays a `[api:SS_List]` in a tabular format. Versatile base class which can be configured to allow editing, sorting, etc.
|
||||
* `[api:ListboxField]`: Multi-line listbox field, through `<select multiple>`.
|
||||
* [api:CheckboxSetField]: Displays a set of checkboxes as a logical group.
|
||||
* [api:TableField]: In-place editing of tabular data.
|
||||
* [api:TreeDropdownField]: Dropdown-like field that allows you to select an item from a hierarchical AJAX-expandable tree.
|
||||
* [api:TreeMultiselectField]: Represents many-many joins using a tree selector shown in a dropdown-like element
|
||||
* [api:GridField]: Displays a [api:SS_List] in a tabular format. Versatile base class which can be configured to allow editing, sorting, etc.
|
||||
* [api:ListboxField]: Multi-line listbox field, through `<select multiple>`.
|
||||
|
||||
|
||||
## Utility
|
||||
|
||||
* `[api:DatalessField]` - Base class for fields which add some HTML to the form but don't submit any data or
|
||||
* [api:DatalessField] - Base class for fields which add some HTML to the form but don't submit any data or
|
||||
save it to the database
|
||||
* `[api:HeaderField]`: Renders a simple HTML header element.
|
||||
* `[api:HiddenField]` - Renders a hidden input field.
|
||||
* `[api:LabelField]`: Simple label tag. This can be used to add extra text in your forms.
|
||||
* `[api:LiteralField]`: Renders arbitrary HTML into a form.
|
||||
* [api:HeaderField]: Renders a simple HTML header element.
|
||||
* [api:HiddenField] - Renders a hidden input field.
|
||||
* [api:LabelField]: Simple label tag. This can be used to add extra text in your forms.
|
||||
* [api:LiteralField]: Renders arbitrary HTML into a form.
|
||||
|
@ -122,9 +122,9 @@ field description as an example.
|
||||
$dateField->setAttribute('placeholder', $dateField->getConfig('dateformat'));
|
||||
|
||||
<div class="notice" markdown="1">
|
||||
Fields scaffolded through [api:DataObject::scaffoldCMSFields] automatically have a description attached to them.
|
||||
Fields scaffolded through [api:DataObject::scaffoldCMSFields()] automatically have a description attached to them.
|
||||
</div>
|
||||
|
||||
## API Documentation
|
||||
|
||||
* [api:DateField]
|
||||
* [api:DateField]
|
||||
|
@ -10,8 +10,8 @@ On top of the base functionality, we use our own insertion dialogs to ensure you
|
||||
files. In addition to the markup managed by TinyMCE, we use [shortcodes](/developer_guides/extending/shortcodes) to store
|
||||
information about inserted images or media elements.
|
||||
|
||||
The framework comes with a `[api:HTMLEditorField]` form field class which encapsulates most of the required
|
||||
functionality. It is usually added through the `[api:DataObject->getCMSFields()]` method:
|
||||
The framework comes with a [api:HTMLEditorField] form field class which encapsulates most of the required
|
||||
functionality. It is usually added through the [api:DataObject::getCMSFields()] method:
|
||||
|
||||
**mysite/code/MyObject.php**
|
||||
|
||||
@ -33,14 +33,14 @@ functionality. It is usually added through the `[api:DataObject->getCMSFields()]
|
||||
|
||||
### Specify which configuration to use
|
||||
|
||||
By default, a config named 'cms' is used in any new `[api:HTMLEditorField]`.
|
||||
By default, a config named 'cms' is used in any new [api:HTMLEditorField].
|
||||
|
||||
If you have created your own `[api:HtmlEditorConfig]` and would like to use it,
|
||||
you can call `HtmlEditorConfig::set_active('myConfig')` and all subsequently created `[api:HTMLEditorField]`
|
||||
If you have created your own [api:HtmlEditorConfig] and would like to use it,
|
||||
you can call `HtmlEditorConfig::set_active('myConfig')` and all subsequently created [api:HTMLEditorField]
|
||||
will use the configuration with the name 'myConfig'.
|
||||
|
||||
You can also specify which `[api:HtmlEditorConfig]` to use on a per field basis via the construct argument.
|
||||
This is particularly useful if you need different configurations for multiple `[api:HTMLEditorField]` on the same page or form.
|
||||
You can also specify which [api:HtmlEditorConfig] to use on a per field basis via the construct argument.
|
||||
This is particularly useful if you need different configurations for multiple [api:HTMLEditorField] on the same page or form.
|
||||
|
||||
:::php
|
||||
class MyObject extends DataObject {
|
||||
@ -62,10 +62,10 @@ In the above example, the 'Content' field will use the default 'cms' config whil
|
||||
## Configuration
|
||||
|
||||
To keep the JavaScript editor configuration manageable and extensible, we've wrapped it in a PHP class called
|
||||
`[api:HtmlEditorConfig]`. The class comes with its own defaults, which are extended through the [Configuration API](../../configuration)
|
||||
[api:HtmlEditorConfig]. The class comes with its own defaults, which are extended through the [Configuration API](../../configuration)
|
||||
in the framework (and the `cms` module in case you've got that installed).
|
||||
|
||||
There can be multiple configs, which should always be created / accessed using `[api:HtmlEditorConfig::get]`. You can
|
||||
There can be multiple configs, which should always be created / accessed using [api:HtmlEditorConfig::get()]. You can
|
||||
then set the currently active config using `set_active()`.
|
||||
|
||||
<div class="info" markdown="1">
|
||||
@ -81,7 +81,7 @@ order is alphabetical, so if you set a TinyMCE option in the `aardvark/_config.p
|
||||
|
||||
In its simplest form, the configuration of the editor includes adding and removing buttons and plugins.
|
||||
|
||||
You can add plugins to the editor using the Framework's `[api:HtmlEditorConfig::enablePlugins]` method. This will
|
||||
You can add plugins to the editor using the Framework's [api:HtmlEditorConfig::enablePlugins()] method. This will
|
||||
transparently generate the relevant underlying TinyMCE code.
|
||||
|
||||
**mysite/_config.php**
|
||||
@ -108,7 +108,7 @@ Buttons can also be removed:
|
||||
HtmlEditorConfig::get('cms')->removeButtons('tablecontrols', 'blockquote', 'hr');
|
||||
|
||||
<div class="notice" markdown="1">
|
||||
Internally `[api:HtmlEditorConfig]` uses the TinyMCE's `theme_advanced_buttons` option to configure these. See the
|
||||
Internally [api:HtmlEditorConfig] uses the TinyMCE's `theme_advanced_buttons` option to configure these. See the
|
||||
[TinyMCE documentation of this option](http://www.tinymce.com/wiki.php/Configuration:theme_advanced_buttons_1_n)
|
||||
for more details.
|
||||
</div>
|
||||
@ -182,7 +182,7 @@ plugin that adds a button to the editor:
|
||||
tinymce.PluginManager.add('myplugin', tinymce.plugins.myplugin);
|
||||
})();
|
||||
|
||||
You can then enable this plugin through the [api:HtmlEditorConfig::enablePlugins]:
|
||||
You can then enable this plugin through the [api:HtmlEditorConfig::enablePlugins()]:
|
||||
|
||||
**mysite/_config.php**
|
||||
:::php
|
||||
@ -193,8 +193,8 @@ documentation, or browse through plugins that come with the Framework at `thirdp
|
||||
|
||||
## Image and media insertion
|
||||
|
||||
The `[api:HtmlEditorField]` API also handles inserting images and media files into the managed HTML content. It can be
|
||||
used both for referencing files on the webserver filesystem (through the `[api:File]` and `[api:Image]` APIs), as well
|
||||
The [api:HtmlEditorField] API also handles inserting images and media files into the managed HTML content. It can be
|
||||
used both for referencing files on the webserver filesystem (through the [api:File] and [api:Image] APIs), as well
|
||||
as hotlinking files from the web.
|
||||
|
||||
We use [shortcodes](/developer_guides/extending/shortcodes) to store information about inserted images or media elements. The
|
||||
@ -207,7 +207,7 @@ The ["oEmbed" standard](http://www.oembed.com/) is implemented by many media ser
|
||||
representation of files just by referencing a website URL. For example, a content author can insert a playable youtube
|
||||
video just by knowing its URL, as opposed to dealing with manual HTML code.
|
||||
|
||||
oEmbed powers the "Insert from web" feature available through `[api:HtmlEditorField]`. Internally, it makes HTTP
|
||||
oEmbed powers the "Insert from web" feature available through [api:HtmlEditorField]. Internally, it makes HTTP
|
||||
queries to a list of external services if it finds a matching URL. These services are described in the
|
||||
`Oembed.providers` configuration. Since these requests are performed on page rendering, they typically have a long
|
||||
cache time (multiple days).
|
||||
@ -245,7 +245,7 @@ By default, TinyMCE and SilverStripe will generate valid HTML5 markup, but it wi
|
||||
`<article>` or `<figure>`. If you plan to use those, add them to the
|
||||
[valid_elements](http://www.tinymce.com/wiki.php/Configuration:valid_elements) configuration setting.
|
||||
|
||||
Also, the `[api:SS_HTMLValue]` API underpinning the HTML processing parses the markup into a temporary object tree
|
||||
Also, the [api:SS_HTMLValue] API underpinning the HTML processing parses the markup into a temporary object tree
|
||||
which can be traversed and modified before saving. The built-in parser only supports HTML4 and XHTML syntax. In order
|
||||
to successfully process HTML5 tags, please use the
|
||||
['silverstripe/html5' module](https://github.com/silverstripe/silverstripe-html5).
|
||||
@ -277,22 +277,22 @@ Adding functionality is a bit more advanced, you'll most likely
|
||||
need to add some fields to the PHP forms, as well as write some
|
||||
JavaScript to ensure the values from those fields make it into the content
|
||||
elements (and back out in case an existing element gets edited).
|
||||
There's lots of extension points in the `[api:HtmlEditorField_Toolbar]` class
|
||||
There's lots of extension points in the [api:HtmlEditorField_Toolbar] class
|
||||
to get you started.
|
||||
|
||||
### Security groups with their own editor configuration
|
||||
|
||||
Different groups of authors can be assigned their own config,
|
||||
e.g. a more restricted rule set for content reviewers (see the "Security" )
|
||||
The config is available on each user record through `[api:Member->getHtmlEditorConfigForCMS()]`.
|
||||
The group assignment is done through the "Security" interface for each `[api:Group]` record.
|
||||
The config is available on each user record through [api:Member::getHtmlEditorConfigForCMS()].
|
||||
The group assignment is done through the "Security" interface for each [api:Group] record.
|
||||
Note: The dropdown is only available if more than one config exists.
|
||||
|
||||
### Using the editor outside of the CMS
|
||||
|
||||
Each interface can have multiple fields of this type, each with their own toolbar to set formatting
|
||||
and insert HTML elements. They do share one common set of dialogs for inserting links and other media though,
|
||||
encapsulated in the `[api:HtmlEditorField_Toolbar]` class.
|
||||
encapsulated in the [api:HtmlEditorField_Toolbar] class.
|
||||
In the CMS, those dialogs are automatically instantiate, but in your own interfaces outside
|
||||
of the CMS you have to take care of instantiate yourself:
|
||||
|
||||
@ -327,7 +327,7 @@ WYSIWYG editors are complex beasts, so replacing it completely is a difficult ta
|
||||
The framework provides a wrapper implementation for the basic required functionality,
|
||||
mainly around selecting and inserting content into the editor view.
|
||||
Have a look in `HtmlEditorField.js` and the `ss.editorWrapper` object to get you started
|
||||
on your own editor wrapper. Note that the `[api:HtmlEditorConfig]` is currently hardwired to support TinyMCE,
|
||||
on your own editor wrapper. Note that the [api:HtmlEditorConfig] is currently hardwired to support TinyMCE,
|
||||
so its up to you to either convert existing configuration as applicable,
|
||||
or start your own configuration.
|
||||
|
||||
|
@ -233,7 +233,7 @@ database.
|
||||
|
||||
The `GridFieldDetailForm` component drives the record viewing and editing form. It takes its' fields from
|
||||
`DataObject->getCMSFields()` method but can be customized to accept different fields via the
|
||||
[api:GridFieldDetailForm->setFields] method.
|
||||
[api:GridFieldDetailForm::setFields()] method.
|
||||
|
||||
:::php
|
||||
$form = $gridField->getConfig()->getComponentByType('GridFieldDetailForm');
|
||||
@ -351,7 +351,7 @@ processed placeholders as opposed to native template syntax.
|
||||
</div>
|
||||
|
||||
Now you can add other components into this area by returning them as an array from your
|
||||
[api:GridFieldComponent->getHTMLFragments()] implementation:
|
||||
[api:GridFieldComponent::getHTMLFragments()] implementation:
|
||||
|
||||
:::php
|
||||
<?php
|
||||
|
@ -321,7 +321,7 @@ The `Upload_Validator` class has configuration options for setting the `default_
|
||||
You can specify the file extension or the app category (as specified in the `File` class) in square brackets. It supports setting the file size in bytes or using the syntax supported by `File::ini2bytes()`.
|
||||
|
||||
|
||||
You can also configure the underlying `[api:Upload]` class, by using the YAML config system.
|
||||
You can also configure the underlying [api:Upload] class, by using the YAML config system.
|
||||
|
||||
```yaml
|
||||
Upload:
|
||||
|
@ -34,14 +34,14 @@ There's quite a bit in this function, so we'll step through one piece at a time.
|
||||
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 `[api: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 [api:FormField] page.
|
||||
|
||||
:::php
|
||||
$actions = FieldList(
|
||||
new FormAction('submit', 'Submit')
|
||||
);
|
||||
|
||||
We then create a `[api: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 [api: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
|
||||
return new Form($this, 'Form', $fields, $actions);
|
||||
@ -104,7 +104,7 @@ The final thing we do is return a 'thank you for your feedback' message to the u
|
||||
|
||||
All forms have some basic validation built in – email fields will only let the user enter email addresses, number fields will only accept numbers, and so on. Sometimes you need more complicated validation, so you can define your own validation by extending the Validator class.
|
||||
|
||||
The framework comes with a predefined validator called `[api:RequiredFields]`, which performs the common task of making sure particular fields are filled out. Below is the code to add validation to a contact form:
|
||||
The framework comes with a predefined validator called [api:RequiredFields], which performs the common task of making sure particular fields are filled out. Below is the code to add validation to a contact form:
|
||||
|
||||
:::php
|
||||
public function Form() {
|
||||
|
@ -46,7 +46,7 @@ be marked `private static` and follow the `lower_case_with_underscores` structur
|
||||
|
||||
## Accessing and Setting Configuration Properties
|
||||
|
||||
This can be done by calling the static method `[api:Config::inst]`, like so:
|
||||
This can be done by calling the static method [api:Config::inst()], like so:
|
||||
|
||||
:::php
|
||||
$config = Config::inst()->get('MyClass');
|
||||
@ -314,4 +314,4 @@ will result in only the latter coming through.
|
||||
|
||||
## API Documentation
|
||||
|
||||
* [api:Config]
|
||||
* [api:Config]
|
||||
|
@ -69,4 +69,4 @@ provide the users a place to configure settings then the `SiteConfig` panel is t
|
||||
|
||||
## API Documentation
|
||||
|
||||
* `[api:SiteConfig]`
|
||||
* [api:SiteConfig]
|
||||
|
@ -114,7 +114,7 @@ we added a `SayHi` method which is unique to our extension.
|
||||
|
||||
If the `Extension` needs to modify an existing method it's a little trickier. It requires that the method you want to
|
||||
customize has provided an *Extension Hook* in the place where you want to modify the data. An *Extension Hook* is done
|
||||
through the `[api:Object->extend]` method.
|
||||
through the [api:Object::extend()] method.
|
||||
|
||||
**framework/security/Member.php**
|
||||
|
||||
@ -209,8 +209,8 @@ In your [api:Extension] class you can only refer to the source object through th
|
||||
|
||||
## Checking to see if an Object has an Extension
|
||||
|
||||
To see what extensions are currently enabled on an object, use [api:Object->getExtensionInstances] and
|
||||
[api:Object->hasExtension]
|
||||
To see what extensions are currently enabled on an object, use [api:Object::getExtensionInstances()] and
|
||||
[api:Object::hasExtension()]
|
||||
|
||||
|
||||
:::php
|
||||
|
@ -33,7 +33,7 @@ Here's some syntax variations:
|
||||
|
||||
Shortcodes are automatically parsed on any database field which is declared as [api:HTMLValue] or [api:HTMLText],
|
||||
when rendered into a template. This means you can use shortcodes on common fields like `SiteTree.Content`, and any
|
||||
other `[api:DataObject::$db]` definitions of these types.
|
||||
other [api:DataObject::$db] definitions of these types.
|
||||
|
||||
Other fields can be manually parsed with shortcodes through the `parse` method.
|
||||
|
||||
|
@ -16,7 +16,7 @@ Some of the goals of dependency injection are:
|
||||
* Improve testability of code
|
||||
* Promoting abstraction of logic
|
||||
|
||||
The following sums up the simplest usage of the `Injector` it creates a new object of type `ClassName` through `create`
|
||||
The following sums up the simplest usage of the `Injector` it creates a new object of type `MyClassName` through `create`
|
||||
|
||||
:::php
|
||||
$object = Injector::inst()->create('MyClassName');
|
||||
@ -57,7 +57,7 @@ object instance as the first call.
|
||||
|
||||
## Dependencies
|
||||
|
||||
The `Injector` API can be used to define the types of `$dependancies` that an object requires.
|
||||
The `Injector` API can be used to define the types of `$dependencies` that an object requires.
|
||||
|
||||
:::php
|
||||
<?php
|
||||
@ -88,7 +88,7 @@ When creating a new instance of `MyController` the dependencies on that class wi
|
||||
echo (is_string($object->textProperty));
|
||||
// returns true;
|
||||
|
||||
The [Configuration YAML](../configuration) does the hard work of configuring those `$dependancies` for us.
|
||||
The [Configuration YAML](../configuration) does the hard work of configuring those `$dependencies` for us.
|
||||
|
||||
**mysite/_config/app.yml**
|
||||
|
||||
|
@ -96,7 +96,7 @@ environments. If for some reason you don't have access to the command line, you
|
||||
|
||||
### Via the CLI
|
||||
|
||||
The [sake](../cli) executable that comes with SilverStripe can trigger a customized `[api:TestRunner]` class that
|
||||
The [sake](../cli) executable that comes with SilverStripe can trigger a customized [api:TestRunner] class that
|
||||
handles the PHPUnit configuration and output formatting. While the custom test runner a handy tool, it's also more
|
||||
limited than using `phpunit` directly, particularly around formatting test output.
|
||||
|
||||
@ -133,7 +133,7 @@ Unless executing a coverage report there is no need to have xDebug enabled.
|
||||
sudo php5enmod xdebug
|
||||
|
||||
### Use SQLite In Memory
|
||||
SQLIte can be configured to fun in memory as opposed to disk and this makes testing an order of magnitude faster. To effect this change add the following to mysite/_config.php - this enables an optional flag to switch between MySQL and SQLite. Note also that the package silverstripe/sqlite3 will need installed, version will vary depending on which version of SilverStripe is being tested.
|
||||
SQLIte can be configured to run in memory as opposed to disk and this makes testing an order of magnitude faster. To effect this change add the following to mysite/_config.php - this enables an optional flag to switch between MySQL and SQLite. Note also that the package silverstripe/sqlite3 will need installed, version will vary depending on which version of SilverStripe is being tested.
|
||||
|
||||
:::php
|
||||
if(Director::isDev()) {
|
||||
|
@ -6,7 +6,7 @@ summary: Populate test databases with fake seed data.
|
||||
To test functionality correctly, we must use consistent data. If we are testing our code with the same data each
|
||||
time, we can trust our tests to yield reliable results and to identify when the logic changes. Each test run in
|
||||
SilverStripe starts with a fresh database containing no records. `Fixtures` provide a way to describe the initial data
|
||||
to load into the database. The `[api:SapphireTest]` class takes care of populating a test database with data from
|
||||
to load into the database. The [api:SapphireTest] class takes care of populating a test database with data from
|
||||
fixtures - all we have to do is define them.
|
||||
|
||||
Fixtures are defined in `YAML`. `YAML` is a markup language which is deliberately simple and easy to read, so it is
|
||||
@ -192,7 +192,7 @@ To provide the value for the `many_many_extraField` use the YAML list syntax.
|
||||
|
||||
While manually defined fixtures provide full flexibility, they offer very little in terms of structure and convention.
|
||||
|
||||
Alternatively, you can use the `[api:FixtureFactory]` class, which allows you to set default values, callbacks on object
|
||||
Alternatively, you can use the [api:FixtureFactory] class, which allows you to set default values, callbacks on object
|
||||
creation, and dynamic/lazy value setting.
|
||||
|
||||
<div class="hint" markdown='1'>
|
||||
|
@ -60,7 +60,7 @@ The test database is rebuilt every time one of these methods is run.
|
||||
|
||||
Inside our test method is the `objFromFixture` method that will generate an object for us based on data from our fixture
|
||||
file. To identify to the object, we provide a class name and an identifier. The identifier is specified in the YAML file
|
||||
but not saved in the database anywhere, `objFromFixture` looks the `[api:DataObject]` up in memory rather than using the
|
||||
but not saved in the database anywhere, `objFromFixture` looks the [api:DataObject] up in memory rather than using the
|
||||
database. This means that you can use it to test the functions responsible for looking up content in the database.
|
||||
|
||||
The final part of our test is an assertion command, `assertEquals`. An assertion command allows us to test for something
|
||||
|
@ -2,8 +2,8 @@ title: How to test emails within unit tests
|
||||
|
||||
# Testing Email within Unit Tests
|
||||
|
||||
SilverStripe's test system has built-in support for testing emails sent using the `[api:Email]` class. If you are
|
||||
running a `[api:SapphireTest]` test, then it holds off actually sending the email, and instead lets you assert that an
|
||||
SilverStripe's test system has built-in support for testing emails sent using the [api:Email] class. If you are
|
||||
running a [api:SapphireTest] test, then it holds off actually sending the email, and instead lets you assert that an
|
||||
email was sent using this method.
|
||||
|
||||
:::php
|
||||
@ -15,7 +15,7 @@ email was sent using this method.
|
||||
$e->send();
|
||||
}
|
||||
|
||||
To test that `MyMethod` sends the correct email, use the [api:SapphireTest::assertEmailSent] method.
|
||||
To test that `MyMethod` sends the correct email, use the [api:SapphireTest::assertEmailSent()] method.
|
||||
|
||||
:::php
|
||||
$this->assertEmailSent($to, $from, $subject, $body);
|
||||
|
@ -55,7 +55,7 @@ should have the appropriate permissions to create new databases on your server,
|
||||
|
||||
## Writing Tests
|
||||
|
||||
Tests are written by creating subclasses of `[api:SapphireTest]`. You should put tests for your site in the
|
||||
Tests are written by creating subclasses of [api:SapphireTest]. You should put tests for your site in the
|
||||
`mysite/tests` directory. If you are writing tests for a module, put them in the `(modulename)/tests` directory.
|
||||
|
||||
Generally speaking, there should be one test class for each application class. The name of the test class should be the
|
||||
@ -100,7 +100,7 @@ All command-line arguments are documented on
|
||||
### Via the "sake" Wrapper on Command Line
|
||||
|
||||
The [sake](/developer_guides/cli/) executable that comes with SilverStripe can trigger a customized
|
||||
`[api:TestRunner]` class that handles the PHPUnit configuration and output formatting.
|
||||
[api:TestRunner] class that handles the PHPUnit configuration and output formatting.
|
||||
While the custom test runner a handy tool, its also more limited than using `phpunit` directly,
|
||||
particularly around formatting test output.
|
||||
|
||||
|
@ -36,7 +36,7 @@ on a public server very carefully.
|
||||
|
||||
Test mode is designed for staging environments or other private collaboration sites before deploying a site live.
|
||||
|
||||
In this mode error messages are hidden from the user and SilverStripe includes `[api:BasicAuth]` integration if you
|
||||
In this mode error messages are hidden from the user and SilverStripe includes [api:BasicAuth] integration if you
|
||||
want to password protect the site. You can enable that but adding this to your `mysite/_config/app.yml` file:
|
||||
|
||||
:::yml
|
||||
|
@ -27,7 +27,7 @@ Append the option and corresponding value to your URL in your browser's address
|
||||
| isDev | | 1 | | Put the site into [development mode](../), enabling debugging messages to the browser on a live server. For security, you'll be asked to log in with an administrator log-in. Will persist for the current browser session. |
|
||||
| isTest | | 1 | | See above. |
|
||||
| debug | | 1 | | Show a collection of debugging information about the director / controller operation |
|
||||
| debug_request | | 1 | | Show all steps of the request from initial `[api:HTTPRequest]` to `[api:Controller]` to Template Rendering |
|
||||
| debug_request | | 1 | | Show all steps of the request from initial [api:HTTPRequest] to [api:Controller] to Template Rendering |
|
||||
|
||||
## Classes and Objects
|
||||
|
||||
|
@ -35,7 +35,7 @@ Here are some more complex examples:
|
||||
<% end_cached %>
|
||||
|
||||
An additional global key is incorporated in the cache lookup. The default value for this is
|
||||
`$CurrentReadingMode, $CurrentUser.ID`. This ensures that the current `[api:Versioned]` state and user ID are used.
|
||||
`$CurrentReadingMode, $CurrentUser.ID`. This ensures that the current [api:Versioned] state and user ID are used.
|
||||
This may be configured by changing the config value of `SSViewer.global_key`. It is also necessary to flush the
|
||||
template caching when modifying this config, as this key is cached within the template itself.
|
||||
|
||||
|
@ -4,14 +4,14 @@
|
||||
|
||||
The framework uses caches to store infrequently changing values.
|
||||
By default, the storage mechanism is simply the filesystem, although
|
||||
other cache backends can be configured. All caches use the `[api:SS_Cache]` API.
|
||||
other cache backends can be configured. All caches use the [api:SS_Cache] API.
|
||||
|
||||
The most common caches are manifests of various resources:
|
||||
|
||||
* PHP class locations (`[api:SS_ClassManifest]`)
|
||||
* Template file locations and compiled templates (`[api:SS_TemplateManifest]`)
|
||||
* Configuration settings from YAML files (`[api:SS_ConfigManifest]`)
|
||||
* Language files (`[api:i18n]`)
|
||||
* PHP class locations ([api:SS_ClassManifest])
|
||||
* Template file locations and compiled templates ([api:SS_TemplateManifest])
|
||||
* Configuration settings from YAML files ([api:SS_ConfigManifest])
|
||||
* Language files ([api:i18n])
|
||||
|
||||
Flushing the various manifests is performed through a GET
|
||||
parameter (`flush=1`). Since this action requires more server resources than normal requests,
|
||||
@ -23,7 +23,7 @@ executing the action is limited to the following cases when performed via a web
|
||||
|
||||
## The Cache API
|
||||
|
||||
The `[api:SS_Cache]` class provides a bunch of static functions wrapping the Zend_Cache system
|
||||
The [api:SS_Cache] class provides a bunch of static functions wrapping the Zend_Cache system
|
||||
in something a little more easy to use with the SilverStripe config system.
|
||||
|
||||
A `Zend_Cache` has both a frontend (determines how to get the value to cache,
|
||||
|
@ -4,11 +4,11 @@ title: Members
|
||||
|
||||
## Introduction
|
||||
|
||||
The `[api:Member]` class is used to represent user accounts on a SilverStripe site (including newsletter recipients).
|
||||
The [api:Member] class is used to represent user accounts on a SilverStripe site (including newsletter recipients).
|
||||
|
||||
## Testing For Logged In Users
|
||||
|
||||
The `[api:Member]` class comes with 2 static methods for getting information about the current logged in user.
|
||||
The [api:Member] class comes with 2 static methods for getting information about the current logged in user.
|
||||
|
||||
**Member::currentUserID()**
|
||||
|
||||
@ -39,11 +39,11 @@ Returns the full *Member* Object for the current user, returns *null* if user is
|
||||
## Subclassing
|
||||
|
||||
<div class="warning" markdown="1">
|
||||
This is the least desirable way of extending the `[api:Member]` class. It's better to use `[api:DataExtension]`
|
||||
This is the least desirable way of extending the [api:Member] class. It's better to use [api:DataExtension]
|
||||
(see below).
|
||||
</div>
|
||||
|
||||
You can define subclasses of `[api:Member]` to add extra fields or functionality to the built-in membership system.
|
||||
You can define subclasses of [api:Member] to add extra fields or functionality to the built-in membership system.
|
||||
|
||||
:::php
|
||||
class MyMember extends Member {
|
||||
@ -54,7 +54,7 @@ You can define subclasses of `[api:Member]` to add extra fields or functionality
|
||||
}
|
||||
|
||||
|
||||
To ensure that all new members are created using this class, put a call to `[api:Object::useCustomClass()]` in
|
||||
To ensure that all new members are created using this class, put a call to [api:Object::useCustomClass()] in
|
||||
(project)/_config.php:
|
||||
|
||||
:::php
|
||||
@ -65,8 +65,8 @@ Note that if you want to look this class-name up, you can call Object::getCustom
|
||||
## Overloading getCMSFields()
|
||||
|
||||
If you overload the built-in public function getCMSFields(), then you can change the form that is used to view & edit member
|
||||
details in the newsletter system. This function returns a `[api:FieldList]` object. You should generally start by calling
|
||||
parent::getCMSFields() and manipulate the `[api:FieldList]` from there.
|
||||
details in the newsletter system. This function returns a [api:FieldList] object. You should generally start by calling
|
||||
parent::getCMSFields() and manipulate the [api:FieldList] from there.
|
||||
|
||||
:::php
|
||||
public function getCMSFields() {
|
||||
@ -80,11 +80,11 @@ parent::getCMSFields() and manipulate the `[api:FieldList]` from there.
|
||||
|
||||
## Extending Member or DataObject?
|
||||
|
||||
Basic rule: Class `[api:Member]` should just be extended for entities who have some kind of login.
|
||||
If you have different types of `[api:Member]`s in the system, you have to make sure that those with login-capabilities have
|
||||
Basic rule: Class [api:Member] should just be extended for entities who have some kind of login.
|
||||
If you have different types of [api:Member]s in the system, you have to make sure that those with login-capabilities have
|
||||
unique email-addresses (as this is used for login-credentials).
|
||||
For persons without login-capabilities (e.g. for an address-database), you shouldn't extend `[api:Member]` to avoid conflicts
|
||||
with the Member-database. This enables us to have a different subclass of `[api:Member]` for an email-address with login-data,
|
||||
For persons without login-capabilities (e.g. for an address-database), you shouldn't extend [api:Member] to avoid conflicts
|
||||
with the Member-database. This enables us to have a different subclass of [api:Member] for an email-address with login-data,
|
||||
and another subclass for the same email-address in the address-database.
|
||||
|
||||
## Member Role Extension
|
||||
@ -98,9 +98,9 @@ class. A better way is to use role extensions to add this behaviour. Add the fol
|
||||
extensions:
|
||||
- MyMemberExtension
|
||||
|
||||
A role extension is simply a subclass of `[api:DataExtension]` that is designed to be used to add behaviour to `[api:Member]`.
|
||||
A role extension is simply a subclass of [api:DataExtension] that is designed to be used to add behaviour to [api:Member].
|
||||
The roles affect the entire class - all members will get the additional behaviour. However, if you want to restrict
|
||||
things, you should add appropriate `[api:Permission::checkMember()]` calls to the role's methods.
|
||||
things, you should add appropriate [api:Permission::checkMember()] calls to the role's methods.
|
||||
|
||||
:::php
|
||||
class MyMemberExtension extends DataExtension {
|
||||
@ -130,4 +130,4 @@ things, you should add appropriate `[api:Permission::checkMember()]` calls to th
|
||||
|
||||
## API Documentation
|
||||
|
||||
`[api:Member]`
|
||||
[api:Member]
|
||||
|
@ -35,7 +35,7 @@ privileges from its parent group.
|
||||
|
||||
## Permission checking is at class level
|
||||
|
||||
SilverStripe provides a security mechanism via the *Permission::check* method (see `[api:LeftAndMain]` for examples on how
|
||||
SilverStripe provides a security mechanism via the *Permission::check* method (see [api:LeftAndMain] for examples on how
|
||||
the admin screens work).
|
||||
|
||||
(next step -- go from *Permission::checkMember*...)
|
||||
@ -48,14 +48,14 @@ works.
|
||||
|
||||
### Loading the admin page: looking at security
|
||||
|
||||
If you go to [your site]/admin *Director.php* maps the 'admin' URL request through a `[api:Director]` rule to the
|
||||
`[api:CMSMain]` controller (see `[api:CMSMain]`, with no arguments).
|
||||
If you go to [your site]/admin *Director.php* maps the 'admin' URL request through a [api:Director] rule to the
|
||||
[api:CMSMain] controller (see [api:CMSMain], with no arguments).
|
||||
|
||||
*CMSMain.init()* calls its parent which, of all things is called `[api:LeftAndMain]`. It's in `[api:LeftAndMain]` that the
|
||||
*CMSMain.init()* calls its parent which, of all things is called [api:LeftAndMain]. It's in [api:LeftAndMain] that the
|
||||
important security checks are made by calling *Permission::check*.
|
||||
|
||||
`[api:Security::permissionFailure]` is the next utility function you can use to redirect to the login form.
|
||||
[api:Security::permissionFailure] is the next utility function you can use to redirect to the login form.
|
||||
|
||||
### Customizing Access Checks in CMS Classes
|
||||
|
||||
see `[api:LeftAndMain]`
|
||||
see [api:LeftAndMain]
|
||||
|
@ -19,7 +19,7 @@ The simple usage, Permission::check("PERM_CODE") will detect if the currently lo
|
||||
|
||||
## PermissionProvider
|
||||
|
||||
`[api:PermissionProvider]` is an interface which lets you define a method *providePermissions()*.
|
||||
[api:PermissionProvider] is an interface which lets you define a method *providePermissions()*.
|
||||
This method should return a map of permission code names with a human readable explanation of its purpose.
|
||||
|
||||
:::php
|
||||
@ -98,4 +98,4 @@ Internally, this checks that the user has any of the defined `CMS_ACCESS_*` perm
|
||||
|
||||
|
||||
## API Documentation
|
||||
`[api:Permission]`
|
||||
[api:Permission]
|
||||
|
@ -3,33 +3,33 @@ summary: Explains SilverStripe's Authentication options and custom authenticator
|
||||
|
||||
# Authentication
|
||||
|
||||
By default, SilverStripe provides a `[api:MemberAuthenticator]` class which hooks into its own internal
|
||||
By default, SilverStripe provides a [api:MemberAuthenticator] class which hooks into its own internal
|
||||
authentication system.
|
||||
|
||||
The main login system uses these controllers to handle the various security requests:
|
||||
|
||||
`[api:Security]` - Which is the controller which handles most front-end security requests, including logging in, logging out, resetting password, or changing password. This class also provides an interface to allow configured `[api:Authenticator]` classes to each display a custom login form.
|
||||
[api:Security] - Which is the controller which handles most front-end security requests, including logging in, logging out, resetting password, or changing password. This class also provides an interface to allow configured [api:Authenticator] classes to each display a custom login form.
|
||||
|
||||
`[api:CMSSecurity]` - Which is the controller which handles security requests within the CMS, and allows users to re-login without leaving the CMS.
|
||||
[api:CMSSecurity] - Which is the controller which handles security requests within the CMS, and allows users to re-login without leaving the CMS.
|
||||
|
||||
## Member Authentication
|
||||
|
||||
The default member authentication system is implemented in the following classes:
|
||||
|
||||
`[api:MemberAuthenticator]` - Which is the default member authentication implementation. This uses the email and password stored internally for each member to authenticate them.
|
||||
[api:MemberAuthenticator] - Which is the default member authentication implementation. This uses the email and password stored internally for each member to authenticate them.
|
||||
|
||||
`[api:MemberLoginForm]` - Is the default form used by `MemberAuthenticator`, and is displayed on the public site at the url `Security/login` by default.
|
||||
[api:MemberLoginForm] - Is the default form used by `MemberAuthenticator`, and is displayed on the public site at the url `Security/login` by default.
|
||||
|
||||
`[api:CMSMemberLoginForm]` - Is the secondary form used by `MemberAuthenticator`, and will be displayed to the user within the CMS any time their session expires or they are logged out via an action. This form is presented via a popup dialog, and can be used to re-authenticate that user automatically without them having to lose their workspace. E.g. if editing a form, the user can login and continue to publish their content.
|
||||
[api:CMSMemberLoginForm] - Is the secondary form used by `MemberAuthenticator`, and will be displayed to the user within the CMS any time their session expires or they are logged out via an action. This form is presented via a popup dialog, and can be used to re-authenticate that user automatically without them having to lose their workspace. E.g. if editing a form, the user can login and continue to publish their content.
|
||||
|
||||
## Custom Authentication
|
||||
|
||||
Additional authentication methods (oauth, etc) can be implemented by creating custom implementations of each of the
|
||||
following base classes:
|
||||
|
||||
`[api:Authenticator]` - The base class for authentication systems. This class also acts as the factory to generate various login forms for parts of the system. If an authenticator supports in-cms reauthentication then it will be necessary to override the `supports_cms` and `get_cms_login_form` methods.
|
||||
[api:Authenticator] - The base class for authentication systems. This class also acts as the factory to generate various login forms for parts of the system. If an authenticator supports in-cms reauthentication then it will be necessary to override the `supports_cms` and `get_cms_login_form` methods.
|
||||
|
||||
`[api:LoginForm]` - which is the base class for a login form which links to a specific authenticator. At the very least, it will be necessary to implement a form class which provides a default login interface. If in-cms re-authentication is desired, then a specialised subclass of this method may be necessary. For example, this form could be extended to require confirmation of username as well as password.
|
||||
[api:LoginForm] - which is the base class for a login form which links to a specific authenticator. At the very least, it will be necessary to implement a form class which provides a default login interface. If in-cms re-authentication is desired, then a specialised subclass of this method may be necessary. For example, this form could be extended to require confirmation of username as well as password.
|
||||
|
||||
## Default Admin
|
||||
|
||||
@ -44,6 +44,6 @@ It is advisable to configure this user in your `_ss_environment.php` file outsid
|
||||
define('SS_DEFAULT_ADMIN_USERNAME', 'admin');
|
||||
define('SS_DEFAULT_ADMIN_PASSWORD', 'password');
|
||||
|
||||
When a user logs in with these credentials, then a `[api:Member]` with the Email 'admin' will be generated in
|
||||
When a user logs in with these credentials, then a [api: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
|
||||
updating the `_ss_environment.php` file.
|
||||
|
@ -58,10 +58,10 @@ Parameterised updates and inserts are also supported, but the syntax is a little
|
||||
|
||||
SilverStripe internally will use parameterised queries in SQL statements wherever possible.
|
||||
|
||||
If necessary Silverstripe performs any required escaping through database-specific methods (see `[api:Database->addslashes()]`).
|
||||
For `[api:MySQLDatabase]`, this will be `[mysql_real_escape_string()](http://de3.php.net/mysql_real_escape_string)`.
|
||||
If necessary Silverstripe performs any required escaping through database-specific methods (see [api:Database::addslashes()]).
|
||||
For [api:MySQLDatabase], this will be `[mysql_real_escape_string()](http://de3.php.net/mysql_real_escape_string)`.
|
||||
|
||||
* Most `[api:DataList]` accessors (see escaping note in method documentation)
|
||||
* Most [api:DataList] accessors (see escaping note in method documentation)
|
||||
* DataObject::get_by_id()
|
||||
* DataObject::update()
|
||||
* DataObject::castedUpdate()
|
||||
@ -213,8 +213,8 @@ We recommend configuring [shortcodes](/developer_guides/extending/shortcodes) th
|
||||
|
||||
### Escaping model properties
|
||||
|
||||
`[api:SSViewer]` (the SilverStripe template engine) automatically takes care of escaping HTML tags from specific
|
||||
object-properties by [casting](/developer_guides/model/data_types_and_casting) its string value into a `[api:DBField]` object.
|
||||
[api:SSViewer] (the SilverStripe template engine) automatically takes care of escaping HTML tags from specific
|
||||
object-properties by [casting](/developer_guides/model/data_types_and_casting) its string value into a [api:DBField] object.
|
||||
|
||||
PHP:
|
||||
|
||||
@ -297,7 +297,7 @@ presentation from business logic.
|
||||
When using *customise()* or *renderWith()* calls in your controller, or otherwise forcing a custom context for your
|
||||
template, you'll need to take care of casting and escaping yourself in PHP.
|
||||
|
||||
The `[api:Convert]` class has utilities for this, mainly *Convert::raw2xml()* and *Convert::raw2att()* (which is
|
||||
The [api:Convert] class has utilities for this, mainly *Convert::raw2xml()* and *Convert::raw2att()* (which is
|
||||
also used by *XML* and *ATT* in template code).
|
||||
|
||||
PHP:
|
||||
@ -372,14 +372,14 @@ SilverStripe has built-in countermeasures against [CSRF](http://shiflett.org/art
|
||||
will automatically contain a `SecurityID` parameter which is generated as a secure hash on the server, connected to the
|
||||
currently active session of the user. If this form is submitted without this parameter, or if the parameter doesn't
|
||||
match the hash stored in the users session, the request is discarded.
|
||||
You can disable this behaviour through `[api:Form->disableSecurityToken()]`.
|
||||
You can disable this behaviour through [api:Form::disableSecurityToken()].
|
||||
|
||||
It is also recommended to limit form submissions to the intended HTTP verb (mostly `GET` or `POST`)
|
||||
through `[api:Form->setStrictFormMethodCheck()]`.
|
||||
through [api:Form::setStrictFormMethodCheck()].
|
||||
|
||||
Sometimes you need to handle state-changing HTTP submissions which aren't handled through
|
||||
SilverStripe's form system. In this case, you can also check the current HTTP request
|
||||
for a valid token through `[api:SecurityToken::checkRequest()]`.
|
||||
for a valid token through [api:SecurityToken::checkRequest()].
|
||||
|
||||
## Casting user input
|
||||
|
||||
|
@ -26,7 +26,7 @@ Feature overview:
|
||||
|
||||
You can use the CsvBulkLoader without subclassing or other customizations, if the column names
|
||||
in your CSV file match `$db` properties in your dataobject. E.g. a simple import for the
|
||||
`[api:Member]` class could have this data in a file:
|
||||
[api:Member] class could have this data in a file:
|
||||
|
||||
FirstName,LastName,Email
|
||||
Donald,Duck,donald@disney.com
|
||||
@ -38,7 +38,7 @@ The loader would be triggered through the `load()` method:
|
||||
$loader = new CsvBulkLoader('Member');
|
||||
$result = $loader->load('<my-file-path>');
|
||||
|
||||
By the way, you can import `[api:Member]` and `[api:Group]` data through `http://localhost/admin/security`
|
||||
By the way, you can import [api:Member] and [api:Group] data through `http://localhost/admin/security`
|
||||
interface out of the box.
|
||||
|
||||
## Import through ModelAdmin
|
||||
@ -52,7 +52,7 @@ The simplest way to use [api:CsvBulkLoader] is through a [api:ModelAdmin] interf
|
||||
'Player'
|
||||
);
|
||||
private static $model_importers = array(
|
||||
'Player' => 'PlayerCsvBulkLoader',
|
||||
'Player' => 'CsvBulkLoader',
|
||||
);
|
||||
private static $url_segment = 'players';
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ summary: Consume external data through their RESTFul interfaces.
|
||||
|
||||
# Restful Service
|
||||
|
||||
`[api:RestfulService]` is used to enable connections to remote web services through PHP's `curl` command. It provides an
|
||||
[api:RestfulService] is used to enable connections to remote web services through PHP's `curl` command. It provides an
|
||||
interface and utility functions for generating a valid request and parsing the response returned from the web service.
|
||||
|
||||
<div class="alert" markdown="1">
|
||||
@ -134,7 +134,7 @@ To get the value of entry node with the namespace media, use:
|
||||
### Handling Errors
|
||||
|
||||
If the web service returned an error (for example, API key not available or inadequate parameters),
|
||||
`[api:RestfulService]` can delegate the error handling to it's descendant class. To handle the errors, subclass
|
||||
[api:RestfulService] can delegate the error handling to it's descendant class. To handle the errors, subclass
|
||||
`RestfulService and define a function called errorCatch.
|
||||
|
||||
:::php
|
||||
@ -220,4 +220,4 @@ $service->request('service.json', 'GET', null, null, $curlOptions);
|
||||
|
||||
## API Documentation
|
||||
|
||||
* `[api:RestfulService]`
|
||||
* [api:RestfulService]
|
||||
|
@ -3,15 +3,15 @@ summary: Output records from your database as an RSS Feed.
|
||||
|
||||
# RSS Feed
|
||||
|
||||
Generating RSS / Atom-feeds is a matter of rendering a `[api:SS_List]` instance through the `[api:RSSFeed]` class.
|
||||
Generating RSS / Atom-feeds is a matter of rendering a [api:SS_List] instance through the [api:RSSFeed] class.
|
||||
|
||||
The `[api:RSSFeed]` class doesn't limit you to generating article based feeds, it is just as easy to create a feed of
|
||||
your current staff members, comments or any other custom `[api:DataObject]` subclasses you have defined. The only
|
||||
The [api:RSSFeed] class doesn't limit you to generating article based feeds, it is just as easy to create a feed of
|
||||
your current staff members, comments or any other custom [api:DataObject] subclasses you have defined. The only
|
||||
logical limitation here is that every item in the RSS-feed should be accessible through a URL on your website, so it's
|
||||
advisable to just create feeds from subclasses of `[api:SiteTree]`.
|
||||
advisable to just create feeds from subclasses of [api:SiteTree].
|
||||
|
||||
<div class="warning" markdown="1">
|
||||
If you wish to generate an RSS feed that contains a `[api:DataObject]`, ensure you define a `AbsoluteLink` method on
|
||||
If you wish to generate an RSS feed that contains a [api:DataObject], ensure you define a `AbsoluteLink` method on
|
||||
the object.
|
||||
</div>
|
||||
|
||||
@ -47,7 +47,7 @@ will normally go in your `Controllers` `init` method.
|
||||
|
||||
### Showing the 10 most recently updated pages
|
||||
|
||||
You can use `[api:RSSFeed]` to easily create a feed showing your latest Page updates. The following example adds a page
|
||||
You can use [api:RSSFeed] to easily create a feed showing your latest Page updates. The following example adds a page
|
||||
`/home/rss/` which displays an XML file the latest updated pages.
|
||||
|
||||
**mysite/code/Page.php**
|
||||
@ -87,7 +87,7 @@ You can use `[api:RSSFeed]` to easily create a feed showing your latest Page upd
|
||||
|
||||
### Rendering DataObjects in a RSSFeed
|
||||
|
||||
DataObjects can be rendered in the feed as well, however, since they aren't explicitly `[api:SiteTree]` subclasses we
|
||||
DataObjects can be rendered in the feed as well, however, since they aren't explicitly [api:SiteTree] subclasses we
|
||||
need to include a function `AbsoluteLink` to allow the RSS feed to link through to the item.
|
||||
|
||||
<div class="info">
|
||||
@ -193,4 +193,4 @@ As we've added a new template (PlayersRss.ss) make sure you clear your SilverStr
|
||||
|
||||
## API Documentation
|
||||
|
||||
* `[api:RSSFeed]`
|
||||
* [api:RSSFeed]
|
||||
|
@ -2,7 +2,7 @@ title: Embed an RSS Feed
|
||||
|
||||
# Embed an RSS Feed
|
||||
|
||||
`[api:RestfulService]` can be used to easily embed an RSS feed from a site. In this How to we'll embed the latest
|
||||
[api:RestfulService] can be used to easily embed an RSS feed from a site. In this How to we'll embed the latest
|
||||
weather information from the Yahoo Weather API.
|
||||
|
||||
First, we write the code to query the API feed.
|
||||
@ -54,4 +54,4 @@ single field `Description`.
|
||||
## Related
|
||||
|
||||
* [RestfulService Documentation](../restfulservice)
|
||||
* `[api:RestfulService]`
|
||||
* [api:RestfulService]
|
||||
|
@ -204,7 +204,7 @@ Results.PaginationSummary(4) defines how many pages the search will show in the
|
||||
|
||||
## Available SearchFilters
|
||||
|
||||
See `[api:SearchFilter]` API Documentation
|
||||
See [api:SearchFilter] API Documentation
|
||||
|
||||
|
||||
## Related Documentation
|
||||
|
@ -41,8 +41,8 @@ default site search, have a look at those extensions and modify as required.
|
||||
|
||||
### Fulltext Filter
|
||||
|
||||
SilverStripe provides a `[api:FulltextFiler]` which you can use to perform custom fulltext searches on
|
||||
`[api:DataList]`'s.
|
||||
SilverStripe provides a [api:FulltextFilter] which you can use to perform custom fulltext searches on
|
||||
[api:DataList]s.
|
||||
|
||||
Example DataObject:
|
||||
|
||||
@ -78,4 +78,4 @@ field instead of the index.
|
||||
|
||||
## API Documentation
|
||||
|
||||
* [api:FulltextSearchable]
|
||||
* [api:FulltextSearchable]
|
||||
|
@ -24,7 +24,7 @@ The i18n class is enabled by default.
|
||||
|
||||
### Setting the locale
|
||||
|
||||
To set the locale you just need to call `[api:i18n::set_locale()]` passing, as a parameter, the name of the locale that
|
||||
To set the locale you just need to call [api:i18n::set_locale()] passing, as a parameter, the name of the locale that
|
||||
you want to set.
|
||||
|
||||
:::php
|
||||
@ -39,7 +39,7 @@ for a complete listing of available locales.
|
||||
|
||||
### Getting the locale
|
||||
|
||||
As you set the locale you can also get the current value, just by calling `[api:i18n::get_locale()]`.
|
||||
As you set the locale you can also get the current value, just by calling [api:i18n::get_locale()].
|
||||
|
||||
### Declaring the content language in HTML {#declaring_the_content_language_in_html}
|
||||
|
||||
@ -72,9 +72,9 @@ to write your own logic for any frontend output.
|
||||
Config::inst()->update('i18n', 'date_format', 'dd.MM.YYYY');
|
||||
Config::inst()->update('i18n', 'time_format', 'HH:mm');
|
||||
|
||||
Most localization routines in SilverStripe use the [Zend_Date API](http://framework.zend.com/manual/en/zend.date.html).
|
||||
Most localization routines in SilverStripe use the [Zend_Date API](http://framework.zend.com/manual/1.12/en/zend.date.overview.html).
|
||||
This means all formats are defined in
|
||||
[ISO date format](http://framework.zend.com/manual/en/zend.date.constants.html#zend.date.constants.selfdefinedformats),
|
||||
[ISO date format](http://framework.zend.com/manual/1.12/en/zend.date.constants.html),
|
||||
not PHP's built-in [date()](http://nz.php.net/manual/en/function.date.php).
|
||||
|
||||
### Language Names
|
||||
@ -272,7 +272,7 @@ There are a few special cases:
|
||||
## Language definitions
|
||||
|
||||
Each module can have one language table per locale, stored by convention in the `lang/` subfolder.
|
||||
The translation is powered by [Zend_Translate](http://framework.zend.com/manual/en/zend.translate.html),
|
||||
The translation is powered by [Zend_Translate](http://framework.zend.com/manual/current/en/modules/zend.i18n.translating.html),
|
||||
which supports different translation adapters, dealing with different storage formats.
|
||||
|
||||
By default, SilverStripe 3.x uses a YAML format (through the [Zend_Translate_RailsYAML adapter](https://github.com/chillu/zend_translate_railsyaml)).
|
||||
@ -411,4 +411,4 @@ The `ss.i18n` object contain a couple functions to help and replace dynamic vari
|
||||
* [Help to translate](../../contributing/translations) - Instructions for online collaboration to translate core
|
||||
* [Help to translate](../../contributing/translation_process) - Instructions for adding translation to your own modules
|
||||
* [http://www.i18nguy.com/](http://www.i18nguy.com/)
|
||||
* [balbus.tk i18n notes](http://www.balbus.tk/internationalize)
|
||||
* [balbus.tk i18n notes](http://www.balbuss.com/internationalize/)
|
||||
|
@ -37,4 +37,4 @@ You may also notice the 'Sync files' button (highlighted below). This button all
|
||||
|
||||
## Upload
|
||||
|
||||
Files can be managed through a `FileField` or an `UploadField`. The `[api:FileField]` class provides a simple HTML input with a type of "file", whereas an `[api:UploadField]` provides a much more feature-rich field (including AJAX-based uploads, previews, relationship management and file data management). See [`Reference - UploadField`](/developer_guides/forms/field_types/uploadfield) for more information about how to use the `UploadField` class.
|
||||
Files can be managed through a `FileField` or an `UploadField`. The [api:FileField] class provides a simple HTML input with a type of "file", whereas an [api:UploadField] provides a much more feature-rich field (including AJAX-based uploads, previews, relationship management and file data management). See [`Reference - UploadField`](/developer_guides/forms/field_types/uploadfield) for more information about how to use the `UploadField` class.
|
||||
|
@ -2,14 +2,14 @@ summary: Learn how to crop and resize images in templates and PHP code
|
||||
|
||||
# Image
|
||||
|
||||
Represents an image object through the `[api:Image]` class, inheriting all base functionality from the `[api:File]` class with extra functionality including resizing.
|
||||
Represents an image object through the [api:Image] class, inheriting all base functionality from the [api:File] class with extra functionality including resizing.
|
||||
|
||||
## Usage
|
||||
|
||||
### Managing images through form fields
|
||||
|
||||
Images can be uploaded like any other file, through `[api:FileField]`.
|
||||
More advanced usage is possible through `[api:UploadField]`,
|
||||
Images can be uploaded like any other file, through [api:FileField].
|
||||
More advanced usage is possible through [api:UploadField],
|
||||
which provides thumbnails, a detail view of the image properties,
|
||||
and management of relationships to other DataObject instances.
|
||||
Allows upload of images through limiting file extensions with `setAllowedExtensions()`.
|
||||
@ -70,7 +70,7 @@ The image manipulation functions can be used in your code with the same names, e
|
||||
|
||||
Some of the MetaData functions need to be prefixed with 'get', example `getHeight()`, `getOrientation()` etc.
|
||||
|
||||
Please refer to the `[api:Image]` API documentation for specific functions.
|
||||
Please refer to the [api:Image] API documentation for specific functions.
|
||||
|
||||
### Creating custom image functions
|
||||
|
||||
@ -117,8 +117,8 @@ You can also create your own functions by extending the image class, for example
|
||||
|
||||
### Form Upload
|
||||
|
||||
For usage on a website form, see `[api:FileField]`.
|
||||
If you want to upload images within the CMS, see `[api:UploadField]`.
|
||||
For usage on a website form, see [api:FileField].
|
||||
If you want to upload images within the CMS, see [api:UploadField].
|
||||
|
||||
### Image Quality
|
||||
|
||||
@ -160,4 +160,4 @@ disappeared, you can try manually flushing the image cache.
|
||||
http://localhost/dev/tasks/FlushGeneratedImagesTask
|
||||
|
||||
## API Documentation
|
||||
`[api:Image]`
|
||||
[api:Image]
|
||||
|
@ -124,7 +124,7 @@ searched results. Every [api:DataObject] can have its own context, based on the
|
||||
class makes a guess at how those fields should be searched, e.g. showing a checkbox for any boolean fields in your
|
||||
`$db` definition.
|
||||
|
||||
To remove, add or modify searchable fields, define a new `[api:DataObject::$searchable_fields]` static on your model
|
||||
To remove, add or modify searchable fields, define a new [api:DataObject::$searchable_fields] static on your model
|
||||
class (see [SearchContext](../search/searchcontext) docs for details).
|
||||
|
||||
**mysite/code/Product.php**
|
||||
@ -167,7 +167,7 @@ model class, where you can add or remove columns. To change the title, use [api:
|
||||
);
|
||||
}
|
||||
|
||||
The results list are retrieved from [api:SearchContext->getResults], based on the parameters passed through the search
|
||||
The results list are retrieved from [api:SearchContext::getResults()], based on the parameters passed through the search
|
||||
form. If no search parameters are given, the results will show every record. Results are a [api:DataList] instance, so
|
||||
can be customized by additional SQL filters, joins.
|
||||
|
||||
@ -225,7 +225,7 @@ checkbox which limits search results to expensive products (over $100).
|
||||
}
|
||||
}
|
||||
|
||||
To alter how the results are displayed (via `[api:GridField]`), you can also overload the `getEditForm()` method. For
|
||||
To alter how the results are displayed (via [api:GridField]), you can also overload the `getEditForm()` method. For
|
||||
example, to add a new component.
|
||||
|
||||
**mysite/code/MyAdmin.php**
|
||||
|
@ -3,8 +3,8 @@
|
||||
## Introduction
|
||||
|
||||
A lot can be achieved in SilverStripe by adding properties and form fields
|
||||
to your own page types (via `[api:SiteTree->getCMSFields()]`), as well as creating
|
||||
your own data management interfaces through `[api:ModelAdmin]`. But sometimes
|
||||
to your own page types (via [api:SiteTree::getCMSFields()]), as well as creating
|
||||
your own data management interfaces through [api:ModelAdmin]. But sometimes
|
||||
you'll want to go deeper and tailor the underlying interface to your needs as well.
|
||||
For example, to build a personalized CMS dashboard, or content "slots" where authors
|
||||
can drag their content into. At its core, SilverStripe is a web application
|
||||
@ -51,14 +51,14 @@ See our [system requirements](/getting_started/server_requirements) for a list o
|
||||
|
||||
## Templates and Controllers
|
||||
|
||||
The CMS backend is handled through the `[api:LeftAndMain]` controller class,
|
||||
The CMS backend is handled through the [api:LeftAndMain] controller class,
|
||||
which contains base functionality like displaying and saving a record.
|
||||
This is extended through various subclasses, e.g. to add a group hierarchy (`[api:SecurityAdmin]`),
|
||||
a search interface (`[api:ModelAdmin]`) or an "Add Page" form (`[api:CMSPageAddController]`).
|
||||
This is extended through various subclasses, e.g. to add a group hierarchy ([api:SecurityAdmin]),
|
||||
a search interface ([api:ModelAdmin]) or an "Add Page" form ([api:CMSPageAddController]).
|
||||
|
||||
The controller structure is too complex to document here, a good starting point
|
||||
for following the execution path in code are `[api:LeftAndMain->getRecord()]` and `[api:LeftAndMain->getEditForm()]`.
|
||||
If you have the `cms` module installed, have a look at `[api:CMSMain->getEditForm()]` for a good
|
||||
for following the execution path in code are [api:LeftAndMain::getRecord()] and [api:LeftAndMain::getEditForm()].
|
||||
If you have the `cms` module installed, have a look at [api:CMSMain::getEditForm()] for a good
|
||||
example on how to extend the base functionality (e.g. by adding page versioning hints to the form).
|
||||
|
||||
CMS templates are inherited based on their controllers, similar to subclasses of
|
||||
@ -72,13 +72,13 @@ which is in charge of rendering the main content area apart from the CMS menu.
|
||||
Depending on the complexity of your layout, you'll also need to overload the
|
||||
"EditForm" template (e.g. `MyCMSController_EditForm.ss`), e.g. to implement
|
||||
a tabbed form which only scrolls the main tab areas, while keeping the buttons at the bottom of the frame.
|
||||
This requires manual assignment of the template to your form instance, see `[api:CMSMain->getEditForm()]` for details.
|
||||
This requires manual assignment of the template to your form instance, see [api:CMSMain::getEditForm()] for details.
|
||||
|
||||
Often its useful to have a "tools" panel in between the menu and your content,
|
||||
usually occupied by a search form or navigational helper.
|
||||
In this case, you can either overload the full base template as described above.
|
||||
To avoid duplicating all this template code, you can also use the special `[api:LeftAndMain->Tools()]` and
|
||||
`[api:LeftAndMain->EditFormTools()]` methods available in `LeftAndMain`.
|
||||
To avoid duplicating all this template code, you can also use the special [api:LeftAndMain::Tools()] and
|
||||
[api:LeftAndMain::EditFormTools()] methods available in `LeftAndMain`.
|
||||
These placeholders are populated by auto-detected templates,
|
||||
with the naming convention of "<controller classname>_Tools.ss" and "<controller classname>_EditFormTools.ss".
|
||||
So to add or "subclass" a tools panel, simply create this file and it's automatically picked up.
|
||||
@ -94,7 +94,7 @@ Refer to [Layout reference](/developer_guides/customising_the_admin_interface/cm
|
||||
## Forms
|
||||
|
||||
SilverStripe constructs forms and its fields within PHP,
|
||||
mainly through the `[getCMSFields()](api:DataObject->getCMSFields())` method.
|
||||
mainly through the [getCMSFields()](api:DataObject::getCMSFields()) method.
|
||||
This in turn means that the CMS loads these forms as HTML via Ajax calls,
|
||||
e.g. after saving a record (which requires a form refresh), or switching the section in the CMS.
|
||||
|
||||
@ -165,10 +165,10 @@ any particular element.
|
||||
## JavaScript and CSS dependencies via Requirements and Ajax
|
||||
|
||||
The JavaScript logic powering the CMS is divided into many files,
|
||||
which typically are included via the `[api:Requirements]` class, by adding
|
||||
them to `[api:LeftAndMain->init()]` and its subclassed methods.
|
||||
which typically are included via the [api:Requirements] class, by adding
|
||||
them to [api:LeftAndMain::init()] and its subclassed methods.
|
||||
This class also takes care of minification and combination of the files,
|
||||
which is crucial for the CMS performance (see `[api:Requirements::combine_files()]`).
|
||||
which is crucial for the CMS performance (see [api:Requirements::combine_files()]).
|
||||
|
||||
Due to the procedural and selector-driven style of UI programming in jQuery.entwine,
|
||||
it can be difficult to find the piece of code responsible for a certain behaviour.
|
||||
@ -188,7 +188,7 @@ and [jQuery.delegate](http://api.jquery.com/delegate/), so takes care of dynamic
|
||||
Most interfaces will require their own JavaScript and CSS files, so the Ajax loading has
|
||||
to ensure they're loaded unless already present. A custom-built library called
|
||||
`jQuery.ondemand` (located in `framework/thirdparty`) takes care of this transparently -
|
||||
so as a developer just declare your dependencies through the `[api:Requirements]` API.
|
||||
so as a developer just declare your dependencies through the [api:Requirements] API.
|
||||
|
||||
## Ajax Loading and Browser History
|
||||
|
||||
@ -220,10 +220,10 @@ we often want to update these sections independently from their neighbouring con
|
||||
|
||||
In order for this to work, the CMS templates declare certain sections as "PJAX fragments"
|
||||
through a `data-pjax-fragment` attribute. These names correlate to specific
|
||||
rendering logic in the PHP controllers, through the `[api:PjaxResponseNegotiator]` class.
|
||||
rendering logic in the PHP controllers, through the [api:PjaxResponseNegotiator] class.
|
||||
|
||||
Through a custom `X-Pjax` HTTP header, the client can declare which view they're expecting,
|
||||
through identifiers like `CurrentForm` or `Content` (see `[api:LeftAndMain->getResponseNegotiator()]`).
|
||||
through identifiers like `CurrentForm` or `Content` (see [api:LeftAndMain::getResponseNegotiator()]).
|
||||
These identifiers are passed to `loadPanel()` via the `pjax` data option.
|
||||
The HTTP response is a JSON object literal, with template replacements keyed by their Pjax fragment.
|
||||
Through PHP callbacks, we ensure that only the required template parts are actually executed and rendered.
|
||||
@ -402,11 +402,11 @@ when using an input of type button, submit or reset, support is limited to plain
|
||||
|
||||
## Menu
|
||||
|
||||
The navigation menu in the CMS is created through the `[api:CMSMenu]` API,
|
||||
The navigation menu in the CMS is created through the [api:CMSMenu] API,
|
||||
which auto-detects all subclasses of `LeftAndMain`. This means that your custom
|
||||
`ModelAdmin` subclasses will already appear in there without any explicit definition.
|
||||
To modify existing menu entries or create new ones, see `[api:CMSMenu::add_menu_item()]`
|
||||
and `[api:CMSMenu::remove_menu_item()]`.
|
||||
To modify existing menu entries or create new ones, see [api:CMSMenu::add_menu_item()]
|
||||
and [api:CMSMenu::remove_menu_item()].
|
||||
|
||||
New content panels are typically loaded via Ajax, which might change
|
||||
the current menu context. For example, a link to edit a file might be clicked
|
||||
@ -422,7 +422,7 @@ which is picked up by the menu:
|
||||
return 'my response';
|
||||
}
|
||||
|
||||
This is usually handled by the existing `[api:LeftAndMain]` logic,
|
||||
This is usually handled by the existing [api:LeftAndMain] logic,
|
||||
so you don't need to worry about it. The same concept applies for
|
||||
'X-Title' (change the window title) and 'X-ControllerURL' (change the URL recorded in browser history).
|
||||
Note: You can see any additional HTTP headers through the web developer tools in your browser of choice.
|
||||
@ -437,13 +437,13 @@ For more information, see the [Howto: Customise the CMS tree](/developer_guides/
|
||||
|
||||
Note that a similar tree logic is also used for the
|
||||
form fields to select one or more entries from those hierarchies
|
||||
(`[api:TreeDropdownField]` and `[api:TreeMultiselectField]`).
|
||||
([api:TreeDropdownField] and [api:TreeMultiselectField]).
|
||||
|
||||
## Tabs
|
||||
|
||||
We're using [jQuery UI tabs](http://jqueryui.com/), but in a customised fashion.
|
||||
HTML with tabs can be created either directly through HTML templates in the CMS,
|
||||
or indirectly through a `[api:TabSet]` form field. Since tabsets are useable
|
||||
or indirectly through a [api:TabSet] form field. Since tabsets are useable
|
||||
outside of the CMS as well, the baseline application of tabs happens via
|
||||
a small wrapper around `jQuery.tabs()` stored in `TabSet.js`.
|
||||
|
@ -349,7 +349,7 @@ attributes, or the jQuery.metadata plugin). For returning status messages, pleas
|
||||
|
||||
Only return evaluated JavaScript snippets if unavoidable. Most of the time you can just pass data around, and let the
|
||||
clientside react to changes appropriately without telling it directly through JavaScript in AJAX responses. Don't use
|
||||
the `[api:Form]` SilverStripe class, which is built solely around
|
||||
the [api:Form] SilverStripe class, which is built solely around
|
||||
this inflexible concept.
|
||||
|
||||
Example: Autocomplete input field loading page matches through AJAX
|
||||
@ -420,7 +420,7 @@ JavaScript:
|
||||
|
||||
Although they are the minority of cases, there are times when a simple HTML fragment isn't enough. For example, if you
|
||||
have server side code that needs to trigger the update of a couple of elements in the CMS left-hand tree, it would be
|
||||
inefficient to send back the HTML of entire tree. SilverStripe can serialize to and from JSON (see the `[api:Convert]` class), and jQuery deals very well with it through
|
||||
inefficient to send back the HTML of entire tree. SilverStripe can serialize to and from JSON (see the [api:Convert] class), and jQuery deals very well with it through
|
||||
[jQuery.getJSON()](http://docs.jquery.com/Ajax/jQuery.getJSON#urldatacallback), as long as the HTTP content-type is
|
||||
properly set.
|
||||
|
||||
|
@ -2,10 +2,10 @@
|
||||
|
||||
## Adding an administration panel
|
||||
|
||||
Every time you add a new extension of the `[api:LeftAndMain]` class to the CMS,
|
||||
SilverStripe will automatically create a new `[api:CMSMenuItem]` for it
|
||||
Every time you add a new extension of the [api:LeftAndMain] class to the CMS,
|
||||
SilverStripe will automatically create a new [api:CMSMenuItem] for it
|
||||
|
||||
The most popular extension of LeftAndMain is a `[api:ModelAdmin]` class, so
|
||||
The most popular extension of LeftAndMain is a [api:ModelAdmin] class, so
|
||||
for a more detailed introduction to creating new `ModelAdmin` interfaces, read
|
||||
the [ModelAdmin reference](../modeladmin).
|
||||
|
||||
@ -51,7 +51,7 @@ On top of your administration windows, the menu can also have external links
|
||||
(e.g. to external reference). In this example, we're going to add a link to
|
||||
Google to the menu.
|
||||
|
||||
First, we need to define a `[api:LeftAndMainExtension]` which will contain our
|
||||
First, we need to define a [api:LeftAndMainExtension] which will contain our
|
||||
button configuration.
|
||||
|
||||
:::php
|
||||
|
@ -10,8 +10,8 @@ as well as sort and filter them in a way that would be hard to achieve in a tree
|
||||
But sometimes the default behaviour isn't powerful enough, and you want a more
|
||||
specific list view for certain page types, for example to sort the list by
|
||||
a different criteria, or add more columns to filter on. The resulting
|
||||
form is mainly based around a `[GridField](/reference/grid-field)` instance,
|
||||
which in turn includes all children in a `[DataList](/topics/datamodel)`.
|
||||
form is mainly based around a [GridField](/reference/grid-field) instance,
|
||||
which in turn includes all children in a [DataList](/topics/datamodel).
|
||||
You can use these two classes as a starting point for your customizations.
|
||||
|
||||
Here's a brief example on how to add sorting and a new column for a
|
||||
@ -69,4 +69,4 @@ Now you just need to enable the extension in your [configuration file](../../con
|
||||
extensions:
|
||||
- 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.
|
||||
|
@ -7,10 +7,10 @@ by the [jstree](http://jstree.com) library. It is configured through
|
||||
`framework/admin/javascript/LeftAndMain.Tree.js`, as well as some
|
||||
HTML5 metadata generated on its container (see the `data-hints` attribute).
|
||||
|
||||
The tree is rendered through `[api:LeftAndMain->getSiteTreeFor()]`,
|
||||
The tree is rendered through [api:LeftAndMain::getSiteTreeFor()],
|
||||
which recursively collects all nodes based on various filtering criteria.
|
||||
The node strictly just has to implement the `[api:Hierarchy]` extension,
|
||||
but in the CMS usually is a `[api:SiteTree]` object.
|
||||
The node strictly just has to implement the [api:Hierarchy] extension,
|
||||
but in the CMS usually is a [api:SiteTree] object.
|
||||
|
||||
## Add status lozenges to tree nodes
|
||||
|
||||
@ -43,13 +43,13 @@ code like this:
|
||||
By applying the proper style sheet, the snippet html above could produce the look of:
|
||||
![Page Node Screenshot](../../../_images/tree_node.png "Page Node")
|
||||
|
||||
SiteTree is a `[api:DataObject]` which is versioned by `[api:Versioned]` extension.
|
||||
SiteTree is a [api:DataObject] which is versioned by [api:Versioned] extension.
|
||||
Each node can optionally have publication status flags, e.g. "Removed from draft".
|
||||
Each flag has a unique identifier, which is also used as a CSS class for easier styling.
|
||||
|
||||
Developers can easily add a new flag, delete or alter an existing flag on how it is looked
|
||||
or changing the flag label. The customization of these lozenges could be done either through
|
||||
inherited subclass or `[api:DataExtension]`. It is just really about how we change the return
|
||||
inherited subclass or [api:DataExtension]. It is just really about how we change the return
|
||||
value of function `SiteTree->getTreeTitle()` by two easily extendable methods
|
||||
`SiteTree->getStatusClass()` and `SiteTree->getStatusFlags()`.
|
||||
|
||||
@ -74,7 +74,7 @@ __Example: using a subclass__
|
||||
}
|
||||
}
|
||||
|
||||
The above subclass of `[api:SiteTree]` will add a new flag for indicating its
|
||||
The above subclass of [api:SiteTree] will add a new flag for indicating its
|
||||
__'Scheduled To Publish'__ status. The look of the page node will be changed
|
||||
from ![Normal Page Node](../../../_images/page_node_normal.png) to ![Scheduled Page Node](../../../_images/page_node_scheduled.png). The getStatusFlags has an `updateStatusFlags()`
|
||||
extension point, so the flags can be modified through `DataExtension` rather than
|
||||
|
@ -71,4 +71,4 @@ More useful reports can be created by changing the `DataList` returned in the `s
|
||||
* More examples
|
||||
|
||||
## API documentation
|
||||
`[api:ReportAdmin]`
|
||||
[api:ReportAdmin]
|
||||
|
@ -1,7 +1,7 @@
|
||||
## Extending existing ModelAdmin
|
||||
|
||||
Sometimes you'll work with ModelAdmins from other modules. To customize these interfaces, you can always subclass. But there's
|
||||
also another tool at your disposal: The `[api:Extension]` API.
|
||||
also another tool at your disposal: The [api:Extension] API.
|
||||
|
||||
:::php
|
||||
class MyAdminExtension extends Extension {
|
||||
@ -19,4 +19,4 @@ Now enable this extension through your `[config.yml](/topics/configuration)` fil
|
||||
- MyAdminExtension
|
||||
|
||||
The following extension points are available: `updateEditForm()`, `updateSearchContext()`,
|
||||
`updateSearchForm()`, `updateList()`, `updateImportForm`.
|
||||
`updateSearchForm()`, `updateList()`, `updateImportForm`.
|
||||
|
@ -6,17 +6,17 @@ summary: Allows a class to define it's own flush functionality.
|
||||
## Introduction
|
||||
|
||||
Allows a class to define it's own flush functionality, which is triggered when `flush=1` is requested in the URL.
|
||||
`[api:FlushRequestFilter]` is run before a request is made, calling `flush()` statically on all
|
||||
implementors of `[api:Flushable]`.
|
||||
[api:FlushRequestFilter] is run before a request is made, calling `flush()` statically on all
|
||||
implementors of [api:Flushable].
|
||||
|
||||
## Usage
|
||||
|
||||
To use this API, you need to make your class implement `[api:Flushable]`, and define a `flush()` static function,
|
||||
To use this API, you need to make your class implement [api:Flushable], and define a `flush()` static function,
|
||||
this defines the actions that need to be executed on a flush request.
|
||||
|
||||
### Using with SS_Cache
|
||||
|
||||
This example uses `[api:SS_Cache]` in some custom code, and the same cache is cleaned on flush:
|
||||
This example uses [api:SS_Cache] in some custom code, and the same cache is cleaned on flush:
|
||||
|
||||
:::php
|
||||
<?php
|
||||
|
@ -26,15 +26,15 @@ You can write your own adapters by implementing the `ManifestCache` interface.
|
||||
## Traversing the Filesystem
|
||||
|
||||
Since manifests usually extract their information from files in the webroot,
|
||||
they require a powerful traversal tool: `[api:SS_FileFinder]`.
|
||||
they require a powerful traversal tool: [api:SS_FileFinder].
|
||||
The class provides filtering abilities for files and folders, as well as
|
||||
callbacks for recursive traversal. Each manifest has its own implementation,
|
||||
for example `[api:ManifestFileFinder]`, adding more domain specific filtering
|
||||
for example [api:ManifestFileFinder], adding more domain specific filtering
|
||||
like including themes, or excluding testss.
|
||||
|
||||
## PHP Class Manifest
|
||||
|
||||
The `[api:ClassManifest]` builds a manifest of all classes, interfaces and some
|
||||
The [api:ClassManifest] builds a manifest of all classes, interfaces and some
|
||||
additional items present in a directory, and caches it.
|
||||
|
||||
It finds the following information:
|
||||
@ -44,15 +44,15 @@ It finds the following information:
|
||||
* All implementors of an interface
|
||||
* All module configuration files
|
||||
|
||||
The gathered information can be accessed through `[api:SS_ClassLoader::instance()]`,
|
||||
as well as `[api:ClassInfo]`. Some useful commands of the `ClassInfo` API:
|
||||
The gathered information can be accessed through [api:SS_ClassLoader::instance()],
|
||||
as well as [api:ClassInfo]. Some useful commands of the `ClassInfo` API:
|
||||
|
||||
* `ClassInfo::subclassesFor($class)`: Returns a list of classes that inherit from the given class
|
||||
* `ClassInfo::ancestry($class)`: Returns the passed class name along with all its parent class names
|
||||
* `ClassInfo::implementorsOf($interfaceName)`: Returns all classes implementing the passed in interface
|
||||
|
||||
In the absence of a generic module API, it is also the primary way to identify
|
||||
which modules are installed, through `[api:ClassManifest::getModules()]`.
|
||||
which modules are installed, through [api:ClassManifest::getModules()].
|
||||
A module is defined as a toplevel folder in the webroot which contains
|
||||
either a `_config.php` file, or a `_config/` folder. Modules can be specifically
|
||||
excluded from manifests by creating a blank `_manifest_exclude` file in the module folder.
|
||||
@ -62,14 +62,14 @@ a `tests/` folder, unless tests are executed.
|
||||
|
||||
## Template Manifest
|
||||
|
||||
The `[api:SS_TemplateManifest]` class builds a manifest of all templates present in a directory,
|
||||
The [api:SS_TemplateManifest] class builds a manifest of all templates present in a directory,
|
||||
in both modules and themes. Templates in `tests/` folders are automatically excluded.
|
||||
The chapter on [template inheritance](../templates/template_inheritance) provides more details
|
||||
on its operation.
|
||||
|
||||
## Config Manifest
|
||||
|
||||
The `[api:SS_ConfigManifest]` loads builds a manifest of configuration items,
|
||||
The [api:SS_ConfigManifest] loads builds a manifest of configuration items,
|
||||
for both PHP and YAML. It also takes care of ordering and merging configuration fragments.
|
||||
The chapter on [configuration](../configuration) has more details.
|
||||
|
||||
@ -80,4 +80,4 @@ on any classes that implement the [Flushable](flushable) interface.
|
||||
This enables developers to clear [manifest caches](manifests),
|
||||
for example when adding new templates or PHP classes.
|
||||
Note that you need to be in [dev mode](/getting_started/environment_management)
|
||||
or logged-in as an administrator for flushing to take effect.
|
||||
or logged-in as an administrator for flushing to take effect.
|
||||
|
@ -93,7 +93,7 @@ All requests go through `framework/main.php`, which sets up the execution enviro
|
||||
* Optionally regenerates these manifests (if a ["flush" query parameter](flushable) is set)
|
||||
* Executes all procedural configuration defined through `_config.php` in all discovered modules
|
||||
* Loads the Composer PHP class autoloader
|
||||
* Hands control over to `[api:Director]`
|
||||
* Hands control over to [api:Director]
|
||||
|
||||
While you usually don't need to modify the bootstrap on this level, some deeper customizations like
|
||||
adding your own manifests or a performance-optimized routing might require it.
|
||||
@ -103,19 +103,19 @@ before handing control off to SilverStripe's own `main.php`.
|
||||
|
||||
## Routing and Request Handling
|
||||
|
||||
The `main.php` script relies on `[api:Director]` to work out which [controller](../controllers/) should handle this request. It parses the URL, matching it to one of a number of patterns,
|
||||
The `main.php` script relies on [api:Director] to work out which [controller](../controllers/) should handle this request. It parses the URL, matching it to one of a number of patterns,
|
||||
and determines the controller, action and any argument to be used ([Routing](../controllers/routing)).
|
||||
|
||||
* Creates a `[api:SS_HTTPRequest]` object containing all request and environment information
|
||||
* Creates a [api:SS_HTTPRequest] object containing all request and environment information
|
||||
* The [session](../cookies_and_sessions/sessions) holds an abstraction of PHP session
|
||||
* Instantiates a [controller](../controllers/) object
|
||||
* The `[api:Injector]` is first referenced, and asks the registered
|
||||
* The [api:Injector] is first referenced, and asks the registered
|
||||
[RequestFilter](../controllers/requestfilters)
|
||||
to pre-process the request object (see below)
|
||||
* The `Controller` executes the actual business logic and populates an `[api:SS_HTTPResponse]`
|
||||
* The `Controller` executes the actual business logic and populates an [api:SS_HTTPResponse]
|
||||
* The `Controller` can optionally hand off control to further nested controllers
|
||||
* The `Controller` optionally renders a response body through `SSViewer` [templates](../templates)
|
||||
* The `[api:RequestProcessor]` is called to post-process the request to allow
|
||||
* The [api:RequestProcessor] is called to post-process the request to allow
|
||||
further filtering before content is sent to the end user
|
||||
* The response is output to the client
|
||||
|
||||
|
40
docs/en/04_Changelogs/3.2.2.md
Normal file
40
docs/en/04_Changelogs/3.2.2.md
Normal file
@ -0,0 +1,40 @@
|
||||
# 3.2.2
|
||||
|
||||
<!--- Changes below this line will be automatically regenerated -->
|
||||
|
||||
## Change Log
|
||||
|
||||
### Security
|
||||
|
||||
* 2016-02-17 [faa94d5](https://github.com/silverstripe/silverstripe-framework/commit/faa94d51d570788dcebc2f2ef6e9de4d179ce1e4) Hostname, IP and Protocol Spoofing through HTTP Headers (Ingo Schommer) - See [ss-2016-003](http://www.silverstripe.org/download/security-releases/ss-2016-003)
|
||||
* 2016-02-17 [15d4db3](https://github.com/silverstripe/silverstripe-framework/commit/15d4db3b4a7dbc9a7e089f9329a396f8408ed7d9) Block unauthenticated access to dev/build/defaults (Damian Mooyman) - See [ss-2015-028](http://www.silverstripe.org/download/security-releases/ss-2015-028)
|
||||
* 2016-02-17 [e2c77c5](https://github.com/silverstripe/silverstripe-framework/commit/e2c77c5a8f13e901c51a3684210811559b592f0c) Ensure Gridfield actions respect CSRF (Damian Mooyman) - See [ss-2016-002](http://www.silverstripe.org/download/security-releases/ss-2016-002)
|
||||
* 2015-11-11 [245e0aa](https://github.com/silverstripe/silverstripe-framework/commit/245e0aae2f5f3eb0acba1d198ad8e196bb224462) Fix FormField error messages not being encoded safely (Damian Mooyman) - See [ss-2015-026](http://www.silverstripe.org/download/security-releases/ss-2015-026)
|
||||
* 2015-11-09 [53b3bc7](https://github.com/silverstripe/silverstripe-framework/commit/53b3bc707bcccb8f5e5060f85ab1398a0975bba2) Dont expose class on error (Hamish Friedlander) - See [ss-2015-025](http://www.silverstripe.org/download/security-releases/ss-2015-025)
|
||||
* 2015-11-01 [ac4342d](https://github.com/silverstripe/silverstripe-framework/commit/ac4342d81d19201bd8d3814f168240db1ac565fe) XML escape RSSFeed $link parameter (Ingo Schommer) - See [ss-2015-022](http://www.silverstripe.org/download/security-releases/ss-2015-022)
|
||||
* 2015-10-28 [97f21fd](https://github.com/silverstripe/silverstripe-framework/commit/97f21fddb3c565052f19ee3b35366f48e1e9a36f) Fix rewrite hash links XSS (Damian Mooyman) - See [ss-2015-021](http://www.silverstripe.org/download/security-releases/ss-2015-021)
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* 2016-02-15 [8771859](https://github.com/silverstripe/silverstripe-framework/commit/87718597e8f04872c285808d0666fbb69c5100ba) "where" method in SQLUpdate Example (Richard Rudy)
|
||||
* 2016-01-28 [3fcf1e2](https://github.com/silverstripe/silverstripe-framework/commit/3fcf1e2c98629dcd0048ff9447bad4cd30b4bf95) edge case on many many extra fields (fixes 4991) (Mark Stephens)
|
||||
* 2016-01-27 [3d0178e](https://github.com/silverstripe/silverstripe-cms/commit/3d0178ebc0b7408442ad2532f998ed47839e7117) Use correct formaction for doRollback exemption (Damian Mooyman)
|
||||
* 2016-01-24 [d8e354d](https://github.com/silverstripe/silverstripe-framework/commit/d8e354d144383fb6459adf92731853d2e54268d6) PHPDocs on DataList::getIDList() and UnsavedRelationList::getIDList() (Damian Mooyman)
|
||||
* 2016-01-22 [bf8bf5e](https://github.com/silverstripe/silverstripe-framework/commit/bf8bf5e4d558126bb99ea63881f1885faafddd3d) Prevent Versioned::doRollbackTo from creating incorrect versions on subclasses of Versioned DataObjects (Damian Mooyman)
|
||||
* 2016-01-21 [cca7129](https://github.com/silverstripe/silverstripe-framework/commit/cca7129385dbb3be1001a8861423c2cf490f02d4) Revert lost documentation (Damian Mooyman)
|
||||
* 2016-01-11 [85ba918](https://github.com/silverstripe/silverstripe-framework/commit/85ba918a54f51dd524d45f2c93172a18421ae3bf) Update field IDs for file link (fixes silverstripe/silverstripe-cms#1307) (Loz Calver)
|
||||
* 2016-01-11 [d637141](https://github.com/silverstripe/silverstripe-cms/commit/d6371414876e32e7369ec0219a57d2186cfe3f0f) preg_quote() anchors in SiteTreeLinkTracking (fixes #1359) (Loz Calver)
|
||||
* 2016-01-05 [00544ff](https://github.com/silverstripe/silverstripe-framework/commit/00544ff100048afdb7ccb1905304dddf8ab3205a) session_regenerate_id uses config system (Daniel Hensby)
|
||||
* 2016-01-05 [4335d8e](https://github.com/silverstripe/silverstripe-framework/commit/4335d8ed221a2b402299b32e31f97fc2956ec161) Members with no ID inherit logged in user permission (Daniel Hensby)
|
||||
* 2015-12-15 [afbb5cf](https://github.com/silverstripe/silverstripe-framework/commit/afbb5cfed4d29aea5868f0f12cd735dc5abe10d3) Vimeo oEmbed endpoint redirecting to no www (UndefinedOffset)
|
||||
* 2015-12-14 [d265c9b](https://github.com/silverstripe/silverstripe-framework/commit/d265c9b733ddac27d6df286ce000b09e1c69b986) Allow omitting a value for OptionsetField submissions (fixes #4824) (Loz Calver)
|
||||
* 2015-12-11 [5a21b2f](https://github.com/silverstripe/silverstripe-framework/commit/5a21b2fb15ed9c675594f0f990765bd4f97155c7) Guard against users being added to all groups on unsaved Group. (Mateusz Uzdowski)
|
||||
* 2015-11-27 [94742fa](https://github.com/silverstripe/silverstripe-framework/commit/94742fa3e2efad8f77f4acd1f9d06bf74916c5e6) Revert method visibility regression (Damian Mooyman)
|
||||
* 2015-11-18 [e9b833f](https://github.com/silverstripe/silverstripe-framework/commit/e9b833f5f0f989af8d611f8cfe71f0b0e2cb0159) ConfirmedPassword field correctly reports mismatching passwords (Christopher Darling)
|
||||
* 2015-11-17 [68d99be](https://github.com/silverstripe/silverstripe-framework/commit/68d99be24b63a933f041cd80a248a7b7fa8d588c) Hidden errors for composite fields nested inside FieldGroups (fixes #4773) (Loz Calver)
|
||||
* 2015-11-17 [97e90b8](https://github.com/silverstripe/silverstripe-cms/commit/97e90b8ebd8078bb60ecea66bdd3761380f93a61) RedirectorPage toggles not working (fixes #1328) (Loz Calver)
|
||||
* 2015-11-17 [b624eb9](https://github.com/silverstripe/silverstripe-cms/commit/b624eb98f1d1ff36811a3294ad29b31a50683d60) Setting target for unwritten VirtualPage breaks write (Loz Calver)
|
||||
* 2015-11-16 [2983d82](https://github.com/silverstripe/silverstripe-cms/commit/2983d823d1eef293ef11aac9e01336e23ed52b59) Ensure VirtualPage forwards request/response data to virtual controllers (fixes #1329) (Loz Calver)
|
||||
* 2015-11-12 [fea1158](https://github.com/silverstripe/silverstripe-framework/commit/fea1158d193ed4d037df94101e3b3f2d24a6ce49) Fix print button only displaying first page (Damian Mooyman)
|
||||
* 2015-11-11 [a40812a](https://github.com/silverstripe/silverstripe-framework/commit/a40812ac3320d27f243ef0ed54aa003fc53720b6) Don’t reuse DBConnector (fixes #4735) (Sam Minnee)
|
||||
* 2015-11-05 [f577ecb](https://github.com/silverstripe/silverstripe-framework/commit/f577ecb81149d0d09dc846204f17b2153a244b5a) prevent use cache on browser back button (Igor Nadj)
|
30
docs/en/04_Changelogs/rc/3.2.2-rc1.md
Normal file
30
docs/en/04_Changelogs/rc/3.2.2-rc1.md
Normal file
@ -0,0 +1,30 @@
|
||||
# 3.2.2-rc1
|
||||
|
||||
<!--- Changes below this line will be automatically regenerated -->
|
||||
|
||||
## Change Log
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* 2016-02-15 [8771859](https://github.com/silverstripe/silverstripe-framework/commit/87718597e8f04872c285808d0666fbb69c5100ba) "where" method in SQLUpdate Example (Richard Rudy)
|
||||
* 2016-01-28 [3fcf1e2](https://github.com/silverstripe/silverstripe-framework/commit/3fcf1e2c98629dcd0048ff9447bad4cd30b4bf95) edge case on many many extra fields (fixes 4991) (Mark Stephens)
|
||||
* 2016-01-27 [3d0178e](https://github.com/silverstripe/silverstripe-cms/commit/3d0178ebc0b7408442ad2532f998ed47839e7117) Use correct formaction for doRollback exemption (Damian Mooyman)
|
||||
* 2016-01-24 [d8e354d](https://github.com/silverstripe/silverstripe-framework/commit/d8e354d144383fb6459adf92731853d2e54268d6) PHPDocs on DataList::getIDList() and UnsavedRelationList::getIDList() (Damian Mooyman)
|
||||
* 2016-01-22 [bf8bf5e](https://github.com/silverstripe/silverstripe-framework/commit/bf8bf5e4d558126bb99ea63881f1885faafddd3d) Prevent Versioned::doRollbackTo from creating incorrect versions on subclasses of Versioned DataObjects (Damian Mooyman)
|
||||
* 2016-01-21 [cca7129](https://github.com/silverstripe/silverstripe-framework/commit/cca7129385dbb3be1001a8861423c2cf490f02d4) Revert lost documentation (Damian Mooyman)
|
||||
* 2016-01-11 [85ba918](https://github.com/silverstripe/silverstripe-framework/commit/85ba918a54f51dd524d45f2c93172a18421ae3bf) Update field IDs for file link (fixes silverstripe/silverstripe-cms#1307) (Loz Calver)
|
||||
* 2016-01-11 [d637141](https://github.com/silverstripe/silverstripe-cms/commit/d6371414876e32e7369ec0219a57d2186cfe3f0f) preg_quote() anchors in SiteTreeLinkTracking (fixes #1359) (Loz Calver)
|
||||
* 2016-01-05 [00544ff](https://github.com/silverstripe/silverstripe-framework/commit/00544ff100048afdb7ccb1905304dddf8ab3205a) session_regenerate_id uses config system (Daniel Hensby)
|
||||
* 2016-01-05 [4335d8e](https://github.com/silverstripe/silverstripe-framework/commit/4335d8ed221a2b402299b32e31f97fc2956ec161) Members with no ID inherit logged in user permission (Daniel Hensby)
|
||||
* 2015-12-15 [afbb5cf](https://github.com/silverstripe/silverstripe-framework/commit/afbb5cfed4d29aea5868f0f12cd735dc5abe10d3) Vimeo oEmbed endpoint redirecting to no www (UndefinedOffset)
|
||||
* 2015-12-14 [d265c9b](https://github.com/silverstripe/silverstripe-framework/commit/d265c9b733ddac27d6df286ce000b09e1c69b986) Allow omitting a value for OptionsetField submissions (fixes #4824) (Loz Calver)
|
||||
* 2015-12-11 [5a21b2f](https://github.com/silverstripe/silverstripe-framework/commit/5a21b2fb15ed9c675594f0f990765bd4f97155c7) Guard against users being added to all groups on unsaved Group. (Mateusz Uzdowski)
|
||||
* 2015-11-27 [94742fa](https://github.com/silverstripe/silverstripe-framework/commit/94742fa3e2efad8f77f4acd1f9d06bf74916c5e6) Revert method visibility regression (Damian Mooyman)
|
||||
* 2015-11-18 [e9b833f](https://github.com/silverstripe/silverstripe-framework/commit/e9b833f5f0f989af8d611f8cfe71f0b0e2cb0159) ConfirmedPassword field correctly reports mismatching passwords (Christopher Darling)
|
||||
* 2015-11-17 [68d99be](https://github.com/silverstripe/silverstripe-framework/commit/68d99be24b63a933f041cd80a248a7b7fa8d588c) Hidden errors for composite fields nested inside FieldGroups (fixes #4773) (Loz Calver)
|
||||
* 2015-11-17 [97e90b8](https://github.com/silverstripe/silverstripe-cms/commit/97e90b8ebd8078bb60ecea66bdd3761380f93a61) RedirectorPage toggles not working (fixes #1328) (Loz Calver)
|
||||
* 2015-11-17 [b624eb9](https://github.com/silverstripe/silverstripe-cms/commit/b624eb98f1d1ff36811a3294ad29b31a50683d60) Setting target for unwritten VirtualPage breaks write (Loz Calver)
|
||||
* 2015-11-16 [2983d82](https://github.com/silverstripe/silverstripe-cms/commit/2983d823d1eef293ef11aac9e01336e23ed52b59) Ensure VirtualPage forwards request/response data to virtual controllers (fixes #1329) (Loz Calver)
|
||||
* 2015-11-12 [fea1158](https://github.com/silverstripe/silverstripe-framework/commit/fea1158d193ed4d037df94101e3b3f2d24a6ce49) Fix print button only displaying first page (Damian Mooyman)
|
||||
* 2015-11-11 [a40812a](https://github.com/silverstripe/silverstripe-framework/commit/a40812ac3320d27f243ef0ed54aa003fc53720b6) Don’t reuse DBConnector (fixes #4735) (Sam Minnee)
|
||||
* 2015-11-05 [f577ecb](https://github.com/silverstripe/silverstripe-framework/commit/f577ecb81149d0d09dc846204f17b2153a244b5a) prevent use cache on browser back button (Igor Nadj)
|
11
docs/en/04_Changelogs/rc/3.2.2-rc2.md
Normal file
11
docs/en/04_Changelogs/rc/3.2.2-rc2.md
Normal file
@ -0,0 +1,11 @@
|
||||
# 3.2.2-rc2
|
||||
|
||||
<!--- Changes below this line will be automatically regenerated -->
|
||||
|
||||
## Change Log
|
||||
|
||||
### Security
|
||||
|
||||
* 2016-02-17 [faa94d5](https://github.com/silverstripe/silverstripe-framework/commit/faa94d51d570788dcebc2f2ef6e9de4d179ce1e4) Hostname, IP and Protocol Spoofing through HTTP Headers (Ingo Schommer) - See [ss-2016-003](http://www.silverstripe.org/download/security-releases/ss-2016-003)
|
||||
* 2016-02-17 [15d4db3](https://github.com/silverstripe/silverstripe-framework/commit/15d4db3b4a7dbc9a7e089f9329a396f8408ed7d9) Block unauthenticated access to dev/build/defaults (Damian Mooyman) - See [ss-2015-028](http://www.silverstripe.org/download/security-releases/ss-2015-028)
|
||||
* 2016-02-17 [e2c77c5](https://github.com/silverstripe/silverstripe-framework/commit/e2c77c5a8f13e901c51a3684210811559b592f0c) Ensure Gridfield actions respect CSRF (Damian Mooyman) - See [ss-2016-002](http://www.silverstripe.org/download/security-releases/ss-2016-002)
|
@ -11,7 +11,7 @@ well written bug reports can be half of the solution already!
|
||||
* [Framework Bugtracker](https://github.com/silverstripe/silverstripe-framework/issues)
|
||||
* [CMS Bugtracker](https://github.com/silverstripe/silverstripe-cms/issues)
|
||||
* [Documentation Bugtracker](https://github.com/silverstripe/silverstripe-framework/issues)
|
||||
* Search on [http://silverstripe.org/modules](http://silverstripe.org/modules) for module-specific bugtrackers
|
||||
* Search on [http://addons.silverstripe.org/](http://addons.silverstripe.org/) for module-specific bugtrackers
|
||||
* Request features: [UserVoice](http://silverstripe.uservoice.com).
|
||||
|
||||
Before submitting a bug:
|
||||
@ -19,7 +19,7 @@ Before submitting a bug:
|
||||
* Ask for assistance on the [forums](http://www.silverstripe.org/community/forums/), [core mailinglist](http://groups.google.com/group/silverstripe-dev) or on [IRC](http://irc.silverstripe.org/) if you're unsure if its really a bug.
|
||||
* Search for similar, existing tickets
|
||||
* Is this a security issue? Please follow our separate reporting guidelines below.
|
||||
* Is this a issue with the core framework or cms? Modules have their own issue trackers (see [silverstripe.org/modules](http://www.silverstripe.org/modules/))
|
||||
* Is this a issue with the core framework or cms? Modules have their own issue trackers (see [http://addons.silverstripe.org/](http://addons.silverstripe.org/))
|
||||
* Try to reproduce your issue on a [clean installation](/getting_started/composer#using-development-versions), maybe the bug has already been fixed on an unreleased branch?
|
||||
* The bugtracker is not the place to discuss enhancements, please use the forums or mailinglist.
|
||||
Only log enhancement tickets if they gather a large interest in the community
|
||||
|
@ -161,7 +161,7 @@ Most importantly: Keep the first line short, and add more detail below.
|
||||
This ensures commits are easy to browse, and look nice on github.com
|
||||
(more info about [proper git commit messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)).
|
||||
|
||||
As we automatically generate [changelogs](http://doc.silverstripe.org/sapphire/en/trunk/changelogs/) from them, we need a way to categorize and filter.
|
||||
As we automatically generate [changelogs](http://localhost/SpiritLevel/SS/doc.silverstripe.org/en/changelogs/) from them, we need a way to categorize and filter.
|
||||
Please prefix **noteworthy** commit messages with one of the following tags:
|
||||
|
||||
* `NEW` New feature or major enhancement (both for users and developers)
|
||||
|
@ -51,7 +51,7 @@ so we strive for giving ample warning on any upcoming changes through a "depreca
|
||||
How to deprecate an API:
|
||||
|
||||
* Add a `@deprecated` item to the docblock tag, with a `{@link <class>}` item pointing to the new API to use.
|
||||
* Update the deprecated code to throw a `[api:Deprecation::notice()]` error.
|
||||
* Update the deprecated code to throw a [api:Deprecation::notice()] error.
|
||||
* Both the docblock and error message should contain the **target version** where the functionality is removed.
|
||||
So, if you're committing the change to a *3.1* minor release, the target version will be *4.0*.
|
||||
* Deprecations should not be committed to patch releases
|
||||
|
@ -73,7 +73,7 @@ the [core committers](core_committers), who will assist with setting up your cre
|
||||
* Admin permissions on [transifex](https://www.transifex.com/silverstripe/).
|
||||
* AWS write permissions on the `silverstripe-ssorg-releases` s3 bucket.
|
||||
* Permission on [silverstripe release announcement](https://groups.google.com/forum/#!forum/silverstripe-announce).
|
||||
* Moderator permissions in the #silverstripe [IRC channel]((http://www.silverstripe.org/community/contributing-to-silverstripe/irc-channel/))
|
||||
* Moderator permissions in the #silverstripe [IRC channel](http://www.silverstripe.org/community/contributing-to-silverstripe/irc-channel/)
|
||||
|
||||
### First time setup: Security releases
|
||||
|
||||
@ -333,7 +333,7 @@ will need to be regularly updated.
|
||||
* Create a release announcement forum sticky on the
|
||||
[releases and announcements](http://www.silverstripe.org/community/forums/releases-and-announcements/)
|
||||
forum category. Make this a global read-only sticky, and un-sticky any older release.
|
||||
* Update the #silverstripe [IRC](http://www.silverstripe.org/community/contributing-to-silverstripe/irc-channel/) topic to include the new release version.
|
||||
* Update the #silverstripe [IRC](https://www.silverstripe.org/community/contributing-to-silverstripe/irc-channel/) topic to include the new release version.
|
||||
|
||||
### Stage 4: Web platform installer release
|
||||
|
||||
@ -342,8 +342,9 @@ The web platform installer is available [on the web app gallery](http://www.micr
|
||||
In order to update this you will need a Microsoft live account, and have it authorised
|
||||
by SilverStripe staff in order to publish these releases.
|
||||
|
||||
|
||||
To update this release there is an additional download tool at
|
||||
[https://code.platform.silverstripe.com/silverstripe/webpi](https://code.platform.silverstripe.com/silverstripe/webpi)
|
||||
`[https://code.platform.silverstripe.com/silverstripe/webpi](https://code.platform.silverstripe.com/silverstripe/webpi)`
|
||||
which will guide you through the process of generating a new zip release.
|
||||
|
||||
./make-package 3.2.4 3.2.4
|
||||
@ -363,6 +364,7 @@ to submit a new version, including:
|
||||
* [Release Process](release_process)
|
||||
* [Translation Process](translation_process)
|
||||
* [Core committers](core_committers)
|
||||
* [WebPI Installer](https://docs.silverstripe.org/en/getting_started/installation/other_installation_options/windows_platform_installer/)
|
||||
|
||||
If at any time a release runs into an unsolveable problem contact the
|
||||
core committers on the [discussion group](https://groups.google.com/forum/#!forum/silverstripe-committers)
|
||||
|
@ -37,7 +37,7 @@ If you submit a new feature or an API change, we strongly recommend that your pa
|
||||
## Repositories
|
||||
|
||||
* End-user help: [userhelp.silverstripe.org](http://github.com/silverstripe/userhelp.silverstripe.org)
|
||||
* Developer guides: [docs.silverstripe.org](http://github.com/silverstripe/docs.silverstripe.org)
|
||||
* Developer guides: [docs.silverstripe.org](http://github.com/silverstripe/doc.silverstripe.org)
|
||||
* Developer API documentation: [api.silverstripe.org](http://github.com/silverstripe/api.silverstripe.org)
|
||||
|
||||
## Source control
|
||||
|
@ -59,7 +59,7 @@ Currently translated entities are not directly factored into code (for security
|
||||
you can't see them straight away.
|
||||
|
||||
It is strongly encouraged that you check your translation this way, as its a good way to double check your translation
|
||||
works in the right context. Please use our [daily-builds](http://www.silverstripe.org/daily-builds/) for your local
|
||||
works in the right context. Please use our `[daily-builds](http://www.silverstripe.org/daily-builds/)` for your local
|
||||
installation, to ensure you're looking at the most up to date interface. See "Download Translations" above to find out
|
||||
how to retrieve the latest translation files.
|
||||
|
||||
@ -135,7 +135,7 @@ This also applies for any modules staying compatible with SilverStripe 2.x.
|
||||
## Contact
|
||||
|
||||
Translators have their own [mailinglist](https://groups.google.com/forum/#!forum/silverstripe-translators), but you can
|
||||
also reach a core member on [IRC](http://silverstripe.org/irc). The transifex.com interface has a built-in discussion
|
||||
also reach a core member on [IRC](https://irc.silverstripe.org). The transifex.com interface has a built-in discussion
|
||||
board if you have specific comments on a translation.
|
||||
|
||||
## Related
|
||||
@ -143,4 +143,4 @@ board if you have specific comments on a translation.
|
||||
* [i18n](/developer_guides/i18n): Developer-level documentation of Silverstripe's i18n capabilities
|
||||
* [Translation Process](translation_process): Information about managing translations for the core team and/or module maintainers.
|
||||
* [translatable](https://github.com/silverstripe/silverstripe-translatable): DataObject-interface powering the website-content translations
|
||||
* ["Translatable ModelAdmin" module](http://silverstripe.org/translatablemodeladmin-module/): An extension which allows translations of DataObjects inside ModelAdmin
|
||||
* `["Translatable ModelAdmin" module](http://silverstripe.org/translatablemodeladmin-module/)`: An extension which allows translations of DataObjects inside ModelAdmin
|
||||
|
@ -130,4 +130,4 @@ files back into the JS files SilverStripe can actually read. This requires an in
|
||||
* [i18n](/developer_guides/i18n/): Developer-level documentation of Silverstripe's i18n capabilities
|
||||
* [Contributing Translations](/contributing/translations): Information for translators looking to contribute translations of the SilverStripe UI.
|
||||
* [translatable](https://github.com/silverstripe/silverstripe-translatable): DataObject-interface powering the website-content translations
|
||||
* ["Translatable ModelAdmin" module](http://silverstripe.org/translatablemodeladmin-module/): An extension which allows translations of DataObjects inside ModelAdmin
|
||||
* `["Translatable ModelAdmin" module](http://silverstripe.org/translatablemodeladmin-module/)`: An extension which allows translations of DataObjects inside ModelAdmin
|
||||
|
@ -57,4 +57,4 @@ https://www.djangoproject.com/conduct/
|
||||
|
||||
http://web.archive.org/web/20141109123859/http://speakup.io/coc.html
|
||||
|
||||
http://www.crnhq.org/pages.php?pID=10
|
||||
http://www.crnhq.org/files/66138/files/Handouts%20and%20Posters/ResolveTheConflictGuideposter.pdf
|
||||
|
@ -20,7 +20,7 @@ community. There are also several other websites with SilverStripe documentation
|
||||
* The [API Documentation](http://api.silverstripe.org/) contains technical reference and class information.
|
||||
* The [User Help](http://userhelp.silverstripe.com) website contains documentation related to working within the CMS.
|
||||
|
||||
New features, API changes and the development [roadmap](http://www.silverstripe.org/roadmap/) for the product are
|
||||
New features, API changes and the development [roadmap](http://www.silverstripe.org/software/roadmap/) for the product are
|
||||
discussed on the [core mailinglist](https://groups.google.com/forum/#!forum/silverstripe-dev) along with
|
||||
[UserVoice](http://silverstripe.uservoice.com/forums/251266-new-features).
|
||||
|
||||
|
@ -887,10 +887,11 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
|
||||
action = "update";
|
||||
}
|
||||
|
||||
if(href.match(/^mailto:(.*)$/)) {
|
||||
if(href.match(/^mailto:([^?]*)(\?subject=(.*))?$/)) {
|
||||
return {
|
||||
LinkType: 'email',
|
||||
email: RegExp.$1,
|
||||
Subject: decodeURIComponent(RegExp.$3),
|
||||
Description: title
|
||||
};
|
||||
} else if(href.match(/^(assets\/.*)$/) || href.match(/^\[file_link\s*(?:\s*|%20|,)?id=([0-9]+)\]?(#.*)?$/)) {
|
||||
|
@ -301,6 +301,8 @@ cs:
|
||||
FROMWEB: 'Z webu'
|
||||
FindInFolder: 'Hledat ve složce'
|
||||
IMAGEALT: 'Alternativní text (alt)'
|
||||
IMAGEALTTEXT: 'Alternativní text (alt) - bude ukázán, když obrázek nemúže být zobrazen'
|
||||
IMAGEALTTEXTDESC: 'Zobrazeno na obrazovce, když obrázek nemůže být zobrazen'
|
||||
IMAGEDIMENSIONS: Rozměry
|
||||
IMAGEHEIGHTPX: Výška
|
||||
IMAGETITLE: 'Titul text (tooltip) - další informace o obrázku'
|
||||
@ -336,9 +338,11 @@ cs:
|
||||
DELETED: Smazáno.
|
||||
DropdownBatchActionsDefault: Akce
|
||||
HELP: Nápověda
|
||||
PAGETYPE: 'Typ stránky'
|
||||
PERMAGAIN: 'Byli jste odhlášeni z CMS. Pokud se chcete znovu přihlásit, zadejte níže své uživatelské jméno a heslo.'
|
||||
PERMALREADY: 'Omlouvám se, ale nemůžete vstoupit do této části CMS. Pokud se chcete přihlásit jako někdo jiný, udělejte tak níže.'
|
||||
PERMDEFAULT: 'Musíte být přihlášen/a k přístup do oblasti administrace, níže zadejte vaše přihlašovací údaje, prosím.'
|
||||
PLEASESAVE: 'Prosím uložte stránku: Tato stránka nemohla být aktualizována, ještě nebyla uložena.'
|
||||
PreviewButton: Náhled
|
||||
REORGANISATIONSUCCESSFUL: 'Strom webu reorganizován úspěšně.'
|
||||
SAVEDUP: Uloženo.
|
||||
|
@ -336,6 +336,7 @@ lt:
|
||||
DELETED: Ištrinta.
|
||||
DropdownBatchActionsDefault: Veiksmai
|
||||
HELP: Pagalba
|
||||
PAGETYPE: 'Puslapio tipas'
|
||||
PERMAGAIN: 'Jūs atsijungėte. Norėdami vėl prisijungti, įveskite savo duomenis į žemiau esančius laukelius.'
|
||||
PERMALREADY: 'Deja, bet Jūs negalite patekti į šią TVS dalį. Jeigu norite prisijungti kitu vartotoju, tai atlikite žemiau.'
|
||||
PERMDEFAULT: 'Jūs turite būti prisijungę, norėdami pasiekti administravimo zoną; prašome suvesti prisijungimo duomenis į žemiau esančius laukelius.'
|
||||
|
@ -301,6 +301,8 @@ sk:
|
||||
FROMWEB: 'Z webu'
|
||||
FindInFolder: 'Vyhľadať v priečinku'
|
||||
IMAGEALT: 'Atlernatívny text (alt)'
|
||||
IMAGEALTTEXT: 'Atlernatívny text (alt) - sa zobrazí, ak nemôže byť zobrazený obrázok'
|
||||
IMAGEALTTEXTDESC: 'Zobrazí sa na obrazovke, keď obrázok nemôže byť zobrazený'
|
||||
IMAGEDIMENSIONS: Rozmery
|
||||
IMAGEHEIGHTPX: Výška
|
||||
IMAGETITLE: 'Text titulky (tooltip) - pre doplňujúce informácie o obrázku'
|
||||
@ -336,9 +338,11 @@ sk:
|
||||
DELETED: Zmazané.
|
||||
DropdownBatchActionsDefault: Akcie
|
||||
HELP: Pomoc
|
||||
PAGETYPE: 'Typ stránky:'
|
||||
PERMAGAIN: 'Boli ste odhlásený'
|
||||
PERMALREADY: 'Je nám ľúto, ale k tejto časti CMS nemáte prístup . Ak sa chcete prihlásiť ako niekto iný, urobte tak nižšie.'
|
||||
PERMDEFAULT: 'Musíte byť prihlásený/á k prístupu do oblasti administrácie, zadajte vaše prihlasovacie údaje dole, prosím.'
|
||||
PLEASESAVE: 'Prosím uložte stránku: Táto stránka nemôže byť aktualizovaná, ešte nebola uložená.'
|
||||
PreviewButton: Náhľad
|
||||
REORGANISATIONSUCCESSFUL: 'Strom webu bol reorganizovaný úspešne.'
|
||||
SAVEDUP: Uložené.
|
||||
|
@ -916,7 +916,9 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
|
||||
|
||||
/**
|
||||
* Returns an array with both the keys and values set to the IDs of the records in this list.
|
||||
* Does not respect sort order. Use ->column("ID") to get an ID list with the current sort.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getIDList() {
|
||||
$ids = $this->column("ID");
|
||||
|
@ -2077,7 +2077,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
||||
$candidateManyManys = (array)Config::inst()->get($candidate, 'many_many', Config::UNINHERITED);
|
||||
|
||||
foreach($candidateManyManys as $relation => $relatedClass) {
|
||||
if($relatedClass === $this->class) {
|
||||
if (is_a($this, $relatedClass)) {
|
||||
$relationName = $relation;
|
||||
}
|
||||
}
|
||||
|
@ -206,8 +206,10 @@ class UnsavedRelationList extends ArrayList {
|
||||
|
||||
/**
|
||||
* Returns an array with both the keys and values set to the IDs of the records in this list.
|
||||
* Does not respect sort order. Use ->column("ID") to get an ID list with the current sort.
|
||||
* Does not return the IDs for unsaved DataObjects.
|
||||
*
|
||||
* Does not return the IDs for unsaved DataObjects
|
||||
* @return array
|
||||
*/
|
||||
public function getIDList() {
|
||||
// Get a list of IDs of our current items - if it's not a number then object then assume it's a DO.
|
||||
|
@ -5,6 +5,8 @@
|
||||
* allowing you to rollback changes and view history. An example of this is
|
||||
* the pages used in the CMS.
|
||||
*
|
||||
* @property int $Version
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage model
|
||||
*/
|
||||
@ -495,12 +497,12 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
|
||||
|
||||
foreach($versionedTables as $child) {
|
||||
if($table === $child) break; // only need subclasses
|
||||
|
||||
|
||||
// Select all orphaned version records
|
||||
$orphanedQuery = SQLSelect::create()
|
||||
->selectField("\"{$table}_versions\".\"ID\"")
|
||||
->setFrom("\"{$table}_versions\"");
|
||||
|
||||
|
||||
// If we have a parent table limit orphaned records
|
||||
// to only those that exist in this
|
||||
if(DB::get_schema()->hasTable("{$child}_versions")) {
|
||||
@ -576,7 +578,7 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
|
||||
*/
|
||||
protected function augmentWriteVersioned(&$manipulation, $table, $recordID) {
|
||||
$baseDataClass = ClassInfo::baseDataClass($table);
|
||||
|
||||
|
||||
// Set up a new entry in (table)_versions
|
||||
$newManipulation = array(
|
||||
"command" => "insert",
|
||||
@ -614,15 +616,17 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
|
||||
}
|
||||
$nextVersion = $nextVersion ?: 1;
|
||||
|
||||
// Add the version number to this data
|
||||
$manipulation[$table]['fields']['Version'] = $nextVersion;
|
||||
$newManipulation['fields']['Version'] = $nextVersion;
|
||||
|
||||
// Write AuthorID for baseclass
|
||||
if($table === $baseDataClass) {
|
||||
// Write AuthorID for baseclass
|
||||
$userID = (Member::currentUser()) ? Member::currentUser()->ID : 0;
|
||||
$newManipulation['fields']['AuthorID'] = $userID;
|
||||
|
||||
// Update main table version if not previously known
|
||||
$manipulation[$table]['fields']['Version'] = $nextVersion;
|
||||
}
|
||||
|
||||
// Update _versions table manipulation
|
||||
$newManipulation['fields']['Version'] = $nextVersion;
|
||||
$manipulation["{$table}_versions"] = $newManipulation;
|
||||
}
|
||||
|
||||
@ -649,6 +653,19 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
|
||||
|
||||
|
||||
public function augmentWrite(&$manipulation) {
|
||||
// get Version number from base data table on write
|
||||
$version = null;
|
||||
$baseDataClass = ClassInfo::baseDataClass($this->owner->class);
|
||||
if(isset($manipulation[$baseDataClass]['fields'])) {
|
||||
if ($this->migratingVersion) {
|
||||
$manipulation[$baseDataClass]['fields']['Version'] = $this->migratingVersion;
|
||||
}
|
||||
if (isset($manipulation[$baseDataClass]['fields']['Version'])) {
|
||||
$version = $manipulation[$baseDataClass]['fields']['Version'];
|
||||
}
|
||||
}
|
||||
|
||||
// Update all tables
|
||||
$tables = array_keys($manipulation);
|
||||
foreach($tables as $table) {
|
||||
|
||||
@ -657,20 +674,15 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
|
||||
unset($manipulation[$table]);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// Get ID field
|
||||
$id = $manipulation[$table]['id'] ? $manipulation[$table]['id'] : $manipulation[$table]['fields']['ID'];
|
||||
$id = $manipulation[$table]['id']
|
||||
? $manipulation[$table]['id']
|
||||
: $manipulation[$table]['fields']['ID'];
|
||||
if(!$id) {
|
||||
user_error("Couldn't find ID in " . var_export($manipulation[$table], true), E_USER_ERROR);
|
||||
}
|
||||
|
||||
if($this->migratingVersion) {
|
||||
$manipulation[$table]['fields']['Version'] = $this->migratingVersion;
|
||||
}
|
||||
|
||||
$version = isset($manipulation[$table]['fields']['Version'])
|
||||
? $manipulation[$table]['fields']['Version']
|
||||
: null;
|
||||
if($version < 0 || $this->_nextWriteWithoutVersion) {
|
||||
// Putting a Version of -1 is a signal to leave the version table alone, despite their being no version
|
||||
unset($manipulation[$table]['fields']['Version']);
|
||||
@ -680,7 +692,7 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
|
||||
$this->augmentWriteVersioned($manipulation, $table, $id);
|
||||
}
|
||||
|
||||
// For base classes of versioned data objects
|
||||
// Remove "Version" column from subclasses of baseDataClass
|
||||
if(!$this->hasVersionField($table)) {
|
||||
unset($manipulation[$table]['fields']['Version']);
|
||||
}
|
||||
@ -898,56 +910,64 @@ class Versioned extends DataExtension implements TemplateGlobalProvider {
|
||||
/**
|
||||
* Move a database record from one stage to the other.
|
||||
*
|
||||
* @param fromStage Place to copy from. Can be either a stage name or a version number.
|
||||
* @param toStage Place to copy to. Must be a stage name.
|
||||
* @param createNewVersion Set this to true to create a new version number. By default, the existing version
|
||||
* number will be copied over.
|
||||
* @param int|string $fromStage Place to copy from. Can be either a stage name or a version number.
|
||||
* @param string $toStage Place to copy to. Must be a stage name.
|
||||
* @param bool $createNewVersion Set this to true to create a new version number.
|
||||
* By default, the existing version number will be copied over.
|
||||
*/
|
||||
public function publish($fromStage, $toStage, $createNewVersion = false) {
|
||||
$this->owner->extend('onBeforeVersionedPublish', $fromStage, $toStage, $createNewVersion);
|
||||
|
||||
$baseClass = $this->owner->class;
|
||||
while( ($p = get_parent_class($baseClass)) != "DataObject") $baseClass = $p;
|
||||
$baseClass = ClassInfo::baseDataClass($this->owner->class);
|
||||
$extTable = $this->extendWithSuffix($baseClass);
|
||||
|
||||
/** @var Versioned|DataObject $from */
|
||||
if(is_numeric($fromStage)) {
|
||||
$from = Versioned::get_version($baseClass, $this->owner->ID, $fromStage);
|
||||
} else {
|
||||
$this->owner->flushCache();
|
||||
$from = Versioned::get_one_by_stage($baseClass, $fromStage, "\"{$baseClass}\".\"ID\"={$this->owner->ID}");
|
||||
$from = Versioned::get_one_by_stage($baseClass, $fromStage, array(
|
||||
"\"{$baseClass}\".\"ID\" = ?" => $this->owner->ID
|
||||
));
|
||||
}
|
||||
if(!$from) {
|
||||
user_error("Can't find {$this->owner->class}/{$this->owner->ID} in stage {$fromStage}", E_USER_WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
$publisherID = isset(Member::currentUser()->ID) ? Member::currentUser()->ID : 0;
|
||||
if($from) {
|
||||
$from->forceChange();
|
||||
if($createNewVersion) {
|
||||
$latest = self::get_latest_version($baseClass, $this->owner->ID);
|
||||
$this->owner->Version = $latest->Version + 1;
|
||||
} else {
|
||||
$from->migrateVersion($from->Version);
|
||||
}
|
||||
// Set version of new record
|
||||
$from->forceChange();
|
||||
if($createNewVersion) {
|
||||
// Clear version to be automatically created on write
|
||||
$from->Version = null;
|
||||
} else {
|
||||
$from->migrateVersion($from->Version);
|
||||
|
||||
// Mark this version as having been published at some stage
|
||||
$publisherID = isset(Member::currentUser()->ID) ? Member::currentUser()->ID : 0;
|
||||
DB::prepared_query("UPDATE \"{$extTable}_versions\"
|
||||
SET \"WasPublished\" = ?, \"PublisherID\" = ?
|
||||
WHERE \"RecordID\" = ? AND \"Version\" = ?",
|
||||
array(1, $publisherID, $from->ID, $from->Version)
|
||||
);
|
||||
|
||||
$oldMode = Versioned::get_reading_mode();
|
||||
Versioned::reading_stage($toStage);
|
||||
|
||||
$conn = DB::get_conn();
|
||||
if(method_exists($conn, 'allowPrimaryKeyEditing')) $conn->allowPrimaryKeyEditing($baseClass, true);
|
||||
$from->write();
|
||||
if(method_exists($conn, 'allowPrimaryKeyEditing')) $conn->allowPrimaryKeyEditing($baseClass, false);
|
||||
|
||||
$from->destroy();
|
||||
|
||||
Versioned::set_reading_mode($oldMode);
|
||||
} else {
|
||||
user_error("Can't find {$this->owner->URLSegment}/{$this->owner->ID} in stage $fromStage", E_USER_WARNING);
|
||||
}
|
||||
|
||||
// Change to new stage, write, and revert state
|
||||
$oldMode = Versioned::get_reading_mode();
|
||||
Versioned::reading_stage($toStage);
|
||||
|
||||
$conn = DB::get_conn();
|
||||
if(method_exists($conn, 'allowPrimaryKeyEditing')) {
|
||||
$conn->allowPrimaryKeyEditing($baseClass, true);
|
||||
$from->write();
|
||||
$conn->allowPrimaryKeyEditing($baseClass, false);
|
||||
} else {
|
||||
$from->write();
|
||||
}
|
||||
|
||||
$from->destroy();
|
||||
|
||||
Versioned::set_reading_mode($oldMode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
119
tests/model/ManyManyListExtensionTest.php
Normal file
119
tests/model/ManyManyListExtensionTest.php
Normal file
@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage tests
|
||||
*/
|
||||
class ManyManyListExtensionTest extends SapphireTest {
|
||||
|
||||
protected static $fixture_file = 'ManyManyListExtensionTest.yml';
|
||||
|
||||
protected $extraDataObjects = array(
|
||||
'ManyManyListTest_IndirectPrimary',
|
||||
'ManyManyListTest_Secondary',
|
||||
'ManyManyListTest_SecondarySub'
|
||||
);
|
||||
|
||||
// Test that when one side of a many-many relationship is added by extension, both
|
||||
// sides still see the extra fields.
|
||||
public function testExtraFieldsViaExtension() {
|
||||
// This extends ManyManyListTest_Secondary with the secondary extension that adds the relationship back
|
||||
// to the primary. The instance from the fixture is ManyManyListTest_SecondarySub, deliberately a sub-class of
|
||||
// the extended class.
|
||||
Object::add_extension('ManyManyListTest_Secondary', 'ManyManyListTest_IndirectSecondaryExtension');
|
||||
|
||||
// Test from the primary (not extended) to the secondary (which is extended)
|
||||
$primary = $this->objFromFixture('ManyManyListTest_IndirectPrimary', 'manymany_extra_primary');
|
||||
$secondaries = $primary->Secondary();
|
||||
$extraFields = $secondaries->getExtraFields();
|
||||
|
||||
$this->assertTrue(count($extraFields) > 0, 'has extra fields');
|
||||
$this->assertTrue(isset($extraFields['DocumentSort']), 'has DocumentSort');
|
||||
|
||||
// Test from the secondary (which is extended) to the primary (not extended)
|
||||
$secondary = $this->objFromFixture('ManyManyListTest_SecondarySub', 'manymany_extra_secondary');
|
||||
|
||||
$primaries = $secondary->Primary();
|
||||
$extraFields = $primaries->getExtraFields();
|
||||
|
||||
$this->assertTrue(count($extraFields) > 0, 'has extra fields');
|
||||
$this->assertTrue(isset($extraFields['DocumentSort']), 'has DocumentSort');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage tests
|
||||
*
|
||||
* A data object that implements the primary side of a many_many (where the extra fields are
|
||||
* defined.) The many-many refers to ManyManyListTest_Secondary rather than ManyManyListTest_SecondarySub
|
||||
* by design, because we're trying to test that a subclass instance picks up the extra fields of it's parent.
|
||||
*/
|
||||
class ManyManyListTest_IndirectPrimary extends DataObject implements TestOnly {
|
||||
|
||||
private static $db = array(
|
||||
'Title' => 'Varchar(255)'
|
||||
);
|
||||
|
||||
private static $many_many = array(
|
||||
'Secondary' => 'ManyManyListTest_Secondary'
|
||||
);
|
||||
|
||||
private static $many_many_extraFields = array(
|
||||
'Secondary' => array(
|
||||
'DocumentSort' => 'Int'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage tests
|
||||
*
|
||||
* A data object that implements the secondary side of a many_many when extended by
|
||||
* ManyManyListTest_IndirectSecondaryExtension.
|
||||
*/
|
||||
class ManyManyListTest_Secondary extends DataObject implements TestOnly {
|
||||
|
||||
// Possibly not required, but want to simulate a real test failure case where
|
||||
// database tables are present.
|
||||
private static $db = array(
|
||||
'Title' => 'Varchar(255)'
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage tests
|
||||
*
|
||||
* A data object that is a subclass of the secondary side. The test will create an instance of this,
|
||||
* and ensure that the extra fields are available on the instance even though the many many is
|
||||
* defined at the parent level.
|
||||
*/
|
||||
class ManyManyListTest_SecondarySub extends ManyManyListTest_Secondary {
|
||||
|
||||
// private static $db = array(
|
||||
// 'Other' => 'Varchar(255)'
|
||||
// );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage tests
|
||||
*
|
||||
* An extension that is applied to ManyManyListTest_Secondary that implements the other side of the many-many
|
||||
* relationship.
|
||||
*/
|
||||
class ManyManyListTest_IndirectSecondaryExtension extends DataExtension implements TestOnly {
|
||||
|
||||
private static $db = array(
|
||||
'Title' => 'Varchar(255)'
|
||||
);
|
||||
|
||||
private static $belongs_many_many = array(
|
||||
'Primary' => 'ManyManyListTest_IndirectPrimary'
|
||||
);
|
||||
|
||||
}
|
6
tests/model/ManyManyListExtensionTest.yml
Normal file
6
tests/model/ManyManyListExtensionTest.yml
Normal file
@ -0,0 +1,6 @@
|
||||
ManyManyListTest_IndirectPrimary:
|
||||
manymany_extra_primary:
|
||||
Title: 'primary'
|
||||
ManyManyListTest_SecondarySub:
|
||||
manymany_extra_secondary:
|
||||
Title: 'secondary'
|
@ -198,15 +198,37 @@ class VersionedTest extends SapphireTest {
|
||||
$page1 = $this->objFromFixture('VersionedTest_DataObject', 'page1');
|
||||
$page1->Content = 'orig';
|
||||
$page1->write();
|
||||
$oldVersion = $page1->Version;
|
||||
$firstVersion = $page1->Version;
|
||||
$page1->publish('Stage', 'Live', false);
|
||||
$this->assertEquals($oldVersion, $page1->Version, 'publish() with $createNewVersion=FALSE');
|
||||
$this->assertEquals(
|
||||
$firstVersion,
|
||||
$page1->Version,
|
||||
'publish() with $createNewVersion=FALSE does not create a new version'
|
||||
);
|
||||
|
||||
$page1->Content = 'changed';
|
||||
$page1->write();
|
||||
$oldVersion = $page1->Version;
|
||||
$secondVersion = $page1->Version;
|
||||
$this->assertTrue($firstVersion < $secondVersion, 'write creates new version');
|
||||
|
||||
$page1->publish('Stage', 'Live', true);
|
||||
$this->assertTrue($oldVersion < $page1->Version, 'publish() with $createNewVersion=TRUE');
|
||||
$thirdVersion = Versioned::get_latest_version('VersionedTest_DataObject', $page1->ID)->Version;
|
||||
$liveVersion = Versioned::get_versionnumber_by_stage('VersionedTest_DataObject', 'Live', $page1->ID);
|
||||
$stageVersion = Versioned::get_versionnumber_by_stage('VersionedTest_DataObject', 'Stage', $page1->ID);
|
||||
$this->assertTrue(
|
||||
$secondVersion < $thirdVersion,
|
||||
'publish() with $createNewVersion=TRUE creates a new version'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$liveVersion,
|
||||
$thirdVersion,
|
||||
'publish() with $createNewVersion=TRUE publishes to live'
|
||||
);
|
||||
$this->assertEquals(
|
||||
$stageVersion,
|
||||
$secondVersion,
|
||||
'publish() with $createNewVersion=TRUE does not affect stage'
|
||||
);
|
||||
}
|
||||
|
||||
public function testRollbackTo() {
|
||||
@ -222,10 +244,11 @@ class VersionedTest extends SapphireTest {
|
||||
$changedVersion = $page1->Version;
|
||||
|
||||
$page1->doRollbackTo($origVersion);
|
||||
$page1 = Versioned::get_one_by_stage('VersionedTest_DataObject', 'Stage',
|
||||
sprintf('"VersionedTest_DataObject"."ID" = %d', $page1->ID));
|
||||
$page1 = Versioned::get_one_by_stage('VersionedTest_DataObject', 'Stage', array(
|
||||
'"VersionedTest_DataObject"."ID" = ?' => $page1->ID
|
||||
));
|
||||
|
||||
$this->assertTrue($page1->Version > $changedVersion, 'Create a new higher version number');
|
||||
$this->assertTrue($page1->Version == $changedVersion + 1, 'Create a new higher version number');
|
||||
$this->assertEquals('orig', $page1->Content, 'Copies the content from the old version');
|
||||
|
||||
// check db entries
|
||||
|
Loading…
Reference in New Issue
Block a user