diff --git a/admin/code/MemberTableField.php b/admin/code/MemberTableField.php index 934daf13e..8a69c638a 100755 --- a/admin/code/MemberTableField.php +++ b/admin/code/MemberTableField.php @@ -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; - } -} - ?> \ No newline at end of file diff --git a/admin/javascript/MemberTableField.js b/admin/javascript/MemberTableField.js index c275468fb..1d3516353 100755 --- a/admin/javascript/MemberTableField.js +++ b/admin/javascript/MemberTableField.js @@ -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) { diff --git a/forms/ComplexTableField.php b/forms/ComplexTableField.php index 0839daf3a..13f18cb4e 100644 --- a/forms/ComplexTableField.php +++ b/forms/ComplexTableField.php @@ -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; } diff --git a/forms/TableListField.php b/forms/TableListField.php index 5d99d4585..ad850e0c7 100755 --- a/forms/TableListField.php +++ b/forms/TableListField.php @@ -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 */ diff --git a/security/Group.php b/security/Group.php index 6ceceb8b5..e340d72f9 100644 --- a/security/Group.php +++ b/security/Group.php @@ -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")); diff --git a/tests/forms/ComplexTableFieldTest.php b/tests/forms/ComplexTableFieldTest.php index d49f664ff..a4620cefd 100644 --- a/tests/forms/ComplexTableFieldTest.php +++ b/tests/forms/ComplexTableFieldTest.php @@ -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', diff --git a/tests/forms/TableListFieldTest.php b/tests/forms/TableListFieldTest.php index 61fb00308..d1ed184c8 100755 --- a/tests/forms/TableListFieldTest.php +++ b/tests/forms/TableListFieldTest.php @@ -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 {