silverstripe-framework/docs/en/changelogs/3.1.0.md

14 KiB

3.1.0 (unreleased)

Overview

CMS

  • "Split view" editing with side-by-side preview of the edited website
  • Resizing of preview to common screen widths ("desktop", "tablet" and "smartphone")
  • Decluttered "Edit Page" buttons by moving minor actions into a "more options" panel
  • Auto-detect CMS changes and highlight the save button for better informancy
  • Display "last edited" and "last published" data for pages in CMS
  • CMS form fields now support help text through setDescription(), both inline and as tooltips
  • Removed SiteTree "MetaTitle" and "MetaKeywords" fields
  • More legible and simplified tab and menu styling in the CMS
  • Dropped support for Internet Explorer 7

Framework

  • Shortcodes are no longer supported in template files. They continue to work in DB fields and other HTMLText-cast fields.
  • DataList and ArrayList are now immutable, they'll return cloned instances on modification
  • Behaviour testing support through Behat, with CMS test coverage (see the SilverStripe Behat Extension for details)
  • Removed legacy table APIs (e.g. TableListField), use GridField instead
  • Deny URL access if Controller::$allowed_actions is undefined
  • Removed support for "*" rules in Controller::$allowed_actions
  • Removed support for overriding rules on parent classes through Controller::$allowed_actions
  • RestfulService verifies SSL peers by default
  • Editing of relation table data ($many_many_extraFields) in GridField
  • Optional integration with ImageMagick as a new image manipulation backend
  • Support for PHP 5.4's built-in webserver
  • Support for Composer dependency manager (also works with 3.0)

Upgrading

Static configuration properties are now immutable, you must use Config API.

A common SilverStripe pattern is to use a static variable on a class to define a configuration parameter. The configuration system added in SilverStripe 3.0 builds on this by using this static variable as a way of defining the default value.

In SilverStripe 3.0, it was possible to edit this value at run-time and have the change propagate into the configuration system. This is no longer the case, for performance reasons.

Many of the configuration variables have been change to "private" so that attempts to change them throw an error, but if you do have a configuration static that is able to be changed, and you change it, then the configuration system will silently ignore it.

Please change all run-time manipulation of configuration to use Config::inst()->update() or $this->config()->update(). For more information about how to use the config system, see the "Configuration" topic.

default_cast is now Text

In order to reduce the chance of accidentally allowing XSS attacks, the value of default_cast has been changed in 3.1 from HTMLText to Text. This means that any values used in a template that haven't been explicitly cast as safe will be escaped (< replaced with &lt; etc).

When upgrading, if methods return HTML fragments they need to explicitly cast them as such. This can either be done by returning an HTMLText object, like:

:::php
return DBField::create_field('HTMLText', '<div></div>');

or by defining the casting of the accessor method, like:

:::php
class Page extends SiteTree {
	static $casting = array(
		'MyDiv' => 'HTMLText'
	)

	function MyDiv() {
		return '<div></div>';
	}
}

SSViewer#process (and as a result ViewableData#renderWith) have been changed to already return explicitly cast HTMLText instances, so functions that return the result of these methods won't have to do any additional casting.

Note that this change means that if code was testing the result via is_string, that is no longer reliable.

Deny URL access if Controller::$allowed_actions is undefined or empty array

In order to make controller access checks more consistent and easier to understand, the routing will require definition of $allowed_actions on your own Controller subclasses if they contain any actions accessible through URLs, or any forms.

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

Please review all rules governing allowed actions in the "controller" topic.

Removed support for "*" rules in Controller::$allowed_actions

The wildcard ('*') character allowed to define fallback rules in case they weren't explicitly defined. This caused a lot of confusion, particularly around inherited rules. We've decided to remove the feature, you'll need to specificy each accessible action individually.

:::php
class MyController extends Controller {
	public static $allowed_actions = array('*' => 'ADMIN');
	// Always denied because not explicitly listed in $allowed_actions
	public function myaction($request) {}
	// Always denied because not explicitly listed in $allowed_actions
	public function myotheraction($request) {}
}

Please review all rules governing allowed actions in the "controller" topic.

Removed support for overriding rules on parent classes through Controller::$allowed_actions

Since 3.1, the $allowed_actions definitions only apply to methods defined on the class they're also defined on. Overriding inherited access definitions is no longer possible.

:::php
class MyController extends Controller {
	public static $allowed_actions = array('myaction' => 'ADMIN');
	public function myaction($request) {}
}
class MySubController extends MyController {
	// No longer works
	public static $allowed_actions = array('myaction' => 'CMS_ACCESS_CMSMAIN');
}

This also applies for custom implementations of handleAction() and handleRequest(), which now have to be listed in the $allowed_actions specifically. It also restricts Extension classes applied to controllers, which now can only grant or deny access or methods they define themselves.

New approach with the Config API

:::php
class MySubController extends MyController {
	public function init() {
		parent::init();

		Config::inst()->update('MyController', 'allowed_actions',
			array('myaction' => 'CMS_ACCESS_CMSMAIN')
		);
	}
}

Please review all rules governing allowed actions in the "controller" topic.

Grouped CMS Buttons

The CMS buttons are now grouped, in order to hide minor actions by default and declutter the interface. This required changing the form field structure from a simple FieldList to a FieldList which contains a CompositeField for all "major actions", and a TabSet with a single tab for all "minor actions". If you have previously added, removed or altered built-in CMS actions in any way, you'll need to adjust your code.

