Version: current

      1 <?php
      2 /**
      3  * Security section of the CMS
      4  * @package cms
      5  * @subpackage security
      6  */
      7 class SecurityAdmin extends LeftAndMain implements PermissionProvider {
      8
      9     static $url_segment = 'security';
     10
     11     static $url_rule = '/$Action/$ID/$OtherID';
     12
     13     static $menu_title = 'Security';
     14
     15     static $tree_class = 'Group';
     16
     17     static $subitem_class = 'Member';
     18
     19     static $allowed_actions = array(
     20         'addgroup',
     21         'addmember',
     22         'autocomplete',
     23         'removememberfromgroup',
     24         'savemember',
     25         'AddRecordForm',
     26         'MemberForm',
     27         'EditForm',
     28         'MemberImportForm',
     29         'memberimport',
     30         'GroupImportForm',
     31         'groupimport',
     32         'RootForm'
     33     );
     34
     35     /**
     36      * @var Array
     37      */
     38     static $hidden_permissions = array();
     39
     40     public function init() {
     41         parent::init();
     42
     43         Requirements::javascript(CMS_DIR . '/javascript/hover.js');
     44         Requirements::javascript(THIRDPARTY_DIR . "/scriptaculous/controls.js");
     45
     46         // needed for MemberTableField (Requirements not determined before Ajax-Call)
     47         Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang');
     48         Requirements::javascript(SAPPHIRE_DIR . "/javascript/TableListField.js");
     49         Requirements::javascript(SAPPHIRE_DIR . "/javascript/TableField.js");
     50         Requirements::javascript(SAPPHIRE_DIR . "/javascript/ComplexTableField.js");
     51         Requirements::javascript(CMS_DIR . "/javascript/MemberTableField.js");
     52         Requirements::css(THIRDPARTY_DIR . "/greybox/greybox.css");
     53         Requirements::css(SAPPHIRE_DIR . "/css/ComplexTableField.css");
     54
     55         Requirements::javascript(CMS_DIR . '/javascript/SecurityAdmin_left.js');
     56         Requirements::javascript(CMS_DIR . '/javascript/SecurityAdmin_right.js');
     57
     58         Requirements::javascript(THIRDPARTY_DIR . "/greybox/AmiJS.js");
     59         Requirements::javascript(THIRDPARTY_DIR . "/greybox/greybox.js");
     60     }
     61
     62     function getEditForm($id = null) {
     63         if(!$id) $id = $this->currentPageID();
     64
     65         if($id && $id != 'root') {
     66             $record = DataObject::get_by_id($this->stat('tree_class'), $id);
     67             if(!$record) return false;
     68         }
     69
     70         if($id && is_numeric($id)) {
     71             $fields = $record->getCMSFields();
     72
     73             if($fields->hasTabSet()) {
     74                 $fields->findOrMakeTab('Root.Import',_t('Group.IMPORTTABTITLE', 'Import'));
     75                 $fields->addFieldToTab('Root.Import',
     76                     new LiteralField(
     77                         'MemberImportFormIframe',
     78                         sprintf(
     79                             '<iframe src="%s" id="MemberImportFormIframe" width="100%%" height="400px" border="0"></iframe>',
     80                             $this->Link('memberimport')
     81                         )
     82                     )
     83                 );
     84                 if(Permission::check('APPLY_ROLES')) {
     85                     $fields->findOrMakeTab('Root.Roles', _t('SecurityAdmin.ROLES', 'Roles'));
     86                     $fields->addFieldToTab(
     87                         'Root.Roles',
     88                         new LiteralField(
     89                             'RolesAddEditLink',
     90                             sprintf(
     91                                 '<p class="add-role"><a href="%s">%s</a></p>',
     92                                 $this->Link('show/root'),
     93                                 // TODO This should include #Root_Roles to switch directly to the tab,
     94                                 // but tabstrip.js doesn't display tabs when directly adressed through a URL pragma
     95                                 _t('Group.RolesAddEditLink', 'Add/edit roles')
     96                             )
     97                         )
     98                     );
     99                 }
    100             }
    101
    102             $actions = new FieldSet(
    103                 new FormAction('addmember',_t('SecurityAdmin.ADDMEMBER','Add Member'))
    104             );
    105
    106             if (Permission::check('EDIT_PERMISSIONS')) {
    107                 $actions->push(new FormAction('save',_t('SecurityAdmin.SAVE','Save')));
    108             }
    109
    110             $form = new Form($this, "EditForm", $fields, $actions);
    111             $form->loadDataFrom($record);
    112
    113             if(!$record->canEdit()) {
    114                 $readonlyFields = $form->Fields()->makeReadonly();
    115                 $form->setFields($readonlyFields);
    116             }
    117
    118             // Filter permissions
    119             $permissionField = $form->Fields()->dataFieldByName('Permissions');
    120             if($permissionField) $permissionField->setHiddenPermissions(self::$hidden_permissions);
    121
    122             $this->extend('updateEditForm', $form);
    123         } else {
    124             $form = $this->RootForm();
    125         }
    126
    127         return $form;
    128     }
    129
    130     /**
    131      * @return FieldSet
    132      */
    133     function RootForm() {
    134         $memberList = new MemberTableField(
    135             $this,
    136             "Members"
    137         );
    138         // unset 'inlineadd' permission, we don't want inline addition
    139         $memberList->setPermissions(array('edit', 'delete', 'add'));
    140         $memberList->setRelationAutoSetting(false);
    141
    142         $fields = new FieldSet(
    143             new TabSet(
    144                 'Root',
    145                 new Tab('Members', singleton('Member')->i18n_plural_name(),
    146                     $memberList,
    147                     new LiteralField('MembersCautionText',
    148                         sprintf('<p class="caution-remove"><strong>%s</strong></p>',
    149                             _t(
    150                                 'SecurityAdmin.MemberListCaution',
    151                                 'Caution: Removing members from this list will remove them from all groups and the database'
    152                             )
    153                         )
    154                     )
    155                 ),
    156                 new Tab('Import', _t('SecurityAdmin.TABIMPORT', 'Import'),
    157                     new LiteralField(
    158                         'GroupImportFormIframe',
    159                         sprintf(
    160                             '<iframe src="%s" id="GroupImportFormIframe" width="100%%" height="400px" border="0"></iframe>',
    161                             $this->Link('groupimport')
    162                         )
    163                     )
    164                 )
    165             ),
    166             // necessary for tree node selection in LeftAndMain.EditForm.js
    167             new HiddenField('ID', false, 0)
    168         );
    169
    170         // Add roles editing interface
    171         if(Permission::check('APPLY_ROLES')) {
    172             $rolesCTF = new ComplexTableField(
    173                 $this,
    174                 'Roles',
    175                 'PermissionRole'
    176             );
    177             $rolesCTF->setPermissions(array('add', 'edit', 'delete'));
    178
    179             $rolesTab = $fields->findOrMakeTab('Root.Roles', _t('SecurityAdmin.TABROLES', 'Roles'));
    180             $rolesTab->push(new LiteralField(
    181                 'RolesDescription',
    182                 ''
    183             ));
    184             $rolesTab->push($rolesCTF);
    185         }
    186
    187         $actions = new FieldSet(
    188             new FormAction('addmember',_t('SecurityAdmin.ADDMEMBER','Add Member'))
    189         );
    190
    191         $this->extend('updateRootFormFields', $fields, $actions);
    192
    193         $form = new Form(
    194             $this,
    195             'EditForm',
    196             $fields,
    197             $actions
    198         );
    199
    200         return $form;
    201     }
    202
    203     public function memberimport() {
    204         Requirements::clear();
    205         Requirements::css(SAPPHIRE_DIR . '/css/Form.css');
    206         Requirements::css(CMS_DIR . '/css/typography.css');
    207         Requirements::css(CMS_DIR . '/css/cms_right.css');
    208         Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery.js');
    209         Requirements::javascript(THIRDPARTY_DIR . '/jquery-livequery/jquery.livequery.js');
    210         Requirements::javascript(SAPPHIRE_DIR . '/javascript/jquery_improvements.js');
    211         Requirements::css(CMS_DIR . '/css/MemberImportForm.css');
    212         Requirements::javascript(CMS_DIR . '/javascript/MemberImportForm.js');
    213
    214         return $this->renderWith('BlankPage', array(
    215             'Form' => $this->MemberImportForm()
    216         ));
    217     }
    218
    219     /**
    220      * @see SecurityAdmin_MemberImportForm
    221      *
    222      * @return Form
    223      */
    224     public function MemberImportForm() {
    225         $group = $this->currentPage();
    226         $form = new MemberImportForm(
    227             $this,
    228             'MemberImportForm'
    229         );
    230         $form->setGroup($group);
    231
    232         return $form;
    233     }
    234
    235     public function groupimport() {
    236         Requirements::clear();
    237         Requirements::css(SAPPHIRE_DIR . '/css/Form.css');
    238         Requirements::css(CMS_DIR . '/css/typography.css');
    239         Requirements::css(CMS_DIR . '/css/cms_right.css');
    240         Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery.js');
    241         Requirements::javascript(THIRDPARTY_DIR . '/jquery-livequery/jquery.livequery.js');
    242         Requirements::javascript(SAPPHIRE_DIR . '/javascript/jquery_improvements.js');
    243         Requirements::css(CMS_DIR . '/css/MemberImportForm.css');
    244         Requirements::javascript(CMS_DIR . '/javascript/MemberImportForm.js');
    245
    246         return $this->renderWith('BlankPage', array(
    247             'Form' => $this->GroupImportForm()
    248         ));
    249     }
    250
    251     /**
    252      * @see SecurityAdmin_MemberImportForm
    253      *
    254      * @return Form
    255      */
    256     public function GroupImportForm() {
    257         $form = new GroupImportForm(
    258             $this,
    259             'GroupImportForm'
    260         );
    261
    262         return $form;
    263     }
    264
    265     public function AddRecordForm() {
    266         $m = SS_Object::create('MemberTableField',
    267             $this,
    268             "Members",
    269             $this->currentPageID()
    270         );
    271         return $m->AddRecordForm();
    272     }
    273
    274     /**
    275      * Ajax autocompletion
    276      */
    277     public function autocomplete() {
    278         $fieldName = $this->urlParams['ID'];
    279         $fieldVal = $_REQUEST[$fieldName];
    280         $result = '';
    281
    282         // Make sure we only autocomplete on keys that actually exist, and that we don't autocomplete on password
    283         if(!singleton($this->stat('subitem_class'))->hasDatabaseField($fieldName)  || $fieldName == 'Password') return;
    284
    285         $matches = DataObject::get($this->stat('subitem_class'),"\"$fieldName\" LIKE '" . Convert::raw2sql($fieldVal) . "%'");
    286         if($matches) {
    287             $result .= "<ul>";
    288             foreach($matches as $match) {
    289                 if(!$match->canView()) continue;
    290
    291                 $data = $match->FirstName;
    292                 $data .= ",$match->Surname";
    293                 $data .= ",$match->Email";
    294                 $result .= "<li>" . $match->$fieldName . "<span class=\"informal\">($match->FirstName $match->Surname, $match->Email)</span><span class=\"informal data\">$data</span></li>";
    295             }
    296             $result .= "</ul>";
    297             return $result;
    298         }
    299     }
    300
    301     public function MemberForm() {
    302         $id = $_REQUEST['ID'] ? $_REQUEST['ID'] : Session::get('currentMember');
    303         if($id) return $this->getMemberForm($id);
    304     }
    305
    306     public function getMemberForm($id) {
    307         if($id && $id != 'new') $record = DataObject::get_by_id('Member', (int) $id);
    308         if($record || $id == 'new') {
    309             $fields = new FieldSet(
    310                 new HiddenField('MemberListBaseGroup', '', $this->currentPageID() )
    311             );
    312
    313             if($extraFields = $record->getCMSFields()) {
    314                 foreach($extraFields as $extra) {
    315                     $fields->push( $extra );
    316                 }
    317             }
    318
    319             $fields->push($idField = new HiddenField('ID'));
    320             $fields->push($groupIDField = new HiddenField('GroupID'));
    321
    322             $actions = new FieldSet();
    323             $actions->push(new FormAction('savemember', _t('SecurityAdmin.SAVE')));
    324
    325             $form = new Form($this, 'MemberForm', $fields, $actions);
    326             if($record) $form->loadDataFrom($record);
    327
    328             $idField->setValue($id);
    329             $groupIDField->setValue($this->currentPageID());
    330
    331             if($record && !$record->canEdit()) {
    332                 $readonlyFields = $form->Fields()->makeReadonly();
    333                 $form->setFields($readonlyFields);
    334             }
    335
    336             return $form;
    337         }
    338     }
    339
    340     function savemember() {
    341         $data = $_REQUEST;
    342         $className = $this->stat('subitem_class');
    343
    344         $id = $_REQUEST['ID'];
    345         if($id == 'new') $id = null;
    346
    347         if($id) {
    348             $record = DataObject::get_by_id($className, $id);
    349             if($record && !$record->canEdit()) return Security::permissionFailure($this);
    350         } else {
    351             if(!singleton($this->stat('subitem_class'))->canCreate()) return Security::permissionFailure($this);
    352             $record = new $className();
    353         }
    354
    355         $record->update($data);
    356         $record->ID = $id;
    357         $record->write();
    358
    359         $record->Groups()->add($data['GroupID']);
    360
    361         FormResponse::add("reloadMemberTableField();");
    362
    363         return FormResponse::respond();
    364     }
    365
    366     function addmember($className=null) {
    367         $data = $_REQUEST;
    368         unset($data['ID']);
    369         if($className == null) $className = $this->stat('subitem_class');
    370
    371         if(!singleton($this->stat('subitem_class'))->canCreate()) return Security::permissionFailure($this);
    372
    373         $record = new $className();
    374
    375         $record->update($data);
    376         $record->write();
    377
    378         if($data['GroupID']) $record->Groups()->add((int)$data['GroupID']);
    379
    380         FormResponse::add("reloadMemberTableField();");
    381
    382         return FormResponse::respond();
    383     }
    384
    385     public function removememberfromgroup() {
    386         $groupID = $this->urlParams['ID'];
    387         $memberID = $this->urlParams['OtherID'];
    388         if(is_numeric($groupID) && is_numeric($memberID)) {
    389             $member = DataObject::get_by_id('Member', (int) $memberID);
    390
    391             if(!$member->canDelete()) return Security::permissionFailure($this);
    392
    393             $member->Groups()->remove((int)$groupID);
    394
    395             FormResponse::add("reloadMemberTableField();");
    396         } else {
    397             user_error("SecurityAdmin::removememberfromgroup: Bad parameters: Group=$groupID, Member=$memberID", E_USER_ERROR);
    398         }
    399
    400         return FormResponse::respond();
    401     }
    402
    403     /**
    404      * Return the entire site tree as a nested set of ULs.
    405      * @return string Unordered list HTML
    406      */
    407     public function SiteTreeAsUL() {
    408         $obj = singleton($this->stat('tree_class'));
    409         $obj->markPartialTree();
    410
    411         if($p = $this->currentPage()) $obj->markToExpose($p);
    412
    413         // getChildrenAsUL is a flexible and complex way of traversing the tree
    414         $siteTreeList = $obj->getChildrenAsUL(
    415             '',
    416             '"<li id=\"record-$child->ID\" class=\"$child->class " . $child->markingClasses() . ($extraArg->isCurrentPage($child) ? " current" : "") . "\">" . ' .
    417             '"<a href=\"" . Controller::join_links(substr($extraArg->Link(),0,-1), "show", $child->ID) . "\" >" . $child->TreeTitle() . "</a>" ',
    418             $this,
    419             true
    420         );
    421
    422         // Wrap the root if needs be
    423         $rootLink = $this->Link() . 'show/root';
    424         $rootTitle = _t('SecurityAdmin.SGROUPS', 'Security Groups');
    425         if(!isset($rootID)) {
    426             $siteTree = "<ul id=\"sitetree\" class=\"tree unformatted\"><li id=\"record-root\" class=\"Root\"><a href=\"$rootLink\"><strong>{$rootTitle}</strong></a>"
    427             . $siteTreeList . "</li></ul>";
    428         }
    429
    430         return $siteTree;
    431     }
    432
    433     public function addgroup() {
    434         if(!singleton($this->stat('tree_class'))->canCreate()) return Security::permissionFailure($this);
    435
    436         $newGroup = SS_Object::create($this->stat('tree_class'));
    437         $newGroup->Title = _t('SecurityAdmin.NEWGROUP',"New Group");
    438         $newGroup->Code = "new-group";
    439         $newGroup->ParentID = (is_numeric($_REQUEST['ParentID'])) ? (int)$_REQUEST['ParentID'] : 0;
    440         $newGroup->write();
    441
    442         return $this->returnItemToUser($newGroup);
    443     }
    444
    445     public function EditedMember() {
    446         if(Session::get('currentMember')) return DataObject::get_by_id('Member', (int) Session::get('currentMember'));
    447     }
    448
    449     function providePermissions() {
    450         return array(
    451             'EDIT_PERMISSIONS' => array(
    452                 'name' => _t('SecurityAdmin.EDITPERMISSIONS', 'Manage permissions for groups'),
    453                 'category' => _t('Permissions.PERMISSIONS_CATEGORY', 'Roles and access permissions'),
    454                 'help' => _t('SecurityAdmin.EDITPERMISSIONS_HELP', 'Ability to edit Permissions and IP Addresses for a group. Requires the "Access to \'Security\' section" permission.'),
    455                 'sort' => 0
    456             ),
    457             'APPLY_ROLES' => array(
    458                 'name' => _t('SecurityAdmin.APPLY_ROLES', 'Apply roles to groups'),
    459                 'category' => _t('Permissions.PERMISSIONS_CATEGORY', 'Roles and access permissions'),
    460                 'help' => _t('SecurityAdmin.APPLY_ROLES_HELP', 'Ability to edit the roles assigned to a group. Requires the "Access to \'Security\' section" permission.'),
    461                 'sort' => 0
    462             )
    463         );
    464     }
    465
    466     /**
    467      * The permissions represented in the $codes will not appearing in the form
    468      * containing {@link PermissionCheckboxSetField} so as not to be checked / unchecked.
    469      *
    470      * @param $codes String|Array
    471      */
    472     static function add_hidden_permission($codes){
    473         if(is_string($codes)) $codes = array($codes);
    474         self::$hidden_permissions = array_merge(self::$hidden_permissions, $codes);
    475     }
    476
    477     /**
    478      * @param $codes String|Array
    479      */
    480     static function remove_hidden_permission($codes){
    481         if(is_string($codes)) $codes = array($codes);
    482         self::$hidden_permissions = array_diff(self::$hidden_permissions, $codes);
    483     }
    484
    485     /**
    486      * @return Array
    487      */
    488     static function get_hidden_permissions(){
    489         return self::$hidden_permissions;
    490     }
    491
    492     /**
    493      * Clear all permissions previously hidden with {@link add_hidden_permission}
    494      */
    495     static function clear_hidden_permissions(){
    496         self::$hidden_permissions = array();
    497     }
    498 }
    499
    500 ?>
    501

[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. -