Details

Security: Require ADMIN for ?flush=1

Flushing the various manifests (class, template, config) is performed through a GET
parameter (flush=1). Since this action requires more server resources than normal requests,
it can facilitate denial-of-service attacks.

To prevent this, main.php now checks and only allows the flush parameter in the following cases:

This applies to both flush=1 and flush=all (technically we only check for the existence of any parameter value)
but only through web requests made through main.php - CLI requests, or any other request that goes through
a custom start up script will still process all flush requests as normal.

Upgrading

Statics in custom Page classes need to be "private"

Requires action on every SilverStripe installation.

Typical error message: Access level to ErrorPage::$db must be public

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.

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 &lt; 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:

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.

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:

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.

class MyController extends Controller {
// This action is now denied because no $allowed_actions are specified
public function myaction($request) {}
}

You can overwrite the default behaviour on undefined $allowed_actions to allow all actions,
by setting the RequestHandler.require_allowed_actions config value to false (not recommended).

This applies to anything extending RequestHandler, so please check your Form and FormField
subclasses as well. Keep in mind, action methods as denoted through FormAction names should NOT
be mentioned in $allowed_actions to avoid CSRF issues.
Please review all rules governing allowed actions in the "controller" topic.

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.

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.

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.

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.

You can also implement custom permission codes.
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

The 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) and general page informancy

Removed SiteTree.MetaTitle as a means to customize the window title, use SiteTree.Title instead

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.

SSViewer::current_custom_theme() has been replaced with the SSViewer.theme_enabled configuration setting.
Please use it to toggle theme behaviour rather than relying on the custom theme being set in the
(now deprecated) SSViewer::set_theme() call.

Scaffolded DateField, TimeField and DatetimeField form field instances automatically include
formatting hints as placeholders and description text below the field itself.
If you change the date/time format of those fields, you need to adjust the hints.
To remove the hints, use setDescription(null) and setAttribute('placeholder', null).

Changed the way FreeStrings in SSTemplateParser are recognized, they will now also break on inequality
operators (<, >). If you use inequality operators in free strings in comparisions like
<% if Some<String == Some>Other>String %>...<% end_if %>
you have to replace them with explicitly markes strings like
<% if "Some<String" == "Some>Other>String" %>...<% end_if %>.
This change was necessary in order to support inequality operators in comparisons in templates

Hard limit displayed pages in the CMS tree to 500, and the number of direct children to 250,
to avoid excessive resource usage. Configure through Hierarchy.node_threshold_total and `
Hierarchy.node_threshold_leaf. Set to0` to show tree unrestricted.

Object now has beforeExtending and afterExtending to inject behaviour around method extension.
DataObject also has beforeUpdateCMSFields to insert fields between automatic scaffolding and extension
by updateCMSFields. See the DataExtension Reference for more information.

Magic quotes is now deprecated. Will trigger user_error on live sites, as well as an error on new installs

Support for Apache 1.x is removed.

Forms created in the CMS should now be instances of a new CMSForm class,
and have the CMS controller's response negotiator passed into them.
Example: $form = new CMSForm(...); $form->setResponseNegotiator($this->getResponseNegotiator());