diff --git a/css/GridField.css b/css/GridField.css index fd5e1b513..d25eb0b03 100644 --- a/css/GridField.css +++ b/css/GridField.css @@ -81,6 +81,7 @@ Used in side panels and action tabs .cms table.ss-gridfield-table tr th.extra { position: relative; background: #637276; background: rgba(0, 0, 0, 0.7); padding: 5px; border-top: rgba(0, 0, 0, 0.2); } .cms table.ss-gridfield-table tr th.extra input { height: 28px; } .cms table.ss-gridfield-table 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; border-bottom-width: 0; -webkit-border-radius: 2px 2px; -moz-border-radius: 2px / 2px; border-radius: 2px / 2px; } +.cms table.ss-gridfield-table tr th.extra select { margin: 0; } .cms table.ss-gridfield-table tr th.first { -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; border-top-left-radius: 5px; } .cms table.ss-gridfield-table tr th.last { -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; border-top-right-radius: 5px; } .cms table.ss-gridfield-table tr th button#action_gridfield_relationadd:hover { color: #444 !important; /* Not sure why IE think it needs this */ } diff --git a/forms/gridfield/GridField.php b/forms/gridfield/GridField.php index 83b1a3cb9..eb18700e2 100644 --- a/forms/gridfield/GridField.php +++ b/forms/gridfield/GridField.php @@ -54,7 +54,9 @@ class GridField extends FormField { protected $config = null; /** - * The components list + * The components list + * + * @var array */ protected $components = array(); @@ -68,9 +70,14 @@ class GridField extends FormField { /** * Map of callbacks for custom data fields + * + * @var array */ protected $customDataFields = array(); + /** + * @var string + */ protected $name = ''; /** @@ -222,12 +229,14 @@ class GridField extends FormField { * Get the current GridState_Data or the GridState * * @param bool $getData - flag for returning the GridState_Data or the GridState + * * @return GridState_data|GridState */ - public function getState($getData=true) { + public function getState($getData = true) { if($getData) { return $this->state->getData(); } + return $this->state; } @@ -465,11 +474,16 @@ class GridField extends FormField { /** * Add additional calculated data fields to be used on this GridField - * @param array $fields a map of fieldname to callback. The callback will bed passed the record as an argument. + * + * @param array $fields a map of fieldname to callback. The callback will + * be passed the record as an argument. */ public function addDataFields($fields) { - if($this->customDataFields) $this->customDataFields = array_merge($this->customDataFields, $fields); - else $this->customDataFields = $fields; + if($this->customDataFields) { + $this->customDataFields = array_merge($this->customDataFields, $fields); + } else { + $this->customDataFields = $fields; + } } /** @@ -580,9 +594,11 @@ class GridField extends FormField { */ protected function buildColumnDispatch() { $this->columnDispatch = array(); + foreach($this->getComponents() as $item) { if($item instanceof GridField_ColumnProvider) { $columns = $item->getColumnsHandled($this); + foreach($columns as $column) { $this->columnDispatch[$column][] = $item; } @@ -603,14 +619,19 @@ class GridField extends FormField { // Update state from client $state = $this->getState(false); - if(isset($fieldData['GridState'])) $state->setValue($fieldData['GridState']); + + if(isset($fieldData['GridState'])) { + $state->setValue($fieldData['GridState']); + } // Try to execute alter action foreach($data as $k => $v) { if(preg_match('/^action_gridFieldAlterAction\?StateID=(.*)/', $k, $matches)) { $id = $matches[1]; $stateChange = Session::get($id); + $actionName = $stateChange['actionName']; + $args = isset($stateChange['args']) ? $stateChange['args'] : array(); $html = $this->handleAlterAction($actionName, $args, $data); // A field can optionally return its own HTML @@ -750,31 +771,31 @@ class GridField extends FormField { class GridField_FormAction extends FormAction { /** - * * @var GridField */ protected $gridField; /** - * * @var array */ protected $stateValues; /** - * * @var array */ - //protected $stateFields = array(); - - protected $actionName; - protected $args = array(); + /** + * @var string + */ + protected $actionName; + + /** + * @var boolean + */ public $useButtonTag = true; /** - * * @param GridField $gridField * @param type $name * @param type $label @@ -785,11 +806,13 @@ class GridField_FormAction extends FormAction { $this->gridField = $gridField; $this->actionName = $actionName; $this->args = $args; + parent::__construct($name, $title); } /** - * urlencode encodes less characters in percent form than we need - we need everything that isn't a \w + * urlencode encodes less characters in percent form than we need - we + * need everything that isn't a \w. * * @param string $val */ @@ -806,13 +829,17 @@ class GridField_FormAction extends FormAction { return '%'.dechex(ord($match[0])); } + /** + * @return array + */ public function getAttributes() { - // Store state in session, and pass ID to client side + // Store state in session, and pass ID to client side. $state = array( 'grid' => $this->getNameFromParent(), 'actionName' => $this->actionName, 'args' => $this->args, ); + $id = preg_replace('/[^\w]+/', '_', uniqid('', true)); Session::set($id, $state); $actionData['StateID'] = $id; @@ -837,10 +864,12 @@ class GridField_FormAction extends FormAction { protected function getNameFromParent() { $base = $this->gridField; $name = array(); + do { array_unshift($name, $base->getName()); $base = $base->getForm(); } while ($base && !($base instanceof Form)); + return implode('.', $name); } } diff --git a/forms/gridfield/GridFieldExportButton.php b/forms/gridfield/GridFieldExportButton.php index 4c19823c9..1d815e614 100644 --- a/forms/gridfield/GridFieldExportButton.php +++ b/forms/gridfield/GridFieldExportButton.php @@ -119,7 +119,7 @@ class GridFieldExportButton implements GridField_HTMLProvider, GridField_ActionP $fileData .= "\n"; } - $items = $gridField->getList(); + $items = $gridField->getManipulatedList(); // @todo should GridFieldComponents change behaviour based on whether others are available in the config? foreach($gridField->getConfig()->getComponents() as $component){ diff --git a/forms/gridfield/GridFieldPrintButton.php b/forms/gridfield/GridFieldPrintButton.php index 9be52fff0..d7124e462 100644 --- a/forms/gridfield/GridFieldPrintButton.php +++ b/forms/gridfield/GridFieldPrintButton.php @@ -1,17 +1,18 @@ tag below the field + * + * @param GridField + * + * @return array */ public function getHTMLFragments($gridField) { $button = new GridField_FormAction( @@ -45,21 +52,34 @@ class GridFieldPrintButton implements GridField_HTMLProvider, GridField_ActionPr 'print', null ); + $button->setAttribute('data-icon', 'grid_print'); $button->addExtraClass('gridfield-button-print'); - //$button->addExtraClass('no-ajax'); + return array( $this->targetFragment => '

' . $button->Field() . '

', ); } /** - * print is an action button + * Print is an action button. + * + * @param GridField + * + * @return array */ public function getActions($gridField) { return array('print'); } + /** + * Handle the print action. + * + * @param GridField + * @param string + * @param array + * @param array + */ public function handleAction(GridField $gridField, $actionName, $arguments, $data) { if($actionName == 'print') { return $this->handlePrint($gridField); @@ -67,7 +87,10 @@ class GridFieldPrintButton implements GridField_HTMLProvider, GridField_ActionPr } /** - * it is also a URL + * Print is accessible via the url + * + * @param GridField + * @return array */ public function getURLHandlers($gridField) { return array( @@ -82,15 +105,20 @@ class GridFieldPrintButton implements GridField_HTMLProvider, GridField_ActionPr set_time_limit(60); Requirements::clear(); Requirements::css(FRAMEWORK_DIR . '/css/GridField_print.css'); + if($data = $this->generatePrintData($gridField)){ return $data->renderWith("GridField_print"); } } /** - * Export core. - */ - public function generatePrintData($gridField) { + * Return the columns to print + * + * @param GridField + * + * @return array + */ + protected function getPrintColumnsForGridField(GridField $gridField) { if($this->printColumns) { $printColumns = $this->printColumns; } else if($dataCols = $gridField->getConfig()->getComponentByType('GridFieldDataColumns')) { @@ -98,74 +126,93 @@ class GridFieldPrintButton implements GridField_HTMLProvider, GridField_ActionPr } else { $printColumns = singleton($gridField->getModelClass())->summaryFields(); } - - $header = null; - if($this->printHasHeader){ - $header = new ArrayList(); - foreach($printColumns as $field => $label){ - $header->push( - new ArrayData(array( - "CellString" => $label, - )) - ); - } - } - - $items = $gridField->getList(); - foreach($gridField->getConfig()->getComponents() as $component){ - if($component instanceof GridFieldFilterHeader || $component instanceof GridFieldSortableHeader) { - $items = $component->getManipulatedData($gridField, $items); - } - } - - $itemRows = new ArrayList(); - foreach($items as $item) { - $itemRow = new ArrayList(); - foreach($printColumns as $field => $label) { - $value = $gridField->getDataFieldValue($item, $field); - $itemRow->push( - new ArrayData(array( - "CellString" => $value, - )) - ); - } - $itemRows->push(new ArrayData( - array( - "ItemRow" => $itemRow - ) - )); - $item->destroy(); - } - - //get title for the print view + + return $printColumns; + } + + /** + * Return the title of the printed page + * + * @param GridField + * + * @return array + */ + public function getTitle(GridField $gridField) { $form = $gridField->getForm(); $currentController = Controller::curr(); $title = ''; + if(method_exists($currentController, 'Title')) { $title = $currentController->Title(); - }else{ - if($currentController->Title){ + } else { + if ($currentController->Title) { $title = $currentController->Title; - }else{ + } else { if($form->Name()){ $title = $form->Name(); } } } - if($fieldTitle = $gridField->Title()){ - if($title) $title .= " - "; + + if($fieldTitle = $gridField->Title()) { + if($title) { + $title .= " - "; + } + $title .= $fieldTitle; } + + return $title; + } + + /** + * Export core. + * + * @param GridField + */ + public function generatePrintData(GridField $gridField) { + $printColumns = $this->getPrintColumnsForGridField($gridField); - $ret = new ArrayData( - array( - "Title" => $title, - "Header" => $header, - "ItemRows" => $itemRows, - "Datetime" => SS_Datetime::now(), - "Member" => Member::currentUser(), - ) - ); + $header = null; + + if($this->printHasHeader) { + $header = new ArrayList(); + + foreach($printColumns as $field => $label){ + $header->push(new ArrayData(array( + "CellString" => $label, + ))); + } + } + + $items = $gridField->getManipulatedList(); + $itemRows = new ArrayList(); + + foreach($items as $item) { + $itemRow = new ArrayList(); + + foreach($printColumns as $field => $label) { + $value = $gridField->getDataFieldValue($item, $field); + + $itemRow->push(new ArrayData(array( + "CellString" => $value, + ))); + } + + $itemRows->push(new ArrayData(array( + "ItemRow" => $itemRow + ))); + + $item->destroy(); + } + + $ret = new ArrayData(array( + "Title" => $this->getTitle($gridField), + "Header" => $header, + "ItemRows" => $itemRows, + "Datetime" => SS_Datetime::now(), + "Member" => Member::currentUser(), + )); return $ret; } @@ -182,6 +229,7 @@ class GridFieldPrintButton implements GridField_HTMLProvider, GridField_ActionPr */ public function setPrintColumns($cols) { $this->printColumns = $cols; + return $this; } @@ -197,6 +245,7 @@ class GridFieldPrintButton implements GridField_HTMLProvider, GridField_ActionPr */ public function setPrintHasHeader($bool) { $this->printHasHeader = $bool; + return $this; } -} +} \ No newline at end of file diff --git a/forms/gridfield/GridFieldSortableHeader.php b/forms/gridfield/GridFieldSortableHeader.php index 003695b6b..012f48f9d 100644 --- a/forms/gridfield/GridFieldSortableHeader.php +++ b/forms/gridfield/GridFieldSortableHeader.php @@ -83,10 +83,12 @@ class GridFieldSortableHeader implements GridField_HTMLProvider, GridField_DataM $state = $gridField->State->GridFieldSortableHeader; $columns = $gridField->getColumns(); $currentColumn = 0; + foreach($columns as $columnField) { $currentColumn++; $metadata = $gridField->getColumnMetadata($columnField); $title = $metadata['title']; + if(isset($this->fieldSorting[$columnField]) && $this->fieldSorting[$columnField]) { $columnField = $this->fieldSorting[$columnField]; } diff --git a/forms/gridfield/GridState.php b/forms/gridfield/GridState.php index cc102214d..3716ab1f0 100644 --- a/forms/gridfield/GridState.php +++ b/forms/gridfield/GridState.php @@ -1,8 +1,8 @@ getName() . '[GridState]'); } - + /** - * - * @param type $value + * @param mixed $d + * @return object */ - public function setValue($value) { - if (is_string($value)) { - $this->gridStateData = new GridState_Data(json_decode($value, true)); + public static function array_to_object($d) { + if(is_array($d)) { + return (object) array_map(array('GridState', 'array_to_object'), $d); } - parent::setValue($value); - } - - public function getData() { - if(!$this->gridStateData) $this->gridStateData = new GridState_Data; - return $this->gridStateData; + + return $d; } /** - * - * @return type + * @param mixed $value + */ + public function setValue($value) { + if (is_string($value)) { + $this->data = new GridState_Data(json_decode($value, true)); + } + + parent::setValue($value); + } + + /** + * @var GridState_Data + */ + public function getData() { + if(!$this->data) { + $this->data = new GridState_Data(); + } + + return $this->data; + } + + /** + * @return DataList */ public function getList() { return $this->grid->getList(); } - /** @return string */ + /** + * Returns a json encoded string representation of this state. + * + * @return string + */ public function Value() { - if(!$this->gridStateData) { + if(!$this->data) { return json_encode(array()); } - return json_encode($this->gridStateData->toArray()); + + return json_encode($this->data->toArray()); } /** + * Returns a json encoded string representation of this state. * - * @return type + * @return string */ public function dataValue() { return $this->Value(); @@ -100,9 +115,18 @@ class GridState extends HiddenField { } /** - * Simple set of data, similar to stdClass, but without the notice-level errors + * Simple set of data, similar to stdClass, but without the notice-level errors. + * + * @see GridState + * + * @package framework + * @subpackage fields-relational */ class GridState_Data { + + /** + * @var array + */ protected $data; public function __construct($data = array()) { @@ -110,42 +134,53 @@ class GridState_Data { } public function __get($name) { - if(!isset($this->data[$name])) $this->data[$name] = new GridState_Data; - if(is_array($this->data[$name])) $this->data[$name] = new GridState_Data($this->data[$name]); + if(!isset($this->data[$name])) { + $this->data[$name] = new GridState_Data(); + } else if(is_array($this->data[$name])) { + $this->data[$name] = new GridState_Data($this->data[$name]); + } + return $this->data[$name]; } + public function __set($name, $value) { $this->data[$name] = $value; } + public function __isset($name) { return isset($this->data[$name]); } public function __toString() { - if(!$this->data) return ""; - else return json_encode($this->toArray()); + if(!$this->data) { + return ""; + } + + return json_encode($this->toArray()); } public function toArray() { $output = array(); + foreach($this->data as $k => $v) { $output[$k] = (is_object($v) && method_exists($v, 'toArray')) ? $v->toArray() : $v; } + return $output; } } - +/** + * @see GridState + * + * @package framework + * @subpackage fields-relational + */ class GridState_Component implements GridField_HTMLProvider { public function getHTMLFragments($gridField) { - - $forTemplate = new ArrayData(array()); - $forTemplate->Fields = new ArrayList; - return array( 'before' => $gridField->getState(false)->Field() ); } - } diff --git a/javascript/GridField.js b/javascript/GridField.js index c06e3bdbb..6b372d411 100644 --- a/javascript/GridField.js +++ b/javascript/GridField.js @@ -157,16 +157,24 @@ }, onclick: function(e){ var btn = this.closest(':button'), grid = this.getGridField(), - form = this.closest('form'), data = form.find(':input').serialize(); + form = this.closest('form'), data = form.find(':input.gridstate').serialize();; // Add current button - data += '&' + encodeURIComponent(btn.attr('name')) + '=' + encodeURIComponent(btn.val()); + data += "&" + encodeURIComponent(btn.attr('name')) + '=' + encodeURIComponent(btn.val()); - // Include any GET parameters from the current URL, as the view state might depend on it. - // For example, a list prefiltered through external search criteria might be passed to GridField. - if(window.location.search) data = window.location.search.replace(/^\?/, '') + '&' + data; + // Include any GET parameters from the current URL, as the view + // state might depend on it. + // For example, a list prefiltered through external search criteria + // might be passed to GridField. + if(window.location.search) { + data = window.location.search.replace(/^\?/, '') + '&' + data; + } + + var url = $.path.makeUrlAbsolute( + grid.data('url') + '?' + data, + $('base').attr('href') + ); - var url = $.path.makeUrlAbsolute(grid.data('url') + '?' + data, $('base').attr('href')); var newWindow = window.open(url); return false; @@ -188,22 +196,30 @@ /** * Prevents actions from causing an ajax reload of the field. - * Useful e.g. for actions which rely on HTTP response headers being interpreted nativel - * by the browser, like file download triggers. + * + * Useful e.g. for actions which rely on HTTP response headers being + * interpreted natively by the browser, like file download triggers. */ $('.ss-gridfield .action.no-ajax').entwine({ onclick: function(e){ var self = this, btn = this.closest(':button'), grid = this.getGridField(), - form = this.closest('form'), data = form.find(':input').serialize(); + form = this.closest('form'), data = form.find(':input.gridstate').serialize(); // Add current button - data += '&' + encodeURIComponent(btn.attr('name')) + '=' + encodeURIComponent(btn.val()); + data += "&" + encodeURIComponent(btn.attr('name')) + '=' + encodeURIComponent(btn.val()); - // Include any GET parameters from the current URL, as the view state might depend on it. - // For example, a list prefiltered through external search criteria might be passed to GridField. - if(window.location.search) data = window.location.search.replace(/^\?/, '') + '&' + data; + // Include any GET parameters from the current URL, as the view + // state might depend on it. For example, a list pre-filtered + // through external search criteria might be passed to GridField. + if(window.location.search) { + data = window.location.search.replace(/^\?/, '') + '&' + data; + } + + window.location.href = $.path.makeUrlAbsolute( + grid.data('url') + '?' + data, + $('base').attr('href') + ); - window.location.href = $.path.makeUrlAbsolute(grid.data('url') + '?' + data, $('base').attr('href')); return false; } }); @@ -340,7 +356,5 @@ } } }); - }); - -}(jQuery)); +}(jQuery)); \ No newline at end of file diff --git a/scss/GridField.scss b/scss/GridField.scss index 3b3a60c4c..05a57fd4f 100644 --- a/scss/GridField.scss +++ b/scss/GridField.scss @@ -382,9 +382,11 @@ $gf_grid_x: 16px; background: rgba(#000, 0.7); padding: 5px; border-top: $gf_colour_text_shadow; + input { height:28px; //height of input field - to match design. } + button.ss-ui-button { padding: .3em; line-height: 1; @@ -392,7 +394,11 @@ $gf_grid_x: 16px; position: relative; border-bottom-width: 0; @include border-radius(2px, 2px); - } + } + + select { + margin: 0; + } } &.first { @include border-top-left-radius($gf_border_radius); diff --git a/templates/Includes/GridField_print.ss b/templates/Includes/GridField_print.ss index 80450a9cb..8595007fd 100644 --- a/templates/Includes/GridField_print.ss +++ b/templates/Includes/GridField_print.ss @@ -10,9 +10,15 @@ <% loop Header %>$CellString<% end_loop %> + <% if ItemRows %> <% loop ItemRows %> <% loop ItemRow %>$CellString<% end_loop %> <% end_loop %> + <% else %> + +

<% _t('GridField.NoItemsFound', 'No items found') %>

+ + <% end_if %>