diff --git a/code/editor/EditableButton.php b/code/editor/EditableButton.php index b03d97d..ae796f7 100755 --- a/code/editor/EditableButton.php +++ b/code/editor/EditableButton.php @@ -1,9 +1,9 @@ readonly = true; - return $this->Option(); - } - - function isReadonly() { - return $this->readonly; - } - - static $has_many = array( - "Options" => "EditableCheckboxOption" - ); - static $singular_name = "Checkbox group"; + static $plural_name = "Checkbox groups"; - - function duplicate() { - $clonedNode = parent::duplicate(); - - foreach( $this->Options() as $field ) { - $newField = $field->duplicate(); - $newField->ParentID = $clonedNode->ID; - $newField->write(); - } - - return $clonedNode; - } - - function delete() { - $options = $this->Options(); - - foreach( $options as $option ) - $option->delete(); - - parent::delete(); - } - - function EditSegment() { - return $this->renderWith( $this->class ); - } - - function populateFromPostData( $data ) { - parent::populateFromPostData( $data ); - - $fieldSet = $this->Options(); - - $deletedOptions = explode( ',', $data['Deleted'] ); - $optionNumber = 0; - - // store default, etc - foreach($fieldSet as $option) { - if($deletedOptions && array_search($option->ID, $deletedOptions) !== false) { - $option->delete(); - continue; - } - - if(isset($data[$option->ID])) { - $option->populateFromPostData( $data[$option->ID] ); - } - - unset( $data[$option->ID] ); - } - - foreach($data as $tempID => $optionData) { - if(!$tempID || !is_array($optionData) || empty($optionData) || !preg_match('/^_?\d+$/', $tempID)) { - continue; - } - - // what will we name the new option? - $newOption = new EditableCheckboxOption(); - $newOption->Name = 'option' . (string)$optionNumber++; - $newOption->ParentID = $this->ID; - $newOption->populateFromPostData($optionData); - } - } - + function DefaultOption() { $defaultOption = 0; @@ -98,14 +25,6 @@ class EditableCheckboxGroupField extends EditableFormField { return -1; } - function getFormField() { - return $this->createField(); - } - - function getFilterField() { - return $this->createField( true ); - } - function createField($asFilter = false) { $optionSet = $this->Options(); $options = array(); @@ -127,7 +46,7 @@ class EditableCheckboxGroupField extends EditableFormField { $entries = array($data[$this->Name]); } - $selectedOptions = DataObject::get('EditableCheckboxOption', "ParentID={$this->ID} AND ID IN (" . implode(',', $entries) . ")"); + $selectedOptions = DataObject::get('EditableOption', "ParentID={$this->ID} AND ID IN (" . implode(',', $entries) . ")"); foreach($selectedOptions as $selected) { if(!$result) { $result = $selected->Title; @@ -138,11 +57,6 @@ class EditableCheckboxGroupField extends EditableFormField { return $result; } - - function TemplateOption() { - $option = new EditableCheckboxOption(); - return $option->EditSegment(); - } } ?> \ No newline at end of file diff --git a/code/editor/EditableCheckboxOption.php b/code/editor/EditableCheckboxOption.php deleted file mode 100755 index 24a4f3f..0000000 --- a/code/editor/EditableCheckboxOption.php +++ /dev/null @@ -1,82 +0,0 @@ - "Varchar", - "Title" => "Varchar", - "Default" => "Boolean", - "Sort" => "Int" - ); - static $has_one = array( - "Parent" => "EditableCheckboxGroupField", - ); - - static $singular_name = "Checkbox option"; - static $plural_name = "Checkbox options"; - - function EditSegment() { - return $this->renderWith('EditableFormFieldOption'); - } - - function TitleField() { - return new TextField( "Fields[{$this->ParentID}][{$this->ID}][Title]", null, $this->Title ); - } - - function Name() { - return "Fields[{$this->ParentID}][{$this->ID}]"; - } - - function populateFromPostData($data) { - $this->Title = $data['Title']; - if(isset($data['Default'])) { - $this->setField('Default', $data['Default']); - } - $this->Sort = $data['Sort']; - $this->write(); - } - - function Option() { - // return new radio field - /*$title = Convert::raw2att( $this->Title ); - - $default = ""; - - if( $this->getField('Default') ) - $default = '+'; - else - $default = '-'; - - //Debug::show($this); - return '';*/ - - return $this->EditSegment(); - } - - function ReadonlyOption() { - $this->readonly = true; - return $this->EditSegment(); - } - - function DefaultSelect() { - if( $this->readonly ) - $disabled = " disabled=\"disabled\""; - else - $disabled = ''; - - if( $this->getField('Default') ) - $default = " checked=\"checked\""; - else - $default = ''; - - return "ParentID}][{$this->ID}][Default]\" value=\"1\"".$disabled.$default." />"; - } -} -?> \ No newline at end of file diff --git a/code/editor/EditableDateField.php b/code/editor/EditableDateField.php index ae21e3a..b982fe1 100755 --- a/code/editor/EditableDateField.php +++ b/code/editor/EditableDateField.php @@ -1,9 +1,10 @@ "EditableDropdownOption" - ); +class EditableDropdown extends EditableMultipleOptionField { static $singular_name = 'Dropdown'; + static $plural_name = 'Dropdowns'; - - function delete() { - $options = $this->Options(); - foreach( $options as $option ) - $option->delete(); - - parent::delete(); - } - function EditSegment() { - return $this->renderWith( $this->class ); - } - - function populateFromPostData( $data ) { - - parent::populateFromPostData( $data ); - - $fieldSet = $this->Options(); - - $deletedOptions = explode( ',', $data['Deleted'] ); - - // store default, etc - foreach( $fieldSet as $option ) { - - if( $deletedOptions && array_search( $option->ID, $deletedOptions ) !== false ) { - $option->delete(); - continue; - } - - if(isset($option) && isset($data[$option->ID])) { - $option->setField('Default', isset($data['Default']) ? ($option->ID == $data['Default']) : false); - $option->populateFromPostData( $data[$option->ID] ); - } - - unset( $data[$option->ID] ); - } - - $optionNumber = 0; - foreach( $data as $tempID => $optionData ) { - - if( !$tempID || !is_array( $optionData ) || empty( $optionData ) || !preg_match('/^_?\d+$/', $tempID ) ) - continue; - - // what will we name the new option? - $newOption = new EditableDropdownOption(); - $newOption->Name = 'option' . (string)$optionNumber++; - $newOption->ParentID = $this->ID; - if(isset($data['Default'])) { - $newOption->setField('Default', $tempID == $data['Default']); - } - - if( Director::is_ajax() ) { - $fieldID = $this->ID; - $fieldEditorName = $this->editor ? $this->editor->Name() : 'Fields'; - $prefix = $fieldEditorName . '[' . $fieldID . ']'; - $newID = $newOption->ID; - $newSort = $newOption->Sort; - echo "\$('". $fieldEditorName . "[$fieldID]').updateOption('$prefix','$tempID','$newID','$newSort');"; - } - - if( !$optionData['Sort'] ) { - - } - - $newOption->populateFromPostData( $optionData ); - } - } - - function getFormField() { - return $this->createField(); - } - - function getFilterField() { - return $this->createField( true ); - } - - function createField( $asFilter = false ) { + function createField($asFilter = false) { $optionSet = $this->Options(); $options = array(); - if( $asFilter ) + if($asFilter) { $options['-1'] = "(Any)"; - + } $defaultOption = '-1'; foreach( $optionSet as $option ) { $options[$option->Title] = $option->Title; - if( $option->getField('Default') && !$asFilter ) $defaultOption = $option->Title; + if($option->getField('Default') && !$asFilter) $defaultOption = $option->Title; } return new DropdownField( $this->Name, $this->Title, $options, $defaultOption ); } - - function TemplateOption() { - $option = new EditableDropdownOption(); - return $option->EditSegment(); - } - - function duplicate() { - $clonedNode = parent::duplicate(); - - foreach( $this->Options() as $field ) { - $newField = $field->duplicate(); - $newField->ParentID = $clonedNode->ID; - $newField->write(); - } - - return $clonedNode; - } + } ?> diff --git a/code/editor/EditableDropdownOption.php b/code/editor/EditableDropdownOption.php deleted file mode 100755 index 7d1bfd0..0000000 --- a/code/editor/EditableDropdownOption.php +++ /dev/null @@ -1,77 +0,0 @@ -readonly = true; - return $this->EditSegment(); - } - - function isReadonly() { - return $this->readonly; - } - - static $default_sort = "Sort"; - - // add required here? - static $db = array( - "Name" => "Varchar", - "Title" => "Varchar", - "Default" => "Boolean", - "Sort" => "Int" - ); - static $has_one = array( - "Parent" => "EditableDropdown", - ); - - static $singular_name = 'Dropdown option'; - static $plural_name = 'Dropdown options'; - - function EditSegment() { - return $this->renderWith('EditableFormFieldOption'); - } - - function TitleField() { - return new TextField( "Fields[{$this->ParentID}][{$this->ID}][Title]", null, $this->Title ); - } - - function Name() { - return "Fields[{$this->ParentID}][{$this->ID}]"; - } - - function populateFromPostData( $data ) { - $this->Title = $data['Title']; - $this->Sort = $data['Sort']; - $this->write(); - } - - function Option() { - // return new radio field - /*$title = $this->Title; - - $default = ""; - - if( $this->getField('Default') ) - $default = 'class="default"'; - - //Debug::show($this); - return '';*/ - - return $this->EditSegment(); - } - - function DefaultSelect() { - $disabled = ($this->readonly) ? " disabled=\"disabled\"" : ''; - - $default = ($this->Parent()->getField('Default') == $this->ID) ? " checked=\"checked\"" : ""; - - return "ParentID}][Default]\" value=\"{$this->ID}\"".$disabled.$default." />"; - } -} -?> diff --git a/code/editor/EditableEmailField.php b/code/editor/EditableEmailField.php index 7c247de..cf96398 100755 --- a/code/editor/EditableEmailField.php +++ b/code/editor/EditableEmailField.php @@ -1,9 +1,10 @@ "Int", "Required" => "Boolean", "CanDelete" => "Boolean", - "CustomParameter" => "Varchar" + "CustomParameter" => "Varchar", + "OptionallyDisplay" => "Boolean" ); static $defaults = array( @@ -26,19 +28,36 @@ class EditableFormField extends DataObject { "Parent" => "SiteTree", ); + /** + * @var bool Is this field readonly to the user + */ protected $readonly; + /** + * @var FieldEditor The current editor + */ protected $editor; - - function setEditor( $editor ) { - $this->editor = $editor; - } - - function __construct( $record = null, $isSingleton = false ) { + + /** + * Construct a new EditableFormField Object. + * + * @param array|null $record This will be null for a new database record. + * @param boolean $isSingleton This this to true if this is a singleton() object, a stub for calling methods. + */ + public function __construct($record = null, $isSingleton = false) { $this->setField('Default', -1); parent::__construct( $record, $isSingleton ); } + /** + * Set the FieldEditor object for this field. + * + * @param FieldEditor The Editor window you wish to use + */ + protected function setEditor($editor) { + $this->editor = $editor; + } + function EditSegment() { return $this->renderWith('EditableFormField'); } @@ -51,6 +70,26 @@ class EditableFormField extends DataObject { return $this->class; } + /** + * Return whether or not this field has addable options + * such as a dropdown field or radio set + * + * @return bool + */ + public function hasAddableOptions() { + return false; + } + + /** + * Return whether or not this field needs to show the extra + * options dropdown list + * + * @return bool + */ + public function showExtraOptions() { + return true; + } + function makeReadonly() { $this->readonly = true; return $this; @@ -62,15 +101,8 @@ class EditableFormField extends DataObject { } function TitleField() { - // return new TextField( "Fields[".$this->ID."][Title]", null, $this->Title ); $titleAttr = Convert::raw2att($this->Title); - $readOnlyAttr = ''; - - if( $this->readonly ) { - $readOnlyAttr = ' disabled="disabled"'; - } else { - $readOnlyAttr = ''; - } + $readOnlyAttr = ($this->readonly) ? ' disabled="disabled"' : ''; return "ID}][Title]\"$readOnlyAttr />"; } @@ -79,25 +111,23 @@ class EditableFormField extends DataObject { return "Fields[".$this->ID."]"; } - /*function getName() { - return "field" . $this->ID; - }*/ - - function populateFromPostData( $data ) { - - $this->Title = isset($data['Title']) ? $data['Title']: ""; - - if(isset($data['Default'])) { - $this->setField('Default', $data['Default']); - } - + /** + * How to save the data submitted in this field into + * the database object which this field represents. + * + * Any class's which call this should also call + * {@link parent::populateFromPostData()} to ensure + * that this method is called + * + * @access public + */ + public function populateFromPostData($data) { + $this->Title = (isset($data['Title'])) ? $data['Title']: ""; + $this->Default = (isset($data['Default'])) ? $data['Default'] : ""; $this->Sort = isset($data['Sort']) ? $data['Sort'] : null; $this->CustomParameter = isset($data['CustomParameter']) ? $data['CustomParameter'] : null; $this->Required = !empty($data['Required']) ? 1 : 0; $this->CanDelete = (isset($data['CanDelete']) && !$data['CanDelete']) ? 0 : 1; - $this->write(); - - // The field must be written to ensure a unique ID. $this->Name = $this->class.$this->ID; $this->write(); } @@ -107,22 +137,28 @@ class EditableFormField extends DataObject { $baseName = "Fields[$this->ID]"; $extraOptions = new FieldSet(); - if( !$this->Parent()->hasMethod( 'hideExtraOption' ) ){ - $extraOptions->push( new CheckboxField($baseName . "[Required]", _t('EditableFormField.REQUIRED', 'Required?'), $this->Required) ); - }elseif( !$this->Parent()->hideExtraOption( 'Required' ) ){ - $extraOptions->push( new CheckboxField($baseName . "[Required]", _t('EditableFormField.REQUIRED', 'Required?'), $this->Required) ); + if(!$this->Parent()->hasMethod('hideExtraOption')){ + $extraOptions->push(new CheckboxField($baseName . "[Required]", _t('EditableFormField.REQUIRED', 'Required?'), $this->Required)); + } + elseif(!$this->Parent()->hideExtraOption('Required')){ + $extraOptions->push(new CheckboxField($baseName . "[Required]", _t('EditableFormField.REQUIRED', 'Required?'), $this->Required)); } - if( $this->Parent()->hasMethod( 'getExtraOptionsForField' ) ) { - $extraFields = $this->Parent()->getExtraOptionsForField( $this ); + if($this->Parent()->hasMethod('getExtraOptionsForField')) { + $extraFields = $this->Parent()->getExtraOptionsForField($this); - foreach( $extraFields as $extraField ) - $extraOptions->push( $extraField ); + foreach($extraFields as $extraField) { + $extraOptions->push($extraField); + } } - if( $this->readonly ) + if($this->readonly) { $extraOptions = $extraOptions->makeReadonly(); - + } + + // support for optionally display field + // $extraOptions->push(new CheckboxField($baseName ."[OptionallyDisplay]", _t('EditableFormField.OPTIONALLYDISPLAY', 'Optionally Display Field'), $this->OptionallyDisplay)); + return $extraOptions; } @@ -159,7 +195,6 @@ class EditableFormField extends DataObject { protected function parsePrepopulateValue( $value ) { $paramList = explode( ',', $value ); - $paramMap = array(); foreach( $paramList as $param ) { @@ -170,33 +205,20 @@ class EditableFormField extends DataObject { } else if( isset( $paramMap[$match[1]] ) ) { $paramMap[$match[1]] = array( $paramMap[$match[1]] ); $paramMap[$match[1]][] = $match[2]; - //Debug::message( $match[1] . '[]=' . $match[2] ); } else { $paramMap[$match[1]] = $match[2]; - //Debug::message( $match[1] . '=' . $match[2] ); } - } else { - //Debug::message('Invalid: ' . $param ); } } - - //Debug::show( $paramMap ); - return $paramMap; } protected function prepopulateFromMap( $paramMap ) { - //Debug::show( $paramMap ); - //Debug::show( $this->stat('db') ); - - foreach( $paramMap as $field => $fieldValue ) { - if( /*$this->hasField( $field ) &&*/ !is_array( $fieldValue ) ) { + foreach($paramMap as $field => $fieldValue) { + if(!is_array($fieldValue)) { $this->$field = $fieldValue; - // Debug::message( 'Set ' . $field . ':'. $fieldValue ); } } - - // exit(); } function Type() { diff --git a/code/editor/EditableFormHeading.php b/code/editor/EditableFormHeading.php index 7f61a53..8055d2e 100755 --- a/code/editor/EditableFormHeading.php +++ b/code/editor/EditableFormHeading.php @@ -1,15 +1,16 @@ Title); $labelField->addExtraClass('FormHeading'); return $labelField; @@ -18,5 +19,9 @@ class EditableFormHeading extends EditableFormField { function showInReports() { return false; } + + function showExtraOptions() { + return false; + } } ?> \ No newline at end of file diff --git a/code/editor/EditableLiteralField.php b/code/editor/EditableLiteralField.php index 001f4ef..ed503ae 100644 --- a/code/editor/EditableLiteralField.php +++ b/code/editor/EditableLiteralField.php @@ -1,10 +1,10 @@ ID]"; @@ -33,32 +28,13 @@ class EditableLiteralField extends EditableFormField { } function populateFromPostData($data) { - $this->Content = $data['Content']; parent::populateFromPostData($data); } - function getFormField() { - return $this->createField(); - } - - function getFilterField() { - return $this->createField(true); - } - function createField() { return new LiteralField("LiteralField[$this->ID]", "
". $this->Content."
"); } - /** - * Populates the default fields. - */ - function DefaultField() { - return ""; - } - - function EditSegment() { - return $this->renderWith( $this->class ); - } } ?> \ No newline at end of file diff --git a/code/editor/EditableMemberListField.php b/code/editor/EditableMemberListField.php index eeb6f95..653bec7 100644 --- a/code/editor/EditableMemberListField.php +++ b/code/editor/EditableMemberListField.php @@ -1,8 +1,8 @@ "EditableOption" + ); + + protected $readonly; + + /** + * Deletes all the options attached to this field before + * deleting the field. Keeps stray options from floating + * around + * + * @return void + */ + public function delete() { + $options = $this->Options(); + if($options) { + foreach($options as $option) { + $option->delete(); + } + } + parent::delete(); + } + + /** + * Duplicate a pages content. We need to make sure all + * the fields attached to that page go with it + * + * @return DataObject a Clone of this node + */ + public function duplicate() { + $clonedNode = parent::duplicate(); + + if($this->Options()) { + foreach($this->Options() as $field) { + $newField = $field->duplicate(); + $newField->ParentID = $clonedNode->ID; + $newField->write(); + } + } + return $clonedNode; + } + + /** + * On before saving this object we need to go through and keep + * an eye on all our option fields that are related to this + * field in the form + * + * @param Array Data + */ + public function populateFromPostData($data) { + parent::populateFromPostData($data); + + // get the current options + $fieldSet = $this->Options(); + + // go over all the current options and check if ID and Title still exists + foreach($fieldSet as $option) { + if(isset($data[$option->ID]) && isset($data[$option->ID]['Title']) && $data[$option->ID]['Title'] != "field-node-deleted") { + $option->populateFromPostData($data[$option->ID]); + } + else { + $option->delete(); + } + } + } + + /** + * Return whether or not this field has addable options + * such as a dropdown field or radio set + * + * @return bool + */ + public function hasAddableOptions() { + return true; + } + + /** + * Set this multipleoptionfield to readonly + */ + protected function ReadonlyOption() { + $this->readonly = true; + } + + /** + * Is this multipleoption field readonly to the user + * + * @return bool + */ + public function isReadonly() { + return $this->readonly; + } + + /** + * Return the form field for this object in the front + * end form view + * + * @return FormField + */ + public function getFormField() { + return $this->createField(); + } + + /** + * Return the form field as a field suitable for insertion + * into the filter form + * + * @return FormField + */ + public function getFilterField() { + return $this->createField(true); + } + + /** + * Return the correct form field for this object. Note this + * does a transformation between being a field on the form and + * a field in the filter search form + * + * This should be extended on your subclass + * + * @param bool - Filter Field? + * @return UserError - You should implement it on your subclass + */ + public function createField($filter = false) { + return user_error('Please implement createField() on '. $this->class, E_USER_ERROR); + } + + /** + * Checkbox to show if this option is the default option selected + * in the form + * + * @return HTML + */ + public function DefaultSelect() { + $disabled = ($this->readonly) ? " disabled=\"disabled\"" : ''; + $default = ($this->Parent()->getField('Default') == $this->ID) ? " checked=\"checked\"" : ''; + + return "ParentID}][Default]\" value=\"{$this->ID}\"".$disabled.$default." />"; + } +} +?> \ No newline at end of file diff --git a/code/editor/EditableOption.php b/code/editor/EditableOption.php new file mode 100644 index 0000000..97c227c --- /dev/null +++ b/code/editor/EditableOption.php @@ -0,0 +1,71 @@ + "Varchar", + "Title" => "Varchar", + "Default" => "Boolean", + "Sort" => "Int" + ); + + static $has_one = array( + "Parent" => "EditableMultipleOptionField", + ); + + /** + * Template for the editing view of this option field + */ + public function EditSegment() { + return $this->renderWith('EditableOption'); + } + + /** + * The Title Field for this object + * + * @return FormField + */ + public function TitleField() { + return new TextField("Fields[{$this->ParentID}][{$this->ID}][Title]", null, $this->Title ); + } + + /** + * Name of this field in the form + * + * @return String + */ + public function Name() { + return "Fields[{$this->ParentID}][{$this->ID}]"; + } + + /** + * Populate this option from the form field + * + * @param Array Data + */ + public function populateFromPostData($data) { + $this->Title = (isset($data['Title'])) ? $data['Title'] : ""; + $this->Default = (isset($data['Default'])) ? $data['Default'] : ""; + $this->Sort = (isset($data['Sort'])) ? $data['Sort'] : 0; + $this->write(); + } + + /** + * Make this option readonly + */ + public function ReadonlyOption() { + $this->readonly = true; + return $this->EditSegment(); + } + +} +?> \ No newline at end of file diff --git a/code/editor/EditableRadioField.php b/code/editor/EditableRadioField.php index 820464c..bc26925 100755 --- a/code/editor/EditableRadioField.php +++ b/code/editor/EditableRadioField.php @@ -1,101 +1,16 @@ "EditableRadioOption" - ); +class EditableRadioField extends EditableMultipleOptionField { static $singular_name = 'Radio field'; + static $plural_name = 'Radio fields'; - - function delete() { - $options = $this->Options(); - - foreach( $options as $option ) - $option->delete(); - - parent::delete(); - } - - function duplicate() { - $clonedNode = parent::duplicate(); - - foreach( $this->Options() as $field ) { - $newField = $field->duplicate(); - $newField->ParentID = $clonedNode->ID; - $newField->write(); - } - - return $clonedNode; - } - - function EditSegment() { - return $this->renderWith( $this->class ); - } - - function populateFromPostData( $data ) { - parent::populateFromPostData( $data ); - - $fieldSet = $this->Options(); - $deletedOptions = explode( ',', $data['Deleted'] ); - - //Debug::show( $deletedOptions ); - - // store default, etc - foreach( $fieldSet as $option ) { - - //Debug::show( $option ); - - if( $deletedOptions && array_search( $option->ID, $deletedOptions ) !== false ) { - $option->delete(); - continue; - } - - if( $data[$option->ID] ) { - $option->setField( 'Default', $option->ID == $data['Default'] ); - $option->populateFromPostData( $data[$option->ID] ); - } - - unset( $data[$option->ID] ); - } - - // Debug::show( $data ); - - foreach( $data as $tempID => $optionData ) { - - $optionNumber = 0; - - if( !$tempID || !is_array( $optionData ) || empty( $optionData ) || !preg_match('/^_?\d+$/', $tempID ) ) - continue; - - // what will we name the new option? - $newOption = new EditableRadioOption(); - $newOption->Name = sprintf( 'option%d', $optionNumber++ ); - $newOption->ParentID = $this->ID; - $newOption->setField( 'Default', $tempID == $data['Default'] ); - $newOption->populateFromPostData( $optionData ); - - // $mail .= "NEW: " . $optionData['Title'] . "\n"; - - if( Director::is_ajax() ) { - $fieldID = $this->ID; - $fieldEditorName = $this->editor ? $this->editor->Name() : 'Fields'; - $prefix = $fieldEditorName . '[' . $fieldID . ']'; - $newID = $newOption->ID; - $newSort = $newOption->Sort; - echo "\$('". $fieldEditorName . "[$fieldID]').updateOption('$prefix','$tempID','$newID','$newSort');"; - } - - if( !$newOption->Title ) - user_error('Added blank option '.$tempID, E_USER_ERROR); - } - } function DefaultOption() { $defaultOption = 0; @@ -110,14 +25,6 @@ class EditableRadioField extends EditableFormField { return -1; } - function getFormField() { - return $this->createField(); - } - - function getFilterField() { - return $this->createField( true ); - } - function createField( $asFilter = false ) { $optionSet = $this->Options(); $options = array(); @@ -136,52 +43,5 @@ class EditableRadioField extends EditableFormField { // return radiofields return new OptionsetField($this->Name, $this->Title, $options, $defaultOption); } - - function prepopulate( $value ) { - - $options = $this->Options(); - - $paramMap = $this->parsePrepopulateValue( $value ); - - // find options and add them - $optionNumber = 0; - foreach( $paramMap['Options'] as $newOption ) { - if( preg_match( '/([^:]+)[:](.*)/', $newOption, $match ) ) { - $newOptionValue = $match[1]; - $newOptionTitle = $match[2]; - - $newOptionTitle = preg_replace('/__/', ' ', $newOptionTitle ); - - $newOption = $this->createOption( - 'option' . (string)$optionNumber, - $newOptionTitle, - 'new-' . (string)$optionNumber, - $newOption['Sort'], - $optionNumber == 1, - false - ); - - $optionNumber++; - $options->addWithoutWrite( $newOption ); - } - } - } - -protected function createOption( $name, $title, $id, $sort = 0, $isDefault = false ) { - $newOption = new EditableRadioOption(); - $newOption->Name = $name; - $newOption->Title = $title; - $newOption->ID = $id; - $newOption->Sort = $sort; - $newOption->setField('Default', $isDefault ? '1' : '0'); - - return $newOption; - } - - function TemplateOption() { - $option = new EditableRadioOption(); - $option->ParentID = $this->ID; - return $option->EditSegment(); - } - } +} ?> diff --git a/code/editor/EditableRadioOption.php b/code/editor/EditableRadioOption.php deleted file mode 100755 index 2c40464..0000000 --- a/code/editor/EditableRadioOption.php +++ /dev/null @@ -1,84 +0,0 @@ -readonly = true; - return $this->EditSegment(); - } - - function isReadonly() { - return $this->readonly; - } - - static $default_sort = "Sort"; - - // add required here? - static $db = array( - "Name" => "Varchar", - "Title" => "Varchar", - "Default" => "Boolean", - "Value" => "Varchar", - "Sort" => "Int" - ); - static $has_one = array( - "Parent" => "EditableRadioField", - ); - - static $singular_name = 'Radio option'; - static $plural_name = 'Radio options'; - - function EditSegment() { - return $this->renderWith('EditableFormFieldOption'); - } - - function TitleField() { - return new TextField( "Fields[{$this->ParentID}][{$this->ID}][Title]", null, $this->Title ); - } - - function Name() { - return "Fields[{$this->ParentID}][{$this->ID}]"; - } - - function populateFromPostData( $data ) { - $this->Title = $data['Title']; - $this->Sort = $data['Sort']; - $this->write(); - } - - function Option() { - // return new radio field - /*$title = Convert::raw2att( $this->Title ); - - $default = ""; - - if( $this->getField('Default') ) - $default = '+'; - else - $default = '-'; - - //Debug::show($this); - return '';*/ - - return $this->EditSegment(); - } - - function DefaultSelect() { - $disabled = ($this->readonly) ? " disabled=\"disabled\"" : ''; - - if($this->Parent()->getField('Default') == $this->ID) { - $default = " checked=\"checked\""; - } else { - $default = ''; - } - - return "ParentID}][Default]\" value=\"{$this->ID}\"".$disabled.$default." />"; - } -} -?> \ No newline at end of file diff --git a/code/editor/EditableTextField.php b/code/editor/EditableTextField.php index c3f6ff2..40bcfa6 100755 --- a/code/editor/EditableTextField.php +++ b/code/editor/EditableTextField.php @@ -1,9 +1,10 @@ name; @@ -75,10 +75,16 @@ class FieldEditor extends FormField { return false; } - + /** + * Handles saving the page. Needs to keep an eye on fields + * and options which have been removed / added + * + * @param DataObject Record to Save it In + */ function saveInto(DataObject $record) { $name = $this->name; + $fieldSet = $record->$name(); // @todo shouldn't we deal with customFormActions on that object? @@ -90,104 +96,109 @@ class FieldEditor extends FormField { // store the field IDs and delete the missing fields // alternatively, we could delete all the fields and re add them $missingFields = array(); - - foreach( $fieldSet as $existingField ){ + + foreach($fieldSet as $existingField){ $missingFields[$existingField->ID] = $existingField; } - - // write the new fields to the database + if($_REQUEST[$name]){ foreach( array_keys( $_REQUEST[$name] ) as $newEditableID ) { $newEditableData = $_REQUEST[$name][$newEditableID]; + + $editable = DataObject::get_one('EditableFormField', "(`ParentID`='{$record->ID}' OR `ParentID`=0) AND `EditableFormField`.`ID`='$newEditableID'" ); + + // check if we are updating an existing field. One odd thing is a 'deleted' field + // still exists in the post data (ID) so we need to check for type. + if($editable && isset($missingFields[$editable->ID]) && isset($newEditableData['Type'])) { + // check if it has been labelled as deleted + if(isset($newEditableData['Title']) && $newEditableData['Title'] != 'field-node-deleted') { + unset($missingFields[$editable->ID]); + } + } - // `ParentID`=0 is for the new page - $editable = DataObject::get_one( 'EditableFormField', "(`ParentID`='{$record->ID}' OR `ParentID`=0) AND `EditableFormField`.`ID`='$newEditableID'" ); - - // check if we are updating an existing field - if( $editable && isset($missingFields[$editable->ID])) - unset( $missingFields[$editable->ID] ); - - // create a new object - // this should now be obsolete - if(!$editable && !empty($newEditableData['Type']) && class_exists($newEditableData['Type'])) { - $editable = new $newEditableData['Type'](); - $editable->ID = 0; - $editable->ParentID = $record->ID; - - if(!is_subclass_of($editable, 'EditableFormField')) { - $editable = null; - } - } - - if($editable) { - if($editable->ParentID == 0) { - $editable->ParentID = $record->ID; - } - $editable->populateFromPostData($newEditableData); - //$editable->write(); - } + if($editable) { + if($editable->ParentID == 0) { + $editable->ParentID = $record->ID; + } + $editable->populateFromPostData($newEditableData); + } } } - + // remove the fields not saved foreach($missingFields as $removedField) { if(is_numeric($removedField->ID)) $removedField->delete(); } - + if($record->hasMethod('customFormSave')) { $record->customFormSave( $_REQUEST[$name], $record ); } - //$record->writeWithoutVersion(); if($record->hasMethod( 'processNewFormFields')) { $record->processNewFormFields(); } } - - function addfield() { + + /** + * Add a field to the field editor. Called via a ajax get request + * from the userdefinedform javascript + * + * @return bool|html + */ + public function addfield() { // get the last field in this form editor $parentID = $this->form->getRecord()->ID; - $lastField = DataObject::get('EditableFormField', "`ParentID`='$parentID'", "`Sort` DESC", null, 1 ); - $nextSort = 1; - - // the new sort value is the value of the last sort + 1 if a field exists - if( $lastField ) - $nextSort += $lastField->Sort; - - $className = "Editable" . ucfirst($_REQUEST['Type']); - $name = $this->name; - if(is_subclass_of($className, "EditableFormField")) { - $e = new $className(); - // $fields = $this->form->getRecord()->$name()->Count(); - // $e->Name = $this->name . "[NewFields][]"; - // Debug::show($fields); - - /*if( $this->form->getRecord()->hasMethod('addField') ) - $this->form->getRecord()->addField( $e ); - else*/ + if($parentID) { + $parentID = Convert::raw2sql($parentID); // who knows what could happen + $highestSort = DB::query("SELECT MAX(Sort) FROM EditableFormField WHERE ParentID = '$parentID'"); + $sort = $highestSort->value() + 1; + + $className = "Editable" . ucfirst($_REQUEST['Type']); + $name = $this->name; + if(is_subclass_of($className, "EditableFormField")) { + $e = new $className(); + $e->write(); $e->ParentID = $this->form->getRecord()->ID; - - //Debug::show($e); - $e->write(); - //$e->ID = "new-" . ( $_REQUEST['NewID'] + 1 ); - $e->Name = $e->class . $e->ID; - $e->write(); - - return $e->EditSegment(); - } else { - user_error("FieldEditor::addfield: Tried to create a field of class '$className'", E_USER_ERROR); + $e->Name = $e->class . $e->ID; + $e->write(); + return $e->EditSegment(); + } } + return false; } - function adddropdownfield() { - return $this->addNewField( new EditableDropdown() ); + /** + * Return the html for a field option such as a + * dropdown field or a radio check box field + * + * @return bool|html + */ + public function addoptionfield() { + // passed via the ajax + $parent = (isset($_REQUEST['Parent'])) ? $_REQUEST['Parent'] : false; + $text = (isset($_REQUEST['Text'])) ? $_REQUEST['Text'] : ""; + + // work out the sort by getting the sort of the last field in the form +1 + if($parent) { + $sql_parent = Convert::raw2sql($parent); + $highestSort = DB::query("SELECT MAX(Sort) FROM EditableOption WHERE ParentID = '$sql_parent'"); + $sort = $highestSort->value() + 1; + + if($parent) { + $object = new EditableOption(); + $object->write(); + $object->ParentID = $parent; + $object->Sort = $sort; + $object->Name = 'option' . $object->ID; + $object->Title = $text; + $object->write(); + return $object->EditSegment(); + } + } + return false; } - - function addcheckboxfield() { - return $this->addNewField( new EditableCheckbox() ); - } - + protected $haveFormOptions = true; function setHaveFormOptions($bool){ diff --git a/css/FieldEditor.css b/css/FieldEditor.css index 131f980..bf1a363 100755 --- a/css/FieldEditor.css +++ b/css/FieldEditor.css @@ -73,7 +73,7 @@ div.FieldEditor div.MenuHolder { border: 0; } -div.FieldEditor ul li { +div.FieldEditor .MenuHolder ul li { display: inline; } @@ -121,12 +121,10 @@ div.EditableFormField.mouseOver { margin-left: 0px; } - div.EditableFormField div.ExtraOptions { + div.EditableFormField div.extraOptions { display: none; margin: 3px 0px 3px 57px; background-color: #EEEEEE; - /* IE has background issues without this */ - /*position: relative;*/ padding: 3px; } @@ -151,10 +149,7 @@ div.EditableDateField div.FieldDefault input { ********************************************************************/ #right #Form_EditForm div.EditableMultiOptionFormField div.FieldDefault ul.EditableDropdownOptions { - border: solid 1px #7F9DB9; - /* IE */ margin: 0px; - /* display: none; */ display: block; position: relative; } diff --git a/images/fe_icons/editablecheckbox.png b/images/editablecheckbox.png similarity index 100% rename from images/fe_icons/editablecheckbox.png rename to images/editablecheckbox.png diff --git a/images/fe_icons/checkboxes.png b/images/editablecheckboxgroupfield.png similarity index 100% rename from images/fe_icons/checkboxes.png rename to images/editablecheckboxgroupfield.png diff --git a/images/fe_icons/editabledatefield.png b/images/editabledatefield.png similarity index 100% rename from images/fe_icons/editabledatefield.png rename to images/editabledatefield.png diff --git a/images/fe_icons/dropdown.png b/images/editabledropdown.png similarity index 100% rename from images/fe_icons/dropdown.png rename to images/editabledropdown.png diff --git a/images/fe_icons/editableemailfield.png b/images/editableemailfield.png similarity index 100% rename from images/fe_icons/editableemailfield.png rename to images/editableemailfield.png diff --git a/images/fe_icons/editablefilefield.png b/images/editablefilefield.png similarity index 100% rename from images/fe_icons/editablefilefield.png rename to images/editablefilefield.png diff --git a/images/fe_icons/editableformheading.png b/images/editableformheading.png similarity index 100% rename from images/fe_icons/editableformheading.png rename to images/editableformheading.png diff --git a/images/editableliteralfield.png b/images/editableliteralfield.png new file mode 100644 index 0000000..9c30878 Binary files /dev/null and b/images/editableliteralfield.png differ diff --git a/images/fe_icons/checkbox.png b/images/fe_icons/checkbox.png deleted file mode 100755 index 8250899..0000000 Binary files a/images/fe_icons/checkbox.png and /dev/null differ diff --git a/images/fe_icons/date-time.png b/images/fe_icons/date-time.png deleted file mode 100755 index d3ddaa4..0000000 Binary files a/images/fe_icons/date-time.png and /dev/null differ diff --git a/images/fe_icons/radio.png b/images/fe_icons/editableradiofield.png similarity index 100% rename from images/fe_icons/radio.png rename to images/fe_icons/editableradiofield.png diff --git a/images/fe_icons/file-upload.png b/images/fe_icons/file-upload.png deleted file mode 100755 index 4f16b13..0000000 Binary files a/images/fe_icons/file-upload.png and /dev/null differ diff --git a/images/fe_icons/heading.png b/images/fe_icons/heading.png deleted file mode 100755 index 3d95723..0000000 Binary files a/images/fe_icons/heading.png and /dev/null differ diff --git a/images/fe_icons/text-email.png b/images/fe_icons/text-email.png deleted file mode 100755 index 7697d66..0000000 Binary files a/images/fe_icons/text-email.png and /dev/null differ diff --git a/images/fe_icons/text-numbers.png b/images/fe_icons/text-numbers.png deleted file mode 100755 index 0ed65d4..0000000 Binary files a/images/fe_icons/text-numbers.png and /dev/null differ diff --git a/images/fe_icons/text-password.png b/images/fe_icons/text-password.png deleted file mode 100755 index 36d69f2..0000000 Binary files a/images/fe_icons/text-password.png and /dev/null differ diff --git a/images/fe_icons/text.png b/images/fe_icons/text.png deleted file mode 100755 index cb7951b..0000000 Binary files a/images/fe_icons/text.png and /dev/null differ diff --git a/javascript/FieldEditor.js b/javascript/FieldEditor.js index 3dbf571..e69de29 100755 --- a/javascript/FieldEditor.js +++ b/javascript/FieldEditor.js @@ -1,555 +0,0 @@ -FieldEditor = Class.create(); -FieldEditor.applyTo('div.FieldEditor'); -FieldEditor.prototype = { - initialize: function() { - FieldEditorField.applyToChildren(this, 'div.EditableFormField'); - FieldEditorHeadingField.applyToChildren(this, 'div.EditableFormHeading'); - FieldEditorRadioField.applyToChildren(this, 'div.EditableRadioField'); - FieldEditorCheckboxGroupField.applyToChildren(this, 'div.EditableCheckboxGroupField'); - FieldEditorDropdown.applyToChildren(this, 'div.EditableDropdown'); - FieldEditorEmailField.applyToChildren(this, 'div.EditableEmailField'); - FieldEditorTextField.applyToChildren(this, 'div.EditableTextField'); - - if( !Element.hasClassName( this, 'readonly' ) ) { - Sortable.create('Fields_fields', {tag: 'div', handle:'handle'}); - $('Form_EditForm').observeMethod('BeforeSave', this.beforeSave.bind(this)); - } - - }, - sortFields: function() { - var fieldEditor = $('Fields_fields'); - - if(fieldEditor) { - - var i, j, div, field, editables = fieldEditor.childNodes; - - for( i = 0; div = editables[i]; i++ ) { - if(div.getElementsByTagName) { - var fields = div.getElementsByTagName('input'); - /*fields[fields.length - 1].value = i;*/ - for( j = 0; field = fields.item(j); j++ ) { - if( field.name == div.id + '[Sort]' ) { - field.value = i; - } - } - } - } - } - }, - beforeSave: function() { - var fieldEditor = $('Fields_fields'); - - if(fieldEditor) { - this.sortFields(); - - var children = $('Fields_fields').childNodes; - - for( var i = 0; i < children.length; ++i ) { - var child = children[i]; - - if( child.beforeSave ) - child.beforeSave(); - } - } - }, - deleteOption: function( optionToRemove ) { - var fields = document.getElementById('Fields_fields'); - fields.removeChild(optionToRemove); - } -} - -FieldEditorField = Class.create(); - -FieldEditorField.prototype = { - initialize: function() { - var fieldInfoDiv = this.findDescendant( 'div', 'FieldInfo' ); - - this.titleField = this.findDescendant( 'input', 'text'); - - this.titleField.onchange = this.changeTitle.bind(this); - this.titleField.onblur = this.changeTitle.bind(this); - this.titleField.onfocus = this.focusTitle.bind(this); - - this.titleField.onchange(); - - var links = fieldInfoDiv.getElementsByTagName('a'); - this.toggler = this.findDescendant( 'a', 'toggler' ); - this.fieldInfo = this.getElementsByTagName('div')[0]; - - - this.toggler.onclick = this.toggle.bind(this); - this.extraOptions = this.getExtraOptions(); - this.visible = false; - this.deleteButton = this.findDescendant('a', 'delete'); - - //this.style.height = "auto"; - - if( this.deleteButton ) - this.deleteButton.onclick = this.confirmDelete.bind(this); - }, - toggle: function() { - // this.parentNode.autoSize(); - - if( this.visible ) - this.hide(); - else - this.show(); - - this.fieldInfo.style.display = 'block'; - - return false; - }, - show: function() { - /*this.style.height = ""; - this.style.overflow = "";*/ - - if( this.selectedOption ) - this.selectedOption.checked = true; - - this.visible = true; - // var extraOptions = this.getExtraOptions(); - // if( this.extraOptions ) - this.extraOptions.style.display = 'block'; - }, - hide: function() { - - this.visible = false; - // var extraOptions = this.getExtraOptions(); - //if( this.extraOptions ) - this.extraOptions.style.display = 'none'; - }, - getExtraOptions: function() { - var extraOptions = this.findDescendant('div', 'ExtraOptions'); - - if( extraOptions.parentNode != this ) - alert("Found extra options but not this parent (" + this.id + ")"); - - return extraOptions; - }, - confirmDelete: function() { - if( confirm( 'Are you sure you want to delete this field from the form?' ) ) - this.parentNode.parentNode.parentNode.deleteOption( this ); - - return false; - }, - findDescendant: function( tag, clsName, element ) { - - if( !element ) - element = this; - - var descendants = element.getElementsByTagName(tag); - - for( var i = 0; i < descendants.length; i++ ) { - var el = descendants[i]; - // alert(el.tagName + ' ' + el.className); - - if( tag.toUpperCase() == el.tagName && el.className.indexOf( clsName ) != -1 ) - return el; - } - - return null; - }, - focusTitle: function() { - if( this.titleField && this.titleField.value == this.titleField.title ) - this.titleField.value = ''; - }, - changeTitle: function() { - if( this.titleField && this.titleField.value == '' ) - this.titleField.value = this.titleField.title; - } -} - -FieldEditorHeadingField = Class.extend('FieldEditorField'); - -FieldEditorHeadingField.prototype = { - initialize: function() { - this.FieldEditorField.initialize(); - } -} - -FieldEditorEmailField = Class.extend('FieldEditorField'); - - -FieldEditorEmailField.prototype = { - initialize: function() { - this.extraOptions = this.getExtraOptions(); - this.defaultText = this.getDefaultText(); - - this.FieldEditorField.initialize(); - }, - getDefaultText: function() { - var defaultField = this.getDefaultField(); - if(defaultField) { - var j, nestedChild, nestedChildren = defaultField.childNodes; - for( j=0; nestedChild = nestedChildren[j]; j++) { - if (nestedChild.className == 'defaultText' ) - { - return nestedChild; - } - } - } - }, - getDefaultField: function() { - - var i, child, children = this.getElementsByTagName('div'); - for( i = 0; child = children[i]; i++){ - if(child.className == 'FieldDefault'){ - return child; - } - } - } -} - - -FieldEditorTextField = Class.extend('FieldEditorField'); -FieldEditorTextField.prototype = { - initialize: function() { - this.FieldEditorField.initialize(); - this.defaultText = this.getDefaultText(); - this.numRows = this.extraOptions.getElementsByTagName('input')[3]; - if(this.numRows) { - this.numRows.onchange = this.changedRows.bind(this); - this.oldNumRows = eval(this.numRows.value); - } - - }, - changedRows: function() { - var newNumRows = eval(this.numRows.value); - - // TODO Show that the field is actually longer than 5 rows - if( newNumRows > 5 ) - newNumRows == 5; - - if( this.oldNumRows == newNumRows ) - return; - - if( newNumRows < 1 ) - newNumRows = 1; - - // resize/convert the textarea - var newType = ''; - - if( newNumRows == 1 ) - newType = 'input'; - else - newType = 'textarea' - - var newDefaultText = document.createElement(newType); - newDefaultText.className = this.defaultText.className; - newDefaultText.value = this.defaultText.value; - newDefaultText.id = this.defaultText.id; - newDefaultText.name = this.defaultText.name; - - if( newDefaultText.rows ) - newDefaultText.rows = newNumRows; - - //Does not work any more - //this.replaceChild( newDefaultText, this.defaultText ); - - //instead, using the following code - var defaultField = this.getDefaultField(); - defaultField.replaceChild(newDefaultText, this.defaultText); - - //keep other codes. - this.defaultText = newDefaultText; - this.oldNumRows = newNumRows; - }, - getDefaultText: function() { - var defaultField = this.getDefaultField(); - - if(defaultField) { - var j, nestedChild, nestedChildren = defaultField.childNodes; - for( j=0; nestedChild = nestedChildren[j]; j++) { - - if (nestedChild.className == 'defaultText' ) - { - return nestedChild; - } - } - } - }, - getDefaultField: function() { - var i, child, children = this.getElementsByTagName('div'); - for( i = 0; child = children[i]; i++){ - if(child.className == 'FieldDefault'){ - return child.getElementsByTagName('div')[0]; - } - } - } -} - -/** - * This should extend FieldEditorField - */ -FieldEditorRadioField = Class.extend('FieldEditorField'); - -FieldEditorRadioField.prototype = { - initialize: function() { - this.FieldEditorField.initialize(); - - this.hiddenFields = this.findDescendant( 'div', 'hidden' ); - - var dropdownBox = this.findDescendant( 'div', 'EditableDropdownBox' ); - - this.optionList = dropdownBox.getElementsByTagName('ul')[0]; - var options = this.optionList.getElementsByTagName('li'); - - if( options && options.length > 0 ) { - this.addOptionField = options[options.length - 1]; - - if( typeof this.addOptionField != 'undefined' && this.addOptionField.className != "AddDropdownOption" ) - this.addOptionField = null; - - // bind each option's delete link - for( var i = 0; i < options.length - 1; i++ ) { - var option = options[i]; - - var links = option.getElementsByTagName('a'); - - links[0].onclick = this.removeOption.bindAsEventListener(this); - } - } - - // Bind method to add option at the bottom of the list - if( this.addOptionField ) { - this.addOptionLink = this.addOptionField.getElementsByTagName('a')[0]; - this.addOptionTitle = this.addOptionField.getElementsByTagName('input')[0]; - this.addOptionLink.onclick = this.addOption.bind(this); - } - - if( !Element.hasClassName( $('Fields'), 'readonly' ) ) { - Sortable.create(this.optionList.id,{handle:'handle',tag:'li',only:'EditableFormFieldOption'}); - } - this.FieldEditorField.initialize(); - - // find the Delete field - var hiddenFields = this.getElementsByTagName('input'); - - for( var i = 0; i < hiddenFields.length; i++ ) { - var field = hiddenFields[i]; - if( field.name.indexOf('[Deleted\]' ) != -1 ) - this.deletedOptions = field; - } - - this.selectedOption = null; - - $('Form_EditForm').observeMethod('BeforeSave', this.beforeSave.bind(this)); - }, - firstElement: function( el ) { - - var node = el.firstChild; - - while( !node.tagName ) - node = node.nextSibling; - - return node; - }, - createOption: function( title, id, selected ) { - var templateNode = this.firstElement( this.hiddenFields ); - var newOptionNode = templateNode.cloneNode( true ); - - var newNodeChildren = newOptionNode.childNodes; - - for( var i = 0; i < newNodeChildren.length; i++ ) { - - var child = newNodeChildren[i]; - - if( !child.tagName ) - continue; - - // input elements - if( child.tagName.toLowerCase() == 'input' ) { - - if( child.className == 'text' ) { - child.name = this.id + '[' + id + '][Title]'; - child.value = title; - } else if( child.type == 'checkbox' ) - child.name = this.id + '[' + id + '][Default]'; - else if( child.type == 'radio' ) { - child.value = id; - } else if( child.type == 'hidden' ) { - child.name = this.id + '[' + id + '][Sort]'; - child.value = -1; - } - } else if ( child.tagName.toLowerCase() == 'a' ) { - child.onclick = this.removeOption.bindAsEventListener(this); - } - } - - this.optionList.insertBefore( newOptionNode, this.addOptionField ); - }, - removeOption: function( event ) { - - var target = event.srcElement; - - if( !target ) - target = event.target; - - var entry = target.parentNode.parentNode; - var id = entry.id; - - if( !id.match( '/^[0-9]+$/' ) ) { - if( this.deletedOptions.value ) - this.deletedOptions.value += ','; - - this.deletedOptions.value += id; - } - - // remove the child from the options - this.optionList.removeChild( entry ); - - // remove the child from the dropdown - /*for( var i = 0; i < this.dropdown.length; i++ ) { - if( this.dropdown.options[i].text == title ) { - this.dropdown.remove(i); - return false; - } - }*/ - - if( !Element.hasClassName( $('Fields'), 'readonly' ) ) - Sortable.create(this.optionList.id,{handle:'handle',tag:'li',only:'EditableFormFieldOption'}); - - // return false so it doesn't follow the link - return false; - }, - addOption: function() { - if( this.addOptionTitle.value.length == 0 ) - return false; - - // The IDs come from the database and are the ID of the actual record - // client-side, we will need a unique identifier that can be differentiated - // from the actual database IDs, unless we just drop all records and - // recreate them - var newID = '_' + this.optionList.childNodes.length; - - this.createOption( this.addOptionTitle.value, newID, this.optionList.childNodes.length == 0 ); - - if( !Element.hasClassName( $('Fields'), 'readonly' ) ) - Sortable.create(this.optionList.id,{handle:'handle',tag:'li',only:'EditableFormFieldOption'}); - - this.addOptionTitle.value = ''; - - return false; - }, - beforeSave: function() { - this.sortOptions(); - }, - sortOptions: function() { - var inputTags = this.optionList.getElementsByTagName('input'); - - var i,item,sort=0; - for(i=0;item=inputTags[i];i++) { - if(item.name.match(/\[Sort\]$/) ) { - item.value = sort++; - } - } - }, - selectOption: function(newOption) { - - if( this.selectedOption ) - this.selectedOption.checked = false; - - newOption.checked = true; - this.selectedOption = newOption; - }, - selectOptionEvent: function(event) { - if(event.srcElement) - this.selectOption(event.srcElement); - else - this.selectOption(event.target); - }, - updateOption: function( prefix, tempID, newID, newSort ) { - var options = this.optionList.childNodes; - - for( var i = 0; i < options.length; i++ ) { - var option = options[i]; - - var fields = option.getElementsByTagName('input'); - - for( var j = 0; j < fields.length; j++ ) { - var field = fields[j]; - - var oldPrefix = prefix + '[' + tempID + ']'; - var newPrefix = prefix + '[' + newID + ']'; - - if( field.name.indexOf( oldPrefix ) == 0 ) { - - if( field.name.match( /\[Sort\]$/ ) ) - field.value = newSort; - - // rename the field - field.name = newPrefix + field.name.substring( oldPrefix.length ); - - } else if( field.name == prefix + '[Default]' ) { - field.value = newID; - } - } - } - } -} - -FieldEditorCheckboxGroupField = Class.extend('FieldEditorRadioField'); - -FieldEditorDropdown = Class.extend('FieldEditorRadioField'); - -Behaviour.register({ - - 'div.FieldEditor ul.Menu li a': { - urlForFieldMethod: function(methodName) { - return this.ownerForm().action + '/field/Fields/' + methodName + '?NewID=' + this.numNewFields; - }, - ownerForm: function() { - var f = this.parentNode; - while(f && f.tagName.toLowerCase() != 'form') f = f.parentNode; - return f; - }, - - onclick: function() { - // get the ID of the field editor here - - if( Element.hasClassName( $('Fields'), 'readonly' ) ) - return false; - - action = this.urlForFieldMethod("addfield") + "&Type=" + this.id + ($('SecurityID') ? '&SecurityID=' + $('SecurityID').value : '');; - - statusMessage('Adding new field' ); - - new Ajax.Request(action, { - method: 'get', - onFailure: reportError, - onSuccess: this.appendNewField.bind(this) - }); - - return false; - }, - - appendNewField: function(response) { - this.numNewFields++; - - var el = document.createElement('div'); - el.innerHTML = response.responseText; - - var i=0; - while(!el.childNodes[i].tagName) i++; - var newField = el.childNodes[i]; - $('Fields_fields').appendChild(newField); - - // Behaviour.debug(); - if(newField) { - Behaviour.apply(newField,true); - FieldEditor.applyTo('div.FieldEditor'); - } - - // do we want to make sorting explicit? - Sortable.create('Fields_fields', {tag: 'div', handle:'handle'}); - - statusMessage('Added new field','good'); - } - } -}); - -function reportError(request){ - // More complex error for developers - statusMessage(request.responseText,'bad'); - -} diff --git a/javascript/UserForm.js b/javascript/UserForm.js index 0569fcd..c0afbea 100644 --- a/javascript/UserForm.js +++ b/javascript/UserForm.js @@ -1,5 +1,21 @@ +/** + * Javascript required to power the user defined forms. + * + * Rewritten and refactored from the prototype version FieldEditor. + * + * @todo Upgrade to jQuery 1.3 so we can use live rather + * then livequery + */ (function($) { $(document).ready(function() { + + /*--------------------- SUBMISSIONS ------------------------ */ + + /** + * Delete a given Submission from the form, or all submissions + * we let the href of the delete link to do all the work for us + */ + $("#FormSubmissions .deleteSubmission").click(function() { var deletedSubmission = $(this); $.post($(this).attr('href'), function(data) { @@ -7,5 +23,126 @@ }); return false; }); - }) -})(jQuery); \ No newline at end of file + + /*-------------------- FIELD EDITOR ----------------------- */ + + /** + * Create a new instance of a field in the current form + * area. the type information should all be on this object + */ + + $("div.FieldEditor ul.Menu li a").livequery('click',function() { + + // if this form is readonly... + if($("#Fields").hasClass('readonly')) return false; + + // Give the user some feedback + statusMessage(ss.i18n._t('UserForms.ADDINGNEWFIELD', 'Adding New Field')); + + // variables + var action = $("#Form_EditForm").attr("action") + '/field/Fields/addfield'; + var length = $(".FieldInfo").length + 1; + var securityID = ($("#SecurityID")) ? '&SecurityID='+$("#SecurityID").attr("value") : ''; + var type = $(this).attr("ID"); + + //send ajax request to the page + $.ajax({ + type: "GET", + url: action, + data: 'NewID='+ length +"&Type="+ type + securityID, + + // create a new field + success: function(msg){ + $('#Fields_fields').append(msg); + statusMessage(ss.i18n._t('UserForms.ADDEDNEWFIELD', 'Added New Field')); + }, + + // error creating new field + error: function(request, text, error) { + statusMessage(ss.i18n._t('UserForms.ERRORCREATINGFIELD', 'Error Creating Field')); + } + }); + }); + + /** + * Show the more options popdown. Or hide it if we + * currently have it open + */ + $(".EditableFormField .moreOptions").livequery('click',function() { + var parentID = $(this).parents(".EditableFormField"); + if(parentID) { + var extraOptions = parentID.children(".extraOptions"); + if(extraOptions) { + if(extraOptions.hasClass('hidden')) { + extraOptions.removeClass('hidden').show(); + } + else { + extraOptions.addClass('hidden').hide(); + } + } + } + return false; + }); + + /** + * Delete a field from the user defined form + */ + $(".EditableFormField .delete").livequery('click', function() { + $(this).parents(".EditableFormField").remove(); + }); + + /** + * Add a suboption to a radio field or to a dropdown box + * for example + */ + $(".EditableFormField .addableOption").livequery('click', function() { + + // Give the user some feedback + statusMessage(ss.i18n._t('UserForms.ADDINGNEWFIELD', 'Adding New Option')); + + // variables + var options = $(this).parent("li"); + var action = $("#Form_EditForm").attr("action") + '/field/Fields/addoptionfield'; + var parent = $(this).attr("rel"); + var text = $(this).parents("li").children(".text").val(); + + // clear input + $(this).parents("li").children(".text").val(""); + + //send ajax request to the page + $.ajax({ + type: "GET", + url: action, + data: 'Parent='+ parent +"&Text="+ text, + + // create a new field + success: function(msg){ + options.before(msg); + statusMessage(ss.i18n._t('UserForms.ADDEDNEWFIELD', 'Added New Field')); + }, + + // error creating new field + error: function(request, text, error) { + statusMessage(ss.i18n._t('UserForms.ERRORCREATINGFIELD', 'Error Creating Field')); + } + }); + return false; + }); + + /** + * Delete a suboption such as an dropdown option or a + * checkbox field + */ + $(".EditableFormField .deleteOption").livequery('click', function() { + // pass the deleted status onto the element + $(this).parents("li").children("[type=text]").attr("value", "field-node-deleted"); + $(this).parents("li").hide(); + + // Give the user some feedback + statusMessage(ss.i18n._t('UserForms.REMOVINGOPTION', 'Removed Option')); + return false; + }); + + }); +}) +(jQuery); \ No newline at end of file diff --git a/templates/EditableCheckbox.ss b/templates/EditableCheckbox.ss deleted file mode 100755 index 5ce547f..0000000 --- a/templates/EditableCheckbox.ss +++ /dev/null @@ -1,26 +0,0 @@ -
-
- <% if isReadonly %> - <% _t('LOCKED', 'This field cannot be modified') %> - <% else %> - <% _t('DRAG', 'Drag to rearrange order of fields') %> - <% end_if %> - <% _t('CHECKBOX', 'Checkbox field') %> - $TitleField - <% _t('MORE', 'More options') %> - <% _t('DELETE', 'Delete this field') %> -
-
-
- -
- <% control ExtraOptions %> - $FieldHolder - <% end_control %> -
- - - -
\ No newline at end of file diff --git a/templates/EditableCheckboxGroupField.ss b/templates/EditableCheckboxGroupField.ss deleted file mode 100755 index 3553ad3..0000000 --- a/templates/EditableCheckboxGroupField.ss +++ /dev/null @@ -1,51 +0,0 @@ -
-
- <% if isReadonly %> - <% _t('LOCKED', 'These fields cannot be modified') %> - <% else %> - <% _t('DRAG', 'Drag to rearrange order of fields') %> - <% end_if %> - <% _t('CHECKBOXGROUP', 'Checkbox group') %> - $TitleField - - <% _t('MORE', 'More options') %> - <% if isReadonly %> - <% _t('LOCKED', 'These fields cannot be modified') %> - <% else %> - <% if CanDelete %> - <% _t('DELETE', 'Delete this field') %> - <% else %> - <% _t('REQUIRED', 'This field is required for this form and cannot be deleted') %> - <% end_if %> - <% end_if %> -
- -
-
-
    - <% if isReadonly %> - <% control Options %> - $ReadonlyOption - <% end_control %> - <% else %> - <% control Options %> - $Option - <% end_control %> -
  • - - <% _t('ADD', 'Add new option') %> -
  • - <% end_if %> -
-
- <% control ExtraOptions %> - $FieldHolder - <% end_control %> -
- - - - -
diff --git a/templates/EditableCheckboxOption.ss b/templates/EditableCheckboxOption.ss deleted file mode 100755 index 70865a9..0000000 --- a/templates/EditableCheckboxOption.ss +++ /dev/null @@ -1,10 +0,0 @@ -
  • - <% _t('DRAG', 'Drag to rearrange order of options') %> - - - <% if isReadonly %> - <% _t('DELETE', 'Remove this option') %> - <% else %> - <% _t('LOCKED', 'These fields cannot be modified') %> - <% end_if %> -
  • \ No newline at end of file diff --git a/templates/EditableDateField.ss b/templates/EditableDateField.ss deleted file mode 100755 index 6343bb5..0000000 --- a/templates/EditableDateField.ss +++ /dev/null @@ -1,20 +0,0 @@ -
    -
    - <% _t('DRAG', 'Drag to rearrange order of fields') %> - <% _t('DATE', 'Date Field') %> - $TitleField - <% _t('MORE', 'More options') %> - <% _t('DELETE', 'Delete this field') %> -
    -
    -
    - $DefaultField -
    - <% control ExtraOptions %> - $FieldHolder - <% end_control %> -
    - - - -
    diff --git a/templates/EditableDropdown.ss b/templates/EditableDropdown.ss deleted file mode 100755 index c02bd6d..0000000 --- a/templates/EditableDropdown.ss +++ /dev/null @@ -1,50 +0,0 @@ -
    -
    - <% if isReadonly %> - <% _t('LOCKED', 'These fields cannot be modified') %> - <% else %> - <% _t('DRAG', 'Drag to rearrange order of fields') %> - <% end_if %> - <% _t('DROPDOWN', 'Dropdown box') %> - $TitleField - <% _t('MORE', 'More options') %> - <% if isReadonly %> - <% _t('LOCKED', 'These fields cannot be modified') %> - <% else %> - <% if CanDelete %> - <% _t('DELETE', 'Delete this field') %> - <% else %> - <% _t('REQUIRED', 'This field is required for this form and cannot be deleted') %> - <% end_if %> - <% end_if %> -
    - -
    -
    -
      - <% if isReadonly %> - <% control Options %> - $ReadonlyOption - <% end_control %> - <% else %> - <% control Options %> - $Option - <% end_control %> -
    • - - <% _t('ADD', 'Add new option') %> -
    • - <% end_if %> -
    -
    - <% control ExtraOptions %> - $FieldHolder - <% end_control %> -
    - - - - -
    diff --git a/templates/EditableDropdownOption.ss b/templates/EditableDropdownOption.ss deleted file mode 100755 index 2bdf47a..0000000 --- a/templates/EditableDropdownOption.ss +++ /dev/null @@ -1,10 +0,0 @@ -
  • - <% _t('DRAG', 'Drag to rearrange order of options') %> - - - <% if isReadonly %> - <% _t('DELETE', 'Remove this option') %> - <% else %> - <% _t('LOCKED', 'These fields cannot be modified') %> - <% end_if %> -
  • \ No newline at end of file diff --git a/templates/EditableEmailField.ss b/templates/EditableEmailField.ss deleted file mode 100755 index 7b7aadd..0000000 --- a/templates/EditableEmailField.ss +++ /dev/null @@ -1,25 +0,0 @@ -
    -
    - <% _t('DRAG', 'Drag to rearrange order of fields') %> - <% _t('EMAIL', 'Email address field') %> - $TitleField - <% _t('MORE', 'More options') %> - <% if CanDelete %> - <% _t('DELETE', 'Delete this field') %> - <% else %> - <% _t('REQUIRED', 'This field is required for this form and cannot be deleted') %> - <% end_if %> -
    -
    -
    - $DefaultField -
    - <% control ExtraOptions %> - $FieldHolder - <% end_control %> -
    - - - - -
    \ No newline at end of file diff --git a/templates/EditableFileField.ss b/templates/EditableFileField.ss deleted file mode 100755 index f8f6b31..0000000 --- a/templates/EditableFileField.ss +++ /dev/null @@ -1,20 +0,0 @@ -
    -
    - <% _t('DRAG', 'Drag to rearrange order of fields') %> - <% _t('FILE', 'File upload field') %> - $TitleField - <% _t('MORE', 'More options') %> - <% _t('DELETE', 'Delete this field') %> -
    -
    -
    - $DefaultField -
    - <% control ExtraOptions %> - $FieldHolder - <% end_control %> -
    - - - -
    diff --git a/templates/EditableFormField.ss b/templates/EditableFormField.ss index 3d2a8bc..06194a6 100755 --- a/templates/EditableFormField.ss +++ b/templates/EditableFormField.ss @@ -1,44 +1,61 @@
    <% if isReadonly %> - <% _t('LOCKED', 'These fields cannot be modified') %> + <% _t('LOCKED', 'These fields cannot be modified') %> <% else %> - <% _t('DRAG', 'Drag to rearrange order of fields') %> + <% _t('DRAG', 'Drag to rearrange order of fields') %> <% end_if %> + $ClassName + $TitleField - <% _t('MORE', 'More options') %> + + <% if showExtraOptions %> + + <% _t('MORE', 'More options') %> + + <% end_if %> + <% if isReadonly %> - <% _t('LOCKED', 'These fields cannot be modified') %> + <% _t('LOCKED', 'These fields cannot be modified') %> <% else %> - <% if CanDelete %> - <% _t('DELETE', 'Delete this field') %> - <% else %> - <% _t('REQUIRED', 'This field is required for this form and cannot be deleted') %> - <% end_if %> - <% end_if %> -
    - <% if Options %> - - <% end_if %> -
    -
    - $DefaultField + <% if CanDelete %> + <% _t('DELETE', 'Delete this field') %> + <% else %> + <% _t('REQUIRED', 'This field is required for this form and cannot be deleted') %> + <% end_if %> + <% end_if %> +
    + + <% if showExtraOptions %> + - <% control ExtraOptions %> - $FieldHolder - <% end_control %> -
    - - - + <% end_if %> + + + +
    \ No newline at end of file diff --git a/templates/EditableFormFieldOption.ss b/templates/EditableFormFieldOption.ss deleted file mode 100755 index 4ec8971..0000000 --- a/templates/EditableFormFieldOption.ss +++ /dev/null @@ -1,15 +0,0 @@ -
  • - <% if isReadonly %> - <% _t('LOCKED', 'These fields cannot be modified') %> - $DefaultSelect - - - <% _t('LOCKED', 'These fields cannot be modified') %> - <% else %> - <% _t('DRAG', 'Drag to rearrange order of fields') %> - $DefaultSelect - - - <% _t('DELETE', 'Remove this option') %> - <% end_if %> -
  • \ No newline at end of file diff --git a/templates/EditableFormHeading.ss b/templates/EditableFormHeading.ss deleted file mode 100755 index 9324c39..0000000 --- a/templates/EditableFormHeading.ss +++ /dev/null @@ -1,17 +0,0 @@ -
    -
    - <% _t('DRAG', 'Drag to rearrange order of fields') %> - <% _t('HEADING', 'Heading field') %> - $TitleField - <% _t('MORE', 'More options') %> - <% _t('DELETE', 'Delete this field') %> -
    -
    - <% control ExtraOptions %> - $FieldHolder - <% end_control %> -
    - - - -
    diff --git a/templates/EditableLiteralField.ss b/templates/EditableLiteralField.ss deleted file mode 100644 index d10f909..0000000 --- a/templates/EditableLiteralField.ss +++ /dev/null @@ -1,20 +0,0 @@ -
    -
    - <% _t('DRAG', 'Drag to rearrange order of fields') %> - <% _t('LITERALFIELD', 'Literal Field') %> - $TitleField - <% _t('MORE', 'More options') %> - <% _t('DELETE', 'Delete this field') %> -
    -
    -
    - $DefaultField -
    - <% control ExtraOptions %> - $FieldHolder - <% end_control %> -
    - - - -
    \ No newline at end of file diff --git a/templates/EditableOption.ss b/templates/EditableOption.ss new file mode 100644 index 0000000..1cb543d --- /dev/null +++ b/templates/EditableOption.ss @@ -0,0 +1,11 @@ +
  • + <% _t('DRAG', 'Drag to rearrange order of options') %> + $DefaultSelect + + + <% if isReadonly %> + <% _t('LOCKED', 'These fields cannot be modified') %> + <% else %> + <% _t('DELETE', 'Remove this option') %> + <% end_if %> +
  • \ No newline at end of file diff --git a/templates/EditableRadioField.ss b/templates/EditableRadioField.ss deleted file mode 100755 index 452905a..0000000 --- a/templates/EditableRadioField.ss +++ /dev/null @@ -1,51 +0,0 @@ -
    -
    - <% if isReadonly %> - <% _t('LOCKED', 'These fields cannot be modified') %> - <% else %> - <% _t('DRAG', 'Drag to rearrange order of fields') %> - <% end_if %> - <% _t('SET', 'Radio button set') %> - $TitleField - - <% _t('MORE', 'More options') %> - <% if isReadonly %> - <% _t('LOCKED', 'These fields cannot be modified') %> - <% else %> - <% if CanDelete %> - <% _t('DELETE', 'Delete this field') %> - <% else %> - <% _t('REQUIRED', 'This field is required for this form and cannot be deleted') %> - <% end_if %> - <% end_if %> -
    - -
    -
    -
      - <% if isReadonly %> - <% control Options %> - $ReadonlyOption - <% end_control %> - <% else %> - <% control Options %> - $Option - <% end_control %> -
    • - - <% _t('ADD', 'Add new option') %> -
    • - <% end_if %> -
    -
    - <% control ExtraOptions %> - $FieldHolder - <% end_control %> -
    - - - - -
    \ No newline at end of file diff --git a/templates/EditableRadioOption.ss b/templates/EditableRadioOption.ss deleted file mode 100755 index 70865a9..0000000 --- a/templates/EditableRadioOption.ss +++ /dev/null @@ -1,10 +0,0 @@ -
  • - <% _t('DRAG', 'Drag to rearrange order of options') %> - - - <% if isReadonly %> - <% _t('DELETE', 'Remove this option') %> - <% else %> - <% _t('LOCKED', 'These fields cannot be modified') %> - <% end_if %> -
  • \ No newline at end of file diff --git a/templates/EditableTextField.ss b/templates/EditableTextField.ss deleted file mode 100755 index b9f59bd..0000000 --- a/templates/EditableTextField.ss +++ /dev/null @@ -1,20 +0,0 @@ -
    -
    - <% _t('DRAG', 'Drag to rearrange order of fields') %> - <% _t('TEXTFIELD', 'Text Field') %> - $TitleField - <% _t('MORE', 'More options') %> - <% _t('DELETE', 'Delete this field') %> -
    -
    -
    - $DefaultField -
    - <% control ExtraOptions %> - $FieldHolder - <% end_control %> -
    - - - -
    \ No newline at end of file diff --git a/tests/UserDefinedFormTest.php b/tests/UserDefinedFormTest.php new file mode 100644 index 0000000..ae92f4a --- /dev/null +++ b/tests/UserDefinedFormTest.php @@ -0,0 +1,28 @@ +Name = "$field"; + $object->Title = "$field"; + $object->write(); + + $this->assertEquals($field, $object->Name); + } + } +} +?> \ No newline at end of file