mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 12:05:37 +00:00
Improved docs on $allowed_actions
Added section to "Controllers" and "Form" topics, added $allowed_actions definitions to all controller examples
This commit is contained in:
parent
50995fbecb
commit
3e27d27f7a
@ -68,6 +68,9 @@ You can have more customized logic and interface feedback through a custom contr
|
|||||||
:::php
|
:::php
|
||||||
<?php
|
<?php
|
||||||
class MyController extends Controller {
|
class MyController extends Controller {
|
||||||
|
|
||||||
|
static $allowed_actions = array('Form');
|
||||||
|
|
||||||
protected $template = "BlankPage";
|
protected $template = "BlankPage";
|
||||||
|
|
||||||
function Link($action = null) {
|
function Link($action = null) {
|
||||||
|
@ -83,11 +83,12 @@ You can access the following controller-method with /team/signup
|
|||||||
class Team extends DataObject {}
|
class Team extends DataObject {}
|
||||||
|
|
||||||
class Team_Controller extends Controller {
|
class Team_Controller extends Controller {
|
||||||
function signup($id, $otherId) {
|
static $allowed_actions = array('signup');
|
||||||
|
public function signup($id, $otherId) {
|
||||||
return $this->renderWith('MyTemplate');
|
return $this->renderWith('MyTemplate');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
## SSViewer template rendering
|
## SSViewer template rendering
|
||||||
|
|
||||||
See [templates](/topics/templates) for information on the SSViewer template system.
|
See [templates](/topics/templates) for information on the SSViewer template system.
|
||||||
|
@ -98,6 +98,7 @@ This code provides a good template:
|
|||||||
|
|
||||||
:::php
|
:::php
|
||||||
class MyProcess extends Controller {
|
class MyProcess extends Controller {
|
||||||
|
public static $allowed_actions = array('index');
|
||||||
function index() {
|
function index() {
|
||||||
set_time_limit(0);
|
set_time_limit(0);
|
||||||
while(memory_get_usage() < 32*1024*1024) {
|
while(memory_get_usage() < 32*1024*1024) {
|
||||||
|
@ -3,8 +3,7 @@
|
|||||||
Base controller class. You will extend this to take granular control over the actions and url handling of aspects of
|
Base controller class. You will extend this to take granular control over the actions and url handling of aspects of
|
||||||
your SilverStripe site.
|
your SilverStripe site.
|
||||||
|
|
||||||
|
## Usage
|
||||||
## Example
|
|
||||||
|
|
||||||
`mysite/code/Controllers/FastFood.php`
|
`mysite/code/Controllers/FastFood.php`
|
||||||
|
|
||||||
@ -12,11 +11,12 @@ your SilverStripe site.
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
class FastFood_Controller extends Controller {
|
class FastFood_Controller extends Controller {
|
||||||
function order($arguments) {
|
public static $allowed_actions = array('order');
|
||||||
|
public function order(SS_HTTPRequest $request) {
|
||||||
print_r($arguments);
|
print_r($arguments);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
|
||||||
@ -37,6 +37,96 @@ Request for `/fastfood/order/24/cheesefries` would result in the following to th
|
|||||||
[Name] => cheesefries
|
[Name] => cheesefries
|
||||||
)
|
)
|
||||||
|
|
||||||
|
<div class="warning" markdown='1'>
|
||||||
|
SilverStripe automatically adds a URL routing entry based on the controller's class name,
|
||||||
|
so a `MyController` class is accessible through `http://yourdomain.com/MyController`.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## Access Control
|
||||||
|
|
||||||
|
### Through $allowed_actions
|
||||||
|
|
||||||
|
All public methods on a controller are accessible by their name through the `$Action`
|
||||||
|
part of the URL routing, so a `MyController->mymethod()` is accessible at
|
||||||
|
`http://yourdomain.com/MyController/mymethod`. This is not always desireable,
|
||||||
|
since methods can return internal information, or change state in a way
|
||||||
|
that's not intended to be used through a URL endpoint.
|
||||||
|
|
||||||
|
SilverStripe strongly recommends securing your controllers
|
||||||
|
through defining a `$allowed_actions` array on the class,
|
||||||
|
which allows whitelisting of methods, as well as a concise
|
||||||
|
way to perform checks against permission codes or custom logic.
|
||||||
|
|
||||||
|
:::php
|
||||||
|
class MyController extends Controller {
|
||||||
|
public static $allowed_actions = array(
|
||||||
|
// someaction can be accessed by anyone, any time
|
||||||
|
'someaction',
|
||||||
|
// So can otheraction
|
||||||
|
'otheraction' => true,
|
||||||
|
// restrictedaction can only be people with ADMIN privilege
|
||||||
|
'restrictedaction' => 'ADMIN',
|
||||||
|
// complexaction can only be accessed if $this->canComplexAction() returns true
|
||||||
|
'complexaction' '->canComplexAction'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
There's a couple of rules guiding these checks:
|
||||||
|
|
||||||
|
* Each controller is only responsible for access control on the methods it defines
|
||||||
|
* If a method on a parent class is overwritten, access control for it has to be redefined as well
|
||||||
|
* An action named "index" is whitelisted by default
|
||||||
|
* A wildcard (`*`) can be used to define access control for all methods (incl. methods on parent classes)
|
||||||
|
* Specific method entries in `$allowed_actions` overrule any `*` settings
|
||||||
|
* Methods returning forms also count as actions which need to be defined
|
||||||
|
* Form action methods (targets of `FormAction`) should NOT be included in `$allowed_actions`,
|
||||||
|
they're handled separately through the form routing (see the ["forms" topic](/topics/forms))
|
||||||
|
* `$allowed_actions` can be defined on `Extension` classes applying to the controller.
|
||||||
|
|
||||||
|
|
||||||
|
If the permission check fails, SilverStripe will return a "403 Forbidden" HTTP status.
|
||||||
|
|
||||||
|
### Through the action
|
||||||
|
|
||||||
|
Each method responding to a URL can also implement custom permission checks,
|
||||||
|
e.g. to handle responses conditionally on the passed request data.
|
||||||
|
|
||||||
|
:::php
|
||||||
|
class MyController extends Controller {
|
||||||
|
public static $allowed_actions = array('myaction');
|
||||||
|
public function myaction($request) {
|
||||||
|
if(!$request->getVar('apikey')) {
|
||||||
|
return $this->httpError(403, 'No API key provided');
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'valid';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Unless you transform the response later in the request processing,
|
||||||
|
it'll look pretty ugly to the user. Alternatively, you can use
|
||||||
|
`ErrorPage::response_for(<status-code>)` to return a more specialized layout.
|
||||||
|
|
||||||
|
Note: This is recommended as an addition for `$allowed_actions`, in order to handle
|
||||||
|
more complex checks, rather than a replacement.
|
||||||
|
|
||||||
|
### Through the init() method
|
||||||
|
|
||||||
|
After checking for allowed_actions, each controller invokes its `init()` method,
|
||||||
|
which is typically used to set up common state in the controller, and
|
||||||
|
include JavaScript and CSS files in the output which are used for any action.
|
||||||
|
If an `init()` method returns a `SS_HTTPResponse` with either a 3xx or 4xx HTTP
|
||||||
|
status code, it'll abort execution. This behaviour can be used to implement
|
||||||
|
permission checks.
|
||||||
|
|
||||||
|
:::php
|
||||||
|
class MyController extends Controller {
|
||||||
|
public static $allowed_actions = array();
|
||||||
|
public function init() {
|
||||||
|
parent::init();
|
||||||
|
if(!Permission::check('ADMIN')) return $this->httpError(403);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
## URL Handling
|
## URL Handling
|
||||||
|
|
||||||
@ -51,9 +141,10 @@ the case below we also want any orders coming through `/fastfood/drivethrough/`
|
|||||||
|
|
||||||
:::php
|
:::php
|
||||||
class FastFood_Controller extends Controller {
|
class FastFood_Controller extends Controller {
|
||||||
|
static $allowed_actions = array('drivethrough');
|
||||||
public static $url_handlers = array(
|
public static $url_handlers = array(
|
||||||
'drivethrough/$Action/$ID/$Name' => 'order'
|
'drivethrough/$Action/$ID/$Name' => 'order'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -45,16 +45,16 @@ TODO Describe behaviour.js solution easily, how to disable it
|
|||||||
Setting fieldEl.requiredErrorMsg or formEl.requiredErrorMsg will override the default error message. Both can include
|
Setting fieldEl.requiredErrorMsg or formEl.requiredErrorMsg will override the default error message. Both can include
|
||||||
the string '$FieldLabel', which will be replaced with the field's label. Otherwise, the message is "Please fill out
|
the string '$FieldLabel', which will be replaced with the field's label. Otherwise, the message is "Please fill out
|
||||||
"$FieldLabel", it is required".
|
"$FieldLabel", it is required".
|
||||||
|
|
||||||
You can use Behaviour to load in the appropriate value:
|
You can use Behaviour to load in the appropriate value:
|
||||||
|
|
||||||
:::js
|
:::js
|
||||||
Behaviour.register({
|
Behaviour.register({
|
||||||
'#Form_Form' : {
|
'#Form_Form' : {
|
||||||
requiredErrorMsg: "Please complete this question before moving on.",
|
requiredErrorMsg: "Please complete this question before moving on.",
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
### Other validation libraries
|
### Other validation libraries
|
||||||
|
|
||||||
By default, SilverStripe forms with an attached Validator instance use the custom Validator.js clientside logic. It is
|
By default, SilverStripe forms with an attached Validator instance use the custom Validator.js clientside logic. It is
|
||||||
@ -65,12 +65,12 @@ Disable for all forms (in `mysite/_config.php`):
|
|||||||
|
|
||||||
:::php
|
:::php
|
||||||
Validator::set_javascript_validation_handler('none');
|
Validator::set_javascript_validation_handler('none');
|
||||||
|
|
||||||
Disable for a specific form:
|
Disable for a specific form:
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
$myForm->getValidator()->setJavascriptValidationHandler('none');
|
$myForm->getValidator()->setJavascriptValidationHandler('none');
|
||||||
|
|
||||||
|
|
||||||
## Related
|
## Related
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ instantiating the Form class itself, or by subclassing it.
|
|||||||
|
|
||||||
## Instantiating a form
|
## Instantiating a form
|
||||||
|
|
||||||
Creating a form is a matter of defining a method to represent that form. This method should return a form object. The
|
Creating a form is a matter of defining a method to represent that form. This method should return a form object. The
|
||||||
constructor takes the following arguments:
|
constructor takes the following arguments:
|
||||||
|
|
||||||
* `$controller`: This must be the controller that contains the form.
|
* `$controller`: This must be the controller that contains the form.
|
||||||
@ -59,9 +59,9 @@ The real difference, however, is that you can then define your controller method
|
|||||||
|
|
||||||
|
|
||||||
## Form Field Types
|
## Form Field Types
|
||||||
|
|
||||||
There are many classes extending `[api:FormField]`. Some examples:
|
There are many classes extending `[api:FormField]`. Some examples:
|
||||||
|
|
||||||
* `[api:TextField]`
|
* `[api:TextField]`
|
||||||
* `[api:EmailField]`
|
* `[api:EmailField]`
|
||||||
* `[api:NumericField]`
|
* `[api:NumericField]`
|
||||||
@ -73,12 +73,12 @@ There are many classes extending `[api:FormField]`. Some examples:
|
|||||||
|
|
||||||
Full overview at [form-field-types](/reference/form-field-types)
|
Full overview at [form-field-types](/reference/form-field-types)
|
||||||
|
|
||||||
|
|
||||||
### Using Form Fields
|
### Using Form Fields
|
||||||
|
|
||||||
To get these fields automatically rendered into a form element, all you need to do is create a new instance of the
|
To get these fields automatically rendered into a form element, all you need to do is create a new instance of the
|
||||||
class, and add it to the fieldset of the form.
|
class, and add it to the fieldset of the form.
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
$form = new Form(
|
$form = new Form(
|
||||||
$controller = $this,
|
$controller = $this,
|
||||||
@ -114,7 +114,7 @@ Implementing the more complex fields requires extra arguments.
|
|||||||
new TextField(
|
new TextField(
|
||||||
$name = "FirstName",
|
$name = "FirstName",
|
||||||
$title = "First name"
|
$title = "First name"
|
||||||
),
|
),
|
||||||
new TextField("Surname"),
|
new TextField("Surname"),
|
||||||
new EmailField("Email", "Email address")
|
new EmailField("Email", "Email address")
|
||||||
new DropdownField(
|
new DropdownField(
|
||||||
@ -122,7 +122,7 @@ Implementing the more complex fields requires extra arguments.
|
|||||||
$title = "Country (if outside nz)",
|
$title = "Country (if outside nz)",
|
||||||
$source = Geoip::getCountryDropDown(),
|
$source = Geoip::getCountryDropDown(),
|
||||||
$value = Geoip::visitor_country()
|
$value = Geoip::visitor_country()
|
||||||
)
|
)
|
||||||
), new FieldSet(
|
), new FieldSet(
|
||||||
// List the action buttons here
|
// List the action buttons here
|
||||||
new FormAction("signup", "Sign up")
|
new FormAction("signup", "Sign up")
|
||||||
@ -173,28 +173,28 @@ First of all, you need to create your form on it's own class, that way you can d
|
|||||||
$fields = new FieldSet(
|
$fields = new FieldSet(
|
||||||
new TextField('FirstName', 'First name'),
|
new TextField('FirstName', 'First name'),
|
||||||
new EmailField('Email', 'Email address')
|
new EmailField('Email', 'Email address')
|
||||||
);
|
);
|
||||||
|
|
||||||
$actions = new FieldSet(
|
$actions = new FieldSet(
|
||||||
new FormAction('submit', 'Submit')
|
new FormAction('submit', 'Submit')
|
||||||
);
|
);
|
||||||
|
|
||||||
parent::__construct($controller, $name, $fields, $actions);
|
parent::__construct($controller, $name, $fields, $actions);
|
||||||
}
|
}
|
||||||
|
|
||||||
function forTemplate() {
|
function forTemplate() {
|
||||||
return $this->renderWith(array(
|
return $this->renderWith(array(
|
||||||
$this->class,
|
$this->class,
|
||||||
'Form'
|
'Form'
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
function submit($data, $form) {
|
function submit($data, $form) {
|
||||||
// do stuff here
|
// do stuff here
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
`forTemplate()` tells the `[api:Form]` class to render with a template of return value of `$this->class`, which in this case
|
`forTemplate()` tells the `[api:Form]` class to render with a template of return value of `$this->class`, which in this case
|
||||||
is *MyForm*, the name of the class. If the template doesn't exist, then it falls back to using Form.ss.
|
is *MyForm*, the name of the class. If the template doesn't exist, then it falls back to using Form.ss.
|
||||||
|
|
||||||
@ -203,36 +203,36 @@ basic customisation:
|
|||||||
|
|
||||||
:::ss
|
:::ss
|
||||||
<form $FormAttributes>
|
<form $FormAttributes>
|
||||||
<% if Message %>
|
<% if Message %>
|
||||||
<p id="{$FormName}_error" class="message $MessageType">$Message</p>
|
<p id="{$FormName}_error" class="message $MessageType">$Message</p>
|
||||||
<% else %>
|
<% else %>
|
||||||
<p id="{$FormName}_error" class="message $MessageType" style="display: none"></p>
|
<p id="{$FormName}_error" class="message $MessageType" style="display: none"></p>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<div id="FirstName" class="field text">
|
<div id="FirstName" class="field text">
|
||||||
<label class="left" for="$FormName_FirstName">First name</label>
|
<label class="left" for="$FormName_FirstName">First name</label>
|
||||||
$dataFieldByName(FirstName)
|
$dataFieldByName(FirstName)
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="Email" class="field email">
|
<div id="Email" class="field email">
|
||||||
<label class="left" for="$FormName_Email">Email</label>
|
<label class="left" for="$FormName_Email">Email</label>
|
||||||
$dataFieldByName(Email)
|
$dataFieldByName(Email)
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
$dataFieldByName(SecurityID)
|
$dataFieldByName(SecurityID)
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<% if Actions %>
|
<% if Actions %>
|
||||||
<div class="Actions">
|
<div class="Actions">
|
||||||
<% control Actions %>$Field<% end_control %>
|
<% control Actions %>$Field<% end_control %>
|
||||||
</div>
|
</div>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
`$dataFieldByName(FirstName)` will return the form control contents of `Field()` for the particular field object, in
|
`$dataFieldByName(FirstName)` will return the form control contents of `Field()` for the particular field object, in
|
||||||
this case `TextField->Field()` or `EmailField->Field()` which returns an `<input>` element with specific markup
|
this case `TextField->Field()` or `EmailField->Field()` which returns an `<input>` element with specific markup
|
||||||
for the type of field. Pass in the name of the field as the first parameter, as done above, to render it into the
|
for the type of field. Pass in the name of the field as the first parameter, as done above, to render it into the
|
||||||
template.
|
template.
|
||||||
|
|
||||||
To find more methods, have a look at the `[api:Form]` class, as there is a lot of different methods of customising the form
|
To find more methods, have a look at the `[api:Form]` class, as there is a lot of different methods of customising the form
|
||||||
|
@ -67,7 +67,8 @@ Example:
|
|||||||
|
|
||||||
:::php
|
:::php
|
||||||
class MyController extends Controller {
|
class MyController extends Controller {
|
||||||
function myurlaction($RAW_urlParams) {
|
static $allowed_actions = array('myurlaction');
|
||||||
|
public function myurlaction($RAW_urlParams) {
|
||||||
$SQL_urlParams = Convert::raw2sql($RAW_urlParams); // works recursively on an array
|
$SQL_urlParams = Convert::raw2sql($RAW_urlParams); // works recursively on an array
|
||||||
$objs = DataObject::get('Player', "Name = '{$SQL_data[OtherID]}'");
|
$objs = DataObject::get('Player', "Name = '{$SQL_data[OtherID]}'");
|
||||||
// ...
|
// ...
|
||||||
@ -81,7 +82,6 @@ This means if you've got a chain of functions passing data through, escaping sho
|
|||||||
:::php
|
:::php
|
||||||
class MyController extends Controller {
|
class MyController extends Controller {
|
||||||
/**
|
/**
|
||||||
|
|
||||||
* @param array $RAW_data All names in an indexed array (not SQL-safe)
|
* @param array $RAW_data All names in an indexed array (not SQL-safe)
|
||||||
*/
|
*/
|
||||||
function saveAllNames($RAW_data) {
|
function saveAllNames($RAW_data) {
|
||||||
@ -210,7 +210,8 @@ PHP:
|
|||||||
|
|
||||||
:::php
|
:::php
|
||||||
class MyController extends Controller {
|
class MyController extends Controller {
|
||||||
function search($request) {
|
static $allowed_actions = array('search');
|
||||||
|
public function search($request) {
|
||||||
$htmlTitle = '<p>Your results for:' . Convert::raw2xml($request->getVar('Query')) . '</p>';
|
$htmlTitle = '<p>Your results for:' . Convert::raw2xml($request->getVar('Query')) . '</p>';
|
||||||
return $this->customise(array(
|
return $this->customise(array(
|
||||||
'Query' => DBField::create('Text', $request->getVar('Query')),
|
'Query' => DBField::create('Text', $request->getVar('Query')),
|
||||||
@ -239,7 +240,8 @@ PHP:
|
|||||||
|
|
||||||
:::php
|
:::php
|
||||||
class MyController extends Controller {
|
class MyController extends Controller {
|
||||||
function search($request) {
|
static $allowed_actions = array('search');
|
||||||
|
public function search($request) {
|
||||||
$rssRelativeLink = "/rss?Query=" . urlencode($_REQUEST['query']) . "&sortOrder=asc";
|
$rssRelativeLink = "/rss?Query=" . urlencode($_REQUEST['query']) . "&sortOrder=asc";
|
||||||
$rssLink = Controller::join_links($this->Link(), $rssRelativeLink);
|
$rssLink = Controller::join_links($this->Link(), $rssRelativeLink);
|
||||||
return $this->customise(array(
|
return $this->customise(array(
|
||||||
|
@ -29,6 +29,8 @@ the form in a method on *HomePage_Controller*.
|
|||||||
|
|
||||||
:::php
|
:::php
|
||||||
class HomePage_Controller extends Page_Controller {
|
class HomePage_Controller extends Page_Controller {
|
||||||
|
static $allowed_actions = array('BrowserPollForm');
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
|
|
||||||
function BrowserPollForm() {
|
function BrowserPollForm() {
|
||||||
@ -415,4 +417,4 @@ In this tutorial we have explored forms, and seen the different approaches to cr
|
|||||||
decide to use the [userforms module](http://silverstripe.org/user-forms-module) or create a form in PHP depends on the situation and flexibility
|
decide to use the [userforms module](http://silverstripe.org/user-forms-module) or create a form in PHP depends on the situation and flexibility
|
||||||
required.
|
required.
|
||||||
|
|
||||||
[Next Tutorial >>](4-site-search)
|
[Next Tutorial >>](4-site-search)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user