Webylon 3.1 API Docs
  • Package
  • Class
  • Tree
  • Deprecated
  • Download
Version: current
  • 3.2
  • 3.1

Packages

  • auth
  • Booking
  • cart
    • shipping
    • steppedcheckout
  • Catalog
  • cms
    • assets
    • batchaction
    • batchactions
    • bulkloading
    • comments
    • content
    • core
    • export
    • newsletter
    • publishers
    • reports
    • security
    • tasks
  • Dashboard
  • DataObjectManager
  • event
  • faq
  • forms
    • actions
    • core
    • fields-basic
    • fields-dataless
    • fields-datetime
    • fields-files
    • fields-formatted
    • fields-formattedinput
    • fields-relational
    • fields-structural
    • transformations
    • validators
  • googlesitemaps
  • guestbook
  • installer
  • newsletter
  • None
  • photo
    • gallery
  • PHP
  • polls
  • recaptcha
  • sapphire
    • api
    • bulkloading
    • control
    • core
    • cron
    • dev
    • email
    • fields-formattedinput
    • filesystem
    • formatters
    • forms
    • i18n
    • integration
    • misc
    • model
    • parsers
    • search
    • security
    • tasks
    • testing
    • tools
    • validation
    • view
    • widgets
  • seo
    • open
      • graph
  • sfDateTimePlugin
  • spamprotection
  • stealth
    • captha
  • subsites
  • userform
    • pagetypes
  • userforms
  • webylon
  • widgets

