mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
2414 lines
111 KiB
Markdown
2414 lines
111 KiB
Markdown
# 4.0.0 (unreleased)
|
||
|
||
## Introduction
|
||
|
||
This version introduces many breaking changes, which in most projects can be managed through a combination
|
||
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
|
||
|
||
## Overview {#overview}
|
||
|
||
* Minimum version dependencies have increased; PHP 5.5 and Internet Explorer 11 (or other modern browser)
|
||
is required.
|
||
* All code earlier marked as deprecated for 4.0 has now been removed (check our
|
||
[deprecation process](/contributing/release_process))
|
||
* 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
|
||
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
|
||
[upgrading notes](#upgrading)).
|
||
* 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
|
||
now exists in addition to references via the existing `File` dataobject. File security and protected files
|
||
are now a core feature ([details](#asset-storage))
|
||
* A new front-end development process has been developed for the construction of javascript based components,
|
||
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
|
||
powered sections.
|
||
* 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/))
|
||
* 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/).
|
||
* Versioning is now a much more powerful feature, with the addition of campaigns to allow batches of related
|
||
or inter-dependent objects to be published as a single "changeset" ([details](#changeset)).
|
||
* Dependencies between versioned objects can be
|
||
declared using the new ownership API, so that developers can ensure that relational consistency is
|
||
maintained during publishing ([details](#ownership))
|
||
This new system can be managed via the new "Campaigns" CMS section ([blog post](https://www.silverstripe.org/blog/campaigns-in-silverstripe-4/))
|
||
* Template variable casting (e.g. `<h1>$Title</h1>`) is enforced by default, which will ensure safe HTML encode
|
||
unless explicitly opted out ([details](#template-casting))
|
||
* Themes are now configured to cascade, where you can specify a list of themes, and have the template engine
|
||
search programatically through a prioritised list when resolving template and CSS file paths.
|
||
* Removed module path constants (e.g. `FRAMEWORK_PATH`) and support for hardcoded file paths (e.g. `mysite/css/styles.css`) ([details](#module-paths))
|
||
* Replaced Zend_Translate with symfony/translation ([details](#i18n))
|
||
* Replaced `Zend_Cache` and the `Cache` API with a PSR-16 implementation (symfony/cache) ([details](#cache))
|
||
* `_ss_environment.php` files have been removed in favour of `.env` and "real" environment variables ([details](#env)).
|
||
* Behat support updated to v3 (
|
||
[details](https://github.com/silverstripe/silverstripe-behat-extension))
|
||
* 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.
|
||
* Dependencies can managed via [recipe-plugin](https://github.com/silverstripe/recipe-plugin). See [recipe-core](https://github.com/silverstripe/recipe-core) and [recipe-cms](https://github.com/silverstripe/recipe-cms) as examples.
|
||
* Authentication has been upgraded to a modular approach using re-usable interfaces and easier to hook in to LoginHandlers ([details](#custom-authenticators)).
|
||
* 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
|
||
|
||
## Upgrading Guide {#upgrading}
|
||
|
||
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
|
||
some automated processes that users can run.
|
||
|
||
### Composer dependency update {#deps}
|
||
|
||
As a first step, you need to update your composer dependencies.
|
||
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.
|
||
|
||
Backup your existing `composer.json` and overwrite it with the following content:
|
||
|
||
```json
|
||
{
|
||
"name": "myvendor/myproject",
|
||
"require": {
|
||
"silverstripe/recipe-cms": "^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).
|
||
|
||
Note: If you have issues with a pre-existing composer install you can force a clean re-install with the
|
||
below commands:
|
||
|
||
```bash
|
||
rm -rf vendor/composer
|
||
rm composer.lock
|
||
composer update
|
||
```
|
||
|
||
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 {#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 {#index-php-rewrites}
|
||
|
||
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`.
|
||
If you are running Apache, adjust your `.htaccess` file. For other webservers,
|
||
please consult the [installation guides](getting_started/installation/).
|
||
Since 4.0, URL rewrite capabilities are required,
|
||
unless you PHP's built-in webserver through [silverstripe/serve](https://github.com/silverstripe/silverstripe-serve).
|
||
|
||
The [upgrader tool](https://github.com/silverstripe/silverstripe-upgrader/) has a task runner which
|
||
automates this process for you.
|
||
|
||
Install the upgrader:
|
||
|
||
```
|
||
export PATH=$PATH:~/.composer/vendor/bin/
|
||
composer global require silverstripe/upgrader
|
||
```
|
||
|
||
Run the upgrader:
|
||
|
||
```
|
||
cd ~/my-project-root
|
||
upgrade-code doctor
|
||
```
|
||
|
||
This will ensure that your `.htaccess` and `index.php` are set to a reasonable default value
|
||
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),
|
||
you'll need to manually reapply these to the copied default file.
|
||
|
||
### Renamed and namespaced classes {#namespaced-classes}
|
||
|
||
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.
|
||
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:
|
||
|
||
```
|
||
cd ~/my-project-root
|
||
~/.composer/vendor/bin/upgrade-code upgrade ./mysite --write
|
||
```
|
||
|
||
If you want to do a dry-run, omit the `--write` option to see a preview of a diff of
|
||
all changed project files.
|
||
|
||
This will resolve the majority of upgrading work, but we strongly recommend reviewing the diff
|
||
running some regression testing on your functionality. SilverStripe core classes can be referenced
|
||
in your PHP files, but also in YAML configuration and SilverStripe templates.
|
||
For a full list of renamed classes, check the `.upgrade.yml` definitions in each module.
|
||
|
||
The rename won't affect class-based permission codes or database table names.
|
||
|
||
### `_ss_environment.php` changed to`.env` {#env}
|
||
|
||
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
|
||
the old php file, `.env` may be placed in either the web root, or one level above.
|
||
|
||
For example, if you have the below `_ss_environment.php` file, your `.env` would be rewritten as follows:
|
||
|
||
`_ss_environment.php`:
|
||
|
||
|
||
```php
|
||
// Environment
|
||
define('SS_ENVIRONMENT_TYPE', 'dev');
|
||
define('SS_DEFAULT_ADMIN_USERNAME', 'admin');
|
||
define('SS_DEFAULT_ADMIN_PASSWORD', 'password');
|
||
$_FILE_TO_URL_MAPPING[__DIR__] = 'http://localhost';
|
||
|
||
// Database
|
||
define('SS_DATABASE_CHOOSE_NAME', true);
|
||
define('SS_DATABASE_CLASS', 'MySQLDatabase');
|
||
define('SS_DATABASE_USERNAME', 'root');
|
||
define('SS_DATABASE_PASSWORD', '');
|
||
define('SS_DATABASE_SERVER', '127.0.0.1');
|
||
```
|
||
|
||
`.env`:
|
||
|
||
```
|
||
## Environment
|
||
SS_ENVIRONMENT_TYPE="dev"
|
||
SS_DEFAULT_ADMIN_USERNAME="admin"
|
||
SS_DEFAULT_ADMIN_PASSWORD="password"
|
||
SS_BASE_URL="http://localhost/"
|
||
|
||
### Database
|
||
SS_DATABASE_CHOOSE_NAME="true"
|
||
SS_DATABASE_CLASS="MySQLDatabase"
|
||
SS_DATABASE_USERNAME="root"
|
||
SS_DATABASE_PASSWORD=""
|
||
SS_DATABASE_SERVER="127.0.0.1"
|
||
```
|
||
|
||
The removal of the `_ss_environment.php` file means that conditional logic is no longer available in the environment
|
||
variable set-up process. This generally encouraged bad practice and should be avoided. If you still require conditional
|
||
logic early in the bootstrap, this is best placed in the `_config.php` files.
|
||
|
||
Note also that `$_FILE_TO_URL_MAPPING` has been removed and replaced with `SS_BASE_URL` env var.
|
||
This url must be an absolute url with an optional protocol. The following are valid, for example:
|
||
|
||
```
|
||
SS_BASE_URL="http://localhost/"
|
||
SS_BASE_URL="https://localhost/"
|
||
SS_BASE_URL="//localhost/"
|
||
```
|
||
|
||
The global values `$database` and `$databaseConfig` have been deprecated, as has `ConfigureFromEnv.php`
|
||
which is no longer necessary.
|
||
|
||
To access environment variables you can use the `SilverStripe\Core\Environment::getEnv()` method.
|
||
|
||
See [Environment Management docs](/getting_started/environment_management/) for full details.
|
||
|
||
### Migrate File DataObject {#migrate-file}
|
||
|
||
Since the structure of `File` dataobjects has changed, a new task `MigrateFileTask`
|
||
has been added to assist in migration of legacy files (see [file migration documentation](/developer_guides/files/file_migration)).
|
||
|
||
```
|
||
$ ./vendor/bin/sake dev/tasks/MigrateFileTask
|
||
```
|
||
|
||
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,
|
||
but will no longer be attached to a dataobject, and should be cleaned up manually.
|
||
To disable this, set the following config:
|
||
|
||
```yaml
|
||
SilverStripe\Assets\FileMigrationHelper:
|
||
delete_invalid_files: false
|
||
```
|
||
|
||
### Get upgrade tips on your code {#inspect-hints}
|
||
|
||
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.
|
||
Hints will generally point to more detail about a specific upgrade in this guide.
|
||
This task should be run *after* `upgrade-code upgrade`.
|
||
|
||
```
|
||
~/.composer/vendor/bin/upgrade-code inspect ./mysite
|
||
```
|
||
|
||
These hints only cover a part of the upgrade work,
|
||
but can serve as a good indicator for where to start.
|
||
|
||
### Rewrite literal table names {#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
|
||
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
|
||
[DataObjectSchema](api:SilverStripe\ORM\DataObjectSchema) service is available to manage these mappings
|
||
(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:
|
||
|
||
```diff
|
||
public function countDuplicates($model, $fieldToCheck)
|
||
{
|
||
$query = new SilverStripe\ORM\Queries\SQLSelect();
|
||
+ $table = SilverStripe\ORM\DataObject::getSchema()->tableForField($model, $field);
|
||
- $query->setFrom("\"{$model}\"");
|
||
+ $query->setFrom("\"{$table}\"");
|
||
- $query->setWhere(["\"{$model}\".\"{$field}\"" => $model->$fieldToCheck]);
|
||
+ $query->setWhere(["\"{$table}\".\"{$field}\"" => $model->$fieldToCheck]);
|
||
return $query->count();
|
||
}
|
||
```
|
||
|
||
### Rewrite literal class names {#literal-class-names}
|
||
|
||
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
|
||
to be updated to refer to fully qualified class names.
|
||
|
||
In configs and with literal PHP strings it is recommended to use the php `::class` constant,
|
||
as demonstrated below.
|
||
|
||
```diff
|
||
<?php
|
||
+ use SilverStripe\ORM\DataObject;
|
||
+ use SilverStripe\Security\Member;
|
||
class MyClass extends DataObject
|
||
{
|
||
private static $has_one = [
|
||
- 'Author' => 'Member',
|
||
+ 'Author' => Member::class,
|
||
];
|
||
}
|
||
```
|
||
|
||
In the context of YAML, the magic constant `::class` does not apply. Fully qualified class names must be hard coded.
|
||
|
||
```diff
|
||
-MyObject:
|
||
+My\Project\MyObject:
|
||
property: value
|
||
```
|
||
|
||
### Move controllers to their own files {#controllers-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.
|
||
|
||
You can still use, for example, `Page_Controller`, but you will get a deprecation notice. It is
|
||
best to change it to `PageController` during your upgrade process. Keep in mind any modules or
|
||
other thirdparty code that extend `PageController` are likely to assume that class exists.
|
||
|
||
By default, a controller for a page type *must* reside in the same namespace as its page. To use different logic, override `SiteTree::getControllerName()`.
|
||
|
||
### Template locations and references {#template-locations}
|
||
|
||
Templates are now more strict about their locations.
|
||
Case is now also checked on case-sensitive filesystems.
|
||
|
||
Either include the folder in the template name (`renderWith('MyEmail.ss')` => `renderWith('emails/MyEmail.ss')`),
|
||
move the template into the correct directory, or both.
|
||
|
||
Core template locations have moved - if you're including or overriding these
|
||
(e.g. for FormField templates) please adjust to the new paths. The `forms` folder
|
||
no longer exists, and instead template locations will be placed in paths that match
|
||
the `SilverStripe\Forms` namespace.
|
||
|
||
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.
|
||
|
||
`<% include Sidebar %>` will match `Includes/Sidebar.ss`, but will also match `Sidebar.ss`
|
||
if the former is not present.
|
||
|
||
Please refer to our [template syntax](/developer_guides/templates/syntax) for details.
|
||
|
||
### Config settings should be set to `private static` {#private-static}
|
||
|
||
Class configuration defined as `static` properties need to be marked as `private` to take effect:
|
||
|
||
```diff
|
||
-public static $allowed_actions = [
|
||
+private static $allowed_actions = [
|
||
'suggest'
|
||
];
|
||
```
|
||
|
||
### Module paths can't be hardcoded {#module-paths}
|
||
|
||
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.
|
||
They expect a module composer name, followed by the module relative path, separated by a double colon.
|
||
This prepares SilverStripe for moving modules out of the webroot at a later point.
|
||
|
||
Usage in templates:
|
||
|
||
```diff
|
||
-<img src="framework/images/image.png" />
|
||
+<img src="$ModulePath(silverstripe/framework)/image.png" />
|
||
|
||
-<% require css("framework/css/styles.css") %>
|
||
+<% require css("silverstripe/framework: css/styles.css") %>
|
||
```
|
||
|
||
Usage in Requirements:
|
||
|
||
```diff
|
||
-Requirements::css('framework/css/styles.css');
|
||
+Requirements::css('silverstripe/framework: css/styles.css');
|
||
```
|
||
|
||
Usage with custom logic:
|
||
|
||
```diff
|
||
+use SilverStripe\Core\Manifest\ModuleLoader;
|
||
+use SilverStripe\View\ThemeResourceLoader;
|
||
|
||
-$moduleFilePath = FRAMEWORK_DIR . '/MyFile.php';
|
||
+$moduleFilePath = ModuleLoader::getModule('silverstripe/framework')->getRelativeResourcePath('MyFile.php');
|
||
|
||
-$baseFilePath = BASE_PATH . '/composer.json';
|
||
+$baseFilePath = Director::baseFolder() . '/composer.json';
|
||
|
||
-$mysiteFilePath = 'mysite/css/styles.css';
|
||
+$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:
|
||
|
||
```php
|
||
class ListingPage extends \Page {
|
||
private static $icon = 'mycompany/silverstripe-mymodule: client/images/sitetree_icon.png';
|
||
}
|
||
```
|
||
|
||
```php
|
||
class MyCustomModelAdmin extends \SilverStripe\Admin\ModelAdmin {
|
||
private static $menu_icon = 'mycompany/silverstripe-mymodule: client/images/modeladmin_icon.png';
|
||
}
|
||
```
|
||
|
||
To ensure consistency, we've also deprecated support for path constants:
|
||
|
||
* Deprecated `FRAMEWORK_DIR` and `FRAMEWORK_PATH`
|
||
* Deprecated `FRAMEWORK_ADMIN_DIR` and `FRAMEWORK_ADMIN_PATH`
|
||
* Deprecated `FRAMEWORK_ADMIN_THIRDPARTY_DIR` and `FRAMEWORK_ADMIN_THIRDPARTY_PATH`
|
||
* Deprecated `THIRDPARTY_DIR` and `THIRDPARTY_PATH`
|
||
* Deprecated `CMS_DIR` and `CMS_PATH`
|
||
* Deprecated `THEMES_DIR` and `THEMES_PATH`
|
||
* Deprecated `MODULES_PATH` and `MODULES_DIR`
|
||
|
||
### Adapt tooling to modules in vendor folder {#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
|
||
that no hardcoded path references exist (see "Upgrade module paths in file references").
|
||
|
||
All core modules have been moved over already:
|
||
|
||
```diff
|
||
-framework/
|
||
+vendor/silverstripe/framework/
|
||
-cms/
|
||
+vendor/silverstripe/cms/
|
||
...
|
||
```
|
||
|
||
Since the `vendor/` folder isn't publicly accessible, modules need to declare
|
||
which files need to be exposed via the new [vendor-plugin](https://github.com/silverstripe/vendor-plugin)
|
||
(e.g. images or CSS/JS files). These files will be either symlinked or copied into a new `resources/`
|
||
folder automatically on `composer install`.
|
||
|
||
If your deployment process relies on `composer install` on the production environment,
|
||
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,
|
||
or explicitly switch to the "copy" mode to avoid symlinks.
|
||
|
||
### SS_Log replaced with PSR-3 logging {#psr3-logging}
|
||
|
||
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
|
||
means we can use thirdparty services like Monolog. `SS_Log` has been replaced
|
||
with a logger which can be accessed using the LoggerInterface::class service.
|
||
|
||
For instance, code which logs errors should be upgraded as below:
|
||
|
||
```diff
|
||
-SS_Log::log('My error message', SS_Log::ERR);
|
||
+use Psr\Log\LoggerInterface;
|
||
+Injector::inst()->get(LoggerInterface::class)->error('My error message');
|
||
```
|
||
|
||
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
|
||
suppress errors. You can temporarily disable logging by setting a `NullHandler`
|
||
|
||
```yml
|
||
---
|
||
Name: custom-dev-logging
|
||
After: dev-logging
|
||
Only:
|
||
environment: dev
|
||
---
|
||
# Replace default handler with null
|
||
SilverStripe\Core\Injector\Injector:
|
||
Monolog\Handler\HandlerInterface: Monolog\Handler\NullHandler
|
||
```
|
||
|
||
Alternatively you can customise one or both of the below services:
|
||
- `Monolog\Formatter\FormatterInterface.detailed` service, which is used for displaying
|
||
detailed error messages useful for developers.
|
||
- `Monolog\Formatter\FormatterInterface.friendly` service, which is used to display "error"
|
||
page content to visitors of the site who encounter errors.
|
||
|
||
For example, a custom error page generator could be added as below:
|
||
|
||
```yml
|
||
---
|
||
Name: custom-errorpage
|
||
After:
|
||
- '#loggingformatters'
|
||
---
|
||
SilverStripe\Core\Injector\Injector:
|
||
Monolog\Formatter\FormatterInterface.friendly:
|
||
class: WebDesignGroup\ShopSite\Logging\ErrorPageFormatter
|
||
```
|
||
|
||
`WebDesignGroup\ShopSite\Logging\ErrorPageFormatter` should be a class that
|
||
implements the `Monolog\Formatter\FormatterInterface` interface.
|
||
|
||
### Upgrade `mysite/_config.php` {#config-php}
|
||
|
||
The globals `$database` and `$databaseConfig` are deprecated. You should upgrade your
|
||
site `_config.php` files to use the [.env configuration](#env)
|
||
|
||
`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,
|
||
or alternatively (but less recommended) use the new `DB::setConfig()` api.
|
||
|
||
The global `$project` is deprecated in favour of the configuration setting
|
||
`SilverStripe\Core\Manifest\ModuleManifest.project`.
|
||
|
||
Changes to `mysite/_config.php`:
|
||
|
||
```diff
|
||
<?php
|
||
-global $project;
|
||
-$project = 'mysite';
|
||
-include 'conf/ConfigureFromEnv.php';
|
||
```
|
||
|
||
And also add to `mysite/_config/mysite.yml`:
|
||
|
||
```yml
|
||
SilverStripe\Core\Manifest\ModuleManifest:
|
||
project: mysite
|
||
```
|
||
|
||
### Object class replaced by traits {#object-replace}
|
||
|
||
Object has been superseded by traits.
|
||
|
||
- `Injectable`: Provides `MyClass::create()` and `MyClass::singleton()`
|
||
- `Configurable`: Provides `MyClass::config()`
|
||
- `Extensible`: Provides all methods related to extensions (E.g. add_extension()).
|
||
|
||
Upgrade subclass use
|
||
|
||
|
||
```diff
|
||
-class MyClass extends Object
|
||
-{
|
||
-}
|
||
+use SilverStripe\Core\Extensible;
|
||
+use SilverStripe\Core\Injector\Injectable;
|
||
+use SilverStripe\Core\Config\Configurable;
|
||
+class MyClass
|
||
+{
|
||
+ use Extensible;
|
||
+ use Injectable;
|
||
+ use Configurable;
|
||
+}
|
||
```
|
||
|
||
|
||
Upgrade references to $this->class
|
||
|
||
```diff
|
||
-$obj->class
|
||
+get_class($obj);
|
||
|
||
-$this->class;
|
||
+static::class;
|
||
```
|
||
|
||
Upgrade parse_class_spec()
|
||
|
||
```diff
|
||
-$spec = Object::parse_class_spec($spec);
|
||
+$spec = ClassInfo::parse_class_spec($spec);
|
||
```
|
||
|
||
Upgrade create_from_string()
|
||
|
||
|
||
```diff
|
||
-$obj = Object::create_from_string('Varchar(100)');
|
||
+$obj = Injector::inst()->create('Varchar(100)');
|
||
```
|
||
|
||
Upgrade extension use
|
||
|
||
```diff
|
||
|
||
-Object::add_extension('File', 'Versioned');
|
||
+File::add_extension(Versioned::class);
|
||
+DataObject::add_extension(File::class, Versioned::class); // alternate
|
||
|
||
-$has = Object::has_extension('File', 'Versioned');
|
||
+$has = File::has_extension(Versioned::class);
|
||
+$has = DataObject::has_extension(File::class, Versioned::class); // alternate
|
||
|
||
-$extensions = Object::get_extensions('File');
|
||
+$extensions = File::get_extensions();
|
||
+$extensions = DataObject::get_extensions(File::class); // alternate
|
||
```
|
||
|
||
### Session object removes static methods {#session}
|
||
|
||
Session object is no longer statically accessible via `Session::inst()`. Instead, Session
|
||
is a member of the current request.
|
||
|
||
```diff
|
||
public function httpSubmission($data, $form, $request)
|
||
{
|
||
- Session::set('loggedIn', null);
|
||
+ $request->getSession()->set('loggedIn', null);
|
||
}
|
||
```
|
||
|
||
|
||
In some places it may still be necessary to access the session object where no request is available.
|
||
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.
|
||
|
||
### Extensions are now singletons {#extensions-singletons}
|
||
|
||
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.
|
||
|
||
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.
|
||
|
||
```diff
|
||
class MyExtension extends Extension {
|
||
public function getContent() {
|
||
- if (!$this->contentCache) {
|
||
- $this->contentCache = $this->generateContent();
|
||
- }
|
||
- return $this->contentCache;
|
||
+ $contentCache = $this->owner->getField('contentCache');
|
||
+ if (!$contentCache) {
|
||
+ $contentCache = $this->generateContent();
|
||
+ $this->owner->setField('contentCache', $contentCache);
|
||
+ }
|
||
+ return $contentCache;
|
||
}
|
||
}
|
||
```
|
||
|
||
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
|
||
`private static $extensions`. Please review the default service definitions below:
|
||
|
||
```yaml
|
||
---
|
||
Name: versionedextension
|
||
---
|
||
SilverStripe\Core\Injector\Injector:
|
||
# Versioning only
|
||
SilverStripe\Versioned\Versioned.versioned:
|
||
class: SilverStripe\Versioned\Versioned
|
||
constructor:
|
||
mode: Versioned
|
||
# Staging and Versioning
|
||
SilverStripe\Versioned\Versioned.stagedversioned:
|
||
class: SilverStripe\Versioned\Versioned
|
||
constructor:
|
||
mode: StagedVersioned
|
||
# Default is alias for .stagedversioned
|
||
SilverStripe\Versioned\Versioned: '%$SilverStripe\Versioned\Versioned.stagedversioned'
|
||
```
|
||
|
||
Upgrade your extension references:
|
||
|
||
```diff
|
||
class MyClass extends DataObject {
|
||
private static $extensions = [
|
||
- Versioned::class . '(Versioned)',
|
||
+ Versioned::class . '.versioned',
|
||
];
|
||
}
|
||
```
|
||
|
||
### Static references to asset paths {#static-asset-paths}
|
||
|
||
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,
|
||
which are also sub-folders of the modules which now reside in `vendor`.
|
||
|
||
This will affect you if you have used `Requirements::block()` on files in the `framework/` or `cms/` folder.
|
||
|
||
In order to identify resources it is preferred to use the new module-root prefixed string form when
|
||
adding requirements.
|
||
|
||
Usage in Requirements:
|
||
|
||
```diff
|
||
-Requirements::css('framework/admin/css/styles.css');
|
||
+Requirements::css('silverstripe/admin: client/dist/styles/bundle.css');
|
||
```
|
||
|
||
The location of these resources is determined from the `name` field in the `composer.json`
|
||
for each module. `silverstripe/admin:` will be mapped to the folder `vendor/silverstripe/admin`
|
||
where this module is installed, based on the `vendor/silverstripe/admin/composer.json` name
|
||
matching the prefix in `Requirements::css()`.
|
||
|
||
Care should also be taken when referencing images in these folders from your own stylesheets (`url()`),
|
||
or via SilverStripe templates (`<img>` tags).
|
||
|
||
`Requirements` now throws an exception then a file is not found, rather than
|
||
failing silently, so check your `Requirements` are pointing to files that exist.
|
||
|
||
```
|
||
framework/javascript => silverstripe/admin:client/dist/
|
||
framework/javascript/lang => silverstripe/admin:client/lang/
|
||
framework/images => silverstripe/admin:client/dist/images/
|
||
framework/css => silverstripe/admin:client/dist/css/
|
||
framework/scss => silverstripe/admin:client/src/styles/
|
||
admin/javascript/ => silverstripe/admin:client/src/
|
||
admin/javascript/src/ => silverstripe/admin:client/src/legacy/ (mostly)
|
||
admin/javascript/lang/ => silverstripe/admin:client/lang/
|
||
admin/scss/ => silverstripe/admin:client/styles/legacy/
|
||
admin/css/ => silverstripe/admin:client/dist/css/
|
||
admin/css/screen.css => silverstripe/admin:client/dist/css/bundle.css
|
||
admin/images/ => silverstripe/admin:client/dist/images/
|
||
admin/images/sprites/src/ => silverstripe/admin:client/src/sprites/
|
||
admin/images/sprites/dist/ => silverstripe/admin:client/dist/sprites/
|
||
admin/font/ => silverstripe/admin:client/dist/font/
|
||
```
|
||
|
||
Most JavaScript files in `framework/javascript` have been removed,
|
||
and are bundled through [Webpack](http://webpack.github.io/) into a combined file instead.
|
||
If you have referenced these files elsewhere, please consider
|
||
running the ES6 source files in `admin/client/src/legacy`
|
||
through your own transpiling and bundle process.
|
||
|
||
This also includes JavaScript i18n support, and the removal of the `i18n::js_i18n`
|
||
configuration option used in `Requirements::add_i18n_javascript()`.
|
||
|
||
The CMS UI is moving away from `Requirements::combine_files()` in favour of Webpack.
|
||
This method is being considered for deprecation in future versions.
|
||
|
||
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
|
||
of these files, please consider packaging your own versions in your projects and modules.
|
||
For CMS modules, you can also use many library globals which the core bundles already expose
|
||
(see [Build Tooling](/contributing/build_tooling)).
|
||
|
||
One commonly linked thirdparty dependency is `jquery.js` bundled with SilverStripe:
|
||
|
||
```
|
||
framework/thirdparty/jquery/jquery.js => framework/admin/thirdparty/jquery/jquery.js
|
||
```
|
||
|
||
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 {#template-casting}
|
||
|
||
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
|
||
the value declared by the `default_cast` on the parent object.
|
||
|
||
The default value of `default_cast` is `Text`, meaning that now many cases where a field was
|
||
left un-uncoded, this will now be safely encoded via `Convert::raw2xml`. In cases where
|
||
un-cast fields were used to place raw HTML into templates, this will now encode this until
|
||
explicitly cast for that field.
|
||
|
||
You can resolve this in your model by adding an explicit cast to HTML for those fields.
|
||
|
||
```diff
|
||
use SilverStripe\View\ViewableData;
|
||
use SilverStripe\Core\Convert;
|
||
|
||
class MyObject extends ViewableData
|
||
{
|
||
+ private static $casting = [
|
||
+ 'SomeHTML' => 'HTMLText'
|
||
+ ];
|
||
|
||
public function getSomeHTML
|
||
{
|
||
- $title = Convert::raw2xml($this->Title);
|
||
+ $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)).
|
||
|
||
### Replace UploadField with injected service {#uploadfield}
|
||
|
||
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
|
||
streamlined simpler mechanism for uploading `File` dataobjects.
|
||
|
||
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.
|
||
Usages of `UploadField` will need to be upgraded as below.
|
||
|
||
```diff
|
||
use SilverStripe\Forms\FieldList;
|
||
-use SilverStripe\AssetAdmin\Forms\UploadField;
|
||
+use SilverStripe\Forms\FileHandleField;
|
||
use SilverStripe\ORM\DataObject;
|
||
|
||
class MyClass extends DataObject
|
||
{
|
||
public function getCMSFields()
|
||
{
|
||
return new FieldList(
|
||
- new UploadField('Files')
|
||
+ Injector::inst()->create(FileHandleField::class, 'Files')
|
||
);
|
||
}
|
||
}
|
||
```
|
||
|
||
### i18n placeholders, plurals and i18nEntityProvider {#i18n}
|
||
|
||
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:
|
||
|
||
- `_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 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.
|
||
|
||
If you attempt to use non-associative injection arguments with named placeholders, the result will
|
||
now trigger an exception.
|
||
|
||
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
|
||
other than itself, it should return an array with the `default` and `module` keys respectively.
|
||
|
||
Full locale-rule respecting localisation for plural forms is now supported. The default
|
||
key for an object plural form is `<Namespaced\ClassName>.PLURALS`, and follows CLDR array form for each
|
||
pluralisation. See [the CLDR chart](http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html)
|
||
for reference.
|
||
|
||
The below demonstrates how you can provide new localisation strings for an object,
|
||
including both plurals and cross-module localisations.
|
||
|
||
|
||
```php
|
||
use SilverStripe\ORM\DataObject;
|
||
|
||
class MyObject extends DataObject, implements i18nEntityProvider
|
||
{
|
||
public function provideI18nEntities()
|
||
{
|
||
return [
|
||
'MyObject.SINGULAR_NAME' => 'object',
|
||
'MyObject.PLURAL_NAME' => 'objects',
|
||
'MyObject.PLURALS' => [
|
||
'one' => 'An object',
|
||
'other' => '{count} objects',
|
||
],
|
||
'AnotherSection.DESCRIPTION' => [
|
||
'default' => 'This is the description for this section',
|
||
'module' => 'extendedmodule',
|
||
],
|
||
];
|
||
}
|
||
}
|
||
```
|
||
|
||
|
||
In YML format this will be expressed as the below:
|
||
|
||
`mymodule/lang/en.yml`:
|
||
|
||
|
||
```yml
|
||
en:
|
||
MyObject:
|
||
SINGULAR_NAME: 'object'
|
||
PLURAL_NAME: 'objects'
|
||
PLURALS:
|
||
one: 'An object',
|
||
other: '{count} objects'
|
||
```
|
||
|
||
`extendedmodule/lang/en.yml`:
|
||
|
||
|
||
```yml
|
||
en:
|
||
AnotherSection:
|
||
DESCRIPTION: 'This is the description for this section'
|
||
```
|
||
|
||
|
||
Usage of these pluralised strings is through the existing `_t()` method,
|
||
and require a `|` pipe-delimeter with a `{count}` argument.
|
||
|
||
|
||
```php
|
||
public function pluralise($count)
|
||
{
|
||
return _t('MyObject.PLURALS', 'An object|{count} objects', [ 'count' => $count ]);
|
||
}
|
||
```
|
||
|
||
In templates this can also be invoked as below:
|
||
|
||
|
||
```ss
|
||
<%t MyObject.PLURALS 'An item|{count} items' count=$Count %>
|
||
```
|
||
|
||
### Removed Member.DateFormat and Member.TimeFormat database settings {#member-date-time-fields}
|
||
|
||
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)),
|
||
where the browser localises the output based on the browser/system preferences.
|
||
In this context it no longer makes sense to give users control over their own
|
||
date and time formats in their CMS profile.
|
||
Consequently, we've also removed `MemberDatetimeOptionsetField`.
|
||
|
||
`Member->getDateFormat()` and `Member->getTimeFormat()` still exist, and default to
|
||
the [IntlDateFormatter defaults](http://php.net/manual/en/class.intldateformatter.php) for the selected locale.
|
||
|
||
|
||
### New asset storage mechanism {#asset-storage}
|
||
|
||
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).
|
||
|
||
Because the filesystem now uses the sha1 of file contents in order to version multiple versions under the same
|
||
filename, the default storage paths in 4.0 will not be the same as in 3.
|
||
|
||
In order to retain existing file paths in line with framework version 3 you should set the
|
||
`\SilverStripe\Filesystem\Flysystem\FlysystemAssetStore.legacy_paths` config to true.
|
||
Note that this will not allow you to utilise certain file versioning features in 4.0.
|
||
|
||
|
||
```yml
|
||
SilverStripe\Filesystem\Flysystem\FlysystemAssetStore:
|
||
legacy_paths: true
|
||
```
|
||
|
||
|
||
See our ["File Management" guide](/developer_guides/files/file_management) for more information.
|
||
|
||
Depending on your server configuration, it may also be necessary to adjust your assets folder
|
||
permissions. Please see the [common installation problems](/getting_started/installation/common_problems)
|
||
guide for configuration instruction.
|
||
|
||
### Image handling {#image-handling}
|
||
|
||
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
|
||
has the `getIsImage()` method. In some cases, it may be preferable to invoke this method to detect
|
||
if the asset is an image or not, rather than checking the subclass, as the asset may also be a `DBFile` with
|
||
an image filter applied, rather than an instance of the `Image` dataobject.
|
||
|
||
In addition, a new file category `image/supported` has been added, which is a subset of the `image` category.
|
||
This is the subset of all image types which may be assigned to the `[Image](api:SilverStripe\Assets\Image)` dataobject, and may have
|
||
manipulations applied to it. This should be used as the file type restriction on any `[UploadField](api:SilverStripe\AssetAdmin\Forms\UploadField)` which
|
||
is intended to upload images for manipulation.
|
||
|
||
```diff
|
||
-if($file instanceof Image) {
|
||
- $upload = new UploadField();
|
||
- $upload->setAllowedFileCategories('image');
|
||
-}
|
||
+if ($file->getIsImage()) {
|
||
+ $upload = new UploadField();
|
||
+ $upload->setAllowedFileCategories('image/supported');
|
||
+}
|
||
```
|
||
|
||
In cases where image-only assets may be assigned to relationships then your datamodel should specify explicitly
|
||
an `Image` datatype, or refer to `DBFile('image/supported')`.
|
||
|
||
```php
|
||
use SilverStripe\Assets\Image;
|
||
class MyObject extends SilverStripe\ORM\DataObject
|
||
{
|
||
private static $has_one = [
|
||
"ImageObject" => Image::class
|
||
];
|
||
private static $db = [
|
||
"ImageField" => "DBFile('image/supported')"
|
||
];
|
||
}
|
||
```
|
||
|
||
### Writing to `File` dataobjects or the assets folder {#write-file-dataobject}
|
||
|
||
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.
|
||
|
||
Since the storage of physical files is no longer a standard location, it's necessary to delegate the writing of such
|
||
files to the asset persistence layer. As a wrapper for an individual file, you can use any of the `setFrom*`
|
||
methods to assign content from a local (e.g. temporary) file, a stream, or a string of content.
|
||
You would need to upgrade your code as below.
|
||
|
||
```diff
|
||
-function importTempFile($tmp)
|
||
-{
|
||
- copy($tmp, ASSETS_PATH . '/imported/' . basename($tmp));
|
||
- $file = new File();
|
||
- $file->setFilename('assets/imported/'.basename($tmp));
|
||
- $file->write();
|
||
-}
|
||
+public function importTempFile($tmp)
|
||
+{
|
||
+ Versioned::reading_stage('Stage');
|
||
+ $file = new File();
|
||
+ $file->setFromLocalFile($tmp, 'imported/' . basename($tmp));
|
||
+ $file->write();
|
||
+ $file->doPublish();
|
||
+}
|
||
```
|
||
|
||
Note that 'assets' is no longer present in the new code, and the path beneath what was once assets is now
|
||
used to generate the 'filename' value. This is because there is no longer an assumption that files are
|
||
stored in the assets folder.
|
||
|
||
There are other important considerations in working with File dataobjects which differ from legacy:
|
||
|
||
* File synchronisation is no longer automatic. This is due to the fact that there is no longer a 1-to-1 relationship
|
||
between physical files and File DataObjects.
|
||
* Folder DataObjects are now purely logical DataObjects, and perform no actual filesystem folder creation on write.
|
||
* All Files are versioned, which means that by default, new File records will not be visibile
|
||
to the public site. You will need to make sure to invoke `->doPublish()` on any File DataObject
|
||
you wish visitors to be able to see.
|
||
|
||
You can disable File versioning by adding the following to your `_config.php`
|
||
|
||
```php
|
||
SilverStripe\Assets\File::remove_extension('Versioned');
|
||
```
|
||
|
||
### Custom image manipulations {#image-manipulations}
|
||
|
||
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,
|
||
which are used to generate what are now called "variants" of assets, is now a generic api available to both `File`
|
||
and `DBFile` classes through this trait.
|
||
|
||
Custom manipulations, applied via extensions, must be modified to use the new API.
|
||
For instance, code which sizes images to a fixed width should be updated as below:
|
||
|
||
Before:
|
||
|
||
```php
|
||
// in MyImageExtension.php
|
||
class MyImageExtension extends SilverStripe\ORM\DataExtension
|
||
{
|
||
public function GalleryThumbnail($height)
|
||
{
|
||
return $this->getFormattedImage('GalleryThumbnail', $height);
|
||
}
|
||
|
||
public function generateGalleryThumbnail(Image_Backend $backend, $height)
|
||
{
|
||
return $backend->paddedResize(300, $height);
|
||
}
|
||
}
|
||
|
||
// in _config.php
|
||
SilverStripe\Assets\Image::add_extension('MyImageExtension');
|
||
```
|
||
|
||
Now image manipulations are implemented with a single method via a callback generator:
|
||
|
||
```php
|
||
use SilverStripe\Assets\File;
|
||
|
||
// in MyImageExtension.php
|
||
class MyImageExtension extends SilverStripe\Core\Extension
|
||
{
|
||
public function GalleryThumbnail($height)
|
||
{
|
||
// Generates the manipulation key
|
||
$variant = $this->owner->variantName(__FUNCTION__, $height);
|
||
|
||
// Instruct the backend to search for an existing variant with this key,
|
||
// and include a callback used to generate this image if it doesn't exist
|
||
return $this->owner->manipulateImage($variant, function (Image_Backend $backend) use ($height) {
|
||
return $backend->paddedResize(300, $height);
|
||
});
|
||
}
|
||
}
|
||
|
||
// in _config.php
|
||
File::add_extension('MyImageExtension');
|
||
\SilverStripe\Filesystem\Storage\DBFile::add_extension('MyImageExtension');
|
||
```
|
||
|
||
There are a few differences in this new API:
|
||
|
||
* The extension is no longer specific to DataObjects, so it uses the generic `Extension` class instead of `DataExtension`
|
||
* This extension is added to both `DBFile` and `File`, or order to make this manipulation available to non-dataobject
|
||
file references as well, but it could be applied to either independently.
|
||
* A helper method `variantName` is invoked in order to help generate a unique variant key. Custom code may use another
|
||
generation mechanism.
|
||
* Non-image files may also have manipulations, however the specific `manipulateImage` should not be used in this case.
|
||
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.
|
||
|
||
### File or Image shortcode handler {#file-shortcode}
|
||
|
||
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`
|
||
|
||
Example of changed shortcode handling:
|
||
|
||
```diff
|
||
+use SilverStripe\Assets\Shortcodes\FileShortcodeProvider;
|
||
class MyShortcodeUser extends Object
|
||
{
|
||
private $content;
|
||
|
||
public function Content($arguments, $parser, $shortcode)
|
||
{
|
||
- return File::handle_shortcode($arguments, $this->content, $parser, $shortcode);
|
||
+ return FileShortcodeProvider::handle_shortcode($arguments, $this->content, $parser, $shortcode);
|
||
}
|
||
}
|
||
```
|
||
|
||
### Composite db fields {#compositedbfield}
|
||
|
||
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.
|
||
|
||
The below describes the minimum amount of effort required to implement a composite DB field.
|
||
|
||
```php
|
||
use SilverStripe\ORM\FieldType\DBComposite;
|
||
|
||
class MyAddressField extends
|
||
{
|
||
private static $composite_db = [
|
||
'Street' => 'Varchar(200)',
|
||
'Suburb' => 'Varchar(100)',
|
||
'City' => 'Varchar(100)',
|
||
'Country' => 'Varchar(100)'
|
||
];
|
||
|
||
public function scaffoldFormField($title = null, $params = null)
|
||
{
|
||
new SilverStripe\Forms\TextField($this->getName(), $title);
|
||
}
|
||
}
|
||
```
|
||
|
||
### Removed `DataObject::database_fields` or `DataObject::db` {#dataobject-db-database-fields}
|
||
|
||
The methods `DataObject::database_fields()`, `DataObject::custom_database_fields()` and `DataObject::db()` have
|
||
been removed.
|
||
|
||
Instead, to get all database fields for a dataobject, including base fields (such as ID, ClassName, Created, and LastEdited), use `DataObject::getSchema()->databaseFields($className, $aggregate = true)`.
|
||
To omit the base fields, pass a value of `false` as the `$aggregate` parameter, e.g. `DataObject::getSchema()->databaseFields(Member::class, false)`.
|
||
|
||
Composite database fields are omitted from the `databaseFields()` method. To get those, use `DataObject::getSchema()->compositeFields($className, $aggregate = true)`.
|
||
|
||
|
||
### Rewrite SQLQuery to more specific classes {#sqlquery}
|
||
|
||
Instead of `SQLQuery`, you should now use `SQLSelect`, `SQLUpdate`, `SQLInsert`
|
||
or `SQLDelete` - check the [3.2.0](3.2.0#sqlquery) upgrading notes for details.
|
||
|
||
Example:
|
||
|
||
```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']);
|
||
}
|
||
}
|
||
```
|
||
|
||
### Upgrade BuildTask classes {#buildtask-segment}
|
||
|
||
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
|
||
will be a fully-qualified class name like `sake dev/tasks/Me-MyModule-Tasks-MyTask`. This can also be configured in YAML.
|
||
|
||
```php
|
||
use SilverStripe\Dev\BuildTask;
|
||
|
||
class MyTask extends BuildTask
|
||
{
|
||
private static $segment = 'MyTask';
|
||
}
|
||
```
|
||
|
||
### Moved ErrorPage into a new module {#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.
|
||
The module is installed by default on new projects, but needs to be added to existing projects
|
||
to preserve functionality on the existing "Page not found" and "Server error" pages in the CMS.
|
||
|
||
composer require silverstripe/errorpage
|
||
|
||
Alternatively you can implement your own `onBeforeHTTPError()` handling to present custom errors.
|
||
By default, SilverStripe will display a plaintext "not found" message when the module isn't installed.
|
||
Check the [module upgrading guide](http://addons.silverstripe.org/add-ons/silverstripe/errorpage)
|
||
for more configuration API changes on the `ErrorPage` class.
|
||
|
||
### Server configuration files for assets {#assets-server-config}
|
||
|
||
Server configuration files for `/assets` are no longer static, and are regenerated via a set of
|
||
standard SilverStripe templates on flush. These templates include:
|
||
|
||
* `Assets_HTAccess.ss`: Template for public permissions on the Apache server.
|
||
* `Assets_WebConfig.ss`: Template for public permissions on the IIS server.
|
||
* `Protected_HTAccess.ss`: Template for the protected store on the Apache server (should deny all requests).
|
||
* `Protected_WebConfig.ss`: Template for the protected store on the IIS server (should deny all requests).
|
||
|
||
You will need to make sure that these files are writable via the web server, and that any necessary
|
||
configuration customisation is done via overriding these templates.
|
||
|
||
Depending on your server configuration, it may also be necessary to adjust your assets folder
|
||
permissions. Please see the [common installation problems](/getting_started/installation/common_problems)
|
||
guide for configuration instruction.
|
||
|
||
If upgrading from an existing installation, make sure to invoke `?flush=all` at least once.
|
||
|
||
See our ["File Security" guide](/developer_guides/files/file_security) for more information.
|
||
|
||
### TinyMCE v4 {#tinymce}
|
||
|
||
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 v3.
|
||
|
||
In Framework 4.0 the user interface for TinyMCE has been trimmed down considerably, with certain toolbar
|
||
buttons removed from the default cms configuration:
|
||
|
||
* Strikethrough
|
||
* Styles dropdown
|
||
* Block quotes
|
||
* Horizontal Rule
|
||
* Undo / Redo
|
||
* Cut / Paste as word
|
||
* Select all
|
||
* Fullscreen
|
||
|
||
However, these function may be enabled on a case by case basis through modifification of the default
|
||
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
|
||
has been removed from core. You can configure the built-in `charmap` plugin instead:
|
||
|
||
```php
|
||
$editor = SilverStripe\Forms\HTMLEditor\HtmlEditorConfig::get('cms');
|
||
$editor->enablePlugins('charmap');
|
||
$editor->addButtonsToLine(1, 'charmap');
|
||
$editor->setOption('charmap_append', [
|
||
['256','A - macron'],
|
||
['274','E - macron'],
|
||
['298','I - macron'],
|
||
['332','O - macron'],
|
||
['362','U - macron'],
|
||
['257','a - macron'],
|
||
['275','e - macron'],
|
||
['299','i - macron'],
|
||
['333','o - macron'],
|
||
['363','u - macron']
|
||
]);
|
||
```
|
||
|
||
### DataObjects with the `Versioned` extension {#dataobject-versioned}
|
||
|
||
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.
|
||
|
||
Rather than declaring the list of stages a model has, the constructor for `Versioned` will take a single mode
|
||
parameter, which declares whether or not the model is versioned and has a draft and live stage, or alternatively
|
||
if it only has versioning without staging.
|
||
|
||
Each form of this extension is registered under the appropriate service identifier, which you should use in your
|
||
model as below:
|
||
|
||
```php
|
||
use SilverStripe\ORM\DataObject;
|
||
use SilverStripe\ORM\Versioning\Versioned;
|
||
|
||
/**
|
||
* This model has staging and versioning. Stages will be "Stage" and "Live"
|
||
*/
|
||
class MyStagedModel extends SilverStripe\ORM\DataObject
|
||
{
|
||
private static $extensions = [
|
||
Versioned::class . '.stagedversioned',
|
||
];
|
||
}
|
||
|
||
/**
|
||
* This model has versioning only, and will not has a draft or live stage, nor be affected by the current stage.
|
||
*/
|
||
class MyVersionedModel extends DataObject
|
||
{
|
||
private static $extensions = [
|
||
Versioned::class . '.versioned',
|
||
];
|
||
}
|
||
```
|
||
|
||
Additionally, the following api methods have been added:
|
||
|
||
* `Versioned::publishRecursive` Publishes this object, and all owned objects
|
||
* `Versioned::publishSingle` Publishes this object, but not owned objects
|
||
* `Versioned::copyVersionToStage` Replaces the old `publish` method.
|
||
|
||
These methods are deprecated:
|
||
|
||
* `Versioned::publish` Replaced by `Versioned::copyVersionToStage`
|
||
* `Versioned::doPublish` Replaced by `Versioned::publishRecursive`
|
||
|
||
### New Ownership API {#ownership}
|
||
|
||
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
|
||
of linked components are consistent with their "owner." Without the concept of ownership, these linked
|
||
components could be implicitly exposed on the frontend, which may not align with the intent of the
|
||
content author.
|
||
|
||
For instance, on a products page which has a list of products, the products should not be published unless the products page is, too. The ownership API solves this by allowing you to declare
|
||
a two-way relationship between objects, typically, but not necessarily, linked by a database relationship
|
||
(`has_many`, `many_many`, etc.).
|
||
|
||
```php
|
||
use SilverStripe\Versioned\Versioned;
|
||
use Page;
|
||
use SilverStripe\ORM\DataObject;
|
||
|
||
class ProductPage extends Page
|
||
{
|
||
private static $has_many = [
|
||
'Products' => Product::class
|
||
];
|
||
|
||
private static $owns = [
|
||
'Products'
|
||
];
|
||
}
|
||
|
||
class Product extends DataObject
|
||
{
|
||
private static $extensions = [
|
||
Versioned::class
|
||
];
|
||
|
||
private static $has_one = [
|
||
'Parent' => ProductPage::class
|
||
];
|
||
}
|
||
```
|
||
If your objects are linked by something other than a database relationship, for instance, a custom
|
||
getter that is computed at runtime, the same rules can be applied, as long as you provide an `$owned_by`
|
||
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
|
||
|
||
### ChangeSet batch publishing {#changeset}
|
||
|
||
ChangeSet objects have been added, which allow groups of objects to be published in
|
||
a single atomic transaction.
|
||
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.
|
||
|
||
### New `[image]` shortcode in `HTMLText` fields {#image-shortcode}
|
||
|
||
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`.
|
||
Images inserted into `HTMLText` fields (through a WYSIWYG editor) need to be tracked as well.
|
||
Instead of `<img>` tags, the field will insert `[image]` shortcodes which point to the database identifier
|
||
of the `Image` record rather than its path on the filesystem. The shortcode will be automatically replaced
|
||
when the field is rendered. Newly inserted images will automatically receive the shortcode and ownership tracking,
|
||
and existing `<img>` will continue to work.
|
||
|
||
### Renamed DBField and subclasses {#dbfield-rename}
|
||
|
||
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/).
|
||
|
||
Example:
|
||
|
||
```diff
|
||
use SilverStripe\ORM\DataObject;
|
||
+use SilverStripe\ORM\FieldType\DBVarchar;
|
||
|
||
class MyObject extends DataObject
|
||
{
|
||
private static $db = [
|
||
'Number' => 'Int',
|
||
- 'Time' => 'SS_Datetime'
|
||
+ 'Time' => 'Datetime'
|
||
];
|
||
|
||
public function TextNumber()
|
||
{
|
||
- return new Varchar('TextNumber', 'Number is ' . $this->Number);
|
||
+ return new DBVarchar('TextNumber', 'Number is ' . $this->Number);
|
||
}
|
||
}
|
||
```
|
||
|
||
### Removed RestfulService {#restfulservice}
|
||
|
||
The `RestfulService` API was a (poor) attempt at a built-in HTTP client.
|
||
We've removed it, and recommend using [Guzzle](http://docs.guzzlephp.org/en/latest/) instead.
|
||
|
||
### Removed Oembed {#oembed}
|
||
|
||
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 this package.
|
||
|
||
### Configurable Admin URL {#admin-url}
|
||
|
||
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
|
||
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
|
||
specific functions.
|
||
|
||
### Custom Authenticators {#custom-authenticators}
|
||
|
||
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
|
||
SilverStripe\Security\Authenticator;
|
||
authenticators:
|
||
- 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:
|
||
|
||
```yml
|
||
SilverStripe\Security\Authenticator:
|
||
default_authenticator: SilverStripe\Security\MemberAuthenticator
|
||
```
|
||
|
||
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.
|
||
|
||
Every request is now authenticated against an `IdentityStore` interface.
|
||
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).
|
||
|
||
### Config is now immutable {#config}
|
||
|
||
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
|
||
bootstrap, and requires a developer to invoke `Config::modify()` to make mutable prior to modification.
|
||
This will immediately have a slight performance hit, so should be done sparingly, and avoided at all
|
||
if possible in performance intensive applications.
|
||
|
||
The `Config::inst()->update()` method is deprecated, and replaced with `Config::modify()->set()` and
|
||
`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)
|
||
now replace any prior values (even arrays).
|
||
|
||
One removed feature is the `Config::FIRST_SET` option. Either use uninherited config directly on the class
|
||
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`.
|
||
|
||
### Replace Zend_Cache with symfony/cache {#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
|
||
[PSR-16](http://www.php-fig.org/psr/psr-16/) cache interface instead.
|
||
|
||
Caches should be retrieved through `Injector` instead of `Cache::factory()`,
|
||
and have a slightly different API (e.g. `set()` instead of `save()`).
|
||
|
||
```diff
|
||
-$cache = Cache::factory('myCache');
|
||
+use Psr\SimpleCache\CacheInterface;
|
||
+$cache = Injector::inst()->get(CacheInterface::class . '.myCache');
|
||
|
||
// create a new item by trying to get it from the cache
|
||
-$myValue = $cache->load('myCacheKey');
|
||
+$myValue = $cache->get('myCacheKey');
|
||
|
||
// set a value and save it via the adapter
|
||
-$cache->save(1234, 'myCacheKey');
|
||
+$cache->set('myCacheKey', 1234);
|
||
|
||
// retrieve the cache item
|
||
-if (!$cache->load('myCacheKey')) {
|
||
- // ... item does not exists in the cache
|
||
-}
|
||
+if (!$cache->has('myCacheKey')) {
|
||
+ // ... item does not exists in the cache
|
||
+}
|
||
|
||
// Remove a cache key
|
||
-$cache->remove('myCacheKey');
|
||
+$cache->delete('myCacheKey');
|
||
```
|
||
|
||
With the necessary minimal config in `_config/mycache.yml`
|
||
|
||
```yml
|
||
---
|
||
Name: mycache
|
||
---
|
||
SilverStripe\Core\Injector\Injector:
|
||
Psr\SimpleCache\CacheInterface.myCache:
|
||
factory: SilverStripe\Core\Cache\CacheFactory
|
||
constructor:
|
||
namespace: 'mycache'
|
||
```
|
||
|
||
Caches are now configured through dependency injection services instead of PHP.
|
||
See our ["Caching" docs](/developer-guides/performance/caching) for more details.
|
||
|
||
Before (`mysite/_config.php`):
|
||
|
||
```php
|
||
Cache::add_backend(
|
||
'primary_memcached',
|
||
'Memcached',
|
||
[
|
||
'servers' => [
|
||
'host' => 'localhost',
|
||
'port' => 11211,
|
||
]
|
||
]
|
||
);
|
||
Cache::pick_backend('primary_memcached', 'any', 10);
|
||
```
|
||
|
||
After (`mysite/_config/config.yml`):
|
||
|
||
```yml
|
||
---
|
||
After:
|
||
- '#corecache'
|
||
---
|
||
SilverStripe\Core\Injector\Injector:
|
||
MemcachedClient:
|
||
class: 'Memcached'
|
||
calls:
|
||
- [ addServer, [ 'localhost', 11211 ] ]
|
||
SilverStripe\Core\Cache\CacheFactory:
|
||
class: 'SilverStripe\Core\Cache\MemcachedCacheFactory'
|
||
constructor:
|
||
client: '%$MemcachedClient
|
||
```
|
||
|
||
### User-code style upgrades {#usercode-style-upgrades}
|
||
|
||
Although it is not mandatory to upgrade project code to follow SilverStripe and
|
||
PSR-2 standard it is highly recommended to ensure that code is consistent. The below sections
|
||
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
|
||
to fix references and usages of framework API.
|
||
|
||
### Upgrading user-code to use namespaces
|
||
|
||
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
|
||
automated as possible.
|
||
|
||
### Using the upgrader tool to automatically apply namespaces
|
||
|
||
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
|
||
references to those classes.
|
||
|
||
Use the below to setup upgrader, and apply a namespace to a given code folder.
|
||
|
||
```
|
||
composer global require silverstripe/upgrader
|
||
cd ~/Project/Root
|
||
~/.composer/vendor/bin/upgrade-code add-namespace "WebDesignGroup\ShopSite" ./mysite/code --recursive --write
|
||
```
|
||
|
||
If you want to do a dry-run, omit the `--write` option to see a preview of a diff of
|
||
all changed project files.
|
||
|
||
This task will do the following:
|
||
|
||
- Add the given namespace to all files in the code class, and subdirectories.
|
||
- Sub-namespaces will be applied based on directory structure
|
||
- All references to classes in any namespaced files will be safely retained with additional `use` directives
|
||
added as necessary.
|
||
- Register all namespaced classese in a mysite/.upgrade.yml file for migration of other code
|
||
|
||
This task will not do the following, and must be done manually:
|
||
|
||
- Adding `table_name` to any namespaced classes
|
||
- Upgrade other references to namespaced classes outside of this folder
|
||
- Migrate any database table records
|
||
|
||
Please see the following steps for more information.
|
||
|
||
### 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`
|
||
file. If you have any user-code that references these, you may need to run the upgrader again (as you did did
|
||
to upgrade your project to namespaced framework classe).
|
||
|
||
```
|
||
composer global require silverstripe/upgrader
|
||
cd ~/Project/Root
|
||
~/.composer/vendor/bin/upgrade-code upgrade ./othercode --write
|
||
```
|
||
|
||
### 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
|
||
for your dataobjects, in order to ensure the correct table is used after upgrade. It is recommended
|
||
to point this to the base name of the class, excluding namespace, as in 3.x.
|
||
|
||
```diff
|
||
namespace WebDesignGroup\ShopSite;
|
||
use SilverStripe\ORM\DataObject;
|
||
use Page;
|
||
class GalleryPage extends Page
|
||
{
|
||
+ private static $table_name = 'GalleryPage';
|
||
}
|
||
```
|
||
|
||
### Class name remapping {#class-name-remapping}
|
||
|
||
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`
|
||
field in the `SiteTree` table still contains the singular model name, e.g. `GalleryPage`
|
||
and that when you change it to `WebDesignGroup\ShopSite\GalleryPage` then everything
|
||
works again.
|
||
|
||
The `dev/build` task is configured to look for a legacy class name mapping
|
||
configuration setting and will update this for you automatically. You can use
|
||
this to add DB upgrading rules for your own classes.
|
||
|
||
For example, you could upgrade references to the newly namespaced Banner class by adding
|
||
this to your `mysite/_config/upgrade.yml` file:
|
||
|
||
```yml
|
||
SilverStripe\ORM\DatabaseAdmin:
|
||
classname_value_remapping:
|
||
GalleryPage: WebDesignGroup\ShopSite\GalleryPage
|
||
```
|
||
|
||
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`
|
||
|
||
### PSR-2 Coding Standard compliance {#psr2}
|
||
|
||
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
|
||
code style migration.
|
||
|
||
- Install the necessary library:
|
||
`composer require squizlabs/php_codesniffer`
|
||
- Copy silverstripe standards config file from framework/phpcs.xml to your project root:
|
||
`cp ./framework/phpcs.xml ./phpcs.xml`
|
||
- Run the automatic upgrade tool on your code folder
|
||
`vendor/bin/phpcbf ./mysite/code`
|
||
- Run the automatic linting tool to detect and manually fix other errors:
|
||
`vendor/bin/phpcs ./mysite/code`
|
||
|
||
Repeat the final step and manually repair suggested changes, as necessary,
|
||
until you no longer have any linting issues.
|
||
|
||
### PSR-4 autoloading for project code {#psr4}
|
||
|
||
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.
|
||
|
||
You can implement this in your composer configuration like so:
|
||
|
||
```json
|
||
{
|
||
"autoload": {
|
||
"psr-4": {
|
||
"WebDesignGroup\\ShopSite\\": "mysite/src/"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
Now you just need to ensure that each class site in the correct folder location
|
||
(including case sensitivity) to match its namespace. For example,
|
||
`WebDesignGroup\ShopSite\Model\GalleryItem.php` should live at `mysite/src/Model/GalleryItem.php`.
|
||
|
||
Note that you don’t have to use `src/` as the root folder name. You can continue
|
||
to use `code/` if you want to. SilverStripe has adopted the PSR-4 approach and
|
||
have also started to use `src/` as a default folder location instead of
|
||
`code/`. If you’re going to change, it's probably a good time to do it while you're
|
||
upgrading.
|
||
|
||
For examples, take a look at the file/folder structure and to the
|
||
`composer.json` configuration in either the `framework` or `cms` modules.
|
||
|
||
Please note that there are changes to template structure which in some cases
|
||
require templates to be in a folder location that matches the namespace of the class
|
||
that it belongs to, e.g. `themes/mytheme/templates/MyVendor/Foobar/Model/MyModel.ss`.
|
||
|
||
## API Changes {#api-changes}
|
||
|
||
### General {#overview-general}
|
||
|
||
* Minimum PHP version raised to 5.6 (with support for PHP 7.x)
|
||
* Dropped support for PHP safe mode (removed php 5.4).
|
||
* Once PHP versions become [unsupported by the PHP Project](http://php.net/supported-versions.php)),
|
||
we drop support for those versions in the [next minor release](/contributing/release-process
|
||
This means PHP 5.6 and PHP 7.0 support will become unsupported in Dec 2018.
|
||
* Minimum CMS browser requirement raised from Internet Explorer 8 to Internet Explorer 11
|
||
* Updated PHPUnit from 3.7 to 4.8 ([upgrade notes](https://github.com/sebastianbergmann/phpunit/wiki/Release-Announcement-for-PHPUnit-4.0.0#backwards-compatibility-issues)).
|
||
Please remove any PHPUnit related `require_once()` calls (e.g. in `FeatureContext`
|
||
definitions of the [behat-extension](https://github.com/silverstripe-labs/silverstripe-behat-extension) module).
|
||
Run `composer require --dev 'phpunit/phpunit:~4.8'` on existing projects to pull in the new dependency.
|
||
* `always_populate_raw_post_data` will now raise a deprecation warning in install.php when running in php 5.x,
|
||
unless set to `-1`. This is due to `$HTTP_RAW_POST_DATA` being removed in php 7.
|
||
See the [http://php.net/manual/en/reserved.variables.httprawpostdata.php](php documentation) for more information.
|
||
* Admin URL can now be configured via custom Director routing rule
|
||
* `Controller::init` visibility changed to protected. Use `Controller::doInit()` instead.
|
||
* `Controller::join_links` supports an array of link sections.
|
||
* `Object::useCustomClass` has been removed. You should use the config API with Injector instead. {#object-usecustomclass}
|
||
* `Object::invokeWithExtensions` now has the same method signature as `Object::extend` and behaves the same way.
|
||
* `ServiceConfigurationLocator` is now an interface not a class.
|
||
* `i18nTextCollectorTask` merge is now true by default.
|
||
* `Object` has been broken up into various traits, each of which can be added to other objects independently:
|
||
* `Configurable` Provides Config API helper methods
|
||
* `Injectable` Provides Injector API helper methods
|
||
* `Extensible` Allows extensions to be applied
|
||
* `Convert` class has extra methods for formatting file sizes in php_ini compatible format
|
||
* `Convert::memstring2bytes()` will parse a php_ini memory size.
|
||
* `Convert::bytes2memstring()` will format the memory size with the appropriate scale.
|
||
* `SiteTree.alternatePreviewLink` is deprecated. Use `updatePreviewLink` instead.
|
||
* `Injector` dependencies no longer automatically inherit from parent classes.
|
||
* `$action` parameter to `Controller::Link()` method is standardised.
|
||
* Moved test database cleanup task from `sake dev/tests/cleanupdb` to `sake dev/tasks/CleanupTestDatabasesTask`
|
||
* `Injector::load` given a `src` parameter will no longer guess the
|
||
service name from the filename. Now the service name will either
|
||
by the array key, or the `class` parameter value.
|
||
* 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.
|
||
* 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.
|
||
* 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/).
|
||
See upgrading notes below.
|
||
* 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.
|
||
E.g. `$cms = ModuleLoader::inst()->getManifest()->getModule('silverstripe/cms')`
|
||
* `ClassManifest::getOwnerModule()` now returns a `Module` object instance.
|
||
* Moved `Object::parse_class_spec()` to `ClassInfo`
|
||
* Removed `create_from_spec()`. Supportede by `Injector` natively.
|
||
* Moved `Controller::Link()` to parent class (`RequestHandler`)
|
||
* Moved `Controller::redirect()` to parent class (`RequestHandler`)
|
||
* Moved `Controller::redirectBack()` to parent class (`RequestHandler`)
|
||
* `RequestHandler::Link()` now relies on the `url_segment` handler being provided for the class.
|
||
If left unset, this will raise an error.
|
||
* `RequestHandler::getBackURL()` and `getReturnReferer()` have been added to safely inspect the current request
|
||
to see if there is a url to redirect back to.
|
||
* Renamed `LeftAndMain_TreeNode` to `CMSMain_TreeNode`
|
||
* Removed `LeftAndMain::SiteTreeAsUL()` (override left in `CMSMain`)
|
||
* Moved `LeftAndMain::getSiteTreeFor()` to `CMSMain`
|
||
* Moved `LeftAndMain::getsubtree()` to `CMSMain`
|
||
* Moved `LeftAndMain::updatetreenodes()` to `CMSMain`
|
||
* Moved `LeftAndMain::savetreenodes()` to `CMSMain`
|
||
* Renamed `LeftAndMain::EditorToolbar()` to `Modals()`. Returns a `ModalController` handler
|
||
instance rather than a `HTMLEditorField_Toolbar`
|
||
* Removed `Director::$dev_servers` and `Director::$test_servers`
|
||
* Removed `Director::$urlParams` and `Director::setUrlParams()`
|
||
* Removed `Director.alternate_host`. Use `Director.alternate_base_url` instead.
|
||
* Removed `Director.alternate_protocol`. Use `Director.alternate_base_url` instead.
|
||
* 'BlockUntrustedIPS' env setting has been removed.
|
||
All IPs are untrusted unless `SS_TRUSTED_PROXY_IPS` is set to '*'
|
||
See [Environment Management docs](/getting_started/environment_management/) for full details.
|
||
* `SS_TRUSTED_PROXY_HOST_HEADER`, `SS_TRUSTED_PROXY_PROTOCOL_HEADER`, and `SS_TRUSTED_PROXY_IP_HEADER`
|
||
are no longer supported. These settings should go into the Injector service configuration for
|
||
TrustedProxyMiddleware instead.
|
||
* Removed `SS_HOST` environment constant. Use `SS_BASE_URL` instead.
|
||
* `Member::canLogIn()` now returns boolean. Use `Member::validateCanLogin()` to get a `ValidationResult`
|
||
* Moved `Security::has_default_admin` to `DefaultAdminService::hasDefaultAdmin()`
|
||
* Moved `Security::check_default_admin` to `DefaultAdminService::isDefaultAdminCredentials()`
|
||
* Moved `Security::default_admin_torname` to `DefaultAdminService::getDefaultAdminUsername()`
|
||
* Moved `Security::default_admin_password` to `DefaultAdminService::getDefaultAdminPassword()`
|
||
* Moved `Security::setDefaultAdmin` to `DefaultAdminService::setDefaultAdmin()`
|
||
* Moved `Security::clearDefaultAdmin` to `DefaultAdminService::clearDefaultAdmin()`
|
||
* Moved `Security::findAnAdministrator` to `DefaultAdminService::findOrCreateDefaultAdmin()`
|
||
* Deprecated `Member::checkPassword()`. Use `Authenticator::checkPassword()` instead
|
||
* Deprecated `RequestFilter`. Use
|
||
[HTTPMiddleware](/developer_guides/controllers/middlewares) instead.
|
||
* Changed `RequestFilter`: The `$session` and `$dataModel` variables removed from preRequest / postRequest.
|
||
* `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.
|
||
* Removed `ConfigureFromEnv.php`
|
||
* Changed `Session` object to avoid static access (`Session::inst()`).
|
||
Use it from the current request via `$request->getSession()` instead.
|
||
All static methods have been removed, and the `inst_` prefix removed from all instance members.
|
||
* `Director.rules` config no longer support `redirect:<url>` directly via config.
|
||
* Removed `Director::get_environment_type()` and `Director::set_environment_type()`.
|
||
Get the `Kernel` instance via injector and query `getEnvironment()` instead.
|
||
(e.g. `$type = Injector::inst()->get(Kernel::class)->getEnvironment()`)
|
||
* Removed `Director.environment_type` to configure the environment via YAML.
|
||
Use a [.env file](/getting_started/environment_management) to manage environment settings.
|
||
* Removed `increase_xdebug_nesting_level_to()` (functionality has been inlined into `AppKernel`)
|
||
* Moved `set_increase_time_limit_max()` to `Environment::setTimeLimitMax()`
|
||
* Moved `get_increase_time_limit_max()` to `Environment::getTimeLimitMax()`
|
||
* Moved `set_increase_memory_limit_max()` to `Environment::setMemoryLimitMax()`
|
||
* Moved `get_increase_memory_limit_max()` to `Environment::getMemoryLimitMax()`
|
||
* Moved `increase_time_limit_to()` to `Environment::increaseTimeLimitTo()`
|
||
* Moved `increase_memory_limit_to()` to `Environment::increaseMemoryLimitTo()`
|
||
* Moved `translate_memstring()` to `Convert::memstring2bytes`.
|
||
* Moved `getTempFolder()` to `TempFolder::getTempFolder()`
|
||
* Removed `getTempParentFolder()`
|
||
* Removed `getTempFolderUsername()`
|
||
* Removed `CMSMain::buildbrokenlinks()`
|
||
* Removed `Injector::unregisterAllObjects()`. Use `unregisterObjects` to unregister
|
||
groups of objects limited by type instead.
|
||
* Removed `SS_Log`. Use `Injector::inst()->get(LoggerInterface::class)` instead.
|
||
* Removed `CMSBatchAction_Delete`
|
||
* Removed `CMSBatchAction_DeleteFromLive`
|
||
* Removed `CMSMain.enabled_legacy_actions` config.
|
||
* `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
|
||
command instead (`vendor/bin/phpunit`).
|
||
* Removed `dev/jstests/` controller
|
||
* Removed `TestRunner` and `JSTestRunner`
|
||
* Removed `PhpUnitWrapper`, `PhpUnitWrapper_3_4`, `PhpUnitWrapper_3_5`, `PhpUnitWrapper_Generic`, `SapphireTestSuite` APIs
|
||
* Removed `SapphireTestReporter` and `CliTestReporter`
|
||
* Removed `SapphireTest::skipTest()`, use `markTestSkipped()` in a `setUp()` method instead
|
||
* `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.
|
||
* Removed `VirtualPage_Controller`. Virtual pages will now share whichever controller the “target” page uses
|
||
* Removed `Config_LRU`
|
||
* Removed `SilverStripeInjectionCreator`
|
||
* Removed `i18n::get_translatable_modules` method.
|
||
* Removed `i18nTextCollector_Writer_Php`
|
||
* `i18nTextCollector` no longer collects from `themes/<theme>` root dir.
|
||
Modules which provide themes via `<moduleName>/themes/<theme>` are now preferred.
|
||
* Removed `i18nSSLegacyAdapter`
|
||
* Removed `FunctionalTest::stat()`
|
||
* Removed `LeftAndMainMarkingFilter`
|
||
* Removed `Controller::getFormOwner()`
|
||
* Removed `TeamCityListener`
|
||
* Removed the `Spyc` YAML library. Please load it yourself, or use the included Symfony YAML component.
|
||
* Removed `RestfulService`. Use Guzzle instead ([details](#restfulservice))
|
||
* Removed `Oembed` in favour of a
|
||
[oscarotero/Embed](https://github.com/oscarotero/Embed)
|
||
* Removed `TextParser` and `BBCodeParser`. These are available in an archived module,
|
||
[silverstripe-archive/bbcodeparser](https://github.com/silverstripe-archive/silverstripe-bbcodeparser)
|
||
* Removed `ViewableData::ThemeDir`. Use `ThemeResourceLoader::findThemedResource` in conjunction with `SSViewer::get_themes` instead.
|
||
* Removed `Config::FIRST_SET` and `Config::INHERITED`
|
||
* Removed `RequestHandler.require_allowed_actions`. This is now fixed to on and cannot be
|
||
disabled.
|
||
* Removed `ClassManifest::getModules()`. Use `ModuleLoader` instead
|
||
* Removed `ClassManifest::getConfigDirs()`. Use `ModuleLoader` instead
|
||
* Removed `ClassManifest::getConfigs()`. Use `ModuleLoader` 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()`.
|
||
Use the `Security.login_recording` config setting instead.
|
||
* Removed `ModelAsController::find_old_page()`. Use `OldPageRedirector::find_old_page()` instead
|
||
* Removed `RootURLController:set_default_homepage_link()` and `RootURLController::get_default_homepage_link()`.
|
||
Use the `RootURLController.default_homepage_link` config setting instead.
|
||
* Removed `CreditCardField`, `CountryDropdownField`, `PhoneNumberField`, `MemberDatetimeOptionsetField`, `InlineFormAction`.
|
||
Use custom code instead
|
||
* Removed `ResetFormAction`, use `FormAction::create()->setAttribute('type', 'reset')` instead
|
||
* `Injector` now complies with [PSR-11](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-11-container.md).
|
||
Accordingly, `hasService()` has been renamed to `has()`, and `get()` will throw
|
||
`SilverStripe\Core\Injector\InjectorNotFoundException` when the service can't be found.
|
||
* Removed `CustomMethods::createMethod()`. Use closures instead.
|
||
* Removed `Extension::$ownerBaseClass` property. You should use `$this->owner->baseClass()` instead.
|
||
The second argument of `Extension::setOwner()` has also been removed.
|
||
* Deprecated `ClassInfo::baseDataClass()`. Use `DataObject::getSchema()->baseDataClass()` instead.
|
||
* Deprecated `ClassInfo::table_for_object_field()`. Use `DataObject::getSchema()->tableForField()` instead
|
||
* Deprecated `Config::inst()->update()`. Use `Config::modify()->set()` or `Config::modify()->merge()`
|
||
instead.
|
||
|
||
### ORM {#overview-orm}
|
||
|
||
* Deprecated `SQLQuery` in favour `SQLSelect` ([details](#sqlquery))
|
||
* Added `DataObject.many_many` 'through' relationships now support join dataobjects in place of
|
||
automatically generated join tables. See the [/developer_guides/relations](datamodel relationship docs)
|
||
for more info.
|
||
* Added `DataList::filter()` by null now internally generates "IS NULL" or "IS NOT NULL" conditions
|
||
appropriately on queries.
|
||
* Changed `DataObject` constructor to require an additional parameter, which must be included in subclasses.
|
||
* `DataObject::db` now returns composite fields.
|
||
* `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::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.
|
||
* `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. Use `HTMLText` for shortcodes enabled,
|
||
and `HTMLFragment` without shortcodes enabled (the new default).
|
||
* Renamed `String::NoHTML()` to `Plain()`
|
||
* Removed `String::LimitWordCountXML()`. Use `LimitWordCount()` instead.
|
||
* Removed `String::BigSummary()`. Use `Summary()` instead.
|
||
* Changed `HTMLText` limit methods to operate on 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).
|
||
* Renamed `FormField#createTag()` to `FormField::create_tag()`
|
||
* `Hierarchy` class has had much of it's functionality refactored out into `MarkedSet`:
|
||
* `isMarked()`
|
||
* `isTreeOpened()`
|
||
* `isExpanded()`
|
||
* `markByID()`
|
||
* `markPartialTree()`
|
||
* `markExpanded()`
|
||
* `markUnexpanded()`
|
||
* `markToExpose()`
|
||
* `markClosed()`
|
||
* `markOpened()`
|
||
* `markedNodeIDs()`
|
||
* `getChildrenAsUL()` replaced with `renderChildren()`, which now takes a template name.
|
||
* `markingFilterMatches()` (and made protected)
|
||
* `markChildren()` (and made protected)
|
||
* Search filter classes (e.g. `ExactMatchFilter`) are now registered with `Injector`
|
||
via a new `DataListFilter.` prefix convention.
|
||
see [search filter documentation](/developer_guides/model/searchfilters) for more information.
|
||
* `Permission::flush_permission_cache()` renamed to `reset()` and added to `Resettable` interface.
|
||
* Changed `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
|
||
with custom stages.
|
||
* Renamed `Versioned::reading_stage()` to `set_stage()` (throws an error if setting an invalid stage)
|
||
* Renamed `Versioned::current_stage()` to `get_stage()`
|
||
* Removed `Versioned::getVersionedStages()`
|
||
* Removed `Versioned::get_live_stage()`. Use the `Versioned::LIVE` constant instead.
|
||
* Removed `Versioned::getDefaultStage()`. Use the `Versioned::DRAFT` constant instead.
|
||
* Changed `Versioned::$versionableExtensions` from `private static` to `protected static`
|
||
* Added `Versioned::hasStages()` to check if an object has a given stage.
|
||
* Added `Versioned::stageTable()` to get the table for a given class and stage.
|
||
* Any extension declared via `versionableExtensions` config on Versioned dataobject must now
|
||
`VersionableExtension` interface at a minimum. `Translatable` has been removed from default
|
||
`versionableExtensions`
|
||
* 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.
|
||
* 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
|
||
available to the CMS and in draft mode.
|
||
* `_versions` suffixed tables are now renamed to `_Versions`. This fix will be automatically
|
||
applied during dev/build.
|
||
* 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 `doPublish()`, `doArchive()`, `doPublish()`, and `doUnpublish()` actions.
|
||
However, `do*()` methods will no longer automatically check `can*()` permissions, and must be done by
|
||
usercode before invocation.
|
||
* Moved `SiteTree::getIsAddedToStage()` to `Versioned::isOnDraftOnly()`
|
||
* Moved `SiteTree::getIsModifiedOnStage()` to `Versioned::isModifiedOnDraft()`
|
||
* Moved `SiteTree::isPublished()` to `Versioned`.
|
||
* Renamed `SiteTree::getExistsOnLive()` to `isPublished()`
|
||
* Added `Versioned::isOnDraft()`
|
||
* Added `Versioned::isArchived()`
|
||
* Added `Versioned::isOnLiveOnly()`
|
||
* Added `ChangeSet` and `ChangeSetItem` for batch publishing of versioned dataobjects.
|
||
* Added `DataObject.table_name` config to customise the database table for any record.
|
||
* Added `DataObjectSchema` class to assist with mapping between classes and tables.
|
||
* Changed `DataObject.indexes` to use `columns` instead of `value` to define index contents.
|
||
* 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.
|
||
* Removed `DatabaseAdmin#clearAllData()`. Use `DB::get_conn()->clearAllData()` instead
|
||
* Moved `SapphireTest` temp DB methods into a new `TempDatabase` class.
|
||
This allows applications to create temp databases when not running tests.
|
||
* Moved `SapphireTest::using_temp_db()` to `TempDatabase->isUsed()`
|
||
* Moved `SapphireTest::kill_temp_db()` to `TempDatabase->kill()`
|
||
* Moved `SapphireTest::empty_temp_db()` to `TempDatabase->clearAllData()`
|
||
* Moved `SapphireTest::create_temp_db()` to `TempDatabase->build()`
|
||
* Moved `SapphireTest::delete_all_temp_dbs()` to `TempDatabase->deleteAll()`
|
||
* Moved `SapphireTest::resetDBSchema()` to `TempDatabase->resetSchema()`
|
||
* `DBDate`, `DBTime` and `DBDatetime` have changed methods:
|
||
* Added `getTimestamp()` to get the respective date / time as unix timestamp (seconds since 1970-01-01)
|
||
* 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).
|
||
E.g. `d/m/Y H:i:s` (php format) should be replaced with to `dd/MM/y HH:mm:ss` (CLDR format).
|
||
* Added `getISOFormat()` to return the standard date/time ISO 8601 pattern in CLDR format.
|
||
* Changed `setValue()` method to expect dates and times to be passed in
|
||
ISO 8601 format (y-MM-dd) or (HH:mm:ss). Certain date formats will attempt to parse with
|
||
the below restrictions:
|
||
- `/`, `.` or `-` are supported date separators, but will be replaced with `-` internally.
|
||
- US date formats (m-d-y / y-d-m) will not be supported and may be parsed incorrectly.
|
||
(Note: Date form fields will still support localised date formats).
|
||
- `dd-MM-y` will be converted to `y-MM-dd` internally.
|
||
- 2-digit values for year will now raise errors.
|
||
* Changed `FormatFromSettings()` to default to `Nice()` format if no member is logged in.
|
||
* Changed `Nice()`, `Long()` and `Full()` methods to follow standard formatting rules for the
|
||
current locale, rather than pre-defined formats.
|
||
* Added `Short()` to format concise date/time values, as a shorter version than `Nice`
|
||
* Added `getFormatter()` to return a locale-specific date/time formatter.
|
||
* Added `DBTime::FormatFromSettings()`
|
||
* Deprecated globals `$database` and `$databaseConfig`. Use `DB::setConfig()` instead.
|
||
* Removed `DataModel`
|
||
* 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()`
|
||
* Moved `DataObject::manyManyComponent()` to `DataObjectSchema` (access through `DataObject->getSchema()`)
|
||
* Moved `DataObject::belongsToComponent()` to `DataObjectSchema` (access through `DataObject->getSchema()`)
|
||
* Moved `DataObject::hasOneComponent()` to `DataObjectSchema` (access through `DataObject->getSchema()`)
|
||
* Moved `DataObject::hasManyComponent()` to `DataObjectSchema` (access through `DataObject->getSchema()`)
|
||
* Moved `DataObject::getRemoteJoinField()` to `DataObjectSchema` (access through `DataObject->getSchema()`)
|
||
* Moved `DataObject::database_fields()` to `DataObjectSchema::databaseFields()`
|
||
* Moved `DataObject::has_own_table()` to `DataObjectSchema::classHasTable()`
|
||
* Moved `DataObject::composite_fields()` to `DataObjectSchema::compositeFields()`
|
||
* Moved `DataObject::manyManyExtraFieldsForComponent()` to `DataObjectSchema`
|
||
* Deprecated `DataObject::$destroyed`
|
||
* Removed `DataObject::validateModelDefinitions`. Relations are now validated within `DataObjectSchema`
|
||
* Removed `DataObject` methods `hasOwnTableDatabaseField`, `has_own_table_database_field` and {#dataobject-has-own}
|
||
`hasDatabaseFields` are superceded by `DataObjectSchema::fieldSpec`.
|
||
Use `$schema->fieldSpec($class, $field, DataObjectSchema::DB_ONLY | DataObjectSchema::UNINHERITED )`.
|
||
Exclude `uninherited` option to search all tables in the class hierarchy.
|
||
* Renamed `DataObject::is_composite_field()` to `DataObjectSchema::compositeField()`
|
||
* Renamed `DataObject::custom_database_fields()`to `DataObjectSchema::databaseFields()`
|
||
or `DataObjectSchema::fieldSpecs()` instead.
|
||
* Removed `DataList::getRelation`, as it was mutable. Use `DataList::applyRelation` instead, which is immutable.
|
||
* `Member` Field 'RememberLoginToken' removed, replaced with 'RememberLoginHashes' has_many relationship
|
||
* Removed `UpgradeSiteTreePermissionSchemaTask`
|
||
* Removed `EncryptAllPasswordsTask`
|
||
* Removed `DBString::LimitWordCountXML()` method. Use `LimitWordCount()` for XML safe version.
|
||
* Removed `SiteTree::getExistsOnLive()`. Use `isPublished()` instead.
|
||
* Removed `SiteTree::getIsDeletedFromStage()`. Use `isOnDraft()` instead (inverse case).
|
||
* 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.
|
||
* Removed `DataObject::validateModelDefinitions()`. Validation and parsing of config is now handled
|
||
within `DataObjectSchema`.
|
||
* `CMSBatchAction_Delete` removed. Use `CMSBatchAction_Archive` instead.
|
||
* Removed `Date::past_date()`
|
||
* Removed `Date::prior_monday()`
|
||
* Removed `Date::weekday()`
|
||
* Removed `Date::next_day()`
|
||
* Removed `Date::day_before()`
|
||
* Removed `Date::days_between()`
|
||
* Removed `Date::nice_format()`. Use locale-specific formatting for `Nice()`
|
||
* Removed `Time::nice_format()`. Use locale-specific formatting for `Nice()`
|
||
* Removed `Datetime::nice_format()`. Use locale-specific formatting for `Nice()`
|
||
* Removed `Time::TwelveHour()`
|
||
* Removed `Time::Nice24()`
|
||
* Removed `Money::NiceWithShortname()`
|
||
* Removed `Money::NiceWithName()`
|
||
* Removed `Money::getShortName()`
|
||
* Removed `Money::getCurrencyName()`
|
||
* Removed additional arguments from `Money::getSymbol()`. The result of this value is
|
||
now localised based on the currency code assigned to the `Money` instance
|
||
* Removed `Money::getAllowedCurrencies`. Apply validation to `MoneyField` instead.
|
||
* Removed `Hierarchy::parentStack()` removed. Use `getAncestors()` instead
|
||
* Removed `Hierarchy::doAllChildrenIncludingDeleted()`. Use `AllChildrenIncludingDeleted()` instead
|
||
* Removed `Hierarchy::naturalNext()`
|
||
* Removed `Hierarchy::naturalPrev()`
|
||
* Removed `Hierarchy::markingFinished()`
|
||
|
||
### Filesystem {#overview-filesystem}
|
||
|
||
* Image manipulations have been moved into a new `[ImageManipulation](api:SilverStripe\Assets\ImageManipulation)` trait.
|
||
* Removed `CMSFileAddController`
|
||
* `UploadField::setAllowedFileCategories('image')` now excludes non-resizeable images. 'unresizeable_image' is
|
||
can be used to validate these types.
|
||
* `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'
|
||
* `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
|
||
urls are no longer able to identify assets.
|
||
* Extension point `HtmlEditorField::processImage` has been removed, and moved to `Image::regenerateImageHTML()`
|
||
* `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
|
||
more information.
|
||
* `File` is now versioned, and should be published before they can be used on the frontend.
|
||
See section on [Migrating File DataObject from 3.x to 4.0](#migrating-file-dataobject-from-3x-to-40)
|
||
below for upgrade notes.
|
||
* 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.
|
||
* `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
|
||
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
|
||
cache or combined 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.
|
||
* `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
|
||
of linked assets and file protection.
|
||
* `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`
|
||
|
||
The following image manipulations have been removed:
|
||
|
||
* Renamed `Image::SetRatioSize()` to `Fit()`
|
||
* Renamed `Image::SetWidth()` to `ScaleWidth()`
|
||
* Renamed `Image::SetHeight()` to `ScaleHeight()`
|
||
* Renamed `Image::SetSize()` to `Pad()`
|
||
* Renamed `Image::PaddedImage()` to `Pad()`
|
||
* Renamed `Image::CroppedImage()` to `Fill()`
|
||
* Renamed `Image::AssetLibraryPreview()` to `PreviewThumbnail()`
|
||
* Renamed `Image::AssetLibraryThumbnail()` to `CMSThumbnail()`
|
||
|
||
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.
|
||
|
||
* Removed `File::deletedatabaseOnly()`
|
||
* Renamed `File::link_shortcode_handler()` to `handle_shortcode()`
|
||
* Removed `File::setParentID()`
|
||
* Removed `File::getFullPath()`
|
||
* Removed `File::getRelativePath()`
|
||
* Removed `File::Content` database field (wasn't used by core)
|
||
|
||
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:
|
||
|
||
* Removed `Image_Cached`
|
||
* Removed `Image::regenerateFormattedImages()`
|
||
* Removed `Image::getGeneratedImages()`
|
||
* Removed `Image::deleteFormattedImages()`
|
||
* Removed `Image::handle_shortcode()` moved to `SilverStripe\Assets\Shortcodes\ImageShortcodeProvider::handle_shortcode()`
|
||
* Removed `AssetAdmin::deleteunusedthumbnails()`
|
||
* 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()`
|
||
|
||
### Templates and Form {#overview-template}
|
||
|
||
* Upgrade to TinyMCE 4.x
|
||
* 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
|
||
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
|
||
TinyMCE editor.
|
||
* `HtmlEditorField::setEditorConfig()` may now take an instance of a `HtmlEditorConfig` class, as well as a
|
||
standard config identifier name.
|
||
* `HeaderField` requires a `$name` constructor argument (`new HeaderField('MyName', 'My Title')`
|
||
* `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.
|
||
* `$module` parameter in `themedCSS()` and `themedJavascript()` removed.
|
||
* 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
|
||
* GridField edit form now has improved support for versioned DataObjects, with basic publishing
|
||
actions available when editing records.
|
||
* `PopoverField` added to provide popup-menu behaviour in react forms (currently not available for
|
||
non-react forms).
|
||
* Introduction of experimental `FormFactory` API as a substitute for DataObject classes being responsible
|
||
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
|
||
module.
|
||
* 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
|
||
does not support inline editing of files.
|
||
* 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
|
||
value from the ORM. The second argument to `setValue()` has been added.
|
||
* `FormField::create_tag()` moved to `SilverStripe\View\HTML->createTag()`.
|
||
* Changed `ListboxField` to multiple only. Previously, this field would operate as either a
|
||
single select (default) or multi-select through `setMultiple()`.
|
||
Now this field should only be used for multi-selection. Single-selection should be done using
|
||
a regular `DropdownField`.
|
||
* `GroupedDropdownField::setDisabled()` now only accepts a list of values
|
||
instead of a list of grouped values. The method now expectes
|
||
a non-associative array of values (not titles) or an `SS_List`.
|
||
|
||
The following methods and properties on `Requirements_Backend` have been renamed: {#requirements}
|
||
|
||
* Renamed `$combine_files` to `$combinedFiles`
|
||
* Renamed `$combine_js_with_min` to `$minifyCombinedFiles`
|
||
* Renamed `$write_header_comments` to `$writeHeaderComment`
|
||
* Renamed `$write_js_to_body` to `$writeJavascriptToBody`
|
||
* Renamed `$force_js_to_bottom` to `$forceJSToBottom`
|
||
* Renamed `get_combined_files_enabled()` to `getCombinedFilesEnabled()`
|
||
* Renamed `set_combined_files_enabled()` to `setCombinedFilesEnabled()`
|
||
* Renamed `get_suffix_requirements()` to `getSuffixRequirements()`
|
||
* Renamed `set_suffix_requirements()` to `setSuffixRequirements()`
|
||
* Renamed `get_custom_scripts()` to `getCustomScripts()`
|
||
* Renamed `unblock_all()` to `unblockAll()`
|
||
* Renamed `include_in_response()` to `includeInResponse()`
|
||
* Renamed `combine_files()` to `combineFiles()`
|
||
* Renamed `get_combine_files()` to `getCombinedFiles()`
|
||
* Renamed `clear_combined_files()` to `clearCombinedFiles()`
|
||
* Renamed `process_combined_files()` to `processCombinedFiles()`
|
||
* Renamed `set_write_js_to_body()` to `setWriteJavascriptToBody()`
|
||
* Renamed `set_force_js_to_bottom()` to `setForceJSToBottom()`
|
||
* Added `get_minify_combined_js_files()` and `set_minify_combined_js_files()`
|
||
* Added `get_force_js_to_bottom()`
|
||
* Added `get_write_js_to_body()`
|
||
* Changed `includeInHTML()` to remove the first parameter (`$template`)
|
||
|
||
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.
|
||
|
||
Form validation has been refactored significantly. A new `FormMessage` trait has been created to {#form-validation}
|
||
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.
|
||
|
||
`Form` behaviour methods have been changed:
|
||
|
||
* `__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.
|
||
* `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.
|
||
You can also save any response in the state by manually invoking `saveFormState` inside a custom
|
||
validation response handler.
|
||
* Renamed `setupFormErrors()` to `restoreFormState()`
|
||
* Renamed `resetValidation()` to `clearFormState()`
|
||
* Added `loadMessagesFrom()` to load a `ValidationResult` into a form.
|
||
* Changed `setMessage()` to accept `$cast` as a third parameter (instead of a `$escapeHtml` boolean)
|
||
* Removed `messageForForm()`. Use `setMessage()` or `sessionMessage()` instead.
|
||
* Added `getSessionValidationResult()` / `setSessionValidationResult()` to get / set session errors
|
||
* Added `getSessionData()` / `setSessionData()` to get / set field values cached in the session
|
||
* Removed `addErrorMessage()`. Use `sessionMessage()` or `sessionError()` to add a
|
||
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
|
||
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
|
||
passed.
|
||
* Moved `buttonClicked()` to `FormRequestHandler`
|
||
* Moved `checkAccessAction()` to `FormRequestHandler`
|
||
* Moved `handleField()` to `FormRequestHandler`
|
||
* Moved `httpSubmission()` to `FormRequestHandler`
|
||
* Moved `Link()` to `FormRequestHandler`
|
||
|
||
`Validator` methods have changed:
|
||
|
||
* Changed `validate()` to return a `ValidationResult` instance.
|
||
* Removed `requireField()`. Use `RequiredFields` subclass instead.
|
||
|
||
`ValidationResult` now has these methods:
|
||
|
||
* Added `serialize()` / `unserialize()` for saving within session state
|
||
* Renamed `messageList()` to `getMessages()`
|
||
* Changed `error()` to `addMessage()` / `addError()` / `addFieldMessage()` / `addFieldError()`
|
||
* Renamed `valid()` to `isValid()`
|
||
|
||
`ValidationException` has these changes:
|
||
|
||
* Changed constructor to remove second argument (`$message`). It now only accepts `$result`,
|
||
which may be a string, and optional `$code`
|
||
|
||
New `DatetimeField` methods replace `getConfig()` / `setConfig()`: {#datetimefield}
|
||
|
||
* Added `getTimezone()` / `setTimezone()`
|
||
* Added `getDateTimeOrder()` / `setDateTimeOrder()`
|
||
* Added `getLocale()` / `setLocale()`
|
||
* Removed `datavaluefield` config as internal data value is now fixed to ISO 8601 format
|
||
|
||
The `DatetimeField` has changed behaviour:
|
||
|
||
* It uses a combined input instead of a composite from `DateField` and `TimeField`
|
||
Consequently, `getDateField()` and `getTimeField()` have been removed.
|
||
* It returns [ISO 8601 normalised dates](https://html.spec.whatwg.org/multipage/infrastructure.html#local-dates-and-times)
|
||
by default in `Value()`, which include a "T" separator between date and time.
|
||
This is required to allow HTML5 input. Either use `setHTML5(false)` to set your custom format,
|
||
or use `dataValue()` to retrieve a whitespace separated representation.
|
||
* It no longer accepts `setValue()` as an array with 'date' and 'time' keys
|
||
* Added `getHTML5()` / `setHTML5()`
|
||
|
||
New `DateField` methods replace `getConfig()` / `setConfig()`: {#datefield}
|
||
|
||
* Added `getDateFormat()` / `setDateFormat()`
|
||
* Added `getMinDate()` / `setMinDate()`
|
||
* Added `getMaxDate()` / `setMaxDate()`
|
||
* Added `getLocale()` / `setLocale()`
|
||
|
||
The `DateField` has changed behavior:
|
||
|
||
* `DateField` no longer provides a jQuery UI date picker (`showcalendar` option),
|
||
and uses [HTML5 date pickers](https://www.wufoo.com/html5/types/4-date.html) by default instead.
|
||
* `DateField` provides an optional polyfill for
|
||
[browsers without HTML5 date picker support](http://caniuse.com/#feat=input-datetime)
|
||
* 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)
|
||
|
||
New `TimeField` methods replace `getConfig()` / `setConfig()` {#timefield}
|
||
|
||
* Added `getTimeFormat()` / `setTimeFormat()`
|
||
* Added `getLocale()` / `setLocale()`
|
||
|
||
Further API changes:
|
||
|
||
* Removed `TabularStyle`
|
||
* Removed `NestedForm`
|
||
* Removed `FieldList->getTabPathRewrites()`
|
||
* Removed `FieldList->setTabPathRewrites()`
|
||
* Removed `FieldList->rewriteTabPath()`
|
||
* Removed `Form->transformTo()`
|
||
* Removed `Form->callfieldmethod()`
|
||
* Removed `Form->single_field_required()`
|
||
* Removed `Form->current_action()`
|
||
* Removed `Form->set_current_action()`
|
||
* Removed `Form->testSubmission()`
|
||
* Removed `Form->testAjaxSubmission()`
|
||
* Removed `ValidationResult->messageList()`
|
||
* Removed `ValidationResult->codeList()`
|
||
* Removed `ValidationResult->message()`
|
||
* Removed `ValidationResult->starredList()`
|
||
* Removed `ValidationResult->error()`
|
||
* Removed `ValidationResult->valid()`
|
||
* Removed `ReportAdminForm.ss` template
|
||
* Removed `FormField::dontEscape()`. Escaping is now managed on a class by class basis.
|
||
* Removed `PermissionCheckboxSetField::getAssignedPermissionCodes()`
|
||
* Removed `Requirements::delete_combined_files()`
|
||
* Removed `NumericField_Readonly`. Use `setReadonly(true)` instead.
|
||
* Removed `SSViewer->set_source_file_comments()`
|
||
* Removed `SSViewer->get_source_file_comments()`
|
||
* Removed `SSViewer->getOption()`
|
||
* Removed `SSViewer->setOption()`
|
||
* Removed `MemberDatetimeOptionsetField` (no replacement)
|
||
* Removed `DateField_View_JQuery` (replaced with native HTML5 support in `DateField`)
|
||
* Moved `HTMLEditorField_Toolbar` to `SilverStripe\Admin\ModalController`
|
||
* Moved `HTMLEditorField_Embed` to`SilverStripe\AssetAdmin\EmbedResource`
|
||
* Removed `HTMLEditorField_File`
|
||
* Removed `HTMLEditorField_Flash`
|
||
* Removed `HTMLEditorField_Image`
|
||
|
||
### i18n {#overview-i18n}
|
||
|
||
* Upgrade of i18n to symfony/translation
|
||
* Localisation based on language-only (without any specific locale) is now supported
|
||
* `i18nEntityProvider::provideI18nEntities()` Now is expected to return only a single array
|
||
map of key to default values.
|
||
* i18n keys for '.PLURAL_NAME' and '.SINGULAR_NAME' have been changed back to use the namespaced class names
|
||
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
|
||
within yaml in array format as per [ruby i18n pluralization rules](http://guides.rubyonrails.org/i18n.html#pluralization).
|
||
* Moved `i18n.all_locales` config setting to `SilverStripe\i18n\Data\Locales.locales`
|
||
* Moved `i18n.common_languages` config setting to `SilverStripe\i18n\Data\Locales.languages`
|
||
* Moved `i18n.likely_subtags` config setting to `SilverStripe\i18n\Data\Locales.likely_subtags`
|
||
* Moved `i18n.tinymce_lang` config setting to `SilverStripe\Forms\HTMLEditor\TinyMCEConfig.tinymce_lang`
|
||
* Moved `i18n::get_tinymce_lang()` to `SilverStripe\Forms\HTMLEditor\TinyMCEConfig::get_tinymce_lang()`
|
||
* Moved `i18n::get_locale_from_lang()` to `SilverStripe\i18n\Data\Locales::localeFromLang()`
|
||
* Moved `i18n::get_lange_from_locale()` to `SilverStripe\i18n\Data\Locales::langFromLocale()`
|
||
* Moved `i18n::validate_locale()` to `SilverStripe\i18n\Data\Locales::validate()`
|
||
* Moved `i18n::get_common_languages()` to `SilverStripe\i18n\Data\Locales::getLanguages()`
|
||
* Moved `i18n::get_locale_name()` to `SilverStripe\i18n\Data\Locales::localeName()`
|
||
* Moved `i18n::get_language_name()` to `SilverStripe\i18n\Data\Locales::languageName()`
|
||
* Moved `i18n.module_priority` config setting to `SilverStripe\i18n\Data\Sources.module_priority`
|
||
* Moved `i18n::get_owner_module()` to `SilverStripe\Core\Manifest\ClassManifest::getOwnerModule()`
|
||
This now returns a `Module` object instance instead of a string.
|
||
* 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`
|
||
|
||
### Email {#overview-mailer}
|
||
|
||
* Changed `Mailer` to an interface
|
||
* `Email` re-written to be powered by [SwiftMailer](https://github.com/swiftmailer/swiftmailer)
|
||
* Default template body variable renamed from `$Body` to `$EmailContent`
|
||
* Renamed `Email->setTemplate()` to `Email->setHTMLTemplate()`
|
||
* Added `Email->setPlainTemplate()` for rendering plain versions of email
|
||
* Renamed `Email->populateTemplate()` to `Email->setData()`
|
||
|
||
### SapphireTest {#overview-testing}
|
||
|
||
* `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()`
|
||
* Removed `setUpOnce()`. Please use `setUpBeforeClass()`
|
||
* Removed `tearDownOnce()`. Please use `tearDownAfterClass()`
|
||
* Removed `TestListener`
|
||
* Renamed `$requiredExtensions` to `$required_extensions` (and made static)
|
||
* Renamed `$extraDataObjects` to `$extra_dataobjects` (and made static)
|
||
* Renamed `$extraControllers` to `$extra_controllers` (and made static)
|
||
|
||
### Security {#overview-security}
|
||
|
||
* `LoginForm` now has an abstract method `getAuthenticatorName()`. If you have made subclasses of this,
|
||
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
|
||
constructed by `MemberAuthenticator` and won't affect normal use.
|
||
* `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.
|