From 79d52a2382ff0cca2068a84b574a4e99d68167c3 Mon Sep 17 00:00:00 2001 From: Normann Lou Date: Mon, 5 Mar 2012 12:45:05 +1300 Subject: [PATCH 001/144] ENHANCEMENT SSF-53: put a unlink button in the "Actions" column rather than holding a separate column for easy styling --- forms/gridfield/GridFieldRelationDelete.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/forms/gridfield/GridFieldRelationDelete.php b/forms/gridfield/GridFieldRelationDelete.php index 7de46085e..b2cd41ba8 100644 --- a/forms/gridfield/GridFieldRelationDelete.php +++ b/forms/gridfield/GridFieldRelationDelete.php @@ -14,7 +14,8 @@ class GridFieldRelationDelete implements GridField_ColumnProvider, GridField_Act * @param array $columns */ public function augmentColumns($gridField, &$columns) { - $columns[] = 'UnlinkRelation'; + if(!in_array('Actions', $columns)) + $columns[] = 'Actions'; } /** @@ -37,7 +38,7 @@ class GridFieldRelationDelete implements GridField_ColumnProvider, GridField_Act * @return array */ public function getColumnMetadata($gridField, $columnName) { - if($columnName == 'UnlinkRelation') { + if($columnName == 'Actions') { return array('title' => ''); } } @@ -49,7 +50,7 @@ class GridFieldRelationDelete implements GridField_ColumnProvider, GridField_Act * @return type */ public function getColumnsHandled($gridField) { - return array('UnlinkRelation'); + return array('Actions'); } /** From d135c13c9419d5187c980406c76d6dffd20211c4 Mon Sep 17 00:00:00 2001 From: jbridson Date: Mon, 5 Mar 2012 13:35:39 +1300 Subject: [PATCH 002/144] MINOR:SSF-53 - Changed the icon for the active state of the filter button on user gridfield --- css/GridField.css | 2 +- scss/GridField.scss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/css/GridField.css b/css/GridField.css index 26a7f227b..9ca2d4790 100644 --- a/css/GridField.css +++ b/css/GridField.css @@ -45,7 +45,7 @@ .cms table.ss-gridfield-table tr th button.ss-gridfield-sort.ss-gridfield-sorted-asc { background-position: right -116px; } .cms table.ss-gridfield-table tr th button.ss-gridfield-button-filter.ss-ui-button { position: absolute; right: 7px; top: -28px; display: block; text-indent: -9999em; width: 30px; height: 28px; border-top-left-radius: 0px; border-bottom-left-radius: 0px; border-top-right-radius: 4px; border-bottom-right-radius: 4px; border-bottom-width: 1px; border-color: #9a9a9a; background: url(../images/icons/filter-icons.png) no-repeat -40px 6px, -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #d9d9d9)); background: url(../images/icons/filter-icons.png) no-repeat -40px 6px, -webkit-linear-gradient(#ffffff, #d9d9d9); background: url(../images/icons/filter-icons.png) no-repeat -40px 6px, -moz-linear-gradient(#ffffff, #d9d9d9); background: url(../images/icons/filter-icons.png) no-repeat -40px 6px, -o-linear-gradient(#ffffff, #d9d9d9); background: url(../images/icons/filter-icons.png) no-repeat -40px 6px, -ms-linear-gradient(#ffffff, #d9d9d9); background: url(../images/icons/filter-icons.png) no-repeat -40px 6px, linear-gradient(#ffffff, #d9d9d9); } .cms table.ss-gridfield-table tr th button.ss-gridfield-button-filter.ss-ui-button:hover { background: url(../images/icons/filter-icons.png) no-repeat -16px 6px, -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #4199cd), color-stop(100%, #2e7ead)); background: url(../images/icons/filter-icons.png) no-repeat -16px 6px, -webkit-linear-gradient(#4199cd, #2e7ead); background: url(../images/icons/filter-icons.png) no-repeat -16px 6px, -moz-linear-gradient(#4199cd, #2e7ead); background: url(../images/icons/filter-icons.png) no-repeat -16px 6px, -o-linear-gradient(#4199cd, #2e7ead); background: url(../images/icons/filter-icons.png) no-repeat -16px 6px, -ms-linear-gradient(#4199cd, #2e7ead); background: url(../images/icons/filter-icons.png) no-repeat -16px 6px, linear-gradient(#4199cd, #2e7ead); } -.cms table.ss-gridfield-table tr th button.ss-gridfield-button-filter.ss-ui-button:active { background: url(../images/icons/filter-icons.png) no-repeat -16px -17px, -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #4199cd), color-stop(100%, #2e7ead)); background: url(../images/icons/filter-icons.png) no-repeat -16px -17px, -webkit-linear-gradient(#4199cd, #2e7ead); background: url(../images/icons/filter-icons.png) no-repeat -16px -17px, -moz-linear-gradient(#4199cd, #2e7ead); background: url(../images/icons/filter-icons.png) no-repeat -16px -17px, -o-linear-gradient(#4199cd, #2e7ead); background: url(../images/icons/filter-icons.png) no-repeat -16px -17px, -ms-linear-gradient(#4199cd, #2e7ead); background: url(../images/icons/filter-icons.png) no-repeat -16px -17px, linear-gradient(#4199cd, #2e7ead); -moz-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); -webkit-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); -o-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); } +.cms table.ss-gridfield-table tr th button.ss-gridfield-button-filter.ss-ui-button:active { background: url(../images/icons/filter-icons.png) no-repeat -16px 6px, -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #4199cd), color-stop(100%, #2e7ead)); background: url(../images/icons/filter-icons.png) no-repeat -16px 6px, -webkit-linear-gradient(#4199cd, #2e7ead); background: url(../images/icons/filter-icons.png) no-repeat -16px 6px, -moz-linear-gradient(#4199cd, #2e7ead); background: url(../images/icons/filter-icons.png) no-repeat -16px 6px, -o-linear-gradient(#4199cd, #2e7ead); background: url(../images/icons/filter-icons.png) no-repeat -16px 6px, -ms-linear-gradient(#4199cd, #2e7ead); background: url(../images/icons/filter-icons.png) no-repeat -16px 6px, linear-gradient(#4199cd, #2e7ead); -moz-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); -webkit-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); -o-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); } .cms table.ss-gridfield-table tr th button.ss-gridfield-button-reset.ss-ui-button { position: absolute; right: 36px; top: -28px; display: block; text-indent: -9999em; width: 30px; height: 28px; float: right; border-radius: 0px; border-bottom-width: 1px; border-color: #9a9a9a; background: url(../images/icons/filter-icons.png) no-repeat 8px 5px, -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #d9d9d9)); background: url(../images/icons/filter-icons.png) no-repeat 8px 5px, -webkit-linear-gradient(#ffffff, #d9d9d9); background: url(../images/icons/filter-icons.png) no-repeat 8px 5px, -moz-linear-gradient(#ffffff, #d9d9d9); background: url(../images/icons/filter-icons.png) no-repeat 8px 5px, -o-linear-gradient(#ffffff, #d9d9d9); background: url(../images/icons/filter-icons.png) no-repeat 8px 5px, -ms-linear-gradient(#ffffff, #d9d9d9); background: url(../images/icons/filter-icons.png) no-repeat 8px 5px, linear-gradient(#ffffff, #d9d9d9); } .cms table.ss-gridfield-table tr th button.ss-gridfield-button-reset.ss-ui-button:hover { background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ff0000), color-stop(100%, #cc0000)); background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, -webkit-linear-gradient(#ff0000, #cc0000); background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, -moz-linear-gradient(#ff0000, #cc0000); background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, -o-linear-gradient(#ff0000, #cc0000); background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, -ms-linear-gradient(#ff0000, #cc0000); background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, linear-gradient(#ff0000, #cc0000); } .cms table.ss-gridfield-table tr th button.ss-gridfield-button-reset.ss-ui-button:active { background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ff0000), color-stop(100%, #cc0000)); background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, -webkit-linear-gradient(#ff0000, #cc0000); background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, -moz-linear-gradient(#ff0000, #cc0000); background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, -o-linear-gradient(#ff0000, #cc0000); background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, -ms-linear-gradient(#ff0000, #cc0000); background: url(../images/icons/filter-icons.png) no-repeat 8px -17px, linear-gradient(#ff0000, #cc0000); -moz-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); -webkit-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); -o-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); } diff --git a/scss/GridField.scss b/scss/GridField.scss index ffb28e3d6..f3549b6d1 100644 --- a/scss/GridField.scss +++ b/scss/GridField.scss @@ -290,7 +290,7 @@ $gf_grid_x: 16px; )); } &:active { - @include background (url(../images/icons/filter-icons.png) no-repeat -16px -17px, + @include background (url(../images/icons/filter-icons.png) no-repeat -16px 6px, linear-gradient( lighten($gf_color_button_filter_hover, 5%), darken($gf_color_button_filter_hover, 5%) From 929182f818cbed84c9fcd95ea53c6be395e4f9ea Mon Sep 17 00:00:00 2001 From: Andrew O'Neil Date: Mon, 5 Mar 2012 15:28:25 +1300 Subject: [PATCH 003/144] MINOR Adjust pagination to match design --- forms/gridfield/GridFieldPaginator.php | 64 +++++++++++++++----- templates/Includes/GridFieldPaginator_Row.ss | 6 +- 2 files changed, 51 insertions(+), 19 deletions(-) diff --git a/forms/gridfield/GridFieldPaginator.php b/forms/gridfield/GridFieldPaginator.php index ff7b944d4..bd3125893 100755 --- a/forms/gridfield/GridFieldPaginator.php +++ b/forms/gridfield/GridFieldPaginator.php @@ -83,25 +83,57 @@ class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipu * @return array */ public function getHTMLFragments($gridField) { - $forTemplate = new ArrayData(array()); - $forTemplate->Fields = new ArrayList; - + // Figure out which page and record range we're on $countList = clone $gridField->List; $totalRows = $countList->limit(null)->count(); $totalPages = ceil($totalRows/$this->itemsPerPage); - for($idx=1; $idx<=$totalPages; $idx++) { - if($idx == $this->currentPage) { - $field = new LiteralField('pagination_'.$idx, $idx); - } else { - $field = new GridField_Action($gridField, 'pagination_'.$idx, $idx, 'paginate', $idx); - $field->addExtraClass('ss-gridfield-button'); - } - - $forTemplate->Fields->push($field); - } - if(!$forTemplate->Fields->Count()) { - return array(); - } + $firstShownRecord = ($this->currentPage - 1) * $this->itemsPerPage + 1; + $lastShownRecord = $this->currentPage * $this->itemsPerPage; + if($lastShownRecord > $totalRows) + $lastShownRecord = $totalRows; + + + // Ten pages back button + $prev10PageNum = $this->currentPage - 10 <= 1 ? 1 : $this->currentPage - 10; + $prev10Page = new GridField_Action($gridField, 'pagination_prev10', '-10', 'paginate', 1); + $prev10Page->addExtraClass('ss-gridfield-prev10page'); + if($this->currentPage == 1) + $prev10Page = $prev10Page->performDisabledTransformation(); + + // Previous page button + $previousPageNum = $this->currentPage <= 1 ? 1 : $this->currentPage - 1; + $previousPage = new GridField_Action($gridField, 'pagination_prev', 'Previous', 'paginate', $previousPageNum); + $previousPage->addExtraClass('ss-gridfield-previouspage'); + if($this->currentPage == 1) + $previousPage = $previousPage->performDisabledTransformation(); + + // Next page button + $nextPageNum = $this->currentPage >= $totalPages ? $totalPages : $this->currentPage + 1; + $nextPage = new GridField_Action($gridField, 'pagination_next', 'Next', 'paginate', $nextPageNum); + $nextPage->addExtraClass('ss-gridfield-nextpage'); + if($this->currentPage == $totalPages) + $nextPage = $nextPage->performDisabledTransformation(); + + // Ten pages forward button + $next10PageNum = $this->currentPage + 10 >= $totalPages ? $totalPages : $this->currentPage + 10; + $next10Page = new GridField_Action($gridField, 'pagination_next10', '+10', 'paginate', $next10PageNum); + $next10Page->addExtraClass('ss-gridfield-next10page'); + if($this->currentPage == $totalPages) + $next10Page = $next10Page->performDisabledTransformation(); + + + // Render in template + $forTemplate = new ArrayData(array( + 'Previous10Page' => $prev10Page, + 'PreviousPage' => $previousPage, + 'CurrentPageNum' => $this->currentPage, + 'NumPages' => $totalPages, + 'NextPage' => $nextPage, + 'Next10Page' => $next10Page, + 'FirstShownRecord' => $firstShownRecord, + 'LastShownRecord' => $lastShownRecord, + 'NumRecords' => $totalRows + )); return array( 'footer' => $forTemplate->renderWith('GridFieldPaginator_Row', array('Colspan'=>count($gridField->getColumns()))), ); diff --git a/templates/Includes/GridFieldPaginator_Row.ss b/templates/Includes/GridFieldPaginator_Row.ss index eea032019..d8a555c10 100644 --- a/templates/Includes/GridFieldPaginator_Row.ss +++ b/templates/Includes/GridFieldPaginator_Row.ss @@ -1,7 +1,7 @@ - <% control Fields %> - $Field - <% end_control %> + $Previous10Page $PreviousPage Page $CurrentPageNum of $NumPages $NextPage $Next10Page + + View $FirstShownRecord - $LastShownRecord of $NumRecords \ No newline at end of file From 3ef957b70465e68754043e2166cbfd5e9f3b05db Mon Sep 17 00:00:00 2001 From: Normann Lou Date: Mon, 5 Mar 2012 15:40:12 +1300 Subject: [PATCH 004/144] APICHANGE SSF-53 : add ability to set an customised class of icons for a GridField_Action button --- forms/gridfield/GridField.php | 16 ++++++++++++++++ templates/Includes/GridField_Action.ss | 4 +++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/forms/gridfield/GridField.php b/forms/gridfield/GridField.php index b2f574727..6714edf77 100755 --- a/forms/gridfield/GridField.php +++ b/forms/gridfield/GridField.php @@ -663,6 +663,7 @@ class GridField_Action extends FormAction { * @var string */ protected $buttonLabel; + protected $buttonIcon; /** * @@ -712,6 +713,20 @@ class GridField_Action extends FormAction { public function _nameEncode($match) { return '%'.dechex(ord($match[0])); } + + /** + * buttonIcon setter + */ + public function setButtonIcon($iconName) { + $this->buttonIcon = $iconName; + } + + /** + * buttonIcon getter + */ + public function getButtonIcon(){ + return $this->buttonIcon; + } /** * Default method used by Templates to render the form @@ -748,6 +763,7 @@ class GridField_Action extends FormAction { 'Name' => 'action_gridFieldAlterAction'. '?' . http_build_query($actionData), 'Disabled' => $this->isReadonly(), 'Label' => $this->buttonLabel, + 'Icon' => $this->buttonIcon, 'DataURL' => $this->gridField->Link(), )); diff --git a/templates/Includes/GridField_Action.ss b/templates/Includes/GridField_Action.ss index 0d8b4787d..28c154f2e 100644 --- a/templates/Includes/GridField_Action.ss +++ b/templates/Includes/GridField_Action.ss @@ -1 +1,3 @@ - \ No newline at end of file + \ No newline at end of file From d26e68c41a242701cebbe5f28311afe498162d4e Mon Sep 17 00:00:00 2001 From: Normann Lou Date: Mon, 5 Mar 2012 15:41:54 +1300 Subject: [PATCH 005/144] ENHANCEMENT SSF-53 : add customised icon class for relational 'Find' button and 'Link Existing' button --- forms/gridfield/GridFieldRelationAdd.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/forms/gridfield/GridFieldRelationAdd.php b/forms/gridfield/GridFieldRelationAdd.php index 0878acebf..2dfc185ee 100755 --- a/forms/gridfield/GridFieldRelationAdd.php +++ b/forms/gridfield/GridFieldRelationAdd.php @@ -66,7 +66,9 @@ class GridFieldRelationAdd implements GridField_HTMLProvider, GridField_ActionPr $searchField->addExtraClass('relation-search'); $findAction = new GridField_Action($gridField, 'gridfield_relationfind', _t('GridField.Find', "Find"), 'find', 'find'); + $findAction->setButtonIcon('relationfind'); $addAction = new GridField_Action($gridField, 'gridfield_relationadd', _t('GridField.LinkExisting', "Link Exisiting"), 'addto', 'addto'); + $addAction->setButtonIcon('add'); // If an object is not found, disable the action if(!is_int($gridField->State->GridFieldAddRelation)) { From 8a56f989ac493ee92fa86d97a4478944d4c5d8a0 Mon Sep 17 00:00:00 2001 From: Normann Lou Date: Mon, 5 Mar 2012 15:47:17 +1300 Subject: [PATCH 006/144] MINOR SSF-53 : give "Add New" button an 'add' icon class, give "Link Exisiting" button a 'linkexisting' class --- forms/gridfield/GridFieldRelationAdd.php | 2 +- templates/Includes/GridFieldTitle.ss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/forms/gridfield/GridFieldRelationAdd.php b/forms/gridfield/GridFieldRelationAdd.php index 2dfc185ee..48add63f0 100755 --- a/forms/gridfield/GridFieldRelationAdd.php +++ b/forms/gridfield/GridFieldRelationAdd.php @@ -68,7 +68,7 @@ class GridFieldRelationAdd implements GridField_HTMLProvider, GridField_ActionPr $findAction = new GridField_Action($gridField, 'gridfield_relationfind', _t('GridField.Find', "Find"), 'find', 'find'); $findAction->setButtonIcon('relationfind'); $addAction = new GridField_Action($gridField, 'gridfield_relationadd', _t('GridField.LinkExisting', "Link Exisiting"), 'addto', 'addto'); - $addAction->setButtonIcon('add'); + $addAction->setButtonIcon('linkexisting'); // If an object is not found, disable the action if(!is_int($gridField->State->GridFieldAddRelation)) { diff --git a/templates/Includes/GridFieldTitle.ss b/templates/Includes/GridFieldTitle.ss index f8fb58da9..ef23c0dcf 100644 --- a/templates/Includes/GridFieldTitle.ss +++ b/templates/Includes/GridFieldTitle.ss @@ -1,3 +1,3 @@ -

$Title

<% _t('GridField.AddNew', 'Add New') %> +

$Title

<% _t('GridField.AddNew', 'Add New') %> \ No newline at end of file From 7dd7e60f5aebc27f3a1fd9ba98049c5736585851 Mon Sep 17 00:00:00 2001 From: Normann Lou Date: Mon, 5 Mar 2012 17:28:42 +1300 Subject: [PATCH 007/144] MINOR SSF-53 : add / remove hover-alike class to "Filter" button for easy css styling while keydown on filter input field --- javascript/GridField.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/javascript/GridField.js b/javascript/GridField.js index eb42af07a..abd3b9866 100644 --- a/javascript/GridField.js +++ b/javascript/GridField.js @@ -153,10 +153,14 @@ */ $('.ss-gridfield .filter-header :input').entwine({ onkeydown: function(e) { + btn = this.closest('.fieldgroup').find('.ss-gridfield-button-filter'); if(e.keyCode == '13') { - btn = this.closest('.filter-header').find('.ss-gridfield-button-filter'); - this.getGridField().reload({data: [{name: btn.attr('name'), value: btn.val()}]}); + btns = this.closest('.filter-header').find('.ss-gridfield-button-filter'); + this.getGridField().reload({data: [{name: btns.attr('name'), value: btns.val()}]}); + btn.removeClass('hover-alike'); return false; + }else{ + btn.addClass('hover-alike'); } } }); From 7a97b52a2e7def820dbdd89a404931acac2b0949 Mon Sep 17 00:00:00 2001 From: Normann Lou Date: Mon, 5 Mar 2012 17:30:02 +1300 Subject: [PATCH 008/144] ENHANCEMENT SSF-53 : add customised icon class for relational 'Export to CSV' button. --- forms/gridfield/GridFieldExporter.php | 1 + 1 file changed, 1 insertion(+) diff --git a/forms/gridfield/GridFieldExporter.php b/forms/gridfield/GridFieldExporter.php index f08d7f8a6..b15c27080 100644 --- a/forms/gridfield/GridFieldExporter.php +++ b/forms/gridfield/GridFieldExporter.php @@ -46,6 +46,7 @@ class GridFieldExporter implements GridField_HTMLProvider, GridField_ActionProvi 'export', null ); + $button->setButtonIcon('exportlist'); return array( 'after' => '

' . $button->Field() . '

', ); From c69e949bc9fb80acd789127f9e4d4d4f4bb1e808 Mon Sep 17 00:00:00 2001 From: Ingo Schommer Date: Sun, 4 Mar 2012 21:57:05 +0100 Subject: [PATCH 009/144] MINOR Show all "can view" options in page settings in their own line, as it makes it easier to scan available options --- admin/css/screen.css | 3 +++ admin/scss/_style.scss | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/admin/css/screen.css b/admin/css/screen.css index 7c64a96de..6b431e2ae 100644 --- a/admin/css/screen.css +++ b/admin/css/screen.css @@ -389,6 +389,9 @@ form.member-profile-form .ui-corner-all, form.member-profile-form .ui-corner-top .cms .cms-content { border-right: 1px solid rgba(201, 205, 206, 0.8); -moz-box-shadow: 3px 0 4px rgba(0, 0, 0, 0.15); -webkit-box-shadow: 3px 0 4px rgba(0, 0, 0, 0.15); -o-box-shadow: 3px 0 4px rgba(0, 0, 0, 0.15); box-shadow: 3px 0 4px rgba(0, 0, 0, 0.15); -moz-border-radius: 0; -webkit-border-radius: 0; -o-border-radius: 0; -ms-border-radius: 0; -khtml-border-radius: 0; border-radius: 0; } .cms .cms-content-fields { /* always show a y scroll bar as popups like TreeDropdowns can trigger longer pages and the extra scroll bar doesn't fire our sizing bar */ overflow-y: auto; overflow-x: auto; background: none; width: 100%; } +/** -------------------------------------------- "Settings" Form -------------------------------------------- */ +#CanViewType .optionset li, #CanEditType .optionset li, #CanCreateTopLevelType .optionset li { float: none; width: auto; } + /** -------------------------------------------- Panels -------------------------------------------- */ .cms-panel { overflow: hidden; } .cms-panel .cms-panel-toggle { -moz-box-shadow: 0 0 5px rgba(107, 120, 123, 0.5); -webkit-box-shadow: 0 0 5px rgba(107, 120, 123, 0.5); -o-box-shadow: 0 0 5px rgba(107, 120, 123, 0.5); box-shadow: 0 0 5px rgba(107, 120, 123, 0.5); } diff --git a/admin/scss/_style.scss b/admin/scss/_style.scss index 3bf2ec065..e55f5a69f 100644 --- a/admin/scss/_style.scss +++ b/admin/scss/_style.scss @@ -888,6 +888,17 @@ form.member-profile-form { } } +/** -------------------------------------------- + * "Settings" Form + * -------------------------------------------- */ +#CanViewType, #CanEditType, #CanCreateTopLevelType { + .optionset li { + // All options on their own line + float: none; + width: auto; + } +} + /** -------------------------------------------- * Panels * -------------------------------------------- */ From e8f9624b42f9764de21c6e9552f55b18f9b48f82 Mon Sep 17 00:00:00 2001 From: Ingo Schommer Date: Sun, 4 Mar 2012 21:58:00 +0100 Subject: [PATCH 010/144] BUGFIX Moved window.beforeunload handler out of onmatch(), was applied multiple times and causing unsaved changes confirmation messages on forms which were no longer loaded --- admin/javascript/LeftAndMain.EditForm.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/admin/javascript/LeftAndMain.EditForm.js b/admin/javascript/LeftAndMain.EditForm.js index 2649c1d2e..3debadd79 100644 --- a/admin/javascript/LeftAndMain.EditForm.js +++ b/admin/javascript/LeftAndMain.EditForm.js @@ -2,6 +2,14 @@ * File: LeftAndMain.EditForm.js */ (function($) { + + // Can't bind this through jQuery + window.onbeforeunload = function(e) { + var form = $('.cms-edit-form'); + form.trigger('beforesave'); + if(form.is('.changed')) return ss.i18n._t('LeftAndMain.CONFIRMUNSAVEDSHORT'); + }; + $.entwine('ss', function($){ /** @@ -52,12 +60,6 @@ this._setupChangeTracker(); - // Can't bind this through jQuery - window.onbeforeunload = function(e) { - self.trigger('beforesave'); - if(self.is('.changed')) return ss.i18n._t('LeftAndMain.CONFIRMUNSAVEDSHORT'); - }; - // Catch navigation events before they reach handleStateChange(), // in order to avoid changing the menu state if the action is cancelled by the user // $('.cms-menu') From a6a7b2e469cf5c937e8c0ed5a8610635c1e0e42f Mon Sep 17 00:00:00 2001 From: Ingo Schommer Date: Sun, 4 Mar 2012 22:07:36 +0100 Subject: [PATCH 011/144] MINOR Fixed viewer/editor groups field widths to work with chosen.js UI component --- admin/css/screen.css | 2 ++ admin/scss/_style.scss | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/admin/css/screen.css b/admin/css/screen.css index 6b431e2ae..aeb297093 100644 --- a/admin/css/screen.css +++ b/admin/css/screen.css @@ -392,6 +392,8 @@ form.member-profile-form .ui-corner-all, form.member-profile-form .ui-corner-top /** -------------------------------------------- "Settings" Form -------------------------------------------- */ #CanViewType .optionset li, #CanEditType .optionset li, #CanCreateTopLevelType .optionset li { float: none; width: auto; } +#ViewerGroups select, #EditorGroups select, #CreateTopLevelGroups select { width: 512px; } + /** -------------------------------------------- Panels -------------------------------------------- */ .cms-panel { overflow: hidden; } .cms-panel .cms-panel-toggle { -moz-box-shadow: 0 0 5px rgba(107, 120, 123, 0.5); -webkit-box-shadow: 0 0 5px rgba(107, 120, 123, 0.5); -o-box-shadow: 0 0 5px rgba(107, 120, 123, 0.5); box-shadow: 0 0 5px rgba(107, 120, 123, 0.5); } diff --git a/admin/scss/_style.scss b/admin/scss/_style.scss index e55f5a69f..01328a28c 100644 --- a/admin/scss/_style.scss +++ b/admin/scss/_style.scss @@ -898,6 +898,13 @@ form.member-profile-form { width: auto; } } +#ViewerGroups, #EditorGroups, #CreateTopLevelGroups { + select { + // Fix for chosen.js width detection on hidden elements. + // TODO Remove once .field styling in _form.scss is refactored to allow flexible field widths + width: $grid-x * 64; + } +} /** -------------------------------------------- * Panels From eab8401c7d8acd39d8a40ac71ab965cd80671d78 Mon Sep 17 00:00:00 2001 From: Ingo Schommer Date: Mon, 5 Mar 2012 09:21:24 +0100 Subject: [PATCH 012/144] MINOR Applying chosen.js behaviour to all element. --- admin/javascript/LeftAndMain.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/admin/javascript/LeftAndMain.js b/admin/javascript/LeftAndMain.js index 74e8a05a3..0ace013e6 100644 --- a/admin/javascript/LeftAndMain.js +++ b/admin/javascript/LeftAndMain.js @@ -363,10 +363,11 @@ jQuery.noConflict(); * we can fix the height cropping. */ - $('.cms .field.dropdown, .cms .field.checkboxset').entwine({ + $('.cms .field.dropdown select, .cms .field select[multiple]').entwine({ onmatch: function() { - $(this).find("select:not(.no-chzn)").data('placeholder', ' ').chosen(); - $(this).addClass("has-chzn"); + if(this.is('.no-chzn')) return; + + this.data('placeholder', ' ').chosen().addClass("has-chzn"); this._super(); } From 6e3ceefbb86b048bb9b12b414103fa578a8f2504 Mon Sep 17 00:00:00 2001 From: Ingo Schommer Date: Mon, 5 Mar 2012 10:55:09 +0100 Subject: [PATCH 013/144] ENHANCEMENT Relationship saving in ListboxField (preparing for chosen.js usage), escaping commas in payload when serialising multiple values into a single field --- forms/ListboxField.php | 58 +++++++++++++++++++++++++--- tests/forms/ListboxFieldTest.php | 66 +++++++++++++++++++++++++++++++- tests/forms/ListboxFieldTest.yml | 13 +++++++ 3 files changed, 130 insertions(+), 7 deletions(-) create mode 100644 tests/forms/ListboxFieldTest.yml diff --git a/forms/ListboxField.php b/forms/ListboxField.php index 81701bcb0..a0423a696 100644 --- a/forms/ListboxField.php +++ b/forms/ListboxField.php @@ -142,19 +142,65 @@ class ListboxField extends DropdownField { return $this; } - + /** - * @return String + * Return the CheckboxSetField value as a string + * selected item keys. + * + * @return string */ function dataValue() { - if($this->value && $this->multiple && is_array($this->value)) { - return implode(',', $this->value); + if($this->value && is_array($this->value) && $this->multiple) { + $filtered = array(); + foreach($this->value as $item) { + if($item) { + $filtered[] = str_replace(",", "{comma}", $item); + } + } + return implode(',', $filtered); } else { return parent::dataValue(); } } - function setValue($val) { + /** + * Save the current value of this field into a DataObject. + * If the field it is saving to is a has_many or many_many relationship, + * it is saved by setByIDList(), otherwise it creates a comma separated + * list for a standard DB text/varchar field. + * + * @param DataObject $record The record to save into + */ + function saveInto(DataObject $record) { + if($this->multiple) { + $fieldname = $this->name; + if($fieldname && $record && ($record->has_many($fieldname) || $record->many_many($fieldname))) { + $idList = (is_array($this->value)) ? array_values($this->value) : array(); + $record->$fieldname()->setByIDList($idList); + } elseif($fieldname && $record) { + if($this->value) { + $this->value = str_replace(',', '{comma}', $this->value); + $record->$fieldname = implode(",", $this->value); + } else { + $record->$fieldname = null; + } + } + } else { + parent::saveInto($record); + } + } + + /** + * Load a value into this CheckboxSetField + */ + function setValue($val, $obj = null) { + // If we're not passed a value directly, + // we can look for it in a relation method on the object passed as a second arg + if(!$val && $obj && $obj instanceof DataObject && $obj->hasMethod($this->name)) { + $funcName = $this->name; + $val = array_values($obj->$funcName()->getIDList()); + } + if($val) { if(!$this->multiple && is_array($val)) { throw new InvalidArgumentException('No array values allowed with multiple=false'); @@ -187,4 +233,4 @@ class ListboxField extends DropdownField { return $this; } -} +} \ No newline at end of file diff --git a/tests/forms/ListboxFieldTest.php b/tests/forms/ListboxFieldTest.php index 3c9d18705..a35e33857 100644 --- a/tests/forms/ListboxFieldTest.php +++ b/tests/forms/ListboxFieldTest.php @@ -5,8 +5,28 @@ */ class ListboxFieldTest extends SapphireTest { + + static $fixture_file = 'ListboxFieldTest.yml'; - protected $extraDataObjects = array('ListboxFieldTest_DataObject'); + protected $extraDataObjects = array('ListboxFieldTest_DataObject', 'ListboxFieldTest_Article', 'ListboxFieldTest_Tag'); + + function testFieldWithManyManyRelationship() { + $articleWithTags = $this->objFromFixture('ListboxFieldTest_Article', 'articlewithtags'); + $tag1 = $this->objFromFixture('ListboxFieldTest_Tag', 'tag1'); + $tag2 = $this->objFromFixture('ListboxFieldTest_Tag', 'tag2'); + $tag3 = $this->objFromFixture('ListboxFieldTest_Tag', 'tag3'); + $field = new ListboxField("Tags", "Test field", DataObject::get("ListboxFieldTest_Tag")->map()->toArray()); + $field->setMultiple(true); + $field->setValue(null, $articleWithTags); + + $p = new CSSContentParser($field->Field()); + $tag1xml = $p->getByXpath('//option[@value=' . $tag1->ID . ']'); + $tag2xml = $p->getByXpath('//option[@value=' . $tag2->ID . ']'); + $tag3xml = $p->getByXpath('//option[@value=' . $tag3->ID . ']'); + $this->assertEquals('selected', (string)$tag1xml[0]['selected']); + $this->assertEquals('selected', (string)$tag2xml[0]['selected']); + $this->assertNull($tag3xml[0]['selected']); + } function testSaveIntoNullValueWithMultipleOff() { $choices = array('a' => 'a value', 'b' => 'b value','c' => 'c value'); @@ -62,6 +82,33 @@ class ListboxFieldTest extends SapphireTest { $field->saveInto($obj2); $this->assertEquals('a,c', $obj2->Choices); } + + function testSaveIntoManyManyRelation() { + $article = $this->objFromFixture('ListboxFieldTest_Article', 'articlewithouttags'); + $articleWithTags = $this->objFromFixture('ListboxFieldTest_Article', 'articlewithtags'); + $tag1 = $this->objFromFixture('ListboxFieldTest_Tag', 'tag1'); + $tag2 = $this->objFromFixture('ListboxFieldTest_Tag', 'tag2'); + $field = new ListboxField("Tags", "Test field", DataObject::get("ListboxFieldTest_Tag")->map()->toArray()); + $field->setMultiple(true); + + // Save new relations + $field->setValue(array($tag1->ID,$tag2->ID)); + $field->saveInto($article); + $article = Dataobject::get_by_id('ListboxFieldTest_Article', $article->ID, false); + $this->assertEquals(array($tag1->ID, $tag2->ID), $article->Tags()->column('ID')); + + // Remove existing relation + $field->setValue(array($tag1->ID)); + $field->saveInto($article); + $article = Dataobject::get_by_id('ListboxFieldTest_Article', $article->ID, false); + $this->assertEquals(array($tag1->ID), $article->Tags()->column('ID')); + + // Set NULL value + $field->setValue(null); + $field->saveInto($article); + $article = Dataobject::get_by_id('ListboxFieldTest_Article', $article->ID, false); + $this->assertEquals(array(), $article->Tags()->column('ID')); + } /** * @expectedException InvalidArgumentException @@ -130,4 +177,21 @@ class ListboxFieldTest_DataObject extends DataObject implements TestOnly { static $db = array( 'Choices' => 'Text' ); +} + +class ListboxFieldTest_Article extends DataObject implements TestOnly { + static $db = array( + "Content" => "Text", + ); + + static $many_many = array( + "Tags" => "ListboxFieldTest_Tag", + ); + +} + +class ListboxFieldTest_Tag extends DataObject implements TestOnly { + static $belongs_many_many = array( + 'Articles' => 'ListboxFieldTest_Article' + ); } \ No newline at end of file diff --git a/tests/forms/ListboxFieldTest.yml b/tests/forms/ListboxFieldTest.yml new file mode 100644 index 000000000..a6ac056fa --- /dev/null +++ b/tests/forms/ListboxFieldTest.yml @@ -0,0 +1,13 @@ +ListboxFieldTest_Tag: + tag1: + Title: Tag 1 + tag2: + Title: Tag 2 + tag3: + Title: Tag 3 +ListboxFieldTest_Article: + articlewithouttags: + Content: Article 1 + articlewithtags: + Content: Article 2 + Tags: =>ListboxFieldTest_Tag.tag1,=>ListboxFieldTest_Tag.tag2 \ No newline at end of file From e8ad2c2173ac541b57c2267aed390ac7e858a1b5 Mon Sep 17 00:00:00 2001 From: Ingo Schommer Date: Mon, 5 Mar 2012 10:55:32 +0100 Subject: [PATCH 014/144] MINOR Using ListboxField instead of CheckboxSetField in Member->getCMSFields() for group assignment --- security/Member.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/security/Member.php b/security/Member.php index 51b0128be..5203e28ab 100644 --- a/security/Member.php +++ b/security/Member.php @@ -1138,9 +1138,8 @@ class Member extends DataObject { $groupsMap = DataList::create('Group')->map('ID', 'Breadcrumbs')->toArray(); asort($groupsMap); $fields->addFieldToTab('Root.Main', - Object::create('CheckboxSetField', 'Groups', singleton('Group')->i18n_plural_name()) - ->setTemplate('CheckboxSetField_Select') - ->setSource($groupsMap) + Object::create('ListboxField', 'Groups', singleton('Group')->i18n_plural_name()) + ->setMultiple(true)->setSource($groupsMap) ); // Add permission field (readonly to avoid complicated group assignment logic). From 344899ab77234b95c56afee509505a59ad1c8658 Mon Sep 17 00:00:00 2001 From: Ingo Schommer Date: Mon, 5 Mar 2012 10:55:44 +0100 Subject: [PATCH 015/144] Revert "ENHANCEMENT Support numeric array values in CheckboxSetField (?Field[]=val1&Field[]=val2 instead of ?Field[val1]=1&Field[val2]=1)" - data handling with numeric/associative arrays too clumsy, fixed up ListboxField instead This reverts commit 8fa266462f2ef3aac5065045649f75f776498728. --- forms/CheckboxSetField.php | 15 ++++-------- tests/forms/CheckboxSetFieldTest.php | 36 +--------------------------- tests/forms/CheckboxSetFieldTest.yml | 13 ++++++++-- 3 files changed, 17 insertions(+), 47 deletions(-) diff --git a/forms/CheckboxSetField.php b/forms/CheckboxSetField.php index 2c0a44bed..07e6d100d 100644 --- a/forms/CheckboxSetField.php +++ b/forms/CheckboxSetField.php @@ -184,16 +184,10 @@ class CheckboxSetField extends OptionsetField { $fieldname = $this->name ; if($fieldname && $record && ($record->has_many($fieldname) || $record->many_many($fieldname))) { $idList = array(); - // Works for both style - array('val1' => true, 'val2' => true). - // The tag containing all the appropriate