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 ;
/**
* @ 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 " ;
/**
* @ 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 ;
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 .
*
2008-01-11 02:49:50 +01: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 ;
/**
* @ 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-04-29 02:07:39 +02:00
Requirements :: javascript ( THIRDPARTY_DIR . '/prototype.js' );
Requirements :: javascript ( THIRDPARTY_DIR . '/behaviour.js' );
Requirements :: javascript ( THIRDPARTY_DIR . '/prototype_improvements.js' );
Requirements :: javascript ( THIRDPARTY_DIR . '/scriptaculous/effects.js' );
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 ();
2007-07-19 12:40:28 +02:00
$sortLink = HTTP :: setGetVar ( " ctf[ { $this -> Name () } ][sort] " , $fieldName , $sortLink );
if ( isset ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'dir' ])) {
$XML_sort = ( isset ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'dir' ])) ? Convert :: raw2xml ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'dir' ]) : null ;
$sortLink = HTTP :: setGetVar ( " ctf[ { $this -> Name () } ][dir] " , $XML_sort , $sortLink );
}
} else {
$sortLink = '#' ;
}
$headings [] = new ArrayData ( array (
" Name " => $fieldName ,
2008-10-10 14:13:23 +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 );
}
/**
* 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 ) {
if ( $this -> customSourceItems ) {
return false ;
}
2008-10-08 04:00:12 +02:00
if ( $this -> __cachedQuery ) {
2007-07-19 12:40:28 +02:00
$query = $this -> __cachedQuery ;
} else {
$query = $this -> __cachedQuery = $this -> getQuery ();
}
$sql = $query -> sql ();
$SQL_fieldName = Convert :: raw2sql ( $fieldName );
return ( in_array ( $SQL_fieldName , $query -> select ) || stripos ( $sql , " AS { $SQL_fieldName } " ));
}
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 ) .
*
* @ param $query Query
*/
function setCustomQuery ( $query ) {
$this -> customQuery = $query ;
}
function setCustomCsvQuery ( $query ) {
$this -> customCsvQuery = $query ;
}
function setCustomSourceItems ( $items ) {
$this -> customSourceItems = $items ;
}
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
2008-04-26 08:54:38 +02:00
if ( ! isset ( $_REQUEST [ 'methodName' ]) || ! in_array ( $_REQUEST [ '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 ) {
2008-08-11 01:53:56 +02:00
$fieldItem = new TableListField_Item ( $item , $this );
2007-07-19 12:40:28 +02:00
if ( $item ) $fieldItems -> push ( new TableListField_Item ( $item , $this ));
}
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 );
2008-11-24 10:31:14 +01:00
$query -> select [] = " { $baseClass } .ID AS \" ID \" " ;
$query -> select [] = " { $baseClass } .ClassName AS \" ClassName \" " ;
2008-11-24 00:28:16 +01:00
$query -> select [] = " { $baseClass } .ClassName AS \" RecordClassName \" " ;
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
// Add more selected fields if they are from joined table.
$SNG = singleton ( $this -> sourceClass );
foreach ( $this -> FieldList () as $k => $title ){
2008-08-12 02:12:29 +02:00
if ( ! $SNG -> hasField ( $k ) && ! $SNG -> hasMethod ( 'get' . $k ) && ! $SNG -> hasMethod ( $k ) && ! strpos ( $k , " . " ))
2008-11-23 01:31:06 +01:00
$query -> select [] = " \" $k\ " " ;
2007-07-19 12:40:28 +02:00
}
}
if ( isset ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'sort' ])) {
$SQL_sort = Convert :: raw2sql ( $_REQUEST [ 'ctf' ][ $this -> Name ()][ 'sort' ]);
$sql = $query -> sql ();
// see {isFieldSortable}
if ( in_array ( $SQL_sort , $query -> select ) || stripos ( $sql , " AS { $SQL_sort } " )) {
$query -> orderby = $SQL_sort ;
}
}
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 );
if ( $this -> customCsvQuery ) {
$query = $this -> customCsvQuery ;
$query -> select [] = " { $baseClass } .ID AS ID " ;
$query -> select [] = " { $baseClass } .ClassName AS ClassName " ;
2008-11-24 00:28:16 +01:00
$query -> select [] = " { $baseClass } .ClassName AS \" RecordClassName \" " ;
2007-07-19 12:40:28 +02:00
} else if ( $this -> customQuery ) {
$query = $this -> customQuery ;
$query -> select [] = " { $baseClass } .ID AS ID " ;
$query -> select [] = " { $baseClass } .ClassName AS ClassName " ;
2008-11-24 00:28:16 +01:00
$query -> select [] = " { $baseClass } .ClassName AS \" RecordClassName \" " ;
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
// Add more selected fields if they are from joined table.
foreach ( $this -> FieldList () as $k => $title ){
2008-03-12 10:23:53 +01:00
if ( singleton ( $this -> sourceClass ) -> hasDatabaseField ( $k ))
2008-11-23 01:31:06 +01:00
$query -> select [] = " \" $k\ " " ;
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 );
$clone -> setPermissions ( array ( 'show' ));
$clone -> addExtraClass ( 'readonly' );
$clone -> setReadonly ( true );
return $clone ;
2007-07-19 12:40:28 +02:00
}
/**
* #################################
* CRUD
* #################################
*/
/**
* @ return String
*/
function delete () {
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 ) {
if ( $item ) $fieldItems -> push ( new TableListField_Item ( $item , $this ));
}
$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 ;
}
2008-08-28 06:14:22 +02:00
$link = Controller :: join_links ( $this -> Link (), " ?ctf[ { $this -> Name () } ][start]= { $start } " );
2008-08-11 01:17:51 +02:00
if ( $this -> extraLinkParams ) $link .= " & " . http_build_query ( $this -> extraLinkParams );
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 ;
2008-08-28 06:14:22 +02:00
$link = Controller :: join_links ( $this -> Link (), " ?ctf[ { $this -> Name () } ][start]= { $start } " );
2008-08-11 01:17:51 +02:00
if ( $this -> extraLinkParams ) $link .= " & " . http_build_query ( $this -> extraLinkParams );
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 ;
}
2008-08-28 06:14:22 +02:00
$link = Controller :: join_links ( $this -> Link (), " ?ctf[ { $this -> Name () } ][start]= { $start } " );
2008-08-11 01:17:51 +02:00
if ( $this -> extraLinkParams ) $link .= " & " . http_build_query ( $this -> extraLinkParams );
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 ;
}
2008-08-28 06:14:22 +02:00
$link = Controller :: join_links ( $this -> Link (), " ?ctf[ { $this -> Name () } ][start]= { $start } " );
2008-08-11 01:17:51 +02:00
if ( $this -> extraLinkParams ) $link .= " & " . http_build_query ( $this -> extraLinkParams );
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 ();
}
$countQuery = $this -> getQuery ();
$countQuery -> orderby = array ();
$baseClass = ClassInfo :: baseDataClass ( $this -> sourceClass );
// we can't clear the select if we're relying on its output by a HAVING clause
if ( count ( $countQuery -> having ) || count ( $countQuery -> groupby )) {
$records = $countQuery -> execute ();
// TODO figure out how to use COUNT and GROUBY together to produce a single rowcount
$this -> totalCount = $records -> numRecords ();
} else {
$countQuery -> select = array ();
$countQuery -> groupby = array ();
2008-11-24 10:31:14 +01:00
$countQuery -> select [] = " COUNT(DISTINCT \" { $baseClass } \" . \" ID \" ) AS \" TotalCount \" " ;
2007-07-19 12:40:28 +02:00
$records = $countQuery -> execute ();
$record = $records -> nextRecord ();
$this -> totalCount = $record [ 'TotalCount' ];
}
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 )){
return HTTPRequest :: send_file ( $fileData , $fileName );
} 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 ();
$records = $dataQuery -> execute ();
$sourceClass = $this -> sourceClass ;
$dataobject = new $sourceClass ();
$items = $dataobject -> buildDataObjectSet ( $records , 'DataObjectSet' );
}
2007-07-19 12:40:28 +02:00
2008-08-11 02:21:44 +02:00
if ( $items && $items -> count ()) foreach ( $items as $item ) {
// create a TableListField_Item to support resolving of
// relation-fields in dot notation via TableListField_Item->Fields()
2008-08-11 01:53:56 +02:00
if ( $item ) $fieldItems -> push ( new TableListField_Item ( $item , $this ));
}
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
if ( $fieldItems ) {
foreach ( $fieldItems as $fieldItem ) {
2008-08-11 02:21:44 +02:00
$fields = $fieldItem -> Fields ();
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 );
$tmpColumnData = " \" " . str_replace ( " \" " , " \" \" " , $value ) . " \" " ;
$columnData [] = $tmpColumnData ;
2008-08-11 02:21:44 +02:00
}
2008-10-08 04:00:12 +02:00
$fileData .= implode ( $separator , $columnData );
$fileData .= " \n " ;
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
unset ( $this -> cachedSourceItems );
$oldShowPagination = $this -> showPagination ;
$this -> showPagination = false ;
$oldLimit = ini_get ( 'max_execution_time' );
set_time_limit ( 0 );
$result = $this -> renderWith ( array ( $this -> template . '_printable' , 'TableListField_printable' ));
$this -> showPagination = $oldShowPagination ;
set_time_limit ( $oldLimit );
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' ];
$link .= " /?ctf[ { $this -> Name () } ][start]= { $start } " ;
}
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 ;
}
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
/**
* #########################
* Highlighting
* #########################
*/
function setHighlightConditions ( $conditions ) {
$this -> highlightConditions = $conditions ;
}
}
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 ;
}
function Fields () {
$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 ) {
$value = ( $this -> item -> val ( $fieldName )) ? $this -> item -> val ( $fieldName ) : $this -> item -> $fieldName ;
// This support the syntax fieldName = Relation.RelatedField
} else {
$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
}
}
}
// 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 ]);
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 );
}
}
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
2008-10-08 04:00:12 +02:00
function Link () {
if ( $this -> parent -> getForm ()) {
2008-11-17 23:59:17 +01:00
$parentUrlParts = parse_url ( $this -> parent -> Link ());
$queryPart = ( isset ( $parentUrlParts [ 'query' ])) ? '?' . $parentUrlParts [ 'query' ] : null ;
return Controller :: join_links ( $parentUrlParts [ 'path' ], 'item' , $this -> item -> ID , $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 } \" /> " ;
}
function HighlightClasses () {
$classes = array ();
foreach ( $this -> parent -> highlightConditions as $condition ) {
$rule = str_replace ( " \$ " , " \$ this->item-> " , $condition [ 'rule' ]);
$ruleApplies = null ;
eval ( '$ruleApplies = (' . $rule . ');' );
if ( $ruleApplies ) {
if ( $condition [ 'exclusive' ]) {
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' );
}
}
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 () {
return $this -> ctf -> Link () . '/item/' . $this -> itemID ;
}
function __construct ( $ctf , $itemID ) {
$this -> ctf = $ctf ;
$this -> itemID = $itemID ;
}
function delete () {
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 ;
}
}
2009-02-02 00:49:53 +01:00
?>