:::php
class MyPage extends Page {
	function getCMSActions() {
		$actions = parent::getCMSActions();

		// Inserting a new toplevel action (old)
		$actions->push(new FormAction('MyAction')); 

		// Inserting a new toplevel action (new)
		$actions->insertAfter(new FormAction('MyAction'), 'MajorActions');

		// Removing an action, both toplevel and nested (no change required)
		$actions->removeByName('action_unpublish'); 

		// Inserting a new minor action (new)
		$actions->addFieldToTab(
			'Root.ActionMenus.MoreOptions',
			new FormAction('MyMinorAction')
		);

		// Finding a toplevel action (no change required)
		$match = $actions->dataFieldByName('action_save');

		// Finding a nested action (new)
		$match = $actions->fieldByName('ActionMenus.MoreOptions')
			->fieldByName('action_MyMinorAction'); 

		return $actions;
	}
}

GridField and ModelAdmin Permission Checks

GridFieldDetailForm now checks for canEdit() and canDelete() permissions on your model. GridFieldAddNewButton checks canCreate(). The default implementation requires ADMIN permissions. You'll need to loosen those permissions if you want other users with CMS access to interact with your data. Since GridField is used in ModelAdmin, this change will affect both classes.

Example: Require "CMS: Pages section" access

:::php
class MyModel extends DataObject {
	public function canView($member = null) {
		return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
	}
	public function canEdit($member = null) {
		return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
	}
	public function canDelete($member = null) {
		return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
	}
	public function canCreate($member = null) {
		return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
	}

You can also implement custom permission codes. For 3.1.0 stable, we aim to further simplify the permission definitions, in order to reduce the boilerplate code required to get a model editable in the CMS.

Note: GridField is already relying on the permission checks performed through the CMS controllers, providing a simple level of security.

RestfulService verifies SSL peers by default

This makes the implementation "secure by default", by removing the call to curl_setopt(CURLOPT_SSL_VERIFYPEER, false). Failing to validate SSL peers makes HTTP requests vulnerable to man in the middle attacks. The underlying curl library relies on the operating system for the resulting CA certificate verification. On some systems (mainly Windows), these certificates are not available on a standard PHP installation, and need to be added manually through CURLOPT_CAINFO. Although it is not recommended, you can restore the old insecure behaviour with the following configuration: RestfulService::set_default_curl_option(CURLOPT_SSL_VERIFYPEER, false).

Other

  • TableListField, ComplexTableField, TableField, HasOneComplexTableField, HasManyComplexTableField and ManyManyComplexTableField have been removed from the core and placed into a module called "legacytablefields" located at https://github.com/silverstripe-labs/legacytablefields
  • prototype.js and behaviour.js have been removed from the core, they are no longer used. If you have custom code relying on these two libraries, please update your code to include the files yourself
  • Object::has_extension() and Object::add_extension() deprecated in favour of using late static binding, please use {class}::has_extension() and {class}::add_extension() instead, where {class} is the class name of your DataObject class.
  • Removed SiteTree.MetaKeywords since they are irrelevant in terms of SEO (seomoz article) and general page informancy
  • Removed SiteTree.MetaTitle as a means to customize the window title, use SiteTree.Title instead
  • Deprecated Profiler class, use third-party solutions like xhprof
  • Removed defunct or unnecessary debug GET parameters: debug_profile, debug_memory, profile_trace, debug_javascript, debug_behaviour
  • Removed Member_ProfileForm, use CMSProfileController instead
  • SiteTree::$nested_urls enabled by default. To disable, call SiteTree::disable_nested_urls().
  • Removed CMS permission checks from File->canEdit() and File->canDelete(). If you have unsecured controllers relying on these permissions, please override them through a DataExtension.
  • Moved email bounce handling to new "emailbouncehandler" module, including Email_BounceHandler and Email_BounceRecord classes, as well as the Member->Bounced property.
  • Deprecated global email methods htmlEmail() and plaintextEmail, as well as various email helper methods like encodeMultipart(). Use the Email API, or the Mailer class where applicable.
  • Removed non-functional $inlineImages option for sending emails
  • Removed support for keyed arrays in SelectionGroup, use new SelectionGroup_Item object to populate the list instead (see API docs).
  • FormField->setDescription() now renders in a <span class="description"> by default, rather than a title attribute * Removed Form->Name(): Use getName()
  • Removed FormField->setContainerFieldSet(): Use setContainerFieldList()
  • Removed FormField->rootFieldSet(): Use rootFieldList()
  • Removed Group::map(): Use DataList::("Group")->map()
  • Removed Member->generateAutologinHash(): Tokens are no longer saved directly into the database in plaintext. Use the return value of the Member::generateAutologinTokenAndHash to get the token
  • Removed Member->sendInfo(): use Member_ChangePasswordEmail or Member_ForgotPasswordEmail directly
  • Removed SQLMap::map(): Use DataList::("Member")->map()
  • Removed SQLMap::mapInGroups(): Use Member::map_in_groups()
  • Removed PasswordEncryptor::register()/unregister(): Use config system instead
  • Methods on DataList and ArrayList that used to both modify the existing list & return a new version now just return a new version. Make sure you change statements like $list->filter(...) to $list = $list->filter(...) for these methods:
    • ArrayList#reverse
    • ArrayList#sort
    • ArrayList#filter
    • ArrayList#exclude
    • DataList#where
    • DataList#limit
    • DataList#sort
    • DataList#addFilter
    • DataList#applyFilterContext
    • DataList#innerJoin
    • DataList#leftJoin
    • DataList#find
    • DataList#byIDs
    • DataList#reverse
  • DataList#dataQuery has been changed to return a clone of the query, and so can't be used to modify the list's query directly. Use DataList#alterDataQuery instead to modify dataQuery in a safe manner.
  • ScheduledTask, QuarterHourlyTask, HourlyTask, DailyTask, MonthlyTask, WeeklyTask and YearlyTask are deprecated, please extend from BuildTask or CliController, and invoke them in self-defined frequencies through Unix cronjobs etc.