2007-08-18 04:33:28 +02:00
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* TableField behaves in the same manner as TableListField, however allows the addition of
|
|
|
|
* fields and editing of attributes specified, and filtering results.
|
|
|
|
*
|
|
|
|
* Caution: If you insert DropdownFields in the fieldTypes-array, make sure they have an empty first option.
|
|
|
|
* Otherwise the saving can't determine if a new row should really be saved.
|
|
|
|
*
|
|
|
|
* Caution: TableField relies on {@FormResponse} to reload the field after it is saved.
|
|
|
|
* A TableField-instance should never be saved twice without reloading, because otherwise it
|
|
|
|
* can't determine if a field is new (=create) or existing (=update), and will produce duplicates.
|
|
|
|
*
|
2011-10-28 03:37:27 +02:00
|
|
|
* @todo We should refactor this to support a single FieldList instead of evaluated Strings for building FormFields
|
2008-04-06 05:36:07 +02:00
|
|
|
*
|
2008-01-09 05:18:36 +01:00
|
|
|
* @package forms
|
|
|
|
* @subpackage fields-relational
|
2007-08-18 04:33:28 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
class TableField extends TableListField {
|
|
|
|
|
|
|
|
protected $fieldList;
|
|
|
|
|
2008-08-22 05:33:43 +02:00
|
|
|
/**
|
|
|
|
* A "Field = Value" filter can be specified by setting $this->filterField and $this->filterValue. This has the advantage of auto-populating
|
|
|
|
* new records
|
|
|
|
*/
|
|
|
|
protected $filterField = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A "Field = Value" filter can be specified by setting $this->filterField and $this->filterValue. This has the advantage of auto-populating
|
|
|
|
* new records
|
|
|
|
*/
|
|
|
|
protected $filterValue = null;
|
|
|
|
|
2007-08-18 04:33:28 +02:00
|
|
|
/**
|
2011-10-28 03:37:27 +02:00
|
|
|
* @var $fieldTypes FieldList
|
2007-08-18 04:33:28 +02:00
|
|
|
* Caution: Use {@setExtraData()} instead of manually adding HiddenFields if you want to
|
|
|
|
* preset relations or other default data.
|
|
|
|
*/
|
|
|
|
protected $fieldTypes;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var $template string Template-Overrides
|
|
|
|
*/
|
|
|
|
protected $template = "TableField";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var $extraData array Any extra data that need to be included, e.g. to retain
|
|
|
|
* has-many relations. Format: array('FieldName' => 'Value')
|
|
|
|
*/
|
|
|
|
protected $extraData;
|
|
|
|
|
|
|
|
protected $tempForm;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Influence output without having to subclass the template.
|
|
|
|
*/
|
|
|
|
protected $permissions = array(
|
|
|
|
"edit",
|
|
|
|
"delete",
|
|
|
|
"add",
|
|
|
|
//"export",
|
|
|
|
);
|
|
|
|
|
|
|
|
public $transformationConditions = array();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var $requiredFields array Required fields as a numerical array.
|
|
|
|
* Please use an instance of Validator on the including
|
|
|
|
* form.
|
|
|
|
*/
|
|
|
|
protected $requiredFields = null;
|
|
|
|
|
|
|
|
/**
|
2008-01-16 21:34:40 +01:00
|
|
|
* Shows a row of empty fields for adding a new record
|
|
|
|
* (turned on by default).
|
|
|
|
* Please use {@link TableField::$permissions} to control
|
|
|
|
* if the "add"-functionality incl. button is shown at all.
|
|
|
|
*
|
|
|
|
* @param boolean $showAddRow
|
2007-08-18 04:33:28 +02:00
|
|
|
*/
|
2008-01-16 21:34:40 +01:00
|
|
|
public $showAddRow = true;
|
2009-07-17 01:49:32 +02:00
|
|
|
|
|
|
|
/**
|
2012-03-12 17:20:40 +01:00
|
|
|
* @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 $fieldTypes array An array of field types of fieldname => fieldType (eg. formfield). Do not use for extra data/hiddenfields.
|
|
|
|
* @param $filterField string The field to filter by. Give the filter value in $sourceFilter. The value will automatically be set on new records.
|
|
|
|
* @param $sourceFilter string If $filterField has a value, then this is the value to filter by. Otherwise, it is a SQL filter expression.
|
|
|
|
* @param $editExisting boolean (Note: Has to stay on this position for legacy reasons)
|
|
|
|
* @param $sourceSort string
|
|
|
|
* @param $sourceJoin string
|
2009-07-17 01:49:32 +02:00
|
|
|
*/
|
2008-08-11 00:49:59 +02:00
|
|
|
function __construct($name, $sourceClass, $fieldList = null, $fieldTypes, $filterField = null,
|
2007-08-18 04:33:28 +02:00
|
|
|
$sourceFilter = null, $editExisting = true, $sourceSort = null, $sourceJoin = null) {
|
|
|
|
|
|
|
|
$this->fieldTypes = $fieldTypes;
|
|
|
|
$this->filterField = $filterField;
|
2007-11-22 03:17:16 +01:00
|
|
|
|
2007-11-25 10:48:25 +01:00
|
|
|
$this->editExisting = $editExisting;
|
2008-08-22 05:33:43 +02:00
|
|
|
|
|
|
|
// If we specify filterField, then an implicit source filter of "filterField = sourceFilter" is used.
|
|
|
|
if($filterField) {
|
|
|
|
$this->filterValue = $sourceFilter;
|
2008-11-23 01:31:06 +01:00
|
|
|
$sourceFilter = "\"$filterField\" = '" . Convert::raw2sql($sourceFilter) . "'";
|
2008-08-22 05:33:43 +02:00
|
|
|
}
|
|
|
|
parent::__construct($name, $sourceClass, $fieldList, $sourceFilter, $sourceSort, $sourceJoin);
|
2007-08-18 04:33:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Displays the headings on the template
|
|
|
|
*
|
2011-10-26 08:09:04 +02:00
|
|
|
* @return SS_List
|
2007-08-18 04:33:28 +02:00
|
|
|
*/
|
|
|
|
function Headings() {
|
|
|
|
$i=0;
|
|
|
|
foreach($this->fieldList as $fieldName => $fieldTitle) {
|
|
|
|
$extraClass = "col".$i;
|
|
|
|
$class = $this->fieldTypes[$fieldName];
|
|
|
|
if(is_object($class)) $class = "";
|
|
|
|
$class = $class." ".$extraClass;
|
2007-11-25 10:48:25 +01:00
|
|
|
$headings[] = new ArrayData(array("Name" => $fieldName, "Title" => $fieldTitle, "Class" => $class));
|
2007-08-18 04:33:28 +02:00
|
|
|
$i++;
|
|
|
|
}
|
2011-05-05 12:40:24 +02:00
|
|
|
return new ArrayList($headings);
|
2007-08-18 04:33:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Calculates the number of columns needed for colspans
|
|
|
|
* used in template
|
|
|
|
*
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
function ItemCount() {
|
|
|
|
return count($this->fieldList);
|
|
|
|
}
|
2010-10-19 05:56:23 +02:00
|
|
|
|
2007-08-18 04:33:28 +02:00
|
|
|
/**
|
2009-07-17 02:13:42 +02:00
|
|
|
* Displays the items from {@link sourceItems()} using the encapsulation object.
|
|
|
|
* If the field value has been set as an array (e.g. after a failed validation),
|
|
|
|
* it generates the rows from array data instead.
|
|
|
|
* Used in the formfield template to iterate over each row.
|
2007-08-18 04:33:28 +02:00
|
|
|
*
|
2011-10-26 08:09:04 +02:00
|
|
|
* @return SS_List Collection of {@link TableField_Item}
|
2007-08-18 04:33:28 +02:00
|
|
|
*/
|
|
|
|
function Items() {
|
2009-07-17 04:09:27 +02:00
|
|
|
// holds TableField_Item instances
|
2011-05-05 12:40:24 +02:00
|
|
|
$items = new ArrayList();
|
2009-07-17 04:09:27 +02:00
|
|
|
|
|
|
|
$sourceItems = $this->sourceItems();
|
|
|
|
|
|
|
|
// either load all rows from the field value,
|
|
|
|
// (e.g. when validation failed), or from sourceItems()
|
|
|
|
if($this->value) {
|
2011-05-05 12:40:24 +02:00
|
|
|
if(!$sourceItems) $sourceItems = new ArrayList();
|
2009-07-17 04:09:27 +02:00
|
|
|
|
|
|
|
// get an array keyed by rows, rather than values
|
|
|
|
$rows = $this->sortData(ArrayLib::invert($this->value));
|
|
|
|
// ignore all rows which are already saved
|
|
|
|
if(isset($rows['new'])) {
|
2011-05-04 03:40:00 +02:00
|
|
|
if($sourceItems instanceof DataList) {
|
|
|
|
$sourceItems = new ArrayList($sourceItems->toArray());
|
|
|
|
}
|
|
|
|
|
2009-07-17 04:09:27 +02:00
|
|
|
$newRows = $this->sortData($rows['new']);
|
|
|
|
// iterate over each value (not each row)
|
|
|
|
$i = 0;
|
|
|
|
foreach($newRows as $idx => $newRow){
|
|
|
|
// set a pseudo-ID
|
|
|
|
$newRow['ID'] = "new";
|
|
|
|
|
|
|
|
// unset any extradata
|
|
|
|
foreach($newRow as $k => $v){
|
|
|
|
if($this->extraData && array_key_exists($k, $this->extraData)){
|
|
|
|
unset($newRow[$k]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// generate a temporary DataObject container (not saved in the database)
|
2009-11-22 07:07:45 +01:00
|
|
|
$sourceClass = $this->sourceClass();
|
2011-05-04 03:40:00 +02:00
|
|
|
$sourceItems->add(new $sourceClass($newRow));
|
2009-07-17 04:09:27 +02:00
|
|
|
|
|
|
|
$i++;
|
2007-08-18 04:33:28 +02:00
|
|
|
}
|
|
|
|
}
|
2009-07-17 04:09:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// generate a new TableField_Item instance from each collected item
|
|
|
|
if($sourceItems) foreach($sourceItems as $sourceItem) {
|
|
|
|
$items->push($this->generateTableFieldItem($sourceItem));
|
2007-08-18 04:33:28 +02:00
|
|
|
}
|
2009-07-17 04:09:27 +02:00
|
|
|
|
|
|
|
// add an empty TableField_Item for a single "add row"
|
|
|
|
if($this->showAddRow && $this->Can('add')) {
|
|
|
|
$items->push(new TableField_Item(null, $this, null, $this->fieldTypes, true));
|
2007-08-18 04:33:28 +02:00
|
|
|
}
|
2009-07-17 04:09:27 +02:00
|
|
|
|
|
|
|
return $items;
|
2007-08-18 04:33:28 +02:00
|
|
|
}
|
2009-07-17 04:09:27 +02:00
|
|
|
|
2007-08-18 04:33:28 +02:00
|
|
|
/**
|
2009-07-17 04:09:27 +02:00
|
|
|
* Generates a new {@link TableField} instance
|
2011-10-28 03:37:27 +02:00
|
|
|
* by loading a FieldList for this row into a temporary form.
|
2007-08-18 04:33:28 +02:00
|
|
|
*
|
2009-07-17 04:09:27 +02:00
|
|
|
* @param DataObject $dataObj
|
|
|
|
* @return TableField_Item
|
2007-08-18 04:33:28 +02:00
|
|
|
*/
|
2009-07-17 04:09:27 +02:00
|
|
|
protected function generateTableFieldItem($dataObj) {
|
|
|
|
// Load the data in to a temporary form (for correct field types)
|
|
|
|
$form = new Form(
|
|
|
|
$this,
|
|
|
|
null,
|
|
|
|
$this->FieldSetForRow(),
|
2011-05-11 09:51:54 +02:00
|
|
|
new FieldList()
|
2009-07-17 04:09:27 +02:00
|
|
|
);
|
|
|
|
$form->loadDataFrom($dataObj);
|
|
|
|
|
2011-05-05 12:40:24 +02:00
|
|
|
// Add the item to our new ArrayList, with a wrapper class.
|
2009-07-17 04:09:27 +02:00
|
|
|
return new TableField_Item($dataObj, $this, $form, $this->fieldTypes);
|
2007-11-25 10:48:25 +01:00
|
|
|
}
|
|
|
|
|
2007-08-18 04:33:28 +02:00
|
|
|
/**
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
function FieldList() {
|
|
|
|
return $this->fieldList;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Saves the Dataobjects contained in the field
|
|
|
|
*/
|
|
|
|
function saveInto(DataObject $record) {
|
|
|
|
// CMS sometimes tries to set the value to one.
|
|
|
|
if(is_array($this->value)){
|
2009-07-17 04:27:13 +02:00
|
|
|
$newFields = array();
|
|
|
|
|
2007-08-18 04:33:28 +02:00
|
|
|
// Sort into proper array
|
2009-07-17 02:12:56 +02:00
|
|
|
$value = ArrayLib::invert($this->value);
|
|
|
|
$dataObjects = $this->sortData($value, $record->ID);
|
|
|
|
|
|
|
|
// New fields are nested in their own sub-array, and need to be sorted separately
|
|
|
|
if(isset($dataObjects['new']) && $dataObjects['new']) {
|
|
|
|
$newFields = $this->sortData($dataObjects['new'], $record->ID);
|
|
|
|
}
|
2007-11-25 10:48:25 +01:00
|
|
|
|
2009-07-17 02:12:56 +02:00
|
|
|
// Update existing fields
|
|
|
|
// @todo Should this be in an else{} statement?
|
|
|
|
$savedObjIds = $this->saveData($dataObjects, $this->editExisting);
|
|
|
|
|
|
|
|
// Save newly added record
|
2009-07-17 04:27:13 +02:00
|
|
|
if($savedObjIds || $newFields) {
|
2009-07-17 02:12:56 +02:00
|
|
|
$savedObjIds = $this->saveData($newFields,false);
|
|
|
|
}
|
2009-07-17 01:49:32 +02:00
|
|
|
|
2009-11-22 07:07:45 +01:00
|
|
|
// Add the new records to the DataList
|
|
|
|
if($savedObjIds) foreach($savedObjIds as $id => $status) {
|
|
|
|
$this->getDataList()->add($id);
|
|
|
|
}
|
2009-07-17 01:49:32 +02:00
|
|
|
|
|
|
|
// Update the internal source items cache
|
2010-04-12 03:35:47 +02:00
|
|
|
$this->value = null;
|
2007-08-18 04:33:28 +02:00
|
|
|
$items = $this->sourceItems();
|
2009-07-17 01:49:32 +02:00
|
|
|
|
2012-03-09 23:19:37 +01:00
|
|
|
// FormResponse::update_dom_id($this->id(), $this->FieldHolder());
|
2007-08-18 04:33:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-07-17 02:13:42 +02:00
|
|
|
* Get all {@link FormField} instances necessary for a single row,
|
|
|
|
* respecting the casting set in {@link $fieldTypes}.
|
|
|
|
* Doesn't populate with any data. Optionally performs a readonly
|
|
|
|
* transformation if {@link $IsReadonly} is set, or the current user
|
|
|
|
* doesn't have edit permissions.
|
2007-08-18 04:33:28 +02:00
|
|
|
*
|
2011-09-27 00:55:49 +02:00
|
|
|
* @return FieldList
|
2007-08-18 04:33:28 +02:00
|
|
|
*/
|
|
|
|
function FieldSetForRow() {
|
2011-05-11 09:51:54 +02:00
|
|
|
$fieldset = new FieldList();
|
2007-08-18 04:33:28 +02:00
|
|
|
if($this->fieldTypes){
|
|
|
|
foreach($this->fieldTypes as $key => $fieldType) {
|
|
|
|
if(isset($fieldType->class) && is_subclass_of($fieldType, 'FormField')) {
|
|
|
|
// using clone, otherwise we would just add stuff to the same field-instance
|
|
|
|
$field = clone $fieldType;
|
|
|
|
} elseif(strpos($fieldType, '(') === false) {
|
|
|
|
$field = new $fieldType($key);
|
|
|
|
} else {
|
|
|
|
$fieldName = $key;
|
|
|
|
$fieldTitle = "";
|
|
|
|
$field = eval("return new $fieldType;");
|
|
|
|
}
|
|
|
|
if($this->IsReadOnly || !$this->Can('edit')) {
|
|
|
|
$field = $field->performReadonlyTransformation();
|
|
|
|
}
|
|
|
|
$fieldset->push($field);
|
|
|
|
}
|
|
|
|
}else{
|
|
|
|
USER_ERROR("TableField::FieldSetForRow() - Fieldtypes were not specified",E_USER_WARNING);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $fieldset;
|
|
|
|
}
|
|
|
|
|
2007-11-25 10:48:25 +01:00
|
|
|
function performReadonlyTransformation() {
|
2008-12-04 23:38:32 +01:00
|
|
|
$clone = clone $this;
|
|
|
|
$clone->permissions = array('show');
|
|
|
|
$clone->setReadonly(true);
|
|
|
|
return $clone;
|
2007-11-25 10:48:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function performDisabledTransformation() {
|
2008-12-04 23:38:32 +01:00
|
|
|
$clone = clone $this;
|
|
|
|
$clone->setPermissions(array('show'));
|
|
|
|
$clone->setDisabled(true);
|
|
|
|
return $clone;
|
2007-11-25 10:48:25 +01:00
|
|
|
}
|
|
|
|
|
2007-08-18 04:33:28 +02:00
|
|
|
/**
|
|
|
|
* Needed for Form->callfieldmethod.
|
|
|
|
*/
|
|
|
|
public function getField($fieldName, $combinedFieldName = null) {
|
|
|
|
$fieldSet = $this->FieldSetForRow();
|
|
|
|
$field = $fieldSet->dataFieldByName($fieldName);
|
|
|
|
if(!$field) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if($combinedFieldName) {
|
|
|
|
$field->Name = $combinedFieldName;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $field;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Called on save, it creates the appropriate objects and writes them
|
|
|
|
* to the database.
|
2009-07-17 02:13:42 +02:00
|
|
|
*
|
2011-10-26 08:09:04 +02:00
|
|
|
* @param SS_List $dataObjects
|
2009-07-17 02:13:42 +02:00
|
|
|
* @param boolean $existingValues If set to TRUE, it tries to find existing objects
|
|
|
|
* based on the database IDs passed as array keys in $dataObjects parameter.
|
|
|
|
* If set to FALSE, it will always create new object (default: TRUE)
|
|
|
|
* @return array Array of saved object IDs in the key, and the status ("Updated") in the value
|
2007-08-18 04:33:28 +02:00
|
|
|
*/
|
2009-07-17 02:17:21 +02:00
|
|
|
function saveData($dataObjects, $existingValues = true) {
|
|
|
|
if(!$dataObjects) return false;
|
|
|
|
|
|
|
|
$savedObjIds = array();
|
2009-07-17 02:16:52 +02:00
|
|
|
$fieldset = $this->FieldSetForRow();
|
2009-07-17 02:17:21 +02:00
|
|
|
|
2009-07-17 02:16:52 +02:00
|
|
|
// add hiddenfields
|
|
|
|
if($this->extraData) {
|
|
|
|
foreach($this->extraData as $fieldName => $fieldValue) {
|
2008-07-30 08:40:17 +02:00
|
|
|
$fieldset->push(new HiddenField($fieldName));
|
2007-08-18 04:33:28 +02:00
|
|
|
}
|
2009-07-17 02:16:52 +02:00
|
|
|
}
|
2009-07-17 02:17:21 +02:00
|
|
|
|
2011-05-11 09:51:54 +02:00
|
|
|
$form = new Form($this, null, $fieldset, new FieldList());
|
2007-08-18 04:33:28 +02:00
|
|
|
|
2009-07-17 02:17:21 +02:00
|
|
|
foreach ($dataObjects as $objectid => $fieldValues) {
|
|
|
|
// 'new' counts as an empty column, don't save it
|
2009-07-17 02:16:52 +02:00
|
|
|
if($objectid === "new") continue;
|
|
|
|
|
|
|
|
// extra data was creating fields, but
|
|
|
|
if($this->extraData) {
|
|
|
|
$fieldValues = array_merge( $this->extraData, $fieldValues );
|
|
|
|
}
|
2007-11-25 10:48:25 +01:00
|
|
|
|
2009-07-17 02:17:21 +02:00
|
|
|
// either look for an existing object, or create a new one
|
|
|
|
if($existingValues) {
|
2009-11-22 07:07:45 +01:00
|
|
|
$obj = DataObject::get_by_id($this->sourceClass(), $objectid);
|
2009-07-17 02:17:21 +02:00
|
|
|
} else {
|
2009-11-22 07:07:45 +01:00
|
|
|
$sourceClass = $this->sourceClass();
|
2009-07-17 02:17:21 +02:00
|
|
|
$obj = new $sourceClass();
|
2009-07-17 02:16:52 +02:00
|
|
|
}
|
2008-07-30 08:40:17 +02:00
|
|
|
|
2009-07-17 02:17:21 +02:00
|
|
|
// Legacy: Use the filter as a predefined relationship-ID
|
|
|
|
if($this->filterField && $this->filterValue) {
|
|
|
|
$filterField = $this->filterField;
|
|
|
|
$obj->$filterField = $this->filterValue;
|
|
|
|
}
|
2009-07-17 02:16:52 +02:00
|
|
|
|
2009-07-17 02:17:21 +02:00
|
|
|
// Determine if there is changed data for saving
|
|
|
|
$dataFields = array();
|
|
|
|
foreach($fieldValues as $type => $value) {
|
|
|
|
// if the field is an actual datafield (not a preset hiddenfield)
|
|
|
|
if(is_array($this->extraData)) {
|
|
|
|
if(!in_array($type, array_keys($this->extraData))) {
|
2008-07-30 08:40:17 +02:00
|
|
|
$dataFields[$type] = $value;
|
2007-08-18 04:33:28 +02:00
|
|
|
}
|
2009-07-17 02:17:21 +02:00
|
|
|
// all fields are real
|
|
|
|
} else {
|
|
|
|
$dataFields[$type] = $value;
|
2008-07-30 08:40:17 +02:00
|
|
|
}
|
2009-07-17 02:17:21 +02:00
|
|
|
}
|
|
|
|
$dataValues = ArrayLib::array_values_recursive($dataFields);
|
|
|
|
// determine if any of the fields have a value (loose checking with empty())
|
|
|
|
$hasData = false;
|
|
|
|
foreach($dataValues as $value) {
|
|
|
|
if(!empty($value)) $hasData = true;
|
|
|
|
}
|
2007-11-22 03:17:16 +01:00
|
|
|
|
2009-07-17 02:17:21 +02:00
|
|
|
if($hasData) {
|
|
|
|
$form->loadDataFrom($fieldValues, true);
|
|
|
|
$form->saveInto($obj);
|
|
|
|
|
|
|
|
$objectid = $obj->write();
|
2008-07-30 08:40:17 +02:00
|
|
|
|
2009-07-17 02:17:21 +02:00
|
|
|
$savedObjIds[$objectid] = "Updated";
|
2007-08-18 04:33:28 +02:00
|
|
|
}
|
2009-07-17 02:17:21 +02:00
|
|
|
|
2009-07-17 02:16:52 +02:00
|
|
|
}
|
2009-07-17 02:17:21 +02:00
|
|
|
|
|
|
|
return $savedObjIds;
|
|
|
|
}
|
2007-08-18 04:33:28 +02:00
|
|
|
|
|
|
|
/**
|
2009-07-17 02:13:42 +02:00
|
|
|
* Organises the data in the appropriate manner for saving
|
|
|
|
*
|
|
|
|
* @param array $data
|
|
|
|
* @param int $recordID
|
|
|
|
* @return array Collection of maps suitable to construct DataObjects
|
2007-08-18 04:33:28 +02:00
|
|
|
*/
|
2007-11-25 10:48:25 +01:00
|
|
|
function sortData($data, $recordID = null) {
|
2009-07-17 02:17:43 +02:00
|
|
|
if(!$data) return false;
|
|
|
|
|
|
|
|
$sortedData = array();
|
|
|
|
|
|
|
|
foreach($data as $field => $rowData) {
|
|
|
|
$i = 0;
|
|
|
|
if(!is_array($rowData)) continue;
|
|
|
|
|
|
|
|
foreach($rowData as $id => $value) {
|
|
|
|
if($value == '$recordID') $value = $recordID;
|
2007-08-18 04:33:28 +02:00
|
|
|
|
2009-07-17 02:17:43 +02:00
|
|
|
if($value) $sortedData[$id][$field] = $value;
|
|
|
|
|
|
|
|
$i++;
|
2007-08-18 04:33:28 +02:00
|
|
|
}
|
2009-07-17 02:17:43 +02:00
|
|
|
|
|
|
|
// TODO ADD stuff for removing rows with incomplete data
|
2007-08-18 04:33:28 +02:00
|
|
|
}
|
2009-07-17 02:17:43 +02:00
|
|
|
|
|
|
|
return $sortedData;
|
2007-08-18 04:33:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param $extraData array
|
|
|
|
*/
|
|
|
|
function setExtraData($extraData) {
|
|
|
|
$this->extraData = $extraData;
|
2012-02-17 13:35:26 +01:00
|
|
|
return $this;
|
2007-08-18 04:33:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
function getExtraData() {
|
|
|
|
return $this->extraData;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the template to be rendered with
|
|
|
|
*/
|
|
|
|
function FieldHolder() {
|
2011-04-19 12:47:56 +02:00
|
|
|
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery/jquery.js');
|
2012-02-16 12:27:47 +01:00
|
|
|
Requirements::javascript(THIRDPARTY_DIR . "/prototype/prototype.js");
|
2009-11-21 03:29:59 +01:00
|
|
|
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/behaviour/behaviour.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::javascript(SAPPHIRE_DIR . '/javascript/TableField.js');
|
|
|
|
Requirements::css(SAPPHIRE_DIR . '/css/TableListField.css');
|
|
|
|
|
2007-08-18 04:33:28 +02:00
|
|
|
return $this->renderWith($this->template);
|
|
|
|
}
|
2009-07-17 02:16:17 +02:00
|
|
|
|
2007-08-18 04:33:28 +02:00
|
|
|
function setTransformationConditions($conditions) {
|
|
|
|
$this->transformationConditions = $conditions;
|
2012-02-17 13:35:26 +01:00
|
|
|
return $this;
|
2007-08-18 04:33:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function php($data) {
|
|
|
|
$valid = true;
|
2007-11-25 10:48:25 +01:00
|
|
|
|
2009-07-17 04:09:27 +02:00
|
|
|
if($data['methodName'] != 'delete') {
|
|
|
|
$items = $this->Items();
|
|
|
|
if($items) foreach($items as $item) {
|
|
|
|
foreach($item->Fields() as $field) {
|
|
|
|
$valid = $field->validate($this) && $valid;
|
|
|
|
}
|
2008-10-08 04:00:12 +02:00
|
|
|
}
|
2009-07-17 04:09:27 +02:00
|
|
|
|
2008-10-08 04:00:12 +02:00
|
|
|
return $valid;
|
2009-07-17 04:09:27 +02:00
|
|
|
} else {
|
2007-11-25 10:48:25 +01:00
|
|
|
return $valid;
|
2007-08-18 04:33:28 +02:00
|
|
|
}
|
2007-11-25 10:48:25 +01:00
|
|
|
}
|
|
|
|
|
2009-01-10 12:35:50 +01:00
|
|
|
function validate($validator) {
|
|
|
|
$errorMessage = '';
|
2007-11-25 10:48:25 +01:00
|
|
|
$valid = true;
|
2009-07-17 04:09:27 +02:00
|
|
|
|
|
|
|
// @todo should only contain new elements
|
|
|
|
$items = $this->Items();
|
|
|
|
if($items) foreach($items as $item) {
|
|
|
|
foreach($item->Fields() as $field) {
|
|
|
|
$valid = $field->validate($validator) && $valid;
|
|
|
|
}
|
2007-08-18 04:33:28 +02:00
|
|
|
}
|
|
|
|
|
2007-11-25 10:48:25 +01:00
|
|
|
//debug::show($this->form->Message());
|
|
|
|
if($this->requiredFields&&$sourceItemsNew&&$sourceItemsNew->count()) {
|
|
|
|
foreach ($this->requiredFields as $field) {
|
|
|
|
foreach($sourceItemsNew as $item){
|
2011-10-29 13:07:40 +02:00
|
|
|
$cellName = $this->getName().'['.$item->ID.']['.$field.']';
|
|
|
|
$cName = $this->getName().'[new]['.$field.'][]';
|
2007-11-25 10:48:25 +01:00
|
|
|
|
|
|
|
if($fieldObj = $fields->dataFieldByName($cellName)) {
|
|
|
|
if(!trim($fieldObj->Value())){
|
|
|
|
$title = $fieldObj->Title();
|
|
|
|
$errorMessage .= sprintf(
|
|
|
|
_t('TableField.ISREQUIRED', "In %s '%s' is required."),
|
|
|
|
$this->name,
|
|
|
|
$title
|
|
|
|
);
|
|
|
|
$errorMessage .= "<br />";
|
|
|
|
}
|
|
|
|
}
|
2007-08-18 04:33:28 +02:00
|
|
|
}
|
|
|
|
}
|
2007-11-25 10:48:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if($errorMessage){
|
|
|
|
$messageType .= "validation";
|
|
|
|
$message .= "<br />".$errorMessage;
|
2007-08-18 04:33:28 +02:00
|
|
|
|
2007-11-25 10:48:25 +01:00
|
|
|
$validator->validationError($this->name, $message, $messageType);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $valid;
|
2007-08-18 04:33:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function setRequiredFields($fields) {
|
|
|
|
$this->requiredFields = $fields;
|
2012-02-17 13:35:26 +01:00
|
|
|
return $this;
|
2007-08-18 04:33:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-01-09 05:18:36 +01:00
|
|
|
* Single record in a TableField.
|
|
|
|
* @package forms
|
|
|
|
* @subpackage fields-relational
|
|
|
|
* @see TableField
|
2007-08-18 04:33:28 +02:00
|
|
|
*/
|
|
|
|
class TableField_Item extends TableListField_Item {
|
|
|
|
|
2009-07-17 02:13:42 +02:00
|
|
|
/**
|
2011-05-11 09:51:54 +02:00
|
|
|
* @var FieldList $fields
|
2009-07-17 02:13:42 +02:00
|
|
|
*/
|
2007-08-18 04:33:28 +02:00
|
|
|
protected $fields;
|
|
|
|
|
|
|
|
protected $data;
|
|
|
|
|
|
|
|
protected $fieldTypes;
|
|
|
|
|
|
|
|
protected $isAddRow;
|
|
|
|
|
|
|
|
protected $extraData;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Each row contains a dataobject with any number of attributes
|
|
|
|
* @param $ID int The ID of the record
|
|
|
|
* @param $form Form A Form object containing all of the fields for this item. The data should be loaded in
|
|
|
|
* @param $fieldTypes array An array of name => fieldtype for use when creating a new field
|
|
|
|
* @param $parent TableListField The parent table for quick reference of names, and id's for storing values.
|
|
|
|
*/
|
|
|
|
function __construct($item = null, $parent, $form, $fieldTypes, $isAddRow = false) {
|
|
|
|
$this->data = $form;
|
|
|
|
$this->fieldTypes = $fieldTypes;
|
|
|
|
$this->isAddRow = $isAddRow;
|
|
|
|
$this->item = $item;
|
|
|
|
|
|
|
|
parent::__construct(($this->item) ? $this->item : new DataObject(), $parent);
|
|
|
|
|
|
|
|
$this->fields = $this->createFields();
|
|
|
|
}
|
|
|
|
/**
|
2009-07-17 02:13:42 +02:00
|
|
|
* Represents each cell of the table with an attribute.
|
2009-07-17 02:15:39 +02:00
|
|
|
*
|
2011-09-27 00:55:49 +02:00
|
|
|
* @return FieldList
|
2007-08-18 04:33:28 +02:00
|
|
|
*/
|
|
|
|
function createFields() {
|
|
|
|
// Existing record
|
|
|
|
if($this->item && $this->data) {
|
|
|
|
$form = $this->data;
|
|
|
|
$this->fieldset = $form->Fields();
|
2008-07-28 09:39:33 +02:00
|
|
|
$this->fieldset->removeByName('SecurityID');
|
2007-08-18 04:33:28 +02:00
|
|
|
if($this->fieldset) {
|
|
|
|
$i=0;
|
|
|
|
foreach($this->fieldset as $field) {
|
2011-10-29 06:01:52 +02:00
|
|
|
$origFieldName = $field->getName();
|
2007-11-25 10:48:25 +01:00
|
|
|
|
|
|
|
// set unique fieldname with id
|
2011-10-29 13:07:40 +02:00
|
|
|
$combinedFieldName = $this->parent->getName() . "[" . $this->ID() . "][" . $origFieldName . "]";
|
2009-07-17 04:09:27 +02:00
|
|
|
// ensure to set field to nested array notation
|
|
|
|
// if its an unsaved row, or the "add row" which is present by default
|
|
|
|
if($this->isAddRow || $this->ID() == 'new') $combinedFieldName .= '[]';
|
2007-11-25 10:48:25 +01:00
|
|
|
|
|
|
|
// get value
|
|
|
|
if(strpos($origFieldName,'.') === false) {
|
|
|
|
$value = $field->dataValue();
|
|
|
|
} else {
|
|
|
|
// this supports the syntax fieldName = Relation.RelatedField
|
|
|
|
$fieldNameParts = explode('.', $origFieldName) ;
|
|
|
|
$tmpItem = $this->item;
|
|
|
|
for($j=0;$j<sizeof($fieldNameParts);$j++) {
|
|
|
|
$relationMethod = $fieldNameParts[$j];
|
|
|
|
$idField = $relationMethod . 'ID';
|
|
|
|
if($j == sizeof($fieldNameParts)-1) {
|
|
|
|
$value = $tmpItem->$relationMethod;
|
|
|
|
} else {
|
|
|
|
$tmpItem = $tmpItem->$relationMethod();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-08-18 04:33:28 +02:00
|
|
|
$field->Name = $combinedFieldName;
|
2007-11-25 10:48:25 +01:00
|
|
|
$field->setValue($field->dataValue());
|
2008-02-20 10:39:17 +01:00
|
|
|
$field->addExtraClass('col'.$i);
|
2008-07-30 08:40:17 +02:00
|
|
|
$field->setForm($this->data);
|
2007-08-18 04:33:28 +02:00
|
|
|
|
|
|
|
// transformation
|
|
|
|
if(isset($this->parent->transformationConditions[$origFieldName])) {
|
|
|
|
$transformation = $this->parent->transformationConditions[$origFieldName]['transformation'];
|
|
|
|
$rule = str_replace("\$","\$this->item->", $this->parent->transformationConditions[$origFieldName]['rule']);
|
|
|
|
$ruleApplies = null;
|
|
|
|
eval('$ruleApplies = ('.$rule.');');
|
|
|
|
if($ruleApplies) {
|
|
|
|
$field = $field->$transformation();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-11-25 10:48:25 +01:00
|
|
|
// formatting
|
|
|
|
$item = $this->item;
|
|
|
|
$value = $field->Value();
|
|
|
|
if(array_key_exists($origFieldName, $this->parent->fieldFormatting)) {
|
|
|
|
$format = str_replace('$value', "__VAL__", $this->parent->fieldFormatting[$origFieldName]);
|
|
|
|
$format = preg_replace('/\$([A-Za-z0-9-_]+)/','$item->$1', $format);
|
|
|
|
$format = str_replace('__VAL__', '$value', $format);
|
|
|
|
eval('$value = "' . $format . '";');
|
|
|
|
$field->dontEscape = true;
|
|
|
|
$field->setValue($value);
|
|
|
|
}
|
|
|
|
|
2007-08-18 04:33:28 +02:00
|
|
|
$this->fields[] = $field;
|
|
|
|
$i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// New record
|
|
|
|
} else {
|
|
|
|
$list = $this->parent->FieldList();
|
2007-11-25 10:48:25 +01:00
|
|
|
$i=0;
|
|
|
|
foreach($list as $fieldName => $fieldTitle) {
|
|
|
|
if(strpos($fieldName, ".")) {
|
|
|
|
$shortFieldName = substr($fieldName, strpos($fieldName, ".")+1, strlen($fieldName));
|
|
|
|
} else {
|
|
|
|
$shortFieldName = $fieldName;
|
|
|
|
}
|
2011-10-29 13:07:40 +02:00
|
|
|
$combinedFieldName = $this->parent->getName() . "[new][" . $shortFieldName . "][]";
|
2007-11-25 10:48:25 +01:00
|
|
|
$fieldType = $this->fieldTypes[$fieldName];
|
2007-08-18 04:33:28 +02:00
|
|
|
if(isset($fieldType->class) && is_subclass_of($fieldType, 'FormField')) {
|
2007-11-25 10:48:25 +01:00
|
|
|
$field = clone $fieldType; // we can't use the same instance all over, as we change names
|
|
|
|
$field->Name = $combinedFieldName;
|
2007-08-18 04:33:28 +02:00
|
|
|
} elseif(strpos($fieldType, '(') === false) {
|
|
|
|
//echo ("<li>Type: ".$fieldType." fieldName: ". $filedName. " Title: ".$fieldTitle."</li>");
|
2007-11-25 10:48:25 +01:00
|
|
|
$field = new $fieldType($combinedFieldName,$fieldTitle);
|
2007-08-18 04:33:28 +02:00
|
|
|
} else {
|
2007-11-25 10:48:25 +01:00
|
|
|
$field = eval("return new " . $fieldType . ";");
|
2007-08-18 04:33:28 +02:00
|
|
|
}
|
2008-02-20 10:39:17 +01:00
|
|
|
$field->addExtraClass('col'.$i);
|
2007-11-25 10:48:25 +01:00
|
|
|
$this->fields[] = $field;
|
|
|
|
$i++;
|
2007-08-18 04:33:28 +02:00
|
|
|
}
|
|
|
|
}
|
2011-05-11 09:51:54 +02:00
|
|
|
return new FieldList($this->fields);
|
2007-08-18 04:33:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function Fields() {
|
|
|
|
return $this->fields;
|
|
|
|
}
|
|
|
|
|
|
|
|
function ExtraData() {
|
|
|
|
$content = "";
|
|
|
|
$id = ($this->item->ID) ? $this->item->ID : "new";
|
|
|
|
if($this->parent->getExtraData()) {
|
|
|
|
foreach($this->parent->getExtraData() as $fieldName=>$fieldValue) {
|
2011-10-29 13:07:40 +02:00
|
|
|
$name = $this->parent->getName() . "[" . $id . "][" . $fieldName . "]";
|
2007-08-18 04:33:28 +02:00
|
|
|
if($this->isAddRow) $name .= '[]';
|
|
|
|
$field = new HiddenField($name, null, $fieldValue);
|
|
|
|
$content .= $field->FieldHolder() . "\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $content;
|
|
|
|
}
|
|
|
|
|
2008-10-17 00:22:32 +02:00
|
|
|
/**
|
|
|
|
* Get the flag isAddRow of this item,
|
|
|
|
* to indicate if the item is that blank last row in the table which is not in the database
|
|
|
|
*
|
|
|
|
* @return boolean
|
|
|
|
*/
|
2008-10-16 02:47:01 +02:00
|
|
|
function IsAddRow(){
|
|
|
|
return $this->isAddRow;
|
|
|
|
}
|
|
|
|
|
2007-08-18 04:33:28 +02:00
|
|
|
}
|
2007-11-25 10:48:25 +01:00
|
|
|
|