mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-06-21 04:01:36 +02:00
Merge branch '3.0'
Conflicts: admin/css/screen.css admin/scss/_forms.scss docs/en/changelogs/3.0.0.md
This commit is contained in:
commit
64357a4522
|
@ -182,6 +182,7 @@ form.small .field input.text, form.small .field textarea, form.small .field sele
|
||||||
.field .chzn-container-single .chzn-single div b { background-position: 4px 0px; }
|
.field .chzn-container-single .chzn-single div b { background-position: 4px 0px; }
|
||||||
.field input.hasDatepicker { width: 50%; max-width: 96px; }
|
.field input.hasDatepicker { width: 50%; max-width: 96px; }
|
||||||
.field input.month, .field input.day, .field input.year { width: 56px; }
|
.field input.month, .field input.day, .field input.year { width: 56px; }
|
||||||
|
.field input.time { width: 64px; }
|
||||||
.field.remove-splitter { border-bottom: none; box-shadow: none; }
|
.field.remove-splitter { border-bottom: none; box-shadow: none; }
|
||||||
|
|
||||||
/** ---------------------------------------------------- Buttons ---------------------------------------------------- */
|
/** ---------------------------------------------------- Buttons ---------------------------------------------------- */
|
||||||
|
|
|
@ -244,6 +244,10 @@ form.small .field, .field.small {
|
||||||
input.month, input.day, input.year {
|
input.month, input.day, input.year {
|
||||||
width: ($grid-x * 7);
|
width: ($grid-x * 7);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input.time {
|
||||||
|
width: ($grid-x * 8); // smaller time field, since input is restricted
|
||||||
|
}
|
||||||
|
|
||||||
/* Hides borders in settings/access. Activated from JS */
|
/* Hides borders in settings/access. Activated from JS */
|
||||||
&.remove-splitter {
|
&.remove-splitter {
|
||||||
|
|
|
@ -37,7 +37,8 @@ chdir(dirname($_SERVER['SCRIPT_FILENAME']));
|
||||||
*/
|
*/
|
||||||
if(isset($_SERVER['argv'][2])) {
|
if(isset($_SERVER['argv'][2])) {
|
||||||
$args = array_slice($_SERVER['argv'],2);
|
$args = array_slice($_SERVER['argv'],2);
|
||||||
$_GET = array();
|
if(!isset($_GET)) $_GET = array();
|
||||||
|
if(!isset($_REQUEST)) $_REQUEST = array();
|
||||||
foreach($args as $arg) {
|
foreach($args as $arg) {
|
||||||
if(strpos($arg,'=') == false) {
|
if(strpos($arg,'=') == false) {
|
||||||
$_GET['args'][] = $arg;
|
$_GET['args'][] = $arg;
|
||||||
|
@ -47,7 +48,7 @@ if(isset($_SERVER['argv'][2])) {
|
||||||
$_GET = array_merge($_GET, $newItems);
|
$_GET = array_merge($_GET, $newItems);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$_REQUEST = $_GET;
|
$_REQUEST = array_merge($_REQUEST, $_GET);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set 'url' GET parameter
|
// Set 'url' GET parameter
|
||||||
|
|
|
@ -409,7 +409,8 @@ function increase_memory_limit_to($memoryLimit = -1) {
|
||||||
|
|
||||||
// Check hard maximums
|
// Check hard maximums
|
||||||
$max = get_increase_memory_limit_max();
|
$max = get_increase_memory_limit_max();
|
||||||
if($max != -1 && translate_memstring($memoryLimit) > translate_memstring($max)) return false;
|
|
||||||
|
if($max && $max != -1 && trANSLATE_MEMSTRING($memoryLimit) > translate_memstring($max)) return false;
|
||||||
|
|
||||||
// Increase the memory limit if it's too low
|
// Increase the memory limit if it's too low
|
||||||
if($memoryLimit == -1 || translate_memstring($memoryLimit) > translate_memstring($curLimit)) {
|
if($memoryLimit == -1 || translate_memstring($memoryLimit) > translate_memstring($curLimit)) {
|
||||||
|
|
|
@ -16,8 +16,10 @@
|
||||||
|
|
||||||
// speed up mysql_connect timeout if the server can't be found
|
// speed up mysql_connect timeout if the server can't be found
|
||||||
ini_set('mysql.connect_timeout', 5);
|
ini_set('mysql.connect_timeout', 5);
|
||||||
|
// Don't die half was through installation; that does more harm than good
|
||||||
ini_set('max_execution_time', 0);
|
ini_set('max_execution_time', 0);
|
||||||
|
// Prevent a white-screen-of-death
|
||||||
|
ini_set('display_errors', 'on');
|
||||||
|
|
||||||
error_reporting(E_ALL | E_STRICT);
|
error_reporting(E_ALL | E_STRICT);
|
||||||
|
|
||||||
|
|
|
@ -2,17 +2,26 @@
|
||||||
|
|
||||||
## Overview ##
|
## Overview ##
|
||||||
|
|
||||||
* New template engine
|
### CMS
|
||||||
* New CMS interface design
|
|
||||||
* Image/Link insertion moved into a modal dialog instead of a sidebar
|
* New CMS interface design more geared towards complex content solutions
|
||||||
|
* List view for pages (sortable and filterable)
|
||||||
|
* More powerful media and link insertion (including auto-embedding of external sources)
|
||||||
* Batch actions on site tree moved to an "Edit Tree" view
|
* Batch actions on site tree moved to an "Edit Tree" view
|
||||||
* "Add pages" dropdown now an "Add new" button which goes to a more descriptive page
|
* "Add pages" shows a dedicated interface with more info about the page type
|
||||||
* Renaming of sapphire to SilverStripe framework
|
|
||||||
* FormField classes now have their own HTML templates
|
|
||||||
* Allow usage of SilverStripe framework without the "cms" module
|
|
||||||
* CMS JavaScript moved to [jQuery.entwine](https://github.com/hafriedlander/jquery.entwine)
|
* CMS JavaScript moved to [jQuery.entwine](https://github.com/hafriedlander/jquery.entwine)
|
||||||
* CMS stylesheets are generated by SCSS to provide more flexible and robust styling
|
* CMS stylesheets are generated by SCSS to provide more flexible and robust styling
|
||||||
|
|
||||||
|
### Framework
|
||||||
|
|
||||||
|
* Renaming of "sapphire" to SilverStripe "framework"
|
||||||
|
* Allow usage of SilverStripe framework without the "cms" module
|
||||||
|
* New template engine with more powerful syntax
|
||||||
|
* New ORM layer with expressive and fluent syntax
|
||||||
|
* New GridField component to replace ComplexTableField
|
||||||
|
* FormField classes now have their own HTML templates
|
||||||
|
* Moved functionality to modules: Widget, RestfulServer, SapphireSoapServer, Translatable, IPRestrictions, PageComment, HomepageForDomain
|
||||||
|
|
||||||
## Detailed change logs ##
|
## Detailed change logs ##
|
||||||
|
|
||||||
The detailed change logs are broken down by pre-release:
|
The detailed change logs are broken down by pre-release:
|
||||||
|
@ -29,19 +38,67 @@ The detailed change logs are broken down by pre-release:
|
||||||
|
|
||||||
## Upgrading ##
|
## Upgrading ##
|
||||||
|
|
||||||
|
### Common Upgrade Tasks
|
||||||
|
|
||||||
|
* Rename foder from `sapphire/`to `framework/`, replace own paths with `FRAMEWORK_DIR` (in PHP) or `$ModulePath(framework)` (in templates). Update paths in `.htaccess` or `web.config` ([more](#sapphire-rename))
|
||||||
|
* Replace `<% control %>` in your templates with `<% loop %>` and `<% with %>` ([more](/reference/templates-upgrading-guide#control))
|
||||||
|
* Replace `DataObjectSet` with `DataList` or `ArrayList` ([more](#deprecated-classes))
|
||||||
|
* Rewrite `ComplexTableField` and `DataObjectManager` instances to `GridField`
|
||||||
|
* Rewrite `Director::redirect()` and `Director::redirectBack()` calls ([more] (#director-static-functions-deprecated-director-redirect-and-director-redirectback-in-particular)
|
||||||
|
* Use `<MyModel>::get()` rather than `DataObject::get()` ([more](#new-orm-datalist))
|
||||||
|
* Use new syntax for `DataObjectDecorator::extraStatics` ([more](#extensions))
|
||||||
|
* Change CMS tab paths from `Root.Content.Main` to `Root.Main`, move some field changes to new `SiteTree->getSettingsFields()` method ([more](#tab-paths))
|
||||||
|
* Add new modules if using specific core features like Widget, RestfulServer, PageComment or Translatable
|
||||||
|
|
||||||
### sapphire renamed to framework {#sapphire-rename}
|
### sapphire renamed to framework {#sapphire-rename}
|
||||||
|
|
||||||
`sapphire` has been renamed to `framework`.
|
The `sapphire` module has been renamed to `framework`. Please ensure the framework now resides in the new folder when upgrading. Here's a list of steps to check:
|
||||||
|
|
||||||
Please ensure the framework now resides in the new folder when upgrading.
|
|
||||||
|
|
||||||
Here's a list of steps to check:
|
|
||||||
|
|
||||||
* Remove your existing `sapphire` directory, and replace with `framework` from the new SilverStripe 3.0 package
|
* Remove your existing `sapphire` directory, and replace with `framework` from the new SilverStripe 3.0 package
|
||||||
* Rename references of `sapphire` to `framework` in `.htaccess`, `web.config` and `/usr/bin/sake` (the last is only necessary if you use `sake`)
|
* Rename references of `sapphire` to `framework` in `.htaccess`, `web.config` and `/usr/bin/sake` (the last is only necessary if you use `sake`)
|
||||||
* Find and replace any references to `sapphire` in your custom code to `framework`. In your PHP code, you can use the constant `FRAMEWORK_DIR`,
|
* Find and replace any references to `sapphire` in your custom code to `framework`. In your PHP code, you can use the constant `FRAMEWORK_DIR`,
|
||||||
which points to the framework directory, and in the templates you can use `$ModulePath(framework)`
|
which points to the framework directory, and in the templates you can use `$ModulePath(framework)`
|
||||||
|
|
||||||
|
### GridField: Replacement for TableListField and ComplexTableField [gridfield]###
|
||||||
|
|
||||||
|
We have a new component for managing lists of objects: The `[GridField](/topics/grid-field)`.
|
||||||
|
It's a substantial rewrite of the features previously captured by `TableListField`,
|
||||||
|
`ComplexTableField`, `HasManyComplexTableField` and `ManyManyComplexTableField`.
|
||||||
|
|
||||||
|
The legacy fields remain operational for now, although a switch to `GridField` is strongly encouraged,
|
||||||
|
for stability, interface and performance reasons. The `HasManyComplexTableField` and `ManyManyComplexTableField`
|
||||||
|
are no longer maintained, for those you do have to make the switch.
|
||||||
|
The `TableField` class will be deprecated soon, but we don't have an adequate replacement for it yet.
|
||||||
|
|
||||||
|
Upgrade example: Record listing
|
||||||
|
|
||||||
|
:::php
|
||||||
|
// before
|
||||||
|
$field = new TableListField('Companies', 'Company');
|
||||||
|
$field->setPageSize(20);
|
||||||
|
// after
|
||||||
|
$field = new GridField('Companies', null, Company::get());
|
||||||
|
$field->getConfig()->getComponentByType('GridFieldPaginator')->setItemsPerPage(20);
|
||||||
|
|
||||||
|
Upgrade example: Record listing with view/edit interface
|
||||||
|
|
||||||
|
:::php
|
||||||
|
// before
|
||||||
|
$field = new ComplexTableField($myController, 'Companies', 'Company');
|
||||||
|
// after
|
||||||
|
$field = new GridField('Companies', null, Company::get(), GridFieldConfig_RecordEditor::create());
|
||||||
|
|
||||||
|
|
||||||
|
Upgrade example: Relationship editing
|
||||||
|
|
||||||
|
:::php
|
||||||
|
// before
|
||||||
|
$field = new HasManyComplexTableField($myController, 'MyRelation', 'MyRelationObject');
|
||||||
|
// after
|
||||||
|
$field = new GridField('MyRelation', null, $myRecord->MyRelation(), GridFieldConfig_RelationEditor::create());
|
||||||
|
|
||||||
|
More information is available in the [GridField documentation](/topics/grid-field).
|
||||||
|
|
||||||
### Object static functions replaced with new Config class {#new-config}
|
### Object static functions replaced with new Config class {#new-config}
|
||||||
Static functions for getting a static variable on the `Object` class have been deprecated,
|
Static functions for getting a static variable on the `Object` class have been deprecated,
|
||||||
in favour of using the new `Config` class instead.
|
in favour of using the new `Config` class instead.
|
||||||
|
@ -66,21 +123,17 @@ Note the different options for the third parameter of `get()`:
|
||||||
If you don't set an option, it will get all the values for the static, including inherited ones.
|
If you don't set an option, it will get all the values for the static, including inherited ones.
|
||||||
This was previously known as `Object::combined_static()`.
|
This was previously known as `Object::combined_static()`.
|
||||||
|
|
||||||
### Director static functions deprecated, Director::redirect() and Director::redirectBack() in particular
|
### Director static functions deprecated (e.g. redirect() and redirectBack())
|
||||||
|
|
||||||
`Director::redirect()` and `Director::redirectBack()` are now marked as deprecated.
|
`Director::redirect()` and `Director::redirectBack()` are now marked as deprecated. If you have a `Controller` instance and need to redirect, call `redirect()` or `redirectBack()` on the instance
|
||||||
|
instead, e.g. `$controller->redirect()` or `$controller->redirectBack()`. Most of the time, form action handler methods on a controller need only call `$this->redirect()` or `$this->redirectBack()`.
|
||||||
|
|
||||||
If you have a `Controller` instance and need to redirect, call `redirect()` or `redirectBack()` on the instance
|
Use `Controller::curr()->redirect()` and `Controller::curr()->redirectBack()` if you need to redirect in contexts where a controller might not be immediately available.
|
||||||
instead, e.g. `$controller->redirect()` or `$controller->redirectBack()`. Most of the time, form action handler
|
|
||||||
methods on a controller need only call `$this->redirect()` or `$this->redirectBack()`.
|
|
||||||
|
|
||||||
Use `Controller::curr()->redirect()` and `Controller::curr()->redirectBack()` if you need to redirect in contexts
|
|
||||||
where a controller might not be immediately available.
|
|
||||||
|
|
||||||
### DataExtension and deprecated extraStatics on extension classes {#extensions}
|
### DataExtension and deprecated extraStatics on extension classes {#extensions}
|
||||||
|
|
||||||
`DataObjectDecorator` has been renamed to `DataExtension`. Any classes that extend `DataObjectDecorator`
|
`DataObjectDecorator` has been renamed to `DataExtension`. Please extend this class in case you
|
||||||
should now extend `DataExtension` instead.
|
have written your own extensions.
|
||||||
|
|
||||||
`extraStatics()` on extensions is now deprecated.
|
`extraStatics()` on extensions is now deprecated.
|
||||||
|
|
||||||
|
@ -89,41 +142,36 @@ Instead of using `extraStatics()`, you can simply define static variables on you
|
||||||
If you need custom logic, e.g. checking for a class before applying the statics on the extension,
|
If you need custom logic, e.g. checking for a class before applying the statics on the extension,
|
||||||
you can use `add_to_class()` as a replacement to `extraStatics()`.
|
you can use `add_to_class()` as a replacement to `extraStatics()`.
|
||||||
|
|
||||||
Given the original `extraStatics` function:
|
:::php
|
||||||
|
class MyExtension extends Extension {
|
||||||
<?php
|
|
||||||
//...
|
// before
|
||||||
function extraStatics($class, $extensionClass) {
|
function extraStatics($class, $extensionClass) {
|
||||||
if($class == 'MyClass') {
|
if($class == 'MyClass') {
|
||||||
return array(
|
return array(
|
||||||
'db' => array(
|
'db' => array(
|
||||||
'Title' => 'Varchar'
|
'Title' => 'Varchar'
|
||||||
|
);
|
||||||
);
|
);
|
||||||
);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// after
|
||||||
|
static $db = array(
|
||||||
|
'Title' => 'Varchar'
|
||||||
|
);
|
||||||
|
|
||||||
|
// advanced syntax
|
||||||
|
static function add_to_class($class, $extensionClass, $args = null) {
|
||||||
|
if($class == 'MyClass') {
|
||||||
|
Config::inst()->update($class, 'db', array(
|
||||||
|
'Title' => 'Varchar'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
parent::add_to_class($class, $extensionClass, $args);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
This would now become a static function `add_to_class`, and calls `update()` with an array
|
|
||||||
instead of returning it. It also needs to call `parent::add_to_class()`:
|
|
||||||
|
|
||||||
<?php
|
|
||||||
//...
|
|
||||||
static function add_to_class($class, $extensionClass, $args = null) {
|
|
||||||
if($class == 'MyClass') {
|
|
||||||
Config::inst()->update($class, 'db', array(
|
|
||||||
'Title' => 'Varchar'
|
|
||||||
));
|
|
||||||
}
|
|
||||||
parent::add_to_class($class, $extensionClass, $args);
|
|
||||||
}
|
|
||||||
|
|
||||||
Alternatively, you can define statics on the extension directly, like this:
|
|
||||||
|
|
||||||
<?php
|
|
||||||
//...
|
|
||||||
static $db = array(
|
|
||||||
'Title' => 'Varchar'
|
|
||||||
);
|
|
||||||
|
|
||||||
### New ORM: More flexible and expressive querying via `DataList` {#new-orm-datalist}
|
### New ORM: More flexible and expressive querying via `DataList` {#new-orm-datalist}
|
||||||
|
|
||||||
|
@ -166,6 +214,7 @@ for the presence of records, please call the count() method on the `DataList`:
|
||||||
// after
|
// after
|
||||||
if(!DataObject::get('SiteTree', '"ParentID" = 5')->count()) echo "Page 5 has no children";
|
if(!DataObject::get('SiteTree', '"ParentID" = 5')->count()) echo "Page 5 has no children";
|
||||||
|
|
||||||
|
Beware that `DataList->remove()` will delete an entry from the database.
|
||||||
See the ["datamodel" documentation](../../topics/datamodel) for more details.
|
See the ["datamodel" documentation](../../topics/datamodel) for more details.
|
||||||
|
|
||||||
### New ORM: Changes to manipulation of SQL queries {#new-orm-sql-queries}
|
### New ORM: Changes to manipulation of SQL queries {#new-orm-sql-queries}
|
||||||
|
@ -207,32 +256,10 @@ The abstract `RelationList` class and its implementations `ManyManyList` and `Ha
|
||||||
are replacing the `ComponentSet` API, which is only relevant if you have instanciated these manually.
|
are replacing the `ComponentSet` API, which is only relevant if you have instanciated these manually.
|
||||||
Relations are retrieved through the same way (e.g. `$myMember->Groups()`).
|
Relations are retrieved through the same way (e.g. `$myMember->Groups()`).
|
||||||
|
|
||||||
### New ORM: DataObjectSet->groupBy() changed to GroupedList decorator
|
|
||||||
|
|
||||||
:::php
|
|
||||||
$members = Member::get();
|
|
||||||
// before
|
|
||||||
$grouped = $members->groupBy('Surname');
|
|
||||||
// after
|
|
||||||
$grouped = GroupedList::create($members)->groupBy('Surname');
|
|
||||||
|
|
||||||
### Aggregate changes for partial caching in templates ###
|
|
||||||
|
|
||||||
`DataObject::Aggregate()` and `DataObject::RelationshipAggregate()` are now deprecated. To replace your deprecated aggregate calls
|
|
||||||
in PHP code, you should query with something like `Member::get()->max('LastEdited')`, that is, calling the aggregate on the `DataList` directly.
|
|
||||||
The same concept applies for replacing `RelationshipAggregate()`, just call the aggregate method on the relationship instead,
|
|
||||||
so something like `Member::get()->Groups()->max('LastEdited')`.
|
|
||||||
|
|
||||||
For partial caching in templates, the syntax `<% cached Aggregate(Page).Max(LastEdited) %>` has been deprecated. The new syntax is similar,
|
|
||||||
except you use `List()` instead of `Aggregate()`, and the aggregate call `Max()` is now lowercase, as in `max()`.
|
|
||||||
An example of the new syntax is `<% cached List(Page).max(LastEdited) %>`. Check `DataList` class for more aggregate methods to use.
|
|
||||||
|
|
||||||
### `SQLQuery` changes ###
|
### `SQLQuery` changes ###
|
||||||
|
|
||||||
`SQLQuery` has been changed so direct access to internal properties `$from`, `$select`, `$orderby` is
|
`SQLQuery` has been changed so direct access to internal properties `$from`, `$select`, `$orderby` is
|
||||||
now deprecated.
|
now deprecated. Instead, there are now methods you can call which allow you to get and set SQL clauses instead.
|
||||||
|
|
||||||
Instead, there are now methods you can call which allow you to get and set SQL clauses instead.
|
|
||||||
|
|
||||||
* `$from` getter is `getFrom()` and setters `setFrom()` and `addFrom()`
|
* `$from` getter is `getFrom()` and setters `setFrom()` and `addFrom()`
|
||||||
* `$select` getter is `getSelect()` and setters `setSelect()` and `addSelect()`
|
* `$select` getter is `getSelect()` and setters `setSelect()` and `addSelect()`
|
||||||
|
@ -244,23 +271,19 @@ Instead, there are now methods you can call which allow you to get and set SQL c
|
||||||
* `$distinct` getter is `getDistinct()` and setter `setDistinct()`
|
* `$distinct` getter is `getDistinct()` and setter `setDistinct()`
|
||||||
* `$delete` getter is `getDelete()` and setter `setDelete()`
|
* `$delete` getter is `getDelete()` and setter `setDelete()`
|
||||||
* `$connective` getter is `getConnective()` and settter `setConnective()`
|
* `$connective` getter is `getConnective()` and settter `setConnective()`
|
||||||
|
|
||||||
* `innerJoin()` has been renamed to `addInnerJoin()`
|
* `innerJoin()` has been renamed to `addInnerJoin()`
|
||||||
* `leftJoin()` has been renamed to `addLeftJoin()`
|
* `leftJoin()` has been renamed to `addLeftJoin()`
|
||||||
|
|
||||||
### TinyMCE upgraded to 3.5 ###
|
### Aggregate changes for partial caching in templates ###
|
||||||
|
|
||||||
TinyMCE has been upgraded to version 3.5.
|
`DataObject::Aggregate()` and `DataObject::RelationshipAggregate()` are now deprecated. To replace your deprecated aggregate calls
|
||||||
|
in PHP code, you should query with something like `Member::get()->max('LastEdited')`, that is, calling the aggregate on the `DataList` directly.
|
||||||
|
The same concept applies for replacing `RelationshipAggregate()`, just call the aggregate method on the relationship instead,
|
||||||
|
so something like `Member::get()->Groups()->max('LastEdited')`.
|
||||||
|
|
||||||
This change should be transparent to most people upgrading, but if you're using custom plugins for TinyMCE,
|
For partial caching in templates, the syntax `<% cached Aggregate(Page).Max(LastEdited) %>` has been deprecated. The new syntax is similar,
|
||||||
please ensure they are still working correctly with the new version.
|
except you use `List()` instead of `Aggregate()`, and the aggregate call `Max()` is now lowercase, as in `max()`.
|
||||||
|
An example of the new syntax is `<% cached List(Page).max(LastEdited) %>`. Check `DataList` class for more aggregate methods to use.
|
||||||
If you're upgrading from an SS 3.0 beta, TinyMCE HTML source editor and other popups might be blank.
|
|
||||||
This is caused by the TinyMCE compressor leaving stale cache files in the system temp folder from an earlier
|
|
||||||
version.
|
|
||||||
|
|
||||||
To resolve this problem, simply delete the `{hash}.gz` files within your temp location (defined by `sys_get_temp_dir()` in PHP.)
|
|
||||||
These cache files will be regenerated next time the CMS is opened.
|
|
||||||
|
|
||||||
### InnoDB driver for existing and new tables on MySQL (instead of MyISAM) [innodb]###
|
### InnoDB driver for existing and new tables on MySQL (instead of MyISAM) [innodb]###
|
||||||
|
|
||||||
|
@ -282,12 +305,9 @@ Note: MySQL has made InnoDB the default engine in its [5.5 release](http://dev.m
|
||||||
|
|
||||||
### Convert::json2array() changes [raw2json]###
|
### Convert::json2array() changes [raw2json]###
|
||||||
|
|
||||||
Convert JSON functions have been changed to use built-in json PHP functions `json_decode()` and `json_encode()`
|
Convert JSON functions have been changed to use built-in json PHP functions `json_decode()` and `json_encode()`.
|
||||||
|
|
||||||
Because `json_decode()` will convert nested JSON structures to arrays as well, this has changed the way it worked,
|
Because `json_decode()` will convert nested JSON structures to arrays as well, this has changed the way it worked,
|
||||||
as before nested structures would be converted to an object instead.
|
as before nested structures would be converted to an object instead. So, given the following JSON input to `Convert::json2array()`:
|
||||||
|
|
||||||
So, given the following JSON input to `Convert::json2array()`:
|
|
||||||
|
|
||||||
{"Joe":"Bloggs","Tom":"Jones","My":{"Complicated":"Structure"}}
|
{"Joe":"Bloggs","Tom":"Jones","My":{"Complicated":"Structure"}}
|
||||||
|
|
||||||
|
@ -311,47 +331,6 @@ Now in SilverStripe 3.x, nested structures are arrays:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
### GridField: Replacement for TableListField and ComplexTableField [gridfield]###
|
|
||||||
|
|
||||||
We have a new component for managing lists of objects: The `[GridField](/topics/grid-field)`.
|
|
||||||
It's a substantial rewrite of the features previously captured by `TableListField`,
|
|
||||||
`ComplexTableField`, `HasManyComplexTableField` and `ManyManyComplexTableField`.
|
|
||||||
|
|
||||||
The legacy fields remain operational for now, although a switch to `GridField` is strongly encouraged,
|
|
||||||
for stability, interface and performance reasons. The `HasManyComplexTableField` and `ManyManyComplexTableField`
|
|
||||||
are no longer maintained, for those you do have to make the switch.
|
|
||||||
The `TableField` class will be deprecated soon, but we don't have an adequate replacement for it yet.
|
|
||||||
|
|
||||||
Upgrade example: Record listing
|
|
||||||
|
|
||||||
:::php
|
|
||||||
// before
|
|
||||||
$field = new TableListField('Companies', 'Company');
|
|
||||||
$field->setPageSize(20);
|
|
||||||
// after
|
|
||||||
$field = new GridField('Companies', null, Company::get());
|
|
||||||
$field->getConfig()->getComponentByType('GridFieldPaginator')->setItemsPerPage(20);
|
|
||||||
|
|
||||||
Upgrade example: Record listing with view/edit interface
|
|
||||||
|
|
||||||
:::php
|
|
||||||
// before
|
|
||||||
$field = new ComplexTableField($myController, 'Companies', 'Company');
|
|
||||||
// after
|
|
||||||
$field = new GridField('Companies', null, Company::get(), GridFieldConfig_RecordEditor::create());
|
|
||||||
|
|
||||||
|
|
||||||
Upgrade example: Relationship editing
|
|
||||||
|
|
||||||
:::php
|
|
||||||
// before
|
|
||||||
$field = new HasManyComplexTableField($myController, 'MyRelation', 'MyRelationObject');
|
|
||||||
// after
|
|
||||||
$field = new GridField('MyRelation', null, $myRecord->MyRelation(), GridFieldConfig_RelationEditor::create());
|
|
||||||
|
|
||||||
More information is available in the [GridField documentation](/topics/grid-field).
|
|
||||||
|
|
||||||
### New template engine [templates]###
|
### New template engine [templates]###
|
||||||
|
|
||||||
The template engine has been completely rewritten, and although it is generally backward compatible, there are new features
|
The template engine has been completely rewritten, and although it is generally backward compatible, there are new features
|
||||||
|
@ -385,6 +364,18 @@ The page tree moved from a bespoke tree library to [JSTree](http://jstree.com),
|
||||||
which required changes to markup of the tree and its JavaScript architecture.
|
which required changes to markup of the tree and its JavaScript architecture.
|
||||||
This includes changes to `TreeDropdownField` and `TreeMultiSelectField`.
|
This includes changes to `TreeDropdownField` and `TreeMultiSelectField`.
|
||||||
|
|
||||||
|
### TinyMCE upgraded to 3.5 ###
|
||||||
|
|
||||||
|
TinyMCE has been upgraded to version 3.5.
|
||||||
|
|
||||||
|
This change should be transparent to most people upgrading, but if you're using custom plugins for TinyMCE,
|
||||||
|
please ensure they are still working correctly with the new version.
|
||||||
|
|
||||||
|
If you're upgrading from an SS 3.0 beta, TinyMCE HTML source editor and other popups might be blank.
|
||||||
|
This is caused by the TinyMCE compressor leaving stale cache files in the system temp folder from an earlier
|
||||||
|
version. To resolve this problem, simply delete the `{hash}.gz` files within your temp location (defined by `sys_get_temp_dir()` in PHP.)
|
||||||
|
These cache files will be regenerated next time the CMS is opened.
|
||||||
|
|
||||||
### Settings-related fields move from SiteTree->getCMSFields() to new SiteTree->getSettingsFields() [getcmsfields]###
|
### Settings-related fields move from SiteTree->getCMSFields() to new SiteTree->getSettingsFields() [getcmsfields]###
|
||||||
|
|
||||||
The fields and tabs are now split into two separate forms, which required a structural
|
The fields and tabs are now split into two separate forms, which required a structural
|
||||||
|
@ -503,10 +494,9 @@ Please use the appropriate setters on the form field instance instead.
|
||||||
### EmailField now uses type "email" instead of type "text" {#email-form-field}
|
### EmailField now uses type "email" instead of type "text" {#email-form-field}
|
||||||
|
|
||||||
EmailField now uses "email" for the `type` attribute, which integrates better with HTML5 features like
|
EmailField now uses "email" for the `type` attribute, which integrates better with HTML5 features like
|
||||||
form validation in the browser.
|
form validation in the browser. If you want to change this back to "text", use `setAttribute()` when constructing the field:
|
||||||
|
|
||||||
If you want to change this back to "text", use `setAttribute()` when constructing the field:
|
|
||||||
|
|
||||||
|
:::php
|
||||||
$field = new EmailField('Email');
|
$field = new EmailField('Email');
|
||||||
$field->setAttribute('type', 'text');
|
$field->setAttribute('type', 'text');
|
||||||
|
|
||||||
|
@ -514,7 +504,6 @@ If you want to change this back to "text", use `setAttribute()` when constructin
|
||||||
|
|
||||||
In order to make the SilverStripe framework useable without the `cms` module,
|
In order to make the SilverStripe framework useable without the `cms` module,
|
||||||
we've moved some files around.
|
we've moved some files around.
|
||||||
|
|
||||||
CMS base functionality which is not directly related to content pages (`SiteTree`)
|
CMS base functionality which is not directly related to content pages (`SiteTree`)
|
||||||
has been moved from the `cms` module into a new "sub-module" located in `framework/admin`.
|
has been moved from the `cms` module into a new "sub-module" located in `framework/admin`.
|
||||||
This includes generic management interfaces like "Files & Images" (`AssetAdmin`),
|
This includes generic management interfaces like "Files & Images" (`AssetAdmin`),
|
||||||
|
|
|
@ -83,11 +83,6 @@ class GridFieldDataColumns implements GridField_ColumnProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify custom formatting for fields, e.g. to render a link instead of pure text.
|
|
||||||
* Caution: Make sure to escape special php-characters like in a normal php-statement.
|
|
||||||
* Example: "myFieldName" => '<a href=\"custom-admin/$ID\">$ID</a>'.
|
|
||||||
* Alternatively, pass a anonymous function, which takes one parameter: The list item.
|
|
||||||
*
|
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getFieldCasting() {
|
public function getFieldCasting() {
|
||||||
|
@ -95,6 +90,12 @@ class GridFieldDataColumns implements GridField_ColumnProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Specify custom formatting for fields, e.g. to render a link instead of pure text.
|
||||||
|
* Caution: Make sure to escape special php-characters like in a normal php-statement.
|
||||||
|
* Example: "myFieldName" => '<a href=\"custom-admin/$ID\">$ID</a>'.
|
||||||
|
* Alternatively, pass a anonymous function, which takes two parameters:
|
||||||
|
* The value returned by Convert::raw2xml and the original list item.
|
||||||
|
*
|
||||||
* @param array $formatting
|
* @param array $formatting
|
||||||
*/
|
*/
|
||||||
public function setFieldFormatting($formatting) {
|
public function setFieldFormatting($formatting) {
|
||||||
|
|
|
@ -253,7 +253,13 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
|
||||||
$comparisor = $this->applyFilterContext($field, $fieldArg, $value);
|
$comparisor = $this->applyFilterContext($field, $fieldArg, $value);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$SQL_Statements[] = '"'.Convert::raw2sql($field).'" '.$customQuery;
|
if($field == 'ID') {
|
||||||
|
$field = sprintf('"%s"."ID"', ClassInfo::baseDataClass($this->dataClass));
|
||||||
|
} else {
|
||||||
|
$field = '"' . Convert::raw2sql($field) . '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
$SQL_Statements[] = $field . ' ' . $customQuery;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(count($SQL_Statements)) {
|
if(count($SQL_Statements)) {
|
||||||
|
@ -350,10 +356,16 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
|
||||||
|
|
||||||
$SQL_Statements = array();
|
$SQL_Statements = array();
|
||||||
foreach($whereArguments as $fieldName => $value) {
|
foreach($whereArguments as $fieldName => $value) {
|
||||||
if(is_array($value)){
|
if($fieldName == 'ID') {
|
||||||
$SQL_Statements[] = ('"'.$fieldName.'" NOT IN (\''.implode('\',\'', Convert::raw2sql($value)).'\')');
|
$fieldName = sprintf('"%s"."ID"', ClassInfo::baseDataClass($this->dataClass));
|
||||||
} else {
|
} else {
|
||||||
$SQL_Statements[] = ('"'.$fieldName.'" != \''.Convert::raw2sql($value).'\'');
|
$fieldName = '"' . Convert::raw2sql($fieldName) . '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(is_array($value)){
|
||||||
|
$SQL_Statements[] = ($fieldName . ' NOT IN (\''.implode('\',\'', Convert::raw2sql($value)).'\')');
|
||||||
|
} else {
|
||||||
|
$SQL_Statements[] = ($fieldName . ' != \''.Convert::raw2sql($value).'\'');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->dataQuery->whereAny($SQL_Statements);
|
$this->dataQuery->whereAny($SQL_Statements);
|
||||||
|
|
|
@ -28,7 +28,8 @@ if(!defined('BASE_PATH')) define('BASE_PATH', dirname($frameworkPath));
|
||||||
// Copied from cli-script.php, to enable same behaviour through phpunit runner.
|
// Copied from cli-script.php, to enable same behaviour through phpunit runner.
|
||||||
if(isset($_SERVER['argv'][2])) {
|
if(isset($_SERVER['argv'][2])) {
|
||||||
$args = array_slice($_SERVER['argv'],2);
|
$args = array_slice($_SERVER['argv'],2);
|
||||||
$_GET = array();
|
if(!isset($_GET)) $_GET = array();
|
||||||
|
if(!isset($_REQUEST)) $_REQUEST = array();
|
||||||
foreach($args as $arg) {
|
foreach($args as $arg) {
|
||||||
if(strpos($arg,'=') == false) {
|
if(strpos($arg,'=') == false) {
|
||||||
$_GET['args'][] = $arg;
|
$_GET['args'][] = $arg;
|
||||||
|
@ -38,7 +39,7 @@ if(isset($_SERVER['argv'][2])) {
|
||||||
$_GET = array_merge($_GET, $newItems);
|
$_GET = array_merge($_GET, $newItems);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$_REQUEST = $_GET;
|
$_REQUEST = array_merge($_REQUEST, $_GET);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always flush the manifest for phpunit test runs
|
// Always flush the manifest for phpunit test runs
|
||||||
|
|
24
tests/model/DataListTest.php
Executable file → Normal file
24
tests/model/DataListTest.php
Executable file → Normal file
|
@ -14,7 +14,8 @@ class DataListTest extends SapphireTest {
|
||||||
'DataObjectTest_FieldlessSubTable',
|
'DataObjectTest_FieldlessSubTable',
|
||||||
'DataObjectTest_ValidatedObject',
|
'DataObjectTest_ValidatedObject',
|
||||||
'DataObjectTest_Player',
|
'DataObjectTest_Player',
|
||||||
'DataObjectTest_TeamComment'
|
'DataObjectTest_TeamComment',
|
||||||
|
'DataObjectTest\NamespacedClass',
|
||||||
);
|
);
|
||||||
|
|
||||||
public function testSubtract(){
|
public function testSubtract(){
|
||||||
|
@ -397,7 +398,26 @@ class DataListTest extends SapphireTest {
|
||||||
$this->assertEquals(1, $list->count(), 'There should be one comments');
|
$this->assertEquals(1, $list->count(), 'There should be one comments');
|
||||||
$this->assertEquals('Bob', $list->first()->Name, 'Only comment should be from Bob');
|
$this->assertEquals('Bob', $list->first()->Name, 'Only comment should be from Bob');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testFilterAndExcludeById() {
|
||||||
|
$id = $this->idFromFixture('DataObjectTest_SubTeam', 'subteam1');
|
||||||
|
$list = DataObjectTest_SubTeam::get()->filter('ID', $id);
|
||||||
|
$this->assertEquals($id, $list->first()->ID);
|
||||||
|
|
||||||
|
$list = DataObjectTest_SubTeam::get();
|
||||||
|
$this->assertEquals(3, count($list));
|
||||||
|
$this->assertEquals(2, count($list->exclude('ID', $id)));
|
||||||
|
|
||||||
|
// Check that classes with namespaces work.
|
||||||
|
$obj = new DataObjectTest\NamespacedClass();
|
||||||
|
$obj->Name = "Test";
|
||||||
|
$obj->write();
|
||||||
|
|
||||||
|
$list = DataObjectTest\NamespacedClass::get()->filter('ID', $obj->ID);
|
||||||
|
$this->assertEquals('Test', $list->First()->Name);
|
||||||
|
$this->assertEquals(0, $list->exclude('ID', $obj->ID)->count());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* $list->exclude('Name', 'bob'); // exclude bob from list
|
* $list->exclude('Name', 'bob'); // exclude bob from list
|
||||||
*/
|
*/
|
||||||
|
|
13
tests/model/DataObjectTest_Namespaced.php
Normal file
13
tests/model/DataObjectTest_Namespaced.php
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace DataObjectTest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Right now this is only used in DataListTest, but extending it to DataObjectTest in the future would make sense.
|
||||||
|
* Note that it was deliberated named to include "\N" to try and trip bad code up.
|
||||||
|
*/
|
||||||
|
class NamespacedClass extends \DataObject {
|
||||||
|
static $db = array(
|
||||||
|
'Name' => 'Varchar',
|
||||||
|
);
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ if (!defined('PHPUnit_MAIN_METHOD')) {
|
||||||
/**
|
/**
|
||||||
* Translate_Adapter_RailsYAML
|
* Translate_Adapter_RailsYAML
|
||||||
*/
|
*/
|
||||||
require_once 'Translate/Adapter/RailsYAML.php';
|
require_once dirname(__FILE__) . '/../../../library/Translate/Adapter/RailsYAML.php';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @category Zend
|
* @category Zend
|
||||||
|
|
Loading…
Reference in New Issue
Block a user