Merge branch 'master' of git://github.com/silverstripe/silverstripe-userforms into issue69

This commit is contained in:
Kirk Mayo 2014-01-06 13:37:55 +13:00
commit 41b6e57e4c
7 changed files with 281 additions and 41 deletions

View File

@ -8,37 +8,51 @@
class FieldEditor extends FormField { class FieldEditor extends FormField {
private static $url_handlers = array(
'$Action!/$ID' => '$Action'
);
private static $allowed_actions = array( private static $allowed_actions = array(
'addfield', 'addfield',
'addoptionfield' 'addoptionfield',
'handleField'
); );
/** /**
* Field Editor Template * @param array $properties
* *
* @return String * @return HTML
*/ */
public function FieldHolder($properties = array()) { public function FieldHolder($properties = array()) {
$this->setAttribute('data-add-url', '\''.Controller::join_links($this->Link('addfield')).'\''); $add = Controller::join_links($this->Link('addfield'));
$this->setAttribute('data-add-url', '\''. $add.'\'');
return $this->renderWith("FieldEditor"); return $this->renderWith("FieldEditor");
} }
/** /**
* Returns whether a user can edit the form * Returns whether a user can edit the form.
*
* @param Member $member
* *
* @return boolean * @return boolean
*/ */
public function canEdit($member = null) { public function canEdit($member = null) {
if($this->readonly) return false; if($this->readonly) {
return false;
}
return $this->form->getRecord()->canEdit(); return $this->form->getRecord()->canEdit();
} }
/** /**
* Returns whether a user delete a field in the form. The {@link EditableFormField}s * Returns whether a user delete a field in the form. The
* check if they can delete themselves but this counts as an {@link self::canEdit()} * {@link EditableFormField} instances check if they can delete themselves
* function rather than a delete * but this counts as an {@link self::canEdit()} function rather than a
* delete.
* *
* @param Member $member
* @return boolean * @return boolean
*/ */
public function canDelete($member = null) { public function canDelete($member = null) {
@ -56,20 +70,24 @@ class FieldEditor extends FormField {
$clone = clone $this; $clone = clone $this;
$clone->readonly = true; $clone->readonly = true;
$fields = $clone->Fields(); $fields = $clone->Fields();
if($fields) foreach($fields as $field) {
$field->setReadonly(); if($fields) {
foreach($fields as $field) {
$field->setReadonly();
}
} }
return $clone->customise(array('Fields' => $fields)); return $clone->customise(array(
'Fields' => $fields
));
} }
/** /**
* Return the fields for the user forms * Return the fields.
* *
* @return DataObjectSet * @return RelationList
*/ */
public function Fields() { public function Fields() {
// Don't return any fields unless we actually have the dependent parameters set on the form field
if($this->form && $this->form->getRecord() && $this->name) { if($this->form && $this->form->getRecord() && $this->name) {
$relationName = $this->name; $relationName = $this->name;
$fields = $this->form->getRecord()->getComponents($relationName); $fields = $this->form->getRecord()->getComponents($relationName);
@ -88,10 +106,10 @@ class FieldEditor extends FormField {
} }
/** /**
* Return a DataObjectSet of all the addable fields to populate * Return a {@link ArrayList} of all the addable fields to populate the add
* the add field menu * field menu.
* *
* @return DataObjectSet * @return ArrayList
*/ */
public function CreatableFields() { public function CreatableFields() {
$fields = ClassInfo::subclassesFor('EditableFormField'); $fields = ClassInfo::subclassesFor('EditableFormField');
@ -99,13 +117,16 @@ class FieldEditor extends FormField {
if($fields) { if($fields) {
array_shift($fields); // get rid of subclass 0 array_shift($fields); // get rid of subclass 0
asort($fields); // get in order asort($fields); // get in order
$output = new ArrayList(); $output = new ArrayList();
foreach($fields as $field => $title) { foreach($fields as $field => $title) {
// get the nice title and strip out field // get the nice title and strip out field
$niceTitle = _t( $niceTitle = _t(
$field.'.SINGULARNAME', $field.'.SINGULARNAME',
$title $title
); );
if($niceTitle) { if($niceTitle) {
$output->push(new ArrayData(array( $output->push(new ArrayData(array(
'ClassName' => $field, 'ClassName' => $field,
@ -113,16 +134,18 @@ class FieldEditor extends FormField {
))); )));
} }
} }
return $output; return $output;
} }
return false; return false;
} }
/** /**
* Handles saving the page. Needs to keep an eye on fields * Handles saving the page. Needs to keep an eye on fields and options which
* and options which have been removed / added * have been removed / added
* *
* @param DataObject Record to Save it In * @param DataObject $record
*/ */
public function saveInto(DataObjectInterface $record) { public function saveInto(DataObjectInterface $record) {
$name = $this->name; $name = $this->name;
@ -166,15 +189,15 @@ class FieldEditor extends FormField {
if($this->canEdit()) { if($this->canEdit()) {
foreach($missingFields as $removedField) { foreach($missingFields as $removedField) {
if(is_numeric($removedField->ID)) { if(is_numeric($removedField->ID)) {
// check we can edit this // check we can edit this
$removedField->delete(); $removedField->delete();
} }
} }
} }
} }
/** /**
* Add a field to the field editor. Called via a ajax get request from the userdefinedform javascript * Add a field to the field editor. Called via a ajax get.
* *
* @return bool|html * @return bool|html
*/ */
@ -257,4 +280,46 @@ class FieldEditor extends FormField {
return false; return false;
} }
/**
* Pass sub {@link FormField} requests through the editor. For example,
* option fields need to be able to call themselves.
*
* @param SS_HTTPRequest
*/
public function handleField(SS_HTTPRequest $request) {
if(!SecurityToken::inst()->checkRequest($this->request)) {
return $this->httpError(400);
}
$fields = $this->Fields();
// extract the ID and option field name
preg_match(
'/Fields\[(?P<ID>\d+)\]\[CustomSettings\]\[(?P<Option>\w+)\]/',
$request->param('ID'), $matches
);
if(isset($matches['ID']) && isset($matches['Option'])) {
foreach($fields as $field) {
$formField = $field->getFormField();
if($matches['ID'] == $field->ID) {
if($field->canEdit()) {
// find the option to handle
$options = $field->getFieldConfiguration();
$optionField = $options->fieldByName($request->param('ID'));
if($optionField) {
return $optionField->handleRequest($request, $optionField);
} else {
return $this->httpError(404);
}
}
}
}
}
return $this->httpError(403);
}
} }

View File

@ -0,0 +1,20 @@
<?php
/**
* {@link TreeDropdownField} subclass for handling loading folders through the
* nested {@link FormField} instances of the {@link FieldEditor}
*
* @package userforms
*/
class UserformsTreeDropdownField extends TreeDropdownField {
public function Link($action = null) {
$form = Controller::curr()->EditForm;
return Controller::join_links(
$form->FormAction(), 'field/Fields/handleField/' . $this->name,
$action .
'?SecurityID='. $form->getSecurityToken()->getValue()
);
}
}

View File

@ -939,13 +939,14 @@ JS
if(!empty($data[$field->Name])){ if(!empty($data[$field->Name])){
if(in_array("EditableFileField", $field->getClassAncestry())) { if(in_array("EditableFileField", $field->getClassAncestry())) {
if(isset($_FILES[$field->Name])) { if(isset($_FILES[$field->Name])) {
$foldername = $field->getFormField()->getFolderName();
// create the file from post data // create the file from post data
$upload = new Upload(); $upload = new Upload();
$file = new File(); $file = new File();
$file->ShowInSearch = 0; $file->ShowInSearch = 0;
try { try {
$upload->loadIntoFile($_FILES[$field->Name], $file); $upload->loadIntoFile($_FILES[$field->Name], $file, $foldername);
} catch( ValidationException $e ) { } catch( ValidationException $e ) {
$validationResult = $e->getResult(); $validationResult = $e->getResult();
$form->addErrorMessage($field->Name, $validationResult->message(), 'bad'); $form->addErrorMessage($field->Name, $validationResult->message(), 'bad');

View File

@ -12,9 +12,36 @@ class EditableFileField extends EditableFormField {
private static $plural_names = 'File Fields'; private static $plural_names = 'File Fields';
public function getFieldConfiguration() {
$field = parent::getFieldConfiguration();
$folder = ($this->getSetting('Folder')) ? $this->getSetting('Folder') : null;
$tree = UserformsTreeDropdownField::create(
$this->getSettingName("Folder"),
_t('EditableUploadField.SELECTUPLOADFOLDER', 'Select upload folder'),
"Folder"
);
$tree->setValue($folder);
$field->push($tree);
return $field;
}
public function getFormField() { public function getFormField() {
$field = new FileField($this->Name, $this->Title); $field = new FileField($this->Name, $this->Title);
if($this->getSetting('Folder')) {
$folder = Folder::get()->byId($this->getSetting('Folder'));
if($folder) {
$field->setFolderName(
preg_replace("/^assets\//","", $folder->Filename)
);
}
}
return $field; return $field;
} }

View File

@ -6,12 +6,12 @@
class EditableFormFieldTest extends FunctionalTest { class EditableFormFieldTest extends FunctionalTest {
static $fixture_file = 'userforms/tests/UserDefinedFormTest.yml'; static $fixture_file = 'userforms/tests/EditableFormFieldTest.yml';
protected $extraDataObjects = array( protected $extraDataObjects = array(
'ExtendedEditableFormField', 'ExtendedEditableFormField',
'EditableFormFieldExtension' 'EditableFormFieldExtension'
); );
function testFormFieldPermissions() { function testFormFieldPermissions() {
$text = $this->objFromFixture('EditableTextField', 'basic-text'); $text = $this->objFromFixture('EditableTextField', 'basic-text');

View File

@ -0,0 +1,135 @@
EditableOption:
option-1:
Name: Option1
Title: Option 1
option-2:
Name: Option2
Title: Option 2
department-1:
Name: dept1
Title: sales@example.com
department-2:
Name: dept2
Title: accounts@example.com
option-3:
Name: Option3
Title: Option 3
option-4:
Name: Option4
Title: Option 4
option-5:
Name: Option5
Title: Option 5
option-6:
Name: Option6
Title: Option 6
UserDefinedForm_EmailRecipient:
recipient-1:
EmailAddress: test@example.com
EmailSubject: Email Subject
EmailFrom: no-reply@example.com
no-html:
EmailAddress: nohtml@example.com
EmailSubject: Email Subject
EmailFrom: no-reply@example.com
SendPlain: true
no-data:
EmailAddress: nodata@example.com
EmailSubject: Email Subject
EmailFrom: no-reply@example.com
HideFormData: true
EditableTextField:
basic-text:
Name: basic-text-name
Title: Basic Text Field
basic-text-2:
Name: basic-text-name
Title: Basic Text Field
required-text:
Name: required-text-field
Title: Required Text Field
CustomErrorMessage: Custom Error Message
Required: true
EditableDropdown:
basic-dropdown:
Name: basic-dropdown
Title: Basic Dropdown Field
Options: =>EditableOption.option-1, =>EditableOption.option-2
department-dropdown:
Name: department
Title: Department
Options: =>EditableOption.department-1, =>EditableOption.department-2
EditableCheckbox:
checkbox-1:
Name: checkbox-1
Title: Checkbox 1
checkbox-2:
Name: checkbox-1
Title: Checkbox 1
EditableCheckboxGroupField:
checkbox-group:
Name: check-box-group
Title: Check box group
Options: =>EditableOption.option-3, =>EditableOption.option-4
EditableEmailField:
email-field:
Name: email-field
Title: Email
EditableRadioField:
radio-field:
Name: radio-option
Title: Radio Option
Options: =>EditableOption.option-5, =>EditableOption.option-6
ExtendedEditableFormField:
extended-field:
Name: extended-field
Title: Extended Field
TestExtraField: Extra Field
TestValidationField: Extra Validation Field
UserDefinedForm:
basic-form-page:
Title: User Defined Form
Fields: =>EditableTextField.basic-text
EmailRecipients: =>UserDefinedForm_EmailRecipient.recipient-1, =>UserDefinedForm_EmailRecipient.no-html, =>UserDefinedForm_EmailRecipient.no-data
form-with-reset-and-custom-action:
Title: Form with Reset Action
SubmitButtonText: Custom Button
ShowClearButton: true
validation-form:
Title: Validation Form
Fields: =>EditableTextField.required-text
custom-rules-form:
Title: Custom Rules Form
Fields: =>EditableCheckbox.checkbox-2, =>EditableTextField.basic-text-2
empty-form:
Title: Empty Form

View File

@ -103,14 +103,6 @@ EditableRadioField:
Options: =>EditableOption.option-5, =>EditableOption.option-6 Options: =>EditableOption.option-5, =>EditableOption.option-6
ExtendedEditableFormField:
extended-field:
Name: extended-field
Title: Extended Field
TestExtraField: Extra Field
TestValidationField: Extra Validation Field
UserDefinedForm: UserDefinedForm:
basic-form-page: basic-form-page:
Title: User Defined Form Title: User Defined Form