filterField = $filterField; $this->managedClass = $managedClass; if($records instanceof SS_List) { $this->records = $records; } elseif($records instanceof Group) { $this->records = new ArrayList(array($records)); } elseif($records) { throw new InvalidArgumentException( '$record should be either a Group record, or a SS_List of Group records'); } // Get all available codes in the system as a categorized nested array $this->source = Permission::get_codes(true); parent::__construct($name, $title); } /** * @param Array $codes */ public function setHiddenPermissions($codes) { $this->hiddenPermissions = $codes; } /** * @return Array */ public function getHiddenPermissions() { return $this->hiddenPermissions; } /** * @param array $properties * @return HTMLText */ public function Field($properties = array()) { Requirements::css(FRAMEWORK_DIR . '/css/CheckboxSetField.css'); Requirements::javascript(FRAMEWORK_DIR . '/javascript/dist/PermissionCheckboxSetField.js'); $uninheritedCodes = array(); $inheritedCodes = array(); $records = ($this->records) ? $this->records : new ArrayList(); // Get existing values from the form record (assuming the formfield name is a join field on the record) if(is_object($this->form)) { $record = $this->form->getRecord(); if( $record && (is_a($record, 'Group') || is_a($record, 'PermissionRole')) && !$records->find('ID', $record->ID) ) { $records->push($record); } } // Get all 'inherited' codes not directly assigned to the group (which is stored in $values) foreach($records as $record) { // Get all uninherited permissions $relationMethod = $this->name; foreach($record->$relationMethod() as $permission) { if(!isset($uninheritedCodes[$permission->Code])) $uninheritedCodes[$permission->Code] = array(); $uninheritedCodes[$permission->Code][] = _t( 'PermissionCheckboxSetField.AssignedTo', 'assigned to "{title}"', array('title' => $record->dbObject('Title')->forTemplate()) ); } // Special case for Group records (not PermissionRole): // Determine inherited assignments if(is_a($record, 'Group')) { // Get all permissions from roles if ($record->Roles()->Count()) { foreach($record->Roles() as $role) { foreach($role->Codes() as $code) { if (!isset($inheritedCodes[$code->Code])) $inheritedCodes[$code->Code] = array(); $inheritedCodes[$code->Code][] = _t( 'PermissionCheckboxSetField.FromRole', 'inherited from role "{title}"', 'A permission inherited from a certain permission role', array('title' => $role->dbObject('Title')->forTemplate()) ); } } } // Get from parent groups $parentGroups = $record->getAncestors(); if ($parentGroups) { foreach ($parentGroups as $parent) { if (!$parent->Roles()->Count()) continue; foreach($parent->Roles() as $role) { if ($role->Codes()) { foreach($role->Codes() as $code) { if (!isset($inheritedCodes[$code->Code])) $inheritedCodes[$code->Code] = array(); $inheritedCodes[$code->Code][] = _t( 'PermissionCheckboxSetField.FromRoleOnGroup', 'inherited from role "%s" on group "%s"', 'A permission inherited from a role on a certain group', array('roletitle' => $role->dbObject('Title')->forTemplate(), 'grouptitle' => $parent->dbObject('Title')->forTemplate()) ); } } } if ($parent->Permissions()->Count()) { foreach($parent->Permissions() as $permission) { if (!isset($inheritedCodes[$permission->Code])) { $inheritedCodes[$permission->Code] = array(); } $inheritedCodes[$permission->Code][] = _t( 'PermissionCheckboxSetField.FromGroup', 'inherited from group "{title}"', 'A permission inherited from a certain group', array('title' => $parent->dbObject('Title')->forTemplate()) ); } } } } } } $odd = 0; $options = ''; $globalHidden = (array)Config::inst()->get('Permission', 'hidden_permissions'); if($this->source) { $privilegedPermissions = Permission::config()->privileged_permissions; // loop through all available categorized permissions and see if they're assigned for the given groups foreach($this->source as $categoryName => $permissions) { $options .= "
  • $categoryName
  • "; foreach($permissions as $code => $permission) { if(in_array($code, $this->hiddenPermissions)) continue; if(in_array($code, $globalHidden)) continue; $value = $permission['name']; $odd = ($odd + 1) % 2; $extraClass = $odd ? 'odd' : 'even'; $extraClass .= ' val' . str_replace(' ', '', $code); $itemID = $this->id() . '_' . preg_replace('/[^a-zA-Z0-9]+/', '', $code); $checked = $disabled = $inheritMessage = ''; $checked = (isset($uninheritedCodes[$code]) || isset($inheritedCodes[$code])) ? ' checked="checked"' : ''; $title = $permission['help'] ? 'title="' . htmlentities($permission['help'], ENT_COMPAT, 'UTF-8') . '" ' : ''; if (isset($inheritedCodes[$code])) { // disable inherited codes, as any saving logic would be too complicate to express in this // interface $disabled = ' disabled="true"'; $inheritMessage = ' (' . join(', ', $inheritedCodes[$code]) . ')'; } elseif($this->records && $this->records->Count() > 1 && isset($uninheritedCodes[$code])) { // If code assignments are collected from more than one "source group", // show its origin automatically $inheritMessage = ' (' . join(', ', $uninheritedCodes[$code]).')'; } // Disallow modification of "privileged" permissions unless currently logged-in user is an admin if(!Permission::check('ADMIN') && in_array($code, $privilegedPermissions)) { $disabled = ' disabled="true"'; } // If the field is readonly, always mark as "disabled" if($this->readonly) $disabled = ' disabled="true"'; $inheritMessage = '' . $inheritMessage . ''; $icon = ($checked) ? 'accept' : 'decline'; // If the field is readonly, add a span that will replace the disabled checkbox input if($this->readonly) { $options .= "
  • " . "name[$code]\" type=\"checkbox\"" . " value=\"$code\"$checked class=\"checkbox\" />" . "" . "
  • \n"; } else { $options .= "
  • " . "name[$code]\" type=\"checkbox\"" . " value=\"$code\"$checked class=\"checkbox\" />" . "" . "
  • \n"; } } } } if($this->readonly) { return DBField::create_field('HTMLText', "\n" ); } else { return DBField::create_field('HTMLText', "\n" ); } } /** * Update the permission set associated with $record DataObject * * @param DataObject $record */ public function saveInto(DataObjectInterface $record) { $fieldname = $this->name; $managedClass = $this->managedClass; // Remove all "privileged" permissions if the currently logged-in user is not an admin $privilegedPermissions = Permission::config()->privileged_permissions; if(!Permission::check('ADMIN')) { foreach($this->value as $id => $bool) { if(in_array($id, $privilegedPermissions)) { unset($this->value[$id]); } } } // remove all permissions and re-add them afterwards $permissions = $record->$fieldname(); foreach ( $permissions as $permission ) { $permission->delete(); } if($fieldname && $record && ($record->hasManyComponent($fieldname) || $record->manyManyComponent($fieldname))) { if(!$record->ID) $record->write(); // We need a record ID to write permissions $idList = array(); if($this->value) foreach($this->value as $id => $bool) { if($bool) { $perm = new $managedClass(); $perm->{$this->filterField} = $record->ID; $perm->Code = $id; $perm->write(); } } } } /** * @return PermissionCheckboxSetField_Readonly */ public function performReadonlyTransformation() { $readonly = new PermissionCheckboxSetField_Readonly( $this->name, $this->title, $this->managedClass, $this->filterField, $this->records ); return $readonly; } /** * Retrieves all permission codes for the currently set records * * @return array */ public function getAssignedPermissionCodes() { if(!$this->records) return false; // TODO return $codes; } } /** * Readonly version of a {@link PermissionCheckboxSetField} - * uses the same structure, but has all checkboxes disabled. * * @package framework * @subpackage security */ class PermissionCheckboxSetField_Readonly extends PermissionCheckboxSetField { protected $readonly = true; public function saveInto(DataObjectInterface $record) { return false; } }