/**
  * Returns a data class that is a DataObject type that this GridField should look like.
  *
  * @return string
  *
  * @throws LogicException
  */
 public function getModelClass()
 {
     if ($this->modelClassName) {
         return $this->modelClassName;
     }
     if ($this->list && $this->list->hasMethod('dataClass')) {
         $class = $this->list->dataClass();
         if ($class) {
             return $class;
         }
     }
     throw new LogicException('GridField doesn\'t have a modelClassName, so it doesn\'t know the columns of this grid.');
 }
 /**
  * If an object ID is set, add the object to the list
  *
  * @param GridField $gridField
  * @param SS_List $dataList
  * @return SS_List
  */
 public function getManipulatedData(GridField $gridField, SS_List $dataList)
 {
     $objectID = $gridField->State->GridFieldAddRelation(null);
     if (empty($objectID)) {
         return $dataList;
     }
     $object = DataObject::get_by_id($gridField->getModelClass(), $objectID);
     if ($object) {
         $dataList->add($object);
     }
     $gridField->State->GridFieldAddRelation = null;
     return $dataList;
 }
 /**
  * Loads the related record values into this field. UploadField can be uploaded
  * in one of three ways:
  *
  *  - By passing in a list of file IDs in the $value parameter (an array with a single
  *    key 'Files', with the value being the actual array of IDs).
  *  - By passing in an explicit list of File objects in the $record parameter, and
  *    leaving $value blank.
  *  - By passing in a dataobject in the $record parameter, from which file objects
  *    will be extracting using the field name as the relation field.
  *
  * Each of these methods will update both the items (list of File objects) and the
  * field value (list of file ID values).
  *
  * @param array $value Array of submitted form data, if submitting from a form
  * @param array|DataObject|SS_List $record Full source record, either as a DataObject,
  * SS_List of items, or an array of submitted form data
  * @return $this Self reference
  * @throws ValidationException
  */
 public function setValue($value, $record = null)
 {
     // If we're not passed a value directly, we can attempt to infer the field
     // value from the second parameter by inspecting its relations
     $items = new ArrayList();
     // Determine format of presented data
     if (empty($value) && $record) {
         // If a record is given as a second parameter, but no submitted values,
         // then we should inspect this instead for the form values
         if ($record instanceof DataObject && $record->hasMethod($this->getName())) {
             // If given a dataobject use reflection to extract details
             $data = $record->{$this->getName()}();
             if ($data instanceof DataObject) {
                 // If has_one, add sole item to default list
                 $items->push($data);
             } elseif ($data instanceof SS_List) {
                 // For many_many and has_many relations we can use the relation list directly
                 $items = $data;
             }
         } elseif ($record instanceof SS_List) {
             // If directly passing a list then save the items directly
             $items = $record;
         }
     } elseif (!empty($value['Files'])) {
         // If value is given as an array (such as a posted form), extract File IDs from this
         $class = $this->getRelationAutosetClass();
         $items = DataObject::get($class)->byIDs($value['Files']);
     }
     // If javascript is disabled, direct file upload (non-html5 style) can
     // trigger a single or multiple file submission. Note that this may be
     // included in addition to re-submitted File IDs as above, so these
     // should be added to the list instead of operated on independently.
     if ($uploadedFiles = $this->extractUploadedFileData($value)) {
         foreach ($uploadedFiles as $tempFile) {
             $file = $this->saveTemporaryFile($tempFile, $error);
             if ($file) {
                 $items->add($file);
             } else {
                 throw new ValidationException($error);
             }
         }
     }
     // Filter items by what's allowed to be viewed
     $filteredItems = new ArrayList();
     $fileIDs = array();
     foreach ($items as $file) {
         if ($file->exists() && $file->canView()) {
             $filteredItems->push($file);
             $fileIDs[] = $file->ID;
         }
     }
     // Filter and cache updated item list
     $this->items = $filteredItems;
     // Same format as posted form values for this field. Also ensures that
     // $this->setValue($this->getValue()); is non-destructive
     $value = $fileIDs ? array('Files' => $fileIDs) : null;
     // Set value using parent
     parent::setValue($value, $record);
     return $this;
 }
 /**
  * @param array $properties
  * @return string
  */
 public function Field($properties = array())
 {
     $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 && ($record instanceof Group || $record instanceof 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 ($record instanceof Group) {
             // Get all permissions from roles
             if ($record->Roles()->count()) {
                 foreach ($record->Roles() as $role) {
                     /** @var PermissionRole $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('SilverStripe\\Security\\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 .= "<li><h5>{$categoryName}</h5></li>";
             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);
                 $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 = '<small>' . $inheritMessage . '</small>';
                 $icon = $checked ? 'accept' : 'decline';
                 // If the field is readonly, add a span that will replace the disabled checkbox input
                 if ($this->readonly) {
                     $options .= "<li class=\"{$extraClass}\">" . "<input id=\"{$itemID}\"{$disabled} name=\"{$this->name}[{$code}]\" type=\"checkbox\"" . " value=\"{$code}\"{$checked} class=\"checkbox\" />" . "<label {$title}for=\"{$itemID}\">" . "<span class=\"ui-button-icon-primary ui-icon btn-icon-{$icon}\"></span>" . "{$value}{$inheritMessage}</label>" . "</li>\n";
                 } else {
                     $options .= "<li class=\"{$extraClass}\">" . "<input id=\"{$itemID}\"{$disabled} name=\"{$this->name}[{$code}]\" type=\"checkbox\"" . " value=\"{$code}\"{$checked} class=\"checkbox\" />" . "<label {$title}for=\"{$itemID}\">{$value}{$inheritMessage}</label>" . "</li>\n";
                 }
             }
         }
     }
     if ($this->readonly) {
         return "<ul id=\"{$this->ID()}\" class=\"optionset checkboxsetfield{$this->extraClass()}\">\n" . "<li class=\"help\">" . _t('Permissions.UserPermissionsIntro', 'Assigning groups to this user will adjust the permissions they have.' . ' See the groups section for details of permissions on individual groups.') . "</li>" . $options . "</ul>\n";
     } else {
         return "<ul id=\"{$this->ID()}\" class=\"optionset checkboxsetfield{$this->extraClass()}\">\n" . $options . "</ul>\n";
     }
 }
 public function debug()
 {
     return $this->list->debug();
 }
 /**
  * Get the list of extra data from the $record as saved into it by
  * {@see Form::saveInto()}
  *
  * Handles detection of falsey values explicitly saved into the
  * DataObject by formfields
  *
  * @param DataObject $record
  * @param SS_List $list
  * @return array List of data to write to the relation
  */
 protected function getExtraSavedData($record, $list)
 {
     // Skip extra data if not ManyManyList
     if (!$list instanceof ManyManyList) {
         return null;
     }
     $data = array();
     foreach ($list->getExtraFields() as $field => $dbSpec) {
         $savedField = "ManyMany[{$field}]";
         if ($record->hasField($savedField)) {
             $data[$field] = $record->getField($savedField);
         }
     }
     return $data;
 }