mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 12:05:37 +00:00
ENHANCEMENT: Refactored MemberTableList field to make better use of DataList and ManyManyList. Refactored ComplexTableField and TableListField to, stripping out as much model logic as possible.
This commit is contained in:
parent
3a17d5c427
commit
4a061fd071
@ -33,8 +33,6 @@ class MemberTableField extends ComplexTableField {
|
||||
|
||||
public $itemClass = 'MemberTableField_Item';
|
||||
|
||||
static $data_class = 'Member';
|
||||
|
||||
/**
|
||||
* Set the page size for this table.
|
||||
* @var int
|
||||
@ -60,8 +58,24 @@ class MemberTableField extends ComplexTableField {
|
||||
* @param boolean $hidePassword Hide the password field or not in the summary?
|
||||
*/
|
||||
function __construct($controller, $name, $group = null, $members = null, $hidePassword = true) {
|
||||
$sourceClass = self::$data_class;
|
||||
$SNG_member = singleton($sourceClass);
|
||||
|
||||
if(!$members) {
|
||||
if($group) {
|
||||
if(is_numeric($group)) $group = DataObject::get_by_id('Group', $group);
|
||||
$this->group = $group;
|
||||
$members = $group->Members();
|
||||
|
||||
} elseif(isset($_REQUEST['ctf'][$this->Name()]["ID"]) && is_numeric($_REQUEST['ctf'][$this->Name()]["ID"])) {
|
||||
throw new Exception("Is this still being used? It's a hack and we should remove it.");
|
||||
$group = DataObject::get_by_id('Group', $_REQUEST['ctf'][$this->Name()]["ID"]);
|
||||
$this->group = $group;
|
||||
$members = $group->Members();
|
||||
} else {
|
||||
$members = DataObject::get("Member");
|
||||
}
|
||||
}
|
||||
|
||||
$SNG_member = singleton('Member');
|
||||
$fieldList = $SNG_member->summaryFields();
|
||||
$memberDbFields = $SNG_member->db();
|
||||
$csvFieldList = array();
|
||||
@ -70,52 +84,24 @@ class MemberTableField extends ComplexTableField {
|
||||
$csvFieldList[$field] = $field;
|
||||
}
|
||||
|
||||
if($group) {
|
||||
if(is_object($group)) {
|
||||
$this->group = $group;
|
||||
} elseif(is_numeric($group)) {
|
||||
$this->group = DataObject::get_by_id('Group', $group);
|
||||
}
|
||||
} else if(isset($_REQUEST['ctf'][$this->Name()]["ID"]) && is_numeric($_REQUEST['ctf'][$this->Name()]["ID"])) {
|
||||
$this->group = DataObject::get_by_id('Group', $_REQUEST['ctf'][$this->Name()]["ID"]);
|
||||
}
|
||||
|
||||
if(!$hidePassword) {
|
||||
$fieldList["SetPassword"] = "Password";
|
||||
}
|
||||
|
||||
$this->hidePassword = $hidePassword;
|
||||
|
||||
// @todo shouldn't this use $this->group? It's unclear exactly
|
||||
// what group it should be customising the custom Member set with.
|
||||
if($members && $group) {
|
||||
$this->setCustomSourceItems($this->memberListWithGroupID($members, $group));
|
||||
}
|
||||
|
||||
parent::__construct($controller, $name, $sourceClass, $fieldList);
|
||||
|
||||
// Add a search filter
|
||||
$SQL_search = isset($_REQUEST['MemberSearch']) ? Convert::raw2sql($_REQUEST['MemberSearch']) : null;
|
||||
if(!empty($_REQUEST['MemberSearch'])) {
|
||||
$searchFilters = array();
|
||||
foreach($SNG_member->searchableFields() as $fieldName => $fieldSpec) {
|
||||
if(strpos($fieldName, '.') === false) $searchFilters[] = "\"$fieldName\" LIKE '%{$SQL_search}%'";
|
||||
}
|
||||
$this->sourceFilter[] = '(' . implode(' OR ', $searchFilters) . ')';
|
||||
$members = $members->filter('(' . implode(' OR ', $searchFilters) . ')');
|
||||
}
|
||||
|
||||
if($this->group) {
|
||||
user_error("MemberTableField's group setting doesn't yet work in the new-orm branch", E_USER_WARNING);
|
||||
/*
|
||||
$groupIDs = array($this->group->ID);
|
||||
if($this->group->AllChildren()) $groupIDs = array_merge($groupIDs, $this->group->AllChildren()->column('ID'));
|
||||
$this->sourceFilter[] = sprintf(
|
||||
'"Group_Members"."GroupID" IN (%s)',
|
||||
implode(',', $groupIDs)
|
||||
);
|
||||
*/
|
||||
}
|
||||
|
||||
$this->sourceJoin = " INNER JOIN \"Group_Members\" ON \"MemberID\"=\"Member\".\"ID\"";
|
||||
parent::__construct($controller, $name, $members, $fieldList);
|
||||
|
||||
$this->setFieldListCsv($csvFieldList);
|
||||
$this->setPageSize($this->stat('page_size'));
|
||||
}
|
||||
@ -130,14 +116,6 @@ class MemberTableField extends ComplexTableField {
|
||||
return $ret;
|
||||
}
|
||||
|
||||
function sourceID() {
|
||||
return ($this->group) ? $this->group->ID : 0;
|
||||
}
|
||||
|
||||
function AddLink() {
|
||||
return Controller::join_links($this->Link(), 'add');
|
||||
}
|
||||
|
||||
function SearchForm() {
|
||||
$groupID = (isset($this->group)) ? $this->group->ID : 0;
|
||||
$query = isset($_GET['MemberSearch']) ? $_GET['MemberSearch'] : null;
|
||||
@ -168,6 +146,7 @@ class MemberTableField extends ComplexTableField {
|
||||
if(!$token->checkRequest($this->controller->getRequest())) return $this->httpError(400);
|
||||
|
||||
$data = $_REQUEST;
|
||||
|
||||
$groupID = (isset($data['ctf']['ID'])) ? $data['ctf']['ID'] : null;
|
||||
|
||||
if(!is_numeric($groupID)) {
|
||||
@ -177,7 +156,7 @@ class MemberTableField extends ComplexTableField {
|
||||
|
||||
// Get existing record either by ID or unique identifier.
|
||||
$identifierField = Member::get_unique_identifier_field();
|
||||
$className = self::$data_class;
|
||||
$className = 'Member';
|
||||
$record = null;
|
||||
if(isset($data[$identifierField])) {
|
||||
$record = DataObject::get_one(
|
||||
@ -204,7 +183,7 @@ class MemberTableField extends ComplexTableField {
|
||||
$valid = $record->validate();
|
||||
if($valid->valid()) {
|
||||
$record->write();
|
||||
$record->Groups()->add($groupID);
|
||||
$this->getDataList()->add($record);
|
||||
|
||||
$this->sourceItems();
|
||||
|
||||
@ -232,82 +211,18 @@ class MemberTableField extends ComplexTableField {
|
||||
return FormResponse::respond();
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom delete implementation:
|
||||
* Remove member from group rather than from the database
|
||||
*/
|
||||
function delete() {
|
||||
// Protect against CSRF on destructive action
|
||||
$token = $this->getForm()->getSecurityToken();
|
||||
// TODO Not sure how this is called, using $_REQUEST to be on the safe side
|
||||
if(!$token->check($_REQUEST['SecurityID'])) return $this->httpError(400);
|
||||
|
||||
$groupID = Convert::raw2sql($_REQUEST['ctf']['ID']);
|
||||
$memberID = Convert::raw2sql($_REQUEST['ctf']['childID']);
|
||||
if(is_numeric($groupID) && is_numeric($memberID)) {
|
||||
$member = DataObject::get_by_id('Member', $memberID);
|
||||
$member->Groups()->remove($groupID);
|
||||
} else {
|
||||
user_error("MemberTableField::delete: Bad parameters: Group=$groupID, Member=$memberID", E_USER_ERROR);
|
||||
}
|
||||
|
||||
return FormResponse::respond();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* #################################
|
||||
* Utility Functions
|
||||
* #################################
|
||||
*/
|
||||
function getParentClass() {
|
||||
return 'Group';
|
||||
}
|
||||
|
||||
function getParentIdName($childClass, $parentClass) {
|
||||
return 'GroupID';
|
||||
}
|
||||
|
||||
/**
|
||||
* #################################
|
||||
* Custom Functions
|
||||
* #################################
|
||||
*/
|
||||
|
||||
/**
|
||||
* Customise an existing DataObjectSet of Member
|
||||
* objects with a GroupID.
|
||||
*
|
||||
* @param DataObjectSet $members Set of Member objects to customise
|
||||
* @param Group $group Group object to customise with
|
||||
* @return DataObjectSet Customised set of Member objects
|
||||
*/
|
||||
function memberListWithGroupID($members, $group) {
|
||||
$newMembers = new DataObjectSet();
|
||||
foreach($members as $member) {
|
||||
$newMembers->push($member->customise(array('GroupID' => $group->ID)));
|
||||
}
|
||||
return $newMembers;
|
||||
}
|
||||
|
||||
function setGroup($group) {
|
||||
$this->group = $group;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Group
|
||||
*/
|
||||
function getGroup() {
|
||||
return $this->group;
|
||||
}
|
||||
|
||||
function setController($controller) {
|
||||
$this->controller = $controller;
|
||||
}
|
||||
|
||||
function GetControllerName() {
|
||||
return $this->controller->class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add existing member to group by name (with JS-autocompletion)
|
||||
@ -361,7 +276,7 @@ class MemberTableField extends ComplexTableField {
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function saveComplexTableField($data, $form, $params) {
|
||||
function saveComplexTableField($data, $form, $params) {
|
||||
$className = $this->sourceClass();
|
||||
$childData = new $className();
|
||||
|
||||
@ -389,66 +304,6 @@ class MemberTableField extends ComplexTableField {
|
||||
$form->sessionMessage($message, 'good');
|
||||
|
||||
$this->controller->redirectBack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cached version for getting the appropraite members for this particular group.
|
||||
*
|
||||
* This includes getting inherited groups, such as groups under groups.
|
||||
*/
|
||||
function sourceItems() {
|
||||
// Caching.
|
||||
if($this->sourceItems) {
|
||||
return $this->sourceItems;
|
||||
}
|
||||
|
||||
// Setup limits
|
||||
$limitClause = '';
|
||||
if(isset($_REQUEST['ctf'][$this->Name()]['start']) && is_numeric($_REQUEST['ctf'][$this->Name()]['start'])) {
|
||||
$limitClause = ($_REQUEST['ctf'][$this->Name()]['start']) . ", {$this->pageSize}";
|
||||
} else {
|
||||
$limitClause = "0, {$this->pageSize}";
|
||||
}
|
||||
|
||||
// We use the group to get the members, as they already have the bulk of the look up functions
|
||||
$start = isset($_REQUEST['ctf'][$this->Name()]['start']) ? $_REQUEST['ctf'][$this->Name()]['start'] : 0;
|
||||
|
||||
$this->sourceItems = false;
|
||||
|
||||
if($this->group) {
|
||||
$this->sourceItems = $this->group->Members(
|
||||
$this->pageSize, // limit
|
||||
$start, // offset
|
||||
$this->sourceFilter,
|
||||
$this->sourceSort
|
||||
);
|
||||
} else {
|
||||
$this->sourceItems = DataObject::get(self::$data_class,
|
||||
$this->sourceFilter,
|
||||
$this->sourceSort,
|
||||
null,
|
||||
array('limit' => $this->pageSize, 'start' => $start)
|
||||
);
|
||||
}
|
||||
// Because we are not used $this->upagedSourceItems any more, and the DataObjectSet is usually the source
|
||||
// that a large member set runs out of memory. we disable it here.
|
||||
//$this->unpagedSourceItems = $this->group->Members('', '', $this->sourceFilter, $this->sourceSort);
|
||||
$this->totalCount = ($this->sourceItems) ? $this->sourceItems->TotalItems() : 0;
|
||||
|
||||
return $this->sourceItems;
|
||||
}
|
||||
|
||||
function TotalCount() {
|
||||
$this->sourceItems(); // Called for its side-effect of setting total count
|
||||
return $this->totalCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles item requests
|
||||
* MemberTableField needs its own item request class so that it can overload the delete method
|
||||
*/
|
||||
function handleItem($request) {
|
||||
return new MemberTableField_ItemRequest($this, $request->param('ID'));
|
||||
}
|
||||
}
|
||||
|
||||
@ -460,7 +315,7 @@ class MemberTableField extends ComplexTableField {
|
||||
class MemberTableField_Popup extends ComplexTableField_Popup {
|
||||
|
||||
function __construct($controller, $name, $fields, $validator, $readonly, $dataObject) {
|
||||
$group = ($controller instanceof MemberTableField) ? $controller->getGroup() : $controller->getParent()->getGroup();
|
||||
$group = ($controller instanceof MemberTableField) ? $controller->getGroup() : $controller->getParentController()->getGroup();
|
||||
// Set default groups - also implemented in AddForm()
|
||||
if($group) {
|
||||
$groupsField = $fields->dataFieldByName('Groups');
|
||||
@ -513,44 +368,4 @@ class MemberTableField_Item extends ComplexTableField_Item {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @package cms
|
||||
* @subpackage security
|
||||
*/
|
||||
|
||||
class MemberTableField_ItemRequest extends ComplexTableField_ItemRequest {
|
||||
|
||||
/**
|
||||
* Deleting an item from a member table field should just remove that member from the group
|
||||
*/
|
||||
function delete($request) {
|
||||
// Protect against CSRF on destructive action
|
||||
$token = $this->ctf->getForm()->getSecurityToken();
|
||||
if(!$token->checkRequest($request)) return $this->httpError('400');
|
||||
|
||||
if($this->ctf->Can('delete') !== true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if a group limitation is set on the table, remove relation.
|
||||
// otherwise remove the record from the database
|
||||
if($this->ctf->getGroup()) {
|
||||
$groupID = $this->ctf->sourceID();
|
||||
$group = DataObject::get_by_id('Group', $groupID);
|
||||
|
||||
// Remove from group and all child groups
|
||||
foreach($group->getAllChildren() as $subGroup) {
|
||||
$this->dataObj()->Groups()->remove($subGroup);
|
||||
}
|
||||
$this->dataObj()->Groups()->remove($groupID);
|
||||
} else {
|
||||
$this->dataObj()->delete();
|
||||
}
|
||||
}
|
||||
|
||||
function getParent() {
|
||||
return $this->ctf;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -305,7 +305,6 @@ MemberFilterButton.prototype = {
|
||||
updateURL += '&' + this.inputFields[index].name + '=' + encodeURIComponent( this.inputFields[index].value );
|
||||
}
|
||||
}
|
||||
updateURL += ($('SecurityID') ? '&SecurityID=' + $('SecurityID').value : '');
|
||||
|
||||
jQuery($(fieldID)).get(updateURL, null, function() {Behaviour.apply($(fieldID), true);});
|
||||
} catch(er) {
|
||||
|
@ -255,7 +255,7 @@ JS;
|
||||
}
|
||||
|
||||
$pageStart = (isset($_REQUEST['ctf'][$this->Name()]['start']) && is_numeric($_REQUEST['ctf'][$this->Name()]['start'])) ? $_REQUEST['ctf'][$this->Name()]['start'] : 0;
|
||||
$sourceItems->setPageLimits($pageStart, $this->pageSize, $this->totalCount);
|
||||
$sourceItems->setPageLimits($pageStart, $this->pageSize, $this->TotalCount());
|
||||
|
||||
$output = new DataObjectSet();
|
||||
foreach($sourceItems as $pageIndex=>$item) {
|
||||
@ -343,85 +343,6 @@ JS;
|
||||
$this->controller = $controller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines on which relation-class the DetailForm is saved
|
||||
* by looking at the surrounding form-record.
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
function getParentClass() {
|
||||
if($this->parentClass === false) {
|
||||
// purposely set parent-relation to false
|
||||
return false;
|
||||
} elseif(!empty($this->parentClass)) {
|
||||
return $this->parentClass;
|
||||
} elseif($this->form && $this->form->getRecord()) {
|
||||
return $this->form->getRecord()->ClassName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the record in which the CTF resides, if it exists.
|
||||
*/
|
||||
function getParentRecord() {
|
||||
if($this->form && $record = $this->form->getRecord()) {
|
||||
return $record;
|
||||
} else {
|
||||
$parentID = (int)$this->sourceID();
|
||||
$parentClass = $this->getParentClass();
|
||||
|
||||
if($parentClass) {
|
||||
if($parentID) return DataObject::get_by_id($parentClass, $parentID);
|
||||
else return singleton($parentClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* (Optional) Setter for a correct parent-relation-class.
|
||||
* Defaults to the record loaded into the surrounding form as a fallback.
|
||||
* Caution: Please use the classname, not the actual column-name in the database.
|
||||
*
|
||||
* @param $className string
|
||||
*/
|
||||
function setParentClass($className) {
|
||||
$this->parentClass = $className;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Note: constructed resolve ambiguous cases in the same manner as
|
||||
* DataObject::getComponentJoinField()
|
||||
*/
|
||||
function getParentIdNameRelation($parentClass, $childClass, $relation) {
|
||||
if($this->parentIdName) return $this->parentIdName;
|
||||
|
||||
$relations = array_flip(singleton($parentClass)->$relation());
|
||||
|
||||
$classes = array_reverse(ClassInfo::ancestry($childClass));
|
||||
foreach($classes as $class) {
|
||||
if(isset($relations[$class])) return $relations[$class] . 'ID';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function setTemplatePopup($template) {
|
||||
$this->templatePopup = $template;
|
||||
}
|
||||
@ -458,45 +379,8 @@ JS;
|
||||
}
|
||||
|
||||
function getFieldsFor($childData) {
|
||||
$hasManyRelationName = null;
|
||||
$manyManyRelationName = null;
|
||||
|
||||
// See if our parent class has any many_many relations by this source class
|
||||
if($parentClass = $this->getParentRecord()) {
|
||||
$manyManyRelations = $parentClass->many_many();
|
||||
$manyManyRelationName = null;
|
||||
$manyManyComponentSet = null;
|
||||
|
||||
$hasManyRelations = $parentClass->has_many();
|
||||
$hasManyRelationName = null;
|
||||
$hasManyComponentSet = null;
|
||||
|
||||
if($manyManyRelations) foreach($manyManyRelations as $relation => $class) {
|
||||
if($class == $this->sourceClass()) {
|
||||
$manyManyRelationName = $relation;
|
||||
}
|
||||
}
|
||||
|
||||
if($hasManyRelations) foreach($hasManyRelations as $relation => $class) {
|
||||
if($class == $this->sourceClass()) {
|
||||
$hasManyRelationName = $relation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add the relation value to related records
|
||||
if(!$childData->ID && $this->getParentClass()) {
|
||||
// make sure the relation-link is existing, even if we just add the sourceClass and didn't save it
|
||||
$parentIDName = $this->getParentIdName($this->getParentClass(), $this->sourceClass());
|
||||
$childData->$parentIDName = $this->sourceID();
|
||||
}
|
||||
|
||||
$detailFields = $this->getCustomFieldsFor($childData);
|
||||
|
||||
if($this->getParentClass() && $hasManyRelationName && $childData->ID) {
|
||||
$hasManyComponentSet = $parentClass->getComponents($hasManyRelationName);
|
||||
}
|
||||
|
||||
// the ID field confuses the Controller-logic in finding the right view for ReferencedField
|
||||
$detailFields->removeByName('ID');
|
||||
|
||||
@ -505,9 +389,7 @@ JS;
|
||||
$detailFields->push(new HiddenField('ctf[childID]', '', $childData->ID));
|
||||
}
|
||||
|
||||
// add a namespaced ID instead thats "converted" by saveComplexTableField()
|
||||
$detailFields->push(new HiddenField('ctf[ClassName]', '', $this->sourceClass()));
|
||||
|
||||
/* TODO: Figure out how to implement this
|
||||
if($this->getParentClass()) {
|
||||
$detailFields->push(new HiddenField('ctf[parentClass]', '', $this->getParentClass()));
|
||||
|
||||
@ -518,6 +400,7 @@ JS;
|
||||
$detailFields->push(new HiddenField($parentIdName, '', $this->sourceID()));
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
return $detailFields;
|
||||
}
|
||||
@ -688,8 +571,8 @@ class ComplexTableField_ItemRequest extends TableListField_ItemRequest {
|
||||
if($this->ctf->Can('delete') !== true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->dataObj()->delete();
|
||||
|
||||
$this->ctf->getDataList()->removeByID($this->itemID);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -761,7 +644,7 @@ class ComplexTableField_ItemRequest extends TableListField_ItemRequest {
|
||||
}
|
||||
|
||||
// Save this item into the given relationship
|
||||
$this->ctf->getDataList()->add($childData);
|
||||
$this->ctf->getDataList()->add($dataObject);
|
||||
|
||||
$referrer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : null;
|
||||
|
||||
@ -798,16 +681,16 @@ class ComplexTableField_ItemRequest extends TableListField_ItemRequest {
|
||||
}
|
||||
|
||||
function PopupLastLink() {
|
||||
if(!isset($_REQUEST['ctf']['start']) || !is_numeric($_REQUEST['ctf']['start']) || $_REQUEST['ctf']['start'] == $this->totalCount-1) {
|
||||
if(!isset($_REQUEST['ctf']['start']) || !is_numeric($_REQUEST['ctf']['start']) || $_REQUEST['ctf']['start'] == $this->TotalCount()-1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$start = $this->totalCount - 1;
|
||||
$start = $this->TotalCount - 1;
|
||||
return Controller::join_links($this->Link(), "$this->methodName?ctf[start]={$start}");
|
||||
}
|
||||
|
||||
function PopupNextLink() {
|
||||
if(!isset($_REQUEST['ctf']['start']) || !is_numeric($_REQUEST['ctf']['start']) || $_REQUEST['ctf']['start'] == $this->totalCount-1) {
|
||||
if(!isset($_REQUEST['ctf']['start']) || !is_numeric($_REQUEST['ctf']['start']) || $_REQUEST['ctf']['start'] == $this->TotalCount()-1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -836,18 +719,18 @@ class ComplexTableField_ItemRequest extends TableListField_ItemRequest {
|
||||
$result = new DataObjectSet();
|
||||
if($currentItem < 6) {
|
||||
$offset = 1;
|
||||
} elseif($this->totalCount - $currentItem <= 4) {
|
||||
$offset = $currentItem - (10 - ($this->totalCount - $currentItem));
|
||||
} elseif($this->TotalCount() - $currentItem <= 4) {
|
||||
$offset = $currentItem - (10 - ($this->TotalCount() - $currentItem));
|
||||
$offset = $offset <= 0 ? 1 : $offset;
|
||||
} else {
|
||||
$offset = $currentItem - 5;
|
||||
}
|
||||
for($i = $offset;$i <= $offset + $this->pageSize && $i <= $this->totalCount;$i++) {
|
||||
for($i = $offset;$i <= $offset + $this->pageSize && $i <= $this->TotalCount();$i++) {
|
||||
$start = $i - 1;
|
||||
$links['link'] = Controller::join_links($this->Link() . "$this->methodName?ctf[start]={$start}");
|
||||
$links['number'] = $i;
|
||||
$links['active'] = $i == $currentItem ? false : true;
|
||||
$result->push(new ArrayData($links));
|
||||
$result->push(new ArrayData($links));
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
@ -863,13 +746,6 @@ class ComplexTableField_ItemRequest extends TableListField_ItemRequest {
|
||||
* #################################
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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()
|
||||
@ -877,25 +753,9 @@ class ComplexTableField_ItemRequest extends TableListField_ItemRequest {
|
||||
* @param String $str Example: FamilyID (when one Individual has_one Family)
|
||||
*/
|
||||
function setParentIdName($str) {
|
||||
$this->parentIdName = $str;
|
||||
throw new Exception("setParentIdName is no longer necessary");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
if($relations) {
|
||||
foreach($relations as $k => $v) {
|
||||
if(array_key_exists($v, $classes)) return $k . 'ID';
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function setTemplatePopup($template) {
|
||||
$this->templatePopup = $template;
|
||||
}
|
||||
|
@ -134,14 +134,6 @@ class TableListField extends FormField {
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @todo Add pagination support for customSourceItems.
|
||||
*/
|
||||
protected $customSourceItems;
|
||||
|
||||
/**
|
||||
* Character to seperate exported columns in the CSV file
|
||||
*/
|
||||
@ -166,12 +158,6 @@ class TableListField extends FormField {
|
||||
"\n"=>"",
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* @var int Shows total count regardless or pagination
|
||||
*/
|
||||
protected $totalCount;
|
||||
|
||||
/**
|
||||
* @var boolean Trigger pagination
|
||||
*/
|
||||
@ -240,22 +226,29 @@ class TableListField extends FormField {
|
||||
|
||||
protected $__cachedQuery;
|
||||
|
||||
/**
|
||||
* This is a flag that enables some backward-compatibility helpers.
|
||||
*/
|
||||
private $getDataListFromForm;
|
||||
|
||||
function __construct($name, $sourceClass = null, $fieldList = null, $sourceFilter = null,
|
||||
$sourceSort = null, $sourceJoin = null) {
|
||||
|
||||
$this->fieldList = ($fieldList) ? $fieldList : singleton($sourceClass)->summaryFields();
|
||||
|
||||
if($sourceClass) {
|
||||
// You can optionally pass a DataList as the 2nd argument to the constructor
|
||||
if($sourceClass instanceof DataList) {
|
||||
// You can optionally pass a DataList/DataObjectSet
|
||||
if($sourceClass instanceof DataObjectSet) {
|
||||
$this->dataList = $sourceClass;
|
||||
|
||||
} else {
|
||||
$this->dataList = DataObject::get($sourceClass)->filter($sourceFilter)
|
||||
->sort($sourceSort)->join($sourceJoin);
|
||||
// Grab it from the form relation, if available.
|
||||
$this->getDataListFromForm = true;
|
||||
}
|
||||
}
|
||||
|
||||
$this->fieldList = ($fieldList) ? $fieldList : singleton($this->sourceClass())->summaryFields();
|
||||
|
||||
$this->readOnly = false;
|
||||
|
||||
parent::__construct($name);
|
||||
@ -271,7 +264,10 @@ class TableListField extends FormField {
|
||||
);
|
||||
|
||||
function sourceClass() {
|
||||
return $this->getDataList()->dataClass();
|
||||
$list = $this->getDataList();
|
||||
if(method_exists($list, 'dataClass')) return $list->dataClass();
|
||||
// Failover for DataObjectSet
|
||||
else return get_class($list->First());
|
||||
}
|
||||
|
||||
function handleItem($request) {
|
||||
@ -358,13 +354,10 @@ JS
|
||||
* @return bool
|
||||
*/
|
||||
function isFieldSortable($fieldName) {
|
||||
if($this->customSourceItems || $this->disableSorting) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!$this->__cachedQuery) $this->__cachedQuery = $this->getQuery();
|
||||
|
||||
return $this->__cachedQuery->canSortBy($fieldName);
|
||||
if($this->disableSorting) return false;
|
||||
$list = $this->getDataList();
|
||||
if(method_exists($list,'canSortBy')) return $list->canSortBy($fieldName);
|
||||
else return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -400,45 +393,42 @@ JS
|
||||
}
|
||||
|
||||
function setCustomSourceItems(DataObjectSet $items) {
|
||||
user_error('TableList::setCustomSourceItems() deprecated, just pass the items into the constructor', E_USER_WARNING);
|
||||
|
||||
// The type-hinting above doesn't seem to work consistently
|
||||
if($items instanceof DataObjectSet) {
|
||||
$this->customSourceItems = $items;
|
||||
$this->dataList = $items;
|
||||
} else {
|
||||
user_error('TableList::setCustomSourceItems() should be passed a DataObjectSet', E_USER_WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get items, with sort & limit applied
|
||||
*/
|
||||
function sourceItems() {
|
||||
// get items (this may actually be a DataObjectSet)
|
||||
$items = clone $this->getDataList();
|
||||
|
||||
// TODO: Sorting could be implemented on regular DataObjectSets.
|
||||
if(method_exists($items,'canSortBy') && isset($_REQUEST['ctf'][$this->Name()]['sort'])) {
|
||||
$sort = $_REQUEST['ctf'][$this->Name()]['sort'];
|
||||
// TODO: sort direction
|
||||
if($items->canSortBy($sort)) $items = $items->sort($sort);
|
||||
}
|
||||
|
||||
// Determine pagination limit, offset
|
||||
$SQL_limit = ($this->showPagination && $this->pageSize) ? "{$this->pageSize}" : null;
|
||||
if(isset($_REQUEST['ctf'][$this->Name()]['start']) && is_numeric($_REQUEST['ctf'][$this->Name()]['start'])) {
|
||||
$SQL_start = (isset($_REQUEST['ctf'][$this->Name()]['start'])) ? intval($_REQUEST['ctf'][$this->Name()]['start']) : "0";
|
||||
} else {
|
||||
$SQL_start = 0;
|
||||
}
|
||||
|
||||
// Custom source items can be explicitly passed
|
||||
if(isset($this->customSourceItems)) {
|
||||
if($this->showPagination && $this->pageSize) {
|
||||
$items = $this->customSourceItems->getRange($SQL_start, $SQL_limit);
|
||||
} else {
|
||||
$items = $this->customSourceItems;
|
||||
}
|
||||
|
||||
// Otherwise we use the internal data list
|
||||
} else {
|
||||
// get the DataList of items
|
||||
$items = $this->getDataList();
|
||||
|
||||
// we don't limit when doing certain actions T
|
||||
$methodName = isset($_REQUEST['url']) ? array_pop(explode('/', $_REQUEST['url'])) : null;
|
||||
if(!$methodName || !in_array($methodName,array('printall','export'))) {
|
||||
$items->limit(array(
|
||||
'limit' => $SQL_limit,
|
||||
'start' => (isset($SQL_start)) ? $SQL_start : null
|
||||
));
|
||||
}
|
||||
}
|
||||
// To disable pagination, set $this->showPagination to false.
|
||||
if($this->showPagination && $this->pageSize) {
|
||||
$SQL_limit = (int)$this->pageSize;
|
||||
if(isset($_REQUEST['ctf'][$this->Name()]['start']) && is_numeric($_REQUEST['ctf'][$this->Name()]['start'])) {
|
||||
$SQL_start = (isset($_REQUEST['ctf'][$this->Name()]['start'])) ? intval($_REQUEST['ctf'][$this->Name()]['start']) : "0";
|
||||
} else {
|
||||
$SQL_start = 0;
|
||||
}
|
||||
|
||||
$items = $items->getRange($SQL_start, $SQL_limit);
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
@ -458,12 +448,9 @@ JS
|
||||
* Returns the DataList for this field.
|
||||
*/
|
||||
function getDataList() {
|
||||
// Load the data from the form
|
||||
// Note that this will override any specific. This is so that explicitly-passed sets of
|
||||
// parameters that represent a relation can be replaced with the relation itself. This is
|
||||
// a little clumsy and won't work if people have used a field name that is the same as a
|
||||
// relation but have specified alternative parameters.
|
||||
if($this->form) {
|
||||
// If we weren't passed in a DataList to begin with, try and get the datalist from the form
|
||||
if($this->form && $this->getDataListFromForm) {
|
||||
$this->getDataListFromForm = false;
|
||||
$relation = $this->name;
|
||||
if($record = $this->form->getRecord()) {
|
||||
if($record->hasMethod($relation)) $this->dataList = $record->$relation();
|
||||
@ -474,20 +461,7 @@ JS
|
||||
user_error(get_class($this). ' is missing a DataList', E_USER_ERROR);
|
||||
}
|
||||
|
||||
$dl = clone $this->dataList;
|
||||
|
||||
if(isset($_REQUEST['ctf'][$this->Name()]['sort'])) {
|
||||
$query = $this->dataList->dataQuery()->query();
|
||||
$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}")) {
|
||||
$dl->sort($SQL_sort);
|
||||
}
|
||||
if($query->canSortBy($column)) $query->orderby = $column.' '.$dir;
|
||||
}
|
||||
|
||||
return $dl;
|
||||
return $this->dataList;
|
||||
}
|
||||
|
||||
function getCsvDataList() {
|
||||
@ -499,14 +473,20 @@ JS
|
||||
* @deprecated Use getDataList() instead.
|
||||
*/
|
||||
function getQuery() {
|
||||
return $this->getDataList()->dataQuery()->query();
|
||||
$list = $this->getDataList();
|
||||
if(method_exists($list,'dataQuery')) {
|
||||
return $this->getDataList()->dataQuery()->query();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use getCsvDataList() instead.
|
||||
*/
|
||||
function getCsvQuery() {
|
||||
return $this->getCsvDataList()->dataQuery()->query();
|
||||
$list = $this->getCsvDataList();
|
||||
if(method_exists($list,'dataQuery')) {
|
||||
return $list->dataQuery()->query();
|
||||
}
|
||||
}
|
||||
|
||||
function FieldList() {
|
||||
@ -562,8 +542,7 @@ JS
|
||||
$childId = Convert::raw2sql($_REQUEST['ctf']['childID']);
|
||||
|
||||
if (is_numeric($childId)) {
|
||||
$childObject = DataObject::get_by_id($this->sourceClass(), $childId);
|
||||
if($childObject) $childObject->delete();
|
||||
$this->getDataList()->removeById($childId);
|
||||
}
|
||||
|
||||
// TODO return status in JSON etc.
|
||||
@ -883,9 +862,20 @@ JS
|
||||
return min($this->pageSize, $this->TotalCount());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
private $_cache_TotalCount;
|
||||
|
||||
/**
|
||||
* Return the total number of items in the source DataList
|
||||
*/
|
||||
function TotalCount() {
|
||||
return $this->getDataList()->Count();
|
||||
if($this->_cache_TotalCount === null) {
|
||||
$this->_cache_TotalCount = $this->getDataList()->Count();
|
||||
}
|
||||
return $this->_cache_TotalCount;
|
||||
}
|
||||
|
||||
|
||||
@ -950,6 +940,14 @@ JS
|
||||
function export() {
|
||||
$now = Date("d-m-Y-H-i");
|
||||
$fileName = "export-$now.csv";
|
||||
|
||||
// No pagination for export
|
||||
$oldShowPagination = $this->showPagination;
|
||||
$this->showPagination = false;
|
||||
|
||||
$result = $this->renderWith(array($this->template . '_printable', 'TableListField_printable'));
|
||||
|
||||
$this->showPagination = $oldShowPagination;
|
||||
|
||||
if($fileData = $this->generateExportFileData($numColumns, $numRows)){
|
||||
return SS_HTTPRequest::send_file($fileData, $fileName);
|
||||
@ -1562,42 +1560,11 @@ class TableListField_ItemRequest extends RequestHandler {
|
||||
// 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);
|
||||
return $this->ctf->getDataList()->byId($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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TableListField
|
||||
*/
|
||||
|
@ -153,7 +153,6 @@ class Group extends DataObject {
|
||||
}
|
||||
|
||||
$memberList->setPermissions(array('edit', 'delete', 'export', 'add', 'inlineadd'));
|
||||
$memberList->setParentClass('Group');
|
||||
$memberList->setPopupCaption(_t('SecurityAdmin.VIEWUSER', 'View User'));
|
||||
|
||||
$fields->push($idField = new HiddenField("ID"));
|
||||
|
@ -100,13 +100,11 @@ class ComplexTableFieldTest_Controller extends Controller {
|
||||
$playersField = new ComplexTableField(
|
||||
$this,
|
||||
'Players',
|
||||
'ComplexTableFieldTest_Player',
|
||||
$team->Players(),
|
||||
ComplexTableFieldTest_Player::$summary_fields,
|
||||
'getCMSFields'
|
||||
);
|
||||
|
||||
$playersField->setParentClass('ComplexTableFieldTest_Team');
|
||||
|
||||
$form = new Form(
|
||||
$this,
|
||||
'ManyManyForm',
|
||||
@ -131,13 +129,11 @@ class ComplexTableFieldTest_Controller extends Controller {
|
||||
$sponsorsField = new ComplexTableField(
|
||||
$this,
|
||||
'Sponsors',
|
||||
'ComplexTableFieldTest_Sponsor',
|
||||
$team->Sponsors(),
|
||||
ComplexTableFieldTest_Sponsor::$summary_fields,
|
||||
'getCMSFields'
|
||||
);
|
||||
|
||||
$sponsorsField->setParentClass('ComplexTableFieldTest_Team');
|
||||
|
||||
$form = new Form(
|
||||
$this,
|
||||
'HasManyForm',
|
||||
|
@ -292,6 +292,38 @@ class TableListFieldTest extends SapphireTest {
|
||||
|
||||
unset($_REQUEST['ctf']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that a DataObjectSet can be passed to TableListField
|
||||
*/
|
||||
function testDataObjectSet() {
|
||||
$one = new TableListFieldTest_Obj;
|
||||
$one->A = "A-one";
|
||||
$two = new TableListFieldTest_Obj;
|
||||
$two->A = "A-two";
|
||||
$three = new TableListFieldTest_Obj;
|
||||
$three->A = "A-three";
|
||||
|
||||
$list = new DataObjectSet($one, $two, $three);
|
||||
|
||||
// A TableListField must be inside a form for its links to be generated
|
||||
$form = new Form(new TableListFieldTest_TestController(), "TestForm", new FieldSet(
|
||||
new TableListField("Tester", $list, array(
|
||||
"A" => "Col A",
|
||||
"B" => "Col B",
|
||||
"C" => "Col C",
|
||||
"D" => "Col D",
|
||||
"E" => "Col E",
|
||||
))
|
||||
), new FieldSet());
|
||||
|
||||
$table = $form->dataFieldByName('Tester');
|
||||
$rendered = $table->FieldHolder();
|
||||
|
||||
$this->assertContains('A-one', $rendered);
|
||||
$this->assertContains('A-two', $rendered);
|
||||
$this->assertContains('A-three', $rendered);
|
||||
}
|
||||
}
|
||||
|
||||
class TableListFieldTest_Obj extends DataObject implements TestOnly {
|
||||
|
Loading…
x
Reference in New Issue
Block a user