diff --git a/security/PermissionCheckboxSetField.php b/security/PermissionCheckboxSetField.php index 428ecfdd4..f094eba71 100644 --- a/security/PermissionCheckboxSetField.php +++ b/security/PermissionCheckboxSetField.php @@ -5,6 +5,8 @@ * (either directly, inherited from parent groups, or through a {@link PermissionRole}) * will be checked automatically. All checkboxes for "inherited" permissions will be readonly. * + * The field can gets its assignment data either from {@link Group} or {@link PermissionRole} records. + * * @package sapphire * @subpackage security */ @@ -19,7 +21,7 @@ class PermissionCheckboxSetField extends FormField { /** * @var DataObjectSet */ - protected $groups = null; + protected $records = null; /** * @var array Array Nested array in same notation as {@link CheckboxSetField}. @@ -31,20 +33,21 @@ class PermissionCheckboxSetField extends FormField { * @param String $title * @param String $managedClass * @param String $filterField - * @param Group|DataObjectSet $groups One or more {@link Group} records used to determine permission checkboxes. - * Caution: saveInto() can only be used with a single group, all inherited permissions will be marked readonly. + * @param Group|DataObjectSet $records One or more {@link Group} or {@link PermissionRole} records + * used to determine permission checkboxes. + * Caution: saveInto() can only be used with a single record, all inherited permissions will be marked readonly. * Setting multiple groups only makes sense in a readonly context. (Optional) */ - function __construct($name, $title, $managedClass, $filterField, $groups = null) { + function __construct($name, $title, $managedClass, $filterField, $records = null) { $this->filterField = $filterField; $this->managedClass = $managedClass; - if(is_a($groups, 'DataObjectSet')) { - $this->groups = $groups; - } elseif(is_a($groups, 'Group')) { - $this->groups = new DataObjectSet($groups); - } elseif($groups) { - throw new InvalidArgumentException('$group should be either a Group record, or a DataObjectSet of Group records'); + if(is_a($records, 'DataObjectSet')) { + $this->records = $records; + } elseif(is_a($records, 'DataObject')) { + $this->records = new DataObjectSet($records); + } elseif($records) { + throw new InvalidArgumentException('$record should be either a Group record, or a DataObjectSet of Group records'); } // Get all available codes in the system as a categorized nested array @@ -70,57 +73,64 @@ class PermissionCheckboxSetField extends FormField { function Field() { Requirements::css(SAPPHIRE_DIR . '/css/CheckboxSetField.css'); - // Get existing values from the form record (assuming the formfield name is a join field on the record) $uninheritedCodes = array(); + $inheritedCodes = array(); + $records = ($this->records) ? $this->records : new DataObjectSet(); + + // 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 && $record->hasMethod($this->name)) { - $funcName = $this->name; - $join = $record->$funcName(); - if($join) foreach($join as $joinItem) { - $uninheritedCodes[$joinItem->Code] = $joinItem->Code; - } + if($record && !$records->find('ID', $record->ID)) { + $records->push($record); } } // Get all 'inherited' codes not directly assigned to the group (which is stored in $values) - $inheritedCodes = array(); - if($this->groups) foreach($this->groups as $group) { + foreach($records as $record) { // Get all uninherited permissions - foreach($group->Permissions() as $permission) { - $uninheritedCodes[$permission->Code] = $permission->Code; - } - - // Get all permissions from roles - if ($group->Roles()->Count()) { - foreach($group->Roles() as $role) { - foreach($role->Codes() as $code) { - if (!isset($inheritedCodes[$code->Code])) $inheritedCodes[$code->Code] = array(); - // TODO i18n - $inheritedCodes[$code->Code][] = 'from role '.$role->Title; - } - } + $relationMethod = $this->name; + foreach($record->$relationMethod() as $permission) { + if(!isset($uninheritedCodes[$permission->Code])) $uninheritedCodes[$permission->Code] = array(); + $uninheritedCodes[$permission->Code][] = sprintf( + _t('PermissionCheckboxSetField.AssignedTo', 'assigned to "%s"'), + $record->Title + ); } - // Get from parent groups - $parentGroups = $group->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(); - // TODO i18n - $inheritedCodes[$code->Code][] = 'role '.$role->Title.' on group '.$parent->Title; - } + // 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(); + // TODO i18n + $inheritedCodes[$code->Code][] = 'from role "'.$role->Title . '"'; } } - if ($parent->Permissions()->Count()) { - foreach($parent->Permissions() as $permission) { - if (!isset($inheritedCodes[$permission->Code])) $inheritedCodes[$permission->Code] = array(); - // TODO i18n - $inheritedCodes[$permission->Code][] = 'group '.$parent->Title; + } + + // 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(); + // TODO i18n + $inheritedCodes[$code->Code][] = 'role "'.$role->Title.'" on group "'.$parent->Title . '"'; + } + } + } + if ($parent->Permissions()->Count()) { + foreach($parent->Permissions() as $permission) { + if (!isset($inheritedCodes[$permission->Code])) $inheritedCodes[$permission->Code] = array(); + // TODO i18n + $inheritedCodes[$permission->Code][] = 'group "'.$parent->Title . '"'; + } } } } @@ -143,19 +153,28 @@ class PermissionCheckboxSetField extends FormField { $extraClass .= ' val' . str_replace(' ', '', $code); $itemID = $this->id() . '_' . ereg_replace('[^a-zA-Z0-9]+', '', $code); $checked = $disabled = $inheritMessage = ''; - $checked = in_array($code, $uninheritedCodes) ? ' checked="checked"' : ''; + $checked = (isset($uninheritedCodes[$code]) || isset($inheritedCodes[$code])) ? ' checked="checked"' : ''; $title = $permission['help'] ? 'title="' . htmlentities($permission['help']) . '" ' : ''; if (isset($inheritedCodes[$code])) { // disable inherited codes, as any saving logic would be too complicate to express in this interface $disabled = ' disabled="true"'; // TODO i18n - $inheritMessage = ' inherited from ' . join(', ', $inheritedCodes[$code]) . ''; - $options .= "
  • \n"; - } else { - // uninherited (and hence editable) code checkbox - $options .= "
  • name[$code]\" type=\"checkbox\" value=\"$code\"$checked class=\"checkbox\" />
  • \n"; + $inheritMessage = ' (inherited from ' . 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]).')'; } + + // If the field is readonly, always mark as "disabled" + if($this->readonly) $disabled = ' disabled="true"'; + + $inheritMessage = '' . $inheritMessage . ''; + $options .= "
  • " . + "name[$code]\" type=\"checkbox\" value=\"$code\"$checked class=\"checkbox\" />" . + "" . + "
  • \n"; } } } @@ -190,4 +209,48 @@ class PermissionCheckboxSetField extends FormField { } } } + + /** + * @return PermissionCheckboxSetField_Readonly + */ + 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 + */ + 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 sapphire + * @subpackage security + */ +class PermissionCheckboxSetField_Readonly extends PermissionCheckboxSetField { + + protected $readonly = true; + + function saveInto($record) { + return false; + } } \ No newline at end of file