diff --git a/forms/gridfield/GridField.php b/forms/gridfield/GridField.php index 43bfc9b02..f6e00fab3 100644 --- a/forms/gridfield/GridField.php +++ b/forms/gridfield/GridField.php @@ -1,25 +1,26 @@ * $gridField = new GridField('ExampleGrid', 'Example grid', new DataList('Page')); * - * + * * @see SS_List - * + * * @package forms * @subpackage fields-gridfield */ class GridField extends FormField { - + /** * * @var array @@ -28,46 +29,49 @@ class GridField extends FormField { 'index', 'gridFieldAlterAction' ); - + /** * The datasource + * * @var SS_List */ protected $list = null; /** * The classname of the DataObject that the GridField will display. Defaults to the value of $this->list->dataClass + * * @var string */ protected $modelClassName = ''; /** * the current state of the GridField + * * @var GridState */ protected $state = null; - + /** * * @var GridFieldConfig */ protected $config = null; - + /** * The components list * * @var array */ protected $components = array(); - + /** * Internal dispatcher for column handlers. * Keys are column names and values are GridField_ColumnProvider objects - * + * * @var array */ protected $columnDispatch = null; - + /** * Map of callbacks for custom data fields * @@ -99,37 +103,38 @@ class GridField extends FormField { $this->setConfig($config ?: GridFieldConfig_Base::create()); $this->config->addComponent(new GridState_Component()); - $this->state = new GridState($this); - + $this->state = new GridState($this); + $this->addExtraClass('ss-gridfield'); } public function index($request) { return $this->gridFieldAlterAction(array(), $this->getForm(), $request); } - + /** * Set the modelClass (dataobject) that this field will get it column headers from. * If no $displayFields has been set, the displayfields will be fetched from * this modelclass $summary_fields - * + * * @param string $modelClassName + * * @see GridFieldDataColumns::getDisplayFields() */ public function setModelClass($modelClassName) { $this->modelClassName = $modelClassName; return $this; } - + /** * Returns a dataclass that is a DataObject type that this GridField should look like. - * + * * @throws Exception * @return string */ public function getModelClass() { - if ($this->modelClassName) return $this->modelClassName; - if ($this->list && method_exists($this->list, 'dataClass')) { + if($this->modelClassName) return $this->modelClassName; + if($this->list && method_exists($this->list, 'dataClass')) { $class = $this->list->dataClass(); if($class) return $class; } @@ -149,6 +154,7 @@ class GridField extends FormField { /** * @param GridFieldConfig $config + * * @return GridField */ public function setConfig(GridFieldConfig $config) { @@ -159,12 +165,13 @@ class GridField extends FormField { public function getComponents() { return $this->config->getComponents(); } - + /** * Cast a arbitrary value with the help of a castingDefintion - * - * @param $value + * + * @param $value * @param $castingDefinition + * * @todo refactor this into GridFieldComponent */ public function getCastedValue($value, $castingDefinition) { @@ -175,21 +182,21 @@ class GridField extends FormField { } else { $castingParams = array(); } - - if(strpos($castingDefinition,'->') === false) { + + if(strpos($castingDefinition, '->') === false) { $castingFieldType = $castingDefinition; $castingField = DBField::create_field($castingFieldType, $value); - $value = call_user_func_array(array($castingField,'XML'),$castingParams); + $value = call_user_func_array(array($castingField, 'XML'), $castingParams); } else { $fieldTypeParts = explode('->', $castingDefinition); - $castingFieldType = $fieldTypeParts[0]; + $castingFieldType = $fieldTypeParts[0]; $castingMethod = $fieldTypeParts[1]; $castingField = DBField::create_field($castingFieldType, $value); - $value = call_user_func_array(array($castingField,$castingMethod),$castingParams); + $value = call_user_func_array(array($castingField, $castingMethod), $castingParams); } - + return $value; - } + } /** * Set the datasource @@ -224,7 +231,7 @@ class GridField extends FormField { } return $list; } - + /** * Get the current GridState_Data or the GridState * @@ -262,7 +269,7 @@ class GridField extends FormField { // Get data $list = $this->getManipulatedList(); - + // Render headers, footers, etc $content = array( "before" => "", @@ -271,7 +278,7 @@ class GridField extends FormField { "footer" => "", ); - foreach($this->getComponents() as $item) { + foreach($this->getComponents() as $item) { if($item instanceof GridField_HTMLProvider) { $fragments = $item->getHTMLFragments($this); if($fragments) foreach($fragments as $k => $v) { @@ -290,10 +297,10 @@ class GridField extends FormField { // Nested dependencies are handled by deferring the rendering of any content item that // Circular dependencies are detected by disallowing any item to be deferred more than 5 times // It's a fairly crude algorithm but it works - + $fragmentDefined = array('header' => true, 'footer' => true, 'before' => true, 'after' => true); reset($content); - while(list($k,$v) = each($content)) { + while(list($k, $v) = each($content)) { if(preg_match_all('/\$DefineFragment\(([a-z0-9\-_]+)\)/i', $v, $matches)) { foreach($matches[1] as $match) { $fragmentName = strtolower($match); @@ -305,10 +312,10 @@ class GridField extends FormField { if(preg_match('/\$DefineFragment\(([a-z0-9\-_]+)\)/i', $fragment, $matches)) { // If we've already deferred this fragment, then we have a circular dependency if(isset($fragmentDeferred[$k]) && $fragmentDeferred[$k] > 5) { - throw new LogicException("GridField HTML fragment '$fragmentName' and '$matches[1]' " . + throw new LogicException("GridField HTML fragment '$fragmentName' and '$matches[1]' " . "appear to have a circular dependency."); } - + // Otherwise we can push to the end of the content array unset($content[$k]); $content[$k] = $v; @@ -331,7 +338,7 @@ class GridField extends FormField { foreach($content as $k => $v) { if(empty($fragmentDefined[$k])) { throw new LogicException("GridField HTML fragment '$k' was given content," - . " but not defined. Perhaps there is a supporting GridField component you need to add?"); + . " but not defined. Perhaps there is a supporting GridField component you need to add?"); } } @@ -345,30 +352,22 @@ class GridField extends FormField { $rowContent = ''; foreach($this->getColumns() as $column) { $colContent = $this->getColumnContent($record, $column); + // A return value of null means this columns should be skipped altogether. - if($colContent === null) continue; + if($colContent === null) { + continue; + } + $colAttributes = $this->getColumnAttributes($record, $column); - $rowContent .= FormField::create_tag('td', $colAttributes, $colContent); + + $rowContent .= $this->newCell($this, $total, $idx, $record, $colAttributes, $colContent); } - $classes = array('ss-gridfield-item'); - if ($idx == 0) $classes[] = 'first'; - if ($idx == $total-1) $classes[] = 'last'; - $classes[] = ($idx % 2) ? 'even' : 'odd'; - $row = FormField::create_tag( - 'tr', - array( - "class" => implode(' ', $classes), - 'data-id' => $record->ID, - // TODO Allow per-row customization similar to GridFieldDataColumns - 'data-class' => $record->ClassName, - ), - $rowContent - ); - $rows[] = $row; + + $rows[] = $this->newRow($this, $total, $idx, $record, array(), $rowContent); } $content['body'] = implode("\n", $rows); - } - + } + // Display a message when the grid field is empty if(!(isset($content['body']) && $content['body'])) { $content['body'] = FormField::create_tag( @@ -395,7 +394,7 @@ class GridField extends FormField { $this->addExtraClass('ss-gridfield field'); $attrs = array_diff_key( - $this->getAttributes(), + $this->getAttributes(), array('value' => false, 'type' => false, 'name' => false) ); $attrs['data-name'] = $this->getName(); @@ -403,25 +402,77 @@ class GridField extends FormField { 'id' => isset($this->id) ? $this->id : null, 'class' => 'ss-gridfield-table', 'cellpadding' => '0', - 'cellspacing' => '0' + 'cellspacing' => '0' ); if($this->getDescription()) { $content['after'] .= FormField::create_tag( - 'span', - array('class' => 'description'), + 'span', + array('class' => 'description'), $this->getDescription() ); } return - FormField::create_tag('fieldset', $attrs, + FormField::create_tag('fieldset', $attrs, $content['before'] . - FormField::create_tag('table', $tableAttrs, $head."\n".$foot."\n".$body) . + FormField::create_tag('table', $tableAttrs, $head . "\n" . $foot . "\n" . $body) . $content['after'] ); } - + + /** + * @param GridField $gridfield + * @param int $total + * @param int $index + * @param DataObject $record + * @param array $attributes + * @param string $content + * + * @return string + */ + protected function newCell($gridfield, $total, $index, $record, $attributes, $content) { + return FormField::create_tag( + 'td', + $attributes, + $content + ); + } + + /** + * @param GridField $gridfield + * @param int $total + * @param int $index + * @param DataObject $record + * @param array $attributes + * @param string $content + * + * @return string + */ + protected function newRow($gridfield, $total, $index, $record, $attributes, $content) { + $classes = array('ss-gridfield-item'); + + if($index == 0) { + $classes[] = 'first'; + } + + if($index == $total - 1) { + $classes[] = 'last'; + } + + $classes[] = ($index % 2) ? 'even' : 'odd'; + + return FormField::create_tag( + 'tr', + array( + 'class' => implode(' ', $classes), + 'data-id' => $record->ID, + 'data-class' => $record->ClassName, + ), + $content + ); + } + public function Field($properties = array()) { return $this->FieldHolder($properties); } @@ -452,6 +503,7 @@ class GridField extends FormField { * * @param DataObject $record * @param string $column + * * @return string * @throws InvalidArgumentException */ @@ -460,7 +512,7 @@ class GridField extends FormField { if(!$this->columnDispatch) { $this->buildColumnDispatch(); } - + if(!empty($this->columnDispatch[$column])) { $content = ""; foreach($this->columnDispatch[$column] as $handler) { @@ -471,12 +523,12 @@ class GridField extends FormField { throw new InvalidArgumentException("Bad column '$column'"); } } - + /** * Add additional calculated data fields to be used on this GridField * * @param array $fields a map of fieldname to callback. The callback will - * be passed the record as an argument. + * be passed the record as an argument. */ public function addDataFields($fields) { if($this->customDataFields) { @@ -485,7 +537,7 @@ class GridField extends FormField { $this->customDataFields = $fields; } } - + /** * Get the value of a named field on the given record. * Use of this method ensures that any special rules around the data for this gridfield are followed. @@ -496,7 +548,7 @@ class GridField extends FormField { $callback = $this->customDataFields[$fieldName]; return $callback($record); } - + // Default implementation if($record->hasMethod('relField')) { return $record->relField($fieldName); @@ -512,6 +564,7 @@ class GridField extends FormField { * * @param DataObject $record * @param string $column + * * @return array * @throws LogicException * @throws InvalidArgumentException @@ -521,7 +574,7 @@ class GridField extends FormField { if(!$this->columnDispatch) { $this->buildColumnDispatch(); } - + if(!empty($this->columnDispatch[$column])) { $attrs = array(); @@ -546,6 +599,7 @@ class GridField extends FormField { * Get metadata for a column, example array('Title'=>'Email address') * * @param string $column + * * @return array * @throws LogicException * @throws InvalidArgumentException @@ -555,22 +609,22 @@ class GridField extends FormField { if(!$this->columnDispatch) { $this->buildColumnDispatch(); } - + if(!empty($this->columnDispatch[$column])) { $metadata = array(); foreach($this->columnDispatch[$column] as $handler) { $column_metadata = $handler->getColumnMetadata($this, $column); - + if(is_array($column_metadata)) { $metadata = array_merge($metadata, $column_metadata); } else { $methodSignature = get_class($handler) . "::getColumnMetadata()"; throw new LogicException("Non-array response from $methodSignature."); } - + } - + return $metadata; } throw new InvalidArgumentException("Bad column '$column'"); @@ -584,13 +638,13 @@ class GridField extends FormField { public function getColumnCount() { // Build the column dispatch if(!$this->columnDispatch) $this->buildColumnDispatch(); - return count($this->columnDispatch); + return count($this->columnDispatch); } /** * Build an columnDispatch that maps a GridField_ColumnProvider to a column * for reference later - * + * */ protected function buildColumnDispatch() { $this->columnDispatch = array(); @@ -610,7 +664,8 @@ class GridField extends FormField { * This is the action that gets executed when a GridField_AlterAction gets clicked. * * @param array $data - * @return string + * + * @return string */ public function gridFieldAlterAction($data, $form, SS_HTTPRequest $request) { $html = ''; @@ -639,7 +694,7 @@ class GridField extends FormField { if($html) return $html; } } - + switch($request->getHeader('X-Pjax')) { case 'CurrentField': return $this->FieldHolder(); @@ -661,6 +716,7 @@ class GridField extends FormField { * @param string $actionName * @param mixed $args * @param arrray $data - send data from a form + * * @return type * @throws InvalidArgumentException */ @@ -670,17 +726,17 @@ class GridField extends FormField { if(!($component instanceof GridField_ActionProvider)) { continue; } - - if(in_array($actionName, array_map('strtolower', (array)$component->getActions($this)))) { + + if(in_array($actionName, array_map('strtolower', (array) $component->getActions($this)))) { return $component->handleAction($this, $actionName, $args, $data); } } throw new InvalidArgumentException("Can't handle action '$actionName'"); } - + /** * Custom request handler that will check component handlers before proceeding to the default implementation. - * + * * @todo There is too much code copied from RequestHandler here. */ public function handleRequest(SS_HTTPRequest $request, DataModel $model) { @@ -693,18 +749,18 @@ class GridField extends FormField { $fieldData = $this->request->requestVar($this->getName()); if($fieldData && isset($fieldData['GridState'])) $this->getState(false)->setValue($fieldData['GridState']); - + foreach($this->getComponents() as $component) { if(!($component instanceof GridField_URLHandler)) { continue; } - + $urlHandlers = $component->getURLHandlers($this); - + if($urlHandlers) foreach($urlHandlers as $rule => $action) { if($params = $request->match($rule, true)) { // Actions can reference URL parameters, eg, '$Action/$ID/$OtherID' => '$Action', - if($action[0] == '$') $action = $params[substr($action,1)]; + if($action[0] == '$') $action = $params[substr($action, 1)]; if(!method_exists($component, 'checkAccessAction') || $component->checkAccessAction($action)) { if(!$action) { $action = "index"; @@ -723,7 +779,8 @@ class GridField extends FormField { } if($this !== $result && !$request->isEmptyPattern($rule) && is_object($result) - && $result instanceof RequestHandler) { + && $result instanceof RequestHandler + ) { $returnValue = $result->handleRequest($request, $model); @@ -733,11 +790,11 @@ class GridField extends FormField { return $returnValue; - // If we return some other data, and all the URL is parsed, then return that + // If we return some other data, and all the URL is parsed, then return that } else if($request->allParsed()) { return $result; - // But if we have more content on the URL and we don't know what to do with it, return an error + // But if we have more content on the URL and we don't know what to do with it, return an error } else { return $this->httpError(404, "I can't handle sub-URLs of a " . get_class($result) . " object."); @@ -746,7 +803,7 @@ class GridField extends FormField { } } } - + return parent::handleRequest($request, $model); } @@ -762,10 +819,10 @@ class GridField extends FormField { /** - * This class is the base class when you want to have an action that alters - * the state of the {@link GridField}, rendered as a button element. - * - * @package forms + * This class is the base class when you want to have an action that alters + * the state of the {@link GridField}, rendered as a button element. + * + * @package forms * @subpackage fields-gridfield */ class GridField_FormAction extends FormAction { @@ -774,12 +831,12 @@ class GridField_FormAction extends FormAction { * @var GridField */ protected $gridField; - + /** - * @var array + * @var array */ protected $stateValues; - + /** * @var array */ @@ -800,7 +857,7 @@ class GridField_FormAction extends FormAction { * @param type $name * @param type $label * @param type $actionName - * @param type $args + * @param type $args */ public function __construct(GridField $gridField, $name, $title, $actionName, $args) { $this->gridField = $gridField; @@ -811,9 +868,9 @@ class GridField_FormAction extends FormAction { } /** - * urlencode encodes less characters in percent form than we need - we + * urlencode encodes less characters in percent form than we need - we * need everything that isn't a \w. - * + * * @param string $val */ public function nameEncode($val) { @@ -822,11 +879,11 @@ class GridField_FormAction extends FormAction { /** * The callback for nameEncode - * + * * @param string $val */ public function _nameEncode($match) { - return '%'.dechex(ord($match[0])); + return '%' . dechex(ord($match[0])); } /** @@ -841,16 +898,16 @@ class GridField_FormAction extends FormAction { ); // Ensure $id doesn't contain only numeric characters - $id = 'gf_'.substr(md5(serialize($state)), 0, 8); + $id = 'gf_' . substr(md5(serialize($state)), 0, 8); Session::set($id, $state); $actionData['StateID'] = $id; - + return array_merge( parent::getAttributes(), array( // Note: This field needs to be less than 65 chars, otherwise Suhosin security patch // will strip it from the requests - 'name' => 'action_gridFieldAlterAction'. '?' . http_build_query($actionData), + 'name' => 'action_gridFieldAlterAction' . '?' . http_build_query($actionData), 'data-url' => $this->gridField->Link(), ) ); @@ -860,16 +917,17 @@ class GridField_FormAction extends FormAction { * Calculate the name of the gridfield relative to the Form * * @param GridField $base + * * @return string */ protected function getNameFromParent() { $base = $this->gridField; $name = array(); - + do { array_unshift($name, $base->getName()); $base = $base->getForm(); - } while ($base && !($base instanceof Form)); + } while($base && !($base instanceof Form)); return implode('.', $name); }