Merge branch '3.3' into 3.4

This commit is contained in:
Daniel Hensby 2016-08-22 16:22:37 +01:00
commit 060bf6b327
No known key found for this signature in database
GPG Key ID: B00D1E9767F0B06E
8 changed files with 154 additions and 45 deletions

View File

@ -19,7 +19,7 @@ name, and normally they all exist under the `Root` [api:TabSet].
display up to two levels of tabs in the interface. If you want to group data further than that, try [api:ToggleField]. display up to two levels of tabs in the interface. If you want to group data further than that, try [api:ToggleField].
</div> </div>
## Adding a field from a tab. ## Adding a field to a tab
:::php :::php
$fields->addFieldToTab('Root.Main', new TextField(..)); $fields->addFieldToTab('Root.Main', new TextField(..));

View File

@ -59,7 +59,7 @@ class GridFieldAddExistingAutocompleter
* 'Team.Name' * 'Team.Name'
* ) * )
* *
* @var Array * @var array
*/ */
protected $searchFields = array(); protected $searchFields = array();
@ -69,7 +69,7 @@ class GridFieldAddExistingAutocompleter
protected $resultsFormat = '$Title'; protected $resultsFormat = '$Title';
/** /**
* @var String Text shown on the search field, instructing what to search for. * @var string Text shown on the search field, instructing what to search for.
*/ */
protected $placeholderText; protected $placeholderText;
@ -90,7 +90,7 @@ class GridFieldAddExistingAutocompleter
/** /**
* *
* @param GridField $gridField * @param GridField $gridField
* @return string - HTML * @return string[] - HTML
*/ */
public function getHTMLFragments($gridField) { public function getHTMLFragments($gridField) {
$dataClass = $gridField->getList()->dataClass(); $dataClass = $gridField->getList()->dataClass();
@ -220,17 +220,20 @@ class GridFieldAddExistingAutocompleter
->limit($this->getResultsLimit()); ->limit($this->getResultsLimit());
$json = array(); $json = array();
$originalSourceFileComments = Config::inst()->get('SSViewer', 'source_file_comments'); Config::nest();
Config::inst()->update('SSViewer', 'source_file_comments', false); Config::inst()->update('SSViewer', 'source_file_comments', false);
$viewer = SSViewer::fromString($this->resultsFormat);
foreach($results as $result) { foreach($results as $result) {
$json[$result->ID] = html_entity_decode(SSViewer::fromString($this->resultsFormat)->process($result)); $json[$result->ID] = html_entity_decode($viewer->process($result));
} }
Config::inst()->update('SSViewer', 'source_file_comments', $originalSourceFileComments); Config::unnest();
return Convert::array2json($json); return Convert::array2json($json);
} }
/** /**
* @param String * @param string $format
*
* @return $this
*/ */
public function setResultsFormat($format) { public function setResultsFormat($format) {
$this->resultsFormat = $format; $this->resultsFormat = $format;
@ -238,7 +241,7 @@ class GridFieldAddExistingAutocompleter
} }
/** /**
* @return String * @return string
*/ */
public function getResultsFormat() { public function getResultsFormat() {
return $this->resultsFormat; return $this->resultsFormat;
@ -252,10 +255,11 @@ class GridFieldAddExistingAutocompleter
*/ */
public function setSearchList(SS_List $list) { public function setSearchList(SS_List $list) {
$this->searchList = $list; $this->searchList = $list;
return $this;
} }
/** /**
* @param Array * @param array $fields
*/ */
public function setSearchFields($fields) { public function setSearchFields($fields) {
$this->searchFields = $fields; $this->searchFields = $fields;
@ -263,7 +267,7 @@ class GridFieldAddExistingAutocompleter
} }
/** /**
* @return Array * @return array
*/ */
public function getSearchFields() { public function getSearchFields() {
return $this->searchFields; return $this->searchFields;
@ -274,8 +278,8 @@ class GridFieldAddExistingAutocompleter
* Falls back to {@link DataObject->summaryFields()} if * Falls back to {@link DataObject->summaryFields()} if
* no custom search fields are defined. * no custom search fields are defined.
* *
* @param String the class name * @param string $dataClass the class name
* @return Array|null names of the searchable fields * @return array|null names of the searchable fields
*/ */
public function scaffoldSearchFields($dataClass) { public function scaffoldSearchFields($dataClass) {
$obj = singleton($dataClass); $obj = singleton($dataClass);
@ -311,8 +315,9 @@ class GridFieldAddExistingAutocompleter
} }
/** /**
* @param String The class of the object being searched for * @param string $dataClass The class of the object being searched for
* @return String *
* @return string
*/ */
public function getPlaceholderText($dataClass) { public function getPlaceholderText($dataClass) {
$searchFields = ($this->getSearchFields()) $searchFields = ($this->getSearchFields())
@ -344,10 +349,13 @@ class GridFieldAddExistingAutocompleter
} }
/** /**
* @param String * @param string $text
*
* @return $this
*/ */
public function setPlaceholderText($text) { public function setPlaceholderText($text) {
$this->placeholderText = $text; $this->placeholderText = $text;
return $this;
} }
/** /**
@ -361,8 +369,11 @@ class GridFieldAddExistingAutocompleter
/** /**
* @param int $limit * @param int $limit
*
* @return $this
*/ */
public function setResultsLimit($limit) { public function setResultsLimit($limit) {
$this->resultsLimit = $limit; $this->resultsLimit = $limit;
return $this;
} }
} }

View File

@ -431,6 +431,7 @@ class ArrayList extends ViewableData implements SS_List, SS_Filterable, SS_Sorta
// This the main sorting algorithm that supports infinite sorting params // This the main sorting algorithm that supports infinite sorting params
$multisortArgs = array(); $multisortArgs = array();
$values = array(); $values = array();
$firstRun = true;
foreach($columnsToSort as $column => $direction) { foreach($columnsToSort as $column => $direction) {
// The reason these are added to columns is of the references, otherwise when the foreach // The reason these are added to columns is of the references, otherwise when the foreach
// is done, all $values and $direction look the same // is done, all $values and $direction look the same
@ -438,7 +439,7 @@ class ArrayList extends ViewableData implements SS_List, SS_Filterable, SS_Sorta
$sortDirection[$column] = $direction; $sortDirection[$column] = $direction;
// We need to subtract every value into a temporary array for sorting // We need to subtract every value into a temporary array for sorting
foreach($this->items as $index => $item) { foreach($this->items as $index => $item) {
$values[$column][] = $this->extractValue($item, $column); $values[$column][] = strtolower($this->extractValue($item, $column));
} }
// PHP 5.3 requires below arguments to be reference when using array_multisort together // PHP 5.3 requires below arguments to be reference when using array_multisort together
// with call_user_func_array // with call_user_func_array
@ -446,6 +447,10 @@ class ArrayList extends ViewableData implements SS_List, SS_Filterable, SS_Sorta
$multisortArgs[] = &$values[$column]; $multisortArgs[] = &$values[$column];
// First argument is the direction to be sorted, // First argument is the direction to be sorted,
$multisortArgs[] = &$sortDirection[$column]; $multisortArgs[] = &$sortDirection[$column];
if ($firstRun) {
$multisortArgs[] = defined('SORT_NATURAL') ? SORT_NATURAL : SORT_STRING;
}
$firstRun = false;
} }
$multisortArgs[] = &$originalKeys; $multisortArgs[] = &$originalKeys;

View File

@ -8,6 +8,9 @@
*/ */
class HasManyList extends RelationList { class HasManyList extends RelationList {
/**
* @var string
*/
protected $foreignKey; protected $foreignKey;
/** /**
@ -34,6 +37,10 @@ class HasManyList extends RelationList {
return $this->foreignKey; return $this->foreignKey;
} }
/**
* @param null|int $id
* @return array
*/
protected function foreignIDFilter($id = null) { protected function foreignIDFilter($id = null) {
if ($id === null) $id = $this->getForeignID(); if ($id === null) $id = $this->getForeignID();
@ -51,7 +58,7 @@ class HasManyList extends RelationList {
* *
* It does so by setting the relationFilters. * It does so by setting the relationFilters.
* *
* @param $item The DataObject to be added, or its ID * @param DataObject|int $item The DataObject to be added, or its ID
*/ */
public function add($item) { public function add($item) {
if(is_numeric($item)) { if(is_numeric($item)) {
@ -83,7 +90,7 @@ class HasManyList extends RelationList {
* *
* Doesn't actually remove the item, it just clears the foreign key value. * Doesn't actually remove the item, it just clears the foreign key value.
* *
* @param $itemID The ID of the item to be removed. * @param int $itemID The ID of the item to be removed.
*/ */
public function removeByID($itemID) { public function removeByID($itemID) {
$item = $this->byID($itemID); $item = $this->byID($itemID);
@ -95,7 +102,7 @@ class HasManyList extends RelationList {
* Remove an item from this relation. * Remove an item from this relation.
* Doesn't actually remove the item, it just clears the foreign key value. * Doesn't actually remove the item, it just clears the foreign key value.
* *
* @param $item The DataObject to be removed * @param DataObject $item The DataObject to be removed
* @todo Maybe we should delete the object instead? * @todo Maybe we should delete the object instead?
*/ */
public function remove($item) { public function remove($item) {

View File

@ -47,7 +47,7 @@ class ManyManyList extends RelationList {
* @param string $joinTable The name of the table whose entries define the content of this many_many relation. * @param string $joinTable The name of the table whose entries define the content of this many_many relation.
* @param string $localKey The key in the join table that maps to the dataClass' PK. * @param string $localKey The key in the join table that maps to the dataClass' PK.
* @param string $foreignKey The key in the join table that maps to joined class' PK. * @param string $foreignKey The key in the join table that maps to joined class' PK.
* @param string $extraFields A map of field => fieldtype of extra fields on the join table. * @param array $extraFields A map of field => fieldtype of extra fields on the join table.
* *
* @example new ManyManyList('Group','Group_Members', 'GroupID', 'MemberID'); * @example new ManyManyList('Group','Group_Members', 'GroupID', 'MemberID');
*/ */
@ -151,9 +151,9 @@ class ManyManyList extends RelationList {
* Return a filter expression for when getting the contents of the * Return a filter expression for when getting the contents of the
* relationship for some foreign ID * relationship for some foreign ID
* *
* @param int $id * @param int|null $id
* *
* @return string * @return array
*/ */
protected function foreignIDFilter($id = null) { protected function foreignIDFilter($id = null) {
if ($id === null) { if ($id === null) {
@ -176,7 +176,7 @@ class ManyManyList extends RelationList {
* entries. However some subclasses of ManyManyList (Member_GroupSet) modify foreignIDFilter to * entries. However some subclasses of ManyManyList (Member_GroupSet) modify foreignIDFilter to
* include additional calculated entries, so we need different filters when reading and when writing * include additional calculated entries, so we need different filters when reading and when writing
* *
* @param array|integer $id (optional) An ID or an array of IDs - if not provided, will use the current ids * @param array|int|null $id (optional) An ID or an array of IDs - if not provided, will use the current ids
* as per getForeignID * as per getForeignID
* @return array Condition In array(SQL => parameters format) * @return array Condition In array(SQL => parameters format)
*/ */
@ -188,7 +188,10 @@ class ManyManyList extends RelationList {
* Add an item to this many_many relationship * Add an item to this many_many relationship
* Does so by adding an entry to the joinTable. * Does so by adding an entry to the joinTable.
* *
* @param mixed $item * @throws InvalidArgumentException
* @throws Exception
*
* @param DataObject|int $item
* @param array $extraFields A map of additional columns to insert into the joinTable. * @param array $extraFields A map of additional columns to insert into the joinTable.
* Column names should be ANSI quoted. * Column names should be ANSI quoted.
*/ */
@ -349,7 +352,7 @@ class ManyManyList extends RelationList {
*/ */
public function getExtraData($componentName, $itemID) { public function getExtraData($componentName, $itemID) {
$result = array(); $result = array();
// Skip if no extrafields or unsaved record // Skip if no extrafields or unsaved record
if(empty($this->extraFields) || empty($itemID)) { if(empty($this->extraFields) || empty($itemID)) {
return $result; return $result;

View File

@ -10,6 +10,9 @@
*/ */
abstract class RelationList extends DataList { abstract class RelationList extends DataList {
/**
* @return string|null
*/
public function getForeignID() { public function getForeignID() {
return $this->dataQuery->getQueryParam('Foreign.ID'); return $this->dataQuery->getQueryParam('Foreign.ID');
} }
@ -19,6 +22,8 @@ abstract class RelationList extends DataList {
* the given foreign ID. * the given foreign ID.
* *
* @param int|array $id An ID or an array of IDs. * @param int|array $id An ID or an array of IDs.
*
* @return DataList
*/ */
public function forForeignID($id) { public function forForeignID($id) {
// Turn a 1-element array into a simple value // Turn a 1-element array into a simple value
@ -27,7 +32,8 @@ abstract class RelationList extends DataList {
// Calculate the new filter // Calculate the new filter
$filter = $this->foreignIDFilter($id); $filter = $this->foreignIDFilter($id);
$list = $this->alterDataQuery(function($query, $list) use ($id, $filter){ $list = $this->alterDataQuery(function($query) use ($id, $filter){
/** @var DataQuery $query */
// Check if there is an existing filter, remove if there is // Check if there is an existing filter, remove if there is
$currentFilter = $query->getQueryParam('Foreign.Filter'); $currentFilter = $query->getQueryParam('Foreign.Filter');
if($currentFilter) { if($currentFilter) {

View File

@ -1413,7 +1413,12 @@ class Member extends DataObject implements TemplateGlobalProvider {
)); ));
$mainFields->removeByName($self->config()->hidden_fields); $mainFields->removeByName($self->config()->hidden_fields);
// make sure that the "LastVisited" field exists
// it may have been removed using $self->config()->hidden_fields
if($mainFields->fieldByName("LastVisited")){
$mainFields->makeFieldReadonly('LastVisited'); $mainFields->makeFieldReadonly('LastVisited');
}
if( ! $self->config()->lock_out_after_incorrect_logins) { if( ! $self->config()->lock_out_after_incorrect_logins) {
$mainFields->removeByName('FailedLoginCount'); $mainFields->removeByName('FailedLoginCount');

View File

@ -247,63 +247,135 @@ class ArrayListTest extends SapphireTest {
$list = new ArrayList(array( $list = new ArrayList(array(
array('Name' => 'Steve'), array('Name' => 'Steve'),
(object) array('Name' => 'Bob'), (object) array('Name' => 'Bob'),
array('Name' => 'John') array('Name' => 'John'),
array('Name' => 'bonny'),
)); ));
// Unquoted name // Unquoted name
$list1 = $list->sort('Name'); $list1 = $list->sort('Name');
$this->assertEquals($list1->toArray(), array( $this->assertEquals(array(
(object) array('Name' => 'Bob'), (object) array('Name' => 'Bob'),
array('Name' => 'bonny'),
array('Name' => 'John'), array('Name' => 'John'),
array('Name' => 'Steve') array('Name' => 'Steve'),
)); ), $list1->toArray());
// Quoted name name // Quoted name name
$list2 = $list->sort('"Name"'); $list2 = $list->sort('"Name"');
$this->assertEquals($list2->toArray(), array( $this->assertEquals(array(
(object) array('Name' => 'Bob'), (object) array('Name' => 'Bob'),
array('Name' => 'bonny'),
array('Name' => 'John'), array('Name' => 'John'),
array('Name' => 'Steve') array('Name' => 'Steve'),
)); ), $list2->toArray());
// Array (non-associative) // Array (non-associative)
$list3 = $list->sort(array('"Name"')); $list3 = $list->sort(array('"Name"'));
$this->assertEquals($list3->toArray(), array( $this->assertEquals(array(
(object) array('Name' => 'Bob'), (object) array('Name' => 'Bob'),
array('Name' => 'bonny'),
array('Name' => 'John'), array('Name' => 'John'),
array('Name' => 'Steve') array('Name' => 'Steve'),
)); ), $list3->toArray());
// Quoted name name with table // Quoted name name with table
$list4 = $list->sort('"Record"."Name"'); $list4 = $list->sort('"Record"."Name"');
$this->assertEquals($list4->toArray(), array( $this->assertEquals(array(
(object) array('Name' => 'Bob'), (object) array('Name' => 'Bob'),
array('Name' => 'bonny'),
array('Name' => 'John'), array('Name' => 'John'),
array('Name' => 'Steve') array('Name' => 'Steve')
)); ), $list4->toArray());
// Quoted name name with table (desc) // Quoted name name with table (desc)
$list5 = $list->sort('"Record"."Name" DESC'); $list5 = $list->sort('"Record"."Name" DESC');
$this->assertEquals($list5->toArray(), array( $this->assertEquals(array(
array('Name' => 'Steve'), array('Name' => 'Steve'),
array('Name' => 'John'), array('Name' => 'John'),
array('Name' => 'bonny'),
(object) array('Name' => 'Bob') (object) array('Name' => 'Bob')
)); ), $list5->toArray());
// Table without quotes // Table without quotes
$list6 = $list->sort('Record.Name'); $list6 = $list->sort('Record.Name');
$this->assertEquals($list6->toArray(), array( $this->assertEquals(array(
(object) array('Name' => 'Bob'), (object) array('Name' => 'Bob'),
array('Name' => 'bonny'),
array('Name' => 'John'), array('Name' => 'John'),
array('Name' => 'Steve') array('Name' => 'Steve')
)); ), $list6->toArray());
// Check original list isn't altered // Check original list isn't altered
$this->assertEquals($list->toArray(), array( $this->assertEquals(array(
array('Name' => 'Steve'), array('Name' => 'Steve'),
(object) array('Name' => 'Bob'), (object) array('Name' => 'Bob'),
array('Name' => 'John') array('Name' => 'John'),
array('Name' => 'bonny'),
), $list->toArray());
}
public function testNaturalSort() {
//natural sort is only available in 5.4+
if (version_compare(phpversion(), '5.4.0', '<')) {
$this->markTestSkipped();
}
$list = new ArrayList(array(
array('Name' => 'Steve'),
(object) array('Name' => 'Bob'),
array('Name' => 'John'),
array('Name' => 'bonny'),
array('Name' => 'bonny1'),
array('Name' => 'bonny10'),
array('Name' => 'bonny2'),
)); ));
// Unquoted name
$list1 = $list->sort('Name');
$this->assertEquals(array(
(object) array('Name' => 'Bob'),
array('Name' => 'bonny'),
array('Name' => 'bonny1'),
array('Name' => 'bonny2'),
array('Name' => 'bonny10'),
array('Name' => 'John'),
array('Name' => 'Steve'),
), $list1->toArray());
// Quoted name name
$list2 = $list->sort('"Name"');
$this->assertEquals(array(
(object) array('Name' => 'Bob'),
array('Name' => 'bonny'),
array('Name' => 'bonny1'),
array('Name' => 'bonny2'),
array('Name' => 'bonny10'),
array('Name' => 'John'),
array('Name' => 'Steve'),
), $list2->toArray());
// Array (non-associative)
$list3 = $list->sort(array('"Name"'));
$this->assertEquals(array(
(object) array('Name' => 'Bob'),
array('Name' => 'bonny'),
array('Name' => 'bonny1'),
array('Name' => 'bonny2'),
array('Name' => 'bonny10'),
array('Name' => 'John'),
array('Name' => 'Steve'),
), $list3->toArray());
// Check original list isn't altered
$this->assertEquals(array(
array('Name' => 'Steve'),
(object) array('Name' => 'Bob'),
array('Name' => 'John'),
array('Name' => 'bonny'),
array('Name' => 'bonny1'),
array('Name' => 'bonny10'),
array('Name' => 'bonny2'),
), $list->toArray());
} }
public function testSortSimpleASCOrder() { public function testSortSimpleASCOrder() {