From c396c2d2ae87c1c76aecdc4d3d211a8d8ab228d1 Mon Sep 17 00:00:00 2001 From: Stig Lindqvist Date: Wed, 25 Jan 2012 17:31:27 +1300 Subject: [PATCH 1/6] API CHANGE Security admin supports adding, removing and searching for members by relations via gridfield This contains some experimental API's when using GridFieldPopupForms on GridFieldPopupForms. - GridFieldRelationAdd - GridFieldRelationDelete --- admin/code/SecurityAdmin.php | 14 +- forms/gridfield/GridField.php | 16 +- forms/gridfield/GridFieldPopupForms.php | 106 +++++++++-- forms/gridfield/GridFieldRelationAdd.php | 198 ++++++++++++++++++++ forms/gridfield/GridFieldRelationDelete.php | 99 ++++++++++ javascript/GridFieldSearch.js | 36 ++++ security/Group.php | 18 +- templates/Includes/GridFieldItemEditView.ss | 3 + templates/Includes/GridFieldRelationAdd.ss | 4 + 9 files changed, 458 insertions(+), 36 deletions(-) mode change 100644 => 100755 admin/code/SecurityAdmin.php mode change 100644 => 100755 forms/gridfield/GridFieldPopupForms.php create mode 100755 forms/gridfield/GridFieldRelationAdd.php create mode 100644 forms/gridfield/GridFieldRelationDelete.php create mode 100644 javascript/GridFieldSearch.js mode change 100644 => 100755 security/Group.php create mode 100644 templates/Includes/GridFieldItemEditView.ss create mode 100644 templates/Includes/GridFieldRelationAdd.ss diff --git a/admin/code/SecurityAdmin.php b/admin/code/SecurityAdmin.php old mode 100644 new mode 100755 index 60f954d8f..652277ecc --- a/admin/code/SecurityAdmin.php +++ b/admin/code/SecurityAdmin.php @@ -107,12 +107,14 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider { * @return FieldList */ function RootForm() { - $memberList = new MemberTableField( - $this, - "Members" - ); - // unset 'inlineadd' permission, we don't want inline addition - $memberList->setPermissions(array('edit', 'delete', 'add')); + $config = new GridFieldConfig(); + $config->addComponent(new GridFieldRelationAdd('Name')); + $config->addComponent(new GridFieldDefaultColumns()); + $config->addComponent(new GridFieldSortableHeader()); + $config->addComponent(new GridFieldPaginator()); + $config->addComponent(new GridFieldAction_Edit()); + $config->addComponent(new GridFieldPopupForms($this, 'RootForm')); + $memberList = new GridField('Members', 'All members', DataList::create('Member'), $config); $fields = new FieldList( $root = new TabSet( diff --git a/forms/gridfield/GridField.php b/forms/gridfield/GridField.php index eb09c34cb..c1e334cbe 100755 --- a/forms/gridfield/GridField.php +++ b/forms/gridfield/GridField.php @@ -511,7 +511,11 @@ class GridField extends FormField { $actionName = $stateChange['actionName']; $args = isset($stateChange['args']) ? $stateChange['args'] : array(); - $grid->handleAction($actionName, $args, $data); + $html = $grid->handleAction($actionName, $args, $data); + + if($html) { + return $html; + } switch($request->getHeader('X-Get-Fragment')) { case 'CurrentField': @@ -539,13 +543,13 @@ class GridField extends FormField { */ public function handleAction($actionName, $args, $data) { $actionName = strtolower($actionName); - foreach($this->components as $item) { - if(!($item instanceof GridField_ActionProvider)) { + foreach($this->components as $component) { + if(!($component instanceof GridField_ActionProvider)) { continue; } - if(in_array($actionName, array_map('strtolower', $item->getActions($this)))) { - return $item->handleAction($this, $actionName, $args, $data); + if(in_array($actionName, array_map('strtolower', $component->getActions($this)))) { + return $component->handleAction($this, $actionName, $args, $data); } } throw new InvalidArgumentException("Can't handle action '$actionName'"); @@ -564,8 +568,6 @@ class GridField extends FormField { $this->request = $request; $this->setModel($model); - /// - foreach($this->components as $component) { if(!($component instanceof GridField_URLHandler)) { continue; diff --git a/forms/gridfield/GridFieldPopupForms.php b/forms/gridfield/GridFieldPopupForms.php old mode 100644 new mode 100755 index 621865312..dc3425141 --- a/forms/gridfield/GridFieldPopupForms.php +++ b/forms/gridfield/GridFieldPopupForms.php @@ -13,16 +13,51 @@ class GridFieldPopupForms implements GridField_URLHandler { * @var String */ protected $template = 'GridFieldItemEditView'; + + /** + * + * @var Controller + */ + protected $popupController; + /** + * + * @var string + */ + protected $popupFormName; + function getURLHandlers($gridField) { return array( 'item/$ID' => 'handleItem', + 'autocomplete' => 'handleAutocomplete', ); } - function handleItem($gridField, $request) { + /** + * Create a popup component. The two arguments will specify how the popup form's HTML and + * behaviour is created. The given controller will be customised, putting the edit form into the + * template with the given name. + * + * The arguments are experimental API's to support partial content to be passed back to whatever + * controller who wants to display the getCMSFields + * + * @param Controller $popupController The controller object that will be used to render the pop-up forms + * @param string $popupFormName The name of the edit form to place into the pop-up form + */ + public function __construct($popupController, $popupFormName) { + $this->popupController = $popupController; + $this->popupFormName = $popupFormName; + } + + /** + * + * @param type $gridField + * @param type $request + * @return GridFieldPopupForm_ItemRequest + */ + public function handleItem($gridField, $request) { $record = $gridField->getList()->byId($request->param("ID")); - $handler = new GridFieldPopupForm_ItemRequest($gridField, $this, $record); + $handler = new GridFieldPopupForm_ItemRequest($gridField, $this, $record, $this->popupController, $this->popupFormName); $handler->setTemplate($this->template); return $handler; } @@ -44,12 +79,36 @@ class GridFieldPopupForms implements GridField_URLHandler { class GridFieldPopupForm_ItemRequest extends RequestHandler { + /** + * + * @var GridField + */ protected $gridField; + /** + * + * @var GridField_URLHandler + */ protected $component; + /** + * + * @var DataObject + */ protected $record; + /** + * + * @var Controller + */ + protected $popupController; + + /** + * + * @var string + */ + protected $popupFormName; + /** * @var String */ @@ -57,45 +116,66 @@ class GridFieldPopupForm_ItemRequest extends RequestHandler { static $url_handlers = array( '$Action!' => '$Action', - '' => 'index', + '' => 'edit', ); - function __construct($gridField, $component, $record) { + /** + * + * @param GridFIeld $gridField + * @param GridField_URLHandler $component + * @param DataObject $record + * @param Controller $popupController + * @param string $popupFormName + */ + public function __construct($gridField, $component, $record, $popupController, $popupFormName) { $this->gridField = $gridField; $this->component = $gridField; $this->record = $record; - + $this->popupController = $popupController; + $this->popupFormName = $popupFormName; parent::__construct(); } - function Link($action = null) { + public function Link($action = null) { return Controller::join_links($this->gridField->Link('item'), $this->record->ID, $action); } function edit($request) { - $controller = $this->gridField->getForm()->Controller(); - + $controller = $this->popupController; + $return = $this->customise(array( - 'Backlink' => $controller->Link(), + 'Backlink' => $this->gridField->getForm()->Controller()->Link(), 'ItemEditForm' => $this->ItemEditForm($this->gridField, $request), ))->renderWith($this->template); if($controller->isAjax()) { return $return; } else { + // If not requested by ajax, we need to render it within the controller context+template return $controller->customise(array( - $this->gridField->getForm()->Name() => $return, + $this->popupFormName => $return, )); } } + /** + * Builds an item edit form. The arguments to getCMSFields() are the popupController and + * popupFormName, however this is an experimental API and may change. + * + * In the future, we will probably need to come up with a tigher object representing a partially + * complete controller with gaps for extra functionality. This, for example, would be a better way + * of letting Security/login put its log-in form inside a UI specified elsewhere. + * + * @return Form + */ function ItemEditForm() { - $request = $this->gridField->getForm()->Controller()->getRequest(); + $request = $this->popupController->getRequest(); $form = new Form( $this, 'ItemEditForm', - $this->record->getCMSFields(), + // WARNING: The arguments passed here are a little arbitrary. This API will need cleanup + $this->record->getCMSFields($this->popupController, $this->popupFormName), new FieldList( $saveAction = new FormAction('doSave', _t('GridFieldDetailsForm.Save', 'Save')) ) @@ -124,7 +204,7 @@ class GridFieldPopupForm_ItemRequest extends RequestHandler { $form->sessionMessage($message, 'good'); - return $this->gridField->getForm()->Controller()->redirectBack(); + return $this->popupController->redirectBack(); } /** diff --git a/forms/gridfield/GridFieldRelationAdd.php b/forms/gridfield/GridFieldRelationAdd.php new file mode 100755 index 000000000..b735d3c21 --- /dev/null +++ b/forms/gridfield/GridFieldRelationAdd.php @@ -0,0 +1,198 @@ +fieldToSearch = $fieldToSearch; + $this->useAutoSuggestion = $autoSuggestion; + } + + /** + * + * @param GridField $gridField + * @return string - HTML + */ + public function getHTMLFragments($gridField) { + $searchState = $gridField->State->GridFieldSearchRelation; + + + if($this->useAutoSuggestion){ + Requirements::css(THIRDPARTY_DIR . '/jquery-ui-themes/smoothness/jquery-ui.css'); + Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang'); + Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery.js'); + Requirements::javascript(SAPPHIRE_DIR . '/javascript/jquery_improvements.js'); + Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-ui/jquery-ui.js'); + Requirements::javascript(SAPPHIRE_DIR . "/javascript/GridFieldSearch.js"); + } + + $forTemplate = new ArrayData(array()); + $forTemplate->Fields = new ArrayList(); + + $value = $this->findSingleEntry($gridField, $this->fieldToSearch, $searchState, $gridField->getList()->dataClass); + $searchField = new TextField('gridfield_relationsearch', 'Auto Suggest Search field', $value); + // Apparently the data-* needs to be double qouted for the jQuery.meta data plugin + // + $searchField->setAttribute('data-search-url', '\''.Controller::join_links($gridField->Link('search').'\'')); + + $findAction = new GridField_Action($gridField, 'gridfield_relationfind', 'Find', 'find', 'find'); + $addAction = new GridField_Action($gridField, 'gridfield_relationadd', 'Add Relation', 'addto', 'addto'); + + // If an object is not found, disable the action + if(!is_int($gridField->State->GridFieldAddRelation)) { + $addAction->setReadonly(true); + } + + $forTemplate->Fields->push($searchField); + $forTemplate->Fields->push($findAction); + $forTemplate->Fields->push($addAction); + return array('before' => $forTemplate->renderWith($this->itemClass)); + } + + /** + * + * @param GridField $gridField + * @return array + */ + public function getActions($gridField) { + return array('addto', 'find'); + } + + /** + * Manipulate the state to either add a new relation, or doing a small search + * + * @param GridField $gridField + * @param string $actionName + * @param string $arguments + * @param string $data + * @return string + */ + public function handleAction(GridField $gridField, $actionName, $arguments, $data) { + switch($actionName) { + case 'addto': + if(isset($data['relationID']) && $data['relationID']){ + $gridField->State->GridFieldAddRelation = $data['relationID']; + } + $gridField->State->GridFieldSearchRelation = ''; + break; + case 'find' && isset($data['autosuggest_search']): + $gridField->State->GridFieldSearchRelation = $data['autosuggest_search']; + break; + } + } + + /** + * If an object ID is set, add the object to the list + * + * @param GridField $gridField + * @param SS_List $dataList + * @return SS_List + */ + public function getManipulatedData(GridField $gridField, SS_List $dataList) { + if(!$gridField->State->GridFieldAddRelation) { + return $dataList; + } + $objectID = Convert::raw2sql($gridField->State->GridFieldAddRelation); + if($objectID) { + $object = DataObject::get_by_id($dataList->dataclass(), $objectID); + if($object) { + $dataList->add($object); + } + } + $gridField->State->GridFieldAddRelation = null; + return $dataList; + } + + /** + * + * @param GridField $gridField + * @return array + */ + public function getURLHandlers($gridField) { + return array( + 'search/$ID' => 'doSearch', + ); + } + + /** + * Returns a json array of a search results that can be used by for example Jquery.ui.autosuggestion + * + * @param GridField $gridField + * @param SS_HTTPRequest $request + */ + public function doSearch($gridField, $request) { + $allList = DataList::create($gridField->getList()->dataClass()); + $results = $allList->subtract($gridField->getList())->filter($this->fieldToSearch.':StartsWith',$request->param('ID')); + $results->sort($this->fieldToSearch, 'ASC'); + + $json = array(); + foreach($results as $result) { + $json[$result->ID] = $result->{$this->fieldToSearch}; + } + return Convert::array2json($json); + } + + /** + * This will provide a StartsWith search that only returns a value if we are + * matching ONE object only. We wouldn't want to attach used any object to + * the list. + * + * @param GridField $gridField + * @param string $field + * @param string $searchTerm + * @param string $dataclass + * @return string + */ + protected function findSingleEntry($gridField, $field, $searchTerm, $dataclass) { + $fullList = DataList::create($dataclass); + $searchTerm = Convert::raw2sql($searchTerm); + if(!$searchTerm) { + return; + } + $existingList = clone $gridField->getList(); + $searchResults = $fullList->subtract($existingList->limit(0))->filter($field.':StartsWith', $searchTerm); + + // If more than one, skip + if($searchResults->count() != 1) { + return ''; + } + + $gridField->State->GridFieldAddRelation = $searchResults->first()->ID; + return $searchResults->first()->$field; + } +} \ No newline at end of file diff --git a/forms/gridfield/GridFieldRelationDelete.php b/forms/gridfield/GridFieldRelationDelete.php new file mode 100644 index 000000000..caf9fe5de --- /dev/null +++ b/forms/gridfield/GridFieldRelationDelete.php @@ -0,0 +1,99 @@ + ''); + } + } + + /** + * Which columns are handled by this component + * + * @param type $gridField + * @return type + */ + public function getColumnsHandled($gridField) { + return array('UnlinkRelation'); + } + + /** + * 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 = new GridField_Action( + $gridField, + 'UnlinkRelation'.$record->ID, + _t('GridAction.UnlinkRelation', "Unlink"), + "unlinkrelation", + array('RecordID' => $record->ID) + ); + $output = $field->Field(); + return $output; + } + + /** + * 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); + } + } +} diff --git a/javascript/GridFieldSearch.js b/javascript/GridFieldSearch.js new file mode 100644 index 000000000..e8a96dcf9 --- /dev/null +++ b/javascript/GridFieldSearch.js @@ -0,0 +1,36 @@ +jQuery(function($){ + + $(document).delegate("#gridfield_relationsearch", "focus", function (event) { + $(this).autocomplete({ + source: function(request, response){ + var searchField = $(this.element); + var form = $(this.element).closest("form"); + // Due to some very weird behaviout of jquery.metadata, the url have to be double quoted + var suggestionUrl = $(searchField).attr('data-search-url').substr(1,$(searchField).attr('data-search-url').length-2); + $.ajax({ + headers: { + "X-Get-Fragment" : 'Partial' + }, + type: "GET", + url: suggestionUrl+'/'+request.term, + data: form.serialize()+'&'+escape(searchField.attr('name'))+'='+escape(searchField.val()), + success: function(data) { + response( $.map(JSON.parse(data), function( name, id ) { + return { label: name, value: name, id: id } + })); + }, + error: function(e) { + alert(ss.i18n._t('GRIDFIELD.ERRORINTRANSACTION', 'An error occured while fetching data from the server\n Please try again later.')); + } + }); + }, + select: function(event, ui) { + $(this).closest("fieldset.ss-gridfield").find("#action_gridfield_relationfind").replaceWith( + '' + ); + $(this).closest("fieldset.ss-gridfield").find("#action_gridfield_relationadd").removeAttr('disabled'); + } + }); + }); + +}); \ No newline at end of file diff --git a/security/Group.php b/security/Group.php old mode 100644 new mode 100755 index e6c00102c..a3c0bcf3f --- a/security/Group.php +++ b/security/Group.php @@ -62,17 +62,18 @@ class Group extends DataObject { public function getCMSFields() { Requirements::javascript(SAPPHIRE_DIR . '/javascript/PermissionCheckboxSetField.js'); + $config = new GridFieldConfig_ManyManyEditor('FirstName', true, 20); + $config->addComponent(new GridFieldPopupForms(Controller::curr(), 'EditForm')); + $memberList = new GridField('Members','Members', $this->Members(), $config); + + // @todo Implement permission checking on GridField + //$memberList->setPermissions(array('edit', 'delete', 'export', 'add', 'inlineadd')); + //$memberList->setPopupCaption(_t('SecurityAdmin.VIEWUSER', 'View User')); $fields = new FieldList( new TabSet("Root", new Tab('Members', _t('SecurityAdmin.MEMBERS', 'Members'), new TextField("Title", $this->fieldLabel('Title')), - $memberList = new MemberTableField( - (Controller::has_curr()) ? Controller::curr() : new Controller(), - "Members", - $this, - null, - false - ) + $memberList ), $permissionsTab = new Tab('Permissions', _t('SecurityAdmin.PERMISSIONS', 'Permissions'), @@ -152,9 +153,6 @@ class Group extends DataObject { $rolesField->setDisabledItems($inheritedRoles->column('ID')); } - $memberList->setPermissions(array('edit', 'delete', 'export', 'add', 'inlineadd')); - $memberList->setPopupCaption(_t('SecurityAdmin.VIEWUSER', 'View User')); - $fields->push($idField = new HiddenField("ID")); $this->extend('updateCMSFields', $fields); diff --git a/templates/Includes/GridFieldItemEditView.ss b/templates/Includes/GridFieldItemEditView.ss new file mode 100644 index 000000000..ed700a4a4 --- /dev/null +++ b/templates/Includes/GridFieldItemEditView.ss @@ -0,0 +1,3 @@ + Go back the way you came! + +$ItemEditForm \ No newline at end of file diff --git a/templates/Includes/GridFieldRelationAdd.ss b/templates/Includes/GridFieldRelationAdd.ss new file mode 100644 index 000000000..f50e6385f --- /dev/null +++ b/templates/Includes/GridFieldRelationAdd.ss @@ -0,0 +1,4 @@ +
<% control Fields %> + $Field + <% end_control %> +
\ No newline at end of file From ed889529b48590f3fe04b217f9abb7c0a6eda98b Mon Sep 17 00:00:00 2001 From: Stig Lindqvist Date: Tue, 24 Jan 2012 12:36:25 +1300 Subject: [PATCH 2/6] API CHANGE Added two predefined GridFieldConfigs and removal of unsused accessors - GridFieldConfig_ManyManyEditor for default relationship list managment - GridFieldConfig_Base for basic list managment --- admin/code/SecurityAdmin.php | 10 +-- forms/gridfield/GridFieldConfig.php | 104 +++++++++++++++++----------- 2 files changed, 64 insertions(+), 50 deletions(-) mode change 100644 => 100755 forms/gridfield/GridFieldConfig.php diff --git a/admin/code/SecurityAdmin.php b/admin/code/SecurityAdmin.php index 652277ecc..795ae41fd 100755 --- a/admin/code/SecurityAdmin.php +++ b/admin/code/SecurityAdmin.php @@ -35,7 +35,6 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider { public function init() { parent::init(); - Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/javascript/SecurityAdmin.js'); } @@ -107,12 +106,7 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider { * @return FieldList */ function RootForm() { - $config = new GridFieldConfig(); - $config->addComponent(new GridFieldRelationAdd('Name')); - $config->addComponent(new GridFieldDefaultColumns()); - $config->addComponent(new GridFieldSortableHeader()); - $config->addComponent(new GridFieldPaginator()); - $config->addComponent(new GridFieldAction_Edit()); + $config = new GridFieldConfig_Base(25); $config->addComponent(new GridFieldPopupForms($this, 'RootForm')); $memberList = new GridField('Members', 'All members', DataList::create('Member'), $config); @@ -134,7 +128,7 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider { new LiteralField( 'GroupImportFormIframe', sprintf( - '', + '', $this->Link('groupimport') ) ) diff --git a/forms/gridfield/GridFieldConfig.php b/forms/gridfield/GridFieldConfig.php old mode 100644 new mode 100755 index fce772231..8e254ea6b --- a/forms/gridfield/GridFieldConfig.php +++ b/forms/gridfield/GridFieldConfig.php @@ -19,24 +19,6 @@ class GridFieldConfig { */ protected $components = null; - /** - * - * @var int - */ - protected $checkboxes = null; - - /** - * - * @var array - */ - protected $affectors = array(); - - /** - * - * @var array - */ - protected $decorators = array(); - /** * */ @@ -59,30 +41,68 @@ class GridFieldConfig { } return $this->components; } - - public function setCheckboxes($row=0){ - $this->checkboxes = $row; - return $this; +} + +class GridFieldConfig_Base extends GridFieldConfig { + + /** + * + * @param int $itemsPerPage - How many items per page should show up per page + * @return GridFieldConfig_Base + */ + public static function create($itemsPerPage=25){ + return new GridFieldConfig_Base($itemsPerPage=25); } - - public function getCheckboxes() { - return $this->checkboxes; - } - - public function addAffector(GridState_Affector $affector) { - $this->affectors[] = $affector; - return $this; - } - - public function getAffectors() { - return $this->affectors; - } - - public function addDecorator($decorator) { - $this->decorators[] = $decorator; - } - - public function getDecorators() { - return $this->decorators; + + /** + * + * @param int $itemsPerPage - How many items per page should show up + */ + public function __construct($itemsPerPage=25) { + $this->addComponent(new GridFieldSortableHeader()); + $this->addComponent(new GridFieldDefaultColumns()); + $this->addComponent(new GridFieldAction_Edit()); + $this->addComponent(new GridFieldPaginator($itemsPerPage)); + } +} + +/** + * This GridFieldConfig bundles a common set of componentes used for displaying + * a gridfield with: + * + * - Relation adding + * - Sortable header + * - Default columns + * - Edit links on every item + * - Action for removing relationship + * - Paginator + * + */ +class GridFieldConfig_ManyManyEditor extends GridFieldConfig { + + /** + * + * @param string $fieldToSearch - Which field on the object should be searched for + * @param bool $autoSuggest - Show a jquery.ui.autosuggest dropdown field + * @param int $itemsPerPage - How many items per page should show up + * @return GridFieldConfig_ManyManyEditor + */ + public static function create($fieldToSearch, $autoSuggest=true, $itemsPerPage=25){ + return new GridFieldConfig_ManyManyEditor($fieldToSearch, $autoSuggest=true, $itemsPerPage=25); + } + + /** + * + * @param string $fieldToSearch - Which field on the object should be searched for + * @param bool $autoSuggest - Show a jquery.ui.autosuggest dropdown field + * @param int $itemsPerPage - How many items per page should show up + */ + public function __construct($fieldToSearch, $autoSuggest=true, $itemsPerPage=25) { + $this->addComponent(new GridFieldRelationAdd($fieldToSearch, $autoSuggest)); + $this->addComponent(new GridFieldSortableHeader()); + $this->addComponent(new GridFieldDefaultColumns()); + $this->addComponent(new GridFieldAction_Edit()); + $this->addComponent(new GridFieldRelationDelete()); + $this->addComponent(new GridFieldPaginator($itemsPerPage)); } } From 07029138bf2f4c837751b831402cab2f8992bc8c Mon Sep 17 00:00:00 2001 From: Stig Lindqvist Date: Mon, 23 Jan 2012 18:19:27 +0100 Subject: [PATCH 3/6] MINOR Updated docblocks for GridFieldPaginator --- forms/gridfield/GridFieldPaginator.php | 67 +++++++++++++++++++------- 1 file changed, 50 insertions(+), 17 deletions(-) mode change 100644 => 100755 forms/gridfield/GridFieldPaginator.php diff --git a/forms/gridfield/GridFieldPaginator.php b/forms/gridfield/GridFieldPaginator.php old mode 100644 new mode 100755 index 8bb5f7c50..d652e5907 --- a/forms/gridfield/GridFieldPaginator.php +++ b/forms/gridfield/GridFieldPaginator.php @@ -1,35 +1,57 @@ itemsPerPage = $itemsPerPage; } + /** + * + * @param GridField $gridField + * @return array + */ public function getActions($gridField) { return array('paginate'); } + /** + * + * @param GridField $gridField + * @param string $actionName + * @param string $arguments + * @param array $data + * @return void + */ public function handleAction(GridField $gridField, $actionName, $arguments, $data) { if($actionName !== 'paginate') { return; @@ -38,17 +60,12 @@ class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipu $this->currentPage = $state->currentPage = (int)$arguments; } - /** Duck check to see if list support methods we need to paginate */ - protected function getListPaginatable(SS_List $list) { - // If no list yet, not paginatable - if (!$list) return false; - // Check for methods we use - if(!method_exists($list, 'getRange')) return false; - if(!method_exists($list, 'limit')) return false; - // Default it true - return true; - } - + /** + * + * @param GridField $gridField + * @param SS_List $dataList + * @return SS_List + */ public function getManipulatedData(GridField $gridField, SS_List $dataList) { if(!$this->getListPaginatable($dataList)) { return $dataList; @@ -60,6 +77,11 @@ class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipu return $dataList->getRange((int)$startRow, (int)$this->itemsPerPage); } + /** + * + * @param GridField $gridField + * @return array + */ public function getHTMLFragments($gridField) { Requirements::javascript(SAPPHIRE_DIR.'/thirdparty/jquery-entwine/dist/jquery.entwine-dist.js'); Requirements::javascript(SAPPHIRE_DIR.'/javascript/GridField.js'); @@ -87,4 +109,15 @@ class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipu 'footer' => $forTemplate->renderWith('GridFieldPaginator_Row', array('Colspan'=>count($gridField->getColumns()))), ); } + + /** Duck check to see if list support methods we need to paginate */ + protected function getListPaginatable(SS_List $list) { + // If no list yet, not paginatable + if (!$list) return false; + // Check for methods we use + if(!method_exists($list, 'getRange')) return false; + if(!method_exists($list, 'limit')) return false; + // Default it true + return true; + } } From 439ed75ff99414038d32e06b28976160ce7c413f Mon Sep 17 00:00:00 2001 From: Stig Lindqvist Date: Wed, 25 Jan 2012 13:49:09 +1300 Subject: [PATCH 4/6] MINOR Removed unused property on GridFieldFilter --- forms/gridfield/GridFieldFilter.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/forms/gridfield/GridFieldFilter.php b/forms/gridfield/GridFieldFilter.php index cd7e423d8..debed9a0e 100644 --- a/forms/gridfield/GridFieldFilter.php +++ b/forms/gridfield/GridFieldFilter.php @@ -9,12 +9,6 @@ */ class GridFieldFilter implements GridField_HTMLProvider, GridField_DataManipulator, GridField_ActionProvider { - /** - * - * @var string - */ - public static $location = 'head'; - /** * * @param GridField $gridField From dc85fa09b96dc77e8d1c553a78f3cec7150807ec Mon Sep 17 00:00:00 2001 From: Stig Lindqvist Date: Mon, 23 Jan 2012 17:12:49 +0100 Subject: [PATCH 5/6] MINOR SecurityAdmin::getEditForm should have same signature as LeftAndMain::getEditForm --- admin/code/SecurityAdmin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/code/SecurityAdmin.php b/admin/code/SecurityAdmin.php index 795ae41fd..fa0925115 100755 --- a/admin/code/SecurityAdmin.php +++ b/admin/code/SecurityAdmin.php @@ -38,7 +38,7 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider { Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/javascript/SecurityAdmin.js'); } - function getEditForm($id = null) { + public function getEditForm($id = null, $fields = null) { // TODO Duplicate record fetching (see parent implementation) if(!$id) $id = $this->currentPageID(); $form = parent::getEditForm($id); From 009000970381f2b3e2be4a7120223dd51c7425aa Mon Sep 17 00:00:00 2001 From: Russell Michell Date: Mon, 30 Jan 2012 09:26:39 +1300 Subject: [PATCH 6/6] BUGFIX: Added default args to GridFieldPopupForms for AssetAdmin which was casing errors BUGFIX: Added GridFieldFilter() to new GridFieldConfig whuch adds filters and fixes the display MINOR: Added gridfield specific margins to form div to push GridField display down-page (due to GF's sort-fields which will be refactored from new designs) --- css/GridField.css | 5 +++-- filesystem/Folder.php | 2 +- forms/gridfield/GridFieldConfig.php | 1 + scss/GridField.scss | 6 +++++- security/Group.php | 2 +- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/css/GridField.css b/css/GridField.css index 38e41e393..4f4ebcc78 100644 --- a/css/GridField.css +++ b/css/GridField.css @@ -1,4 +1,5 @@ /** Core styles for the basic GridField form field without any specific style. @package sapphire @subpackage scss @todo Add radial gradient to default delete button state @todo Create SASS mixin-function to simply swap the from/to, to to/from colours in grsdient mixins? */ +.cms fieldset.ss-gridfield > div { margin-bottom: 35px; } .cms table.ss-gridfield.field { box-shadow: none; padding: 0; margin: 20px 0 0 0; border-collapse: separate; border-bottom: 0 none; } .cms table.ss-gridfield.field thead { color: #1d2224; background: transparent; } .cms table.ss-gridfield.field tbody { background: #FFF; } @@ -11,7 +12,7 @@ .cms table.ss-gridfield.field tr:first-child { background: transparent; } .cms table.ss-gridfield.field tr.ss-gridfield-even { background: #f2f9fd; } .cms table.ss-gridfield.field tr.ss-gridfield-even.ss-gridfield-last { border-bottom: none; } -.cms table.ss-gridfield.field tr th { font-weight: bold; font-size: 12px; color: #FFF; padding: 0; border-right: 1px solid #85959C; height: 20px; /* Makes it appear as though the text sits over the boundary of the two 's in */ } +.cms table.ss-gridfield.field tr th { font-weight: bold; font-size: 12px; color: #FFF; padding: 0; border-right: 1px solid #85959C; height: 20px; white-space: nowrap; /* Makes it appear as though the text sits over the boundary of the two 's in */ } .cms table.ss-gridfield.field tr th span { display: block; position: relative; left: 20px; top: -7px; width: 100%; } .cms table.ss-gridfield.field tr th div.fieldgroup, .cms table.ss-gridfield.field tr th div.fieldgroup-field { width: auto; } .cms table.ss-gridfield.field tr th div.fieldgroup { min-width: 200px; } @@ -23,7 +24,7 @@ .cms table.ss-gridfield.field tr th.first { -moz-border-radius-topleft: 7px; -webkit-border-top-left-radius: 7px; -o-border-top-left-radius: 7px; -ms-border-top-left-radius: 7px; -khtml-border-top-left-radius: 7px; border-top-left-radius: 7px; } .cms table.ss-gridfield.field tr th.last { -moz-border-radius-topright: 7px; -webkit-border-top-right-radius: 7px; -o-border-top-right-radius: 7px; -ms-border-top-right-radius: 7px; -khtml-border-top-right-radius: 7px; border-top-right-radius: 7px; } .cms table.ss-gridfield.field tr th button, .cms table.ss-gridfield.field tr th button:hover { font-size: 12px; margin-left: -0.9em; border-bottom: 0; border-bottom-left-radius: 0; border-bottom-right-radius: 0; } -.cms table.ss-gridfield.field tr th button.ss-gridfield-sort, .cms table.ss-gridfield.field tr th button:hover.ss-gridfield-sort { text-align: left; padding: 0; color: #FFF; width: 95%; background: transparent; border: 0 none; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; text-shadow: none; } +.cms table.ss-gridfield.field tr th button.ss-gridfield-sort, .cms table.ss-gridfield.field tr th button:hover.ss-gridfield-sort { text-align: left; padding: 0; color: #FFF; min-width: 100px; background: transparent; border: 0 none; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; text-shadow: none; } .cms table.ss-gridfield.field tr th button:hover { color: #CCC !important; /* Not sure why IE think it needs this */ } .cms table.ss-gridfield.field tr th.extra button.ss-ui-button { padding: .3em; line-height: 1; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; position: relative; top: -24px; border: #b1c0c5 solid 10px; border-bottom-width: 0; } .cms table.ss-gridfield.field tr th input.ss-gridfield-sort { position: relative; top: -24px; padding: 2px; width: 65%; margin: 0 auto; border: #b1c0c5 solid 10px; border-bottom: 0; border-bottom-left-radius: 0; border-bottom-right-radius: 0; } diff --git a/filesystem/Folder.php b/filesystem/Folder.php index 850926960..323b2854c 100644 --- a/filesystem/Folder.php +++ b/filesystem/Folder.php @@ -405,7 +405,7 @@ class Folder extends File { $config->addComponent(new GridFieldPaginator(10)); $config->addComponent(new GridFieldAction_Delete()); $config->addComponent(new GridFieldAction_Edit()); - $config->addComponent($gridFieldForm = new GridFieldPopupForms()); + $config->addComponent($gridFieldForm = new GridFieldPopupForms(Controller::curr(), 'EditForm')); $gridFieldForm->setTemplate('CMSGridFieldPopupForms'); $files = DataList::create('File')->filter('ParentID', $this->ID)->exclude('ClassName', 'Folder'); $gridField = new GridField('File','Files', $files, $config); diff --git a/forms/gridfield/GridFieldConfig.php b/forms/gridfield/GridFieldConfig.php index 8e254ea6b..686d91c3c 100755 --- a/forms/gridfield/GridFieldConfig.php +++ b/forms/gridfield/GridFieldConfig.php @@ -98,6 +98,7 @@ class GridFieldConfig_ManyManyEditor extends GridFieldConfig { * @param int $itemsPerPage - How many items per page should show up */ public function __construct($fieldToSearch, $autoSuggest=true, $itemsPerPage=25) { + $this->addComponent(new GridFieldFilter()); $this->addComponent(new GridFieldRelationAdd($fieldToSearch, $autoSuggest)); $this->addComponent(new GridFieldSortableHeader()); $this->addComponent(new GridFieldDefaultColumns()); diff --git a/scss/GridField.scss b/scss/GridField.scss index 3e5e9482e..c09d297a2 100644 --- a/scss/GridField.scss +++ b/scss/GridField.scss @@ -45,6 +45,9 @@ $gf_border_radius: 7px; } .cms { + fieldset.ss-gridfield>div { + margin-bottom: 35px; + } table.ss-gridfield.field { box-shadow: none; padding: 0; @@ -108,6 +111,7 @@ $gf_border_radius: 7px; padding: 0; border-right: 1px solid #85959C; height: 20px; + white-space: nowrap; /* Makes it appear as though the text sits over the boundary of the two 's in */ span { @@ -164,7 +168,7 @@ $gf_border_radius: 7px; text-align: left; padding: 0; color: #FFF; - width: 95%; + min-width: 100px; background: transparent; border: 0 none; @include box-shadow-none; diff --git a/security/Group.php b/security/Group.php index a3c0bcf3f..16980747e 100755 --- a/security/Group.php +++ b/security/Group.php @@ -62,7 +62,7 @@ class Group extends DataObject { public function getCMSFields() { Requirements::javascript(SAPPHIRE_DIR . '/javascript/PermissionCheckboxSetField.js'); - $config = new GridFieldConfig_ManyManyEditor('FirstName', true, 20); + $config = new GridFieldConfig_ManyManyEditor('FirstName', false, 20); $config->addComponent(new GridFieldPopupForms(Controller::curr(), 'EditForm')); $memberList = new GridField('Members','Members', $this->Members(), $config);