Classes

  • MemberTableField
  • MemberTableField_Item
  • MemberTableField_ItemRequest
  • MemberTableField_Popup
  • SecurityAdmin
  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.
  5  *
  6  * In contrast to the original implementation, the URL-parameters "ParentClass" and "ParentID" are used
  7  * to specify "Group" (hardcoded) and the GroupID-relation.
  8  *
  9  *  @todo write a better description about what this field does.
 10  *
 11  * Returns either:
 12  * - provided members
 13  * - members of a provided group
 14  * - all members
 15  * - members based on a search-query
 16  * 
 17  * @package cms
 18  * @subpackage security
 19  */
 20 class MemberTableField extends ComplexTableField {
 21     
 22     protected $members;
 23     
 24     protected $hidePassword;
 25     
 26     protected $detailFormValidator;
 27     
 28     protected $group;
 29 
 30     protected $template = 'MemberTableField';
 31 
 32     public $popupClass = 'MemberTableField_Popup';
 33     
 34     public $itemClass = 'MemberTableField_Item';
 35     
 36     static $data_class = 'Member';
 37     
 38     /**
 39      * Set the page size for this table. 
 40      * @var int 
 41      */ 
 42     public static $page_size = 20; 
 43     
 44     protected $permissions = array(
 45         "add",
 46         "edit",
 47         "show",
 48         "delete",
 49 //      'inlineadd',
 50 //      "export",
 51     );
 52 
 53     /**
 54      * Constructor method for MemberTableField.
 55      * 
 56      * @param Controller $controller Controller class which created this field
 57      * @param string $name Name of the field (e.g. "Members")
 58      * @param mixed $group Can be the ID of a Group instance, or a Group instance itself
 59      * @param DataObjectSet $members Optional set of Members to set as the source items for this field
 60      * @param boolean $hidePassword Hide the password field or not in the summary?
 61      */
 62     function __construct($controller, $name, $group = null, $members = null, $hidePassword = true) {
 63         $sourceClass = self::$data_class;
 64         $SNG_member = singleton($sourceClass);
 65         $fieldList = $SNG_member->summaryFields();
 66         $memberDbFields = $SNG_member->db();
 67         $csvFieldList = array();
 68 
 69         foreach($memberDbFields as $field => $dbFieldType) {
 70             $csvFieldList[$field] = $field;
 71         }
 72         
 73         if($group) {
 74             if(is_object($group)) {
 75                 $this->group = $group;
 76             } elseif(is_numeric($group)) {
 77                 $this->group = DataObject::get_by_id('Group', $group);
 78             }
 79         } else if(isset($_REQUEST['ctf'][$this->Name()]["ID"]) && is_numeric($_REQUEST['ctf'][$this->Name()]["ID"])) {
 80             $this->group = DataObject::get_by_id('Group', $_REQUEST['ctf'][$this->Name()]["ID"]);
 81         }
 82 
 83         if(!$hidePassword) {
 84             $fieldList["SetPassword"] = "Password"; 
 85         }
 86 
 87         $this->hidePassword = $hidePassword;
 88         
 89         // @todo shouldn't this use $this->group? It's unclear exactly
 90         // what group it should be customising the custom Member set with.
 91         if($members && $group) {
 92             $this->setCustomSourceItems($this->memberListWithGroupID($members, $group));
 93         }
 94 
 95         parent::__construct($controller, $name, $sourceClass, $fieldList);
 96         
 97         $SQL_search = isset($_REQUEST['MemberSearch']) ? Convert::raw2sql($_REQUEST['MemberSearch']) : null;
 98         if(!empty($_REQUEST['MemberSearch'])) {
 99             $searchFilters = array();
100             foreach($SNG_member->searchableFields() as $fieldName => $fieldSpec) {
101                 if(strpos($fieldName, '.') === false) $searchFilters[] = "\"$fieldName\" LIKE '%{$SQL_search}%'";
102             }
103             $this->sourceFilter[] = '(' . implode(' OR ', $searchFilters) . ')';
104         }
105         
106         if($this->group) {
107             $groupIDs = array($this->group->ID);
108             if($this->group->AllChildren()) $groupIDs = array_merge($groupIDs, $this->group->AllChildren()->column('ID'));
109             $this->sourceFilter[] = sprintf(
110                 '"Group_Members"."GroupID" IN (%s)', 
111                 implode(',', $groupIDs)
112             );
113         }
114 
115         $this->sourceJoin = " INNER JOIN \"Group_Members\" ON \"MemberID\"=\"Member\".\"ID\"";
116         $this->setFieldListCsv($csvFieldList);
117         $this->setPageSize($this->stat('page_size'));
118     }
119     
120     function FieldHolder() {
121         $ret = parent::FieldHolder();
122         
123         Requirements::javascript(CMS_DIR . '/javascript/MemberTableField.js');
124         Requirements::javascript(CMS_DIR . "/javascript/MemberTableField_popup.js");
125         
126         return $ret;
127     }
128 
129     function sourceID() {
130         return ($this->group) ? $this->group->ID : 0;
131     }
132 
133     function AddLink() {
134         return $this->Link() . '/add';
135     }
136 
137     function SearchForm() {
138         $groupID = (isset($this->group)) ? $this->group->ID : 0;
139         $query = isset($_GET['MemberSearch']) ? $_GET['MemberSearch'] : null;
140         
141         $searchFields = new FieldGroup(
142             new TextField('MemberSearch', _t('MemberTableField.SEARCH', 'Search'), $query),
143             new HiddenField("ctf[ID]", '', $groupID),
144             new HiddenField('MemberFieldName', '', $this->name),
145             new HiddenField('MemberDontShowPassword', '', $this->hidePassword)
146         );
147 
148         $actionFields = new LiteralField('MemberFilterButton','<input type="submit" class="action" name="MemberFilterButton" value="'._t('MemberTableField.FILTER', 'Filter').'" id="MemberFilterButton"/>');
149 
150         $fieldContainer = new FieldGroup(
151             $searchFields,
152             $actionFields
153         );
154 
155         return $fieldContainer->FieldHolder();
156     }
157 
158     /**
159      * Add existing member to group rather than creating a new member
160      */
161     function addtogroup() {
162         $data = $_REQUEST;
163         $groupID = (isset($data['ctf']['ID'])) ? $data['ctf']['ID'] : null;
164 
165         if(!is_numeric($groupID)) {
166             FormResponse::status_messsage(_t('MemberTableField.ADDINGFIELD', 'Adding failed'), 'bad');
167             return;
168         }
169 
170         // Get existing record either by ID or unique identifier.
171         $identifierField = Member::get_unique_identifier_field();
172         $className = self::$data_class;
173         $record = null;
174         if(isset($data[$identifierField])) {
175             $record = DataObject::get_one(
176                 $className, 
177                 sprintf('"%s" = \'%s\'', $identifierField, $data[$identifierField])
178             );
179         } 
180             
181         // Fall back to creating a new record
182         if(!$record) $record = new $className();
183         
184         // Update an existing record, or populate a new one.
185         // If values on an existing (autocompleted) record have been changed,
186         // they will overwrite current data. We need to unset 'ID'
187         // record as it points to the group rather than the member record, and would
188         // cause the member to be written to a potentially existing record.
189         unset($data['ID']);
190         $record->update($data);
191         
192         // Validate record, mainly password restrictions.
193         // Note: Doesn't use Member_Validator
194         $valid = $record->validate();
195         if($valid->valid()) {
196             $record->write();
197             $record->Groups()->add($groupID);
198 
199             $this->sourceItems();
200 
201             // TODO add javascript to highlight added row (problem: might not show up due to sorting/filtering)
202             FormResponse::update_dom_id($this->id(), $this->renderWith($this->template), true);
203             FormResponse::status_message(
204                 _t(
205                     'MemberTableField.ADDEDTOGROUP','Added member to group'
206                 ),
207                 'good'
208             );
209         
210         } else {
211             $message = sprintf(
212                 _t(
213                     'MemberTableField.ERRORADDINGUSER',
214                     'There was an error adding the user to the group: %s'
215                 ),
216                 Convert::raw2xml($valid->starredList())
217             );
218             
219             FormResponse::status_message($message, 'bad');
220         }
221 
222         return FormResponse::respond();
223     }
224 
225     /**
226      * Custom delete implementation:
227      * Remove member from group rather than from the database
228      */
229     function delete() {
230         $groupID = Convert::raw2sql($_REQUEST['ctf']['ID']);
231         $memberID = Convert::raw2sql($_REQUEST['ctf']['childID']);
232         if(is_numeric($groupID) && is_numeric($memberID)) {
233             $member = DataObject::get_by_id('Member', $memberID);
234             $member->Groups()->remove($groupID);
235         } else {
236             user_error("MemberTableField::delete: Bad parameters: Group=$groupID, Member=$memberID", E_USER_ERROR);
237         }
238 
239         return FormResponse::respond();
240 
241     }
242 
243     /**
244      * #################################
245      *           Utility Functions
246      * #################################
247      */
248     function getParentClass() {
249         return 'Group';
250     }
251 
252     function getParentIdName($childClass, $parentClass) {
253         return 'GroupID';
254     }
255 
256     /**
257      * #################################
258      *           Custom Functions
259      * #################################
260      */
261     
262     /**
263      * Customise an existing DataObjectSet of Member
264      * objects with a GroupID.
265      * 
266      * @param DataObjectSet $members Set of Member objects to customise
267      * @param Group $group Group object to customise with
268      * @return DataObjectSet Customised set of Member objects
269      */
270     function memberListWithGroupID($members, $group) {
271         $newMembers = new DataObjectSet();
272         foreach($members as $member) {
273             $newMembers->push($member->customise(array('GroupID' => $group->ID)));
274         }
275         return $newMembers;
276     }
277 
278     function setGroup($group) {
279         $this->group = $group;
280     }
281     
282     /**
283      * @return Group
284      */
285     function getGroup() {
286         return $this->group;
287     }
288     
289     function setController($controller) {
290         $this->controller = $controller;
291     }
292 
293     function GetControllerName() {
294         return $this->controller->class;
295     }
296 
297     /**
298      * Add existing member to group by name (with JS-autocompletion)
299      */
300     function AddRecordForm() {
301         return '';
302         $fields = new FieldSet();
303         foreach($this->FieldList() as $fieldName => $fieldTitle) {
304             // If we're adding the set password field, we want to hide the text from any peeping eyes
305             if($fieldName == 'SetPassword') {
306                 $fields->push($f = new PasswordField($fieldName));
307             } else {
308                 $fields->push($f = new TextField($fieldName));
309             }
310             $f->addExtraAttribute('autocomplete', 'off');
311         }
312         if($this->group) {
313             $fields->push(new HiddenField('ctf[ID]', null, $this->group->ID));
314         }
315         $actions = new FieldSet(
316             new FormAction('addtogroup', _t('MemberTableField.ADD','Add'))
317         );
318         
319         return new TabularStyle(
320             new NestedForm(
321                 new Form(
322                     $this,
323                     'AddRecordForm',
324                     $fields,
325                     $actions
326                 )
327             )
328         );
329     }
330     
331     function AddForm() {
332         $form = parent::AddForm();
333         
334         // Set default groups - also implemented in MemberTableField_Popup::__construct()
335         if($this->group) {
336             $groupsField = $form->Fields()->dataFieldByName('Groups');
337             if($groupsField) $groupsField->setValue($this->group->ID);
338         }
339         
340         return $form;
341     }
342 
343     /**
344      * Same behaviour as parent class, but adds the
345      * member to the passed GroupID.
346      *
347      * @return string
348      */
349     function saveComplexTableField($data, $form, $params) {     
350         $className = $this->sourceClass();
351         $childData = new $className();
352         
353         // Needs to write before saveInto() to ensure the 'Groups' TreeMultiselectField saves
354         $childData->write();
355         
356         try {
357             $form->saveInto($childData);
358             $childData->write();
359         } catch(ValidationException $e) {
360             var_dump($e->getResult());
361             $form->sessionMessage($e->getResult()->message(), 'bad');
362             return Director::redirectBack();
363         }
364         
365         $closeLink = sprintf(
366             '<small><a href="' . $_SERVER['HTTP_REFERER'] . '" onclick="javascript:window.top.GB_hide(); return false;">(%s)</a></small>',
367             _t('ComplexTableField.CLOSEPOPUP', 'Close Popup')
368         );
369         $message = sprintf(
370             _t('ComplexTableField.SUCCESSADD', 'Added %s %s %s'),
371             $childData->i18n_singular_name(),
372             '<a href="' . $this->Link() . '">' . htmlspecialchars($childData->Title, ENT_QUOTES) . '</a>',
373             $closeLink
374         );
375         $form->sessionMessage($message, 'good');
376 
377         Director::redirectBack();       
378     }   
379     
380     /**
381      * Cached version for getting the appropraite members for this particular group.
382      *
383      * This includes getting inherited groups, such as groups under groups.
384      */
385     function sourceItems() {
386         // Caching.
387         if($this->sourceItems) {
388             return $this->sourceItems;
389         }
390 
391         // Setup limits
392         $limitClause = '';
393         if(isset($_REQUEST['ctf'][$this->Name()]['start']) && is_numeric($_REQUEST['ctf'][$this->Name()]['start'])) {
394             $limitClause = ($_REQUEST['ctf'][$this->Name()]['start']) . ", {$this->pageSize}";
395         } else {
396             $limitClause = "0, {$this->pageSize}";
397         }
398                 
399         // We use the group to get the members, as they already have the bulk of the look up functions
400         $start = isset($_REQUEST['ctf'][$this->Name()]['start']) ? $_REQUEST['ctf'][$this->Name()]['start'] : 0; 
401         
402         $this->sourceItems = false;
403 
404         if($this->group) {
405             $this->sourceItems = $this->group->Members( 
406                 $this->pageSize, // limit 
407                 $start, // offset 
408                 $this->sourceFilter,
409                 $this->sourceSort
410             );  
411         } else {
412             $this->sourceItems = DataObject::get(self::$data_class,
413                 $this->sourceFilter,
414                 $this->sourceSort,
415                 null,
416                 array('limit' => $this->pageSize, 'start' => $start)
417             );
418         }
419         // Because we are not used $this->upagedSourceItems any more, and the DataObjectSet is usually the source
420         // that a large member set runs out of memory. we disable it here.
421         //$this->unpagedSourceItems = $this->group->Members('', '', $this->sourceFilter, $this->sourceSort);
422         $this->totalCount = ($this->sourceItems) ? $this->sourceItems->TotalItems() : 0;
423         
424         return $this->sourceItems;
425     }
426 
427     function TotalCount() {
428         $this->sourceItems(); // Called for its side-effect of setting total count
429         return $this->totalCount;
430     }
431 
432     /**
433      * Handles item requests
434      * MemberTableField needs its own item request class so that it can overload the delete method
435      */
436     function handleItem($request) {
437         return new MemberTableField_ItemRequest($this, $request->param('ID'));
438     }
439 }
440 
441 /**
442  * Popup window for {@link MemberTableField}.
443  * @package cms
444  * @subpackage security
445  */
446 class MemberTableField_Popup extends ComplexTableField_Popup {
447 
448     function __construct($controller, $name, $fields, $validator, $readonly, $dataObject) {
449         $group = ($controller instanceof MemberTableField) ? $controller->getGroup() : $controller->getParent()->getGroup();
450         // Set default groups - also implemented in AddForm()
451         if($group) {
452             $groupsField = $fields->dataFieldByName('Groups');
453             if($groupsField) $groupsField->setValue($group->ID);
454         }
455         
456         parent::__construct($controller, $name, $fields, $validator, $readonly, $dataObject);
457     }
458     
459     function forTemplate() {
460         $ret = parent::forTemplate();
461         
462         Requirements::css(CMS_DIR . '/css/SecurityAdmin.css');
463         Requirements::javascript(CMS_DIR . '/javascript/MemberTableField.js');
464         Requirements::javascript(CMS_DIR . '/javascript/MemberTableField_popup.js');
465         
466         return $ret;
467     }
468 
469 }
470 
471 /**
472 * @package cms
473 * @subpackage security
474 */
475 class MemberTableField_Item extends ComplexTableField_Item {
476 
477     function Actions() {
478         $actions = parent::Actions();
479         
480         foreach($actions as $action) {
481             if($action->Name == 'delete') {
482                 if($this->parent->getGroup()) {
483                     $action->TitleText = _t('MemberTableField.DeleteTitleText',
484                         'Delete from this group',
485                         PR_MEDIUM,
486                         'Delete button hover text'
487                     );
488                 } else {
489                     $action->TitleText = _t('MemberTableField.DeleteTitleTextDatabase',
490                         'Delete from database and all groups',
491                         PR_MEDIUM,
492                         'Delete button hover text'
493                     );
494                 }
495             }
496         }
497 
498         return $actions;
499     }
500 }
501 
502 /**
503 * @package cms
504 * @subpackage security
505 */
506 
507 class MemberTableField_ItemRequest extends ComplexTableField_ItemRequest {
508 
509     /**
510      * Deleting an item from a member table field should just remove that member from the group
511      */
512     function delete() {
513         if($this->ctf->Can('delete') !== true) {
514             return false;
515         }
516 
517         // if a group limitation is set on the table, remove relation.
518         // otherwise remove the record from the database
519         if($this->ctf->getGroup()) {
520             $groupID = $this->ctf->sourceID();
521             $group = DataObject::get_by_id('Group', $groupID);
522             
523             // Remove from group and all child groups
524             foreach($group->getAllChildren() as $subGroup) {
525                 $this->dataObj()->Groups()->remove($subGroup);
526             }
527             $this->dataObj()->Groups()->remove($groupID);
528         } else {
529             $this->dataObj()->delete();
530         }   
531     }
532     
533     function getParent() {
534         return $this->ctf;
535     }
536 }
537 
538 ?>
[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the Silverstripe Forums to ask development related questions. -
Webylon 3.1 API Docs API documentation generated by ApiGen 2.8.0