Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Sean Harvey 2012-03-09 12:21:46 +13:00
commit 7eddf4298f
14 changed files with 363 additions and 14 deletions

View File

@ -43,6 +43,7 @@ if($envFileExists) {
} }
include_once('sapphire/core/Object.php'); include_once('sapphire/core/Object.php');
include_once('sapphire/view/TemplateGlobalProvider.php');
include_once('sapphire/i18n/i18n.php'); include_once('sapphire/i18n/i18n.php');
include_once('sapphire/dev/install/DatabaseConfigurationHelper.php'); include_once('sapphire/dev/install/DatabaseConfigurationHelper.php');
include_once('sapphire/dev/install/DatabaseAdapterRegistry.php'); include_once('sapphire/dev/install/DatabaseAdapterRegistry.php');

View File

@ -384,8 +384,11 @@ class GridField extends FormField {
if($total > 0) { if($total > 0) {
$rows = array(); $rows = array();
foreach($list as $idx => $record) { foreach($list as $idx => $record) {
if(!$record->canView()) {
continue;
}
$rowContent = ''; $rowContent = '';
foreach($columns as $column) { foreach($this->getColumns() as $column) {
$colContent = $this->getColumnContent($record, $column); $colContent = $this->getColumnContent($record, $column);
// A return value of null means this columns should be skipped altogether. // A return value of null means this columns should be skipped altogether.
if($colContent === null) continue; if($colContent === null) continue;
@ -409,8 +412,10 @@ class GridField extends FormField {
$rows[] = $row; $rows[] = $row;
} }
$content['body'] = implode("\n", $rows); $content['body'] = implode("\n", $rows);
}
} else { //display a message when the grid field is empty // Display a message when the grid field is empty
if(!(isset($content['body']) && $content['body'])) {
$content['body'] = $this->createTag( $content['body'] = $this->createTag(
'tr', 'tr',
array("class" => 'ss-gridfield-item ss-gridfield-no-items'), array("class" => 'ss-gridfield-item ss-gridfield-no-items'),

View File

@ -69,6 +69,9 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio
* @return string - the HTML for the column * @return string - the HTML for the column
*/ */
public function getColumnContent($gridField, $record, $columnName) { public function getColumnContent($gridField, $record, $columnName) {
if(!$record->canDelete()) {
return;
}
$field = Object::create('GridField_FormAction', $field = Object::create('GridField_FormAction',
$gridField, $gridField,
'DeleteRecord'.$record->ID, 'DeleteRecord'.$record->ID,
@ -96,6 +99,9 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio
$id = $arguments['RecordID']; $id = $arguments['RecordID'];
// Always deletes a record. Use GridFieldRelationDelete to detach it from the current relationship. // Always deletes a record. Use GridFieldRelationDelete to detach it from the current relationship.
$item = $gridField->getList()->byID($id); $item = $gridField->getList()->byID($id);
if(!$item->canDelete()) {
throw new ValidationException(_t('GridFieldAction_Delete.DeletePermissionsFailure',"No delete permissions"),0);
}
if(!$item) return; if(!$item) return;
$item->delete(); $item->delete();
} }

View File

@ -72,6 +72,9 @@ class GridFieldEditAction implements GridField_ColumnProvider {
* @return string - the HTML for the column * @return string - the HTML for the column
*/ */
public function getColumnContent($gridField, $record, $columnName) { public function getColumnContent($gridField, $record, $columnName) {
if(!$record->canEdit()){
return;
}
$data = new ArrayData(array( $data = new ArrayData(array(
'Link' => Controller::join_links($gridField->Link('item'), $record->ID, 'edit') 'Link' => Controller::join_links($gridField->Link('item'), $record->ID, 'edit')
)); ));

View File

@ -237,7 +237,7 @@ class GridFieldRelationAdd implements GridField_HTMLProvider, GridField_ActionPr
return $this->placeholderText; return $this->placeholderText;
} else { } else {
$labels = array(); $labels = array();
foreach($searchFields as $searchField) { if($searchFields) foreach($searchFields as $searchField) {
$label = singleton($dataClass)->fieldLabel($searchField); $label = singleton($dataClass)->fieldLabel($searchField);
if($label) $labels[] = $label; if($label) $labels[] = $label;
} }

View File

@ -16,9 +16,20 @@
*/ */
class GridFieldTitle implements GridField_HTMLProvider { class GridFieldTitle implements GridField_HTMLProvider {
/**
*
* @var bool
*/
protected $newEnabled = true; protected $newEnabled = true;
function getHTMLFragments($gridField) { /**
*
* @var type
*/
protected $gridField = null;
public function getHTMLFragments( $gridField) {
$this->gridField = $gridField;
return array( return array(
'header' => $gridField->customise(array( 'header' => $gridField->customise(array(
'NewLink' => Controller::join_links($gridField->Link('item'), 'new'), 'NewLink' => Controller::join_links($gridField->Link('item'), 'new'),
@ -31,7 +42,14 @@ class GridFieldTitle implements GridField_HTMLProvider {
* Returns whether or not the "add new" button will appear when rendering this DataGrid title * Returns whether or not the "add new" button will appear when rendering this DataGrid title
* @return bool * @return bool
*/ */
function getNewEnabled() { public function getNewEnabled() {
if($this->gridField) {
$model = singleton($this->gridField->getModelClass());
if(!$model->canCreate()) {
return false;
}
}
return $this->newEnabled; return $this->newEnabled;
} }
@ -39,9 +57,7 @@ class GridFieldTitle implements GridField_HTMLProvider {
* Enable or disable the "add new" button to add new DataGrid object instances * Enable or disable the "add new" button to add new DataGrid object instances
* @param $enabled * @param $enabled
*/ */
function setNewEnabled($enabled) { public function setNewEnabled($enabled) {
$this->newEnabled = $enabled; $this->newEnabled = $enabled;
} }
} }
?>

View File

@ -417,6 +417,34 @@ class GridFieldTest extends SapphireTest {
$this->setExpectedException('LogicException'); $this->setExpectedException('LogicException');
$field->FieldHolder(); $field->FieldHolder();
} }
/**
* @covers GridField::FieldHolder
*/
public function testCanViewOnlyOddIDs() {
$this->logInWithPermission();
$list = new ArrayList(array(
new GridFieldTest_Permissions(array("ID" => 1, "Email" => "ongi.schwimmer@example.org", 'Name' => 'Ongi Schwimmer')),
new GridFieldTest_Permissions(array("ID" => 2, "Email" => "klaus.lozenge@example.org", 'Name' => 'Klaus Lozenge')),
new GridFieldTest_Permissions(array("ID" => 3, "Email" => "otto.fischer@example.org", 'Name' => 'Otto Fischer'))
));
$config = new GridFieldConfig();
$config->addComponent(new GridFieldDefaultColumns());
$obj = new GridField('testfield', 'testfield', $list, $config);
$form = new Form(new Controller(), 'mockform', new FieldList(array($obj)), new FieldList());
$content = new CSSContentParser($obj->FieldHolder());
$members = $content->getBySelector('.ss-gridfield-item tr');
$this->assertEquals(2, count($members));
$this->assertEquals((string)$members[0]->td[0], 'Ongi Schwimmer', 'First object Name should be Ongi Schwimmer');
$this->assertEquals((string)$members[0]->td[1], 'ongi.schwimmer@example.org', 'First object Email should be ongi.schwimmer@example.org');
$this->assertEquals((string)$members[1]->td[0], 'Otto Fischer', 'Second object Name should be Otto Fischer');
$this->assertEquals((string)$members[1]->td[1], 'otto.fischer@example.org', 'Second object Email should be otto.fischer@example.org');
}
} }
class GridFieldTest_Component implements GridField_ColumnProvider, GridField_ActionProvider, TestOnly{ class GridFieldTest_Component implements GridField_ColumnProvider, GridField_ActionProvider, TestOnly{
@ -473,7 +501,6 @@ class GridFieldTest_Player extends DataObject implements TestOnly {
static $belongs_many_many = array('Teams' => 'GridFieldTest_Team'); static $belongs_many_many = array('Teams' => 'GridFieldTest_Team');
} }
class GridFieldTest_HTMLFragments implements GridField_HTMLProvider, TestOnly{ class GridFieldTest_HTMLFragments implements GridField_HTMLProvider, TestOnly{
function __construct($fragments) { function __construct($fragments) {
$this->fragments = $fragments; $this->fragments = $fragments;
@ -483,3 +510,21 @@ class GridFieldTest_HTMLFragments implements GridField_HTMLProvider, TestOnly{
return $this->fragments; return $this->fragments;
} }
} }
class GridFieldTest_Permissions extends DataObject implements TestOnly {
public static $db = array(
'Name' => 'Varchar',
'Email' => 'Varchar',
);
public static $summary_fields = array(
'Name',
'Email'
);
public function canView($member = null) {
// Only records with odd numbers are viewable
if(!($this->ID % 2)){ return false; }
return true;
}
}

View File

@ -0,0 +1,18 @@
GridFieldAction_Delete_Team:
team1:
Name: Team 1
City: Cologne
team2:
Name: Team 2
City: Wellington
team3:
Name: Team 3
City: Auckland
GridFieldAction_Edit_Team:
team1:
Name: Team 1
City: Cologne
team2:
Name: Team 2
City: Wellington

View File

@ -0,0 +1,78 @@
<?php
class GridFieldDeleteActionTest extends SapphireTest {
/** @var ArrayList */
protected $list;
/** @var GridField */
protected $gridField;
/** @var Form */
protected $form;
/** @var string */
public static $fixture_file = 'sapphire/tests/forms/gridfield/GridFieldActionTest.yml';
/** @var array */
protected $extraDataObjects = array('GridFieldAction_Delete_Team', 'GridFieldAction_Edit_Team');
public function setUp() {
parent::setUp();
$this->list = new DataList('GridFieldAction_Delete_Team');
$config = GridFieldConfig::create()->addComponent(new GridFieldDeleteAction());
$this->gridField = new GridField('testfield', 'testfield', $this->list, $config);
$this->form = new Form(new Controller(), 'mockform', new FieldList(array($this->gridField)), new FieldList());
}
public function testDontShowDeleteButtons() {
if(Member::currentUser()) { Member::currentUser()->logOut(); }
$content = new CSSContentParser($this->gridField->FieldHolder());
// Check that there are content
$this->assertEquals(4, count($content->getBySelector('.ss-gridfield-item')));
// Make sure that there are no delete buttons
$this->assertEmpty($content->getBySelector('.gridfield-button-delete'), 'Delete buttons should not show when not logged in.');
}
public function testShowDeleteButtonsWithAdminPermission() {
$this->logInWithPermission('ADMIN');
$content = new CSSContentParser($this->gridField->FieldHolder());
$deleteButtons = $content->getBySelector('.gridfield-button-delete');
$this->assertEquals(3, count($deleteButtons), 'Delete buttons should show when logged in.');
}
public function testDeleteActionWithoutCorrectPermission() {
if(Member::currentUser()) { Member::currentUser()->logOut(); }
$this->setExpectedException('ValidationException');
$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(3, $this->list->count(), 'User should\'t be able to delete records without correct permissions.');
}
public function testDeleteActionWithAdminPermission() {
$this->logInWithPermission('ADMIN');
$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 {
static $db = array(
'Name' => 'Varchar',
'City' => 'Varchar'
);
public function canView($member = null) {
return true;
}
public function canDelete($member = null) {
return parent::canDelete($member);
}
}

View File

@ -0,0 +1,55 @@
<?php
class GridFieldEditActionTest extends SapphireTest {
/** @var ArrayList */
protected $list;
/** @var GridField */
protected $gridField;
/** @var Form */
protected $form;
/** @var string */
public static $fixture_file = 'sapphire/tests/forms/gridfield/GridFieldActionTest.yml';
/** @var array */
protected $extraDataObjects = array('GridFieldAction_Delete_Team', 'GridFieldAction_Edit_Team');
public function setUp() {
parent::setUp();
$this->list = new DataList('GridFieldAction_Edit_Team');
$config = GridFieldConfig::create()->addComponent(new GridFieldEditAction());
$this->gridField = new GridField('testfield', 'testfield', $this->list, $config);
$this->form = new Form(new Controller(), 'mockform', new FieldList(array($this->gridField)), new FieldList());
}
public function testDontShowEditLinks() {
if(Member::currentUser()) { Member::currentUser()->logOut(); }
$content = new CSSContentParser($this->gridField->FieldHolder());
// Check that there are content
$this->assertEquals(3, count($content->getBySelector('.ss-gridfield-item')));
// Make sure that there are no edit links
$this->assertEmpty($content->getBySelector('.edit-link'), 'Edit links should not show when not logged in.');
}
public function testShowEditLinksWithAdminPermission() {
$this->logInWithPermission('ADMIN');
$content = new CSSContentParser($this->gridField->FieldHolder());
$editLinks = $content->getBySelector('.edit-link');
$this->assertEquals(2, count($editLinks), 'Edit links should show when logged in.');
}
}
class GridFieldAction_Edit_Team extends DataObject implements TestOnly {
static $db = array(
'Name' => 'Varchar',
'City' => 'Varchar'
);
public function canView($member = null) {
return true;
}
}

View File

@ -5,11 +5,13 @@ class GridFieldPopupFormsTest extends FunctionalTest {
protected $extraDataObjects = array( protected $extraDataObjects = array(
'GridFieldPopupFormsTest_Person', 'GridFieldPopupFormsTest_Person',
'GridFieldPopupFormsTest_PeopleGroup' 'GridFieldPopupFormsTest_PeopleGroup',
'GridFieldPopupFormsTest_Category',
); );
function testAddForm() { function testAddForm() {
$this->logInWithPermission('ADMIN');
$group = DataList::create('GridFieldPopupFormsTest_PeopleGroup') $group = DataList::create('GridFieldPopupFormsTest_PeopleGroup')
->filter('Name', 'My Group') ->filter('Name', 'My Group')
->First(); ->First();
@ -45,6 +47,7 @@ class GridFieldPopupFormsTest extends FunctionalTest {
} }
function testEditForm() { function testEditForm() {
$this->logInWithPermission('ADMIN');
$group = DataList::create('GridFieldPopupFormsTest_PeopleGroup') $group = DataList::create('GridFieldPopupFormsTest_PeopleGroup')
->filter('Name', 'My Group') ->filter('Name', 'My Group')
->First(); ->First();
@ -80,6 +83,47 @@ class GridFieldPopupFormsTest extends FunctionalTest {
$firstperson = $group->People()->First(); $firstperson = $group->People()->First();
$this->assertEquals($firstperson->Surname, 'Baggins'); $this->assertEquals($firstperson->Surname, 'Baggins');
} }
function testNestedEditForm() {
$this->logInWithPermission('ADMIN');
$group = $this->objFromFixture('GridFieldPopupFormsTest_PeopleGroup', 'group');
$person = $group->People()->First();
$category = $person->Categories()->First();
// Get first form (GridField managing PeopleGroup)
$response = $this->get('GridFieldPopupFormsTest_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',
(string)$groupEditLink[0]['href']
);
// Get second level form (GridField managing Person)
$response = $this->get((string)$groupEditLink[0]['href']);
$this->assertFalse($response->isError());
$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',
(string)$personEditLink[0]['href']
);
// Get third level form (GridField managing Category)
$response = $this->get((string)$personEditLink[0]['href']);
$this->assertFalse($response->isError());
$parser = new CSSContentParser($response->getBody());
$categoryEditLink = $parser->getByXpath('//fieldset[@id="Form_ItemEditForm_Categories"]//tr[contains(@class, "ss-gridfield-item") and contains(@data-id, "' . $category->ID . '")]//a');
// 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',
(string)$categoryEditLink[0]['href']
);
}
} }
class GridFieldPopupFormsTest_Person extends DataObject implements TestOnly { class GridFieldPopupFormsTest_Person extends DataObject implements TestOnly {
@ -91,6 +135,22 @@ class GridFieldPopupFormsTest_Person extends DataObject implements TestOnly {
static $has_one = array( static $has_one = array(
'Group' => 'GridFieldPopupFormsTest_PeopleGroup' 'Group' => 'GridFieldPopupFormsTest_PeopleGroup'
); );
static $many_many = array(
'Categories' => 'GridFieldPopupFormsTest_Category'
);
function getCMSFields() {
$fields = parent::getCMSFields();
// TODO No longer necessary once FormScaffolder uses GridField
$fields->replaceField('Categories',
Object::create('GridField', 'Categories', 'Categories',
$this->Categories(),
GridFieldConfig_RelationEditor::create()
)
);
return $fields;
}
} }
class GridFieldPopupFormsTest_PeopleGroup extends DataObject implements TestOnly { class GridFieldPopupFormsTest_PeopleGroup extends DataObject implements TestOnly {
@ -101,6 +161,40 @@ class GridFieldPopupFormsTest_PeopleGroup extends DataObject implements TestOnly
static $has_many = array( static $has_many = array(
'People' => 'GridFieldPopupFormsTest_Person' 'People' => 'GridFieldPopupFormsTest_Person'
); );
function getCMSFields() {
$fields = parent::getCMSFields();
// TODO No longer necessary once FormScaffolder uses GridField
$fields->replaceField('People',
Object::create('GridField', 'People', 'People',
$this->People(),
GridFieldConfig_RelationEditor::create()
)
);
return $fields;
}
}
class GridFieldPopupFormsTest_Category extends DataObject implements TestOnly {
static $db = array(
'Name' => 'Varchar'
);
static $belongs_many_many = array(
'People' => 'GridFieldPopupFormsTest_Person'
);
function getCMSFields() {
$fields = parent::getCMSFields();
// TODO No longer necessary once FormScaffolder uses GridField
$fields->replaceField('People',
Object::create('GridField', 'People', 'People',
$this->People(),
GridFieldConfig_RelationEditor::create()
)
);
return $fields;
}
} }
class GridFieldPopupFormsTest_Controller extends Controller implements TestOnly { class GridFieldPopupFormsTest_Controller extends Controller implements TestOnly {
@ -118,4 +212,13 @@ class GridFieldPopupFormsTest_Controller extends Controller implements TestOnly
} }
} }
?> class GridFieldPopupFormsTest_GroupController extends Controller implements TestOnly {
protected $template = 'BlankPage';
function Form() {
$field = new GridField('testfield', 'testfield', DataList::create('GridFieldPopupFormsTest_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

@ -10,3 +10,8 @@ GridFieldPopupFormsTest_PeopleGroup:
group: group:
Name: My Group Name: My Group
People: =>GridFieldPopupFormsTest_Person.joe,=>GridFieldPopupFormsTest_Person.jane People: =>GridFieldPopupFormsTest_Person.joe,=>GridFieldPopupFormsTest_Person.jane
GridFieldPopupFormsTest_Category:
category1:
Name: Category 1
People: =>GridFieldPopupFormsTest_Person.joe,=>GridFieldPopupFormsTest_Person.jane

View File

@ -37,6 +37,7 @@ class GridFieldRelationAddTest extends FunctionalTest {
} }
function testAdd() { function testAdd() {
$this->logInWithPermission('ADMIN');
$team1 = $this->objFromFixture('GridFieldTest_Team', 'team1'); $team1 = $this->objFromFixture('GridFieldTest_Team', 'team1');
$team2 = $this->objFromFixture('GridFieldTest_Team', 'team2'); $team2 = $this->objFromFixture('GridFieldTest_Team', 'team2');

View File

@ -3,6 +3,7 @@ class GridFieldTitleTest extends SapphireTest {
public function testGridTitleAddNewEnabled() { public function testGridTitleAddNewEnabled() {
$this->logInWithPermission('ADMIN');
//construct a fake form field to render out the grid field within it //construct a fake form field to render out the grid field within it
$config = new GridFieldConfig(); $config = new GridFieldConfig();
$config->addComponent($titleField = new GridFieldTitle()); $config->addComponent($titleField = new GridFieldTitle());
@ -17,6 +18,7 @@ class GridFieldTitleTest extends SapphireTest {
} }
public function testGridTitleAddNewDisabled() { public function testGridTitleAddNewDisabled() {
$this->logInWithPermission('ADMIN');
//construct a fake form field to render out the grid field within it //construct a fake form field to render out the grid field within it
$config = new GridFieldConfig(); $config = new GridFieldConfig();
$config->addComponent($titleField = new GridFieldTitle()); $config->addComponent($titleField = new GridFieldTitle());
@ -29,5 +31,16 @@ class GridFieldTitleTest extends SapphireTest {
$html = $form->forTemplate(); $html = $form->forTemplate();
$this->assertNotContains('data-icon="add"', $html,"HTML does not contain the 'add new' button"); $this->assertNotContains('data-icon="add"', $html,"HTML does not contain the 'add new' button");
} }
public function testGridTitleAddNewWithoutPermission() {
if(Member::currentUser()) { Member::currentUser()->logOut(); }
$config = new GridFieldConfig();
$config->addComponent($titleField = new GridFieldTitle());
$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());
$html = $form->forTemplate();
$this->assertNotContains('data-icon="add"', $html, "HTML should not contain the 'add new' button");
}
} }
?>