mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
e97c034922
They are now accessed via the Config API, and contain associative rather than indexed arrays. Before: `array('de_DE' => array('German', 'Deutsch'))`, after: `array('de_DE' => array('name' => 'German', 'native' => 'Deutsch'))`. Also fixed a i18n.js_i18n config accessor
430 lines
19 KiB
Markdown
430 lines
19 KiB
Markdown
# 3.1.0 (unreleased)
|
|
|
|
## Overview ##
|
|
|
|
### CMS
|
|
|
|
* "Split view" editing with side-by-side preview of the edited website
|
|
* Resizing of preview to common screen widths ("desktop", "tablet" and "smartphone")
|
|
* Decluttered "Edit Page" buttons by moving minor actions into a "more options" panel
|
|
* Auto-detect CMS changes and highlight the save button for better informancy
|
|
* Display "last edited" and "last published" data for pages in CMS
|
|
* CMS form fields now support help text through `setDescription()`, both inline and as tooltips
|
|
* Removed SiteTree "MetaTitle" and "MetaKeywords" fields
|
|
* More legible and simplified tab and menu styling in the CMS
|
|
* Dropped support for Internet Explorer 7
|
|
|
|
### Framework
|
|
|
|
* Shortcodes are no longer supported in template files. They continue to work in DB fields and other
|
|
HTMLText-cast fields.
|
|
* `DataList` and `ArrayList` are now immutable, they'll return cloned instances on modification
|
|
* Behaviour testing support through [Behat](http://behat.org), with CMS test coverage
|
|
(see the [SilverStripe Behat Extension]() for details)
|
|
* Removed legacy table APIs (e.g. `TableListField`), use GridField instead
|
|
* Deny URL access if `Controller::$allowed_actions` is undefined
|
|
* Removed support for "*" rules in `Controller::$allowed_actions`
|
|
* Removed support for overriding rules on parent classes through `Controller::$allowed_actions`
|
|
* `RestfulService` verifies SSL peers by default
|
|
* Editing of relation table data (`$many_many_extraFields`) in `GridField`
|
|
* Optional integration with ImageMagick as a new image manipulation backend
|
|
* Support for PHP 5.4's built-in webserver
|
|
* Support for [Composer](http://getcomposer.org) dependency manager (also works with 3.0)
|
|
|
|
## Upgrading
|
|
|
|
### Static properties are immutable and private, you must use Config API.
|
|
|
|
A common SilverStripe pattern is to use a static variable on a class to define a configuration parameter.
|
|
The configuration system added in SilverStripe 3.0 builds on this by using this static variable as a way
|
|
of defining the default value.
|
|
|
|
In SilverStripe 3.0, it was possible to edit this value at run-time and have the change propagate into the
|
|
configuration system. This is no longer the case, for performance reasons. We've marked all "configurable"
|
|
statics as `private`, so you can't set or retrieve their value directly.
|
|
When using static setters or getters, the system throws a deprecation warning.
|
|
Notable exceptions to this rule are all static setters which accept objects, such as `SS_Cache::add_backend()`.
|
|
|
|
Please change all run-time manipulation of configuration to use `Config::inst()->update()` or
|
|
`$this->config()->update()`. You can keep using procedural configuration through `_config.php`
|
|
through this new notation, although its encouraged to use the (faster) YAML config wherever possible.
|
|
For this purpose, we have added a `mysite/_config/config.yml` file.
|
|
|
|
Here's an example on how to rewrite a common `_config.php` configuration:
|
|
|
|
:::php
|
|
<?php
|
|
global $project;
|
|
$project = 'mysite';
|
|
|
|
global $database;
|
|
$database = 'SS_mydb';
|
|
|
|
require_once('conf/ConfigureFromEnv.php');
|
|
SSViewer::set_theme('simple');
|
|
|
|
if(class_exists('SiteTree')) SiteTree::enable_nested_urls();
|
|
|
|
if(Director::isLive()) Email::setAdminEmail('support@mydomain.com');
|
|
|
|
if(is_defined('MY_REDIRECT_EMAILS')) Email::send_all_emails_to('developer@mydomain.com');
|
|
|
|
SS_Log::add_writer(new SS_LogFileWriter(BASE_PATH . '/mylog.log'), SS_Log::WARN);
|
|
|
|
if(strpos('Internet Explorer', $_SERVER['HTTP_USER_AGENT']) !== false) {
|
|
SSViewer::set_theme('basic');
|
|
}
|
|
|
|
Object::add_extension('Member', 'MyMemberExtension');
|
|
|
|
The ugpraded `_config.php`:
|
|
|
|
:::php
|
|
<?php
|
|
global $project;
|
|
$project = 'mysite';
|
|
|
|
global $database;
|
|
$database = 'SS_mydb';
|
|
|
|
require_once('conf/ConfigureFromEnv.php');
|
|
|
|
// Removed SiteTree::enable_nested_urls() since its configured by default
|
|
|
|
// Requires PHP objects, keep in PHP config
|
|
SS_Log::add_writer(new SS_LogFileWriter(BASE_PATH . '/mylog.log'), SS_Log::WARN);
|
|
// Non-trivial conditional, keep in PHP config
|
|
if(strpos('Internet Explorer', $_SERVER['HTTP_USER_AGENT']) !== false) {
|
|
// Overwrites any earlier YAML config
|
|
Config::inst()->update('SSViewer'. 'theme', 'basic');
|
|
}
|
|
|
|
The upgraded `config.yml`:
|
|
|
|
:::yml
|
|
---
|
|
Name: mysite
|
|
After: 'framework/*','cms/*'
|
|
---
|
|
SSViewer:
|
|
theme: 'simple'
|
|
Member:
|
|
extensions:
|
|
MyMemberExtension
|
|
---
|
|
Only:
|
|
environment: 'live'
|
|
---
|
|
Email:
|
|
admin_email: 'support@mydomain.com'
|
|
|
|
Some examples of changed notations (not exhaustive, there's over a hundred in total):
|
|
|
|
* `SSViewer::set_theme()`: Use `SSViewer.theme` instead
|
|
* `SecurityAdmin::$hidden_permissions`: Use `Permission.hidden_permissions` instead
|
|
* `Director::setBaseFolder`: Use `Director.alternate_base_folder` instead
|
|
* `Director::setBaseURL`: Use `Director.alternate_base_url` instead
|
|
* `SSViewer::setOption('rewriteHashlinks', ...)`: Use `SSViewer.rewrite_hashlinks` instead
|
|
|
|
<div class="warning" markdown='1'>
|
|
Please remember to upgrade the installer project as well, particularly
|
|
your `.htaccess` or `web.config` files. Web access to these sensitive YAML configuration files
|
|
needs to be explicitly denied through these configuration files (see the [3.0.5 security release](/changelogs/3.0.4))
|
|
for details.
|
|
</div>
|
|
|
|
For more information about how to use the config system, see the ["Configuration" topic](/topic/configuration).
|
|
|
|
### Statics in custom Page classes need to be "private"
|
|
|
|
Related to the configuration change described above, many statics in core are now
|
|
marked with `private` visibility. While PHP allows making variables more visible
|
|
(e.g. from "private" to "public"), it complains if you try to restrict visibility in subclasses.
|
|
The core framework extends from the `Page` class in your own codebase (`mysite/`),
|
|
which means you need to change those statics to `private` yourself.
|
|
The same rules apply to controllers subclassd from `Page_Controller`.
|
|
|
|
Before:
|
|
|
|
:::php
|
|
<?php
|
|
class Page extends SiteTree {
|
|
static $db = array('MyVar' => 'Text');
|
|
}
|
|
class Page_Controller extends ContentController {
|
|
static $allowed_actions = array('myaction');
|
|
}
|
|
|
|
After:
|
|
|
|
:::php
|
|
<?php
|
|
class Page extends SiteTree {
|
|
private static $db = array('MyVar' => 'Text');
|
|
}
|
|
class Page_Controller extends ContentController {
|
|
private static $allowed_actions = array('myaction');
|
|
}
|
|
|
|
Most statics defined in `SiteTree` and `DataObject` are affected, for example:
|
|
`$db`, `$has_one`, `$has_many`, `$many_many`, `$defaults`, `$allowed_children`.
|
|
The same goes for statics defined in `ContentController`, e.g. `$allowed_actions`.
|
|
|
|
Classes which are not further extended by the core (e.g. all custom `DataObject` subclasses)
|
|
are not affected by this change, although we recommend to mark those inherited statics
|
|
as `private` as well, to make it clear that they should be accessed through the Config API.
|
|
|
|
### default_cast is now Text
|
|
|
|
In order to reduce the chance of accidentally allowing XSS attacks, the value of default_cast
|
|
has been changed in 3.1 from HTMLText to Text. This means that any values used in a template
|
|
that haven't been explicitly cast as safe will be escaped (`<` replaced with `<` etc).
|
|
|
|
When upgrading, if methods return HTML fragments they need to explicitly cast them
|
|
as such. This can either be done by returning an HTMLText object, like:
|
|
|
|
:::php
|
|
return DBField::create_field('HTMLText', '<div></div>');
|
|
|
|
or by defining the casting of the accessor method, like:
|
|
|
|
:::php
|
|
class Page extends SiteTree {
|
|
private static $casting = array(
|
|
'MyDiv' => 'HTMLText'
|
|
)
|
|
|
|
function MyDiv() {
|
|
return '<div></div>';
|
|
}
|
|
}
|
|
|
|
SSViewer#process (and as a result ViewableData#renderWith) have been changed to already return
|
|
explicitly cast HTMLText instances, so functions that return the result of these methods won't
|
|
have to do any additional casting.
|
|
|
|
Note that this change means that if code was testing the result via is_string, that is no longer
|
|
reliable.
|
|
|
|
### Deny URL access if `Controller::$allowed_actions` is undefined or empty array
|
|
|
|
In order to make controller access checks more consistent and easier to
|
|
understand, the routing will require definition of `$allowed_actions`
|
|
on your own `Controller` subclasses if they contain any actions
|
|
accessible through URLs, or any forms.
|
|
|
|
:::php
|
|
class MyController extends Controller {
|
|
// This action is now denied because no $allowed_actions are specified
|
|
public function myaction($request) {}
|
|
}
|
|
|
|
Please review all rules governing allowed actions in the
|
|
["controller" topic](/topics/controller).
|
|
|
|
### Removed support for "*" rules in `Controller::$allowed_actions`
|
|
|
|
The wildcard ('*') character allowed to define fallback rules
|
|
in case they weren't explicitly defined. This caused a lot of confusion,
|
|
particularly around inherited rules. We've decided to remove the feature,
|
|
you'll need to specificy each accessible action individually.
|
|
|
|
:::php
|
|
class MyController extends Controller {
|
|
public static $allowed_actions = array('*' => 'ADMIN');
|
|
// Always denied because not explicitly listed in $allowed_actions
|
|
public function myaction($request) {}
|
|
// Always denied because not explicitly listed in $allowed_actions
|
|
public function myotheraction($request) {}
|
|
}
|
|
|
|
Please review all rules governing allowed actions in the
|
|
["controller" topic](/topics/controller).
|
|
|
|
### Removed support for overriding rules on parent classes through `Controller::$allowed_actions`
|
|
|
|
Since 3.1, the `$allowed_actions` definitions only apply
|
|
to methods defined on the class they're also defined on.
|
|
Overriding inherited access definitions is no longer possible.
|
|
|
|
:::php
|
|
class MyController extends Controller {
|
|
public static $allowed_actions = array('myaction' => 'ADMIN');
|
|
public function myaction($request) {}
|
|
}
|
|
class MySubController extends MyController {
|
|
// No longer works
|
|
public static $allowed_actions = array('myaction' => 'CMS_ACCESS_CMSMAIN');
|
|
}
|
|
|
|
This also applies for custom implementations of `handleAction()` and `handleRequest()`,
|
|
which now have to be listed in the `$allowed_actions` specifically.
|
|
It also restricts `Extension` classes applied to controllers, which now
|
|
can only grant or deny access or methods they define themselves.
|
|
|
|
New approach with the [Config API](/topics/configuration)
|
|
|
|
:::php
|
|
class MySubController extends MyController {
|
|
public function init() {
|
|
parent::init();
|
|
|
|
Config::inst()->update('MyController', 'allowed_actions',
|
|
array('myaction' => 'CMS_ACCESS_CMSMAIN')
|
|
);
|
|
}
|
|
}
|
|
|
|
Please review all rules governing allowed actions in the
|
|
["controller" topic](/topics/controller).
|
|
|
|
### Grouped CMS Buttons
|
|
|
|
The CMS buttons are now grouped, in order to hide minor actions by default and declutter the interface.
|
|
This required changing the form field structure from a simple `FieldList`
|
|
to a `FieldList` which contains a `CompositeField` for all "major actions",
|
|
and a `TabSet` with a single tab for all "minor actions".
|
|
If you have previously added, removed or altered built-in CMS actions in any way,
|
|
you'll need to adjust your code.
|
|
|
|
:::php
|
|
class MyPage extends Page {
|
|
function getCMSActions() {
|
|
$actions = parent::getCMSActions();
|
|
|
|
// Inserting a new toplevel action (old)
|
|
$actions->push(new FormAction('MyAction'));
|
|
|
|
// Inserting a new toplevel action (new)
|
|
$actions->insertAfter(new FormAction('MyAction'), 'MajorActions');
|
|
|
|
// Removing an action, both toplevel and nested (no change required)
|
|
$actions->removeByName('action_unpublish');
|
|
|
|
// Inserting a new minor action (new)
|
|
$actions->addFieldToTab(
|
|
'Root.ActionMenus.MoreOptions',
|
|
new FormAction('MyMinorAction')
|
|
);
|
|
|
|
// Finding a toplevel action (no change required)
|
|
$match = $actions->dataFieldByName('action_save');
|
|
|
|
// Finding a nested action (new)
|
|
$match = $actions->fieldByName('ActionMenus.MoreOptions')
|
|
->fieldByName('action_MyMinorAction');
|
|
|
|
return $actions;
|
|
}
|
|
}
|
|
|
|
### GridField and ModelAdmin Permission Checks
|
|
|
|
`GridFieldDetailForm` now checks for `canEdit()` and `canDelete()` permissions
|
|
on your model. `GridFieldAddNewButton` checks `canCreate()`.
|
|
The default implementation requires `ADMIN` permissions.
|
|
You'll need to loosen those permissions if you want other users with CMS
|
|
access to interact with your data.
|
|
Since `GridField` is used in `ModelAdmin`, this change will affect both classes.
|
|
|
|
Example: Require "CMS: Pages section" access
|
|
|
|
:::php
|
|
class MyModel extends DataObject {
|
|
public function canView($member = null) {
|
|
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
|
|
}
|
|
public function canEdit($member = null) {
|
|
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
|
|
}
|
|
public function canDelete($member = null) {
|
|
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
|
|
}
|
|
public function canCreate($member = null) {
|
|
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
|
|
}
|
|
|
|
You can also implement [custom permission codes](/topics/permissions).
|
|
For 3.1.0 stable, we aim to further simplify the permission definitions,
|
|
in order to reduce the boilerplate code required to get a model editable in the CMS.
|
|
|
|
Note: GridField is already relying on the permission checks performed
|
|
through the CMS controllers, providing a simple level of security.
|
|
|
|
### RestfulService verifies SSL peers by default
|
|
|
|
This makes the implementation "secure by default", by removing
|
|
the call to `curl_setopt(CURLOPT_SSL_VERIFYPEER, false)`.
|
|
Failing to validate SSL peers makes HTTP requests vulnerable to man in the middle attacks.
|
|
The underlying `curl` library relies on the operating system for the resulting CA certificate
|
|
verification. On some systems (mainly Windows), these certificates are not available on
|
|
a standard PHP installation, and need to be added manually through `CURLOPT_CAINFO`.
|
|
Although it is not recommended, you can restore the old insecure behaviour with
|
|
the following configuration: `RestfulService::set_default_curl_option(CURLOPT_SSL_VERIFYPEER, false)`.
|
|
|
|
### Deprecation API {#deprecation}
|
|
|
|
The `[api:Deprecation]` API generates deprecation notices to help you future-proof your code.
|
|
Calls to ceprecated methods will only produce errors if the API was deprecated in the
|
|
release equal to or earlier than the "notification version" (currently set to "3.1.0").
|
|
|
|
If you change the notification version to 3.1.0-dev, then only methods deprecated in older versions
|
|
(e.g. 3.0) will trigger notices, and the other methods will silently pass. This can be useful if
|
|
you don't yet have time to remove all calls to deprecated methods.
|
|
|
|
Deprecation::notification_version('3.1.0-dev');
|
|
|
|
On the other hand, if you want to identify which APIs will be removed in the next minor release (3.2.0),
|
|
you can enable those warnings and future-proof your code already.
|
|
|
|
Deprecation::notification_version('3.2.0');
|
|
|
|
### Other
|
|
|
|
* `TableListField`, `ComplexTableField`, `TableField`, `HasOneComplexTableField`, `HasManyComplexTableField` and `ManyManyComplexTableField` have been removed from the core and placed into a module called "legacytablefields" located at https://github.com/silverstripe-labs/legacytablefields
|
|
* `prototype.js` and `behaviour.js` have been removed from the core, they are no longer used. If you have custom code relying on these two libraries, please update your code to include the files yourself
|
|
* Removed `SiteTree.MetaKeywords` since they are irrelevant in terms of SEO ([seomoz article](http://www.mattcutts.com/blog/keywords-meta-tag-in-web-search/)) and general page informancy
|
|
* Removed `SiteTree.MetaTitle` as a means to customize the window title, use `SiteTree.Title` instead
|
|
* Deprecated `Profiler` class, use third-party solutions like [xhprof](https://github.com/facebook/xhprof/)
|
|
* Removed defunct or unnecessary debug GET parameters:
|
|
`debug_profile`, `debug_memory`, `profile_trace`, `debug_javascript`, `debug_behaviour`
|
|
* Removed `Member_ProfileForm`, use `CMSProfileController` instead
|
|
* `SiteTree::$nested_urls` enabled by default. To disable, call `SiteTree::disable_nested_urls()`.
|
|
* Removed CMS permission checks from `File->canEdit()` and `File->canDelete()`. If you have unsecured controllers relying on these permissions, please override them through a `DataExtension`.
|
|
* Moved email bounce handling to new ["emailbouncehandler" module](https://github.com/silverstripe-labs/silverstripe-emailbouncehandler),
|
|
including `Email_BounceHandler` and `Email_BounceRecord` classes,
|
|
as well as the `Member->Bounced` property.
|
|
* Deprecated global email methods `htmlEmail()` and `plaintextEmail`, as well as various email helper methods like `encodeMultipart()`. Use the `Email` API, or the `Mailer` class where applicable.
|
|
* Removed non-functional `$inlineImages` option for sending emails
|
|
* Removed support for keyed arrays in `SelectionGroup`, use new `SelectionGroup_Item` object
|
|
to populate the list instead (see [API docs](api:SelectionGroup)).
|
|
* `FormField->setDescription()` now renders in a `<span class="description">` by default, rather than a `title` attribute * Removed `Form->Name()`: Use getName()
|
|
* Removed `FormField->setContainerFieldSet()`: Use setContainerFieldList()
|
|
* Removed `FormField->rootFieldSet()`: Use rootFieldList()
|
|
* Removed `Group::map()`: Use DataList::("Group")->map()
|
|
* Removed `Member->generateAutologinHash()`: Tokens are no longer saved directly into the database in plaintext. Use the return value of the Member::generateAutologinTokenAndHash to get the token
|
|
* Removed `Member->sendInfo()`: use Member_ChangePasswordEmail or Member_ForgotPasswordEmail directly
|
|
* Removed `SQLMap::map()`: Use DataList::("Member")->map()
|
|
* Removed `SQLMap::mapInGroups()`: Use Member::map_in_groups()
|
|
* Removed `PasswordEncryptor::register()/unregister()`: Use config system instead
|
|
* Methods on DataList and ArrayList that used to both modify the existing list & return a new version now just return a new version. Make sure you change statements like `$list->filter(...)` to $`list = $list->filter(...)` for these methods:
|
|
- `ArrayList#reverse`
|
|
- `ArrayList#sort`
|
|
- `ArrayList#filter`
|
|
- `ArrayList#exclude`
|
|
- `DataList#where`
|
|
- `DataList#limit`
|
|
- `DataList#sort`
|
|
- `DataList#addFilter`
|
|
- `DataList#applyFilterContext`
|
|
- `DataList#innerJoin`
|
|
- `DataList#leftJoin`
|
|
- `DataList#find`
|
|
- `DataList#byIDs`
|
|
- `DataList#reverse`
|
|
* `DataList#dataQuery` has been changed to return a clone of the query, and so can't be used to modify the list's query directly. Use `DataList#alterDataQuery` instead to modify dataQuery in a safe manner.
|
|
* `ScheduledTask`, `QuarterHourlyTask`, `HourlyTask`, `DailyTask`, `MonthlyTask`, `WeeklyTask` and
|
|
`YearlyTask` are deprecated, please extend from `BuildTask` or `CliController`,
|
|
and invoke them in self-defined frequencies through Unix cronjobs etc.
|
|
* `i18n::$common_locales` and `i18n::$common_languages` are now accessed via the Config API, and contain associative rather than indexed arrays.
|
|
Before: `array('de_DE' => array('German', 'Deutsch'))`, after: `array('de_DE' => array('name' => 'German', 'native' => 'Deutsch'))`. |