Merge remote-tracking branch 'upstream/master' into deprecation_fixes

This commit is contained in:
Sean Harvey 2012-03-09 15:03:43 +13:00
commit 0d8151fd3d
34 changed files with 214 additions and 281 deletions

View File

@ -710,7 +710,7 @@ class ModelAdmin_CollectionController extends Controller {
false,
$datalist,
$fieldConfig = GridFieldConfig_RecordEditor::create($numItemsPerPage)
->addComponent(new GridFieldExporter())->removeComponentsByType('GridFieldFilter')
->addComponent(new GridFieldExportButton())->removeComponentsByType('GridFieldFilterHeader')
)->setDisplayFields($this->getResultColumns($searchCriteria));
return $tf;

View File

@ -48,9 +48,9 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
false,
DataList::create('Member'),
$memberListConfig = GridFieldConfig_RecordEditor::create()
->addComponent(new GridFieldExporter())
->addComponent(new GridFieldExportButton())
)->addExtraClass("members_grid");
$memberListConfig->getComponentByType('GridFieldPopupForms')->setValidator(new Member_Validator());
$memberListConfig->getComponentByType('GridFieldDetailForm')->setValidator(new Member_Validator());
$groupList = Object::create('GridField',
'Groups',

View File

@ -9,7 +9,7 @@ class SecurityAdminTest extends FunctionalTest {
protected $extraDataObjects = array('LeftAndMainTest_Object');
// TODO Fix export feature (moved from MemberTableField to GridFieldExporter)
// TODO Fix export feature (moved from MemberTableField to GridFieldExportButton)
// function testGroupExport() {
// $this->session()->inst_set('loggedInAs', $this->idFromFixture('Member', 'admin'));
@ -27,7 +27,7 @@ class SecurityAdminTest extends FunctionalTest {
// $this->assertRegExp('/"","","admin@example.com"/', $lines[1], "Member values are correctly exported");
// }
// TODO Fix export feature (moved from MemberTableField to GridFieldExporter)
// TODO Fix export feature (moved from MemberTableField to GridFieldExportButton)
// function testEmptyGroupExport() {
// $this->session()->inst_set('loggedInAs', $this->idFromFixture('Member', 'admin'));

View File

@ -802,19 +802,31 @@ class Director implements TemplateGlobalProvider {
/**
* This function will return true if the site is in a development environment.
* For information about environment types, see {@link Director::set_environment_type()}.
* @param $dontTouchDB If true, the database checks are not performed, which allows certain DB checks
* to not fail before the DB is ready. If false (default), DB checks are included.
*/
static function isDev() {
static function isDev($dontTouchDB = false) {
// This variable is used to supress repetitions of the isDev security message below.
static $firstTimeCheckingGetVar = true;
$result = false;
if(isset($_SESSION['isDev']) && $_SESSION['isDev']) $result = true;
if(self::$environment_type && self::$environment_type == 'dev') $result = true;
if(!empty(Director::$dev_servers)) {
Deprecation::notice('3.0', 'Director::$dev_servers doesn\'t work anymore');
}
// Use ?isDev=1 to get development access on the live server
if(isset($_GET['isDev'])) {
if(!$dontTouchDB && !$result && isset($_GET['isDev'])) {
if(Security::database_is_ready()) {
if($firstTimeCheckingGetVar && !Permission::check('ADMIN')){
BasicAuth::requireLogin("SilverStripe developer access. Use your CMS login", "ADMIN");
}
$_SESSION['isDev'] = $_GET['isDev'];
if($firstTimeCheckingGetVar) $firstTimeCheckingGetVar = false;
$firstTimeCheckingGetVar = false;
$result = $_GET['isDev'];
} else {
if($firstTimeCheckingGetVar && DB::connection_attempted()) {
echo "<p style=\"padding: 3px; margin: 3px; background-color: orange;
@ -826,16 +838,7 @@ class Director implements TemplateGlobalProvider {
}
}
if(isset($_SESSION['isDev']) && $_SESSION['isDev']) return true;
if(self::$environment_type) return self::$environment_type == 'dev';
// Check if we are running on one of the development servers
if(isset($_SERVER['HTTP_HOST']) && in_array($_SERVER['HTTP_HOST'], Director::$dev_servers)) {
return true;
}
return false;
return $result;
}
/**

View File

@ -30,9 +30,9 @@ This example creates exactly the same kind of grid as the previous example, but
:::php
$config = GridFieldConfig::create();
// Provide a header row with filter controls
$config->addComponent(new GridFieldFilter());
$config->addComponent(new GridFieldFilterHeader());
// Provide a default set of columns based on $summary_fields
$config->addComponent(new GridFieldDefaultColumns());
$config->addComponent(new GridFieldDataColumns());
// Provide a header row with sort controls
$config->addComponent(new GridFieldSortableHeader());
// Paginate results to 25 items per page, and show a footer with pagination controls
@ -44,7 +44,7 @@ If we wanted to make a simpler grid without pagination or filtering, we could do
:::php
$config = GridFieldConfig::create();
// Provide a default set of columns based on $summary_fields
$config->addComponent(new GridFieldDefaultColumns());
$config->addComponent(new GridFieldDataColumns());
// Provide a header row with sort controls
$config->addComponent(new GridFieldPaginator(25));
$field = new GridField("Members", "Members of this group", $this->group->Members(), $config);
@ -57,11 +57,11 @@ A `GridFieldConfig` is made up of a new of `GridFieldComponent` objects, which a
`GridFieldComponent` is a family of interfaces.
SilverStripe Framework comes with the following components that you can use out of the box.
### GridFieldDefaultColumns
### GridFieldDataColumns
This is the one component that, in most cases, you must include. It provides the default columns, sourcing them from the underlying DataObject's `$summary_fields` if no specific configuration is provided.
Without GridFieldDefaultColumns added to a GridField, it would have no columns whatsoever. Although this isn't particularly useful most of the time, we have allowed for this for two reasons:
Without GridFieldDataColumns added to a GridField, it would have no columns whatsoever. Although this isn't particularly useful most of the time, we have allowed for this for two reasons:
* You may have a grid whose fields are generated purely by another non-standard component.
* It keeps the core of the GridField lean, focused solely on providing APIs to the components.
@ -99,7 +99,7 @@ You can also specify formatting replacements, to replace column contents with HT
This component will add a header to the grid with sort buttons. It will detect which columns are sortable and only provide sort controls on those columns.
### GridFieldFilter
### GridFieldFilterHeader
This component will add a header row with a text field filter for each column, letting you filter the results with text searches. It will detect which columns are filterable and only provide sort controls on those columns.
@ -109,34 +109,34 @@ This component will limit output to a fixed number of items per page add a foote
### GridFieldAction
TODO Describe component, including GridFieldEditAction/GridFieldDeleteAction
TODO Describe component, including GridFieldEditButton/GridFieldDeleteAction
### GridFieldRelationAdd
### GridFieldAddExistingAutocompleter
This class is is responsible for adding objects to another object's has_many and many_many relation,
as defined by the `[api:RelationList]` passed to the GridField constructor.
Objects can be searched through an input field (partially matching one or more fields).
Selecting from the results will add the object to the relation.
Often used alongside `[api:GridFieldRelationDelete]` for detaching existing records from a relatinship.
Often used alongside `[api:GridFieldRemoveButton]` for detaching existing records from a relatinship.
For easier setup, have a look at a sample configuration in `[api:GridFieldConfig_RelationEditor]`.
### GridFieldRelationDelete
### GridFieldRemoveButton
Allows to detach an item from an existing has_many or many_many relationship.
Similar to {@link GridFieldDeleteAction}, but allows to distinguish between
a "delete" and "detach" action in the UI - and to use both in parallel, if required.
Requires the GridField to be populated with a `[api:RelationList]` rather than a plain DataList.
Often used alongside `[api:GridFieldRelationAdd]` to add existing records to the relationship.
Often used alongside `[api:GridFieldAddExistingAutocompleter]` to add existing records to the relationship.
### GridFieldPopupForms
### GridFieldDetailForm
TODO Describe component, including how it relates to GridFieldEditAction. Point to GridFieldConfig_RelationEditor for easier defaults.
TODO Describe component, including how it relates to GridFieldEditButton. Point to GridFieldConfig_RelationEditor for easier defaults.
### GridFieldTitle
### GridFieldToolbarHeader
TODO
### GridFieldExporter
### GridFieldExportButton
TODO
@ -152,16 +152,16 @@ It's common for a component to implement several of these interfaces in order to
* `GridField_ActionProvider`, to define the sortasc and sortdesc actions that add sort column and direction to the state.
* `GridField_DataManipulator`, to alter the sorting of the data list based on the sort column and direction values in the state.
### GridFieldRelationAdd
### GridFieldAddExistingAutocompleter
A GridFieldRelationAdd is responsible for adding objects to another object's `has_many` and `many_many` relation,
A GridFieldAddExistingAutocompleter is responsible for adding objects to another object's `has_many` and `many_many` relation,
as defined by the `[api:RelationList]` passed to the GridField constructor.
Objects can be searched through an input field (partially matching one or more fields).
Selecting from the results will add the object to the relation.
:::php
$group = DataObject::get_one('Group');
$config = GridFieldConfig::create()->addComponent(new GridFieldRelationAdd(array('FirstName', 'Surname', 'Email'));
$config = GridFieldConfig::create()->addComponent(new GridFieldAddExistingAutocompleter(array('FirstName', 'Surname', 'Email'));
$gridField = new GridField('Members', 'Members', $group->Members(), $config);
## Component interfaces
@ -233,7 +233,7 @@ By default, a grid contains no columns. All the columns displayed in a grid wil
For example, you may create a grid field with several components providing columns:
* `GridFieldDefaultColumns` could provide basic data columns.
* `GridFieldDataColumns` could provide basic data columns.
* An editor component could provide a column containing action buttons on the right.
* A multiselect component clould provide a column showing a checkbox on the left.
@ -293,7 +293,7 @@ Here is an example in full. The actual implementation of the view and edit form
* - <FormURL>/field/<GridFieldName>/item/<RecordID>
* - <FormURL>/field/<GridFieldName>/item/<RecordID>/edit
*/
class GridFieldPopupForms implements GridField_URLHandler {
class GridFieldDetailForm implements GridField_URLHandler {
public function getURLHandlers($gridField) {
return array(
'item/$ID' => 'handleItem',

View File

@ -355,8 +355,8 @@ class HtmlEditorField_Toolbar extends RequestHandler {
$fileFieldConfig = GridFieldConfig::create();
$fileFieldConfig->addComponent(new GridFieldSortableHeader());
$fileFieldConfig->addComponent(new GridFieldFilter());
$fileFieldConfig->addComponent(new GridFieldDefaultColumns());
$fileFieldConfig->addComponent(new GridFieldFilterHeader());
$fileFieldConfig->addComponent(new GridFieldDataColumns());
$fileFieldConfig->addComponent(new GridFieldPaginator(5));
$fileField = new GridField('Files', false, null, $fileFieldConfig);
$fileField->setList($this->getFiles($parentID));

View File

@ -846,8 +846,8 @@ class UploadField_SelectHandler extends RequestHandler {
$folder = $this->getFolder();
$config = GridFieldConfig::create();
$config->addComponent(new GridFieldSortableHeader());
$config->addComponent(new GridFieldFilter());
$config->addComponent(new GridFieldDefaultColumns());
$config->addComponent(new GridFieldFilterHeader());
$config->addComponent(new GridFieldDataColumns());
$config->addComponent(new GridFieldPaginator(10));
$field = new GridField('Files', false, $folder->stageChildren(), $config);

View File

@ -404,7 +404,7 @@ class GridField extends FormField {
array(
"class" => implode(' ', $classes),
'data-id' => $record->ID,
// TODO Allow per-row customization similar to GridFieldDefaultColumns
// TODO Allow per-row customization similar to GridFieldDataColumns
'data-class' => $record->ClassName,
),
$rowContent

View File

@ -4,17 +4,17 @@
* as defined by the {@link RelationList} passed to the GridField constructor.
* Objects can be searched through an input field (partially matching one or more fields).
* Selecting from the results will add the object to the relation.
* Often used alongside {@link GridFieldRelationDelete} for detaching existing records from a relatinship.
* Often used alongside {@link GridFieldRemoveButton} for detaching existing records from a relatinship.
* For easier setup, have a look at a sample configuration in {@link GridFieldConfig_RelationEditor}.
*/
class GridFieldRelationAdd implements GridField_HTMLProvider, GridField_ActionProvider, GridField_DataManipulator, GridField_URLHandler {
class GridFieldAddExistingAutocompleter implements GridField_HTMLProvider, GridField_ActionProvider, GridField_DataManipulator, GridField_URLHandler {
/**
* Which template to use for rendering
*
* @var string $itemClass
*/
protected $itemClass = 'GridFieldRelationAdd';
protected $itemClass = 'GridFieldAddExistingAutocompleter';
/**
* Which columns that should be used for doing a "StartsWith" search.
@ -161,7 +161,7 @@ class GridFieldRelationAdd implements GridField_HTMLProvider, GridField_ActionPr
$searchFields = ($this->getSearchFields()) ? $this->getSearchFields() : $this->scaffoldSearchFields($dataClass);
if(!$searchFields) {
throw new LogicException(
sprintf('GridFieldRelationAdd: No searchable fields could be found for class "%s"', $dataClass)
sprintf('GridFieldAddExistingAutocompleter: No searchable fields could be found for class "%s"', $dataClass)
);
}

View File

@ -36,7 +36,7 @@ interface GridField_ColumnProvider extends GridFieldComponent {
/**
* Modify the list of columns displayed in the table.
* See {@link GridField->getDisplayFields()} and {@link GridFieldDefaultColumns}.
* See {@link GridField->getDisplayFields()} and {@link GridFieldDataColumns}.
*
* @param GridField
* @param Array List reference of all column names.

View File

@ -131,10 +131,10 @@ class GridFieldConfig_Base extends GridFieldConfig {
* @param int $itemsPerPage - How many items per page should show up
*/
public function __construct($itemsPerPage=null) {
$this->addComponent(new GridFieldTitle());
$this->addComponent(new GridFieldToolbarHeader());
$this->addComponent(new GridFieldSortableHeader());
$this->addComponent(new GridFieldFilter());
$this->addComponent(new GridFieldDefaultColumns());
$this->addComponent(new GridFieldFilterHeader());
$this->addComponent(new GridFieldDataColumns());
$this->addComponent(new GridFieldPaginator($itemsPerPage));
}
}
@ -158,14 +158,14 @@ class GridFieldConfig_RecordEditor extends GridFieldConfig {
* @param int $itemsPerPage - How many items per page should show up
*/
public function __construct($itemsPerPage=null) {
$this->addComponent(new GridFieldTitle());
$this->addComponent(new GridFieldToolbarHeader());
$this->addComponent(new GridFieldSortableHeader());
$this->addComponent(new GridFieldFilter());
$this->addComponent(new GridFieldDefaultColumns());
$this->addComponent(new GridFieldEditAction());
$this->addComponent(new GridFieldFilterHeader());
$this->addComponent(new GridFieldDataColumns());
$this->addComponent(new GridFieldEditButton());
$this->addComponent(new GridFieldDeleteAction());
$this->addComponent(new GridFieldPaginator($itemsPerPage));
$this->addComponent(new GridFieldPopupForms());
$this->addComponent(new GridFieldDetailForm());
}
}
@ -181,7 +181,7 @@ class GridFieldConfig_RecordEditor extends GridFieldConfig {
* for example to change the field to search.
* <code>
* GridFieldConfig_RelationEditor::create()
* ->getComponentByType('GridFieldRelationAdd')->setSearchFields('MyField');
* ->getComponentByType('GridFieldAddExistingAutocompleter')->setSearchFields('MyField');
* </code>
*/
class GridFieldConfig_RelationEditor extends GridFieldConfig {
@ -200,14 +200,14 @@ class GridFieldConfig_RelationEditor extends GridFieldConfig {
* @param int $itemsPerPage - How many items per page should show up
*/
public function __construct($itemsPerPage=null) {
$this->addComponent(new GridFieldTitle());
$this->addComponent(new GridFieldRelationAdd());
$this->addComponent(new GridFieldToolbarHeader());
$this->addComponent(new GridFieldAddExistingAutocompleter());
$this->addComponent(new GridFieldSortableHeader());
$this->addComponent(new GridFieldFilter());
$this->addComponent(new GridFieldDefaultColumns());
$this->addComponent(new GridFieldEditAction());
$this->addComponent(new GridFieldRelationDelete());
$this->addComponent(new GridFieldFilterHeader());
$this->addComponent(new GridFieldDataColumns());
$this->addComponent(new GridFieldEditButton());
$this->addComponent(new GridFieldDeleteAction(true));
$this->addComponent(new GridFieldPaginator($itemsPerPage));
$this->addComponent(new GridFieldPopupForms());
$this->addComponent(new GridFieldDetailForm());
}
}

View File

@ -6,7 +6,7 @@
* @package sapphire
* @subpackage fields-relational
*/
class GridFieldDefaultColumns implements GridField_ColumnProvider {
class GridFieldDataColumns implements GridField_ColumnProvider {
public function augmentColumns($gridField, &$columns) {
$baseColumns = array_keys($gridField->getDisplayFields());

View File

@ -1,10 +1,28 @@
<?php
/**
* This class is an GridField Component that add Delete action for Objects in the GridField.
* See {@link GridFieldRelationDelete} for detaching an item from the current relationship instead.
* See {@link GridFieldRemoveButton} for detaching an item from the current relationship instead.
*/
class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_ActionProvider {
/**
* If this is set to true, this actionprovider will remove the object from the list, instead of
* deleting. In the case of a has one, has many or many many list it will uncouple the item from
* the list.
*
* @var boolean
*/
protected $removeRelation = false;
/**
*
* @param boolean $unlinkRelation - true if removing the item from the list, but not deleting it
*/
public function __construct($unlinkRelation = false) {
$this->removeRelation = $unlinkRelation;
}
/**
* Add a column 'Delete'
*
@ -58,7 +76,7 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio
* @return array
*/
public function getActions($gridField) {
return array('deleterecord');
return array('deleterecord', 'unlinkrelation');
}
/**
@ -69,19 +87,20 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio
* @return string - the HTML for the column
*/
public function getColumnContent($gridField, $record, $columnName) {
if(!$record->canDelete()) {
return;
if($this->removeRelation) {
$field = Object::create('GridField_FormAction', $gridField, 'UnlinkRelation'.$record->ID, false, "unlinkrelation", array('RecordID' => $record->ID))
->addExtraClass('gridfield-button-unlink')
->setAttribute('title', _t('GridAction.UnlinkRelation', "Unlink"))
->setAttribute('data-icon', 'chain--minus');
} else {
if(!$record->canDelete()) {
return;
}
$field = Object::create('GridField_FormAction', $gridField, 'DeleteRecord'.$record->ID, false, "deleterecord", array('RecordID' => $record->ID))
->addExtraClass('gridfield-button-delete')
->setAttribute('title', _t('GridAction.Delete', "Delete"))
->setAttribute('data-icon', 'decline');
}
$field = Object::create('GridField_FormAction',
$gridField,
'DeleteRecord'.$record->ID,
false,
"deleterecord",
array('RecordID' => $record->ID)
)
->addExtraClass('gridfield-button-delete')
->setAttribute('title', _t('GridAction.Delete', "delete"))
->setAttribute('data-icon', 'decline');
return $field->Field();
}
@ -95,15 +114,15 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio
* @return void
*/
public function handleAction(GridField $gridField, $actionName, $arguments, $data) {
if($actionName == 'deleterecord') {
$id = $arguments['RecordID'];
// Always deletes a record. Use GridFieldRelationDelete to detach it from the current relationship.
$item = $gridField->getList()->byID($id);
if(!$item->canDelete()) {
if($actionName == 'deleterecord' || $actionName == 'unlinkrelation') {
$item = $gridField->getList()->byID($arguments['RecordID']);
if(!$item) {
return;
}
if($actionName == 'deleterecord' && !$item->canDelete()) {
throw new ValidationException(_t('GridFieldAction_Delete.DeletePermissionsFailure',"No delete permissions"),0);
}
if(!$item) return;
$item->delete();
}
$gridField->getList()->remove($item);
}
}
}

View File

@ -3,21 +3,21 @@
/**
* Provides view and edit forms at GridField-specific URLs.
* These can be placed into pop-ups by an appropriate front-end.
* Usually added to a grid field alongside of {@link GridFieldEditAction}
* Usually added to a grid field alongside of {@link GridFieldEditButton}
* which takes care of linking the individual rows to their edit view.
*
* The URLs provided will be off the following form:
* - <FormURL>/field/<GridFieldName>/item/<RecordID>
* - <FormURL>/field/<GridFieldName>/item/<RecordID>/edit
*/
class GridFieldPopupForms implements GridField_URLHandler {
class GridFieldDetailForm implements GridField_URLHandler {
/**
* @var String
*/
protected $template = 'GridFieldPopupForms';
protected $template = 'GridFieldDetailForm';
/**
*

View File

@ -3,10 +3,10 @@
* Provides the entry point to editing a single record presented by the grid.
* Doesn't show an edit view on its own or modifies the record, but rather relies on routing conventions
* established in {@link getColumnContent()}. The default routing applies to
* the {@link GridFieldPopupForms} component, which has to be added separately
* the {@link GridFieldDetailForm} component, which has to be added separately
* to the grid field configuration.
*/
class GridFieldEditAction implements GridField_ColumnProvider {
class GridFieldEditButton implements GridField_ColumnProvider {
/**
* Add a column 'Delete'
@ -79,7 +79,7 @@ class GridFieldEditAction implements GridField_ColumnProvider {
'Link' => Controller::join_links($gridField->Link('item'), $record->ID, 'edit')
));
return $data->renderWith('GridFieldEditAction');
return $data->renderWith('GridFieldEditButton');
}
/**

View File

@ -10,7 +10,7 @@
* WARNING: This is experimental and its API is subject to change. Feel free to use it as long as you are happy of
* refactoring your code in the future.
*/
class GridFieldExporter implements GridField_HTMLProvider, GridField_ActionProvider, GridField_URLHandler {
class GridFieldExportButton implements GridField_HTMLProvider, GridField_ActionProvider, GridField_URLHandler {
/**
* @var array Map of a property name on the exported objects, with values being the column title in the CSV file.

View File

@ -1,13 +1,13 @@
<?php
/**
* GridFieldFilter alters the gridfield with some filtering fields in the header of each column
* GridFieldFilterHeader alters the gridfield with some filtering fields in the header of each column
*
* @see GridField
*
* @package sapphire
* @subpackage fields-relational
*/
class GridFieldFilter implements GridField_HTMLProvider, GridField_DataManipulator, GridField_ActionProvider {
class GridFieldFilterHeader implements GridField_HTMLProvider, GridField_DataManipulator, GridField_ActionProvider {
/**
*
@ -19,7 +19,7 @@ class GridFieldFilter implements GridField_HTMLProvider, GridField_DataManipulat
}
function handleAction(GridField $gridField, $actionName, $arguments, $data) {
$state = $gridField->State->GridFieldFilter;
$state = $gridField->State->GridFieldFilterHeader;
if($actionName === 'filter') {
if(isset($data['filter'])){
foreach($data['filter'] as $key => $filter ){
@ -39,7 +39,7 @@ class GridFieldFilter implements GridField_HTMLProvider, GridField_DataManipulat
* @return SS_List
*/
public function getManipulatedData(GridField $gridField, SS_List $dataList) {
$state = $gridField->State->GridFieldFilter;
$state = $gridField->State->GridFieldFilterHeader;
if(!isset($state->Columns)) {
return $dataList;
}
@ -58,7 +58,7 @@ class GridFieldFilter implements GridField_HTMLProvider, GridField_DataManipulat
$forTemplate->Fields = new ArrayList;
$columns = $gridField->getColumns();
$filterArguments = $gridField->State->GridFieldFilter->Columns->toArray();
$filterArguments = $gridField->State->GridFieldFilterHeader->Columns->toArray();
$currentColumn = 0;
foreach($columns as $columnField) {
@ -92,7 +92,7 @@ class GridFieldFilter implements GridField_HTMLProvider, GridField_DataManipulat
}
return array(
'header' => $forTemplate->renderWith('GridFieldFilter_Row'),
'header' => $forTemplate->renderWith('GridFieldFilterHeader_Row'),
);
}
}

View File

@ -1,106 +0,0 @@
<?php
/**
* Allows to detach an item from an existing has_many or many_many relationship.
* Similar to {@link GridFieldDeleteAction}, but allows to distinguish between
* a "delete" and "detach" action in the UI - and to use both in parallel, if required.
* Requires the GridField to be populated with a {@link RelationList} rather than a plain {@link DataList}.
* Often used alongside {@link GridFieldRelationAdd} to add existing records to the relationship.
* For easier setup, have a look at a sample configuration in {@link GridFieldConfig_RelationEditor}.
*/
class GridFieldRelationDelete implements GridField_ColumnProvider, GridField_ActionProvider {
/**
* Add a column 'UnlinkRelation'
*
* @param type $gridField
* @param array $columns
*/
public function augmentColumns($gridField, &$columns) {
if(!in_array('Actions', $columns))
$columns[] = 'Actions';
}
/**
* Return any special attributes that will be used for FormField::createTag()
*
* @param GridField $gridField
* @param DataObject $record
* @param string $columnName
* @return array
*/
public function getColumnAttributes($gridField, $record, $columnName) {
return array();
}
/**
* Don't add an title
*
* @param GridField $gridField
* @param string $columnName
* @return array
*/
public function getColumnMetadata($gridField, $columnName) {
if($columnName == 'Actions') {
return array('title' => '');
}
}
/**
* Which columns are handled by this component
*
* @param type $gridField
* @return type
*/
public function getColumnsHandled($gridField) {
return array('Actions');
}
/**
* Which GridField actions are this component handling
*
* @param GridField $gridField
* @return array
*/
public function getActions($gridField) {
return array('unlinkrelation');
}
/**
*
* @param GridField $gridField
* @param DataObject $record
* @param string $columnName
* @return string - the HTML for the column
*/
public function getColumnContent($gridField, $record, $columnName) {
$field = Object::create('GridField_FormAction',
$gridField,
'UnlinkRelation'.$record->ID,
false,
"unlinkrelation",
array('RecordID' => $record->ID)
)
->setAttribute('title', _t('GridAction.UnlinkRelation', "Unlink"))
->setAttribute('data-icon', 'chain--minus')
->addExtraClass('gridfield-button-unlink');
return $field->Field();
}
/**
* Handle the actions and apply any changes to the GridField
*
* @param GridField $gridField
* @param string $actionName
* @param mixed $arguments
* @param array $data - form data
* @return void
*/
public function handleAction(GridField $gridField, $actionName, $arguments, $data) {
$id = $arguments['RecordID'];
$item = $gridField->getList()->byID($id);
if(!$item) return;
if($actionName == 'unlinkrelation') {
$gridField->getList()->remove($item);
}
}
}

View File

@ -14,7 +14,7 @@
* @package sapphire
* @subpackage gridfield
*/
class GridFieldTitle implements GridField_HTMLProvider {
class GridFieldToolbarHeader implements GridField_HTMLProvider {
/**
*
@ -34,7 +34,7 @@ class GridFieldTitle implements GridField_HTMLProvider {
'header' => $gridField->customise(array(
'NewLink' => Controller::join_links($gridField->Link('item'), 'new'),
'NewEnabled' => $this->getNewEnabled()
))->renderWith('GridFieldTitle')
))->renderWith('GridFieldToolbarHeader')
);
}

View File

@ -109,13 +109,13 @@ class MySQLDatabase extends SS_Database {
return;
}
if(isset($_REQUEST['showqueries'])) {
if(isset($_REQUEST['showqueries']) && Director::isDev(true)) {
$starttime = microtime(true);
}
$handle = $this->dbConn->query($sql);
if(isset($_REQUEST['showqueries'])) {
if(isset($_REQUEST['showqueries']) && Director::isDev(true)) {
$endtime = round(microtime(true) - $starttime,4);
Debug::message("\n$sql\n{$endtime}ms\n", false);
}

View File

@ -88,15 +88,15 @@ class Group extends DataObject {
$parentidfield->setRightTitle('<span class="aligned_right_label">' . _t('Group.GroupReminder', 'If you choose a parent group, this group will take all it\'s roles') . '</span>');
// Filter permissions
// TODO SecurityAdmin coupling, not easy to get to the form fields through GridFieldPopupForms
// TODO SecurityAdmin coupling, not easy to get to the form fields through GridFieldDetailForm
$permissionsField->setHiddenPermissions(SecurityAdmin::$hidden_permissions);
if($this->ID) {
$config = new GridFieldConfig_RelationEditor();
$config->addComponents(new GridFieldExporter());
$config->getComponentByType('GridFieldRelationAdd')
$config->addComponents(new GridFieldExportButton());
$config->getComponentByType('GridFieldAddExistingAutocompleter')
->setResultsFormat('$Title ($Email)')->setSearchFields(array('FirstName', 'Surname', 'Email'));
$config->getComponentByType('GridFieldPopupForms')->setValidator(new Member_Validator());
$config->getComponentByType('GridFieldDetailForm')->setValidator(new Member_Validator());
$memberList = Object::create('GridField', 'Members',false, $this->Members(), $config)->addExtraClass('members_grid');
// @todo Implement permission checking on GridField
//$memberList->setPermissions(array('edit', 'delete', 'export', 'add', 'inlineadd'));

View File

@ -29,10 +29,10 @@ class GridFieldTest extends SapphireTest {
$obj = new GridField('testfield', 'testfield');
$expectedComponents = new ArrayList(array(
new GridFieldTitle(),
new GridFieldToolbarHeader(),
new GridFieldSortableHeader,
new GridFieldFilter,
new GridFieldDefaultColumns,
new GridFieldFilterHeader,
new GridFieldDataColumns,
new GridFieldPaginator,
new GridState_Component,
));
@ -48,13 +48,13 @@ class GridFieldTest extends SapphireTest {
$config = GridFieldConfig::create();
$config->addComponent(new GridFieldSortableHeader());
$config->addComponent(new GridFieldDefaultColumns());
$config->addComponent(new GridFieldDataColumns());
$obj = new GridField('testfield', 'testfield', ArrayList::create(array()),$config);
$expectedComponents = new ArrayList(array(
0 => new GridFieldSortableHeader,
1 => new GridFieldDefaultColumns,
1 => new GridFieldDataColumns,
2 => new GridState_Component,
));
@ -430,7 +430,7 @@ class GridFieldTest extends SapphireTest {
));
$config = new GridFieldConfig();
$config->addComponent(new GridFieldDefaultColumns());
$config->addComponent(new GridFieldDataColumns());
$obj = new GridField('testfield', 'testfield', $list, $config);
$form = new Form(new Controller(), 'mockform', new FieldList(array($obj)), new FieldList());
$content = new CSSContentParser($obj->FieldHolder());

View File

@ -1,5 +1,5 @@
<?php
class GridFieldRelationAddTest extends FunctionalTest {
class GridFieldAddExistingAutocompleterTest extends FunctionalTest {
static $fixture_file = 'sapphire/tests/forms/gridfield/GridFieldTest.yml';
@ -9,13 +9,13 @@ class GridFieldRelationAddTest extends FunctionalTest {
$team1 = $this->objFromFixture('GridFieldTest_Team', 'team1');
$team2 = $this->objFromFixture('GridFieldTest_Team', 'team2');
$response = $this->get('GridFieldRelationAddTest_Controller');
$response = $this->get('GridFieldAddExistingAutocompleterTest_Controller');
$this->assertFalse($response->isError());
$parser = new CSSContentParser($response->getBody());
$btns = $parser->getBySelector('.ss-gridfield #action_gridfield_relationfind');
$response = $this->post(
'GridFieldRelationAddTest_Controller/Form/field/testfield/search/Team 2',
'GridFieldAddExistingAutocompleterTest_Controller/Form/field/testfield/search/Team 2',
array(
(string)$btns[0]['name'] => 1
)
@ -26,7 +26,7 @@ class GridFieldRelationAddTest extends FunctionalTest {
$this->assertEquals(array($team2->ID => 'Team 2'), $result);
$response = $this->post(
'GridFieldRelationAddTest_Controller/Form/field/testfield/search/Unknown',
'GridFieldAddExistingAutocompleterTest_Controller/Form/field/testfield/search/Unknown',
array(
(string)$btns[0]['name'] => 1
)
@ -41,7 +41,7 @@ class GridFieldRelationAddTest extends FunctionalTest {
$team1 = $this->objFromFixture('GridFieldTest_Team', 'team1');
$team2 = $this->objFromFixture('GridFieldTest_Team', 'team2');
$response = $this->get('GridFieldRelationAddTest_Controller');
$response = $this->get('GridFieldAddExistingAutocompleterTest_Controller');
$this->assertFalse($response->isError());
$parser = new CSSContentParser($response->getBody());
$items = $parser->getBySelector('.ss-gridfield .ss-gridfield-items .ss-gridfield-item');
@ -50,7 +50,7 @@ class GridFieldRelationAddTest extends FunctionalTest {
$btns = $parser->getBySelector('.ss-gridfield #action_gridfield_relationadd');
$response = $this->post(
'GridFieldRelationAddTest_Controller/Form/field/testfield',
'GridFieldAddExistingAutocompleterTest_Controller/Form/field/testfield',
array(
'relationID' => $team2->ID,
(string)$btns[0]['name'] => 1
@ -67,15 +67,15 @@ class GridFieldRelationAddTest extends FunctionalTest {
}
class GridFieldRelationAddTest_Controller extends Controller implements TestOnly {
class GridFieldAddExistingAutocompleterTest_Controller extends Controller implements TestOnly {
protected $template = 'BlankPage';
function Form() {
$player = DataObject::get('GridFieldTest_Player')->find('Email', 'player1@test.com');
$config = GridFieldConfig::create()->addComponents(
$relationComponent = new GridFieldRelationAdd('Name'),
new GridFieldDefaultColumns()
$relationComponent = new GridFieldAddExistingAutocompleter('Name'),
new GridFieldDataColumns()
);
$field = new GridField('testfield', 'testfield', $player->Teams(), $config);
return new Form($this, 'Form', new FieldList($field), new FieldList());

View File

@ -60,6 +60,23 @@ class GridFieldDeleteActionTest extends SapphireTest {
$this->gridField->gridFieldAlterAction(array('StateID'=>$stateID), $this->form, $request);
$this->assertEquals(2, $this->list->count(), 'User should be able to delete records with ADMIN permission.');
}
public function testDeleteActionRemoveRelation() {
$this->logInWithPermission('ADMIN');
$config = GridFieldConfig::create()->addComponent(new GridFieldDeleteAction(true));
$gridField = new GridField('testfield', 'testfield', $this->list, $config);
$form = new Form(new Controller(), 'mockform', new FieldList(array($this->gridField)), new FieldList());
$stateID = 'testGridStateActionField';
Session::set($stateID, array('grid'=>'', 'actionName'=>'deleterecord','args'=>array('RecordID'=>1)));
$request = new SS_HTTPRequest('POST', 'url', array(), array('action_gridFieldAlterAction?StateID='.$stateID=>true));
$this->gridField->gridFieldAlterAction(array('StateID'=>$stateID), $this->form, $request);
$this->assertEquals(2, $this->list->count(), 'User should be able to delete records with ADMIN permission.');
}
}
class GridFieldAction_Delete_Team extends DataObject implements TestOnly {

View File

@ -1,23 +1,23 @@
<?php
class GridFieldPopupFormsTest extends FunctionalTest {
static $fixture_file = 'GridFieldPopupFormsTest.yml';
class GridFieldDetailFormTest extends FunctionalTest {
static $fixture_file = 'GridFieldDetailFormTest.yml';
protected $extraDataObjects = array(
'GridFieldPopupFormsTest_Person',
'GridFieldPopupFormsTest_PeopleGroup',
'GridFieldPopupFormsTest_Category',
'GridFieldDetailFormTest_Person',
'GridFieldDetailFormTest_PeopleGroup',
'GridFieldDetailFormTest_Category',
);
function testAddForm() {
$this->logInWithPermission('ADMIN');
$group = DataList::create('GridFieldPopupFormsTest_PeopleGroup')
$group = DataList::create('GridFieldDetailFormTest_PeopleGroup')
->filter('Name', 'My Group')
->First();
$count = $group->People()->Count();
$response = $this->get('GridFieldPopupFormsTest_Controller');
$response = $this->get('GridFieldDetailFormTest_Controller');
$this->assertFalse($response->isError());
$parser = new CSSContentParser($response->getBody());
$addlinkitem = $parser->getBySelector('.ss-gridfield .new-link');
@ -40,7 +40,7 @@ class GridFieldPopupFormsTest extends FunctionalTest {
);
$this->assertFalse($response->isError());
$group = DataList::create('GridFieldPopupFormsTest_PeopleGroup')
$group = DataList::create('GridFieldDetailFormTest_PeopleGroup')
->filter('Name', 'My Group')
->First();
$this->assertEquals($count + 1, $group->People()->Count());
@ -48,13 +48,13 @@ class GridFieldPopupFormsTest extends FunctionalTest {
function testEditForm() {
$this->logInWithPermission('ADMIN');
$group = DataList::create('GridFieldPopupFormsTest_PeopleGroup')
$group = DataList::create('GridFieldDetailFormTest_PeopleGroup')
->filter('Name', 'My Group')
->First();
$firstperson = $group->People()->First();
$this->assertTrue($firstperson->Surname != 'Baggins');
$response = $this->get('GridFieldPopupFormsTest_Controller');
$response = $this->get('GridFieldDetailFormTest_Controller');
$this->assertFalse($response->isError());
$parser = new CSSContentParser($response->getBody());
$editlinkitem = $parser->getBySelector('.ss-gridfield-items .first .edit-link');
@ -77,7 +77,7 @@ class GridFieldPopupFormsTest extends FunctionalTest {
);
$this->assertFalse($response->isError());
$group = DataList::create('GridFieldPopupFormsTest_PeopleGroup')
$group = DataList::create('GridFieldDetailFormTest_PeopleGroup')
->filter('Name', 'My Group')
->First();
$firstperson = $group->People()->First();
@ -87,18 +87,18 @@ class GridFieldPopupFormsTest extends FunctionalTest {
function testNestedEditForm() {
$this->logInWithPermission('ADMIN');
$group = $this->objFromFixture('GridFieldPopupFormsTest_PeopleGroup', 'group');
$group = $this->objFromFixture('GridFieldDetailFormTest_PeopleGroup', 'group');
$person = $group->People()->First();
$category = $person->Categories()->First();
// Get first form (GridField managing PeopleGroup)
$response = $this->get('GridFieldPopupFormsTest_GroupController');
$response = $this->get('GridFieldDetailFormTest_GroupController');
$this->assertFalse($response->isError());
$parser = new CSSContentParser($response->getBody());
$groupEditLink = $parser->getByXpath('//tr[contains(@class, "ss-gridfield-item") and contains(@data-id, "' . $group->ID . '")]//a');
$this->assertEquals(
'GridFieldPopupFormsTest_GroupController/Form/field/testfield/item/1/edit',
'GridFieldDetailFormTest_GroupController/Form/field/testfield/item/1/edit',
(string)$groupEditLink[0]['href']
);
@ -108,7 +108,7 @@ class GridFieldPopupFormsTest extends FunctionalTest {
$parser = new CSSContentParser($response->getBody());
$personEditLink = $parser->getByXpath('//fieldset[@id="Form_ItemEditForm_People"]//tr[contains(@class, "ss-gridfield-item") and contains(@data-id, "' . $person->ID . '")]//a');
$this->assertEquals(
'GridFieldPopupFormsTest_GroupController/Form/field/testfield/item/1/ItemEditForm/field/People/item/1/edit',
'GridFieldDetailFormTest_GroupController/Form/field/testfield/item/1/ItemEditForm/field/People/item/1/edit',
(string)$personEditLink[0]['href']
);
@ -120,24 +120,24 @@ class GridFieldPopupFormsTest extends FunctionalTest {
// Get fourth level form (Category detail view)
$this->assertEquals(
'GridFieldPopupFormsTest_GroupController/Form/field/testfield/item/1/ItemEditForm/field/People/item/1/ItemEditForm/field/Categories/item/1/edit',
'GridFieldDetailFormTest_GroupController/Form/field/testfield/item/1/ItemEditForm/field/People/item/1/ItemEditForm/field/Categories/item/1/edit',
(string)$categoryEditLink[0]['href']
);
}
}
class GridFieldPopupFormsTest_Person extends DataObject implements TestOnly {
class GridFieldDetailFormTest_Person extends DataObject implements TestOnly {
static $db = array(
'FirstName' => 'Varchar',
'Surname' => 'Varchar'
);
static $has_one = array(
'Group' => 'GridFieldPopupFormsTest_PeopleGroup'
'Group' => 'GridFieldDetailFormTest_PeopleGroup'
);
static $many_many = array(
'Categories' => 'GridFieldPopupFormsTest_Category'
'Categories' => 'GridFieldDetailFormTest_Category'
);
function getCMSFields() {
@ -153,13 +153,13 @@ class GridFieldPopupFormsTest_Person extends DataObject implements TestOnly {
}
}
class GridFieldPopupFormsTest_PeopleGroup extends DataObject implements TestOnly {
class GridFieldDetailFormTest_PeopleGroup extends DataObject implements TestOnly {
static $db = array(
'Name' => 'Varchar'
);
static $has_many = array(
'People' => 'GridFieldPopupFormsTest_Person'
'People' => 'GridFieldDetailFormTest_Person'
);
function getCMSFields() {
@ -175,13 +175,13 @@ class GridFieldPopupFormsTest_PeopleGroup extends DataObject implements TestOnly
}
}
class GridFieldPopupFormsTest_Category extends DataObject implements TestOnly {
class GridFieldDetailFormTest_Category extends DataObject implements TestOnly {
static $db = array(
'Name' => 'Varchar'
);
static $belongs_many_many = array(
'People' => 'GridFieldPopupFormsTest_Person'
'People' => 'GridFieldDetailFormTest_Person'
);
function getCMSFields() {
@ -197,26 +197,26 @@ class GridFieldPopupFormsTest_Category extends DataObject implements TestOnly {
}
}
class GridFieldPopupFormsTest_Controller extends Controller implements TestOnly {
class GridFieldDetailFormTest_Controller extends Controller implements TestOnly {
protected $template = 'BlankPage';
function Form() {
$group = DataList::create('GridFieldPopupFormsTest_PeopleGroup')
$group = DataList::create('GridFieldDetailFormTest_PeopleGroup')
->filter('Name', 'My Group')
->First();
$field = new GridField('testfield', 'testfield', $group->People());
$field->getConfig()->addComponent($gridFieldForm = new GridFieldPopupForms($this, 'Form'));
$field->getConfig()->addComponent(new GridFieldEditAction());
$field->getConfig()->addComponent($gridFieldForm = new GridFieldDetailForm($this, 'Form'));
$field->getConfig()->addComponent(new GridFieldEditButton());
return new Form($this, 'Form', new FieldList($field), new FieldList());
}
}
class GridFieldPopupFormsTest_GroupController extends Controller implements TestOnly {
class GridFieldDetailFormTest_GroupController extends Controller implements TestOnly {
protected $template = 'BlankPage';
function Form() {
$field = new GridField('testfield', 'testfield', DataList::create('GridFieldPopupFormsTest_PeopleGroup'));
$field = new GridField('testfield', 'testfield', DataList::create('GridFieldDetailFormTest_PeopleGroup'));
$field->getConfig()->addComponent($gridFieldForm = new GridFieldPopupForms($this, 'Form'));
$field->getConfig()->addComponent(new GridFieldEditAction());
return new Form($this, 'Form', new FieldList($field), new FieldList());

View File

@ -0,0 +1,17 @@
GridFieldDetailFormTest_Person:
joe:
FirstName: Joe
Surname: Bloggs
jane:
FirstName: Jane
Surname: Doe
GridFieldDetailFormTest_PeopleGroup:
group:
Name: My Group
People: =>GridFieldDetailFormTest_Person.joe,=>GridFieldDetailFormTest_Person.jane
GridFieldDetailFormTest_Category:
category1:
Name: Category 1
People: =>GridFieldDetailFormTest_Person.joe,=>GridFieldDetailFormTest_Person.jane

View File

@ -1,6 +1,6 @@
<?php
class GridFieldEditActionTest extends SapphireTest {
class GridFieldEditButtonTest extends SapphireTest {
/** @var ArrayList */
protected $list;
@ -20,7 +20,7 @@ class GridFieldEditActionTest extends SapphireTest {
public function setUp() {
parent::setUp();
$this->list = new DataList('GridFieldAction_Edit_Team');
$config = GridFieldConfig::create()->addComponent(new GridFieldEditAction());
$config = GridFieldConfig::create()->addComponent(new GridFieldEditButton());
$this->gridField = new GridField('testfield', 'testfield', $this->list, $config);
$this->form = new Form(new Controller(), 'mockform', new FieldList(array($this->gridField)), new FieldList());
}

View File

@ -1,17 +0,0 @@
GridFieldPopupFormsTest_Person:
joe:
FirstName: Joe
Surname: Bloggs
jane:
FirstName: Jane
Surname: Doe
GridFieldPopupFormsTest_PeopleGroup:
group:
Name: My Group
People: =>GridFieldPopupFormsTest_Person.joe,=>GridFieldPopupFormsTest_Person.jane
GridFieldPopupFormsTest_Category:
category1:
Name: Category 1
People: =>GridFieldPopupFormsTest_Person.joe,=>GridFieldPopupFormsTest_Person.jane

View File

@ -1,12 +1,12 @@
<?php
class GridFieldTitleTest extends SapphireTest {
class GridFieldToolbarHeaderTest extends SapphireTest {
public function testGridTitleAddNewEnabled() {
$this->logInWithPermission('ADMIN');
//construct a fake form field to render out the grid field within it
$config = new GridFieldConfig();
$config->addComponent($titleField = new GridFieldTitle());
$config->addComponent($titleField = new GridFieldToolbarHeader());
$actions = new FieldList();
$grid = new GridField('TestField', 'Test Field', new DataList('Company'),$config);
$fields = new FieldList($rootTab = new TabSet("Root",$tabMain = new Tab('Main',$grid)));
@ -21,7 +21,7 @@ class GridFieldTitleTest extends SapphireTest {
$this->logInWithPermission('ADMIN');
//construct a fake form field to render out the grid field within it
$config = new GridFieldConfig();
$config->addComponent($titleField = new GridFieldTitle());
$config->addComponent($titleField = new GridFieldToolbarHeader());
$actions = new FieldList();
$grid = new GridField('TestField', 'Test Field', new DataList('Company'),$config);
$fields = new FieldList($rootTab = new TabSet("Root",$tabMain = new Tab('Main',$grid)));
@ -35,7 +35,7 @@ class GridFieldTitleTest extends SapphireTest {
public function testGridTitleAddNewWithoutPermission() {
if(Member::currentUser()) { Member::currentUser()->logOut(); }
$config = new GridFieldConfig();
$config->addComponent($titleField = new GridFieldTitle());
$config->addComponent($titleField = new GridFieldToolbarHeader());
$grid = new GridField('TestField', 'Test Field', new DataList('Company'),$config);
$fields = new FieldList(new TabSet("Root",$tabMain = new Tab('Main',$grid)));
$form = new Form(Controller::curr(), "TestForm", $fields, new FieldList());