83 KiB
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
Highlights of major changes
- Minimum version dependencies have increased; PHP 5.5 and Internet Explorer 10 (or other modern browser) is required.
- All code earlier marked as deprecated for 4.0 has now been removed. See deprecation documentation for information on code deprecation.
- 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).
- Asset storage has been abstracted, and a new concept of
DBFile
references via database column references now exists in addition to references via the existingFile
dataobject. File security and protected files are now a core feature. - 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 4 as a base (blog post)
- Asset admin has been replaced with a purely ReactJS powered upgrade, and split out
module called asset-admin. You'll need to add this to your
composer.json
to retain file management capabilities for your CMS authors. - 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 act. Dependencies between versioned objects can be declared using the new ownership API, so that developers can ensure that relational consistency is maintained during publishing. This new system can be managed via the new "Campaigns" CMS section (blog post)
- Template variable casting (e.g.
<h1>$Title</h1>
) is enforced by default, which will ensure safe HTML encode unless explicitly opted out. - 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.
- i18n Updated to use symfony/translation over zend Framework 1. Zend_Translate has been removed.
- Replaced
Zend_Cache
and theCache
API with a PSR-16 implementation (symfony/cache) - _ss_environment.php files have been removed in favour of
.env
and "real" environment variables. - admin has been moved to a new module silverstripe/admin.
- Behat support updated to behat 3. See the behat extension for more information.
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 to
Standard Upgrade
This section describes the processes which every project upgrading to 4.0 should follow. This should be followed as a standard first point of upgrade.
Upgrade references to renamed and namespaced classes
Nearly all core PHP classes have been namespaced. For example, DataObject
is now called SilverStripe\ORM\DataObject
.
We have developed an upgrader tool to (semi-)automatically update your 3.x code to the new naming. Here's an example how to upgrade your mysite
folder:
composer global require silverstripe/upgrader
cd ~/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.
Upgrade template locations and references
Templates are now much more strict about their locations. You can no longer put a template in an arbitrary folder and have it be found. 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. This also affects <% include %>
statements in templates.
When using <% include %>
template tag you should continue to leave out the Includes
folder.
<% include Sidebar %>
will match only match Includes/Sidebar.ss
, not Sidebar.ss
.
Please refer to our template syntax for details.
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.
API Specific Upgrades
The below sections deal with upgrades to specific parts of various API. Projects which rely on certain API should be upgraded as appropriate using any of the relevant processes documented below.
Upgrade of _ss_environment.php
to .env
configuration
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
<?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.
See Environment Management docs for full details.
Compatibility with the new front-end building tools
If you are using Requirements from 3.x then your scripts should continue to work as they did before. The new front-end tooling is intended only for those wishing to customise the CMS look and feel, or behaviour.
Those wishing to customise the CMS should read about how to customise the admin interface.
Upgrade static references to 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,
to account for a structural change where both javascript and styles are co-located in component-specific folders.
This will affect you if you have used Requirements::block()
on files in the framework/
or cms/
folder.
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 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()
.
SilverStripe core is moving away from Requirements::combine_files
in favour of Webpack as of
4.0. Requirements::combine_files
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).
One commonly linked thirdparty dependency is jquery.js
bundled with SilverStripe:
framework/thirdparty/jquery/jquery.js => framework/admin/thirdparty/jquery/jquery.js
Explicit text casting is now enforced on all template variables
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.
Before:
class MyObject extends ViewableData {
public function getSomeHTML {
$title = Convert::raw2xml($this->Title);
return "<h1>{$title}</h1>";
}
}
After:
class MyObject extends ViewableData
{
private static $casting = [
'SomeHTML' => 'HTMLText'
];
public function getSomeHTML
{
$title = Convert::raw2xml($this->Title);
return "<h1>{$title}</h1>";
}
}
If you need to encode a field (such as HTMLText) for use in html attributes, use .ATT
instead, or if used in an actual XML file use .CDATA
(see template casting).
Upgrade code that uses SQLQuery
Where your code once used SQLQuery you should now use SQLSelect in all cases, as this has been removed (check the 3.2.0 upgrading notes).
Upgrade code that uses UploadField
This field has been superceded by a new class provided by the 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:
3.x code
:::php
class MyClass extends DataObject {
public function getCMSFields() {
return new FieldList(
new UploadField('Files')
);
}
}
4.x code
:::php
use SilverStripe\ORM\DataObject;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\FileHandleField;
class MyClass extends DataObject {
public function getCMSFields() {
return FieldList::create(
Injector::inst()->create(FileHandleField::class, 'Files')
);
}
}
Upgrade code that uses i18n
In many cases, localisation strings which worked in 3.x will continue to work in 4.0, however certain patterns 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.
Note: 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
for reference.
The below demonstrates how you can provide new localisation strings for an object, including both plurals and cross-module localisations.
:::php
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
:
:::yaml
en:
MyObject:
SINGULAR_NAME: 'object'
PLURAL_NAME: 'objects'
PLURALS:
one: 'An object',
other: '{count} objects'
extendedmodule/lang/en.yml
:
:::yaml
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
We're using native HTML5 date and time pickers
in DateField
and TimeField
now (discussion),
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 for the selected locale.
New asset storage mechanism
File system has been abstracted into an abstract interface. By default, the out of the box filesystem uses Flysystem 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.
SilverStripe\Filesystem\Flysystem\FlysystemAssetStore:
legacy_paths: true
See our "File Management" guide for more information.
Migrating File DataObject from 3.x to 4.0
Since the structure of File
dataobjects has changed, a new task MigrateFileTask
has been added to assist
in migration of legacy files. Migration can be invoked by either this task, or can be configured to automatically
run during dev build by setting the File.migrate_legacy_file
config to true. However, it's recommended that
this task is run manually during an explicit migration process, as this process could potentially consume
large amounts of memory and run for an extended time.
File:
migrate_legacy_file: true
This task will also support migration of existing File DataObjects to file versioning. Any pre-existing File DataObjects will be automatically published to the live stage, to ensure that previously visible assets remain visible to the public site.
If additional security or visibility rules should be applied to File dataobjects, then
make sure to correctly extend canView
via extensions.
Note that pre-existing security solutions for 3.x (such as secure assets module) are incompatible with core file security.
Upgrade code which acts on Image
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 [api:Image]
dataobject, and may have
manipulations applied to it. This should be used as the file type restriction on any [api:UploadField]
which
is intended to upload images for manipulation.
Before:
if($file instanceof Image) {
$upload = new UploadField();
$upload->setAllowedFileCategories('image');
}
After:
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')
.
E.g.
class MyObject extends DataObject
{
private static $has_one = array(
"ImageObject" => "Image"
);
private static $db = array(
"ImageField" => "DBFile('image/supported')"
);
}
Upgrading code that writes to File
dataobjects, or writes files to the 'assets' folder
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.
Before:
function importTempFile($tmp) {
copy($tmp, ASSETS_PATH . '/imported/' . basename($tmp));
$file = new File();
$file->setFilename('assets/imported/'.basename($tmp));
$file->write();
}
After:
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
File::remove_extension('Versioned');
Upgrading code performs custom 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:
// in MyImageExtension.php
class MyImageExtension extends 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
Image::add_extension('MyImageExtension');
Now image manipulations are implemented with a single method via a callback generator:
// in MyImageExtension.php
class MyImageExtension extends 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 ofDataExtension
- This extension is added to both
DBFile
andFile
, 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 genericmanipulate
method may be used, although the callback for this method both is given, and should return, anAssetStore
instance and file tuple (Filename, Hash, and Variant) rather than an Image_Backend.
Upgrading code that uses composite db fields.
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
class MyAddressField extends DBComposite
{
private static $composite_db = array(
'Street' => 'Varchar(200)',
'Suburb' => 'Varchar(100)',
'City' => 'Varchar(100)',
'Country' => 'Varchar(100)'
);
public function scaffoldFormField($title = null)
{
new AddressFormField($this->getName(), $title);
}
}
Upgrading code that references DataObject::database_fields
or DataObject::db
These methods have been updated to include base fields (such as ID, ClassName, Created, and LastEdited), as well as composite DB fields.
DataObject::database_fields
does not have a second parameter anymore, and can be called directly on an object
or class. E.g. Member::database_fields()
.
If user code requires the list of fields excluding base fields, then use custom_database_fields instead, or
make sure to call unset($fields['ID']);
if this field should be excluded.
DataObject:db()
will return all logical fields, including foreign key ids and composite DB Fields, alongside
any child fields of these composites. This method can now take a second parameter $includesTable, which
when set to true (with a field name as the first parameter), will also include the table prefix in
Table.ClassName(args)
format.
Upgrade code that uses SQLQuery
SQLQuery is still implemented, but now extends the new SQLSelect class and has some methods deprecated. Previously this class was used for both selecting and deleting, but these have been superceded by the specialised SQLSelect and SQLDelete classes.
Take care for any code or functions which expect an object of type SQLQuery
, as
these references should be replaced with SQLSelect
. Legacy code which generates
SQLQuery
can still communicate with new code that expects SQLSelect
as it is a
subclass of SQLSelect
, but the inverse is not true.
Upgrade code that references table names
A major change in 4.0.0 is that now tables and class names can differ from model to model. In order to
fix a table name, to prevent it being changed (for instance, when applying a namespace to a model)
the table_name
config can be applied to any DataObject class.
namespace SilverStripe\BannerManager;
use SilverStripe\ORM\DataObject;
class BannerImage extends DataObject
{
private static $table_name = 'BannerImage';
}
In order to ensure you are using the correct table for any class a new [api:DataObjectSchema] service is available to manage these mappings (see versioned documentation).
public function countDuplicates($model, $fieldToCheck)
{
$table = DataObject::getSchema()->tableForField($model, $field);
$query = new SQLSelect();
$query->setFrom("\"{$table}\"");
$query->setWhere(["\"{$table}\".\"{$field}\"" => $model->$fieldToCheck]);
return $query->count();
}
Upgrade BuildTask classes
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.
use SilverStripe\Dev\BuildTask;
class MyTask extends BuildTask
{
private static $segment = 'MyTask';
}
Upgrade implementations of augmentSQL
Since this method now takes a SQLSelect
as a first parameter, existing code referencing the deprecated SQLQuery
type will raise a PHP error.
Before:
function augmentSQL(SQLQuery &$query, DataQuery &$dataQuery = null) {
$locale = Translatable::get_current_locale();
if(!preg_match('/("|\'|`)Locale("|\'|`)/', implode(' ', $query->getWhere()))) {
$qry = sprintf('"Locale" = \'%s\'', Convert::raw2sql($locale));
$query->addWhere($qry);
}
}
After:
public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null)
{
$locale = Translatable::get_current_locale();
if (!preg_match('/("|\'|`)Locale("|\'|`)/', implode(' ', $query->getWhereParameterised($parameters)))) {
$query->addWhere(array(
'"Locale"' => $locale
));
}
}
Upgrade code that modifies the behaviour of ErrorPage
ErrorPage has been updated to use a configurable asset backend, similar to the AssetStore
described above.
This replaces the ErrorPage.static_filepath
config that was used to write local files.
As a result, error pages may be cached either to a local filesystem, or an external Flysystem store (which is configured via setting a new Flysystem backend with YAML).
ErrorPage::get_filepath_for_errorcode()
has been removed, because the local path for a specific code is
no longer assumed. Instead you should use ErrorPage::get_content_for_errorcode
which retrieves the
appropriate content for that error using one of the methods above.
In order to retrieve the actual filename (which is used to identify an error page regardless of base
path), you can use ErrorPage::get_error_filename()
instead. Unlike the old get_filepath_for_errorcode
method, there is no $locale parameter.
In case that user code must customise this filename, such as for extensions which provide a locale value
for any error page, the extension point updateErrorFilename
can be used. This extension point should
also be used to replace any alternateFilepathForErrorcode
used.
class MyErrorPageExtension extends SiteTreeExtension
{
public function updateErrorFilename(&$name, &$statuscode)
{
if ($this->owner->exists()) {
$locale = $this->Locale;
} else {
$locale = Translatable::get_current_locale();
}
$name = "error-{$statusCode}-{$locale}.html";
}
}
ErrorPage:
extensions:
- MyErrorPageExtension
Upgrading asset web.config, .htaccess, or other server configuration
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.
If upgrading from an existing installation, make sure to invoke ?flush=all
at least once.
See our "File Security" guide for more information.
ListboxField
is now multiple-only
Previously, this field would operate as either a single select (default) or multi-select by setting
setMultiple
to either true or false.
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.
Where previously you could specify a list of grouped values in the same way as setSource
, this
method now only accepts either a non-associative array of values (not titles) or an SS_List
of items to disable.
Upgrading TinyMCE to 4.0
Please see the tinymce upgrading guide to assist with upgrades to customisations to tinymce 3.
In Framework 4.0 the user interface for TinyMCE has been trimmed down considerably, with certain toolbar buttons removed from the default cms configuration. These include:
- 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.
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:
$editor = 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']
]);
For more information on available options and plugins please refer to the TinyMCE documentation
Upgrading DataObjects with the Versioned
extension
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.
/**
* This model has staging and versioning. Stages will be "Stage" and "Live"
*/
class MyStagedModel extends DataObject
{
private staic $extensions = array(
"SilverStripe\\ORM\\Versioning\\Versioned('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 = array(
"SilverStripe\\ORM\\Versioning\\Versioned('Versioned')"
);
}
Additionally, the following api methods have been added:
Versioned::publishRecursive
Publishes this object, and all owned objectsVersioned::publishSingle
Publishes this object, but not owned objectsVersioned::copyVersionToStage
Replaces the oldpublish
method.
These methods are deprecated:
Versioned::publish
Replaced byVersioned::copyVersionToStage
Versioned::doPublish
Replaced byVersioned::publishRecursive
Implementation of ownership API
In order to support the recursive publishing of dataobjects, a new API has been developed to allow developers to declare dependencies between objects. See the versioned documentation for more information.
By default all versioned dataobjects will automatically publish objects that they own.
ChangeSet batch publishing
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
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.
Upgrading references to DBField and subclasses
A major change in 4.0 is the introduction of namespaced DBField subclasses. Now as a standard, all DBField subclasses have a DB
prefix, are namespaced, and have an associative alias which omits the DB prefix.
This means that for the most part, code that worked in 3.0 won't need to be changed, although if you have any hard class literals which reference the old classes, they will need to be updated to point to the new namespaced classes.
An exception to this is any classes which once had the SS_
prefix, which will now be instead prefixed with DB
, and have an un-aliased prefix. For example SS_Datetime
is now DBDateTime
, and has the alias DateTime
which may be used in config.
Before:
class MyObject extends DataObject {
private static $db = array(
'Number' => 'Int',
'Time' => 'SS_Datetime'
);
/**
* @param Int $val
* @return Varchar
*/
public function TextNumber() {
return new Varchar('TextNumber', 'Number is ' . $this->Number);
}
}
After:
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\FieldType\DBVarchar;
class MyObject extends DataObject
{
private static $db = array(
'Number' => 'Int',
'Time' => 'Datetime'
);
/**
* @param Int $val
* @return Varchar
*/
public function TextNumber()
{
return new DBVarchar('TextNumber', 'Number is ' . $this->Number);
}
}
Note that string references to SS_Datetime
passed to injector, or used in config values, will still work, and will refer to the updated class names.
Upgrading from deprecated RestfulService
Install Guzzle to get an API consuming library.
composer require guzzlehttp/guzzle
or add guzzlehttp/guzzle: "^6.0"
to your composer.json.
For information on how to use Guzzle, please see the extensive Guzzle documentation
In case you want to keep using RestfulService, you can use Firesphere/silverstripe-restfulservice
, but it is unmaintained and deprecated.
Upgrading from deprecated Oembed
Instead of Oembed, the framework now relies on oscarotero/Embed to handle getting the shortcode-data for embedding. If you have custom embedding-code relying on Oembed, please refer to the documentation provided by oscarotero.
Admin URL can now be configured via custom Director routing rule
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 for language
specific functions.
Upgrading custom Authenticators
The methods register
and unregister
on Authenticator
are deprecated in favor of the Config
system. This means that any custom Authenticator needs to be registered through the yml config:
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:
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.
Upgrading Config API usages
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.
In addition, 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
.
Upgrading Cache API
We have replaced the unsupported Zend_Cache
library with symfony/cache.
This also allowed us to remove SilverStripe's Cache
API and use dependency injection with a standard
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()
).
Before:
:::php
$cache = Cache::factory('myCache');
// create a new item by trying to get it from the cache
$myValue = $cache->load('myCacheKey');
// set a value and save it via the adapter
$cache->save(1234, 'myCacheKey');
// retrieve the cache item
if (!$cache->load('myCacheKey')) {
// ... item does not exists in the cache
}
// Remove a cache key
$cache->remove('myCacheKey');
After:
:::php
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->get('myCacheKey');
// set a value and save it via the adapter
$cache->set('myCacheKey', 1234);
// retrieve the cache item
if (!$cache->has('myCacheKey')) {
// ... item does not exists in the cache
}
$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'
Configuration Changes
Caches are now configured through dependency injection services instead of PHP. See our "Caching" docs for more details.
Before (mysite/_config.php
):
:::php
Cache::add_backend(
'primary_memcached',
'Memcached',
array(
'servers' => array(
'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
API Changes
General and Core API
General and Core API Additions / Changes
- Minimum PHP version raised to 5.6 (with support for PHP 7.x)
- Once PHP versions become unsupported by the PHP Project), 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).
Please remove any PHPUnit related
require_once()
calls (e.g. inFeatureContext
definitions of the behat-extension module). Runcomposer 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. UseController::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::invokeWithExtensions
now has the same method signature asObject::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 methodsInjectable
Provides Injector API helper methodsExtensible
Allows extensions to be applied
SiteTree.alternatePreviewLink
is deprecated. UseupdatePreviewLink
instead.Injector
dependencies no longer automatically inherit from parent classes.$action
parameter toController::Link()
method is standardised.- Moved test database cleanup task from
sake dev/tests/cleanupdb
tosake dev/tasks/CleanupTestDatabasesTask
Injector::load
given asrc
parameter will no longer guess the service name from the filename. Now the service name will either by the array key, or theclass
parameter value.- Uniqueness checks for
File.Name
is performed on write only (not insetName()
) - 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. 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::instance()->getManifest()->getModule('silverstripe/cms')
ClassManifest::getOwnerModule()
now returns aModule
object instance.- Certain methods have been moved from
Controller
toRequestHandler
:Link
redirect
redirectBack
RequestHandler
link and redirection behaviour has been enhanced slightly:Link
now relies on theurl_segment
handler being provided for the class. If left unset, this will raise an error.getBackURL
andgetReturnReferer
have been added to safely inspect the current request to see if there is a url to redirect back to.
LeftAndMain
class has had all tree manipulation code moved into CMS module classCMSMain
. This includes:LeftAndMain_TreeNode
class -> renamed toCMSMain_TreeNode
SiteTreeAsUL
removed (override left inCMSMain
)getSiteTreeFor()
-> moved toCMSMain
getsubtree()
-> moved toCMSMain
updatetreenodes()
-> moved toCMSMain
savetreenodes()
-> moved toCMSMain
- Some
Director
API have been removed.- $dev_servers
- $test_servers
- $urlParams and setUrlParams()
Director.alternate_host
removed. UseDirector.alternate_base_url
instead.Director.alternate_protocol
removed. UseDirector.alternate_base_url
instead.
- Global enviromnent variables changed:
- 'BlockUntrustedIPS' env setting has been removed.
All IPs are untrusted unless
SS_TRUSTED_PROXY_IPS
is set to '*' See Environment Management docs for full details. MODULES_PATH
removedMODULES_DIR
removedSS_HOST
removed. UseSS_BASE_URL
instead.
- 'BlockUntrustedIPS' env setting has been removed.
All IPs are untrusted unless
General and Core Removed API
CMSMain::buildbrokenlinks()
action is removed.SS_Log
class has been removed. UseInjector::inst()->get(LoggerInterface::class)
instead.- Removed
CMSBatchAction_Delete
- Removed
CMSBatchAction_DeleteFromLive
- Removed
CMSMain.enabled_legacy_actions
config. CMSmain.getCMSTreeTitle
is now ignored on extensions. UseupdateCMSTreeTitle
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 (no replacement) - Removed
TestRunner
andJSTestRunner
APIs - Removed
PhpUnitWrapper
,PhpUnitWrapper_3_4
,PhpUnitWrapper_3_5
,PhpUnitWrapper_Generic
,SapphireTestSuite
APIs - Removed
SapphireTestReporter
andCliTestReporter
- Removed
SapphireTest::skipTest()
, usemarkTestSkipped()
in asetUp()
method instead - Removed the
History.js
javascript library. debugmethods
querystring argument has been removed from debugging.ErrorPage.static_filepath
config has been removed.ErrorPage::get_filepath_for_errorcode
has been removedErrorPage::alternateFilepathForErrorcode
extension point has been removed- 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 fromthemes/<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
- The Spyc YAML library has been removed from /thirdparty. Please load it yourself, or use the Symfony YAML component that’s automatically installed by composer.
RestfulService
has been removed. Use Guzzle instead. See Upgrading notes.- Our self-maintained
Oembed
implementation has been removed, in favour of introducing oscarotero/Embed as a dependency. - Removed TextParser and BBCodeParser. These are available in an archived module, silverstripe-archive/bbcodeparser
- Removed
ViewableData::ThemeDir
. UseThemeResourceLoader::findThemedResource
in conjunction withSSViewer::get_themes
instead. - Removed
Config::FIRST_SET
andConfig::INHERITED
- Removed
RequestHandler.require_allowed_actions
. This is now fixed to on and cannot be disabled. - Config or module searching methods have been removed from
ClassManifest
. UseModuleLoader
to get this information instead:getModules
getConfigDirs
getConfigs
- Removed
Session::set_config()
andSession::get_config()
. Use theSession.timeout
config setting instead
General and Core Deprecated API
A very small number of methods were chosen for deprecation, and will be removed in 5.0 rather than 4.0
ClassInfo::baseDataClass
- UseDataObject::getSchema()->baseDataClass()
instead.ClassInfo::table_for_object_field
- UseDataObject::getSchema()->tableForField()
insteadConfig::inst()->update()
is deprecated. UseConfig::modify()->set()
orConfig::modify()->merge()
instead.
ORM API
ORM API Additions / Changes
- Deprecate
SQLQuery
in favourSQLSelect
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.DataList::filter
by null now internally generates "IS NULL" or "IS NOT NULL" conditions appropriately on queries.DataList::createDataObject
is now public.DataObject
constructor now has an additional parameter, which must be included in subclasses.DataObject::db
now returns composite fields.DataObject::ClassName
field has been refactored into aDBClassName
type field.DataObject::can
has new method signature with$context
parameter.DBHTMLText
no longer enables shortcodes by default. Two injector aliases have been created for this class which can be used to select the correct behaviour:HTMLText
:DBHTMLText
with shortcodes enabledHTMLFragment
:DBHTMLText
without shortcodes enabled (as default)
CMSPreviewable
has been moved from theSilverStripe\Admin
namespace toSilverStripe\ORM
- Changes to
DBString
formatting:NoHTML
is renamed toPlain
LimitWordCountXML
is removed. UseLimitWordCount
instead.BigSummary
is removed. UseSummary
instead.- Most limit methods on
DBHTMLText
now plain text rather than attempt to manipulate the underlying HTML.
FormField::Title
andFormField::RightTitle
are now cast as plain text by default (but can be overridden).FormField#createTag()
has been renamed toFormField::create_tag()
Hierarchy
class has had much of it's functionality refactored out intoMarkedSet
:isMarked
isTreeOpened
isExpanded
markByID
markPartialTree
markExpanded
markUnexpanded
markToExpose
markClosed
markOpened
markedNodeIDs
getChildrenAsUL
replaced withrenderChildren
, which now takes a template name.markingFilterMatches
(and made protected)markChildren
(and made protected)
- Removed
DataList::applyFilterContext
private method - Search filter classes (e.g.
ExactMatchFilter
) are now registered withInjector
via a newDataListFilter.
prefix convention. see search filter documentation for more information. Permission::flush_permission_cache()
renamed toreset()
and added toResettable
interface.Versioned
API has some breaking changes:- 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.
reading_stage
is nowset_stage
and throws an error if setting an invalid stage.current_stage
is nowget_stage
getVersionedStages
is gone.get_live_stage
is removed. Use theVersioned::LIVE
constant instead.getDefaultStage
is removed. Use theVersioned::DRAFT
constant instead.$versionableExtensions
is nowprivate static
instead ofprotected static
hasStages
is addded to check if an object has a given stage.stageTable
is added to get the table for a given class and stage.- Any extension declared via
versionableExtensions
config on Versioned dataobject must nowVersionableExtension
interface at a minimum.Translatable
has been removed from defaultversionableExtensions
- 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
intoVersioned
extension.- All versioned DataObjects have
canPublish()
,canArchive()
,canUnpublish()
permission checks - All versioned Dataobjects have
doPublish()
,doArchive()
,doPublish()
, anddoUnpublish()
actions. However,do*()
methods will no longer automatically checkcan*()
permissions, and must be done by usercode before invocation. SiteTree::getIsAddedToStage()
moved toVersioned
and renamed toisOnDraftOnly()
SiteTre::getIsModifiedOnStage()
moved toVersioned
and renamed toisModifiedOnDraft()
SiteTree::isPublished()
moved toVersioned
.SiteTree::getExistsOnLive()
removed in favour ofisPublished()
isOnDraft()
added toVersioned
.isArchived()
added toVersioned
.isOnLiveOnly()
added toVersioned
.
- All versioned DataObjects have
ChangeSet
andChangeSetItem
have been added for batch publishing of versioned dataobjects.DataObject.table_name
config can now be used to customise the database table for any record.DataObjectSchema
class added to assist with mapping between classes and tables.DBMoney
values are now treated as empty only ifAmount
field is null. If anAmount
value is provided without aCurrency
specified, it will be formatted as per the current locale.- Removed
DatabaseAdmin#clearAllData()
. UseDB::get_conn()->clearAllData()
instead
The below methods have been added or had their functionality updated to DBDate
, DBTime
and DBDatetime
getTimestamp()
added to get the respective date / time as unix timestamp (seconds since 1970-01-01)Format()
method now use CLDR format strings, rather than PHP format string. E.g.d/m/Y H:i:s
(php format) should be replaced with todd/MM/y HH:mm:ss
(CLDR format).- getISOFormat() added which returns the standard date/time ISO 8601 pattern in CLDR format.
setValue
method is now a lot more restrictive, and expects 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 toy-MM-dd
internally.- 2-digit values for year will now raise errors.
FormatFromSettings
will default toNice()
format if no member is logged in.Nice
,Long
andFull
methods will now follow standard formatting rules for the current locale, rather than pre-defined formats.Short
added to format concise date/time values, as a shorter version thanNice
getFormatter
method added, which returns a locale-specific date/time formatter.
DBTime
specific changes:
- Added
DBTime::FormatFromSettings
ORM Removed API
DataObject::db
removed and replaced withDataObjectSchema::fieldSpec
andDataObjectSchema::fieldSpecs
DataObject::manyManyComponent
moved toDataObjectSchema
DataObject::belongsToComponent
moved toDataObjectSchema
DataObject::hasOneComponent
moved toDataObjectSchema
DataObject::hasManyComponent
moved toDataObjectSchema
DataObject::getRemoteJoinField
moved toDataObjectSchema
DataObject::database_fields
renamed and moved toDataObjectSchema::databaseFields
DataObject::has_own_table
renamed and moved toDataObjectSchema::classHasTable
DataObject::composite_fields
renamed and moved to `DataObjectSchema::compositeFields``DataObject::manyManyExtraFieldsForComponent
moved toDataObjectSchema
- Removed
DataObject::validateModelDefinitions
. Relations are now validated withinDataObjectSchema
- Removed
DataObject
methodshasOwnTableDatabaseField
,has_own_table_database_field
andhasDatabaseFields
are superceded byDataObjectSchema::fieldSpec
. Use$schema->fieldSpec($class, $field, DataObjectSchema::DB_ONLY | DataObjectSchema::UNINHERITED )
. Excludeuninherited
option to search all tables in the class hierarchy. - Removed
DataObject::is_composite_field
. UseDataObjectSchema::compositeField
instead. - Removed
DataObject::custom_database_fields
. UseDataObjectSchema::databaseFields
orDataObjectSchema::fieldSpecs
instead. - Removed
DataList::getRelation
, as it was mutable. UseDataList::applyRelation
instead, which is immutable. - Removed
DataList::applyFilterContext
private method Member
Field 'RememberLoginToken' removed, replaced with 'RememberLoginHashes' has_many relationship- Removed
UpgradeSiteTreePermissionSchemaTask
- Removed
EncryptAllPasswordsTask
- Removed
DBString::LimitWordCountXML()
method. UseLimitWordCount
for XML safe version. - Removed
SiteTree::getExistsOnLive()
. UseisPublished()
instead. - Removed
SiteTree::getIsDeletedFromStage()
. UseisOnDraft()
instead (inverse case). DataObject.many_many
no longer supports triangular resolution. Both themany_many
andbelongs_many_many
must point directly to the specific class on the opposing side, not a subclass or parent.DataObject::validateModelDefinitions()
has been removed. Validation and parsing of config is now handled withinDataObjectSchema
.CMSBatchAction_Delete
removed. UseCMSBatchAction_Archive
instead.- Removed several DBDate methods:
past_date
prior_monday
weekday
next_day
day_before
days_between
nice_format
has been removed fromDBDate
/DBTime
/DBDatetime
has been removed in favour of locale-specific formatting for Nice()- Removed several
DBTime
methods:TwelveHour
Nice24
- Removed some
DBMoney
methods due to lack of support in php-intl.NiceWithShortname
NiceWithName
getShortName
getCurrencyName
- Removed additional arguments from
DBMoney::getSymbol
. The result of this value is now localised based on the currency code assigned to theDBMoney
instance - Removed
DBMoney::getAllowedCurrencies
. Apply validation toMoneyField
instead. Hierarchy
has lots of removed api:parentStack()
removed. UsegetAncestors()
insteaddoAllChildrenIncludingDeleted()
removed. UseAllChildrenIncludingDeleted()
instead.naturalNext
removed.naturalPrev
removed.markingFinished
removed.
Filesystem API
Filesystem API Additions / Changes
- Image manipulations have been moved into a new
[api:ImageManipulation]
trait. CMSFileAddController
removed.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 fromAssetContainer
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 theDataObject::ID
in adata-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 toImage::regenerateImageHTML
Upload::load
now stores assets directly without saving into aFile
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 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.AssetNameGenerator
interface, including aDefaultAssetNameGenerator
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 theJSMinifier
class, but user code can substitute their own.AssetField
formfield to provide anUploadField
style uploader for the newDBFile
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.
Filesystem removed API
The following image manipulations previously deprecated has been removed:
Image::SetRatioSize
superceded byFit
Image::SetWidth
superceded byScaleWidth
Image::SetHeight
superceded byScaleHeight
Image::SetSize
superceded byPad
Image::PaddedImage
superceded byPad
Image::CroppedImage
superceded byFill
Image::AssetLibraryPreview
superceded byPreviewThumbnail
Image::AssetLibraryThumbnail
superceded byCMSThumbnail
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.
File::deletedatabaseOnly
File::link_shortcode_handler
renamed tohandle_shortcode
File::setParentID
File::getFullPath
File::getRelativePath
File::Content
database field is removed
Image manipulations have been moved out of Image.php and now available to any File or DBFile which has the appropriate mime types. The following file manipulations classes and methods have been removed:
Image_Cached
classImage::regenerateFormattedImages
methodImage::getGeneratedImages
methodImage::deleteFormattedImages
methodAssetAdmin::deleteunusedthumbnails
methodAssetAdmin::getUnusedThumbnails
method
Many Folder
api have also been removed:
Folder_UnusedAssetsField
classFolder::syncChildren
methodFolder::constructChild
methodFolder::addUploadToFolder
method
The following filesystem synchronisation methods and tasks are also removed
RegenerateCachedImagesTask
classCleanImageManipulationCache
classFilesystem::sync
methodAssetAdmin::doSync
method
Template and Form API
Template and Form API Additions / Changes
- Upgrade of TinyMCE to version 4.
- Templates now use a standard template lookup system via
SSViewer::get_templates_by_class
which builds a candidate list for a given class. Actual resolution of existing templates for any list of candidates is actually performed bySSViewer::chooseTemplate
HtmlEditorConfig
is now an abstract class, with a default implementationTinyMCEConfig
for the built in TinyMCE editor.HtmlEditorField::setEditorConfig
may now take an instance of aHtmlEditorConfig
class, as well as a standard config identifier name.HeaderField
requires a$name
constructor argument (new HeaderField('MyName', 'My Title')
default_cast
is now enforced on all template variables. See upgrading notes below.- FormField templates no longer look in the 'forms' folder for templates. As all form fields are now namespaced, the path for these templates will now match the namespace of the given class instead.
$module
parameter inthemedCSS
andthemedJavascript
removed.- Theme selector 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 toFormField::setValue($value, $data)
which is intended to load its value from the ORM. The second argument to setValue() has been added.
The following methods and properties on Requirements_Backend
have been renamed:
Requirements_Backund::$combine_files
made protected and renamed$combinedFiles
Requirements_Backend::$combine_js_with_min
made protected and renamed$minifyCombinedFiles
Requirements_Backend::$write_header_comments
made protected and renamed$writeHeaderComment
Requirements_Backend::$write_js_to_body
made protected and renamed to$writeJavascriptToBody
Requirements_Backend::$force_js_to_bottom
renamed to$forceJSToBottom
get_combined_files_enabled
renamed togetCombinedFilesEnabled
set_combined_files_enabled
renamed tosetCombinedFilesEnabled
get_suffix_requirements
renamed togetSuffixRequirements
set_suffix_requirements
renamed tosetSuffixRequirements
get_custom_scripts
renamed togetCustomScripts
unblock_all
renamed tounblockAll
include_in_response
renamed toincludeInResponse
combine_files
renamed tocombineFiles
get_combine_files
renamed togetCombinedFiles
clear_combined_files
renamed toclearCombinedFiles
process_combined_files
renamed toprocessCombinedFiles
set_write_js_to_body
renamed tosetWriteJavascriptToBody
set_force_js_to_bottom
renamed tosetForceJSToBottom
New methods on Requirements
are added to access these:
get_minify_combined_js_files
set_minify_combined_js_files
get_force_js_to_bottom
get_write_js_to_body
Some methods on Requirements
have had their method signatures changed:
includeInHTML
has had the first parameter $template removed as it was previously deprecated.
A new config Requirements_Backend.combine_in_dev
has been added in order to allow combined files to be
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
handle field-level and form-level messages. This has the following properties:
setMessage
to assign a message, type, and castgetMessage
retrieves the message stringgetMessageType
retrieves the message type (E.g. error, good, info)getMessageCast
retrieves the cast typegetMessageCastingHelper
retrieves the DBField cast to use for the appropriate message castgetSchemaMessage
encodes this message for form schema use in ReactJS.
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 withvalidationResult
instead, which returns aValidationResult
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 invokingsaveFormState
inside a custom validation response handler.setupFormErrors
renamed torestoreFormState
resetValidation
renamed toclearFormState
loadMessagesFrom
method created to load a ValidationResult into a form.setMessage
. third parameter is now $cast typemessageForForm
removed. UsesetMessage
orsessionMessage
instead.getSessionValidationResult
/setSessionValidationResult
used to get / set session errorsgetSessionData
/setSessionData
used to get / set field values cached in the sessiongetAjaxErrorResponse
andgetRedirectReferer
created to simplifygetValidationErrorResponse
addErrorMessage
removed. Users can either usesessionMessage
orsessionError
to add a form level message, throw a ValidationException during submission, or add a custom validator.Form
is no longer aRequestHandler
, but implements theHasRequestHandler
interface and returns aFormRequestHandler
instance fromgetRequestHandler()
. theForm
constructor no longer has any mandatory parameters, and the first parameter allows a non-Controller
RequestHandler
to be passed. Certain methods have been moved fromForm
toFormRequestHandler
:buttonClicked
checkAccessAction
handleField
httpSubmission
Link
Validator
methods have changed:
validate
method now returns aValidationResult
instance.requireField
method removed. UseRequiredFields
subclass instead.
ValidationResult
now has these methods:
serialize
/unserialize
for saving within session statemessageList
renamed togetMessages
error
method replaced withaddMessage
/addError
/addFieldMessage
/addFieldError
valid
renamed toisValid
ValidationException
has these changes:
$message
second constructor parameter is removed. Constructor only accepts$result
, which may be a string, and optional$code
New DatetimeField
methods replace getConfig()
/ setConfig()
:
getTimezone()
/setTimezone()
getDateTimeOrder()
/setDateTimeOrder()
getLocale()
/setLocale()
datavaluefield
config is removed 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
andTimeField
Consequently,getDateField()
andgetTimeField()
have been removed. - It returns ISO 8601 normalised dates
by default in
Value()
, which include a "T" separator between date and time. This is required to allow HTML5 input. Either usesetHTML5(false)
to set your custom format, or usedataValue()
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()
:
getDateFormat()
/setDateFormat()
getMinDate()
/setMinDate()
getMaxDate()
/setMaxDate()
getLocale()
/setLocale()
The DateField
has changed behavior:
DateField
no longer provides a jQuery UI date picker (showcalendar
option), and uses HTML5 date pickers by default instead.DateField
provides an optional polyfill for browsers without HTML5 date picker support- The
dmyfields
option has been replced with native HTML5 behaviour (as one single<input type=date>
). getClientLocale
/setClientLocale
have been removed (handled byDateField->locale
and browser settings)
New TimeField
methods replace getConfig()
/ setConfig()
getTimeFormat()
/setTimeFormat()
getLocale()
/setLocale()
getClientConfig()
has been removed (in favour ofsetHTML5()
)
Template and Form Removed API
- Removed
TabularStyle
- Removed
NestedForm
- Removed
FieldList
methods:getTabPathRewrites
setTabPathRewrites
rewriteTabPath
- Removed
Form
methods (see above for replacements):transformTo
callfieldmethod
single_field_required
current_action
set_current_action
setupFormErrors
resetValidation
messageForForm
addErrorMessage
testSubmission
testAjaxSubmission
- Removed
Validator::requireField()
method. - Removed
ValidationResult
(see above for replacements):messageList
codeList
message
starredList
error
valid
- Removed
ReportAdminForm.ss
template FormField::dontEscape()
has been removed. Escaping is now managed on a class by class basis.- Removed
PermissionCheckboxSetField::getAssignedPermissionCodes()
(never implemented) Requirements::delete_combined_files()
andRequirements::delete_combined_files()
methods have been removed as they are obsolete.- Removed
DatetimeField
,DateField
andTimeField
methodsgetConfig
andsetConfig
. Individual getters and setters for individual options are provided instead. See above for list of new methods. - Removed
NumericField_Readonly
. UsesetReadonly(true)
instead. SSViewer
deprecated methods removed:set_source_file_comments()
get_source_file_comments()
getOption
setOption
- Removed
MemberDatetimeOptionsetField
(no replacement) - Removed
DateField_View_JQuery
(replaced with native HTML5 support inDateField
)
i18n API
i18n API Additions / Changes
- 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.
i18n.all_locales
config moved toSilverStripe\i18n\Data\Locales.locales
i18n.common_languages
config moved toSilverStripe\i18n\Data\Locales.languages
i18n.likely_subtags
config moved toSilverStripe\i18n\Data\Locales.likely_subtags
i18n.tinymce_lang
config moved toSilverStripe\Forms\HTMLEditor\TinyMCEConfig.tinymce_lang
i18n::get_tinymce_lang()
moved toSilverStripe\Forms\HTMLEditor\TinyMCEConfig::get_tinymce_lang()
i18n::get_locale_from_lang()
moved toSilverStripe\i18n\Data\Locales::localeFromLang()
i18n::get_lange_from_locale()
moved toSilverStripe\i18n\Data\Locales::langFromLocale()
i18n::validate_locale()
moved toSilverStripe\i18n\Data\Locales::validate()
i18n::get_common_languages()
moved toSilverStripe\i18n\Data\Locales::getLanguages()
i18n::get_locale_name()
moved toSilverStripe\i18n\Data\Locales::localeName()
i18n::get_language_name()
moved toSilverStripe\i18n\Data\Locales::languageName()
i18n.module_priority
config moved toSilverStripe\i18n\Data\Sources.module_priority
i18n::get_owner_module()
moved toSilverStripe\Core\Manifest\ClassManifest::getOwnerModule()
This now returns aModule
object instance instead of a string.i18n::get_existing_translations()
moved toSilverStripe\i18n\Data\Sources::getKnownLocales()
i18n API Removed API
Zend_Translate
removedi18n::_t()
Support for sprintf-style%s
arguments deprecatedi18n::_t()
Using non-associative injection with named parameters is now an errori18n::get_language_name()
removed.i18n::get_language_code()
removed.i18n::get_common_locales()
removed.i18n.common_locales
config removed
Email and Mailer
Email Additions / Changes
Mailer
converted to an interfaceSwfitMailer
added as new default mailerEmail
re-written to be powered by SwiftMailer- Default template body variable renamed from
$Body
to$EmailContent
$email->setTemplate()
renamed to$email->setHTMLTemplate()
- Added
$email->setPlainTemplate
for rendering plain versions of email $email->populateTemplate()
has been replaced with$email->setData()
SapphireTest
setUpOnce
removed. Please usesetUpBeforeClass
tearDownOnce
removed. Please usetearDownAfterClass
TestListener
class removedSapphireTest::$requiredExtensions
renamed toSapphireTest::$required_extensions
and made staticSapphireTest::$extraDataObjects
renamed toSapphireTest::$extra_dataobjects
and made staticSapphireTest::$extraControllers
renamed toSapphireTest::$extra_controllers
and made static
Security
LoginForm
now has an abstract methodgetAuthenticatorName()
. 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, athough tis is usually constructed byMemberAuthenticator
and won't affect normal use.Authenticator
methodsregister
andunregister
are deprecated in favor of usingConfig