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

Packages

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