2007-07-19 12:40:28 +02:00
< ? php
2008-08-11 02:21:44 +02:00
/**
* @ package forms
* @ subpackage fields - relational
*/
2007-07-19 12:40:28 +02:00
/**
* Form field that embeds a list into a form , such as a member list or a file list .
*
* All get variables are namespaced in the format ctf [ MyFieldName ][ MyParameter ] to avoid collisions
* when multiple TableListFields are present in a form .
*
* @ param $name string The fieldname
* @ param $sourceClass string The source class of this field
* @ param $fieldList array An array of field headings of Fieldname => Heading Text ( eg . heading1 )
* @ param $sourceFilter string The filter field you wish to limit the objects by ( eg . parentID )
* @ param $sourceSort string
* @ param $sourceJoin string
2008-01-09 05:18:36 +01:00
* @ package forms
* @ subpackage fields - relational
2007-07-19 12:40:28 +02:00
*/
class TableListField extends FormField {
/**
* @ var $cachedSourceItems DataObjectSet Prevent { @ sourceItems ()} from being called multiple times .
*/
protected $cachedSourceItems ;
protected $sourceClass ;
2008-08-20 07:13:53 +02:00
protected $sourceFilter = " " ;
2007-07-19 12:40:28 +02:00
protected $sourceSort = " " ;
protected $sourceJoin = array ();
protected $fieldList ;
2009-10-15 23:53:15 +02:00
protected $disableSorting = false ;
2007-07-19 12:40:28 +02:00
/**
* @ var $fieldListCsv array
*/
protected $fieldListCsv ;
/**
* @ var $clickAction
*/
protected $clickAction ;
/**
* @ var bool
*/
public $IsReadOnly ;
/**
* Called method ( needs to be retained for AddMode ())
*/
protected $methodName ;
/**
* @ var $summaryFieldList array Shows a row which summarizes the contents of a column by a predefined
* Javascript - function
*/
protected $summaryFieldList ;
/**
* @ var $summaryTitle string The title which will be shown in the first column of the summary - row .
* Accordingly , the first column can ' t be used for summarizing .
*/
protected $summaryTitle ;
/**
* @ var $template string Template - Overrides
*/
protected $template = " TableListField " ;
2010-02-11 05:21:04 +01:00
/**
* @ var $itemClass string Class name for each item / row
*/
public $itemClass = 'TableListField_Item' ;
2007-07-19 12:40:28 +02:00
/**
* @ var bool Do we use checkboxes to mark records , or delete them one by one ?
*/
public $Markable ;
2008-08-12 01:24:54 +02:00
public $MarkableTitle = null ;
2010-10-15 04:27:59 +02:00
/**
* @ var array See { @ link SelectOptions ()}
*/
2010-10-15 04:51:04 +02:00
2010-10-15 04:27:59 +02:00
protected $selectOptions = array ();
2007-07-19 12:40:28 +02:00
/**
* @ var $readOnly boolean Deprecated , please use $permssions instead
*/
protected $readOnly ;
/**
* @ var $permissions array Influence output without having to subclass the template .
2008-08-12 01:24:54 +02:00
* See $actions for adding your custom actions / permissions .
2007-07-19 12:40:28 +02:00
*/
protected $permissions = array (
//"print",
//"export",
" delete "
);
2008-08-12 01:24:54 +02:00
/**
* @ var $actions array Action that can be performed on a single row - entry .
* Has to correspond to a method in a TableListField - class ( or subclass ) .
* Actions can be disabled through $permissions .
* Format ( key is used for the methodname and CSS - class ) :
* array (
2009-03-13 11:07:27 +01:00
* 'delete' => array (
* 'label' => 'Delete' ,
* 'icon' => 'cms/images/delete.gif' ,
* 'icon_disabled' => 'cms/images/delete_disabled.gif' ,
* 'class' => 'deletelink' ,
* )
2008-08-12 01:24:54 +02:00
* )
*/
public $actions = array (
'delete' => array (
'label' => 'Delete' ,
'icon' => 'cms/images/delete.gif' ,
2009-03-13 11:07:27 +01:00
'icon_disabled' => 'cms/images/delete_disabled.gif' ,
2008-08-12 01:24:54 +02:00
'class' => 'deletelink'
)
);
/**
* @ var $defaultAction String Action being executed when clicking on table - row ( defaults to " show " ) .
* Mostly needed in ComplexTableField - subclass .
*/
public $defaultAction = '' ;
2007-07-19 12:40:28 +02:00
/**
* @ var $customQuery Specify custom query , e . g . for complicated having / groupby - constructs .
* Caution : TableListField automatically selects the ID from the { @ sourceClass }, because it relies
* on this information e . g . in saving a TableField . Please use a custom select if you want to filter
* for other IDs in joined tables : $query -> select [] = " MyJoinedTable.ID AS MyJoinedTableID "
*/
protected $customQuery ;
/**
* @ var $customCsvQuery Query for CSV - export ( might need different fields or further filtering )
*/
protected $customCsvQuery ;
/**
* @ var $customSourceItems DataObjectSet Use the manual setting of a result - set only as a last - resort
* for sets which can ' t be resolved in a single query .
*
2010-04-23 02:11:41 +02:00
* @ todo Add pagination support for customSourceItems .
2007-07-19 12:40:28 +02:00
*/
protected $customSourceItems ;
/**
* Character to seperate exported columns in the CSV file
*/
2007-11-13 00:07:45 +01:00
protected $csvSeparator = " , " ;
2007-07-19 12:40:28 +02:00
/*
* Boolean deciding whether to include a header row in the CSV file
*/
protected $csvHasHeader = true ;
2008-08-11 02:21:44 +02:00
/**
* @ var array Specify custom escape for the fields .
*
* < code >
* array ( " \" " => " \" \" " , " \r " => " " , " \r \n " => " " , " \n " => " " )
* </ code >
*/
public $csvFieldEscape = array (
" \" " => " \" \" " ,
" \r \n " => " " ,
" \r " => " " ,
" \n " => " " ,
);
2007-07-19 12:40:28 +02:00
/**
* @ var int Shows total count regardless or pagination
*/
protected $totalCount ;
/**
* @ var boolean Trigger pagination
*/
protected $showPagination = false ;
2009-09-15 06:11:13 +02:00
/**
* @ var string Override the { @ link Link ()} method
* for all pagination . Useful to force rendering of the field
* in a different context .
*/
public $paginationBaseLink = null ;
2007-07-19 12:40:28 +02:00
/**
* @ var int Number of items to show on a single page ( needed for pagination )
*/
protected $pageSize = 10 ;
/**
* @ var array Definitions for highlighting table - rows with a specific class . You can use all column - names
* in the result of a query . Use in combination with { @ setCustomQuery } to select custom properties and joined objects .
*
* Example :
* array (
* array (
* " rule " => '$Flag == "red"' ,
* " class " => " red "
* ),
* array (
* " rule " => '$Flag == "orange"' ,
* " class " => " orange "
* )
* )
*/
public $highlightConditions = array ();
/**
* @ var array Specify castings with fieldname as the key , and the desired casting as value .
* Example : array ( " MyCustomDate " => " Date " , " MyShortText " => " Text->FirstSentence " )
*/
public $fieldCasting = array ();
/**
* @ var array 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>'
*/
public $fieldFormatting = array ();
2008-10-08 04:00:12 +02:00
public $csvFieldFormatting = array ();
2007-07-19 12:40:28 +02:00
/**
* @ var string
*/
public $exportButtonLabel = 'Export as CSV' ;
/**
* @ var string $groupByField Used to group by a specific column in the DataObject
* and create partial summaries .
*/
public $groupByField = null ;
2008-08-11 01:17:51 +02:00
/**
* @ var array
*/
protected $extraLinkParams ;
2008-10-08 04:00:12 +02:00
protected $__cachedQuery ;
2008-08-11 00:49:59 +02:00
function __construct ( $name , $sourceClass , $fieldList = null , $sourceFilter = null ,
2007-07-19 12:40:28 +02:00
$sourceSort = null , $sourceJoin = null ) {
2008-08-11 00:49:59 +02:00
$this -> fieldList = ( $fieldList ) ? $fieldList : singleton ( $sourceClass ) -> summaryFields ();
2007-07-19 12:40:28 +02:00
$this -> sourceClass = $sourceClass ;
$this -> sourceFilter = $sourceFilter ;
$this -> sourceSort = $sourceSort ;
$this -> sourceJoin = $sourceJoin ;
$this -> readOnly = false ;
parent :: __construct ( $name );
}
2008-09-23 03:24:03 +02:00
/**
* Get the filter
*/
function sourceFilter () {
return $this -> sourceFilter ;
}
2008-08-11 05:39:14 +02:00
function index () {
return $this -> FieldHolder ();
}
2008-10-08 05:32:33 +02:00
2008-10-08 04:00:12 +02:00
static $url_handlers = array (
'item/$ID' => 'handleItem' ,
2008-10-08 05:32:33 +02:00
'$Action' => '$Action' ,
2008-10-08 04:00:12 +02:00
);
function sourceClass () {
return $this -> sourceClass ;
}
function handleItem ( $request ) {
return new TableListField_ItemRequest ( $this , $request -> param ( 'ID' ));
}
2007-07-19 12:40:28 +02:00
function FieldHolder () {
2009-11-21 03:29:59 +01:00
Requirements :: javascript ( SAPPHIRE_DIR . '/thirdparty/prototype/prototype.js' );
Requirements :: javascript ( SAPPHIRE_DIR . '/thirdparty/behaviour/behaviour.js' );
Requirements :: javascript ( SAPPHIRE_DIR . '/javascript/prototype_improvements.js' );
Requirements :: javascript ( SAPPHIRE_DIR . '/thirdparty/scriptaculous/effects.js' );
2009-04-29 02:07:39 +02:00
Requirements :: add_i18n_javascript ( SAPPHIRE_DIR . '/javascript/lang' );
Requirements :: javascript ( SAPPHIRE_DIR . '/javascript/TableListField.js' );
Requirements :: css ( SAPPHIRE_DIR . '/css/TableListField.css' );
2007-07-19 12:40:28 +02:00
if ( $this -> clickAction ) {
$id = $this -> id ();
Requirements :: customScript ( <<< JS
Behaviour . register ({
'#$id tr' : {
onclick : function () {
$this -> clickAction
return false ;
}
}
});
JS
);}
return $this -> renderWith ( $this -> template );
}
function Headings () {
2008-08-11 01:29:30 +02:00
$headings = array ();
2007-07-19 12:40:28 +02:00
foreach ( $this -> fieldList as $fieldName => $fieldTitle ) {
$isSorted = ( isset ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'sort' ]) && $fieldName == $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'sort' ]);
// we can't allow sorting with partial summaries (groupByField)
$isSortable = ( $this -> form && $this -> isFieldSortable ( $fieldName ) && ! $this -> groupByField );
// sorting links (only if we have a form to refresh with)
if ( $this -> form ) {
2008-08-09 06:38:44 +02:00
$sortLink = $this -> Link ();
2010-10-13 03:48:24 +02:00
$sortLink = HTTP :: setGetVar ( " ctf[ { $this -> Name () } ][sort] " , $fieldName , $sortLink , '&' );
2010-04-13 04:15:23 +02:00
2010-04-13 04:15:50 +02:00
// Apply sort direction to the current sort field
if ( ! empty ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'sort' ]) && ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'sort' ] == $fieldName )) {
$dir = isset ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'dir' ]) ? $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'dir' ] : null ;
$dir = trim ( strtolower ( $dir ));
$newDir = ( $dir == 'desc' ) ? null : 'desc' ;
2010-10-13 03:48:24 +02:00
$sortLink = HTTP :: setGetVar ( " ctf[ { $this -> Name () } ][dir] " , Convert :: raw2xml ( $newDir ), $sortLink , '&' );
2010-04-13 04:15:50 +02:00
}
2010-04-13 04:15:23 +02:00
2009-05-28 09:08:23 +02:00
if ( isset ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'search' ]) && is_array ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'search' ])) {
2009-05-19 12:32:06 +02:00
foreach ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'search' ] as $parameter => $value ) {
$XML_search = Convert :: raw2xml ( $value );
2010-10-13 03:48:24 +02:00
$sortLink = HTTP :: setGetVar ( " ctf[ { $this -> Name () } ][search][ $parameter ] " , $XML_search , $sortLink , '&' );
2009-05-19 12:32:06 +02:00
}
}
2007-07-19 12:40:28 +02:00
} else {
$sortLink = '#' ;
}
$headings [] = new ArrayData ( array (
" Name " => $fieldName ,
2010-10-15 05:43:47 +02:00
" Title " => ( $this -> sourceClass ) ? singleton ( $this -> sourceClass ) -> fieldLabel ( $fieldTitle ) : $fieldTitle ,
2007-07-19 12:40:28 +02:00
" IsSortable " => $isSortable ,
2008-08-19 12:07:28 +02:00
" SortLink " => $sortLink ,
2007-07-19 12:40:28 +02:00
" SortBy " => $isSorted ,
" SortDirection " => ( isset ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'dir' ])) ? $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'dir' ] : null
));
}
return new DataObjectSet ( $headings );
}
2009-10-15 23:53:15 +02:00
function disableSorting ( $to = true ) {
$this -> disableSorting = $to ;
}
2007-07-19 12:40:28 +02:00
/**
* Determines if a field is " sortable " .
* If the field is generated by a custom getter , we can ' t sort on it
* without generating all objects first ( which would be a huge performance impact ) .
*
* @ param string $fieldName
* @ return bool
*/
function isFieldSortable ( $fieldName ) {
2009-10-15 23:53:15 +02:00
if ( $this -> customSourceItems || $this -> disableSorting ) {
2007-07-19 12:40:28 +02:00
return false ;
}
2010-04-13 04:06:12 +02:00
if ( ! $this -> __cachedQuery ) $this -> __cachedQuery = $this -> getQuery ();
return $this -> __cachedQuery -> canSortBy ( $fieldName );
2007-07-19 12:40:28 +02:00
}
2008-08-12 01:24:54 +02:00
/**
* Dummy function to get number of actions originally generated in
* TableListField_Item .
*
* @ return DataObjectSet
*/
function Actions () {
$allowedActions = new DataObjectSet ();
foreach ( $this -> actions as $actionName => $actionSettings ) {
if ( $this -> Can ( $actionName )) {
$allowedActions -> push ( new ViewableData ());
}
}
2008-08-14 06:38:22 +02:00
2008-08-12 01:24:54 +02:00
return $allowedActions ;
}
2007-07-19 12:40:28 +02:00
/**
* Provide a custom query to compute sourceItems . This is the preferred way to using
* { @ setSourceItems }, because we can still paginate .
* Caution : Other parameters such as { @ sourceFilter } will be ignored .
* Please use this only as a fallback for really complex queries ( e . g . involving HAVING and GROUPBY ) .
*
API CHANGE: Renamed conflicting classes to have an "SS_" namespace, and renamed existing "SS" namespace to "SS_". The affected classes are: HTTPRequest, HTTPResponse, Query, Database, SSBacktrace, SSCli, SSDatetime, SSDatetimeTest, SSLog, SSLogTest, SSLogEmailWriter, SSLogErrorEmailFormatter, SSLogErrorFileFormatter, SSLogFileWriter and SSZendLog.
MINOR: Replaced usage of renamed classes with the new namespaced name.
From: Andrew Short <andrewjshort@gmail.com>
git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@90075 467b73ca-7a2a-4603-9d3b-597d59a354a9
2009-10-26 04:06:31 +01:00
* @ param $query SS_Query
2007-07-19 12:40:28 +02:00
*/
2010-04-13 04:01:39 +02:00
function setCustomQuery ( SQLQuery $query ) {
// The type-hinting above doesn't seem to work consistently
if ( $query instanceof SQLQuery ) {
$this -> customQuery = $query ;
} else {
user_error ( 'TableList::setCustomQuery() should be passed a SQLQuery' , E_USER_WARNING );
}
2007-07-19 12:40:28 +02:00
}
2010-04-13 04:01:39 +02:00
function setCustomCsvQuery ( SQLQuery $query ) {
// The type-hinting above doesn't seem to work consistently
if ( $query instanceof SQLQuery ) {
$this -> customCsvQuery = $query ;
} else {
user_error ( 'TableList::setCustomCsvQuery() should be passed a SQLQuery' , E_USER_WARNING );
}
2007-07-19 12:40:28 +02:00
}
2010-04-13 04:01:39 +02:00
function setCustomSourceItems ( DataObjectSet $items ) {
// The type-hinting above doesn't seem to work consistently
if ( $items instanceof DataObjectSet ) {
$this -> customSourceItems = $items ;
} else {
user_error ( 'TableList::setCustomSourceItems() should be passed a DataObjectSet' , E_USER_WARNING );
}
2007-07-19 12:40:28 +02:00
}
function sourceItems () {
2008-08-13 03:39:46 +02:00
$SQL_limit = ( $this -> showPagination && $this -> pageSize ) ? " { $this -> pageSize } " : null ;
2008-04-26 08:54:38 +02:00
if ( isset ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'start' ]) && is_numeric ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'start' ])) {
2007-07-19 12:40:28 +02:00
$SQL_start = ( isset ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'start' ])) ? intval ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'start' ]) : " 0 " ;
2008-08-09 08:29:50 +02:00
} else {
$SQL_start = 0 ;
2007-07-19 12:40:28 +02:00
}
if ( isset ( $this -> customSourceItems )) {
2008-08-12 23:48:07 +02:00
if ( $this -> showPagination && $this -> pageSize ) {
2007-07-19 12:40:28 +02:00
$items = $this -> customSourceItems -> getRange ( $SQL_start , $SQL_limit );
} else {
2008-08-12 23:48:07 +02:00
$items = $this -> customSourceItems ;
2007-07-19 12:40:28 +02:00
}
} elseif ( isset ( $this -> cachedSourceItems )) {
$items = $this -> cachedSourceItems ;
} else {
// get query
$dataQuery = $this -> getQuery ();
2008-08-11 01:53:56 +02:00
// we don't limit when doing certain actions T
2010-04-13 04:26:38 +02:00
$methodName = isset ( $_REQUEST [ 'url' ]) ? array_pop ( explode ( '/' , $_REQUEST [ 'url' ])) : null ;
2010-04-13 04:26:27 +02:00
if ( ! $methodName || ! in_array ( $methodName , array ( 'printall' , 'export' ))) {
2008-08-11 01:29:30 +02:00
$dataQuery -> limit ( array (
'limit' => $SQL_limit ,
'start' => ( isset ( $SQL_start )) ? $SQL_start : null
));
2007-07-19 12:40:28 +02:00
}
// get data
$records = $dataQuery -> execute ();
$sourceClass = $this -> sourceClass ;
$dataobject = new $sourceClass ();
$items = $dataobject -> buildDataObjectSet ( $records , 'DataObjectSet' );
$this -> cachedSourceItems = $items ;
}
return $items ;
}
function Items () {
$fieldItems = new DataObjectSet ();
if ( $items = $this -> sourceItems ()) foreach ( $items as $item ) {
2010-02-11 05:21:04 +01:00
if ( $item ) $fieldItems -> push ( new $this -> itemClass ( $item , $this ));
2007-07-19 12:40:28 +02:00
}
return $fieldItems ;
}
/**
* Generates the query for sourceitems ( without pagination / limit - clause )
*
* @ return string
*/
function getQuery () {
if ( $this -> customQuery ) {
2008-10-08 04:00:12 +02:00
$query = clone $this -> customQuery ;
2007-07-19 12:40:28 +02:00
$baseClass = ClassInfo :: baseDataClass ( $this -> sourceClass );
} else {
2008-09-23 03:24:03 +02:00
$query = singleton ( $this -> sourceClass ) -> extendedSQL ( $this -> sourceFilter (), $this -> sourceSort , null , $this -> sourceJoin );
2007-07-19 12:40:28 +02:00
}
2010-04-13 04:15:23 +02:00
if ( ! empty ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'sort' ])) {
2010-04-13 04:16:34 +02:00
$column = $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'sort' ];
$dir = 'ASC' ;
2010-04-13 04:15:23 +02:00
if ( ! empty ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'dir' ])) {
$dir = $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'dir' ];
2010-04-13 04:16:34 +02:00
if ( strtoupper ( trim ( $dir )) == 'DESC' ) $dir = 'DESC' ;
2007-07-19 12:40:28 +02:00
}
2010-04-13 04:16:34 +02:00
if ( $query -> canSortBy ( $column )) $query -> orderby = $column . ' ' . $dir ;
2007-07-19 12:40:28 +02:00
}
2008-11-17 23:59:17 +01:00
2008-10-08 04:00:12 +02:00
return $query ;
2007-07-19 12:40:28 +02:00
}
function getCsvQuery () {
$baseClass = ClassInfo :: baseDataClass ( $this -> sourceClass );
2009-10-21 04:36:33 +02:00
if ( $this -> customCsvQuery || $this -> customQuery ) {
2009-12-16 06:49:14 +01:00
$query = $this -> customCsvQuery ? $this -> customCsvQuery : $this -> customQuery ;
2007-07-19 12:40:28 +02:00
} else {
2008-09-23 03:24:03 +02:00
$query = singleton ( $this -> sourceClass ) -> extendedSQL ( $this -> sourceFilter (), $this -> sourceSort , null , $this -> sourceJoin );
2007-07-19 12:40:28 +02:00
}
2009-12-16 06:49:14 +01:00
2007-07-19 12:40:28 +02:00
return clone $query ;
}
function FieldList () {
return $this -> fieldList ;
}
/**
* Configure this table to load content into a subform via ajax
*/
function setClick_AjaxLoad ( $urlBase , $formID ) {
$this -> clickAction = " this.ajaxRequest(' " . addslashes ( $urlBase ) . " ', ' " . addslashes ( $formID ) . " ') " ;
}
/**
* Configure this table to open a popup window
*/
function setClick_PopupLoad ( $urlBase ) {
2008-07-29 04:23:54 +02:00
$this -> clickAction = " var w = window.open(baseHref() + ' $urlBase ' + this.id.replace(/.*-( \ d*) $ /,' $ 1'), 'popup'); w.focus(); " ;
2007-07-19 12:40:28 +02:00
}
function performReadonlyTransformation () {
2008-12-04 23:38:32 +01:00
$clone = clone $this ;
$clone -> setShowPagination ( false );
2009-10-16 00:39:07 +02:00
// Only include the show action if it was in the original CTF.
$clone -> setPermissions ( in_array ( 'show' , $this -> permissions ) ? array ( 'show' ) : array ());
2008-12-04 23:38:32 +01:00
$clone -> addExtraClass ( 'readonly' );
$clone -> setReadonly ( true );
return $clone ;
2007-07-19 12:40:28 +02:00
}
/**
* #################################
* CRUD
* #################################
*/
/**
* @ return String
*/
2010-12-05 09:26:03 +01:00
function delete ( $request ) {
// Protect against CSRF on destructive action
$token = $this -> getForm () -> getSecurityToken ();
if ( ! $token -> checkRequest ( $request )) return $this -> httpError ( '400' );
2007-07-19 12:40:28 +02:00
if ( $this -> Can ( 'delete' ) !== true ) {
return false ;
}
$this -> methodName = " delete " ;
$childId = Convert :: raw2sql ( $_REQUEST [ 'ctf' ][ 'childID' ]);
if ( is_numeric ( $childId )) {
$childObject = DataObject :: get_by_id ( $this -> sourceClass , $childId );
if ( $childObject ) $childObject -> delete ();
}
// TODO return status in JSON etc.
//return $this->renderWith($this->template);
}
/**
* #################################
* Summary - Row
* #################################
*/
/**
* Can utilize some built - in summary - functions , with optional casting .
* Currently supported :
* - sum
* - avg
*
* @ param $summaryTitle string
* @ param $summaryFields array
* Simple Format : array ( " MyFieldName " => " sum " )
* With Casting : array ( " MyFieldname " => array ( " sum " , " Currency->Nice " ))
*/
function addSummary ( $summaryTitle , $summaryFieldList ) {
$this -> summaryTitle = $summaryTitle ;
$this -> summaryFieldList = $summaryFieldList ;
}
function removeSummary () {
$this -> summaryTitle = null ;
$this -> summaryFields = null ;
}
function HasSummary () {
return ( isset ( $this -> summaryFieldList ));
}
function SummaryTitle () {
return $this -> summaryTitle ;
}
/**
* @ param DataObjectSet $items Only used to pass grouped sourceItems for creating
* partial summaries .
*/
function SummaryFields ( $items = null ) {
if ( ! isset ( $this -> summaryFieldList )) {
return false ;
}
$summaryFields = array ();
$fieldListWithoutFirst = $this -> fieldList ;
if ( ! empty ( $this -> summaryTitle )) {
array_shift ( $fieldListWithoutFirst );
}
foreach ( $fieldListWithoutFirst as $fieldName => $fieldTitle ) {
if ( in_array ( $fieldName , array_keys ( $this -> summaryFieldList ))) {
if ( is_array ( $this -> summaryFieldList [ $fieldName ])) {
$summaryFunction = " colFunction_ { $this -> summaryFieldList [ $fieldName ][ 0 ] } " ;
$casting = $this -> summaryFieldList [ $fieldName ][ 1 ];
} else {
$summaryFunction = " colFunction_ { $this -> summaryFieldList [ $fieldName ] } " ;
$casting = null ;
}
// fall back to integrated sourceitems if not passed
if ( ! $items ) $items = $this -> sourceItems ();
$summaryValue = ( $items ) ? $this -> $summaryFunction ( $items -> column ( $fieldName )) : null ;
// Optional casting, Format: array('MyFieldName'=>array('sum','Currency->Nice'))
if ( isset ( $casting )) {
2008-08-13 01:04:14 +02:00
$summaryValue = $this -> getCastedValue ( $summaryValue , $casting );
2007-07-19 12:40:28 +02:00
}
} else {
$summaryValue = null ;
$function = null ;
}
$summaryFields [] = new ArrayData ( array (
'Function' => $function ,
'SummaryValue' => $summaryValue ,
2008-10-08 04:00:12 +02:00
'Name' => DBField :: create ( 'Varchar' , $fieldName ),
'Title' => DBField :: create ( 'Varchar' , $fieldTitle ),
2007-07-19 12:40:28 +02:00
));
}
return new DataObjectSet ( $summaryFields );
}
function HasGroupedItems () {
return ( $this -> groupByField );
}
function GroupedItems () {
if ( ! $this -> groupByField ) {
return false ;
}
$items = $this -> sourceItems ();
if ( ! $items || ! $items -> Count ()) {
return false ;
}
$groupedItems = $items -> groupBy ( $this -> groupByField );
$groupedArrItems = new DataObjectSet ();
foreach ( $groupedItems as $key => $group ) {
$fieldItems = new DataObjectSet ();
foreach ( $group as $item ) {
2010-02-11 05:21:04 +01:00
if ( $item ) $fieldItems -> push ( new $this -> itemClass ( $item , $this ));
2007-07-19 12:40:28 +02:00
}
$groupedArrItems -> push ( new ArrayData ( array (
'Items' => $fieldItems ,
'SummaryFields' => $this -> SummaryFields ( $group )
)));
}
return $groupedArrItems ;
}
function colFunction_sum ( $values ) {
return array_sum ( $values );
}
function colFunction_avg ( $values ) {
return array_sum ( $values ) / count ( $values );
}
/**
* #################################
* Permissions
* #################################
*/
/**
2009-03-13 11:07:27 +01:00
* Template accessor for Permissions .
* See { @ link TableListField_Item -> Can ()} for object - specific
* permissions .
*
* @ return boolean
2007-07-19 12:40:28 +02:00
*/
function Can ( $mode ) {
2008-08-12 04:58:48 +02:00
if ( $mode == 'add' && $this -> isReadonly ()) {
2007-07-19 12:40:28 +02:00
return false ;
2008-08-12 04:58:48 +02:00
} else if ( $mode == 'delete' && $this -> isReadonly ()) {
2007-07-19 12:40:28 +02:00
return false ;
2008-08-12 04:58:48 +02:00
} else if ( $mode == 'edit' && $this -> isReadonly ()) {
2007-07-19 12:40:28 +02:00
return false ;
} else {
return ( in_array ( $mode , $this -> permissions ));
}
}
function setPermissions ( $arr ) {
$this -> permissions = $arr ;
}
/**
* @ return array
*/
2008-08-26 03:45:52 +02:00
function getPermissions () {
2007-07-19 12:40:28 +02:00
return $this -> permissions ;
}
2008-08-11 01:17:51 +02:00
2007-07-19 12:40:28 +02:00
/**
* #################################
* Pagination
* #################################
*/
function setShowPagination ( $bool ) {
$this -> showPagination = ( bool ) $bool ;
}
/**
* @ return boolean
*/
function ShowPagination () {
if ( $this -> showPagination && ! empty ( $this -> summaryFieldList )) {
user_error ( " You can't combine pagination and summaries - please disable one of them. " , E_USER_ERROR );
}
return $this -> showPagination ;
}
function setPageSize ( $pageSize ) {
$this -> pageSize = $pageSize ;
}
function PageSize () {
return $this -> pageSize ;
}
function ListStart () {
return $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'start' ];
}
2008-08-11 01:17:51 +02:00
/**
* @ param array
2008-08-26 03:45:52 +02:00
* @ deprecated Put the query string onto your form ' s link instead :- )
2008-08-11 01:17:51 +02:00
*/
function setExtraLinkParams ( $params ){
2008-08-26 03:45:52 +02:00
user_error ( " TableListField::setExtraLinkParams() deprecated - put the query string onto your form's FormAction instead; it will be handed down to all field with special handlers " , E_USER_NOTICE );
2008-08-11 01:17:51 +02:00
$this -> extraLinkParams = $params ;
}
/**
* @ return array
*/
function getExtraLinkParams (){
return $this -> extraLinkParams ;
}
2007-07-19 12:40:28 +02:00
function FirstLink () {
$start = 0 ;
if ( ! isset ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'start' ]) || ! is_numeric ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'start' ]) || $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'start' ] == 0 ) {
return null ;
}
2009-09-15 06:11:13 +02:00
$baseLink = ( $this -> paginationBaseLink ) ? $this -> paginationBaseLink : $this -> Link ();
$link = Controller :: join_links ( $baseLink , " ?ctf[ { $this -> Name () } ][start]= { $start } " );
2008-08-11 01:17:51 +02:00
if ( $this -> extraLinkParams ) $link .= " & " . http_build_query ( $this -> extraLinkParams );
2010-10-15 04:29:44 +02:00
// preserve sort options
if ( isset ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'sort' ])) {
$link .= " &ctf[ { $this -> Name () } ][sort]= " . $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'sort' ];
// direction
if ( isset ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'dir' ])) {
$link .= " &ctf[ { $this -> Name () } ][dir]= " . $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'dir' ];
}
}
2008-08-11 01:17:51 +02:00
return $link ;
2007-07-19 12:40:28 +02:00
}
function PrevLink () {
$currentStart = isset ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'start' ]) ? $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'start' ] : 0 ;
if ( $currentStart == 0 ) {
return null ;
}
$start = ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'start' ] - $this -> pageSize < 0 ) ? 0 : $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'start' ] - $this -> pageSize ;
2009-09-15 06:11:13 +02:00
$baseLink = ( $this -> paginationBaseLink ) ? $this -> paginationBaseLink : $this -> Link ();
$link = Controller :: join_links ( $baseLink , " ?ctf[ { $this -> Name () } ][start]= { $start } " );
2008-08-11 01:17:51 +02:00
if ( $this -> extraLinkParams ) $link .= " & " . http_build_query ( $this -> extraLinkParams );
2010-10-15 04:29:44 +02:00
// preserve sort options
if ( isset ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'sort' ])) {
$link .= " &ctf[ { $this -> Name () } ][sort]= " . $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'sort' ];
// direction
if ( isset ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'dir' ])) {
$link .= " &ctf[ { $this -> Name () } ][dir]= " . $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'dir' ];
}
}
2008-08-11 01:17:51 +02:00
return $link ;
2007-07-19 12:40:28 +02:00
}
2008-08-26 03:45:52 +02:00
2007-07-19 12:40:28 +02:00
function NextLink () {
$currentStart = isset ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'start' ]) ? $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'start' ] : 0 ;
$start = ( $currentStart + $this -> pageSize < $this -> TotalCount ()) ? $currentStart + $this -> pageSize : $this -> TotalCount () % $this -> pageSize > 0 ;
if ( $currentStart >= $start - 1 ) {
return null ;
}
2009-09-15 06:11:13 +02:00
$baseLink = ( $this -> paginationBaseLink ) ? $this -> paginationBaseLink : $this -> Link ();
$link = Controller :: join_links ( $baseLink , " ?ctf[ { $this -> Name () } ][start]= { $start } " );
2008-08-11 01:17:51 +02:00
if ( $this -> extraLinkParams ) $link .= " & " . http_build_query ( $this -> extraLinkParams );
2010-10-15 04:29:44 +02:00
// preserve sort options
if ( isset ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'sort' ])) {
$link .= " &ctf[ { $this -> Name () } ][sort]= " . $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'sort' ];
// direction
if ( isset ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'dir' ])) {
$link .= " &ctf[ { $this -> Name () } ][dir]= " . $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'dir' ];
}
}
2008-08-11 01:17:51 +02:00
return $link ;
2007-07-19 12:40:28 +02:00
}
function LastLink () {
$pageSize = ( $this -> TotalCount () % $this -> pageSize > 0 ) ? $this -> TotalCount () % $this -> pageSize : $this -> pageSize ;
2008-08-11 01:17:51 +02:00
$start = $this -> TotalCount () - $pageSize ;
2007-07-19 12:40:28 +02:00
// Check if there is only one page, or if we are on last page
2008-08-11 01:17:51 +02:00
if ( $this -> TotalCount () <= $pageSize || ( isset ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'start' ]) && $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'start' ] >= $start )) {
2007-07-19 12:40:28 +02:00
return null ;
}
2009-09-15 06:11:13 +02:00
$baseLink = ( $this -> paginationBaseLink ) ? $this -> paginationBaseLink : $this -> Link ();
$link = Controller :: join_links ( $baseLink , " ?ctf[ { $this -> Name () } ][start]= { $start } " );
2008-08-11 01:17:51 +02:00
if ( $this -> extraLinkParams ) $link .= " & " . http_build_query ( $this -> extraLinkParams );
2010-10-15 04:29:44 +02:00
// preserve sort options
if ( isset ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'sort' ])) {
$link .= " &ctf[ { $this -> Name () } ][sort]= " . $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'sort' ];
// direction
if ( isset ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'dir' ])) {
$link .= " &ctf[ { $this -> Name () } ][dir]= " . $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'dir' ];
}
}
2008-08-11 01:17:51 +02:00
return $link ;
2007-07-19 12:40:28 +02:00
}
function FirstItem () {
2009-02-02 00:49:53 +01:00
if ( $this -> TotalCount () < 1 ) return 0 ;
2007-07-19 12:40:28 +02:00
return isset ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'start' ]) ? $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'start' ] + 1 : 1 ;
}
function LastItem () {
if ( isset ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'start' ])) {
return $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'start' ] + min ( $this -> pageSize , $this -> TotalCount () - $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'start' ]);
} else {
return min ( $this -> pageSize , $this -> TotalCount ());
}
}
function TotalCount () {
if ( $this -> totalCount ) {
return $this -> totalCount ;
}
if ( $this -> customSourceItems ) {
return $this -> customSourceItems -> Count ();
}
2010-04-13 04:05:45 +02:00
$this -> totalCount = $this -> getQuery () -> unlimitedRowCount ();
2007-07-19 12:40:28 +02:00
return $this -> totalCount ;
}
/**
* #################################
* Search
* #################################
*
2008-01-11 02:49:50 +01:00
* @ todo Not fully implemented at the moment
2007-07-19 12:40:28 +02:00
*/
/**
* Compile all request - parameters for search and pagination
* ( except the actual list - positions ) as a query - string .
*
* @ return String URL - parameters
*/
2008-08-11 01:17:51 +02:00
function filterString () {
2007-07-19 12:40:28 +02:00
}
/**
* #################################
* CSV Export
* #################################
*/
function setFieldListCsv ( $fields ) {
$this -> fieldListCsv = $fields ;
}
/**
2007-11-13 00:07:45 +01:00
* Set the CSV separator character . Defaults to ,
2007-07-19 12:40:28 +02:00
*/
function setCsvSeparator ( $csvSeparator ) {
$this -> csvSeparator = $csvSeparator ;
}
2008-08-11 01:53:56 +02:00
/**
* Get the CSV separator character . Defaults to ,
*/
function getCsvSeparator () {
return $this -> csvSeparator ;
}
2007-07-19 12:40:28 +02:00
/**
* Remove the header row from the CSV export
*/
function removeCsvHeader () {
$this -> csvHasHeader = false ;
}
/**
* Exports a given set of comma - separated IDs ( from a previous search - query , stored in a HiddenField ) .
* Uses { $csv_columns } if present , and falls back to { $result_columns } .
2009-02-02 00:49:53 +01:00
* We move the most filedata generation code to the function { @ link generateExportFileData ()} so that a child class
* could reuse the filedata generation code while overwrite export function .
2007-07-19 12:40:28 +02:00
*
2008-01-11 02:49:50 +01:00
* @ todo Make relation - syntax available ( at the moment you ' ll have to use custom sql )
2007-07-19 12:40:28 +02:00
*/
function export () {
$now = Date ( " d-m-Y-H-i " );
$fileName = " export- $now .csv " ;
2008-10-08 04:00:12 +02:00
2009-02-02 00:49:53 +01:00
if ( $fileData = $this -> generateExportFileData ( $numColumns , $numRows )){
API CHANGE: Renamed conflicting classes to have an "SS_" namespace, and renamed existing "SS" namespace to "SS_". The affected classes are: HTTPRequest, HTTPResponse, Query, Database, SSBacktrace, SSCli, SSDatetime, SSDatetimeTest, SSLog, SSLogTest, SSLogEmailWriter, SSLogErrorEmailFormatter, SSLogErrorFileFormatter, SSLogFileWriter and SSZendLog.
MINOR: Replaced usage of renamed classes with the new namespaced name.
From: Andrew Short <andrewjshort@gmail.com>
git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@90075 467b73ca-7a2a-4603-9d3b-597d59a354a9
2009-10-26 04:06:31 +01:00
return SS_HTTPRequest :: send_file ( $fileData , $fileName );
2009-02-02 00:49:53 +01:00
} else {
user_error ( " No records found " , E_USER_ERROR );
}
}
function generateExportFileData ( & $numColumns , & $numRows ) {
2007-07-19 12:40:28 +02:00
$separator = $this -> csvSeparator ;
$csvColumns = ( $this -> fieldListCsv ) ? $this -> fieldListCsv : $this -> fieldList ;
2009-02-02 00:49:53 +01:00
$fileData = '' ;
$columnData = array ();
$fieldItems = new DataObjectSet ();
2007-07-19 12:40:28 +02:00
if ( $this -> csvHasHeader ) {
2009-02-02 00:49:53 +01:00
$fileData .= " \" " . implode ( " \" { $separator } \" " , array_values ( $csvColumns )) . " \" " ;
2007-07-19 12:40:28 +02:00
$fileData .= " \n " ;
}
2009-02-02 00:49:53 +01:00
if ( isset ( $this -> customSourceItems )) {
2008-10-08 04:00:12 +02:00
$items = $this -> customSourceItems ;
2009-02-02 00:49:53 +01:00
} else {
2008-10-08 04:00:12 +02:00
$dataQuery = $this -> getCsvQuery ();
2009-05-14 07:26:47 +02:00
$items = $dataQuery -> execute ();
2008-08-11 01:53:56 +02:00
}
2008-08-11 02:21:44 +02:00
// temporary override to adjust TableListField_Item behaviour
2008-08-11 01:53:56 +02:00
$this -> setFieldFormatting ( array ());
2008-08-11 02:21:44 +02:00
$this -> fieldList = $csvColumns ;
2008-08-11 01:53:56 +02:00
2009-05-14 07:26:47 +02:00
if ( $items ) {
foreach ( $items as $item ) {
if ( is_array ( $item )) {
$className = isset ( $item [ 'RecordClassName' ]) ? $item [ 'RecordClassName' ] : $item [ 'ClassName' ];
$item = new $className ( $item );
}
2010-04-14 04:20:28 +02:00
$fieldItem = new $this -> itemClass ( $item , $this );
2009-05-14 07:26:47 +02:00
2009-05-20 05:09:50 +02:00
$fields = $fieldItem -> Fields ( false );
2009-02-02 00:49:53 +01:00
$columnData = array ();
if ( $fields ) foreach ( $fields as $field ) {
2008-10-08 04:00:12 +02:00
$value = $field -> Value ;
// TODO This should be replaced with casting
if ( array_key_exists ( $field -> Name , $this -> csvFieldFormatting )) {
2009-02-02 00:49:53 +01:00
$format = str_replace ( '$value' , " __VAL__ " , $this -> csvFieldFormatting [ $field -> Name ]);
2008-10-08 04:00:12 +02:00
$format = preg_replace ( '/\$([A-Za-z0-9-_]+)/' , '$item->$1' , $format );
$format = str_replace ( '__VAL__' , '$value' , $format );
eval ( '$value = "' . $format . '";' );
2008-08-11 02:21:44 +02:00
}
2008-10-08 04:00:12 +02:00
$value = str_replace ( array ( " \r " , " \n " ), " \n " , $value );
2010-04-13 04:16:08 +02:00
$tmpColumnData = '"' . str_replace ( '"' , '\"' , $value ) . '"' ;
2008-10-08 04:00:12 +02:00
$columnData [] = $tmpColumnData ;
2008-08-11 02:21:44 +02:00
}
2008-10-08 04:00:12 +02:00
$fileData .= implode ( $separator , $columnData );
$fileData .= " \n " ;
2009-05-14 07:26:47 +02:00
$item -> destroy ();
unset ( $item );
unset ( $fieldItem );
2007-07-19 12:40:28 +02:00
}
2009-02-02 00:49:53 +01:00
$numColumns = count ( $columnData );
$numRows = $fieldItems -> count ();
return $fileData ;
2007-07-19 12:40:28 +02:00
} else {
2009-02-02 00:49:53 +01:00
return null ;
2007-07-19 12:40:28 +02:00
}
}
/**
* We need to instanciate this button manually as a normal button has no means of adding inline onclick - behaviour .
*/
function ExportLink () {
2008-08-11 01:53:56 +02:00
$exportLink = Controller :: join_links ( $this -> Link (), 'export' );
2008-08-26 03:45:52 +02:00
2008-08-11 01:53:56 +02:00
if ( $this -> extraLinkParams ) $exportLink .= " ? " . http_build_query ( $this -> extraLinkParams );
return $exportLink ;
2007-07-19 12:40:28 +02:00
}
function printall () {
Requirements :: clear ();
ENHANCEMENT Introduced constants for system paths like /sapphire in preparation for a more flexible directory reorganisation. Instead of hardcoding your path, please use the following constants: BASE_PATH, BASE_URL, SAPPHIRE_DIR, SAPPHIRE_PATH, CMS_DIR, CMS_PATH, THIRDPARTY_DIR, THIRDPARTY_PATH, ASSETS_DIR, ASSETS_PATH, THEMES_DIR, THEMES_PATH
git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@63154 467b73ca-7a2a-4603-9d3b-597d59a354a9
2008-09-27 18:02:38 +02:00
Requirements :: css ( CMS_DIR . '/css/typography.css' );
Requirements :: css ( CMS_DIR . '/css/cms_right.css' );
Requirements :: css ( SAPPHIRE_DIR . '/css/TableListField_print.css' );
2008-10-08 04:00:12 +02:00
2010-10-04 06:44:58 +02:00
$this -> cachedSourceItems = null ;
2008-10-08 04:00:12 +02:00
$oldShowPagination = $this -> showPagination ;
$this -> showPagination = false ;
2009-06-28 04:36:46 +02:00
increase_time_limit_to ();
2010-04-13 04:26:52 +02:00
$this -> Print = true ;
2008-10-08 04:00:12 +02:00
$result = $this -> renderWith ( array ( $this -> template . '_printable' , 'TableListField_printable' ));
$this -> showPagination = $oldShowPagination ;
return $result ;
2007-07-19 12:40:28 +02:00
}
function PrintLink () {
2008-08-09 06:38:44 +02:00
$link = Controller :: join_links ( $this -> Link (), 'printall' );
2007-07-19 12:40:28 +02:00
if ( isset ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'sort' ])) {
$link = HTTP :: setGetVar ( " ctf[ { $this -> Name () } ][sort] " , Convert :: raw2xml ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'sort' ]), $link );
}
return $link ;
}
/**
* #################################
* Utilty
* #################################
*/
function Utility () {
$links = new DataObjectSet ();
if ( $this -> can ( 'export' )) {
$links -> push ( new ArrayData ( array (
2008-01-10 23:27:38 +01:00
'Title' => _t ( 'TableListField.CSVEXPORT' , 'Export to CSV' ),
2007-07-19 12:40:28 +02:00
'Link' => $this -> ExportLink ()
)));
}
if ( $this -> can ( 'print' )) {
$links -> push ( new ArrayData ( array (
2008-01-10 23:27:38 +01:00
'Title' => _t ( 'TableListField.PRINT' , 'Print' ),
2007-07-19 12:40:28 +02:00
'Link' => $this -> PrintLink ()
)));
}
return $links ;
}
2008-08-11 04:25:44 +02:00
/**
2008-08-28 06:14:22 +02:00
* Returns the content of the TableListField as a piece of FormResponse javascript
* @ deprecated Please use the standard URL through Link () which gives you the FieldHolder as an HTML fragment .
2008-08-11 04:25:44 +02:00
*/
2007-07-19 12:40:28 +02:00
function ajax_refresh () {
// compute sourceItems here instead of Items() to ensure that
// pagination and filters are respected on template accessors
//$this->sourceItems();
2008-08-26 03:45:52 +02:00
2008-08-27 06:58:08 +02:00
$response = $this -> renderWith ( $this -> template );
2008-08-26 03:45:52 +02:00
FormResponse :: update_dom_id ( $this -> id (), $response , 1 );
2007-07-19 12:40:28 +02:00
FormResponse :: set_non_ajax_content ( $response );
return FormResponse :: respond ();
}
function setFieldCasting ( $casting ) {
$this -> fieldCasting = $casting ;
}
function setFieldFormatting ( $formatting ) {
$this -> fieldFormatting = $formatting ;
}
2008-10-08 04:00:12 +02:00
function setCSVFieldFormatting ( $formatting ) {
$this -> csvFieldFormatting = $formatting ;
}
2008-12-04 23:38:32 +01:00
/**
* Edit the field list
*/
function setFieldList ( $fieldList ) {
$this -> fieldList = $fieldList ;
}
2007-07-19 12:40:28 +02:00
/**
* @ return String
*/
function Name () {
return $this -> name ;
}
function Title () {
2008-01-10 23:27:38 +01:00
// adding translating functionality
// this is a bit complicated, because this parameter is passed to this class
// and should come here translated already
// adding this to TODO probably add a method to the classes
// to return they're translated string
// added by ruibarreiros @ 27/11/2007
2008-10-08 04:00:12 +02:00
return $this -> sourceClass ? singleton ( $this -> sourceClass ) -> singular_name () : $this -> Name ();
2007-07-19 12:40:28 +02:00
}
function NameSingular () {
2008-01-10 23:27:38 +01:00
// same as Title()
// added by ruibarreiros @ 27/11/2007
2008-10-08 04:00:12 +02:00
return $this -> sourceClass ? singleton ( $this -> sourceClass ) -> singular_name () : $this -> Name ();
2007-07-19 12:40:28 +02:00
}
function NamePlural () {
2008-01-10 23:27:38 +01:00
// same as Title()
// added by ruibarreiros @ 27/11/2007
2008-10-08 04:00:12 +02:00
return $this -> sourceClass ? singleton ( $this -> sourceClass ) -> plural_name () : $this -> Name ();
2007-07-19 12:40:28 +02:00
}
function setTemplate ( $template ) {
$this -> template = $template ;
}
2008-08-11 04:57:59 +02:00
function CurrentLink () {
$link = $this -> Link ();
if ( isset ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'start' ]) && is_numeric ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'start' ])) {
$start = ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'start' ] < 0 ) ? 0 : $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'start' ];
2010-04-12 05:27:03 +02:00
$link = Controller :: join_links ( $link , " ?ctf[ { $this -> Name () } ][start]= { $start } " );
2008-08-11 04:57:59 +02:00
}
2008-08-09 06:38:44 +02:00
2008-08-11 04:57:59 +02:00
if ( $this -> extraLinkParams ) $link .= " & " . http_build_query ( $this -> extraLinkParams );
return $link ;
}
2010-12-05 09:24:58 +01:00
/**
* Overloaded to automatically add security token .
*
* @ param String $action
* @ return String
*/
function Link ( $action = null ) {
$form = $this -> getForm ();
if ( $form ) {
$token = $form -> getSecurityToken ();
$parentUrlParts = parse_url ( parent :: Link ( $action ));
$queryPart = ( isset ( $parentUrlParts [ 'query' ])) ? '?' . $parentUrlParts [ 'query' ] : null ;
// Ensure that URL actions not routed through Form->httpSubmission() are protected against CSRF attacks.
if ( $form -> securityTokenEnabled ()) $queryPart = $token -> addtoUrl ( $queryPart );
2011-01-10 04:46:28 +01:00
return Controller :: join_links ( $parentUrlParts [ 'path' ], $queryPart );
2010-12-05 09:24:58 +01:00
} else {
// allow for instanciation of this FormField outside of a controller/form
// context (e.g. for unit tests)
return false ;
}
}
2008-08-11 04:57:59 +02:00
2007-07-19 12:40:28 +02:00
function BaseLink () {
2008-08-09 06:38:44 +02:00
user_error ( " TableListField::BaseLink() deprecated, use Link() instead " , E_USER_NOTICE );
return $this -> Link ();
2007-07-19 12:40:28 +02:00
}
/**
* @ return Int
*/
function sourceID () {
$idField = $this -> form -> dataFieldByName ( 'ID' );
if ( ! isset ( $idField )) {
user_error ( " TableListField needs a formfield named 'ID' to be present " , E_USER_ERROR );
}
return $idField -> Value ();
}
2008-08-09 08:29:50 +02:00
/**
* Helper method to determine permissions for a scaffolded
* TableListField ( or subclasses ) - currently used in { @ link ModelAdmin } and { @ link DataObject -> scaffoldFormFields ()} .
* Returns true for each permission that doesn ' t have an explicit getter .
*
* @ todo Temporary method , implement directly in FormField subclasses with object - level permissions .
*
* @ param string $class
* @ param numeric $id
* @ return array
*/
public static function permissions_for_object ( $class , $id = null ) {
$permissions = array ();
$obj = ( $id ) ? DataObject :: get_by_id ( $class , $id ) : singleton ( $class );
if ( ! $obj -> hasMethod ( 'canView' ) || $obj -> canView ()) $permissions [] = 'show' ;
if ( ! $obj -> hasMethod ( 'canEdit' ) || $obj -> canEdit ()) $permissions [] = 'edit' ;
if ( ! $obj -> hasMethod ( 'canDelete' ) || $obj -> canDelete ()) $permissions [] = 'delete' ;
if ( ! $obj -> hasMethod ( 'canCreate' ) || $obj -> canCreate ()) $permissions [] = 'add' ;
return $permissions ;
}
2007-07-19 12:40:28 +02:00
2008-08-13 01:04:14 +02:00
/**
* @ param $value
*
*/
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 ;
}
2007-07-19 12:40:28 +02:00
function setHighlightConditions ( $conditions ) {
$this -> highlightConditions = $conditions ;
}
2010-10-15 04:27:59 +02:00
/**
* See { @ link SelectOptions ()} for introduction .
*
* @ param $options array Options to add , key being a unique identifier of the action ,
* and value a title for the rendered link element ( can contain HTML ) .
* The keys for 'all' and 'none' have special behaviour associated
* through TableListField . js JavaScript .
* For any other key , the JavaScript automatically checks all checkboxes contained in
* < td > elements with a matching classname .
*/
function addSelectOptions ( $options ){
foreach ( $options as $k => $title )
$this -> selectOptions [ $k ] = $title ;
}
/**
* Remove one all more table ' s { @ link $selectOptions }
*
* @ param $optionsNames array
*/
function removeSelectOptions ( $names ){
foreach ( $names as $name ){
unset ( $this -> selectOptions [ trim ( $name )]);
}
}
/**
* Return the table ' s { @ link $selectOptions } .
* Used to toggle checkboxes for each table row through button elements .
*
* Requires { @ link Markable ()} to return TRUE .
* This is only functional with JavaScript enabled .
*
* @ return DataObjectSet of ArrayData objects
*/
function SelectOptions (){
if ( ! $this -> selectOptions ) return ;
$selectOptionsSet = new DataObjectSet ();
foreach ( $this -> selectOptions as $k => $v ) {
$selectOptionsSet -> push ( new ArrayData ( array (
'Key' => $k ,
'Value' => $v
)));
}
return $selectOptionsSet ;
}
2007-07-19 12:40:28 +02:00
}
2008-01-09 05:18:36 +01:00
/**
* A single record in a TableListField .
* @ package forms
* @ subpackage fields - relational
* @ see TableListField
*/
2007-07-19 12:40:28 +02:00
class TableListField_Item extends ViewableData {
2009-03-13 11:07:27 +01:00
/**
* @ var DataObject The underlying data record ,
* usually an element of { @ link TableListField -> sourceItems ()} .
*/
protected $item ;
/**
* @ var TableListField
*/
protected $parent ;
2007-07-19 12:40:28 +02:00
function __construct ( $item , $parent ) {
$this -> failover = $this -> item = $item ;
$this -> parent = $parent ;
parent :: __construct ();
}
function ID () {
return $this -> item -> ID ;
}
function Parent () {
return $this -> parent ;
}
2009-05-20 05:09:50 +02:00
function Fields ( $xmlSafe = true ) {
2007-07-19 12:40:28 +02:00
$list = $this -> parent -> FieldList ();
foreach ( $list as $fieldName => $fieldTitle ) {
2008-08-11 02:03:57 +02:00
$value = " " ;
2007-07-19 12:40:28 +02:00
// This supports simple FieldName syntax
if ( strpos ( $fieldName , '.' ) === false ) {
2010-04-13 04:16:21 +02:00
$value = ( $this -> item -> XML_val ( $fieldName ) && $xmlSafe ) ? $this -> item -> XML_val ( $fieldName ) : $this -> item -> RAW_val ( $fieldName );
2009-05-28 09:08:23 +02:00
// This support the syntax fieldName = Relation.RelatedField
} else {
2007-07-19 12:40:28 +02:00
$fieldNameParts = explode ( '.' , $fieldName ) ;
$tmpItem = $this -> item ;
for ( $j = 0 ; $j < sizeof ( $fieldNameParts ); $j ++ ) {
$relationMethod = $fieldNameParts [ $j ];
$idField = $relationMethod . 'ID' ;
if ( $j == sizeof ( $fieldNameParts ) - 1 ) {
2008-08-11 01:17:51 +02:00
if ( $tmpItem ) $value = $tmpItem -> $relationMethod ;
2007-07-19 12:40:28 +02:00
} else {
2008-08-11 01:17:51 +02:00
if ( $tmpItem ) $tmpItem = $tmpItem -> $relationMethod ();
2007-07-19 12:40:28 +02:00
}
}
}
2009-05-18 07:30:26 +02:00
2007-07-19 12:40:28 +02:00
// casting
if ( array_key_exists ( $fieldName , $this -> parent -> fieldCasting )) {
2008-08-13 01:04:14 +02:00
$value = $this -> parent -> getCastedValue ( $value , $this -> parent -> fieldCasting [ $fieldName ]);
2009-05-21 06:48:24 +02:00
} elseif ( is_object ( $value ) && method_exists ( $value , 'Nice' )) {
$value = $value -> Nice ();
2007-07-19 12:40:28 +02:00
}
2009-05-20 05:09:50 +02:00
2007-07-19 12:40:28 +02:00
// formatting
$item = $this -> item ;
if ( array_key_exists ( $fieldName , $this -> parent -> fieldFormatting )) {
$format = str_replace ( '$value' , " __VAL__ " , $this -> parent -> fieldFormatting [ $fieldName ]);
$format = preg_replace ( '/\$([A-Za-z0-9-_]+)/' , '$item->$1' , $format );
$format = str_replace ( '__VAL__' , '$value' , $format );
eval ( '$value = "' . $format . '";' );
}
2008-08-11 01:53:56 +02:00
//escape
if ( $escape = $this -> parent -> fieldEscape ){
foreach ( $escape as $search => $replace ){
$value = str_replace ( $search , $replace , $value );
}
}
2009-05-18 07:30:26 +02:00
2007-07-19 12:40:28 +02:00
$fields [] = new ArrayData ( array (
" Name " => $fieldName ,
" Title " => $fieldTitle ,
" Value " => $value ,
2008-08-11 01:53:56 +02:00
" CsvSeparator " => $this -> parent -> getCsvSeparator (),
2007-07-19 12:40:28 +02:00
));
}
return new DataObjectSet ( $fields );
}
function Markable () {
return $this -> parent -> Markable ;
}
2009-03-13 11:07:27 +01:00
/**
* Checks global permissions for field in { @ link TableListField -> Can ()} .
* If they are allowed , it checks for object permissions by assuming
* a method with " can " + $mode parameter naming , e . g . canDelete () .
*
* @ param string $mode See { @ link TableListField :: $permissions } array .
* @ return boolean
*/
2007-07-19 12:40:28 +02:00
function Can ( $mode ) {
2009-03-13 11:07:27 +01:00
$canMethod = " can " . ucfirst ( $mode );
if ( ! $this -> parent -> Can ( $mode )) {
// check global settings for the field instance
return false ;
} elseif ( $this -> item -> hasMethod ( $canMethod )) {
// if global allows, check object specific permissions (e.g. canDelete())
return $this -> item -> $canMethod ();
} else {
// otherwise global allowed this action, so return TRUE
return true ;
}
2007-07-19 12:40:28 +02:00
}
2008-08-11 04:57:59 +02:00
2009-10-11 02:07:16 +02:00
function Link ( $action = null ) {
2010-12-05 09:24:58 +01:00
$form = $this -> parent -> getForm ();
if ( $form ) {
2010-12-05 09:26:03 +01:00
$token = $form -> getSecurityToken ();
2008-11-17 23:59:17 +01:00
$parentUrlParts = parse_url ( $this -> parent -> Link ());
$queryPart = ( isset ( $parentUrlParts [ 'query' ])) ? '?' . $parentUrlParts [ 'query' ] : null ;
2010-12-05 09:24:58 +01:00
// Ensure that URL actions not routed through Form->httpSubmission() are protected against CSRF attacks.
if ( $form -> securityTokenEnabled ()) $queryPart = $token -> addtoUrl ( $queryPart );
2009-10-11 02:07:16 +02:00
return Controller :: join_links ( $parentUrlParts [ 'path' ], 'item' , $this -> item -> ID , $action , $queryPart );
2008-10-08 04:00:12 +02:00
} else {
// allow for instanciation of this FormField outside of a controller/form
// context (e.g. for unit tests)
return false ;
}
}
2008-08-14 06:38:22 +02:00
/**
* Returns all row - based actions not disallowed through permissions .
* See TableListField -> Action for a similiar dummy - function to work
* around template - inheritance issues .
*
* @ return DataObjectSet
*/
function Actions () {
$allowedActions = new DataObjectSet ();
foreach ( $this -> parent -> actions as $actionName => $actionSettings ) {
if ( $this -> parent -> Can ( $actionName )) {
$allowedActions -> push ( new ArrayData ( array (
'Name' => $actionName ,
'Link' => $this -> { ucfirst ( $actionName ) . 'Link' }(),
'Icon' => $actionSettings [ 'icon' ],
2009-03-13 11:07:27 +01:00
'IconDisabled' => $actionSettings [ 'icon_disabled' ],
2008-08-14 06:38:22 +02:00
'Label' => $actionSettings [ 'label' ],
'Class' => $actionSettings [ 'class' ],
'Default' => ( $actionName == $this -> parent -> defaultAction ),
2009-03-13 11:07:27 +01:00
'IsAllowed' => $this -> Can ( $actionName ),
2008-08-14 06:38:22 +02:00
)));
}
}
return $allowedActions ;
}
2008-10-08 04:00:12 +02:00
2007-07-19 12:40:28 +02:00
function BaseLink () {
2008-08-09 06:38:44 +02:00
user_error ( " TableListField_Item::BaseLink() deprecated, use Link() instead " , E_USER_NOTICE );
2008-08-28 06:14:22 +02:00
return $this -> Link ();
2008-08-09 06:38:44 +02:00
}
2007-07-19 12:40:28 +02:00
function DeleteLink () {
2008-08-09 06:38:44 +02:00
return Controller :: join_links ( $this -> Link (), " delete " );
2007-07-19 12:40:28 +02:00
}
function MarkingCheckbox () {
$name = $this -> parent -> Name () . '[]' ;
2008-08-12 04:58:48 +02:00
if ( $this -> parent -> isReadonly ())
2007-07-19 12:40:28 +02:00
return " <input class= \" checkbox \" type= \" checkbox \" name= \" $name\ " value = \ " { $this -> item -> ID } \" disabled= \" disabled \" /> " ;
else
return " <input class= \" checkbox \" type= \" checkbox \" name= \" $name\ " value = \ " { $this -> item -> ID } \" /> " ;
}
2010-10-15 04:27:59 +02:00
/**
* According to { @ link TableListField -> selectOptions }, each record will check if the options ' key on the object is true ,
* if it is true , add the key as a class to the record
*
* @ return string Value for a 'class' HTML attribute .
*/
function SelectOptionClasses (){
$tagArray = array ( 'markingcheckbox' );
2010-10-15 04:52:05 +02:00
$options = $this -> parent -> SelectOptions ();
2010-10-15 04:51:33 +02:00
if ( $options && $options -> exists ()){
foreach ( $options as $option ){
if ( $option -> Key !== 'all' && $option -> Key !== 'none' ){
if ( $this -> { $option -> Key }) {
$tagArray [] = $option -> Key ;
2010-10-15 04:27:59 +02:00
}
}
}
}
return implode ( " " , $tagArray );
}
2007-07-19 12:40:28 +02:00
function HighlightClasses () {
$classes = array ();
foreach ( $this -> parent -> highlightConditions as $condition ) {
$rule = str_replace ( " \$ " , " \$ this->item-> " , $condition [ 'rule' ]);
$ruleApplies = null ;
eval ( '$ruleApplies = (' . $rule . ');' );
if ( $ruleApplies ) {
2010-10-19 03:36:39 +02:00
if ( isset ( $condition [ 'exclusive' ]) && $condition [ 'exclusive' ]) {
2007-07-19 12:40:28 +02:00
return $condition [ 'class' ];
} else {
$classes [] = $condition [ 'class' ];
}
}
}
return ( count ( $classes ) > 0 ) ? " " . implode ( " " , $classes ) : false ;
}
2008-08-14 06:38:22 +02:00
2007-07-19 12:40:28 +02:00
/**
* Legacy : Please use permissions instead
*/
2008-08-12 04:58:48 +02:00
function isReadonly () {
2007-07-19 12:40:28 +02:00
return $this -> parent -> Can ( 'delete' );
}
}
2010-04-23 02:11:41 +02:00
/**
* @ package forms
* @ subpackage fields - relational
*/
2008-10-30 23:03:21 +01:00
class TableListField_ItemRequest extends RequestHandler {
2008-10-08 04:00:12 +02:00
protected $ctf ;
protected $itemID ;
protected $methodName ;
static $url_handlers = array (
'$Action!' => '$Action' ,
'' => 'index' ,
);
function Link () {
2010-04-12 05:27:03 +02:00
return Controller :: join_links ( $this -> ctf -> Link (), 'item/' . $this -> itemID );
2008-10-08 04:00:12 +02:00
}
function __construct ( $ctf , $itemID ) {
$this -> ctf = $ctf ;
$this -> itemID = $itemID ;
2009-05-18 00:59:48 +02:00
parent :: __construct ();
2008-10-08 04:00:12 +02:00
}
2010-12-05 09:26:03 +01:00
function delete ( $request ) {
// Protect against CSRF on destructive action
$token = $this -> ctf -> getForm () -> getSecurityToken ();
if ( ! $token -> checkRequest ( $request )) return $this -> httpError ( '400' );
2008-10-08 04:00:12 +02:00
if ( $this -> ctf -> Can ( 'delete' ) !== true ) {
return false ;
}
$this -> dataObj () -> delete ();
}
///////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Return the data object being manipulated
*/
function dataObj () {
// used to discover fields if requested and for population of field
if ( is_numeric ( $this -> itemID )) {
// we have to use the basedataclass, otherwise we might exclude other subclasses
return DataObject :: get_by_id ( ClassInfo :: baseDataClass ( Object :: getCustomClass ( $this -> ctf -> sourceClass ())), $this -> itemID );
}
}
/**
* Returns the db - fieldname of the currently used has_one - relationship .
*/
function getParentIdName ( $parentClass , $childClass ) {
return $this -> getParentIdNameRelation ( $childClass , $parentClass , 'has_one' );
}
/**
* Manually overwrites the parent - ID relations .
* @ see setParentClass ()
*
* @ param String $str Example : FamilyID ( when one Individual has_one Family )
*/
function setParentIdName ( $str ) {
$this -> parentIdName = $str ;
}
/**
* Returns the db - fieldname of the currently used relationship .
*/
function getParentIdNameRelation ( $parentClass , $childClass , $relation ) {
if ( $this -> parentIdName ) return $this -> parentIdName ;
$relations = singleton ( $parentClass ) -> $relation ();
$classes = ClassInfo :: ancestry ( $childClass );
foreach ( $relations as $k => $v ) {
if ( array_key_exists ( $v , $classes )) return $k . 'ID' ;
}
return false ;
}
2010-05-25 07:00:25 +02:00
/**
* @ return TableListField
*/
function getParentController () {
return $this -> ctf ;
}
2008-10-08 04:00:12 +02:00
}
2009-05-18 07:30:26 +02:00
?>