2011-12-06 13:56:24 +13:00
< ? php
/**
* Displays a { @ link SS_List } in a grid format .
*
* GridField is a field that takes an SS_List and displays it in an table with rows
* and columns . It reminds of the old TableFields but works with SS_List types
* and only loads the necessary rows from the list .
*
* The minimum configuration is to pass in name and title of the field and a
* SS_List .
*
* < code >
* $gridField = new GridField ( 'ExampleGrid' , 'Example grid' , new DataList ( 'Page' ));
* </ code >
*
* @ see SS_List
*
* @ package sapphire
* @ subpackage fields - relational
*/
class GridField extends FormField {
/**
*
* @ var array
*/
public static $allowed_actions = array (
2012-02-09 11:16:29 +01:00
'index' ,
2011-12-06 13:56:24 +13:00
'gridFieldAlterAction'
);
/** @var SS_List - the datasource */
protected $list = null ;
/** @var string - the classname of the DataObject that the GridField will display. Defaults to the value of $this->list->dataClass */
protected $modelClassName = '' ;
/** @var array */
public $fieldCasting = array ();
/** @var array */
public $fieldFormatting = array ();
/** @var GridState - the current state of the GridField */
protected $state = null ;
/**
*
* @ var GridFieldConfig
*/
protected $config = null ;
/**
* The components list
*/
protected $components = array ();
/**
* This is the columns that will be visible
*
* @ var array
*/
protected $displayFields = array ();
/**
* Internal dispatcher for column handlers .
* Keys are column names and values are GridField_ColumnProvider objects
2012-01-07 05:18:00 +01:00
*
* @ var array
2011-12-06 13:56:24 +13:00
*/
protected $columnDispatch = null ;
/**
* Creates a new GridField field
*
* @ param string $name
* @ param string $title
* @ param SS_List $dataList
* @ param GridFieldConfig $config
*/
public function __construct ( $name , $title = null , SS_List $dataList = null , GridFieldConfig $config = null ) {
parent :: __construct ( $name , $title , null );
if ( $dataList ) {
$this -> setList ( $dataList );
}
if ( ! $config ) {
2012-01-30 15:51:07 +01:00
$this -> config = GridFieldConfig_Base :: create ();
2011-12-06 13:56:24 +13:00
} else {
$this -> config = $config ;
}
2012-01-07 04:48:49 +01:00
$this -> config -> addComponent ( new GridState_Component ());
2011-12-06 13:56:24 +13:00
$this -> setComponents ( $this -> config );
$this -> state = new GridState ( $this );
$this -> addExtraClass ( 'ss-gridfield' );
2012-02-09 11:16:29 +01:00
}
function index ( $request ) {
return $this -> gridFieldAlterAction ( array (), $this -> getForm (), $request );
2011-12-06 13:56:24 +13:00
}
/**
2012-01-07 05:18:00 +01:00
* 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
2011-12-06 13:56:24 +13:00
*
2012-01-07 05:18:00 +01:00
* @ param string $modelClassName
* @ see GridField :: getDisplayFields ()
2011-12-06 13:56:24 +13:00
*/
public function setModelClass ( $modelClassName ) {
$this -> modelClassName = $modelClassName ;
return $this ;
}
/**
2012-01-07 05:18:00 +01:00
* Returns a dataclass that is a DataObject type that this GridField should look like .
2011-12-06 13:56:24 +13:00
*
* @ throws Exception
* @ return string
*/
public function getModelClass () {
if ( $this -> modelClassName ) return $this -> modelClassName ;
2012-01-10 16:59:28 +13:00
if ( $this -> list && method_exists ( $this -> list , 'dataClass' )) {
$class = $this -> list -> dataClass ();
if ( $class ) return $class ;
}
2011-12-06 13:56:24 +13:00
throw new LogicException ( 'GridField doesn\'t have a modelClassName, so it doesn\'t know the columns of this grid.' );
}
/**
2012-01-07 05:18:00 +01:00
* Set which GridFieldComponent ' s that this GridFields contain by using a GridFieldConfig
2011-12-06 13:56:24 +13:00
*
2012-01-07 05:18:00 +01:00
* @ param GridFieldConfig $config
* @ see GridFieldComponent
2011-12-06 13:56:24 +13:00
*/
protected function setComponents ( GridFieldConfig $config ) {
$this -> components = $config -> getComponents ();
return $this ;
}
/**
2012-01-07 05:18:00 +01:00
* Get the DisplayFields
*
2011-12-06 13:56:24 +13:00
* @ return array
2012-01-07 05:18:00 +01:00
* @ see GridField :: setDisplayFields
2011-12-06 13:56:24 +13:00
*/
public function getDisplayFields () {
if ( ! $this -> displayFields ) {
return singleton ( $this -> getModelClass ()) -> summaryFields ();
}
return $this -> displayFields ;
}
/**
2012-01-07 05:18:00 +01:00
* Get the GridFieldConfig
2011-12-06 13:56:24 +13:00
*
* @ return GridFieldConfig
*/
public function getConfig () {
return $this -> config ;
}
/**
2012-01-07 05:18:00 +01:00
* Override the default behaviour of showing the models summaryFields with
* these fields instead
* Example : array ( 'Name' => 'Members name' , 'Email' => 'Email address' )
2011-12-06 13:56:24 +13:00
*
* @ param array $fields
*/
2012-01-07 04:48:49 +01:00
public function setDisplayFields ( $fields ) {
2011-12-06 13:56:24 +13:00
if ( ! is_array ( $fields )) {
throw new InvalidArgumentException ( 'Arguments passed to GridField::setDisplayFields() must be an array' );
}
$this -> displayFields = $fields ;
return $this ;
}
/**
2012-01-07 04:48:49 +01:00
* Specify castings with fieldname as the key , and the desired casting as value .
* Example : array ( " MyCustomDate " => " Date " , " MyShortText " => " Text->FirstSentence " )
*
2011-12-06 13:56:24 +13:00
* @ param array $casting
2012-01-07 04:48:49 +01:00
* @ todo refactor this into GridFieldComponent
2011-12-06 13:56:24 +13:00
*/
public function setFieldCasting ( $casting ) {
$this -> fieldCasting = $casting ;
return $this ;
}
/**
2012-01-07 04:48:49 +01:00
* Specify custom formatting for fields , e . g . to render a link instead of pure text .
* Caution : Make sure to escape special php - characters like in a normal php - statement .
* Example : " myFieldName " => '<a href=\"custom-admin/$ID\">$ID</a>'
*
2011-12-06 13:56:24 +13:00
* @ param array $casting
2012-01-07 04:48:49 +01:00
* @ todo refactor this into GridFieldComponent
2011-12-06 13:56:24 +13:00
*/
public function getFieldCasting () {
return $this -> fieldCasting ;
}
/**
* @ param array $casting
2012-01-07 04:48:49 +01:00
* @ todo refactor this into GridFieldComponent
2011-12-06 13:56:24 +13:00
*/
public function setFieldFormatting ( $formatting ) {
$this -> fieldFormatting = $formatting ;
return $this ;
}
/**
* @ param array $casting
2012-01-07 04:48:49 +01:00
* @ todo refactor this into GridFieldComponent
2011-12-06 13:56:24 +13:00
*/
public function getFieldFormatting () {
return $this -> fieldFormatting ;
}
/**
2012-01-07 04:48:49 +01:00
* Cast a arbitrary value with the help of a castingDefintion
2011-12-06 13:56:24 +13:00
*
2012-01-07 04:48:49 +01:00
* @ param $value
* @ param $castingDefinition
* @ todo refactor this into GridFieldComponent
2011-12-06 13:56:24 +13:00
*/
public function getCastedValue ( $value , $castingDefinition ) {
if ( is_array ( $castingDefinition )) {
$castingParams = $castingDefinition ;
array_shift ( $castingParams );
$castingDefinition = array_shift ( $castingDefinition );
} else {
$castingParams = array ();
}
if ( strpos ( $castingDefinition , '->' ) === false ) {
$castingFieldType = $castingDefinition ;
$castingField = DBField :: create ( $castingFieldType , $value );
$value = call_user_func_array ( array ( $castingField , 'XML' ), $castingParams );
} else {
$fieldTypeParts = explode ( '->' , $castingDefinition );
$castingFieldType = $fieldTypeParts [ 0 ];
$castingMethod = $fieldTypeParts [ 1 ];
$castingField = DBField :: create ( $castingFieldType , $value );
$value = call_user_func_array ( array ( $castingField , $castingMethod ), $castingParams );
}
return $value ;
}
/**
* Set the datasource
*
* @ param SS_List $list
*/
public function setList ( SS_List $list ) {
$this -> list = $list ;
return $this ;
}
/**
* Get the datasource
*
* @ return SS_List
*/
public function getList () {
return $this -> list ;
}
/**
2012-01-07 05:18:00 +01:00
* Get the current GridState_Data or the GridState
2011-12-06 13:56:24 +13:00
*
2012-01-07 04:48:49 +01:00
* @ param bool $getData - flag for returning the GridState_Data or the GridState
* @ return GridState_data | GridState
2011-12-06 13:56:24 +13:00
*/
public function getState ( $getData = true ) {
if ( $getData ) {
return $this -> state -> getData ();
}
return $this -> state ;
}
/**
2012-01-07 05:18:00 +01:00
* Returns the whole gridfield rendered with all the attached components
2011-12-06 13:56:24 +13:00
*
* @ return string
*/
public function FieldHolder () {
2012-03-06 16:58:13 +01:00
Requirements :: css ( THIRDPARTY_DIR . '/jquery-ui-themes/smoothness/jquery-ui.css' );
Requirements :: css ( SAPPHIRE_DIR . '/css/GridField.css' );
Requirements :: javascript ( THIRDPARTY_DIR . '/jquery/jquery.js' );
Requirements :: javascript ( SAPPHIRE_DIR . '/thirdparty/jquery-ui/jquery-ui.js' );
Requirements :: javascript ( THIRDPARTY_DIR . '/json-js/json2.js' );
Requirements :: javascript ( SAPPHIRE_DIR . '/javascript/i18n.js' );
Requirements :: add_i18n_javascript ( SAPPHIRE_DIR . '/javascript/lang' );
Requirements :: javascript ( THIRDPARTY_DIR . '/jquery-entwine/dist/jquery.entwine-dist.js' );
Requirements :: javascript ( SAPPHIRE_DIR . '/javascript/GridField.js' );
2011-12-06 13:56:24 +13:00
// Get columns
$columns = $this -> getColumns ();
// Get data
$list = $this -> getList ();
foreach ( $this -> components as $item ) {
if ( $item instanceof GridField_DataManipulator ) {
$list = $item -> getManipulatedData ( $this , $list );
}
}
// Render headers, footers, etc
$content = array (
2012-03-08 19:04:07 +13:00
" before " => " " ,
" after " => " " ,
" header " => " " ,
" footer " => " " ,
2011-12-06 13:56:24 +13:00
);
foreach ( $this -> components as $item ) {
if ( $item instanceof GridField_HTMLProvider ) {
$fragments = $item -> getHTMLFragments ( $this );
foreach ( $fragments as $k => $v ) {
2012-03-08 19:04:07 +13:00
$k = strtolower ( $k );
if ( ! isset ( $content [ $k ])) $content [ $k ] = " " ;
$content [ $k ] .= $v . " \n " ;
2011-12-06 13:56:24 +13:00
}
}
}
2012-03-08 19:04:07 +13:00
foreach ( $content as $k => $v ) {
$content [ $k ] = trim ( $v );
}
// Replace custom fragments and check which fragments are defined
// 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 )) {
if ( preg_match_all ( '/\$DefineFragment\(([a-z0-9\-_]+)\)/i' , $v , $matches )) {
foreach ( $matches [ 1 ] as $match ) {
$fragmentName = strtolower ( $match );
$fragmentDefined [ $fragmentName ] = true ;
$fragment = isset ( $content [ $fragmentName ]) ? $content [ $fragmentName ] : " " ;
// If the fragment still has a fragment definition in it, when we should defer this item until later.
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] ' appear to have a circular dependency. " );
// Otherwise we can push to the end of the content array
unset ( $content [ $k ]);
$content [ $k ] = $v ;
if ( ! isset ( $fragmentDeferred [ $k ])) $fragmentDeferred [ $k ] = 1 ;
else $fragmentDeferred [ $k ] ++ ;
break ;
} else {
$content [ $k ] = preg_replace ( '/\$DefineFragment\(' . $fragmentName . '\)/i' , $fragment , $content [ $k ]);
}
}
}
}
// Check for any undefined fragments, and if so throw an exception
// While we're at it, trim whitespace off the elements
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? " );
}
$rows = array ();
foreach ( $list as $idx => $record ) {
$rowContent = '' ;
foreach ( $columns as $column ) {
$colContent = $this -> getColumnContent ( $record , $column );
// A return value of null means this columns should be skipped altogether.
if ( $colContent === null ) continue ;
$colAttributes = $this -> getColumnAttributes ( $record , $column );
$rowContent .= $this -> createTag ( 'td' , $colAttributes , $colContent );
}
$rows [] = $row ;
}
$content [ 'body' ] = implode ( " \n " , $rows );
2012-02-11 15:26:26 +13:00
2012-03-08 20:25:21 +01:00
$total = $list -> count ();
if ( $total > 0 ) {
2012-03-06 12:36:32 +13:00
foreach ( $list as $idx => $record ) {
$rowContent = '' ;
foreach ( $columns as $column ) {
$colContent = $this -> getColumnContent ( $record , $column );
// A return value of null means this columns should be skipped altogether.
if ( $colContent === null ) continue ;
$colAttributes = $this -> getColumnAttributes ( $record , $column );
$rowContent .= $this -> createTag ( 'td' , $colAttributes , $colContent );
}
2012-03-08 20:25:21 +01:00
$classes = array ( 'ss-gridfield-item' );
if ( $idx == 0 ) $classes [] = 'first' ;
if ( $idx == $total - 1 ) $classes [] = 'last' ;
$classes [] = ( $idx % 2 ) ? 'even' : 'odd' ;
2012-03-06 12:36:32 +13:00
$row = $this -> createTag (
'tr' ,
array (
2012-03-08 20:25:21 +01:00
" class " => implode ( ' ' , $classes ),
2012-03-06 12:36:32 +13:00
'data-id' => $record -> ID ,
// TODO Allow per-row customization similar to GridFieldDefaultColumns
'data-class' => $record -> ClassName ,
),
$rowContent
);
$content [ 'body' ][] = $row ;
2011-12-06 13:56:24 +13:00
}
2012-03-06 12:36:32 +13:00
} else { //display a message when the grid field is empty
2012-02-07 20:55:54 +01:00
$row = $this -> createTag (
2012-03-06 12:36:32 +13:00
'tr' ,
array ( " class " => 'ss-gridfield-item ss-gridfield-no-items' ),
2012-03-08 17:51:03 +01:00
$this -> createTag ( 'td' , array ( 'colspan' => count ( $columns )), _t ( 'GridField.NoItemsFound' , 'No items found' ))
2012-02-07 20:55:54 +01:00
);
2011-12-06 13:56:24 +13:00
}
// Turn into the relevant parts of a table
2012-03-08 19:04:07 +13:00
$head = $content [ 'header' ] ? $this -> createTag ( 'thead' , array (), $content [ 'header' ]) : '' ;
$body = $content [ 'body' ] ? $this -> createTag ( 'tbody' , array ( 'class' => 'ss-gridfield-items' ), $content [ 'body' ]) : '' ;
$foot = $content [ 'footer' ] ? $this -> createTag ( 'tfoot' , array (), $content [ 'footer' ]) : '' ;
2011-12-06 13:56:24 +13:00
2012-02-23 15:17:39 +01:00
$this -> addExtraClass ( 'ss-gridfield field' );
2012-02-07 20:55:54 +01:00
$attrs = array_diff_key (
$this -> getAttributes (),
array ( 'value' => false , 'type' => false , 'name' => false )
);
2012-02-09 17:17:39 +01:00
$attrs [ 'data-name' ] = $this -> getName ();
2012-02-07 20:55:54 +01:00
$tableAttrs = array (
2011-12-06 13:56:24 +13:00
'id' => isset ( $this -> id ) ? $this -> id : null ,
2012-02-23 15:17:39 +01:00
'class' => 'ss-gridfield-table' ,
2012-01-10 13:05:20 +13:00
'cellpadding' => '0' ,
'cellspacing' => '0'
2011-12-06 13:56:24 +13:00
);
return
2012-02-07 20:55:54 +01:00
$this -> createTag ( 'fieldset' , $attrs ,
2012-03-08 19:04:07 +13:00
$content [ 'before' ] .
2012-02-07 20:55:54 +01:00
$this -> createTag ( 'table' , $tableAttrs , $head . " \n " . $foot . " \n " . $body ) .
2012-03-08 19:04:07 +13:00
$content [ 'after' ]
2012-01-09 18:21:14 +13:00
);
2011-12-06 13:56:24 +13:00
}
2012-03-08 19:11:51 +13:00
public function Field () {
return $this -> FieldHolder ();
}
2011-12-06 13:56:24 +13:00
2012-02-09 11:16:29 +01:00
public function getAttributes () {
return array_merge ( parent :: getAttributes (), array ( 'data-url' => $this -> Link ()));
}
2012-01-07 05:18:00 +01:00
/**
* Get the columns of this GridField , they are provided by attached GridField_ColumnProvider
*
* @ return array
*/
public function getColumns () {
2011-12-06 13:56:24 +13:00
// Get column list
$columns = array ();
foreach ( $this -> components as $item ) {
if ( $item instanceof GridField_ColumnProvider ) {
$item -> augmentColumns ( $this , $columns );
}
}
2012-02-08 11:21:34 +13:00
2011-12-06 13:56:24 +13:00
return $columns ;
}
2012-01-07 05:18:00 +01:00
/**
* Get the value from a column
*
* @ param DataObject $record
* @ param string $column
* @ return string
* @ throws InvalidArgumentException
*/
2011-12-06 13:56:24 +13:00
public function getColumnContent ( $record , $column ) {
// Build the column dispatch
2012-01-07 04:48:49 +01:00
if ( ! $this -> columnDispatch ) {
$this -> buildColumnDispatch ();
}
2011-12-06 13:56:24 +13:00
2012-01-10 16:24:00 +13:00
if ( ! empty ( $this -> columnDispatch [ $column ])) {
2012-02-08 11:21:34 +13:00
$content = " " ;
foreach ( $this -> columnDispatch [ $column ] as $handler ) {
$content .= $handler -> getColumnContent ( $this , $record , $column );
}
return $content ;
2011-12-06 13:56:24 +13:00
} else {
throw new InvalidArgumentException ( " Bad column ' $column ' " );
}
}
2012-01-07 05:18:00 +01:00
/**
* Get extra columns attributes used as HTML attributes
*
* @ param DataObject $record
* @ param string $column
* @ return array
* @ throws LogicException
* @ throws InvalidArgumentException
*/
2011-12-06 13:56:24 +13:00
public function getColumnAttributes ( $record , $column ) {
// Build the column dispatch
2012-01-07 04:48:49 +01:00
if ( ! $this -> columnDispatch ) {
$this -> buildColumnDispatch ();
}
2011-12-06 13:56:24 +13:00
2012-01-10 16:24:00 +13:00
if ( ! empty ( $this -> columnDispatch [ $column ])) {
2012-02-08 11:21:34 +13:00
$attrs = array ();
foreach ( $this -> columnDispatch [ $column ] as $handler ) {
$column_attrs = $handler -> getColumnAttributes ( $this , $record , $column );
if ( is_array ( $column_attrs ))
$attrs = array_merge ( $attrs , $column_attrs );
elseif ( $column_attrs )
throw new LogicException ( " Non-array response from " . get_class ( $handler ) . " ::getColumnAttributes() " );
2012-01-07 04:48:49 +01:00
}
2012-02-08 11:21:34 +13:00
return $attrs ;
2011-12-06 13:56:24 +13:00
} else {
throw new InvalidArgumentException ( " Bad column ' $column ' " );
}
}
2012-01-07 05:18:00 +01:00
/**
* Get metadata for a column , example array ( 'Title' => 'Email address' )
*
* @ param string $column
* @ return array
* @ throws LogicException
* @ throws InvalidArgumentException
*/
2011-12-06 13:56:24 +13:00
public function getColumnMetadata ( $column ) {
// Build the column dispatch
2012-01-07 04:48:49 +01:00
if ( ! $this -> columnDispatch ) {
$this -> buildColumnDispatch ();
}
2011-12-06 13:56:24 +13:00
2012-01-10 16:24:00 +13:00
if ( ! empty ( $this -> columnDispatch [ $column ])) {
2012-02-08 11:21:34 +13:00
$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
throw new LogicException ( " Non-array response from " . get_class ( $handler ) . " ::getColumnMetadata() " );
2012-01-07 04:48:49 +01:00
}
2012-02-08 11:21:34 +13:00
return $metadata ;
2011-12-06 13:56:24 +13:00
}
2012-01-07 04:48:49 +01:00
throw new InvalidArgumentException ( " Bad column ' $column ' " );
2011-12-06 13:56:24 +13:00
}
2012-01-07 05:18:00 +01:00
/**
* Return how many columns the grid will have
*
* @ return int
*/
2011-12-06 13:56:24 +13:00
public function getColumnCount () {
// Build the column dispatch
if ( ! $this -> columnDispatch ) $this -> buildColumnDispatch ();
return count ( $this -> columnDispatch );
}
2012-01-07 04:48:49 +01:00
2012-01-07 05:18:00 +01:00
/**
* Build an columnDispatch that maps a GridField_ColumnProvider to a column
* for reference later
*
*/
2011-12-06 13:56:24 +13:00
protected function buildColumnDispatch () {
$this -> columnDispatch = array ();
foreach ( $this -> components as $item ) {
if ( $item instanceof GridField_ColumnProvider ) {
$columns = $item -> getColumnsHandled ( $this );
foreach ( $columns as $column ) {
2012-02-08 11:21:34 +13:00
$this -> columnDispatch [ $column ][] = $item ;
2011-12-06 13:56:24 +13:00
}
}
2012-02-08 11:21:34 +13:00
}
2011-12-06 13:56:24 +13:00
}
2012-02-09 11:16:29 +01:00
2011-12-06 13:56:24 +13:00
/**
* This is the action that gets executed when a GridField_AlterAction gets clicked .
*
* @ param array $data
* @ return string
*/
2012-01-09 18:21:14 +13:00
public function gridFieldAlterAction ( $data , $form , SS_HTTPRequest $request ) {
2012-02-09 11:16:29 +01:00
$html = '' ;
$data = $request -> requestVars ();
2012-02-09 17:17:39 +01:00
$fieldData = @ $data [ $this -> getName ()];
2012-02-09 11:16:29 +01:00
// Update state from client
$state = $this -> getState ( false );
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 -> handleAction ( $actionName , $args , $data );
// A field can optionally return its own HTML
if ( $html ) return $html ;
}
2012-01-25 17:31:27 +13:00
}
2011-12-06 13:56:24 +13:00
2012-01-09 18:21:14 +13:00
switch ( $request -> getHeader ( 'X-Get-Fragment' )) {
case 'CurrentField' :
2012-02-09 11:16:29 +01:00
return $this -> FieldHolder ();
2012-01-09 18:21:14 +13:00
break ;
case 'CurrentForm' :
return $form -> forTemplate ();
break ;
default :
return $form -> forTemplate ();
break ;
2011-12-06 13:56:24 +13:00
}
}
2012-01-07 05:18:00 +01:00
/**
* Pass an action on the first GridField_ActionProvider that matches the $actionName
*
* @ param string $actionName
* @ param mixed $args
* @ param arrray $data - send data from a form
* @ return type
* @ throws InvalidArgumentException
*/
2011-12-06 13:56:24 +13:00
public function handleAction ( $actionName , $args , $data ) {
$actionName = strtolower ( $actionName );
2012-01-25 17:31:27 +13:00
foreach ( $this -> components as $component ) {
if ( ! ( $component instanceof GridField_ActionProvider )) {
2011-12-06 13:56:24 +13:00
continue ;
}
2012-01-25 17:31:27 +13:00
if ( in_array ( $actionName , array_map ( 'strtolower' , $component -> getActions ( $this )))) {
return $component -> handleAction ( $this , $actionName , $args , $data );
2011-12-06 13:56:24 +13:00
}
}
throw new InvalidArgumentException ( " Can't handle action ' $actionName ' " );
}
2012-01-09 18:47:31 +13:00
/**
* Custom request handler that will check component handlers before proceeding to the default implementation .
*
* @ todo There is too much code copied from RequestHandler here .
*/
function handleRequest ( SS_HTTPRequest $request , DataModel $model ) {
if ( $this -> brokenOnConstruct ) {
user_error ( " parent::__construct() needs to be called on { $handlerClass } ::__construct() " , E_USER_WARNING );
}
$this -> request = $request ;
$this -> setModel ( $model );
2012-02-09 11:16:29 +01:00
2012-02-09 17:17:39 +01:00
$fieldData = $this -> request -> requestVar ( $this -> getName ());
2012-02-09 11:16:29 +01:00
if ( $fieldData && $fieldData [ 'GridState' ]) $this -> getState ( false ) -> setValue ( $fieldData [ 'GridState' ]);
2012-01-09 18:47:31 +13:00
foreach ( $this -> components 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 ( ! method_exists ( $component , 'checkAccessAction' ) || $component -> checkAccessAction ( $action )) {
if ( ! $action ) {
$action = " index " ;
} else if ( ! is_string ( $action )) {
throw new LogicException ( " Non-string method name: " . var_export ( $action , true ));
}
try {
$result = $component -> $action ( $this , $request );
} catch ( SS_HTTPResponse_Exception $responseException ) {
$result = $responseException -> getResponse ();
}
if ( $result instanceof SS_HTTPResponse && $result -> isError ()) {
return $result ;
}
if ( $this !== $result && ! $request -> isEmptyPattern ( $rule ) && is_object ( $result ) && $result instanceof RequestHandler ) {
$returnValue = $result -> handleRequest ( $request , $model );
if ( is_array ( $returnValue )) {
throw new LogicException ( " GridField_URLHandler handlers can't return arrays " );
}
return $returnValue ;
// 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.
} else {
return $this -> httpError ( 404 , " I can't handle sub-URLs of a " . get_class ( $result ) . " object. " );
}
}
}
}
}
return parent :: handleRequest ( $request , $model );
}
2011-12-06 13:56:24 +13:00
}
/**
2012-03-06 12:51:57 +01:00
* This class is the base class when you want to have an action that alters the state of the gridfield ,
* rendered as a button element .
2011-12-06 13:56:24 +13:00
*
* @ package sapphire
* @ subpackage forms
*
*/
2012-03-06 12:51:57 +01:00
class GridField_FormAction extends FormAction {
2011-12-06 13:56:24 +13:00
/**
*
* @ var GridField
*/
protected $gridField ;
/**
*
* @ var array
*/
protected $stateValues ;
/**
*
* @ var array
*/
//protected $stateFields = array();
protected $actionName ;
2012-03-06 16:58:13 +01:00
2011-12-06 13:56:24 +13:00
protected $args = array ();
2012-03-06 16:58:13 +01:00
public $useButtonTag = true ;
2011-12-06 13:56:24 +13:00
/**
*
* @ param GridField $gridField
* @ param type $name
* @ param type $label
* @ param type $actionName
* @ param type $args
*/
2012-03-06 16:58:13 +01:00
public function __construct ( GridField $gridField , $name , $title , $actionName , $args ) {
2011-12-06 13:56:24 +13:00
$this -> gridField = $gridField ;
$this -> actionName = $actionName ;
$this -> args = $args ;
2012-03-06 16:58:13 +01:00
parent :: __construct ( $name , $title );
2011-12-06 13:56:24 +13:00
}
/**
* 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 ) {
return preg_replace_callback ( '/[^\w]/' , array ( $this , '_nameEncode' ), $val );
}
/**
* The callback for nameEncode
*
* @ param string $val
*/
public function _nameEncode ( $match ) {
return '%' . dechex ( ord ( $match [ 0 ]));
}
2012-03-06 16:58:13 +01:00
public function getAttributes () {
2011-12-06 13:56:24 +13:00
// 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 ;
2012-03-06 16:58:13 +01:00
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 ),
'data-url' => $this -> gridField -> Link (),
)
);
2011-12-06 13:56:24 +13:00
}
/**
* 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 ));
return implode ( '.' , $name );
}
}