Consistent 4.0 changelog presentation

- Use "Removed/Changed/Added/Moved" prefixes to make content more scannable
- Removed "change" vs "remove" distinction in subheadings (hard to separate, and not meaningful in practice)
- Linearised messages, less nesting - easier to parse (don't need to remember context from a few lines above)
- Simplified language (e.g. "use" instead of "please use")
- Simplified headline wording (remove repetitive "upgrade code that uses X")
- More anchor links across sections
- More use of diffs to clearly communicate before/after states
- Consistently apply backtick code formatting
- Consistently use "()" to denote methods (easier to read)
- Removed duplicate SQLQuery instructions (and simplified the augmentSQL example)
- Add new composer.json intro
This commit is contained in:
Ingo Schommer 2017-10-24 22:18:46 +13:00
parent 13d2350ad9
commit 5027af3926
2 changed files with 678 additions and 886 deletions

View File

@ -452,7 +452,7 @@ the default ones.
class: FormTemplateHelper_Pre32 class: FormTemplateHelper_Pre32
### Update code that uses SQLQuery ### <a name="sqlquery"></a>Update code that uses SQLQuery
SQLQuery has been changed. Previously this class was used for both selecting and deleting, but SQLQuery has been changed. Previously this class was used for both selecting and deleting, but
deletion is now handled by the new SQLDelete class. deletion is now handled by the new SQLDelete class.

View File

@ -6,7 +6,7 @@ This version introduces many breaking changes, which in most projects can be man
of automatic upgrade processes as well as manual code review. This document reviews these changes and will of automatic upgrade processes as well as manual code review. This document reviews these changes and will
guide developers in preparing existing 3.x code for compatibility with 4.0 guide developers in preparing existing 3.x code for compatibility with 4.0
* [Highlights of Major Changes](#overview) * [Overview](#overview)
* [Upgrading Guide](#upgrading) * [Upgrading Guide](#upgrading)
* [Standard Upgrade](#upgrading-primary) * [Standard Upgrade](#upgrading-primary)
* [API Specific Upgrades](#upgrading-specifics) * [API Specific Upgrades](#upgrading-specifics)
@ -16,24 +16,24 @@ guide developers in preparing existing 3.x code for compatibility with 4.0
* [ORM API](#overview-orm) * [ORM API](#overview-orm)
* [Filesystem API](#overview-filesystem) * [Filesystem API](#overview-filesystem)
* [Template and Form API](#overview-template) * [Template and Form API](#overview-template)
* [i18n](#overview-i18n) * [i18n API](#overview-i18n)
* [Email and Mailer](#overview-mailer) * [Email and Mailer API](#overview-mailer)
* [SapphireTest](#overview-testing) * [SapphireTest API](#overview-testing)
* [Security](#overview-security) * [Security API](#overview-security)
## <a name="overview"></a>Highlights of major changes ## <a name="overview"></a>Overview
* Minimum version dependencies have increased; PHP 5.5 and Internet Explorer 11 (or other modern browser) * Minimum version dependencies have increased; PHP 5.5 and Internet Explorer 11 (or other modern browser)
is required. is required.
* All code earlier marked as deprecated for 4.0 has now been removed. See * All code earlier marked as deprecated for 4.0 has now been removed (check our
[deprecation documentation](/contributing/release_process) for information on code deprecation. [deprecation process](/contributing/release_process))
* All code has been migrated to follow the PSR-2 coding standard. Most significantly, all SilverStripe * All code has been migrated to follow the PSR-2 coding standard. Most significantly, all SilverStripe
classes are now namespaced, and some have been renamed. This has major implications for classes are now namespaced, and some have been renamed. This has major implications for
arrangement of templates, as well as other references to classes via string literals or configuration. arrangement of templates, as well as other references to classes via string literals or configuration.
Automatic upgrading tools have been developed to cope with the bulk of these changes (see Automatic upgrading tools have been developed to cope with the bulk of these changes (see
[upgrading notes](#upgrading)). Some classes have been rearranged so that each file contains only one class definition. It is recommended that user code follow the same convention. [upgrading notes](#upgrading)). Some classes have been rearranged so that each file contains only one class definition. It is recommended that user code follow the same convention.
For example, page types and their controllers should longer be collated in a single file. For example, page types and their controllers should longer be collated in a single file.
* Object class has been removed. * Object class has been replaced with traits ([details](object-replace)).
* Asset storage has been abstracted, and a new concept of `DBFile` references via database column references * Asset storage has been abstracted, and a new concept of `DBFile` references via database column references
now exists in addition to references via the existing `File` dataobject. File security and protected files now exists in addition to references via the existing `File` dataobject. File security and protected files
are now a core feature. are now a core feature.
@ -41,7 +41,7 @@ guide developers in preparing existing 3.x code for compatibility with 4.0
prominently featuring ReactJS to develop highly functional CMS content areas. A new standard form schema prominently featuring ReactJS to develop highly functional CMS content areas. A new standard form schema
API has been developed to allow back-end PHP constructed forms to scaffold themselves within ReactJS API has been developed to allow back-end PHP constructed forms to scaffold themselves within ReactJS
powered sections. powered sections.
* CMS CSS has been re-developed using bootstrap 4 as a base * CMS CSS has been re-developed using Bootstrap v4 as a base
([blog post](https://www.silverstripe.org/blog/a-frameworks-framework-why-silverstripe-4-will-use-bootstrap/)) ([blog post](https://www.silverstripe.org/blog/a-frameworks-framework-why-silverstripe-4-will-use-bootstrap/))
* Asset admin has been replaced with a purely ReactJS powered upgrade, and split out * Asset admin has been replaced with a purely ReactJS powered upgrade, and split out
module called [asset-admin](https://github.com/silverstripe/silverstripe-asset-admin/). You'll need to add this to your `composer.json` to retain file management capabilities for your CMS authors. module called [asset-admin](https://github.com/silverstripe/silverstripe-asset-admin/). You'll need to add this to your `composer.json` to retain file management capabilities for your CMS authors.
@ -58,7 +58,7 @@ guide developers in preparing existing 3.x code for compatibility with 4.0
* Replaced `Zend_Cache` and the `Cache` API with a PSR-16 implementation (symfony/cache) * Replaced `Zend_Cache` and the `Cache` API with a PSR-16 implementation (symfony/cache)
* _ss_environment.php files have been removed in favour of `.env` and "real" environment variables. * _ss_environment.php files have been removed in favour of `.env` and "real" environment variables.
* admin has been moved to a new module [silverstripe/admin](https://github.com/silverstripe/silverstripe-admin). * admin has been moved to a new module [silverstripe/admin](https://github.com/silverstripe/silverstripe-admin).
* Behat support updated to behat 3. See the * Behat support updated to v3. See the
[behat extension](https://github.com/silverstripe/silverstripe-behat-extension) for more information. [behat extension](https://github.com/silverstripe/silverstripe-behat-extension) for more information.
* The `GDBackend` and `ImagickBackend` classes have been replaced by a unified `InterventionBackend` which uses the * The `GDBackend` and `ImagickBackend` classes have been replaced by a unified `InterventionBackend` which uses the
[intervention/image](https://github.com/intervention/image) library to power manipualations. [intervention/image](https://github.com/intervention/image) library to power manipualations.
@ -67,18 +67,83 @@ guide developers in preparing existing 3.x code for compatibility with 4.0
* Core modules are installed in the `vendor/` folder by default (other modules can opt-in, see [guide](/developer_guides/extending/how_tos/publish_a_module)) * Core modules are installed in the `vendor/` folder by default (other modules can opt-in, see [guide](/developer_guides/extending/how_tos/publish_a_module))
* Renamed constant for temp folder from `TEMP_FOLDER` to `TEMP_PATH` for naming consistency with other path variables and constants * Renamed constant for temp folder from `TEMP_FOLDER` to `TEMP_PATH` for naming consistency with other path variables and constants
## <a name="upgrading"></a>Upgrading ## <a name="upgrading"></a>Upgrading Guide
The below sections describe how to go about updating an existing site to be prepared for upgrade to 4.0. The below sections describe how to go about updating an existing site to be prepared for upgrade to 4.0.
Most of these upgrading tasks will involve manual code review, although in some cases there are Most of these upgrading tasks will involve manual code review, although in some cases there are
some automated processes that users can run to some automated processes that users can run.
### <a name="upgrading-primary"></a>Standard Upgrade ### Composer dependency update
This section describes the processes which every project upgrading to 4.0 should follow. This should be followed As a first step, you need to update your composer dependencies.
as a standard first point of upgrade. The easiest way is to start with a new `composer.json` file
and gradually move over settings from your old one.
This way you don't get dependency conflicts with potentially incompatible modules.
#### Upgrade project index.php and .htaccess rewrites Backup your existing `composer.json` and overwrite it with the following content:
```json
{
"name": "myvendor/myproject",
"require": {
"silverstripe/recipe-plugin": ">=0.1@dev <1.0",
"silverstripe/recipe-cms": "~1.1"
},
"prefer-stable": true,
"minimum-stability": "dev"
}
```
This composer file uses the new [recipe](https://github.com/silverstripe/recipe-plugin) approach
which bundles all core dependencies in a meta package. If you want more granular control over what gets installed,
check the `composer.json` files in [recipe-core](https://github.com/silverstripe/recipe-core) and [recipe-cms](https://github.com/silverstripe/recipe-cms).
Since this is a pre-release, you need to allow composer to install unstable dependencies via `minimum-stability: dev`.
Now run a `composer update`. This will remove all existing modules from your local codebase
since we replaced your project's `composer.json`. Now you can move back your modules
one by one, checking for compatible versions on [packagist.org](http://packagist.org).
For modules with stable releases, simply set your composer constraints to the new version
(with a [next significant release](https://getcomposer.org/doc/articles/versions.md#next-significant-release-operators) operator).
```diff
{
"require": {
- "myvendor/compatible-module": "~2.0",
+ "myvendor/compatible-module": "~3.0",
}
}
```
For modules with a compatible pre-release, use an explicit [stability constraints](https://getcomposer.org/doc/articles/versions.md#stability-constraints).
This can be changed to a [next significant release](https://getcomposer.org/doc/articles/versions.md#next-significant-release-operators) operator
once the module is stable.
```diff
{
"require": {
- "myvendor/prerelease-module": "~2.0",
+ "myvendor/prerelease-module": "~3.0@dev",
}
}
```
For modules that don't have a pre-release branch started,
you should raise an issue on the repository asking for 4.0 compatibility.
For now, you should attempt to continue the upgrade without the module
and temporarily disable its functionality.
### Install the upgrader tool
A lot of upgrade work can be automated, and we've written an
[upgrader tool](https://github.com/silverstripe/silverstripe-upgrader/) for this purpose.
Install it via composer:
```
composer global require silverstripe/upgrader
```
### index.php and .htaccess rewrites
The location of SilverStripe's "entry file" has changed. Your project and server environment will need The location of SilverStripe's "entry file" has changed. Your project and server environment will need
to adjust the path to this file from `framework/main.php` to `index.php`. to adjust the path to this file from `framework/main.php` to `index.php`.
@ -91,8 +156,7 @@ The [upgrader tool](https://github.com/silverstripe/silverstripe-upgrader/) has
automates this process for you. automates this process for you.
``` ```
composer global require silverstripe/upgrader cd ~/my-project-root
cd ~/Project/Root
~/.composer/vendor/bin/upgrade-code doctor ~/.composer/vendor/bin/upgrade-code doctor
``` ```
@ -101,21 +165,15 @@ for a clean installation. If you have applied customisations to your `.htaccess`
file (e.g. a custom `main.php`, HTTP header configuration, deny file access), file (e.g. a custom `main.php`, HTTP header configuration, deny file access),
you'll need to manually reapply these to the copied default file. you'll need to manually reapply these to the copied default file.
#### Upgrade references to renamed and namespaced classes ### Renamed and namespaced classes
Nearly all core PHP classes have been namespaced. For example, `DataObject` is now called `SilverStripe\ORM\DataObject`. Nearly all core PHP classes have been namespaced. For example, `DataObject` is now called `SilverStripe\ORM\DataObject`.
The below tasks describe how to upgrade an existing site to remain compatible with the newly upgraded classes. The below tasks describe how to upgrade an existing site to remain compatible with the newly upgraded classes.
##### Using the upgrader tool to automatically upgrade
This task should be run on every upgraded project.
We have developed an [upgrader tool](https://github.com/silverstripe/silverstripe-upgrader/) to (semi-)automatically We have developed an [upgrader tool](https://github.com/silverstripe/silverstripe-upgrader/) to (semi-)automatically
update your 3.x code to the new naming. Here's an example how to upgrade your `mysite` folder: update your 3.x code to the new naming. Here's an example how to upgrade your `mysite` folder:
``` ```
composer global require silverstripe/upgrader cd ~/my-project-root
cd ~/Project/Root
~/.composer/vendor/bin/upgrade-code upgrade ./mysite --write ~/.composer/vendor/bin/upgrade-code upgrade ./mysite --write
``` ```
@ -129,7 +187,7 @@ For a full list of renamed classes, check the `.upgrade.yml` definitions in each
The rename won't affect class-based permission codes or database table names. The rename won't affect class-based permission codes or database table names.
##### Using the upgrader tool to get upgrade tips on your code ### Get upgrade tips on your code
While there's some code we can automatically rewrite, other uses of changed SilverStripe APIs aren't that obvious. While there's some code we can automatically rewrite, other uses of changed SilverStripe APIs aren't that obvious.
You can use our heuristics to get some hints on where you need to review code manually. You can use our heuristics to get some hints on where you need to review code manually.
@ -143,15 +201,14 @@ This task should be run *after* `upgrade-code upgrade`.
These hints only cover a part of the upgrade work, These hints only cover a part of the upgrade work,
but can serve as a good indicator for where to start. but can serve as a good indicator for where to start.
##### Upgrade references to literal or class table names ### Rewrite literal table names
In 3.x the class name of any DataObject matched the table name, but in 4.x all classes are namespaced, and it is In 3.x the class name of any DataObject matched the table name, but in 4.x all classes are namespaced, and it is
necessary to map between table and class for querying the database. necessary to map between table and class for querying the database.
In order to ensure you are using the correct table for any class a new In order to ensure you are using the correct table for any class a new
[DataObjectSchema](api:SilverStripe\ORM\DataObjectSchema) service is available to manage these mappings [DataObjectSchema](api:SilverStripe\ORM\DataObjectSchema) service is available to manage these mappings
(see [versioned documentation](/developer_guides/model/data_model_and_orm)). (see [Versioned documentation](/developer_guides/model/data_model_and_orm)).
For example, the below shows how you would update a query with a hard-coded table name: For example, the below shows how you would update a query with a hard-coded table name:
```diff ```diff
@ -167,7 +224,7 @@ public function countDuplicates($model, $fieldToCheck)
} }
``` ```
##### Replacing literal strings with fully qualified class name, or ::class constant ### Rewrite literal class names
You'll need to update any strings that represent class names and make sure they're fully You'll need to update any strings that represent class names and make sure they're fully
qualified. In particular, relationship definitions such as `has_one` and `has_many` will need qualified. In particular, relationship definitions such as `has_one` and `has_many` will need
@ -197,20 +254,7 @@ In the context of YAML, the magic constant `::class` does not apply. Fully quali
property: value property: value
``` ```
##### Update references to literal classes to use Injector ### Move controllers to their own files
In many places, classes namespaced in SilverStripe 4.x will still have a non-namespaced service
name that can be accessed via Injector. You should upgrade direct object constructors with
the Injector API.
E.g.
```diff
- $field = new Varchar();
+ $field = Injector::inst()->create('Varchar');
```
#### Migrate your controllers to their own files
The convention for naming controllers is now `[MyPageType]Controller`, where it used to be `[MyPageType]_Controller`. This change was made to be more compatible with the PSR-2 standards. The convention for naming controllers is now `[MyPageType]Controller`, where it used to be `[MyPageType]_Controller`. This change was made to be more compatible with the PSR-2 standards.
@ -220,10 +264,10 @@ other thirdparty code that extend `PageController` are likely to assume that cla
By default, a controller for a page type *must* reside in the same namespace as its page. To use different logic, override `SiteTree::getControllerName()`. By default, a controller for a page type *must* reside in the same namespace as its page. To use different logic, override `SiteTree::getControllerName()`.
#### Upgrade template locations and references ### Template locations and references
Templates are now much more strict about their locations. You can no longer put a template in an arbitrary Templates are now more strict about their locations.
folder and have it be found. Case is now also checked on case-sensitive filesystems. Case is now also checked on case-sensitive filesystems.
Either include the folder in the template name (`renderWith('MyEmail.ss')` => `renderWith('emails/MyEmail.ss')`), Either include the folder in the template name (`renderWith('MyEmail.ss')` => `renderWith('emails/MyEmail.ss')`),
move the template into the correct directory, or both. move the template into the correct directory, or both.
@ -233,8 +277,6 @@ Core template locations have moved - if you're including or overriding these
no longer exists, and instead template locations will be placed in paths that match no longer exists, and instead template locations will be placed in paths that match
the `SilverStripe\Forms` namespace. the `SilverStripe\Forms` namespace.
#### Upgrade <% include %>
When using `<% include %>` template tag you can continue to leave out the `Includes` folder, When using `<% include %>` template tag you can continue to leave out the `Includes` folder,
but this now will also search templates in the base folder if no Include can be found. but this now will also search templates in the base folder if no Include can be found.
@ -243,20 +285,9 @@ if the former is not present.
Please refer to our [template syntax](/developer_guides/templates/syntax) for details. Please refer to our [template syntax](/developer_guides/templates/syntax) for details.
#### Upgrade static config settings to `private static` ### Config settings should be set to `private static`
If you have some class configuration statics defined and they aren't private, Class configuration defined as `static` properties need to be marked as `private` to take effect:
you may find that they don't register anymore. For example, this code, taken from
the `silverstripe/tagfield` module will no longer work in SilverStripe 4.0.
```php
public static $allowed_actions = [
'suggest'
];
```
Changing the visibility to `private` (as per `RequestHandler::$allowed_actions`
visibility) will make it 4.0 compatible.
```diff ```diff
-public static $allowed_actions = [ -public static $allowed_actions = [
@ -265,7 +296,7 @@ visibility) will make it 4.0 compatible.
]; ];
``` ```
#### <a name="module-paths"></a>Upgrade module paths in file references ### <a name="module-paths"></a>Module paths can't be hardcoded
You should no longer rely on modules being placed in a deterministic folder (e.g. `/framework`), You should no longer rely on modules being placed in a deterministic folder (e.g. `/framework`),
and use getters on the [Module](api:SilverStripe\Core\Manifest\Module) object instead. and use getters on the [Module](api:SilverStripe\Core\Manifest\Module) object instead.
@ -274,44 +305,41 @@ This prepares SilverStripe for moving modules out of the webroot at a later poin
Usage in templates: Usage in templates:
```ss ```diff
<!-- before --> -<img src="framework/images/image.png" />
<img src="framework/images/image.png" /> +<img src="$ModulePath(silverstripe/framework)/image.png" />
<% require css("framework/css/styles.css") %>
<!-- after --> -<% require css("framework/css/styles.css") %>
<img src="$ModulePath(silverstripe/framework)/image.png" /> +<% require css("silverstripe/framework: css/styles.css") %>
<% require css("silverstripe/framework: css/styles.css") %>
``` ```
Usage in Requirements: Usage in Requirements:
```php ```diff
// before -Requirements::css('framework/css/styles.css');
Requirements::css('framework/css/styles.css'); +Requirements::css('silverstripe/framework: css/styles.css');
// after
Requirements::css('silverstripe/framework: css/styles.css');
``` ```
Usage with custom logic: Usage with custom logic:
```php ```diff
// before +use SilverStripe\Core\Manifest\ModuleLoader;
$moduleFilePath = FRAMEWORK_DIR . '/MyFile.php'; +use SilverStripe\View\ThemeResourceLoader;
$baseFilePath = BASE_PATH . '/composer.json';
$mysiteFilePath = 'mysite/css/styles.css';
$themesFilePath = SSViewer::get_theme_folder() . '/css/styles.css';
$themeFolderPath = THEMES_DIR . '/simple';
// after -$moduleFilePath = FRAMEWORK_DIR . '/MyFile.php';
use SilverStripe\Core\Manifest\ModuleLoader; +$moduleFilePath = ModuleLoader::getModule('silverstripe/framework')->getRelativeResourcePath('MyFile.php');
use SilverStripe\View\ThemeResourceLoader;
$moduleFilePath = ModuleLoader::getModule('silverstripe/framework')->getRelativeResourcePath('MyFile.php'); -$baseFilePath = BASE_PATH . '/composer.json';
$baseFilePath = Director::baseFolder() . '/composer.json'; +$baseFilePath = Director::baseFolder() . '/composer.json';
$mysiteFilePath = ModuleLoader::getModule('mysite')->getRelativeResourcePath('css/styles.css');
$themesFilePath = ThemeResourceLoader::inst()->findThemedResource('css/styles.css'); -$mysiteFilePath = 'mysite/css/styles.css';
$themeFolderPath = ThemeResourceLoader::inst()->getPath('simple'); +$mysiteFilePath = ModuleLoader::getModule('mysite')->getRelativeResourcePath('css/styles.css');
-$themesFilePath = SSViewer::get_theme_folder() . '/css/styles.css';
+$themesFilePath = ThemeResourceLoader::inst()->findThemedResource('css/styles.css');
-$themeFolderPath = THEMES_DIR . '/simple';
+$themeFolderPath = ThemeResourceLoader::inst()->getPath('simple');
``` ```
Usage for Page and ModelAdmin: Usage for Page and ModelAdmin:
@ -330,14 +358,15 @@ class MyCustomModelAdmin extends \SilverStripe\Admin\ModelAdmin {
To ensure consistency, we've also deprecated support for path constants: To ensure consistency, we've also deprecated support for path constants:
* `FRAMEWORK_DIR` and `FRAMEWORK_PATH` * Deprecated `FRAMEWORK_DIR` and `FRAMEWORK_PATH`
* `FRAMEWORK_ADMIN_DIR` and `FRAMEWORK_ADMIN_PATH` * Deprecated `FRAMEWORK_ADMIN_DIR` and `FRAMEWORK_ADMIN_PATH`
* `FRAMEWORK_ADMIN_THIRDPARTY_DIR` and `FRAMEWORK_ADMIN_THIRDPARTY_PATH` * Deprecated `FRAMEWORK_ADMIN_THIRDPARTY_DIR` and `FRAMEWORK_ADMIN_THIRDPARTY_PATH`
* `THIRDPARTY_DIR` and `THIRDPARTY_PATH` * Deprecated `THIRDPARTY_DIR` and `THIRDPARTY_PATH`
* `CMS_DIR` and `CMS_PATH` * Deprecated `CMS_DIR` and `CMS_PATH`
* `THEMES_DIR` and `THEMES_PATH` * Deprecated `THEMES_DIR` and `THEMES_PATH`
* Deprecated `MODULES_PATH` and `MODULES_DIR`
#### Adapt tooling to modules in vendor folder ### Adapt tooling to modules in vendor folder
SilverStripe modules can now be installed like any other composer package: In the `vendor/` folder SilverStripe modules can now be installed like any other composer package: In the `vendor/` folder
instead of the webroot. Modules need to opt in to this behaviour after they've ensured instead of the webroot. Modules need to opt in to this behaviour after they've ensured
@ -347,10 +376,10 @@ All core modules have been moved over already:
```diff ```diff
-framework/ -framework/
+vendor/silverstripe/framework/
-cms/ -cms/
+vendor/silverstripe/cms/
... ...
+vendor/silverstripe/framework
+vendor/silverstripe/cms
``` ```
Since the `vendor/` folder isn't publicly accessible, modules need to declare Since the `vendor/` folder isn't publicly accessible, modules need to declare
@ -363,12 +392,7 @@ and this environment supports symlinks, you don't need to change anything.
If you deploy release archives, either ensure those archives can correctly extract symlinks, If you deploy release archives, either ensure those archives can correctly extract symlinks,
or explicitly switch to the "copy" mode to avoid symlinks. or explicitly switch to the "copy" mode to avoid symlinks.
### <a name="upgrading-specifics"></a>API Specific Upgrades ### <a name="psr3-logging"></a>SS_Log replaced with PSR-3 logging
The below sections deal with upgrades to specific parts of various API. Projects which rely on certain
API should be upgraded as appropriate using any of the relevant processes documented below.
#### <a name="psr3-logging"></a>Upgrade references of SS_Log to use PSR-3 logging
One of the great changes that comes with SilverStripe 4 is the introduction of One of the great changes that comes with SilverStripe 4 is the introduction of
[PSR-3](http://www.php-fig.org/psr/psr-3/) compatible logger interfaces. This [PSR-3](http://www.php-fig.org/psr/psr-3/) compatible logger interfaces. This
@ -383,8 +407,6 @@ For instance, code which logs errors should be upgraded as below:
+Injector::inst()->get(LoggerInterface::class)->error('My error message'); +Injector::inst()->get(LoggerInterface::class)->error('My error message');
``` ```
##### How to customise the default PSR-3 logger
If necessary, you may need to customise either the default logging handler, or If necessary, you may need to customise either the default logging handler, or
one of the error formatters. For example, if running unit tests you may want to one of the error formatters. For example, if running unit tests you may want to
suppress errors. You can temporarily disable logging by setting a `NullHandler` suppress errors. You can temporarily disable logging by setting a `NullHandler`
@ -423,12 +445,12 @@ SilverStripe\Core\Injector\Injector:
`WebDesignGroup\ShopSite\Logging\ErrorPageFormatter` should be a class that `WebDesignGroup\ShopSite\Logging\ErrorPageFormatter` should be a class that
implements the `Monolog\Formatter\FormatterInterface` interface. implements the `Monolog\Formatter\FormatterInterface` interface.
#### Upgrade `mysite/_config.php` ### Upgrade `mysite/_config.php`
The globals `$database` and `$databaseConfig` are deprecated. You should upgrade your The globals `$database` and `$databaseConfig` are deprecated. You should upgrade your
site `_config.php` files to use the `.env` configuration (below). site `_config.php` files to use the [.env configuration](#env)
`conf/ConfigureFromEnv.php` is also no longer used, and references to this file should be deleted. `conf/ConfigureFromEnv.php` is no longer used, and references to this file should be deleted.
If you need to configure database details in PHP you should configure these details via `.env` file, If you need to configure database details in PHP you should configure these details via `.env` file,
or alternatively (but less recommended) use the new `DB::setConfig()` api. or alternatively (but less recommended) use the new `DB::setConfig()` api.
@ -452,7 +474,7 @@ SilverStripe\Core\Manifest\ModuleManifest:
project: mysite project: mysite
``` ```
#### Upgrade of `_ss_environment.php` to `.env` configuration ### <a name="env"></a>`_ss_environment.php` changed to`.env`
The php configuration `_ss_environment.php` file has been replaced in favour of a non-executable The php configuration `_ss_environment.php` file has been replaced in favour of a non-executable
`.env` file, which follows a syntax similar to an `.ini` file for key/value pair assignment. Like `.env` file, which follows a syntax similar to an `.ini` file for key/value pair assignment. Like
@ -518,20 +540,15 @@ To access environment variables you can use the `SilverStripe\Core\Environment::
See [Environment Management docs](/getting-started/environment_management/) for full details. See [Environment Management docs](/getting-started/environment_management/) for full details.
#### <a name="object-replace"></a>Replace usages of Object class ### <a name="object-replace"></a>Object class replaced by traits
Object has been superseded by a trio of traits which replace components of this legacy class: Object has been superseded by traits.
- Injectable: Provides `MyClass::create()` and `MyClass::singleton()` - `Injectable`: Provides `MyClass::create()` and `MyClass::singleton()`
- Configurable: Provides `MyClass::config()` - `Configurable`: Provides `MyClass::config()`
- Extensible: Provides all methods related to extensions (E.g. add_extension()). - `Extensible`: Provides all methods related to extensions (E.g. add_extension()).
All custom method and extension instances are constructed lazily. Upgrade subclass use
In particular specific Object class usages should be replaced as below:
Upgrade subclasses
```diff ```diff
@ -550,7 +567,7 @@ Upgrade subclasses
``` ```
References to $this->class Upgrade references to $this->class
```diff ```diff
-$obj->class -$obj->class
@ -575,7 +592,7 @@ Upgrade create_from_string()
+$obj = Injector::inst()->create('Varchar(100)'); +$obj = Injector::inst()->create('Varchar(100)');
``` ```
Extensions Upgrade extension use
```diff ```diff
@ -592,20 +609,17 @@ Extensions
+$extensions = DataObject::get_extensions(File::class); // alternate +$extensions = DataObject::get_extensions(File::class); // alternate
``` ```
#### <a name="session"></a>Upgrade references to Session object ### <a name="session"></a>Session object removes static methods
Session object is no longer statically accessible via `Session::inst()`. Instead, Session Session object is no longer statically accessible via `Session::inst()`. Instead, Session
is a member of the current request. is a member of the current request.
```diff ```diff
-public function httpSubmission($data, $form, $request) public function httpSubmission($data, $form, $request)
-{ {
- Session::set('loggedIn', null); - Session::set('loggedIn', null);
-}
+public function httpSubmission($data, $form, $request)
+{
+ $request->getSession()->set('loggedIn', null); + $request->getSession()->set('loggedIn', null);
+} }
``` ```
@ -613,16 +627,14 @@ In some places it may still be necessary to access the session object where no r
In rare cases it is still possible to access the request of the current controller via In rare cases it is still possible to access the request of the current controller via
`Controller::curr()->getRequest()` to gain access to the current session. `Controller::curr()->getRequest()` to gain access to the current session.
#### Upgrade extensions to work as singletons ### Extensions are now singletons
In SilverStripe 4.0 extensions are now all singletons, meaning that state stored as protected vars Extensions are now all singletons, meaning that state stored as protected vars
within extensions are now shared across all object instances that use this extension. within extensions are now shared across all object instances that use this extension.
As a result, you must take care that all protected vars are either refactored to be stored against As a result, you must take care that all protected vars are either refactored to be stored against
the owner object, or are keyed in a fashion distinct to the parent. the owner object, or are keyed in a fashion distinct to the parent.
E.g.
```diff ```diff
class MyExtension extends Extension { class MyExtension extends Extension {
public function getContent() { public function getContent() {
@ -640,11 +652,9 @@ class MyExtension extends Extension {
} }
``` ```
In addition when using extensions with distinct constructor arguments it's advisable to When using extensions with distinct constructor arguments it's advisable to
use `yml` to register those constructor arguments and use a service name or alias in use `yml` to register those constructor arguments and use a service name or alias in
`private static $extensions`. `private static $extensions`. Please review the default service definitions below:
E.g. This is the service definition for the various versioned extension variants
```yaml ```yaml
--- ---
@ -665,7 +675,7 @@ SilverStripe\Core\Injector\Injector:
SilverStripe\Versioned\Versioned: '%$SilverStripe\Versioned\Versioned.stagedversioned' SilverStripe\Versioned\Versioned: '%$SilverStripe\Versioned\Versioned.stagedversioned'
``` ```
Upgrade your extension references as below: Upgrade your extension references:
```diff ```diff
class MyClass extends DataObject { class MyClass extends DataObject {
@ -676,15 +686,7 @@ class MyClass extends DataObject {
} }
``` ```
#### Compatibility with the new front-end building tools ### Static references to asset paths
If you are using Requirements from 3.x then your scripts should continue to work as they did before.
The new front-end tooling is intended only for those wishing to customise the CMS look and feel, or behaviour.
Those wishing to customise the CMS should read about how to
[customise the admin interface](/developer_guides/customising_the_admin_interface/).
#### Upgrade static references to asset paths
All static files (images, javascript, stylesheets, fonts) used for the CMS and forms interfaces All static files (images, javascript, stylesheets, fonts) used for the CMS and forms interfaces
in `framework` and `cms` have moved locations. These assets are now placed in a `client/` subfolder, in `framework` and `cms` have moved locations. These assets are now placed in a `client/` subfolder,
@ -723,8 +725,8 @@ through your own transpiling and bundle process.
This also includes JavaScript i18n support, and the removal of the `i18n::js_i18n` This also includes JavaScript i18n support, and the removal of the `i18n::js_i18n`
configuration option used in `Requirements::add_i18n_javascript()`. configuration option used in `Requirements::add_i18n_javascript()`.
SilverStripe core is moving away from `Requirements::combine_files` in favour of Webpack as of The CMS UI is moving away from `Requirements::combine_files()` in favour of Webpack.
4.0. `Requirements::combine_files` is being considered for deprecation in future versions. This method is being considered for deprecation in future versions.
All JavaScript thirdparty dependencies have either been moved to NPM (see `package.json`), All JavaScript thirdparty dependencies have either been moved to NPM (see `package.json`),
or moved into the `framework/admin/thirdparty` folder. If you are hotlinking to any or moved into the `framework/admin/thirdparty` folder. If you are hotlinking to any
@ -738,7 +740,10 @@ One commonly linked thirdparty dependency is `jquery.js` bundled with SilverStri
framework/thirdparty/jquery/jquery.js => framework/admin/thirdparty/jquery/jquery.js framework/thirdparty/jquery/jquery.js => framework/admin/thirdparty/jquery/jquery.js
``` ```
#### Explicit text casting is now enforced on all template variables If you have customised the CMS UI (via JavaScript or CSS), please read our guide to
[customise the admin interface](/developer_guides/customising_the_admin_interface/).
### Explicit text casting on template variables
Now whenever a `$Variable` is used in a template, regardless of whether any casts or methods are Now whenever a `$Variable` is used in a template, regardless of whether any casts or methods are
suffixed to the reference, it will be cast to either an explicit DBField for that field, or suffixed to the reference, it will be cast to either an explicit DBField for that field, or
@ -751,63 +756,42 @@ explicitly cast for that field.
You can resolve this in your model by adding an explicit cast to HTML for those fields. You can resolve this in your model by adding an explicit cast to HTML for those fields.
Before: ```diff
```php
use SilverStripe\View\ViewableData; use SilverStripe\View\ViewableData;
use SilverStripe\Core\Convert; use SilverStripe\Core\Convert;
class MyObject extends ViewableData class MyObject extends ViewableData
{ {
+ private static $casting = [
+ 'SomeHTML' => 'HTMLText'
+ ];
public function getSomeHTML public function getSomeHTML
{ {
$title = Convert::raw2xml($this->Title); - $title = Convert::raw2xml($this->Title);
+ $title = Convert::raw2xml($this->Title);
return "<h1>{$title}</h1>"; return "<h1>{$title}</h1>";
} }
} }
``` ```
After: If you need to encode a field (such as `HTMLText`) for use in HTML attributes, use `.ATT`
```php
use SilverStripe\Core\Convert;
class MyObject extends SilverStripe\View\ViewableData
{
private static $casting = [
'SomeHTML' => 'HTMLText'
];
public function getSomeHTML
{
$title = Convert::raw2xml($this->Title);
return "<h1>{$title}</h1>";
}
}
```
If you need to encode a field (such as HTMLText) for use in html attributes, use `.ATT`
instead, or if used in an actual XML file use `.CDATA` (see [template casting](/developer_guides/templates/casting)). instead, or if used in an actual XML file use `.CDATA` (see [template casting](/developer_guides/templates/casting)).
#### <a name="sqlquery"></a>Upgrade code that uses SQLQuery ### <a name="uploadfield"></a>Replace UploadField with injected service
Where your code once used SQLQuery you should now use SQLSelect in all cases, as this has been removed (check the [3.2.0](3.2.0) upgrading notes).
#### <a name="uploadfield"></a>Upgrade code that uses UploadField
This field has been superceded by a new class provided by the This field has been superceded by a new class provided by the
[asset-admin](https://github.com/silverstripe/silverstripe-asset-admin) module, which provides a more [asset-admin](https://github.com/silverstripe/silverstripe-asset-admin) module, which provides a more
streamlined simpler mechanism for uploading File dataobjects. streamlined simpler mechanism for uploading `File` dataobjects.
A helper service `FileHandleField` is provided to assist with dependency injection. Where the asset-admin A helper service `FileHandleField` is provided to assist with dependency injection. Where the asset-admin
module is not installed this service will fall back to the `FileField` class instead. module is not installed this service will fall back to the `FileField` class instead.
Usages of UploadField will need to be upgraded as below. Usages of `UploadField` will need to be upgraded as below.
Before: ```diff
```php
use SilverStripe\Forms\FieldList; use SilverStripe\Forms\FieldList;
use SilverStripe\AssetAdmin\Forms\UploadField; -use SilverStripe\AssetAdmin\Forms\UploadField;
+use SilverStripe\Forms\FileHandleField;
use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DataObject;
class MyClass extends DataObject class MyClass extends DataObject
@ -815,45 +799,27 @@ class MyClass extends DataObject
public function getCMSFields() public function getCMSFields()
{ {
return new FieldList( return new FieldList(
new UploadField('Files') - new UploadField('Files')
+ Injector::inst()->create(FileHandleField::class, 'Files')
); );
} }
} }
``` ```
### <a name="i18n"></a>i18n placeholders, plurals and i18nEntityProvider
After:
```php
use SilverStripe\ORM\DataObject;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\FileHandleField;
class MyClass extends DataObject
{
public function getCMSFields()
{
return FieldList::create(
Injector::inst()->create(FileHandleField::class, 'Files')
);
}
}
```
#### Upgrade code that uses i18n
In many cases, localisation strings which worked in 3.x will continue to work in 4.0, however certain patterns In many cases, localisation strings which worked in 3.x will continue to work in 4.0, however certain patterns
have been deprecated and will be removed in 5.0. These include: have been deprecated and will be removed in 5.0. These include:
- _t calls with sprintf-style placeholders (`%s`). Replace with named placeholders instead. - `_t()` calls with sprintf-style placeholders (`%s`). Replace with named placeholders instead.
- _t calls with non-associative injection arguments. Please use an associative array for all arguments. - `_t()` calls with non-associative injection arguments. Please use an associative array for all arguments.
- _t calls which do not include a default value will now raise a warning. This can be disabled by setting - `_t()` calls which do not include a default value will now raise a warning. This can be disabled by setting
the `i18n.missing_default_warning` config to false. the `i18n.missing_default_warning` config to false.
Note: If you attempt to use non-associative injection arguments with named placeholders, the result will If you attempt to use non-associative injection arguments with named placeholders, the result will
now trigger an exception. now trigger an exception.
Implementors of i18nEntityProvider should note that the return type for provideI18nEntities() has changed as well. Implementors of `i18nEntityProvider` should note that the return type for `provideI18nEntities()` has changed as well.
The non-associative array return type is deprecated. If returning a default string for a module The non-associative array return type is deprecated. If returning a default string for a module
other than itself, it should return an array with the `default` and `module` keys respectively. other than itself, it should return an array with the `default` and `module` keys respectively.
@ -915,8 +881,8 @@ en:
``` ```
Usage of these pluralised strings is through the existing _t() method, Usage of these pluralised strings is through the existing `_t()` method,
and require a `|` pipe-delimeter with a {count} argument. and require a `|` pipe-delimeter with a `{count}` argument.
```php ```php
@ -933,7 +899,7 @@ In templates this can also be invoked as below:
<%t MyObject.PLURALS 'An item|{count} items' count=$Count %> <%t MyObject.PLURALS 'An item|{count} items' count=$Count %>
``` ```
#### Removed Member.DateFormat and Member.TimeFormat database settings ### Removed Member.DateFormat and Member.TimeFormat database settings
We're using [native HTML5 date and time pickers](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date) We're using [native HTML5 date and time pickers](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date)
in `DateField` and `TimeField` now ([discussion](https://github.com/silverstripe/silverstripe-framework/issues/6626)), in `DateField` and `TimeField` now ([discussion](https://github.com/silverstripe/silverstripe-framework/issues/6626)),
@ -946,7 +912,7 @@ Consequently, we've also removed `MemberDatetimeOptionsetField`.
the [IntlDateFormatter defaults](http://php.net/manual/en/class.intldateformatter.php) for the selected locale. the [IntlDateFormatter defaults](http://php.net/manual/en/class.intldateformatter.php) for the selected locale.
#### <a name="asset-storage"></a>New asset storage mechanism ### <a name="asset-storage"></a>New asset storage mechanism
File system has been abstracted into an abstract interface. By default, the out of the box filesystem File system has been abstracted into an abstract interface. By default, the out of the box filesystem
uses [Flysystem](http://flysystem.thephpleague.com/) with a local storage mechanism (under the assets directory). uses [Flysystem](http://flysystem.thephpleague.com/) with a local storage mechanism (under the assets directory).
@ -971,23 +937,18 @@ Depending on your server configuration, it may also be necessary to adjust your
permissions. Please see the [common installation problems](/getting_started/installation/common_problems) permissions. Please see the [common installation problems](/getting_started/installation/common_problems)
guide for configuration instruction. guide for configuration instruction.
#### Migrating File DataObject from 3.x to 4.0 ### Migrate File DataObject
Since the structure of `File` dataobjects has changed between 3.0 and 4.0, a new task `MigrateFileTask` Since the structure of `File` dataobjects has changed, a new task `MigrateFileTask`
has been added to assist in migration of legacy files. has been added to assist in migration of legacy files (see [file migration documentation](/developer_guides/files/file_migration)).
You can run this task on the command line:
``` ```
$ ./framework/sake dev/tasks/MigrateFileTask $ ./framework/sake dev/tasks/MigrateFileTask
``` ```
For more information on this task please consult the [file migration documentation](/developer_guides/files/file_migration). Any `File` dataobject which is not in the `File.allowed_extensions` config will be deleted
Note that any File dataobject which is not in the `File.allowed_extensions` config will be deleted
from the database during migration. Any invalid file on the filesystem will not be deleted, from the database during migration. Any invalid file on the filesystem will not be deleted,
but will no longer be attached to a dataobject anymore, and should be cleaned up manually. but will no longer be attached to a dataobject, and should be cleaned up manually.
To disable this, set the following config: To disable this, set the following config:
```yaml ```yaml
@ -995,7 +956,7 @@ SilverStripe\Assets\FileMigrationHelper:
delete_invalid_files: false delete_invalid_files: false
``` ```
#### Upgrade code which acts on `Image` ### Image handling
As all image-specific manipulations has been refactored from `Image` into an `ImageManipulations` trait, which As all image-specific manipulations has been refactored from `Image` into an `ImageManipulations` trait, which
is applied to both `File` and `DBFile`. These both implement a common interface `AssetContainer`, which is applied to both `File` and `DBFile`. These both implement a common interface `AssetContainer`, which
@ -1035,7 +996,7 @@ class MyObject extends SilverStripe\ORM\DataObject
} }
``` ```
#### <a name="write-file-dataobject"></a>Upgrading code that writes to `File` dataobjects, or writes files to the 'assets' folder ### <a name="write-file-dataobject"></a>Writing to `File` dataobjects or the assets folder
In the past all that was necessary to write a `File` DataObject to the database was to ensure a physical file In the past all that was necessary to write a `File` DataObject to the database was to ensure a physical file
existed in the assets folder, and that the Filename of the DataObject was set to the same location. existed in the assets folder, and that the Filename of the DataObject was set to the same location.
@ -1082,7 +1043,7 @@ You can disable File versioning by adding the following to your `_config.php`
SilverStripe\Assets\File::remove_extension('Versioned'); SilverStripe\Assets\File::remove_extension('Versioned');
``` ```
#### Upgrading code performs custom image manipulations ### Custom image manipulations
As file storage and handling has been refactored into the abstract interface, many other components which were As file storage and handling has been refactored into the abstract interface, many other components which were
once specific to Image.php have now been moved into a shared `ImageManipulation` trait. Manipulations of file content, once specific to Image.php have now been moved into a shared `ImageManipulation` trait. Manipulations of file content,
@ -1150,47 +1111,32 @@ There are a few differences in this new API:
A generic `manipulate` method may be used, although the callback for this method both is given, and should return, A generic `manipulate` method may be used, although the callback for this method both is given, and should return,
an `AssetStore` instance and file tuple (Filename, Hash, and Variant) rather than an Image_Backend. an `AssetStore` instance and file tuple (Filename, Hash, and Variant) rather than an Image_Backend.
#### <a name="file-shortcode"></a>Upgrading code that uses File or Image shortcode handler ### <a name="file-shortcode"></a>File or Image shortcode handler
The `handle_shortcode` methods have been removed from the core File and Image classes and moved to separate classes in their own respective namespace. Image and File do not implement the ShortcodeHandler interface anymore. The `handle_shortcode` methods have been removed from the core File and Image classes
and moved to separate classes in their own respective namespace.
`Image` and `File` do not implement the `ShortcodeHandler` interface anymore.
The shortcode handler for `File` has been moved to
`SilverStripe\Assets\ShortcodesFileShortcodeProvider` and
the Image handler has been moved to `SilverStripe\Assets\Shortcodes\ImageShortcodeProvider`
The shortcode handler for `File` has been moved to `SilverStripe\Assets\ShortcodesFileShortcodeProvider` and the Image handler has been moved to `SilverStripe\Assets\Shortcodes\ImageShortcodeProvider` Example of changed shortcode handling:
Before this change, to use the handle_shortcode method, you would do something like this:
```
<?php
```diff
+use SilverStripe\Assets\Shortcodes\FileShortcodeProvider;
class MyShortcodeUser extends Object class MyShortcodeUser extends Object
{ {
private $content; private $content;
public function Content($arguments, $parser, $shortcode) public function Content($arguments, $parser, $shortcode)
{ {
return File::handle_shortcode($arguments, $this->content, $parser, $shortcode); - return File::handle_shortcode($arguments, $this->content, $parser, $shortcode);
+ return FileShortcodeProvider::handle_shortcode($arguments, $this->content, $parser, $shortcode);
} }
} }
``` ```
In the new situation, this would look like this: ### <a name="compositedbfield"></a>Composite db fields
```
<?php
use SilverStripe\Assets\Shortcodes\FileShortcodeProvider;
class MyShortcodeUser extends Object
{
private $content;
public function Content($arguments, $parser, $shortcode)
{
return FileShortcodeProvider::handle_shortcode($arguments, $this->content, $parser, $shortcode);
}
}
```
#### <a name="compositedbfield"></a>Upgrading code that uses composite db fields.
The `CompositeDBField` interface has been replaced with an abstract class, `DBComposite`. In many cases, custom code The `CompositeDBField` interface has been replaced with an abstract class, `DBComposite`. In many cases, custom code
that handled saving of content into composite fields can be removed, as it is now handled by the base class. that handled saving of content into composite fields can be removed, as it is now handled by the base class.
@ -1216,7 +1162,7 @@ class MyAddressField extends
} }
``` ```
#### <a name="dataobject-db-database-fields"></a>Upgrading code that references `DataObject::database_fields` or `DataObject::db` ### <a name="dataobject-db-database-fields"></a>`DataObject::database_fields` or `DataObject::db`
These methods have been updated to include base fields (such as ID, ClassName, Created, and LastEdited), as These methods have been updated to include base fields (such as ID, ClassName, Created, and LastEdited), as
well as composite DB fields. well as composite DB fields.
@ -1232,18 +1178,25 @@ when set to true (with a field name as the first parameter), will also include t
`Table.ClassName(args)` format. `Table.ClassName(args)` format.
#### Upgrade code that uses SQLQuery ### <a name="sqlquery"></a>Rewrite SQLQuery to more specific classes
SQLQuery is still implemented, but now extends the new SQLSelect class and has some methods Instead of `SQLQuery`, you should now use `SQLSelect`, `SQLUpdate`, `SQLInsert`
deprecated. Previously this class was used for both selecting and deleting, but these or `SQLDelete` - check the [3.2.0](3.2.0#sqlquery) upgrading notes for details.
have been superceded by the specialised SQLSelect and SQLDelete classes.
Take care for any code or functions which expect an object of type `SQLQuery`, as Example:
these references should be replaced with `SQLSelect`. Legacy code which generates
`SQLQuery` can still communicate with new code that expects `SQLSelect` as it is a
subclass of `SQLSelect`, but the inverse is not true.
#### <a name="buildtask-segment"></a>Upgrade BuildTask classes ```diff
-function augmentSQL(SQLQuery &$query, DataQuery &$dataQuery = null)
+public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null)
{
if(!preg_match('/MyField/', implode(' ', $query->getWhere()))) {
- $query->addWhere('"MyField" = 'foo');
+ $query->addWhere(['"MyField"' => 'foo']);
}
}
```
### <a name="buildtask-segment"></a>Upgrade BuildTask classes
Similarly to the `$table_name` configuration property for DataObjects, you should define a `private static $segment` for `BuildTask` Similarly to the `$table_name` configuration property for DataObjects, you should define a `private static $segment` for `BuildTask`
instances to ensure that you can still run your task via `sake dev/tasks/MyTask`. Without defining it, the default instances to ensure that you can still run your task via `sake dev/tasks/MyTask`. Without defining it, the default
@ -1258,39 +1211,7 @@ class MyTask extends BuildTask
} }
``` ```
#### Upgrade implementations of augmentSQL ### Moved ErrorPage into a new module
Since this method now takes a `SQLSelect` as a first parameter, existing code referencing the deprecated `SQLQuery`
type will raise a PHP error.
Before:
```php
function augmentSQL(SQLQuery &$query, DataQuery &$dataQuery = null)
{
$locale = Translatable::get_current_locale();
if(!preg_match('/("|\'|`)Locale("|\'|`)/', implode(' ', $query->getWhere()))) {
$qry = sprintf('"Locale" = \'%s\'', Convert::raw2sql($locale));
$query->addWhere($qry);
}
}
```
After:
```php
public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null)
{
$locale = Translatable::get_current_locale();
if (!preg_match('/("|\'|`)Locale("|\'|`)/', implode(' ', $query->getWhereParameterised($parameters)))) {
$query->addWhere([
'"Locale"' => $locale
]);
}
}
```
#### Moved ErrorPage into a new module
ErrorPage has been moved to a separate [silverstripe/errorpage module](http://addons.silverstripe.org/add-ons/silverstripe/errorpage) ErrorPage has been moved to a separate [silverstripe/errorpage module](http://addons.silverstripe.org/add-ons/silverstripe/errorpage)
to allow for alternative approaches to managing error responses. to allow for alternative approaches to managing error responses.
@ -1304,7 +1225,7 @@ By default, SilverStripe will display a plaintext "not found" message when the m
Check the [module upgrading guide](http://addons.silverstripe.org/add-ons/silverstripe/errorpage) Check the [module upgrading guide](http://addons.silverstripe.org/add-ons/silverstripe/errorpage)
for more configuration API changes on the `ErrorPage` class. for more configuration API changes on the `ErrorPage` class.
#### Upgrading asset web.config, .htaccess, or other server configuration ### Server configuration files for assets
Server configuration files for `/assets` are no longer static, and are regenerated via a set of Server configuration files for `/assets` are no longer static, and are regenerated via a set of
standard SilverStripe templates on flush. These templates include: standard SilverStripe templates on flush. These templates include:
@ -1325,7 +1246,7 @@ If upgrading from an existing installation, make sure to invoke `?flush=all` at
See our ["File Security" guide](/developer_guides/files/file_security) for more information. See our ["File Security" guide](/developer_guides/files/file_security) for more information.
#### `ListboxField` is now multiple-only ### `ListboxField` is now multiple-only
Previously, this field would operate as either a single select (default) or multi-select by setting Previously, this field would operate as either a single select (default) or multi-select by setting
`setMultiple` to either true or false. `setMultiple` to either true or false.
@ -1333,19 +1254,19 @@ Previously, this field would operate as either a single select (default) or mult
Now this field should only be used for multi-selection. Single-selection should be done using Now this field should only be used for multi-selection. Single-selection should be done using
a regular `DropdownField`. a regular `DropdownField`.
#### `GroupedDropdownField::setDisabled` now only accepts a list of values. ### `GroupedDropdownField::setDisabled` now only accepts a list of values.
Where previously you could specify a list of grouped values in the same way as `setSource`, this Where previously you could specify a list of grouped values in the same way as `setSource`, this
method now only accepts either a non-associative array of values (not titles) or an `SS_List` method now only accepts either a non-associative array of values (not titles) or an `SS_List`
of items to disable. of items to disable.
#### Upgrading TinyMCE to 4.0 ### <a name="tinymce"></a>TinyMCE v4
Please see the [tinymce upgrading guide](http://archive.tinymce.com/wiki.php/Tutorial:Migration_guide_from_3.x) Please see the [tinymce upgrading guide](http://archive.tinymce.com/wiki.php/Tutorial:Migration_guide_from_3.x)
to assist with upgrades to customisations to tinymce 3. to assist with upgrades to customisations to TinyMCE v3.
In Framework 4.0 the user interface for TinyMCE has been trimmed down considerably, with certain toolbar In Framework 4.0 the user interface for TinyMCE has been trimmed down considerably, with certain toolbar
buttons removed from the default cms configuration. These include: buttons removed from the default cms configuration:
* Strikethrough * Strikethrough
* Styles dropdown * Styles dropdown
@ -1357,7 +1278,7 @@ buttons removed from the default cms configuration. These include:
* Fullscreen * Fullscreen
However, these function may be enabled on a case by case basis through modifification of the default However, these function may be enabled on a case by case basis through modifification of the default
tinymce config, or by creating custom configurations. tinymce config, or by creating custom configurations (check [TinyMCE documentation](https://www.tinymce.com/docs/configure/)).
The optional `ss_macron` plugin for inserting Māori diacritical marks The optional `ss_macron` plugin for inserting Māori diacritical marks
has been removed from core. You can configure the built-in `charmap` plugin instead: has been removed from core. You can configure the built-in `charmap` plugin instead:
@ -1380,10 +1301,7 @@ $editor->setOption('charmap_append', [
]); ]);
``` ```
For more information on available options and plugins please refer to the ### <a name="dataobject-versioned"></a>DataObjects with the `Versioned` extension
[TinyMCE documentation](https://www.tinymce.com/docs/configure/)
#### Upgrading DataObjects with the `Versioned` extension
In most cases, versioned models with the default versioning parameters will not need to be changed. However, In most cases, versioned models with the default versioning parameters will not need to be changed. However,
there are now additional restrictions on the use of custom stage names. there are now additional restrictions on the use of custom stage names.
@ -1431,7 +1349,7 @@ These methods are deprecated:
* `Versioned::publish` Replaced by `Versioned::copyVersionToStage` * `Versioned::publish` Replaced by `Versioned::copyVersionToStage`
* `Versioned::doPublish` Replaced by `Versioned::publishRecursive` * `Versioned::doPublish` Replaced by `Versioned::publishRecursive`
#### Implementation of ownership API ### <a name="ownership"></a>New Ownership API
In order to support the recursive publishing of dataobjects, a new API has been developed to allow In order to support the recursive publishing of dataobjects, a new API has been developed to allow
developers to declare dependencies between objects. This is done to ensure that the published state developers to declare dependencies between objects. This is done to ensure that the published state
@ -1476,14 +1394,14 @@ setting on the child object.
For more information, see the [DataObject ownership](https://docs.silverstripe.org/en/4/developer_guides/model/versioning/#dataobject-ownership) documentation and the [versioning](/developer_guides/model/versioning) documentation For more information, see the [DataObject ownership](https://docs.silverstripe.org/en/4/developer_guides/model/versioning/#dataobject-ownership) documentation and the [versioning](/developer_guides/model/versioning) documentation
#### ChangeSet batch publishing ### <a name="changeset"></a>ChangeSet batch publishing
ChangeSet objects have been added, which allow groups of objects to be published in ChangeSet objects have been added, which allow groups of objects to be published in
a single atomic transaction. a single atomic transaction.
This API will utilise the ownership API to ensure that changes to any object include This API will utilise the ownership API to ensure that changes to any object include
all necessary changes to owners or owned entities within the same changeset. all necessary changes to owners or owned entities within the same changeset.
#### New `[image]` shortcode in `HTMLText` fields ### <a name="image-shortcode"></a>New `[image]` shortcode in `HTMLText` fields
The new Ownership API relies on relationships between objects. The new Ownership API relies on relationships between objects.
Many of these relationships are already made explicit through `has_one`, `has_many` and `many_many`. Many of these relationships are already made explicit through `has_one`, `has_many` and `many_many`.
@ -1493,78 +1411,49 @@ of the `Image` record rather than its path on the filesystem. The shortcode will
when the field is rendered. Newly inserted images will automatically receive the shortcode and ownership tracking, when the field is rendered. Newly inserted images will automatically receive the shortcode and ownership tracking,
and existing `<img>` will continue to work. and existing `<img>` will continue to work.
#### Upgrading references to DBField and subclasses ### <a name="dbfield-rename"></a>Renamed DBField and subclasses
A major change in 4.0 is the introduction of namespaced DBField subclasses. Now as a standard, all DBField subclasses have a `DB` prefix, are namespaced, and have an associative alias which omits the DB prefix. All `DBField` subclasses are namespaced, have a `DB` prefix, and drop any existing `SS_` prefix.
For example, `Text` becomes `SilverStripe\ORM\FieldType\DBText`,
and `SS_Datetime` becomes `SilverStripe\ORM\FieldType\DBDatetime`.
Since they are aliased to their old name, you likely won't need to change your `DataObject::$db` definitions.
If you are instanciating or otherwise referencing those classes directly (not through strings),
they'll likely get rewritten automatically through the
[upgrader tool](https://github.com/silverstripe/silverstripe-upgrader/).
This means that for the most part, code that worked in 3.0 won't need to be changed, although if you have any hard class literals which reference the old classes, they will need to be updated to point to the new namespaced classes. Example:
An exception to this is any classes which once had the `SS_` prefix, which will now be instead prefixed with `DB`, and have an un-aliased prefix. For example `SS_Datetime` is now `DBDateTime`, and has the alias `DateTime` which may be used in config. ```diff
Before:
```php
use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DataObject;
+use SilverStripe\ORM\FieldType\DBVarchar;
class MyObject extends DataObject class MyObject extends DataObject
{ {
private static $db = [ private static $db = [
'Number' => 'Int', 'Number' => 'Int',
'Time' => 'SS_Datetime' - 'Time' => 'SS_Datetime'
+ 'Time' => 'Datetime'
]; ];
/**
* @param Int $val
* @return Varchar
*/
public function TextNumber() public function TextNumber()
{ {
return new Varchar('TextNumber', 'Number is ' . $this->Number); - return new Varchar('TextNumber', 'Number is ' . $this->Number);
+ return new DBVarchar('TextNumber', 'Number is ' . $this->Number);
} }
} }
``` ```
After: ### <a name="restfulservice"></a>Removed RestfulService
```php The `RestfulService` API was a (poor) attempt at a built-in HTTP client.
use SilverStripe\ORM\DataObject; We've removed it, and recommend using [Guzzle](http://docs.guzzlephp.org/en/latest/) instead.
use SilverStripe\ORM\FieldType\DBVarchar;
class MyObject extends DataObject ### <a name="oembed"></a>Removed Oembed
{
private static $db = [
'Number' => 'Int',
'Time' => 'Datetime'
];
/**
* @param Int $val
* @return Varchar
*/
public function TextNumber()
{
return new DBVarchar('TextNumber', 'Number is ' . $this->Number);
}
}
```
Note that string references to `SS_Datetime` passed to injector, or used in config values, will still work, and will refer to the updated class names.
#### <a name="restfulservice"></a>Upgrading from deprecated RestfulService
Install Guzzle to get an API consuming library.
`composer require guzzlehttp/guzzle` or add `guzzlehttp/guzzle: "^6.0"` to your composer.json.
For information on how to use Guzzle, please see the extensive [Guzzle documentation](http://docs.guzzlephp.org/en/latest/)
In case you want to keep using RestfulService, you can use `Firesphere/silverstripe-restfulservice`, but it is unmaintained and deprecated.
#### <a name="oembed"></a>Upgrading from deprecated Oembed
Instead of Oembed, the framework now relies on [oscarotero/Embed](https://github.com/oscarotero/Embed) to handle getting the shortcode-data for embedding. Instead of Oembed, the framework now relies on [oscarotero/Embed](https://github.com/oscarotero/Embed) to handle getting the shortcode-data for embedding.
If you have custom embedding-code relying on Oembed, please refer to the documentation provided by oscarotero. If you have custom embedding-code relying on `Oembed`, please refer to the documentation provided by this package.
#### Admin URL can now be configured via custom Director routing rule ### <a name="admin-url"></a>Configurable Admin URL
The default `admin/` URL to access the CMS interface can now be changed via a custom Director routing rule for The default `admin/` URL to access the CMS interface can now be changed via a custom Director routing rule for
`AdminRootController`. If your website or module has hard coded `admin` URLs in PHP, templates or JavaScript, make sure `AdminRootController`. If your website or module has hard coded `admin` URLs in PHP, templates or JavaScript, make sure
@ -1572,9 +1461,12 @@ to update those with the appropriate function or config call. See
[CMS architecture](/developer_guides/customising_the_admin_interface/cms-architecture#the-admin-url) for language [CMS architecture](/developer_guides/customising_the_admin_interface/cms-architecture#the-admin-url) for language
specific functions. specific functions.
#### <a name="custom-authenticators"></a>Upgrading custom Authenticators ### <a name="custom-authenticators"></a>Custom Authenticators
The methods `register` and `unregister` on `Authenticator` are deprecated in favor of the `Config` system. This means that any custom Authenticator needs to be registered through the yml config. For further information on how to create a custom authentication method, [see the Authentication documentation](/developer_guides/Security/Authentication). The methods `register()` and `unregister()` on `Authenticator` are deprecated in favour
of the `Config` system. This means that any custom `Authenticator` needs to be registered
through the YAML config. Check the [Authentication docs](/developer_guides/Security/Authentication)
for details how to setup a custom handler.
```yml ```yml
SilverStripe\Security\Authenticator; SilverStripe\Security\Authenticator;
@ -1582,7 +1474,8 @@ SilverStripe\Security\Authenticator;
- MyVendor\MyModule\MyAuthenticator - MyVendor\MyModule\MyAuthenticator
``` ```
If there is no authenticator registered, `Authenticator` will try to fall back on the `default_authenticator`, which can be changed using the following config, replacing the MemberAuthenticator with your authenticator: If there is no authenticator registered, `Authenticator` will try to fall back on the `default_authenticator`,
which can be changed using the following config, replacing the `MemberAuthenticator` with your authenticator:
```yml ```yml
SilverStripe\Security\Authenticator: SilverStripe\Security\Authenticator:
@ -1590,15 +1483,15 @@ SilverStripe\Security\Authenticator:
``` ```
As soon as a custom authenticator is registered, the default authenticator will not be available anymore, unless enabled specifically in the config. As soon as a custom authenticator is registered, the default authenticator will not be available anymore, unless enabled specifically in the config.
By default, the `SilverStripe\Security\MemberAuthenticator` is seen as the default authenticator until it's explicitly set in the config. By default, the `SilverStripe\Security\MemberAuthenticator` is seen as the default authenticator until it's explicitly set in the config.
##### IdentityStore and RequestFilters Every request is now authenticated against an `IdentityStore` interface.
As of SilverStripe 4, every request is authenticated against an `IdentityStore`, by default a CookieAuthenticationHandler and a SessionAuthenticationHandler, which are called from the `AuthenticationHandler`. If there is a valid `Member`, it is set on `Security::setCurrentUser()`, which defaults no `null`. By default that's a `CookieAuthenticationHandler` and a `SessionAuthenticationHandler`,
which are called from the `AuthenticationHandler`.
If there is a valid `Member`, it is set on `Security::setCurrentUser()`, which defaults to `null`.
IdentityStores are responsible for logging members in and out (e.g. destroy cookies and sessions, or instantiate them). IdentityStores are responsible for logging members in and out (e.g. destroy cookies and sessions, or instantiate them).
#### Upgrading Config API usages ### <a name="config"></a>Config is now immutable
Performance optimisations have been made to Config which, under certain circumstances, require developer Performance optimisations have been made to Config which, under certain circumstances, require developer
care when modifying or caching config values. The top level config object is now immutable on application care when modifying or caching config values. The top level config object is now immutable on application
@ -1606,7 +1499,7 @@ bootstrap, and requires a developer to invoke `Config::modify()` to make mutable
This will immediately have a slight performance hit, so should be done sparingly, and avoided at all This will immediately have a slight performance hit, so should be done sparingly, and avoided at all
if possible in performance intensive applications. if possible in performance intensive applications.
In addition, the `Config::inst()->update()` method is deprecated, and replaced with `Config::modify()->set()` and The `Config::inst()->update()` method is deprecated, and replaced with `Config::modify()->set()` and
`Config::modify()->merge()` to respectively replace and merge config. `Config::modify()->merge()` to respectively replace and merge config.
When config is merged (either via modification or merged between yml blocks) falsey-values (including nulls) When config is merged (either via modification or merged between yml blocks) falsey-values (including nulls)
@ -1616,7 +1509,7 @@ One removed feature is the `Config::FIRST_SET` option. Either use uninherited co
directly, or use the inherited config lookup. As falsey values now overwrite all parent class values, it is directly, or use the inherited config lookup. As falsey values now overwrite all parent class values, it is
now generally safer to use the default inherited config, where in the past you would need to use `FIRST_SET`. now generally safer to use the default inherited config, where in the past you would need to use `FIRST_SET`.
#### <a name="cache"></a>Upgrading Cache API ### <a name="cache"></a>Replace Zend_Cache with symfony/cache
We have replaced the unsupported `Zend_Cache` library with [symfony/cache](https://github.com/symfony/cache). We have replaced the unsupported `Zend_Cache` library with [symfony/cache](https://github.com/symfony/cache).
This also allowed us to remove SilverStripe's `Cache` API and use dependency injection with a standard This also allowed us to remove SilverStripe's `Cache` API and use dependency injection with a standard
@ -1651,7 +1544,7 @@ and have a slightly different API (e.g. `set()` instead of `save()`).
+$cache->delete('myCacheKey'); +$cache->delete('myCacheKey');
``` ```
With the necessary minimal config in _config/mycache.yml With the necessary minimal config in `_config/mycache.yml`
```yml ```yml
--- ---
@ -1664,12 +1557,10 @@ SilverStripe\Core\Injector\Injector:
namespace: 'mycache' namespace: 'mycache'
``` ```
##### Configuration Changes
Caches are now configured through dependency injection services instead of PHP. Caches are now configured through dependency injection services instead of PHP.
See our ["Caching" docs](/developer-guides/performance/caching) for more details. See our ["Caching" docs](/developer-guides/performance/caching) for more details.
Before (mysite/_config.php): Before (`mysite/_config.php`):
```php ```php
Cache::add_backend( Cache::add_backend(
@ -1685,7 +1576,7 @@ Cache::add_backend(
Cache::pick_backend('primary_memcached', 'any', 10); Cache::pick_backend('primary_memcached', 'any', 10);
``` ```
After (mysite/_config/config.yml): After (`mysite/_config/config.yml`):
```yml ```yml
--- ---
@ -1712,13 +1603,13 @@ will assist you with bringing yourself up to speed.
Please note that before upgrading user code style it is necessary to run the standard upgrade path Please note that before upgrading user code style it is necessary to run the standard upgrade path
to fix references and usages of framework API. to fix references and usages of framework API.
#### Upgrading user-code to use namespaces ### Upgrading user-code to use namespaces
Upgrading code to use namespaces is quite a complex process, and as such we have provided Upgrading code to use namespaces is quite a complex process, and as such we have provided
several development tools and processes to help make the upgrade user friendly and as several development tools and processes to help make the upgrade user friendly and as
automated as possible. automated as possible.
##### Using the upgrader tool to automatically apply namespaces ### Using the upgrader tool to automatically apply namespaces
The [upgrader tool](https://github.com/silverstripe/silverstripe-upgrader/) provides a feature The [upgrader tool](https://github.com/silverstripe/silverstripe-upgrader/) provides a feature
to not only automatically namespace code, but will provide automatic upgrade of other code to not only automatically namespace code, but will provide automatic upgrade of other code
@ -1751,7 +1642,7 @@ This task will not do the following, and must be done manually:
Please see the following steps for more information. Please see the following steps for more information.
##### Using the upgrader tool to update references to namespaced user classes ### Using the upgrader tool to update references to namespaced user classes
Once a project has been namespaced all newly renamed classes will have a mapping included in the `mysite/.upgrade.yml` Once a project has been namespaced all newly renamed classes will have a mapping included in the `mysite/.upgrade.yml`
file. If you have any user-code that references these, you may need to run the upgrader again (as you did did file. If you have any user-code that references these, you may need to run the upgrader again (as you did did
@ -1763,7 +1654,7 @@ cd ~/Project/Root
~/.composer/vendor/bin/upgrade-code upgrade ./othercode --write ~/.composer/vendor/bin/upgrade-code upgrade ./othercode --write
``` ```
##### Updating custom dataobjects to use existing table names ### Updating custom dataobjects to use existing table names
Once you have namespaced your user code it will be necessary to customise the `table_name` config Once you have namespaced your user code it will be necessary to customise the `table_name` config
for your dataobjects, in order to ensure the correct table is used after upgrade. It is recommended for your dataobjects, in order to ensure the correct table is used after upgrade. It is recommended
@ -1779,7 +1670,7 @@ class GalleryPage extends Page
} }
``` ```
##### Class name remapping ### Class name remapping
If you've namespaced one of your custom page types, you may notice a message in the CMS If you've namespaced one of your custom page types, you may notice a message in the CMS
telling you it's obsolete. This is likely because the `ClassName` telling you it's obsolete. This is likely because the `ClassName`
@ -1803,7 +1694,7 @@ SilverStripe\ORM\DatabaseAdmin:
The next time you run a dev/build the class name for all `GalleryPage` pages will The next time you run a dev/build the class name for all `GalleryPage` pages will
be automatically updated to the new `WebDesignGroup\ShopSite\GalleryPage` be automatically updated to the new `WebDesignGroup\ShopSite\GalleryPage`
#### Using php code checker to automatically update to PSR-2 ### PSR-2 Coding Standard compliance
You can use the [php codesniffer](https://github.com/squizlabs/PHP_CodeSniffer) tool You can use the [php codesniffer](https://github.com/squizlabs/PHP_CodeSniffer) tool
to not only detect and lint PSR-2 coding errors, but also do some minimal automatic to not only detect and lint PSR-2 coding errors, but also do some minimal automatic
@ -1821,7 +1712,7 @@ code style migration.
Repeat the final step and manually repair suggested changes, as necessary, Repeat the final step and manually repair suggested changes, as necessary,
until you no longer have any linting issues. until you no longer have any linting issues.
#### Upgrade user-code to use PSR-4 autoloading ### PSR-4 autoloading for project code
While not critical to an upgrade, SilverStripe 4.0 has adopted the [PS-4 autoloading](http://www.php-fig.org/psr/psr-4/) While not critical to an upgrade, SilverStripe 4.0 has adopted the [PS-4 autoloading](http://www.php-fig.org/psr/psr-4/)
standard for the core modules, so it's probably a good idea to be consistent. standard for the core modules, so it's probably a good idea to be consistent.
@ -1857,9 +1748,7 @@ that it belongs to, e.g. `themes/mytheme/templates/MyVendor/Foobar/Model/MyModel
## <a name="api-changes"></a>API Changes ## <a name="api-changes"></a>API Changes
### <a name="overview-general"></a>General and Core API ### <a name="overview-general"></a>General
#### <a name="overview-general-api"></a>General and Core API Additions / Changes
* Minimum PHP version raised to 5.6 (with support for PHP 7.x) * Minimum PHP version raised to 5.6 (with support for PHP 7.x)
* Dropped support for PHP safe mode (removed php 5.4). * Dropped support for PHP safe mode (removed php 5.4).
@ -1898,111 +1787,92 @@ that it belongs to, e.g. `themes/mytheme/templates/MyVendor/Foobar/Model/MyModel
* Uniqueness checks for `File.Name` is performed on write only (not in `setName()`) * Uniqueness checks for `File.Name` is performed on write only (not in `setName()`)
* Created `Resettable` interface to better declare objects which should be reset between tests. * Created `Resettable` interface to better declare objects which should be reset between tests.
* Added a server requirement for the php-intl extension (shipped by default with most PHP distributions) * Added a server requirement for the php-intl extension (shipped by default with most PHP distributions)
* Replaced Zend_Date and Zend_Locale with the php-intl extension. * Replaced `Zend_Date` and `Zend_Locale` with the `php-intl` extension.
* Consistently use CLDR date formats (rather than a mix of CLDR and date() formats) * Consistently use CLDR date formats (rather than a mix of CLDR and `date()` formats)
* Moved config into a new module: [silverstripe/config](https://github.com/silverstripe/silverstripe-config/). * Moved config into a new module: [silverstripe/config](https://github.com/silverstripe/silverstripe-config/).
See upgrading notes below. See upgrading notes below.
* Falsey config values (null, 0, false, etc) can now replace non-falsey values. * Falsey config values (null, 0, false, etc) can now replace non-falsey values.
* Introduced new ModuleLoader manifest, which allows modules to be found via composer name. * Introduced new `ModuleLoader` manifest, which allows modules to be found via composer name.
E.g. `$cms = ModuleLoader::inst()->getManifest()->getModule('silverstripe/cms')` E.g. `$cms = ModuleLoader::inst()->getManifest()->getModule('silverstripe/cms')`
* `ClassManifest::getOwnerModule()` now returns a `Module` object instance. * `ClassManifest::getOwnerModule()` now returns a `Module` object instance.
* `Object` class has been removed. * Moved `Object::parse_class_spec()` to `ClassInfo`
* `parse_class_spec` moved to `ClassInfo` * Removed `create_from_spec()`. Supportede by `Injector` natively.
* `create_from_spec` functionality removed, but now supportede by `Injector` natively. * Moved `Controller::Link()` to parent class (`RequestHandler`)
* `Injectable`, `Extensible` and `Configurable` traits added to support other methods. * Moved `Controller::redirect()` to parent class (`RequestHandler`)
* Certain methods have been moved from `Controller` to `RequestHandler`: * Moved `Controller::redirectBack()` to parent class (`RequestHandler`)
* `Link` * `RequestHandler::Link()` now relies on the `url_segment` handler being provided for the class.
* `redirect` If left unset, this will raise an error.
* `redirectBack` * `RequestHandler::getBackURL()` and `getReturnReferer()` have been added to safely inspect the current request
* `RequestHandler` link and redirection behaviour has been enhanced slightly:
* `Link` now relies on the `url_segment` handler being provided for the class.
If left unset, this will raise an error.
* `getBackURL` and `getReturnReferer` have been added to safely inspect the current request
to see if there is a url to redirect back to. to see if there is a url to redirect back to.
* `LeftAndMain` class has had all tree manipulation code moved into CMS module class `CMSMain`. * Renamed `LeftAndMain_TreeNode` to `CMSMain_TreeNode`
This includes: * Removed `LeftAndMain::SiteTreeAsUL()` (override left in `CMSMain`)
* `LeftAndMain_TreeNode` class -> renamed to `CMSMain_TreeNode` * Moved `LeftAndMain::getSiteTreeFor()` to `CMSMain`
* `SiteTreeAsUL` removed (override left in `CMSMain`) * Moved `LeftAndMain::getsubtree()` to `CMSMain`
* `getSiteTreeFor()` -> moved to `CMSMain` * Moved `LeftAndMain::updatetreenodes()` to `CMSMain`
* `getsubtree()` -> moved to `CMSMain` * Moved `LeftAndMain::savetreenodes()` to `CMSMain`
* `updatetreenodes()` -> moved to `CMSMain` * Renamed `LeftAndMain::EditorToolbar()` to `Modals()`. Returns a `ModalController` handler
* `savetreenodes()` -> moved to `CMSMain` instance rather than a `HTMLEditorField_Toolbar`
* `EditorToolbar()` method renamed to `Modals()` and now returns a `ModalController` handler * Removed `Director::$dev_servers` and `Director::$test_servers`
instance rather than a `HTMLEditorField_Toolbar` * Removed `Director::$urlParams` and `Director::setUrlParams()`
* Some `Director` API have been removed. * Removed `Director.alternate_host`. Use `Director.alternate_base_url` instead.
* $dev_servers * Removed `Director.alternate_protocol`. Use `Director.alternate_base_url` instead.
* $test_servers * 'BlockUntrustedIPS' env setting has been removed.
* $urlParams and setUrlParams() All IPs are untrusted unless `SS_TRUSTED_PROXY_IPS` is set to '*'
* `Director.alternate_host` removed. Use `Director.alternate_base_url` instead. See [Environment Management docs](/getting-started/environment_management/) for full details.
* `Director.alternate_protocol` removed. Use `Director.alternate_base_url` instead. * `SS_TRUSTED_PROXY_HOST_HEADER`, `SS_TRUSTED_PROXY_PROTOCOL_HEADER`, and `SS_TRUSTED_PROXY_IP_HEADER`
* Global enviromnent variables changed: are no longer supported. These settings should go into the Injector service configuration for
* 'BlockUntrustedIPS' env setting has been removed. TrustedProxyMiddleware instead.
All IPs are untrusted unless `SS_TRUSTED_PROXY_IPS` is set to '*' * Removed `SS_HOST` environment constant. Use `SS_BASE_URL` instead.
See [Environment Management docs](/getting-started/environment_management/) for full details. * `Member::canLogIn()` now returns boolean. Use `Member::validateCanLogin()` to get a `ValidationResult`
* `SS_TRUSTED_PROXY_HOST_HEADER`, `SS_TRUSTED_PROXY_PROTOCOL_HEADER`, and `SS_TRUSTED_PROXY_IP_HEADER` * Moved `Security::has_default_admin` to `DefaultAdminService::hasDefaultAdmin()`
are no longer supported. These settings should go into the Injector service configuration for * Moved `Security::check_default_admin` to `DefaultAdminService::isDefaultAdminCredentials()`
TrustedProxyMiddleware instead. * Moved `Security::default_admin_torname` to `DefaultAdminService::getDefaultAdminUsername()`
* `MODULES_PATH` removed * Moved `Security::default_admin_password` to `DefaultAdminService::getDefaultAdminPassword()`
* `MODULES_DIR` removed * Moved `Security::setDefaultAdmin` to `DefaultAdminService::setDefaultAdmin()`
* `SS_HOST` removed. Use `SS_BASE_URL` instead. * Moved `Security::clearDefaultAdmin` to `DefaultAdminService::clearDefaultAdmin()`
* `Member::canLogIn()` now returns boolean. Use `Member::validateCanLogin()` to get a `ValidationResult` * Moved `Security::findAnAdministrator` to `DefaultAdminService::findOrCreateDefaultAdmin()`
* `Security` methods deprecated: * Deprecated `Member::checkPassword()`. Use `Authenticator::checkPassword()` instead
* `has_default_admin` use `DefaultAdminService::hasDefaultAdmin()` instead * Deprecated `RequestFilter`. Use
* `check_default_admin` use `DefaultAdminService::isDefaultAdminCredentials()` instead [HTTPMiddleware](/developer_guides/controllers/middlewares) instead.
* `default_admin_username` use `DefaultAdminService::getDefaultAdminUsername()` instead * Changed `RequestFilter`: The `$session` and `$dataModel` variables removed from preRequest / postRequest.
* `default_admin_password` use `DefaultAdminService::getDefaultAdminPassword()` instead
* `setDefaultAdmin` use `DefaultAdminService::setDefaultAdmin()` instead
* `clearDefaultAdmin` use `DefaultAdminService::clearDefaultAdmin()` instead
* `findAnAdministrator` use `DefaultAdminService::findOrCreateDefaultAdmin()` instead
* `Member` methods deprecated:
* `checkPassword`. Use Authenticator::checkPassword() instead
* `RequestFilter` has been deprecated in favour of
[HTTPMiddleware](/developer_guides/controllers/middlewares). Also the legacy RequestFilter
API has changed: $session and $dataModel variables removed from preRequest / postRequest.
* `Extension` instances are now singletons and no longer are constructed once per extended object. * `Extension` instances are now singletons and no longer are constructed once per extended object.
See the 'Upgrade extensions to work as singletons' section on this page for more information. See the 'Upgrade extensions to work as singletons' section on this page for more information.
* Removed `ConfigureFromEnv.php`
#### <a name="overview-general-removed"></a>General and Core Removed API * Changed `Session` object to avoid static access (`Session::inst()`).
Use it from the current request via `$request->getSession()` instead.
* Removed `ConfigureFromEnv.php` as it's no longer necessary.
* `Session` object has had significant refactoring. This object no longer is accessed via
`Session::inst()`, but instead should be queried from the current request via `$request->getSession()`.
All static methods have been removed, and the `inst_` prefix removed from all instance members. All static methods have been removed, and the `inst_` prefix removed from all instance members.
Please see the upgrading section on Session object for more details.
* `Director.rules` config no longer support `redirect:<url>` directly via config. * `Director.rules` config no longer support `redirect:<url>` directly via config.
* `Director::set_environment_type()` has been removed. * Removed `Director::get_environment_type()` and `Director::set_environment_type()`.
Get the `Kernel` instance via injector and query `getEnvironment()` instead. Get the `Kernel` instance via injector and query `getEnvironment()` instead.
E.g. `$type = Injector::inst()->get(Kernel::class)->getEnvironment();`. (e.g. `$type = Injector::inst()->get(Kernel::class)->getEnvironment()`)
* The environment can no longer be configured via YAML (`Director.environment_type`). * Removed `Director.environment_type` to configure the environment via YAML.
Use a [.env file](/getting_started/environment_management) to manage environment settings. Use a [.env file](/getting_started/environment_management) to manage environment settings.
* Many global methods have been refactored into `Environment` or `Convert` class. * Removed `increase_xdebug_nesting_level_to()` (functionality has been inlined into `AppKernel`)
* `increase_xdebug_nesting_level_to` removed (functionality has been inlined into `AppKernel`) * Moved `set_increase_time_limit_max()` to `Environment::setTimeLimitMax()`
* `set_increase_time_limit_max` moved to `Environment::setTimeLimitMax()` * Moved `get_increase_time_limit_max()` to `Environment::getTimeLimitMax()`
* `get_increase_time_limit_max` moved to `Environment::getTimeLimitMax()` * Moved `set_increase_memory_limit_max()` to `Environment::setMemoryLimitMax()`
* `set_increase_memory_limit_max` moved to `Environment::setMemoryLimitMax()` * Moved `get_increase_memory_limit_max()` to `Environment::getMemoryLimitMax()`
* `get_increase_memory_limit_max` moved to `Environment::getMemoryLimitMax()` * Moved `increase_time_limit_to()` to `Environment::increaseTimeLimitTo()`
* `increase_time_limit_to` moved to `Environment::increaseTimeLimitTo()` * Moved `increase_memory_limit_to()` to `Environment::increaseMemoryLimitTo()`
* `increase_memory_limit_to` moved to `Environment::increaseMemoryLimitTo()` * Moved `translate_memstring()` to `Convert::memstring2bytes`.
* `translate_memstring` moved to `Convert::memstring2bytes`. * Moved `getTempFolder()` to `TempFolder::getTempFolder()`
* `getTempFolder` moved to `TempFolder::getTempFolder()` * Removed `getTempParentFolder()`
* `getTempParentFolder` removed. * Removed `getTempFolderUsername()`
* `getTempFolderUsername` removed. * Removed `CMSMain::buildbrokenlinks()`
* `CMSMain::buildbrokenlinks()` action is removed. * Removed `Injector::unregisterAllObjects()`. Use `unregisterObjects` to unregister
* `Injector::unregisterAllObjects()` has been removed. Use `unregisterObjects` to unregister
groups of objects limited by type instead. groups of objects limited by type instead.
* `SS_Log` class has been removed. Use `Injector::inst()->get(LoggerInterface::class)` instead. * Removed `SS_Log`. Use `Injector::inst()->get(LoggerInterface::class)` instead.
* Removed `CMSBatchAction_Delete` * Removed `CMSBatchAction_Delete`
* Removed `CMSBatchAction_DeleteFromLive` * Removed `CMSBatchAction_DeleteFromLive`
* Removed `CMSMain.enabled_legacy_actions` config. * Removed `CMSMain.enabled_legacy_actions` config.
* `CMSmain.getCMSTreeTitle` is now ignored on extensions. Use `updateCMSTreeTitle` in extensions instead. * `CMSmain.getCMSTreeTitle` is now ignored on extensions. Use `updateCMSTreeTitle` in extensions instead.
* Removed ability to run tests via web requests (`http://mydomain.com/dev/tests`), use the standard CLI * Removed ability to run tests via web requests (`http://mydomain.com/dev/tests`), use the standard CLI
command instead (`vendor/bin/phpunit`). command instead (`vendor/bin/phpunit`).
* Removed `dev/jstests/` controller (no replacement) * Removed `dev/jstests/` controller
* Removed `TestRunner` and `JSTestRunner` APIs * Removed `TestRunner` and `JSTestRunner`
* Removed `PhpUnitWrapper`, `PhpUnitWrapper_3_4`, `PhpUnitWrapper_3_5`, `PhpUnitWrapper_Generic`, `SapphireTestSuite` APIs * Removed `PhpUnitWrapper`, `PhpUnitWrapper_3_4`, `PhpUnitWrapper_3_5`, `PhpUnitWrapper_Generic`, `SapphireTestSuite` APIs
* Removed `SapphireTestReporter` and `CliTestReporter` * Removed `SapphireTestReporter` and `CliTestReporter`
* Removed `SapphireTest::skipTest()`, use `markTestSkipped()` in a `setUp()` method instead * Removed `SapphireTest::skipTest()`, use `markTestSkipped()` in a `setUp()` method instead
* Removed the `History.js` javascript library.
* `debugmethods` querystring argument has been removed from debugging. * `debugmethods` querystring argument has been removed from debugging.
* Moved `ErrorPage` into a new module: [silverstripe/errorpage](http://addons.silverstripe.org/add-ons/silverstripe/errorpage). See upgrading notes in that module. * Moved `ErrorPage` into a new module: [silverstripe/errorpage](http://addons.silverstripe.org/add-ons/silverstripe/errorpage). See upgrading notes in that module.
* Removed `VirtualPage_Controller`. Virtual pages will now share whichever controller the “target” page uses * Removed `VirtualPage_Controller`. Virtual pages will now share whichever controller the “target” page uses
@ -2013,26 +1883,23 @@ that it belongs to, e.g. `themes/mytheme/templates/MyVendor/Foobar/Model/MyModel
* `i18nTextCollector` no longer collects from `themes/<theme>` root dir. * `i18nTextCollector` no longer collects from `themes/<theme>` root dir.
Modules which provide themes via `<moduleName>/themes/<theme>` are now preferred. Modules which provide themes via `<moduleName>/themes/<theme>` are now preferred.
* Removed `i18nSSLegacyAdapter` * Removed `i18nSSLegacyAdapter`
* Removed `FunctionalTest::stat` * Removed `FunctionalTest::stat()`
* Removed `LeftAndMainMarkingFilter` * Removed `LeftAndMainMarkingFilter`
* Removed `Controller::getFormOwner` * Removed `Controller::getFormOwner()`
* Removed `TeamCityListener` * Removed `TeamCityListener`
* The Spyc YAML library has been removed from /thirdparty. Please load it yourself, or use the * Removed the `Spyc` YAML library. Please load it yourself, or use the included Symfony YAML component.
Symfony YAML component thats automatically installed by composer. * Removed `RestfulService`. Use Guzzle instead ([details](#restfulservice))
* `RestfulService` has been removed. Use Guzzle instead. See Upgrading notes. * Removed `Oembed` in favour of a
* Our self-maintained `Oembed` implementation has been removed, in favour of introducing [oscarotero/Embed](https://github.com/oscarotero/Embed)
[oscarotero/Embed](https://github.com/oscarotero/Embed) as a dependency. * Removed `TextParser` and `BBCodeParser`. These are available in an archived module,
* Removed TextParser and BBCodeParser. These are available in an archived module,
[silverstripe-archive/bbcodeparser](https://github.com/silverstripe-archive/silverstripe-bbcodeparser) [silverstripe-archive/bbcodeparser](https://github.com/silverstripe-archive/silverstripe-bbcodeparser)
* Removed `ViewableData::ThemeDir`. Use `ThemeResourceLoader::findThemedResource` in conjunction with `SSViewer::get_themes` instead. * Removed `ViewableData::ThemeDir`. Use `ThemeResourceLoader::findThemedResource` in conjunction with `SSViewer::get_themes` instead.
* Removed `Config::FIRST_SET` and `Config::INHERITED` * Removed `Config::FIRST_SET` and `Config::INHERITED`
* Removed `RequestHandler.require_allowed_actions`. This is now fixed to on and cannot be * Removed `RequestHandler.require_allowed_actions`. This is now fixed to on and cannot be
disabled. disabled.
* Config or module searching methods have been removed from `ClassManifest`. Use `ModuleLoader` * Removed `ClassManifest::getModules()`. Use `ModuleLoader` instead
to get this information instead: * Removed `ClassManifest::getConfigDirs()`. Use `ModuleLoader` instead
- `getModules` * Removed `ClassManifest::getConfigs()`. Use `ModuleLoader` instead
- `getConfigDirs`
- `getConfigs`
* Removed `Session::set_config()` and `Session::get_config()`. Use the `Session.timeout` config setting instead * Removed `Session::set_config()` and `Session::get_config()`. Use the `Session.timeout` config setting instead
* Removed `Security::set_login_recording()` and `Security::get_login_recording()`. * Removed `Security::set_login_recording()` and `Security::get_login_recording()`.
Use the `Security.login_recording` config setting instead. Use the `Security.login_recording` config setting instead.
@ -2046,223 +1913,194 @@ that it belongs to, e.g. `themes/mytheme/templates/MyVendor/Foobar/Model/MyModel
Accordingly, `hasService()` has been renamed to `has()`, and `get()` will throw Accordingly, `hasService()` has been renamed to `has()`, and `get()` will throw
`SilverStripe\Core\Injector\InjectorNotFoundException` when the service can't be found. `SilverStripe\Core\Injector\InjectorNotFoundException` when the service can't be found.
* Removed `CustomMethods::createMethod()`. Use closures instead. * Removed `CustomMethods::createMethod()`. Use closures instead.
* Removed `Extension::$ownerBaseClass` property. You should use $this->owner->baseClass() instead. * Removed `Extension::$ownerBaseClass` property. You should use `$this->owner->baseClass()` instead.
The second argument of `Extension::setOwner()` has also been removed. The second argument of `Extension::setOwner()` has also been removed.
* Deprecated `ClassInfo::baseDataClass()`. Use `DataObject::getSchema()->baseDataClass()` instead.
#### <a name="overview-general-deprecated"></a>General and Core Deprecated API * Deprecated `ClassInfo::table_for_object_field()`. Use `DataObject::getSchema()->tableForField()` instead
* Deprecated `Config::inst()->update()`. Use `Config::modify()->set()` or `Config::modify()->merge()`
A very small number of methods were chosen for deprecation, and will be removed in 5.0 rather than 4.0
* `ClassInfo::baseDataClass` - Use `DataObject::getSchema()->baseDataClass()` instead.
* `ClassInfo::table_for_object_field` - Use `DataObject::getSchema()->tableForField()` instead
* `Config::inst()->update()` is deprecated. Use `Config::modify()->set()` or `Config::modify()->merge()`
instead. instead.
### <a name="overview-orm"></a>ORM API ### <a name="overview-orm"></a>ORM
#### <a name="overview-orm-api"></a>ORM API Additions / Changes * Deprecated `SQLQuery` in favour `SQLSelect` ([details](#sqlquery))
* Added `DataObject.many_many` 'through' relationships now support join dataobjects in place of
* Deprecate `SQLQuery` in favour `SQLSelect`
* `DataObject.many_many` 'through' relationships now support join dataobjects in place of
automatically generated join tables. See the [/developer_guides/relations](datamodel relationship docs) automatically generated join tables. See the [/developer_guides/relations](datamodel relationship docs)
for more info. for more info.
* `DataList::filter` by null now internally generates "IS NULL" or "IS NOT NULL" conditions * Added `DataList::filter()` by null now internally generates "IS NULL" or "IS NOT NULL" conditions
appropriately on queries. appropriately on queries.
* `DataList::createDataObject` is now public. * Changed `DataObject` constructor to require an additional parameter, which must be included in subclasses.
* `DataObject` constructor now has an additional parameter, which must be included in subclasses.
* `DataObject::db` now returns composite fields. * `DataObject::db` now returns composite fields.
* `DataObject::ClassName` field has been refactored into a `DBClassName` type field. * `DataObject::ClassName` field has been refactored into a `DBClassName` type field (instead of a string).
* `DataObject::can` has new method signature with `$context` parameter. * `DataObject::can()` has new method signature with `$context` parameter.
* `DataObject::duplicate` Now requires explicit flag to duplicate belongs_many_many (off by default), * `DataObject::duplicate()` now requires explicit flag to duplicate `belongs_many_many` (off by default),
but now works with unsaved relations. By default only many_many are duplicated. but now works with unsaved relations. By default only `many_many` are duplicated.
* `DBHTMLText` no longer enables shortcodes by default. Two injector aliases have been created for this * `HTMLText` no longer enables shortcodes by default. Two injector aliases have been created for this
class which can be used to select the correct behaviour: class which can be used to select the correct behaviour. Use `HTMLText` for shortcodes enabled,
* `HTMLText`: `DBHTMLText` with shortcodes enabled and `HTMLFragment` without shortcodes enabled (the new default).
* `HTMLFragment`: `DBHTMLText` without shortcodes enabled (as default) * Renamed `String::NoHTML()` to `Plain()`
* `CMSPreviewable` has been moved from the `SilverStripe\Admin` namespace to `SilverStripe\ORM` * Removed `String::LimitWordCountXML()`. Use `LimitWordCount()` instead.
* Changes to `DBString` formatting: * Removed `String::BigSummary()`. Use `Summary()` instead.
* `NoHTML` is renamed to `Plain` * Changed `HTMLText` limit methods to operate on plain text rather than attempt to manipulate the underlying HTML.
* `LimitWordCountXML` is removed. Use `LimitWordCount` instead. * `FormField::Title()` and `FormField::RightTitle()` are now cast as plain text by default (but can be overridden).
* `BigSummary` is removed. Use `Summary` instead. * Renamed `FormField#createTag()` to `FormField::create_tag()`
* Most limit methods on `DBHTMLText` now plain text rather than attempt to manipulate the underlying HTML.
* `FormField::Title` and `FormField::RightTitle` are now cast as plain text by default (but can be overridden).
* `FormField#createTag()` has been renamed to `FormField::create_tag()`
* `Hierarchy` class has had much of it's functionality refactored out into `MarkedSet`: * `Hierarchy` class has had much of it's functionality refactored out into `MarkedSet`:
* `isMarked` * `isMarked()`
* `isTreeOpened` * `isTreeOpened()`
* `isExpanded` * `isExpanded()`
* `markByID` * `markByID()`
* `markPartialTree` * `markPartialTree()`
* `markExpanded` * `markExpanded()`
* `markUnexpanded` * `markUnexpanded()`
* `markToExpose` * `markToExpose()`
* `markClosed` * `markClosed()`
* `markOpened` * `markOpened()`
* `markedNodeIDs` * `markedNodeIDs()`
* `getChildrenAsUL` replaced with `renderChildren`, which now takes a template name. * `getChildrenAsUL()` replaced with `renderChildren()`, which now takes a template name.
* `markingFilterMatches` (and made protected) * `markingFilterMatches()` (and made protected)
* `markChildren` (and made protected) * `markChildren()` (and made protected)
* Removed `DataList::applyFilterContext` private method
* Search filter classes (e.g. `ExactMatchFilter`) are now registered with `Injector` * Search filter classes (e.g. `ExactMatchFilter`) are now registered with `Injector`
via a new `DataListFilter.` prefix convention. via a new `DataListFilter.` prefix convention.
see [search filter documentation](/developer_guides/model/searchfilters) for more information. see [search filter documentation](/developer_guides/model/searchfilters) for more information.
* `Permission::flush_permission_cache()` renamed to `reset()` and added to `Resettable` interface. * `Permission::flush_permission_cache()` renamed to `reset()` and added to `Resettable` interface.
* `Versioned` API has some breaking changes: * Changed `Versioned` constructor now only allows a single string to declare whether staging is enabled or not. The
* Versioned constructor now only allows a single string to declare whether staging is enabled or not. The number of names of stages are no longer able to be specified. See below for upgrading notes for models
number of names of stages are no longer able to be specified. See below for upgrading notes for models with custom stages.
with custom stages. * Renamed `Versioned::reading_stage()` to `set_stage()` (throws an error if setting an invalid stage)
* `reading_stage` is now `set_stage` and throws an error if setting an invalid stage. * Renamed `Versioned::current_stage()` to `get_stage()`
* `current_stage` is now `get_stage` * Removed `Versioned::getVersionedStages()`
* `getVersionedStages` is gone. * Removed `Versioned::get_live_stage()`. Use the `Versioned::LIVE` constant instead.
* `get_live_stage` is removed. Use the `Versioned::LIVE` constant instead. * Removed `Versioned::getDefaultStage()`. Use the `Versioned::DRAFT` constant instead.
* `getDefaultStage` is removed. Use the `Versioned::DRAFT` constant instead. * Changed `Versioned::$versionableExtensions` from `private static` to `protected static`
* `$versionableExtensions` is now `private static` instead of `protected static` * Added `Versioned::hasStages()` to check if an object has a given stage.
* `hasStages` is addded to check if an object has a given stage. * Added `Versioned::stageTable()` to get the table for a given class and stage.
* `stageTable` is added to get the table for a given class and stage. * Any extension declared via `versionableExtensions` config on Versioned dataobject must now
* Any extension declared via `versionableExtensions` config on Versioned dataobject must now `VersionableExtension` interface at a minimum. `Translatable` has been removed from default
`VersionableExtension` interface at a minimum. `Translatable` has been removed from default `versionableExtensions`
`versionableExtensions` * The default CMS delete behaviour for versioned dataobjects is now to delete from both draft
* The default CMS delete behaviour for versioned dataobjects is now to delete from both draft and live stages, and to save to the archive. There is no longer a separate "Archive" action.
and live stages, and to save to the archive. There is no longer a separate "Archive" action. * Any writes to versioned dataobjects (in either Draft or Live) will always write to the draft
* Any writes to versioned dataobjects (in either Draft or Live) will always write to the draft (main) table as a source of truth. This means that records created in Live mode will always be
(main) table as a source of truth. This means that records created in Live mode will always be available to the CMS and in draft mode.
available to the CMS and in draft mode. * `_versions` suffixed tables are now renamed to `_Versions`. This fix will be automatically
* `_versions` suffixed tables are now renamed to `_Versions`. This fix will be automatically applied during dev/build.
applied during dev/build.
* A lot of standard versioned API has been refactored from `SiteTree` into `Versioned` extension. * A lot of standard versioned API has been refactored from `SiteTree` into `Versioned` extension.
* All versioned DataObjects have `canPublish()`, `canArchive()`, `canUnpublish()` permission checks * All versioned DataObjects have `canPublish()`, `canArchive()`, `canUnpublish()` permission checks
* All versioned Dataobjects have `doPublish()`, `doArchive()`, `doPublish()`, and `doUnpublish()` actions. * All versioned Dataobjects have `doPublish()`, `doArchive()`, `doPublish()`, and `doUnpublish()` actions.
However, `do*()` methods will no longer automatically check `can*()` permissions, and must be done by However, `do*()` methods will no longer automatically check `can*()` permissions, and must be done by
usercode before invocation. usercode before invocation.
* `SiteTree::getIsAddedToStage()` moved to `Versioned` and renamed to `isOnDraftOnly()` * Moved `SiteTree::getIsAddedToStage()` to `Versioned::isOnDraftOnly()`
* `SiteTre::getIsModifiedOnStage()` moved to `Versioned` and renamed to `isModifiedOnDraft()` * Moved `SiteTree::getIsModifiedOnStage()` to `Versioned::isModifiedOnDraft()`
* `SiteTree::isPublished()` moved to `Versioned`. * Moved `SiteTree::isPublished()` to `Versioned`.
* `SiteTree::getExistsOnLive()` removed in favour of `isPublished()` * Renamed `SiteTree::getExistsOnLive()` to `isPublished()`
* `isOnDraft()` added to `Versioned`. * Added `Versioned::isOnDraft()`
* `isArchived()` added to `Versioned`. * Added `Versioned::isArchived()`
* `isOnLiveOnly()` added to `Versioned`. * Added `Versioned::isOnLiveOnly()`
* `ChangeSet` and `ChangeSetItem` have been added for batch publishing of versioned dataobjects. * Added `ChangeSet` and `ChangeSetItem` for batch publishing of versioned dataobjects.
* `DataObject.table_name` config can now be used to customise the database table for any record. * Added `DataObject.table_name` config to customise the database table for any record.
* `DataObjectSchema` class added to assist with mapping between classes and tables. * Added `DataObjectSchema` class to assist with mapping between classes and tables.
* `DataObject.indexes` now uses `columns` instead of `value` to define index contents. * Changed `DataObject.indexes` to use `columns` instead of `value` to define index contents.
* `DBMoney` values are now treated as empty only if `Amount` field is null. If an `Amount` value * Changed `Money` to treat values as empty only if `Amount` field is null. If an `Amount` value
is provided without a `Currency` specified, it will be formatted as per the current locale. is provided without a `Currency` specified, it will be formatted as per the current locale.
* Removed `DatabaseAdmin#clearAllData()`. Use `DB::get_conn()->clearAllData()` instead * Removed `DatabaseAdmin#clearAllData()`. Use `DB::get_conn()->clearAllData()` instead
* `SapphireTest` temp DB methods have been removed and put into a new `TempDatabase` class. * Moved `SapphireTest` temp DB methods into a new `TempDatabase` class.
This allows applications to create temp databases when not running tests. This allows applications to create temp databases when not running tests.
This class now takes a connection name as a constructor class, and no longer * Moved `SapphireTest::using_temp_db()` to `TempDatabase->isUsed()`
has static methods. * Moved `SapphireTest::kill_temp_db()` to `TempDatabase->kill()`
The following methods have been moved to this new class and renamed as non-static: * Moved `SapphireTest::empty_temp_db()` to `TempDatabase->clearAllData()`
* `using_temp_db` -> `isUsed()` * Moved `SapphireTest::create_temp_db()` to `TempDatabase->build()`
* `kill_temp_db` -> `kill()` * Moved `SapphireTest::delete_all_temp_dbs()` to `TempDatabase->deleteAll()`
* `empty_temp_db` _> `clearAllData()` * Moved `SapphireTest::resetDBSchema()` to `TempDatabase->resetSchema()`
* `create_temp_db` -> `build()` * `DBDate`, `DBTime` and `DBDatetime` have changed methods:
* `delete_all_temp_dbs` -> `deleteAll()` * Added `getTimestamp()` to get the respective date / time as unix timestamp (seconds since 1970-01-01)
* `resetDBSchema` -> `resetSchema()` * Changed `Format()` method to use [CLDR format strings](http://userguide.icu-project.org/formatparse/datetime),
rather than [PHP format string](http://php.net/manual/en/function.date.php).
The below methods have been added or had their functionality updated to `DBDate`, `DBTime` and `DBDatetime` E.g. `d/m/Y H:i:s` (php format) should be replaced with to `dd/MM/y HH:mm:ss` (CLDR format).
* `getTimestamp()` added to get the respective date / time as unix timestamp (seconds since 1970-01-01) * Added `getISOFormat()` to return the standard date/time ISO 8601 pattern in CLDR format.
* `Format()` method now use [CLDR format strings](http://userguide.icu-project.org/formatparse/datetime), * Changed `setValue()` method to expect dates and times to be passed in
rather than [PHP format string](http://php.net/manual/en/function.date.php). ISO 8601 format (y-MM-dd) or (HH:mm:ss). Certain date formats will attempt to parse with
E.g. `d/m/Y H:i:s` (php format) should be replaced with to `dd/MM/y HH:mm:ss` (CLDR format). the below restrictions:
* getISOFormat() added which returns the standard date/time ISO 8601 pattern in CLDR format. - `/`, `.` or `-` are supported date separators, but will be replaced with `-` internally.
* `setValue` method is now a lot more restrictive, and expects dates and times to be passed in - US date formats (m-d-y / y-d-m) will not be supported and may be parsed incorrectly.
ISO 8601 format (y-MM-dd) or (HH:mm:ss). Certain date formats will attempt to parse with (Note: Date form fields will still support localised date formats).
the below restrictions: - `dd-MM-y` will be converted to `y-MM-dd` internally.
- `/`, `.` or `-` are supported date separators, but will be replaced with `-` internally. - 2-digit values for year will now raise errors.
- US date formats (m-d-y / y-d-m) will not be supported and may be parsed incorrectly. * Changed `FormatFromSettings()` to default to `Nice()` format if no member is logged in.
(Note: Date form fields will still support localised date formats). * Changed `Nice()`, `Long()` and `Full()` methods to follow standard formatting rules for the
- `dd-MM-y` will be converted to `y-MM-dd` internally. current locale, rather than pre-defined formats.
- 2-digit values for year will now raise errors. * Added `Short()` to format concise date/time values, as a shorter version than `Nice`
* `FormatFromSettings` will default to `Nice()` format if no member is logged in. * Added `getFormatter()` to return a locale-specific date/time formatter.
* `Nice`, `Long` and `Full` methods will now follow standard formatting rules for the * Added `DBTime::FormatFromSettings()`
current locale, rather than pre-defined formats. * Deprecated globals `$database` and `$databaseConfig`. Use `DB::setConfig()` instead.
* `Short` added to format concise date/time values, as a shorter version than `Nice` * Removed `DataModel`
* `getFormatter` method added, which returns a locale-specific date/time formatter. * Changed `DataObject::can*()` methods to no longer accept a member ID. These must now be passed a `Member` object or left null
* Moved `DataObject::db()` to `DataObjectSchema::fieldSpec()` and `DataObjectSchema::fieldSpecs()`
`DBTime` specific changes: * Moved `DataObject::manyManyComponent()` to `DataObjectSchema` (access through `DataObject->getSchema()`)
* Moved `DataObject::belongsToComponent()` to `DataObjectSchema` (access through `DataObject->getSchema()`)
* Added `DBTime::FormatFromSettings` * Moved `DataObject::hasOneComponent()` to `DataObjectSchema` (access through `DataObject->getSchema()`)
* Moved `DataObject::hasManyComponent()` to `DataObjectSchema` (access through `DataObject->getSchema()`)
#### <a name="overview-orm-removed"></a>ORM Removed API * Moved `DataObject::getRemoteJoinField()` to `DataObjectSchema` (access through `DataObject->getSchema()`)
* Moved `DataObject::database_fields()` to `DataObjectSchema::databaseFields()`
* Deprecated globals `$database` and `$databaseConfig`. Please use `DB::setConfig()` instead. * Moved `DataObject::has_own_table()` to `DataObjectSchema::classHasTable()`
* `DataModel` removed * Moved `DataObject::composite_fields()` to `DataObjectSchema::compositeFields()`
* `DataObject::can*` methods no longer accept a member ID. These must now be passed a Member object or left null * Moved `DataObject::manyManyExtraFieldsForComponent()` to `DataObjectSchema`
* `DataObject::db` removed and replaced with `DataObjectSchema::fieldSpec` and `DataObjectSchema::fieldSpecs` * Deprecated `DataObject::$destroyed`
* `DataObject::manyManyComponent` moved to `DataObjectSchema` (access through `DataObject->getSchema()`)
* `DataObject::belongsToComponent` moved to `DataObjectSchema` (access through `DataObject->getSchema()`)
* `DataObject::hasOneComponent` moved to `DataObjectSchema` (access through `DataObject->getSchema()`)
* `DataObject::hasManyComponent` moved to `DataObjectSchema` (access through `DataObject->getSchema()`)
* `DataObject::getRemoteJoinField` moved to `DataObjectSchema` (access through `DataObject->getSchema()`)
* `DataObject::database_fields()` renamed and moved to `DataObjectSchema::databaseFields`
* `DataObject::has_own_table()` renamed and moved to `DataObjectSchema::classHasTable`
* `DataObject::composite_fields()` renamed and moved to `DataObjectSchema::compositeFields`
* `DataObject::manyManyExtraFieldsForComponent()` moved to `DataObjectSchema`
* `DataObject::$destroyed` is now deprecated
* Removed `DataObject::validateModelDefinitions`. Relations are now validated within `DataObjectSchema` * Removed `DataObject::validateModelDefinitions`. Relations are now validated within `DataObjectSchema`
* <a name="dataobject-has-own"></a>Removed `DataObject` methods `hasOwnTableDatabaseField`, `has_own_table_database_field` and * <a name="dataobject-has-own"></a>Removed `DataObject` methods `hasOwnTableDatabaseField`, `has_own_table_database_field` and
`hasDatabaseFields` are superceded by `DataObjectSchema::fieldSpec`. `hasDatabaseFields` are superceded by `DataObjectSchema::fieldSpec`.
Use `$schema->fieldSpec($class, $field, DataObjectSchema::DB_ONLY | DataObjectSchema::UNINHERITED )`. Use `$schema->fieldSpec($class, $field, DataObjectSchema::DB_ONLY | DataObjectSchema::UNINHERITED )`.
Exclude `uninherited` option to search all tables in the class hierarchy. Exclude `uninherited` option to search all tables in the class hierarchy.
* Removed `DataObject::is_composite_field`. Use `DataObjectSchema::compositeField` instead. * Renamed `DataObject::is_composite_field()` to `DataObjectSchema::compositeField()`
* Removed `DataObject::custom_database_fields`. Use `DataObjectSchema::databaseFields` * Renamed `DataObject::custom_database_fields()`to `DataObjectSchema::databaseFields()`
or `DataObjectSchema::fieldSpecs` instead. or `DataObjectSchema::fieldSpecs()` instead.
* Removed `DataList::getRelation`, as it was mutable. Use `DataList::applyRelation` instead, which is immutable. * Removed `DataList::getRelation`, as it was mutable. Use `DataList::applyRelation` instead, which is immutable.
* Removed `DataList::applyFilterContext` private method
* `Member` Field 'RememberLoginToken' removed, replaced with 'RememberLoginHashes' has_many relationship * `Member` Field 'RememberLoginToken' removed, replaced with 'RememberLoginHashes' has_many relationship
* Removed `UpgradeSiteTreePermissionSchemaTask` * Removed `UpgradeSiteTreePermissionSchemaTask`
* Removed `EncryptAllPasswordsTask` * Removed `EncryptAllPasswordsTask`
* Removed `DBString::LimitWordCountXML()` method. Use `LimitWordCount` for XML safe version. * Removed `DBString::LimitWordCountXML()` method. Use `LimitWordCount()` for XML safe version.
* Removed `SiteTree::getExistsOnLive()`. Use `isPublished()` instead. * Removed `SiteTree::getExistsOnLive()`. Use `isPublished()` instead.
* Removed `SiteTree::getIsDeletedFromStage()`. Use `isOnDraft()` instead (inverse case). * Removed `SiteTree::getIsDeletedFromStage()`. Use `isOnDraft()` instead (inverse case).
* `DataObject.many_many` no longer supports triangular resolution. Both the `many_many` and `belongs_many_many` * Changed `DataObject.many_many` to remove triangular resolution. Both the `many_many` and `belongs_many_many`
must point directly to the specific class on the opposing side, not a subclass or parent. must point directly to the specific class on the opposing side, not a subclass or parent.
* `DataObject::validateModelDefinitions()` has been removed. Validation and parsing of config is now handled * Removed `DataObject::validateModelDefinitions()`. Validation and parsing of config is now handled
within `DataObjectSchema`. within `DataObjectSchema`.
* `CMSBatchAction_Delete` removed. Use `CMSBatchAction_Archive` instead. * `CMSBatchAction_Delete` removed. Use `CMSBatchAction_Archive` instead.
* Removed several DBDate methods: * Removed `Date::past_date()`
- `past_date` * Removed `Date::prior_monday()`
- `prior_monday` * Removed `Date::weekday()`
- `weekday` * Removed `Date::next_day()`
- `next_day` * Removed `Date::day_before()`
- `day_before` * Removed `Date::days_between()`
- `days_between` * Removed `Date::nice_format()`. Use locale-specific formatting for `Nice()`
* `nice_format` has been removed from `DBDate` / `DBTime` / `DBDatetime` has been removed in favour of * Removed `Time::nice_format()`. Use locale-specific formatting for `Nice()`
locale-specific formatting for Nice() * Removed `Datetime::nice_format()`. Use locale-specific formatting for `Nice()`
* Removed several `DBTime` methods: * Removed `Time::TwelveHour()`
- `TwelveHour` * Removed `Time::Nice24()`
- `Nice24` * Removed `Money::NiceWithShortname()`
* Removed some `DBMoney` methods due to lack of support in php-intl. * Removed `Money::NiceWithName()`
- `NiceWithShortname` * Removed `Money::getShortName()`
- `NiceWithName` * Removed `Money::getCurrencyName()`
- `getShortName` * Removed additional arguments from `Money::getSymbol()`. The result of this value is
- `getCurrencyName` now localised based on the currency code assigned to the `Money` instance
* Removed additional arguments from `DBMoney::getSymbol`. The result of this value is * Removed `Money::getAllowedCurrencies`. Apply validation to `MoneyField` instead.
now localised based on the currency code assigned to the `DBMoney` instance * Removed `Hierarchy::parentStack()` removed. Use `getAncestors()` instead
* Removed `DBMoney::getAllowedCurrencies`. Apply validation to `MoneyField` instead. * Removed `Hierarchy::doAllChildrenIncludingDeleted()`. Use `AllChildrenIncludingDeleted()` instead
* `Hierarchy` has lots of removed api: * Removed `Hierarchy::naturalNext()`
- `parentStack()` removed. Use `getAncestors()` instead * Removed `Hierarchy::naturalPrev()`
- `doAllChildrenIncludingDeleted()` removed. Use `AllChildrenIncludingDeleted()` instead. * Removed `Hierarchy::markingFinished()`
- `naturalNext` removed.
- `naturalPrev` removed.
- `markingFinished` removed.
### <a name="overview-filesystem"></a>Filesystem API ### <a name="overview-filesystem"></a>Filesystem
#### <a name="overview-filesystem-api"></a>Filesystem API Additions / Changes
* Image manipulations have been moved into a new `[ImageManipulation](api:SilverStripe\Assets\ImageManipulation)` trait. * Image manipulations have been moved into a new `[ImageManipulation](api:SilverStripe\Assets\ImageManipulation)` trait.
* `CMSFileAddController` removed. * Removed `CMSFileAddController`
* `UploadField::setAllowedFileCategories('image')` now excludes non-resizeable images. 'unresizeable_image' is * `UploadField::setAllowedFileCategories('image')` now excludes non-resizeable images. 'unresizeable_image' is
can be used to validate these types. can be used to validate these types.
* `Image_Backend` API now loads and saves from `AssetContainer` instances rather than local files. * `Image_Backend` API now loads and saves from `AssetContainer` instances rather than local files.
* The following File categories have been renamed: 'zip' to 'archive', 'doc' to 'document', and 'mov' to 'video' * The following File categories have been renamed: 'zip' to 'archive', 'doc' to 'document', and 'mov' to 'video'
* `File::updateLinks` no longer takes urls as parameters. All file links are now identified either by * `File::updateLinks()` no longer takes urls as parameters. All file links are now identified either by
the `DataObject::ID` in a `data-fileid` property, or via shortcodes. This is necessary because file the `DataObject::ID` in a `data-fileid` property, or via shortcodes. This is necessary because file
urls are no longer able to identify assets. urls are no longer able to identify assets.
* Extension point `HtmlEditorField::processImage` has been removed, and moved to `Image::regenerateImageHTML` * Extension point `HtmlEditorField::processImage` has been removed, and moved to `Image::regenerateImageHTML()`
* `Upload::load` now stores assets directly without saving into a `File` dataobject. * `Upload::load()` now stores assets directly without saving into a `File` dataobject.
* Protected file storage is now a core Framework API. See [/developer_guides/files/file_security] for * Protected file storage is now a core Framework API. See [/developer_guides/files/file_security] for
more information. more information.
* `File` is now versioned, and should be published before they can be used on the frontend. * `File` is now versioned, and should be published before they can be used on the frontend.
@ -2270,86 +2108,76 @@ The below methods have been added or had their functionality updated to `DBDate`
below for upgrade notes. below for upgrade notes.
* New filesystem abstraction including new `DBFile` database field to hold file references. * New filesystem abstraction including new `DBFile` database field to hold file references.
* `ShortcodeHandler` interface to help generate standard handlers for HTML shortcodes in the editor. * `ShortcodeHandler` interface to help generate standard handlers for HTML shortcodes in the editor.
* `handle_shortcode` methods are removed from `File` and `Image` and moved to their own classes in `SilverStripe\Assets\Shortcodes` and are named `FileShortcodeProvider` and `ImageShortcodeProvider` respectively. * `File::handle_shortcode()` and `Image::handle_shortcode()` have moved to their own classes in `SilverStripe\Assets\Shortcodes`,
and are named `FileShortcodeProvider` and `ImageShortcodeProvider` respectively.
* `AssetNameGenerator` interface, including a `DefaultAssetNameGenerator` implementation, which is used to generate * `AssetNameGenerator` interface, including a `DefaultAssetNameGenerator` implementation, which is used to generate
renaming suggestions based on an original given filename in order to resolve file duplication issues. renaming suggestions based on an original given filename in order to resolve file duplication issues.
* `GeneratedAssetHandler` API now used to store and manage generated files (such as those used for error page * `GeneratedAssetHandler` API now used to store and manage generated files (such as those used for error page
cache or combined files). cache or combined files).
* `Requirements_Minifier` API can be used to declare any new mechanism for minifying combined required files. * `Requirements_Minifier` API can be used to declare any new mechanism for minifying combined required files.
By default this api is provided by the `JSMinifier` class, but user code can substitute their own. By default this api is provided by the `JSMinifier` class, but user code can substitute their own.
* `AssetField` formfield to provide an `UploadField` style uploader for the new `DBFile` database field. * `AssetField` form field to provide an `UploadField` style uploader for the new `DBFile` database field.
* `AssetControlExtension` is applied by default to all DataObjects, in order to support the management * `AssetControlExtension` is applied by default to all DataObjects, in order to support the management
of linked assets and file protection. of linked assets and file protection.
* `ProtectedFileController` class is used to serve up protected assets. * `ProtectedFileController` class is used to serve up protected assets.
* `AssetAdaptor` has a new config `default_server` which helps tell the code which server type to use if no matching type was found by scanning the server software - defaults to `apache` * `AssetAdaptor` has a new config `default_server` which helps tell the code which server type to use if no matching type was found by scanning the server software - defaults to `apache`
#### <a name="overview-filesystem-removed"></a>Filesystem removed API The following image manipulations have been removed:
The following image manipulations previously deprecated has been removed: * Renamed `Image::SetRatioSize()` to `Fit()`
* Renamed `Image::SetWidth()` to `ScaleWidth()`
* `Image::SetRatioSize` superceded by `Fit` * Renamed `Image::SetHeight()` to `ScaleHeight()`
* `Image::SetWidth` superceded by `ScaleWidth` * Renamed `Image::SetSize()` to `Pad()`
* `Image::SetHeight` superceded by `ScaleHeight` * Renamed `Image::PaddedImage()` to `Pad()`
* `Image::SetSize` superceded by `Pad` * Renamed `Image::CroppedImage()` to `Fill()`
* `Image::PaddedImage` superceded by `Pad` * Renamed `Image::AssetLibraryPreview()` to `PreviewThumbnail()`
* `Image::CroppedImage` superceded by `Fill` * Renamed `Image::AssetLibraryThumbnail()` to `CMSThumbnail()`
* `Image::AssetLibraryPreview` superceded by `PreviewThumbnail`
* `Image::AssetLibraryThumbnail` superceded by `CMSThumbnail`
The following `File` methods have been removed. Since there is no longer any assumed local path for any file, The following `File` methods have been removed. Since there is no longer any assumed local path for any file,
methods which dealt with such paths may no longer be relied on. methods which dealt with such paths may no longer be relied on.
* `File::deletedatabaseOnly` * Removed `File::deletedatabaseOnly()`
* `File::link_shortcode_handler` renamed to `handle_shortcode` * Renamed `File::link_shortcode_handler()` to `handle_shortcode()`
* `File::setParentID` * Removed `File::setParentID()`
* `File::getFullPath` * Removed `File::getFullPath()`
* `File::getRelativePath` * Removed `File::getRelativePath()`
* `File::Content` database field is removed * Removed `File::Content` database field (wasn't used by core)
Image manipulations have been moved out of Image.php and now available to any File or DBFile which has the Image manipulations have been moved out of `Image` and now available to any `File` or `DBFile` which has the
appropriate mime types. The following file manipulations classes and methods have been removed: appropriate mime types. The following file manipulations classes and methods have been removed:
* `Image_Cached` class * Removed `Image_Cached`
* `Image::regenerateFormattedImages` method * Removed `Image::regenerateFormattedImages()`
* `Image::getGeneratedImages` method * Removed `Image::getGeneratedImages()`
* `Image::deleteFormattedImages` method * Removed `Image::deleteFormattedImages()`
* `Image::handle_shortcode` moved to `SilverStripe\Assets\Shortcodes\ImageShortcodeProvider::handle_shortcode` * Removed `Image::handle_shortcode()` moved to `SilverStripe\Assets\Shortcodes\ImageShortcodeProvider::handle_shortcode()`
* `AssetAdmin::deleteunusedthumbnails` method * Removed `AssetAdmin::deleteunusedthumbnails()`
* `AssetAdmin::getUnusedThumbnails` method * Removed `AssetAdmin::getUnusedThumbnails()`
* Removed `Folder_UnusedAssetsField`
* Removed `Folder::syncChildren()`
* Removed `Folder::constructChild()`
* Removed `Folder::addUploadToFolder()`
* Removed `RegenerateCachedImagesTask`
* Removed `CleanImageManipulationCache`
* Removed `Filesystem::sync()`
* Removed `AssetAdmin::doSync()`
Many `Folder` api have also been removed: ### <a name="overview-template"></a>Templates and Form
* `Folder_UnusedAssetsField` class * Upgrade to TinyMCE 4.x
* `Folder::syncChildren` method * Templates now use a standard template lookup system via `SSViewer::get_templates_by_class()`
* `Folder::constructChild` method
* `Folder::addUploadToFolder` method
The following filesystem synchronisation methods and tasks are also removed
* `RegenerateCachedImagesTask` class
* `CleanImageManipulationCache` class
* `Filesystem::sync` method
* `AssetAdmin::doSync` method
### <a name="overview-template"></a>Template and Form API
#### <a name="overview-template-api"></a>Template and Form API Additions / Changes
* Upgrade of TinyMCE to version 4.
* Templates now use a standard template lookup system via `SSViewer::get_templates_by_class`
which builds a candidate list for a given class. Actual resolution of existing templates which builds a candidate list for a given class. Actual resolution of existing templates
for any list of candidates is actually performed by `SSViewer::chooseTemplate` for any list of candidates is actually performed by `SSViewer::chooseTemplate`
* `HtmlEditorConfig` is now an abstract class, with a default implementation `TinyMCEConfig` for the built in * `HtmlEditorConfig` is now an abstract class, with a default implementation `TinyMCEConfig` for the built in
TinyMCE editor. TinyMCE editor.
* `HtmlEditorField::setEditorConfig` may now take an instance of a `HtmlEditorConfig` class, as well as a * `HtmlEditorField::setEditorConfig()` may now take an instance of a `HtmlEditorConfig` class, as well as a
standard config identifier name. standard config identifier name.
* `HeaderField` requires a `$name` constructor argument (`new HeaderField('MyName', 'My Title')` * `HeaderField` requires a `$name` constructor argument (`new HeaderField('MyName', 'My Title')`
* `default_cast` is now enforced on all template variables. See upgrading notes below. * `FormField` templates no longer look in the 'forms' folder for templates. As all form fields are
* FormField templates no longer look in the 'forms' folder for templates. As all form fields are
now namespaced, the path for these templates will now match the namespace of the given class instead. now namespaced, the path for these templates will now match the namespace of the given class instead.
* `$module` parameter in `themedCSS` and `themedJavascript` removed. * `$module` parameter in `themedCSS()` and `themedJavascript()` removed.
* Theme selector has been removed from SiteConfig. Please use `SSViewer.themes` config instead. * Ability to select a theme through `admin/settings` has been removed from `SiteConfig`. Please use `SSViewer.themes` config instead.
* `FormAction::setValidationExempt` can be used to turn on or off form validation for individual actions * `FormAction::setValidationExempt())` can be used to turn on or off form validation for individual actions
* GridField edit form now has improved support for versioned DataObjects, with basic publishing * GridField edit form now has improved support for versioned DataObjects, with basic publishing
actions available when editing records. actions available when editing records.
* `PopoverField` added to provide popup-menu behaviour in react forms (currently not available for * `PopoverField` added to provide popup-menu behaviour in react forms (currently not available for
@ -2358,112 +2186,96 @@ The following filesystem synchronisation methods and tasks are also removed
for building their own form fields. This builds a form based on a given controller and model, for building their own form fields. This builds a form based on a given controller and model,
and can be customised on a case by case basis. This has been introduced initially for the asset-admin and can be customised on a case by case basis. This has been introduced initially for the asset-admin
module. module.
* Introduced `AssetAdmin\Forms\UploadField` as a react-friendly version of UploadField. This may also * Introduced `AssetAdmin\Forms\UploadField` as a React-friendly version of `UploadField`. This may also
be used in normal entwine forms for managing files in a similar way to UploadField. However, this be used in normal entwine forms for managing files in a similar way to `UploadField`. However, this
does not support inline editing of files. does not support inline editing of files.
* Added method `FormField::setSubmittedValue($value, $data)` to process input submitted from form * Added method `FormField::setSubmittedValue($value, $data)` to process input submitted from form
submission, in contrast to `FormField::setValue($value, $data)` which is intended to load its submission, in contrast to `FormField::setValue($value, $data)` which is intended to load its
value from the ORM. The second argument to setValue() has been added. value from the ORM. The second argument to `setValue()` has been added.
* `HTMLValue` service name is now fully qualified `SilverStripe\View\Parsers\HTMLValue`. * `FormField::create_tag()` moved to `SilverStripe\View\HTML->createTag()`.
* FormField::create_tag() has been moved to `HTML` class and renamed to createTag. Invoke with
`HTML::createTag()`.
The following methods and properties on `Requirements_Backend` have been renamed: <a name="requirements"></a>The following methods and properties on `Requirements_Backend` have been renamed:
* `Requirements_Backund::$combine_files` made protected and renamed `$combinedFiles` * Renamed `$combine_files` to `$combinedFiles`
* `Requirements_Backend::$combine_js_with_min` made protected and renamed `$minifyCombinedFiles` * Renamed `$combine_js_with_min` to `$minifyCombinedFiles`
* `Requirements_Backend::$write_header_comments` made protected and renamed `$writeHeaderComment` * Renamed `$write_header_comments` to `$writeHeaderComment`
* `Requirements_Backend::$write_js_to_body` made protected and renamed to `$writeJavascriptToBody` * Renamed `$write_js_to_body` to `$writeJavascriptToBody`
* `Requirements_Backend::$force_js_to_bottom` renamed to `$forceJSToBottom` * Renamed `$force_js_to_bottom` to `$forceJSToBottom`
* `get_combined_files_enabled` renamed to `getCombinedFilesEnabled` * Renamed `get_combined_files_enabled()` to `getCombinedFilesEnabled()`
* `set_combined_files_enabled` renamed to `setCombinedFilesEnabled` * Renamed `set_combined_files_enabled()` to `setCombinedFilesEnabled()`
* `get_suffix_requirements` renamed to `getSuffixRequirements` * Renamed `get_suffix_requirements()` to `getSuffixRequirements()`
* `set_suffix_requirements` renamed to `setSuffixRequirements` * Renamed `set_suffix_requirements()` to `setSuffixRequirements()`
* `get_custom_scripts` renamed to `getCustomScripts` * Renamed `get_custom_scripts()` to `getCustomScripts()`
* `unblock_all` renamed to `unblockAll` * Renamed `unblock_all()` to `unblockAll()`
* `include_in_response` renamed to `includeInResponse` * Renamed `include_in_response()` to `includeInResponse()`
* `combine_files` renamed to `combineFiles` * Renamed `combine_files()` to `combineFiles()`
* `get_combine_files` renamed to `getCombinedFiles` * Renamed `get_combine_files()` to `getCombinedFiles()`
* `clear_combined_files` renamed to `clearCombinedFiles` * Renamed `clear_combined_files()` to `clearCombinedFiles()`
* `process_combined_files` renamed to `processCombinedFiles` * Renamed `process_combined_files()` to `processCombinedFiles()`
* `set_write_js_to_body` renamed to `setWriteJavascriptToBody` * Renamed `set_write_js_to_body()` to `setWriteJavascriptToBody()`
* `set_force_js_to_bottom` renamed to `setForceJSToBottom` * Renamed `set_force_js_to_bottom()` to `setForceJSToBottom()`
* Added `get_minify_combined_js_files()` and `set_minify_combined_js_files()`
New methods on `Requirements` are added to access these: * Added `get_force_js_to_bottom()`
* Added `get_write_js_to_body()`
* `get_minify_combined_js_files` * Changed `includeInHTML()` to remove the first parameter (`$template`)
* `set_minify_combined_js_files`
* `get_force_js_to_bottom`
* `get_write_js_to_body`
Some methods on `Requirements` have had their method signatures changed:
* `includeInHTML` has had the first parameter $template removed as it was previously deprecated.
A new config `Requirements_Backend.combine_in_dev` has been added in order to allow combined files to be A new config `Requirements_Backend.combine_in_dev` has been added in order to allow combined files to be
forced on during development. If this is off, combined files is only enabled in live environments. forced on during development. If this is off, combined files is only enabled in live environments.
Form validation has been refactored significantly. A new `FormMessage` trait has been created to <a name="form-validation"></a>Form validation has been refactored significantly. A new `FormMessage` trait has been created to
handle field-level and form-level messages. This has the following properties: handle `FormField` and `Form` messages. This trait has a new`setMessage()` API to assign a message, type, and cast.
Use `getMessage()`, `getMessageType()`, `getMessageCast()` and `getMessageCastingHelper()` to retrieve them.
* `setMessage` to assign a message, type, and cast
* `getMessage` retrieves the message string
* `getMessageType` retrieves the message type (E.g. error, good, info)
* `getMessageCast` retrieves the cast type
* `getMessageCastingHelper` retrieves the DBField cast to use for the appropriate message cast
* `getSchemaMessage` encodes this message for form schema use in ReactJS.
`Form` behaviour methods have been changed: `Form` behaviour methods have been changed:
* __construct() now allows a RequestHandler to be passed as a first argument, rather than a Controller. * `__construct()` now allows a `RequestHandler` to be passed as a first argument, rather than a controller.
in addition this argument is now optional. This allows forms to be constructed as a model only. In addition this argument is now optional. This allows forms to be constructed as a model only.
* `validate` is replaced with `validationResult` instead, which returns a `ValidationResult` instance. * `validate` is replaced with `validationResult` instead, which returns a `ValidationResult` instance.
This is no longer automatically persisted in the state by default, unless a redirection occurs. This is no longer automatically persisted in the state by default, unless a redirection occurs.
You can also save any response in the state by manually invoking `saveFormState` inside a custom You can also save any response in the state by manually invoking `saveFormState` inside a custom
validation response handler. validation response handler.
* `setupFormErrors` renamed to `restoreFormState` * Renamed `setupFormErrors()` to `restoreFormState()`
* `resetValidation` renamed to `clearFormState` * Renamed `resetValidation()` to `clearFormState()`
* `loadMessagesFrom` method created to load a ValidationResult into a form. * Added `loadMessagesFrom()` to load a `ValidationResult` into a form.
* `setMessage`. third parameter is now $cast type * Changed `setMessage()` to accept `$cast` as a third parameter (instead of a `$escapeHtml` boolean)
* `messageForForm` removed. Use `setMessage` or `sessionMessage` instead. * Removed `messageForForm()`. Use `setMessage()` or `sessionMessage()` instead.
* `getSessionValidationResult` / `setSessionValidationResult` used to get / set session errors * Added `getSessionValidationResult()` / `setSessionValidationResult()` to get / set session errors
* `getSessionData` / `setSessionData` used to get / set field values cached in the session * Added `getSessionData()` / `setSessionData()` to get / set field values cached in the session
* `getAjaxErrorResponse` and `getRedirectReferer` created to simplify `getValidationErrorResponse` * Removed `addErrorMessage()`. Use `sessionMessage()` or `sessionError()` to add a
* `addErrorMessage` removed. Users can either use `sessionMessage` or `sessionError` to add a form level message, throw a `ValidationException` during submission, or add a custom validator.
form level message, throw a ValidationException during submission, or add a custom validator.
* `Form` is no longer a `RequestHandler`, but implements the `HasRequestHandler` interface and returns * `Form` is no longer a `RequestHandler`, but implements the `HasRequestHandler` interface and returns
a `FormRequestHandler` instance from `getRequestHandler()`. the `Form` constructor no longer has a `FormRequestHandler` instance from `getRequestHandler()`. the `Form` constructor no longer has
any mandatory parameters, and the first parameter allows a non-`Controller` `RequestHandler` to be any mandatory parameters, and the first parameter allows a non-`Controller` `RequestHandler` to be
passed. Certain methods have been moved from `Form` to `FormRequestHandler`: passed.
* `buttonClicked` * Moved `buttonClicked()` to `FormRequestHandler`
* `checkAccessAction` * Moved `checkAccessAction()` to `FormRequestHandler`
* `handleField` * Moved `handleField()` to `FormRequestHandler`
* `httpSubmission` * Moved `httpSubmission()` to `FormRequestHandler`
* `Link` * Moved `Link()` to `FormRequestHandler`
`Validator` methods have changed: `Validator` methods have changed:
* `validate` method now returns a `ValidationResult` instance. * Changed `validate()` to return a `ValidationResult` instance.
* `requireField` method removed. Use `RequiredFields` subclass instead. * Removed `requireField()`. Use `RequiredFields` subclass instead.
`ValidationResult` now has these methods: `ValidationResult` now has these methods:
* `serialize` / `unserialize` for saving within session state * Added `serialize()` / `unserialize()` for saving within session state
* `messageList` renamed to `getMessages` * Renamed `messageList()` to `getMessages()`
* `error` method replaced with `addMessage` / `addError` / `addFieldMessage` / `addFieldError` * Changed `error()` to `addMessage()` / `addError()` / `addFieldMessage()` / `addFieldError()`
* `valid` renamed to `isValid` * Renamed `valid()` to `isValid()`
`ValidationException` has these changes: `ValidationException` has these changes:
* `$message` second constructor parameter is removed. Constructor only accepts `$result`, * Changed constructor to remove second argument (`$message`). It now only accepts `$result`,
which may be a string, and optional `$code` which may be a string, and optional `$code`
New `DatetimeField` methods replace `getConfig()` / `setConfig()`: <a name="datetimefield"></a>New `DatetimeField` methods replace `getConfig()` / `setConfig()`:
* `getTimezone()` / `setTimezone()` * Added `getTimezone()` / `setTimezone()`
* `getDateTimeOrder()` / `setDateTimeOrder()` * Added `getDateTimeOrder()` / `setDateTimeOrder()`
* `getLocale()` / `setLocale()` * Added `getLocale()` / `setLocale()`
* `datavaluefield` config is removed as internal data value is now fixed to ISO 8601 format * Removed `datavaluefield` config as internal data value is now fixed to ISO 8601 format
The `DatetimeField` has changed behaviour: The `DatetimeField` has changed behaviour:
@ -2476,12 +2288,12 @@ The `DatetimeField` has changed behaviour:
* It no longer accepts `setValue()` as an array with 'date' and 'time' keys * It no longer accepts `setValue()` as an array with 'date' and 'time' keys
* Added `getHTML5()` / `setHTML5()` * Added `getHTML5()` / `setHTML5()`
New `DateField` methods replace `getConfig()` / `setConfig()`: <a name="datefield"></a>New `DateField` methods replace `getConfig()` / `setConfig()`:
* `getDateFormat()` / `setDateFormat()` * Added `getDateFormat()` / `setDateFormat()`
* `getMinDate()` / `setMinDate()` * Added `getMinDate()` / `setMinDate()`
* `getMaxDate()` / `setMaxDate()` * Added `getMaxDate()` / `setMaxDate()`
* `getLocale()` / `setLocale()` * Added `getLocale()` / `setLocale()`
The `DateField` has changed behavior: The `DateField` has changed behavior:
@ -2492,65 +2304,49 @@ The `DateField` has changed behavior:
* The `dmyfields` option has been replced with native HTML5 behaviour (as one single `<input type=date>`). * The `dmyfields` option has been replced with native HTML5 behaviour (as one single `<input type=date>`).
* `getClientLocale` / `setClientLocale` have been removed (handled by `DateField->locale` and browser settings) * `getClientLocale` / `setClientLocale` have been removed (handled by `DateField->locale` and browser settings)
New `TimeField` methods replace `getConfig()` / `setConfig()` <a name="timefield"></a>New `TimeField` methods replace `getConfig()` / `setConfig()`
* `getTimeFormat()` / `setTimeFormat()` * Added `getTimeFormat()` / `setTimeFormat()`
* `getLocale()` / `setLocale()` * Added `getLocale()` / `setLocale()`
* `getClientConfig()` has been removed (in favour of `setHTML5()`)
#### <a name="overview-template-removed"></a>Template and Form Removed API Further API changes:
* Removed `TabularStyle` * Removed `TabularStyle`
* Removed `NestedForm` * Removed `NestedForm`
* Removed `FieldList` methods: * Removed `FieldList->getTabPathRewrites()`
* `getTabPathRewrites` * Removed `FieldList->setTabPathRewrites()`
* `setTabPathRewrites` * Removed `FieldList->rewriteTabPath()`
* `rewriteTabPath` * Removed `Form->transformTo()`
* Removed `Form` methods (see above for replacements): * Removed `Form->callfieldmethod()`
* `transformTo` * Removed `Form->single_field_required()`
* `callfieldmethod` * Removed `Form->current_action()`
* `single_field_required` * Removed `Form->set_current_action()`
* `current_action` * Removed `Form->testSubmission()`
* `set_current_action` * Removed `Form->testAjaxSubmission()`
* `setupFormErrors` * Removed `ValidationResult->messageList()`
* `resetValidation` * Removed `ValidationResult->codeList()`
* `messageForForm` * Removed `ValidationResult->message()`
* `addErrorMessage` * Removed `ValidationResult->starredList()`
* `testSubmission` * Removed `ValidationResult->error()`
* `testAjaxSubmission` * Removed `ValidationResult->valid()`
* Removed `Validator::requireField()` method.
* Removed `ValidationResult` (see above for replacements):
* `messageList`
* `codeList`
* `message`
* `starredList`
* `error`
* `valid`
* Removed `ReportAdminForm.ss` template * Removed `ReportAdminForm.ss` template
* `FormField::dontEscape()` has been removed. Escaping is now managed on a class by class basis. * Removed `FormField::dontEscape()`. Escaping is now managed on a class by class basis.
* Removed `PermissionCheckboxSetField::getAssignedPermissionCodes()` (never implemented) * Removed `PermissionCheckboxSetField::getAssignedPermissionCodes()`
* `Requirements::delete_combined_files()` and `Requirements::delete_combined_files()` methods have been removed * Removed `Requirements::delete_combined_files()`
as they are obsolete.
* Removed `DatetimeField`, `DateField` and `TimeField` methods `getConfig` and `setConfig`. Individual
getters and setters for individual options are provided instead. See above for list of new methods.
* Removed `NumericField_Readonly`. Use `setReadonly(true)` instead. * Removed `NumericField_Readonly`. Use `setReadonly(true)` instead.
* `SSViewer` deprecated methods removed: * Removed `SSViewer->set_source_file_comments()`
* `set_source_file_comments()` * Removed `SSViewer->get_source_file_comments()`
* `get_source_file_comments()` * Removed `SSViewer->getOption()`
* `getOption` * Removed `SSViewer->setOption()`
* `setOption`
* Removed `MemberDatetimeOptionsetField` (no replacement) * Removed `MemberDatetimeOptionsetField` (no replacement)
* Removed `DateField_View_JQuery` (replaced with native HTML5 support in `DateField`) * Removed `DateField_View_JQuery` (replaced with native HTML5 support in `DateField`)
* The following HTMLEditorField_* classes have been removed: * Moved `HTMLEditorField_Toolbar` to `SilverStripe\Admin\ModalController`
* `HTMLEditorField_Toolbar` (replaced With `ModalController` in admin module) * Moved `HTMLEditorField_Embed` to`SilverStripe\AssetAdmin\EmbedResource`
* `HTMLEditorField_Embed` (replaced with `EmbedResource` in asset-admin module) * Removed `HTMLEditorField_File`
* `HTMLEditorField_File` * Removed `HTMLEditorField_Flash`
* `HTMLEditorField_Flash` * Removed `HTMLEditorField_Image`
* `HTMLEditorField_Image`
### <a name="overview-i18n"></a>i18n API ### <a name="overview-i18n"></a>i18n
#### <a name="overview-i18n-api"></a>i18n API Additions / Changes
* Upgrade of i18n to symfony/translation * Upgrade of i18n to symfony/translation
* Localisation based on language-only (without any specific locale) is now supported * Localisation based on language-only (without any specific locale) is now supported
@ -2560,52 +2356,48 @@ New `TimeField` methods replace `getConfig()` / `setConfig()`
for all DataObject subclasses, rather than just the basename without namespace. for all DataObject subclasses, rather than just the basename without namespace.
* i18n key for locale-respective pluralisation rules added as '.PLURALS'. These can be configured * i18n key for locale-respective pluralisation rules added as '.PLURALS'. These can be configured
within yaml in array format as per [ruby i18n pluralization rules](http://guides.rubyonrails.org/i18n.html#pluralization). within yaml in array format as per [ruby i18n pluralization rules](http://guides.rubyonrails.org/i18n.html#pluralization).
* `i18n.all_locales` config moved to `SilverStripe\i18n\Data\Locales.locales` * Moved `i18n.all_locales` config setting to `SilverStripe\i18n\Data\Locales.locales`
* `i18n.common_languages` config moved to `SilverStripe\i18n\Data\Locales.languages` * Moved `i18n.common_languages` config setting to `SilverStripe\i18n\Data\Locales.languages`
* `i18n.likely_subtags` config moved to `SilverStripe\i18n\Data\Locales.likely_subtags` * Moved `i18n.likely_subtags` config setting to `SilverStripe\i18n\Data\Locales.likely_subtags`
* `i18n.tinymce_lang` config moved to `SilverStripe\Forms\HTMLEditor\TinyMCEConfig.tinymce_lang` * Moved `i18n.tinymce_lang` config setting to `SilverStripe\Forms\HTMLEditor\TinyMCEConfig.tinymce_lang`
* `i18n::get_tinymce_lang()` moved to `SilverStripe\Forms\HTMLEditor\TinyMCEConfig::get_tinymce_lang()` * Moved `i18n::get_tinymce_lang()` to `SilverStripe\Forms\HTMLEditor\TinyMCEConfig::get_tinymce_lang()`
* `i18n::get_locale_from_lang()` moved to `SilverStripe\i18n\Data\Locales::localeFromLang()` * Moved `i18n::get_locale_from_lang()` to `SilverStripe\i18n\Data\Locales::localeFromLang()`
* `i18n::get_lange_from_locale()` moved to `SilverStripe\i18n\Data\Locales::langFromLocale()` * Moved `i18n::get_lange_from_locale()` to `SilverStripe\i18n\Data\Locales::langFromLocale()`
* `i18n::validate_locale()` moved to `SilverStripe\i18n\Data\Locales::validate()` * Moved `i18n::validate_locale()` to `SilverStripe\i18n\Data\Locales::validate()`
* `i18n::get_common_languages()` moved to `SilverStripe\i18n\Data\Locales::getLanguages()` * Moved `i18n::get_common_languages()` to `SilverStripe\i18n\Data\Locales::getLanguages()`
* `i18n::get_locale_name()` moved to `SilverStripe\i18n\Data\Locales::localeName()` * Moved `i18n::get_locale_name()` to `SilverStripe\i18n\Data\Locales::localeName()`
* `i18n::get_language_name()` moved to `SilverStripe\i18n\Data\Locales::languageName()` * Moved `i18n::get_language_name()` to `SilverStripe\i18n\Data\Locales::languageName()`
* `i18n.module_priority` config moved to `SilverStripe\i18n\Data\Sources.module_priority` * Moved `i18n.module_priority` config setting to `SilverStripe\i18n\Data\Sources.module_priority`
* `i18n::get_owner_module()` moved to `SilverStripe\Core\Manifest\ClassManifest::getOwnerModule()` * Moved `i18n::get_owner_module()` to `SilverStripe\Core\Manifest\ClassManifest::getOwnerModule()`
This now returns a `Module` object instance instead of a string. This now returns a `Module` object instance instead of a string.
* `i18n::get_existing_translations()` moved to `SilverStripe\i18n\Data\Sources::getKnownLocales()` * Moved `i18n::get_existing_translations()` to `SilverStripe\i18n\Data\Sources::getKnownLocales()`
* Removed `Zend_Translate`
* Changed `i18n::_t()` to remove support for sprintf-style `%s` arguments
* Changed `i18n::_t()` to remove support for non-associative injection with named parameters
* Removed `i18n::get_language_name()`
* Removed `i18n::get_language_code()`
* Removed `i18n::get_common_locales()`
* Removed `i18n.common_locales`
#### <a name="overview-i18n-removed"></a>i18n API Removed API ### <a name="overview-mailer"></a>Email
* `Zend_Translate` removed * Changed `Mailer` to an interface
* `i18n::_t()` Support for sprintf-style `%s` arguments deprecated
* `i18n::_t()` Using non-associative injection with named parameters is now an error
* `i18n::get_language_name()` removed.
* `i18n::get_language_code()` removed.
* `i18n::get_common_locales()` removed.
* `i18n.common_locales` config removed
#### <a name="overview-mailer"></a>Email Additions / Changes
* `Mailer` converted to an interface
* `SwfitMailer` added as new default mailer
* `Email` re-written to be powered by [SwiftMailer](https://github.com/swiftmailer/swiftmailer) * `Email` re-written to be powered by [SwiftMailer](https://github.com/swiftmailer/swiftmailer)
* Default template body variable renamed from `$Body` to `$EmailContent` * Default template body variable renamed from `$Body` to `$EmailContent`
* `$email->setTemplate()` renamed to `$email->setHTMLTemplate()` * Renamed `Email->setTemplate()` to `Email->setHTMLTemplate()`
* Added `$email->setPlainTemplate` for rendering plain versions of email * Added `Email->setPlainTemplate()` for rendering plain versions of email
* `$email->populateTemplate()` has been replaced with `$email->setData()` * Renamed `Email->populateTemplate()` to `Email->setData()`
### <a name="overview-testing"></a>SapphireTest ### <a name="overview-testing"></a>SapphireTest
* `is_running_tests()` is no longer public and user code should not rely on this. Test-specific behaviour * `is_running_tests()` is no longer public and user code should not rely on this. Test-specific behaviour
should be implemented in `setUp()` and `tearDown()` should be implemented in `setUp()` and `tearDown()`
* `setUpOnce` removed. Please use `setUpBeforeClass` * Removed `setUpOnce()`. Please use `setUpBeforeClass()`
* `tearDownOnce` removed. Please use `tearDownAfterClass` * Removed `tearDownOnce()`. Please use `tearDownAfterClass()`
* `TestListener` class removed * Removed `TestListener`
* `SapphireTest::$requiredExtensions` renamed to `SapphireTest::$required_extensions` and made static * Renamed `$requiredExtensions` to `$required_extensions` (and made static)
* `SapphireTest::$extraDataObjects` renamed to `SapphireTest::$extra_dataobjects` and made static * Renamed `$extraDataObjects` to `$extra_dataobjects` (and made static)
* `SapphireTest::$extraControllers` renamed to `SapphireTest::$extra_controllers` and made static * Renamed `$extraControllers` to `$extra_controllers` (and made static)
### <a name="overview-security"></a>Security ### <a name="overview-security"></a>Security
@ -2613,5 +2405,5 @@ New `TimeField` methods replace `getConfig()` / `setConfig()`
you will need to define this method and return a short name describing the login method. you will need to define this method and return a short name describing the login method.
* `MemberLoginForm` has a new constructor argument for the authenticator class, although this is usually * `MemberLoginForm` has a new constructor argument for the authenticator class, although this is usually
constructed by `MemberAuthenticator` and won't affect normal use. constructed by `MemberAuthenticator` and won't affect normal use.
* `Authenticator` methods `register` and `unregister` are deprecated in favour of using `Config` * `Authenticator` methods `register()` and `unregister()` are deprecated in favour of using `Config`
* Unused `SetPassword` property removed from `Member`. Use `Member::changePassword` or set `Password` directly. * Unused `SetPassword` property removed from `Member`. Use `Member::changePassword` or set `Password` directly.