Use simply 'group' instead of 'security group' for consistency in alert about no...
[silverstripe-elijah.git] / code / MemberTableField.php
blob34fda9ac0776caec1df81eaa867b783d8261c09d
1 <?php
2 /**
3 * Enhances {ComplexTableField} with the ability to list groups and given members.
4 * It is based around groups, so it deletes Members from a Group rather than from the entire system.
6 * In contrast to the original implementation, the URL-parameters "ParentClass" and "ParentID" are used
7 * to specify "Group" (hardcoded) and the GroupID-relation.
9 * Returns either:
10 * - provided members
11 * - members of a provided group
12 * - all members
13 * - members based on a search-query
15 class MemberTableField extends ComplexTableField {
16 protected $members;
17 protected $hidePassword;
18 protected $pageSize;
19 protected $detailFormValidator;
20 protected $group;
22 protected $template = "MemberTableField";
24 static $data_class = "Member";
26 protected $permissions = array(
27 "add",
28 "edit",
29 "delete"
32 private static $addedPermissions = array();
33 private static $addedFields = array();
34 private static $addedCsvFields = array();
36 public static function addPermissions( $addingPermissionList ) {
37 self::$addedPermissions = $addingPermissionList;
40 public static function addMembershipFields( $addingFieldList, $addingCsvFieldList = null ) {
41 self::$addedFields = $addingFieldList;
42 $addingCsvFieldList == null ? self::$addedCsvFields = $addingFieldList : self::$addedCsvFields = $addingCsvFieldList;
45 function __construct($controller, $name, $group, $members = null, $hidePassword = true, $pageLimit = 10) {
47 if($group) {
48 if(is_object($group)) {
49 $this->group = $group;
50 } else if(is_numeric($group)){
51 $this->group = DataObject::get_by_id('Group',$group);
53 } else if(is_numeric($_REQUEST['ctf'][$this->Name()]["ID"])) {
54 $this->group = DataObject::get_by_id('Group',$_REQUEST['ctf'][$this->Name()]["ID"]);
58 $sourceClass = $this->stat("data_class");
60 foreach( self::$addedPermissions as $permission )
61 array_push( $this->permissions, $permission );
63 $fieldList = array(
64 "FirstName" => "Firstname",
65 "Surname" => "Surname",
66 "Email" => "Email"
69 $csvFieldList = $fieldList;
70 foreach( self::$addedCsvFields as $key => $value ) {
71 $csvFieldList[$key] = $value;
74 foreach( self::$addedFields as $key => $value ) {
75 $fieldList[$key] = $value;
78 if(!$hidePassword) {
79 $fieldList["Password"] = "Password";
82 if(isset($_REQUEST['ctf']['childID']) && is_numeric($_REQUEST['ctf']['childID'])) {
83 $SNG_member = DataObject::get_by_id($this->stat("data_class"),$_REQUEST['ctf']['childID']);
84 } else {
85 $SNG_member = singleton(Object::getCustomClass($this->stat("data_class")));
87 $detailFormFields = $SNG_member->getCMSFields();
88 $this->detailFormValidator = $SNG_member->getValidator();
90 $this->pageSize = $pageLimit;
92 // Legacy: Use setCustomSourceItems() instead.
93 if($members) {
94 $this->customSourceItems = $this->memberListWithGroupID($members, $group);
97 $this->hidePassword = $hidePassword;
99 parent::__construct($controller, $name, $sourceClass, $fieldList, $detailFormFields);
101 Requirements::javascript("cms/javascript/MemberTableField.js");
103 // construct the filter and sort
104 if(isset($_REQUEST['MemberOrderByField'])) {
105 $this->sourceSort = "`" . Convert::raw2sql($_REQUEST['MemberOrderByField']) . "`" . Convert::raw2sql( $_REQUEST['MemberOrderByOrder'] );
108 // search
109 $search = isset($_REQUEST['MemberSearch']) ? Convert::raw2sql($_REQUEST['MemberSearch']) : null;
110 if(!empty($_REQUEST['MemberSearch'])) {
111 //$this->sourceFilter[] = "( `Email` LIKE '%$search%' OR `FirstName` LIKE '%$search%' OR `Surname` LIKE '%$search%' )";
112 $sourceF = "( ";
113 foreach( $fieldList as $k => $v )
114 $sourceF .= "`$k` LIKE '%$search%' OR ";
115 $this->sourceFilter[] = substr( $sourceF, 0, -3 ) . ")";
118 // filter by groups
119 // TODO Not implemented yet
120 if(isset($_REQUEST['ctf'][$this->Name()]['GroupID']) && is_numeric($_REQUEST['ctf'][$this->Name()]['GroupID'])) {
121 $this->sourceFilter[] = "`GroupID`='{$_REQUEST['ctf'][$this->Name()]['GroupID']}'";
122 } elseif($this->group) {
123 //$this->sourceFilter[] = "`GroupID`='{$this->group->ID}'";
124 // If the table is not clean (without duplication), the total and navigation wil not work well, so uncheck the big line below
125 $this->sourceFilter[] = "`Group_Members`.`ID` IN (SELECT `ID` FROM `Group_Members` WHERE `GroupID`='{$this->group->ID}' GROUP BY `MemberID` HAVING MIN(`ID`))";
128 $this->sourceJoin = " INNER JOIN `Group_Members` ON `MemberID`=`Member`.`ID`";
130 $this->setFieldListCsv( $csvFieldList );
134 * Overridden functions
138 function sourceID() {
139 return $this->group->ID;
142 function AddLink() {
143 return "{$this->PopupBaseLink()}&methodName=add";
146 function DetailForm() {
147 $ID = Convert::raw2xml(isset($_REQUEST['ctf']['ID'])
148 ? $_REQUEST['ctf']['ID']
149 : '');
150 $childID = Convert::raw2xml(@$_REQUEST['ctf']['childID']);
151 $childClass = Convert::raw2xml($_REQUEST['fieldName']);
152 $methodName = isset($_REQUEST['methodName']) ? $_REQUEST['methodName'] : '';
154 if($methodName == "add") {
155 $parentIdName = $this->getParentIdName($childClass,$this->getParentClass());
156 if(!$parentIdName) {
157 user_error("ComplexTableField::DetailForm() Dataobject does not seem to have an 'has-one'-relationship", E_USER_WARNING);
158 return;
160 $this->detailFormFields->push(new HiddenField('parentClass'," ",$this->getParentClass()));
163 // the ID field confuses the Controller-logic in finding the right view for ReferencedField
164 $this->detailFormFields->removeByName('ID');
166 $this->detailFormFields->push(new HiddenField("ctf[ID]"," ",$ID));
167 // add a namespaced ID instead thats "converted" by saveComplexTableField()
168 $this->detailFormFields->push(new HiddenField("ctf[childID]","",$childID));
169 $this->detailFormFields->push(new HiddenField("ClassName","",$this->sourceClass));
171 $form = new MemberTableField_Popup($this, "DetailForm", $this->detailFormFields, $this->sourceClass, $methodName == "show", $this->detailFormValidator);
173 if (is_numeric($childID)) {
174 if ($methodName == "show" || $methodName == "edit") {
175 $childData = DataObject::get_by_id($this->sourceClass, $childID);
176 $form->loadDataFrom($childData);
180 if ($methodName == "show") {
181 $form->makeReadonly();
184 return $form;
187 function SearchForm() {
188 $searchFields = new FieldGroup(
189 new TextField('MemberSearch', 'Search'),
190 new HiddenField("ctf[ID]",'',$this->group->ID),
191 new HiddenField('MemberFieldName','',$this->name),
192 new HiddenField('MemberDontShowPassword','',$this->hidePassword)
195 $orderByFields = new FieldGroup(
196 new LabelField('Order by'),
197 new FieldSet(
198 new DropdownField('MemberOrderByField','', array(
199 'FirstName' => 'FirstName',
200 'Surname' => 'Surname',
201 'Email' => 'Email'
203 new DropdownField('MemberOrderByOrder','',array(
204 'ASC' => 'Ascending',
205 'DESC' => 'Descending'
210 $groups = DataObject::get('Group');
211 $groupArray = array('' => 'Any group');
212 foreach( $groups as $group ) {
213 $groupArray[$group->ID] = $group->Title;
215 $groupFields = new DropdownField('MemberGroup','Filter by group',$groupArray );
217 $actionFields = new LiteralField('MemberFilterButton','<input type="submit" name="MemberFilterButton" value="Filter" id="MemberFilterButton"/>');
219 $fieldContainer = new FieldGroup(
220 $searchFields,
221 // $orderByFields,
222 // $groupFields,
223 $actionFields
226 return $fieldContainer->FieldHolder();
231 * Add existing member to group rather than creating a new member
233 function addtogroup() {
234 $data = $_REQUEST;
235 unset($data['ID']);
237 if(!is_numeric($data['ctf']['ID'])) {
238 FormResponse::status_messsage('Adding failed', 'bad');
241 $className = $this->stat('data_class');
242 $record = new $className();
244 $record->update($data);
245 $record->write();
247 // To Avoid duplication in the Group_Members table if the ComponentSet.php is not modified just uncomment le line below
249 //if( ! $record->isInGroup( $data['ctf']['ID'] ) )
250 $record->Groups()->add( $data['ctf']['ID'] );
252 $this->sourceItems();
254 // TODO add javascript to highlight added row (problem: might not show up due to sorting/filtering)
255 FormResponse::update_dom_id($this->id(), $this->renderWith($this->template), true);
256 FormResponse::status_message('Added member to group', 'good');
258 return FormResponse::respond();
262 * Custom delete implementation:
263 * Remove member from group rather than from the database
265 function delete() {
266 $groupID = Convert::raw2sql($_REQUEST["ctf"]["ID"]);
267 $memberID = Convert::raw2sql($_REQUEST["ctf"]["childID"]);
268 if(is_numeric($groupID) && is_numeric($memberID)) {
269 $member = DataObject::get_by_id('Member', $memberID);
270 $member->Groups()->remove($groupID);
271 } else {
272 user_error("MemberTableField::delete: Bad parameters: Group=$groupID, Member=$memberID", E_USER_ERROR);
275 return FormResponse::respond();
282 * #################################
283 * Utility Functions
284 * #################################
286 function getParentClass() {
287 return "Group";
290 function getParentIdName($childClass,$parentClass){
291 return "GroupID";
296 * #################################
297 * Custom Functions
298 * #################################
300 function memberListWithGroupID($members, $group) {
301 $newMembers = new DataObjectSet();
302 foreach($members as $member) {
303 $newMembers->push($member->customise(array("GroupID" => $group->ID)));
305 return $newMembers;
308 function setGroup($group) {
309 $this->group = $group;
311 function setController($controller) {
312 $this->controller = $controller;
315 function GetControllerName() {
316 return $this->controller->class;
320 * Add existing member to group by name (with JS-autocompletion)
322 function AddRecordForm() {
323 $fields = new FieldSet();
324 foreach($this->FieldList() as $fieldName=>$fieldTitle) {
325 $fields->push(new TextField($fieldName));
327 $fields->push(new HiddenField("ctf[ID]", null, $this->group->ID));
329 return new TabularStyle(new Form($this->controller,'AddRecordForm',
330 $fields,
331 new FieldSet(
332 new FormAction("addtogroup", "Add")
338 * Cached version for getting the appropraite members for this particular group.
340 * This includes getting inherited groups, such as groups under groups.
342 function sourceItems(){
343 // Caching.
344 if($this->sourceItems) {
345 return $this->sourceItems;
348 // Setup limits
349 $limitClause = "";
350 if(isset($_REQUEST['ctf'][$this->Name()]['start']) && is_numeric($_REQUEST['ctf'][$this->Name()]['start'])) {
351 $limitClause = ($_REQUEST['ctf'][$this->Name()]['start']) . ", {$this->pageSize}";
352 } else {
353 $limitClause = "0, {$this->pageSize}";
357 // We use the group to get the members, as they already have the bulk of the look up functions
358 $start = isset($_REQUEST['ctf'][$this->Name()]['start']) ? $_REQUEST['ctf'][$this->Name()]['start'] : 0;
359 $this->sourceItems = $this->group->Members(
360 $this->pageSize, // limit
361 $start, // offset
362 $this->sourceFilter,
363 $this->sourceSort
365 $this->unpagedSourceItems = $this->group->Members( "", "", $this->sourceFilter, $this->sourceSort );
366 $this->totalCount = ($this->sourceItems) ? $this->sourceItems->TotalItems() : 0;
367 return $this->sourceItems;
370 function TotalCount() {
371 $this->sourceItems(); // Called for its side-effect of setting total count
372 return $this->totalCount;
380 class MemberTableField_Popup extends ComplexTableField_Popup {
381 function __construct($controller, $name, $fields, $sourceClass, $readonly=false, $validator = null) {
383 // DO NOT CHANGE THE ORDER OF THESE JS FILES. THESE ARE ONLY REQUIRED FOR THIS INSTANCE !!!11onetwo
385 parent::__construct($controller, $name, $fields, $sourceClass, $readonly, $validator);
387 Requirements::javascript("cms/javascript/MemberTableField.js");
388 Requirements::javascript("cms/javascript/MemberTableField_popup.js");
392 function saveComplexTableField() {
393 $id = Convert::raw2sql($_REQUEST['ctf']['childID']);
395 if (is_numeric($id)) {
396 $childObject = DataObject::get_by_id($this->sourceClass, $id);
397 } else {
398 $childObject = new $this->sourceClass();
400 $this->saveInto($childObject);
401 $childObject->write();
403 $childObject->Groups()->add($_REQUEST['ctf']['ID']);
405 // if ajax-call in an iframe, close window by javascript, else redirect to referrer
406 if(!Director::is_ajax()) {
407 Director::redirect(substr($_SERVER['REQUEST_URI'],0,strpos($_SERVER['REQUEST_URI'],"?")));