diff --git a/_config.php b/_config.php new file mode 100644 index 0000000..8c53a52 --- /dev/null +++ b/_config.php @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/code/UserDefinedForm.php b/code/UserDefinedForm.php new file mode 100755 index 0000000..cd3dfc8 --- /dev/null +++ b/code/UserDefinedForm.php @@ -0,0 +1,341 @@ + "Varchar", + "EmailOnSubmit" => "Boolean", + "SubmitButtonText" => "Varchar", + "OnCompleteMessage" => "HTMLText" + ); + + static $defaults = array( + "OnCompleteMessage" => "

Thanks, we've received your submission.

", + ); + + static $has_many = array( + "Fields" => "EditableFormField", + "Submissions" => "SubmittedForm" + ); + + protected $fields; + + function getCMSFields($cms) { + $fields = parent::getCMSFields($cms); + + $fields->addFieldToTab("Root."._t('UserDefinedForm.FORM', 'Form'), new FieldEditor("Fields", 'Fields', "", $this )); + $fields->addFieldToTab("Root."._t('UserDefinedForm.SUBMISSIONS','Submissions'), new SubmittedFormReportField( "Reports", _t('UserDefinedForm.RECEIVED', 'Received Submissions'), "", $this ) ); + $fields->addFieldToTab("Root.Content."._t('UserDefinedForm.ONCOMPLETE','On complete'), new HtmlEditorField( "OnCompleteMessage", _t('UserDefinedForm.ONCOMPLETELABEL', 'Show on completion'),3,"",_t('UserDefinedForm.ONCOMPLETEMESSAGE', $this->OnCompleteMessage), $this ) ); + + return $fields; + } + + function FilterForm() { + // Build fields + $fields = new FieldSet(); + $required = array(); + + foreach( $this->Fields() as $field ) { + $fields->push( $field->getFilterField() ); + } + + // Build actions + $actions = new FieldSet( + new FormAction( "filter", _t('UserDefinedForm.SUBMIT', 'Submit') ) + ); + + // set the name of the form + return new Form( $this, "Form", $fields, $actions ); + } + + /** + * Filter the submissions by the given criteria + */ + function filter( $data, $form ) { + + $filterClause = array( "`SubmittedForm`.`ParentID` = '{$this->ID}'" ); + + $keywords = preg_split( '/\s+/', $data['FilterKeyword'] ); + + $keywordClauses = array(); + + // combine all keywords into one clause + foreach( $keywords as $keyword ) { + + // escape %, \ and _ in the keyword. These have special meanings in a LIKE string + $keyword = preg_replace( '/([%_])/', '\\\\1', addslashes( $keyword ) ); + + $keywordClauses[] = "`Value` LIKE '%$keyword%'"; + } + + if( count( $keywordClauses ) > 0 ) { + $filterClause[] = "( " . implode( ' OR ', $keywordClauses ) . ")"; + $searchQuery = 'keywords \'' . implode( "', '", $keywords ) . '\' '; + } + + $fromDate = addslashes( $data['FilterFromDate'] ); + $toDate = addslashes( $data['FilterToDate'] ); + + // use date objects to convert date to value expected by database + if( ereg('^([0-9]+)/([0-9]+)/([0-9]+)$', $fromDate, $parts) ) + $fromDate = $parts[3] . '-' . $parts[2] . '-' . $parts[1]; + + if( ereg('^([0-9]+)/([0-9]+)/([0-9]+)$', $toDate, $parts) ) + $toDate = $parts[3] . '-' . $parts[2] . '-' . $parts[1]; + + if( $fromDate ) { + $filterClause[] = "`SubmittedForm`.`Created` >= '$fromDate'"; + $searchQuery .= 'from ' . $fromDate . ' '; + } + + if( $toDate ) { + $filterClause[] = "`SubmittedForm`.`Created` <= '$toDate'"; + $searchQuery .= 'to ' . $toDate; + } + + $submittedValues = DataObject::get( 'SubmittedFormField', implode( ' AND ', $filterClause ), "", "INNER JOIN `SubmittedForm` ON `SubmittedFormField`.`ParentID`=`SubmittedForm`.`ID`" ); + + if( !$submittedValues || $submittedValues->Count() == 0 ) + return _t('UserDefinedForm.NORESULTS', 'No matching results found'); + + $submissions = $submittedValues->groupWithParents( 'ParentID', 'SubmittedForm' ); + + if( !$submissions || $submissions->Count() == 0 ) + return _t('UserDefinedForm.NORESULTS', 'No matching results found'); + + return $submissions->customise( + array( 'Submissions' => $submissions ) + )->renderWith( 'SubmittedFormReportField_Reports' ); + } + + function ReportFilterForm() { + return new SubmittedFormReportField_FilterForm( $this, 'ReportFilterForm' ); + } + + function delete() { + // remove all the fields associated with this page + foreach( $this->Fields() as $field ) + $field->delete(); + + parent::delete(); + } + + public function customFormActions( $isReadonly = false ) { + return new FieldSet( new TextField( "SubmitButtonText", _t('UserDefinedForm.TEXTONSUBMIT', 'Text on submit button:'), $this->SubmitButtonText ) ); + } + + /** + * Duplicate this UserDefinedForm page, and its form fields. + * Submissions, on the other hand, won't be duplicated. + */ + public function duplicate() { + $page = parent::duplicate(); + foreach($this->Fields() as $field) { + $newField = $field->duplicate(); + $newField->ParentID = $page->ID; + $newField->write(); + } + return $page; + } +} + +/** + * Controller for the {@link UserDefinedForm} page type. + * @package cms + * @subpackage pagetypes + */ +class UserDefinedForm_Controller extends Page_Controller { + + function init() { + Requirements::javascript(THIRDPARTY_DIR . 'jsparty/prototype-safe.js'); + Requirements::javascript(THIRDPARTY_DIR . 'jsparty/behaviour.js'); + + parent::init(); + } + + function Form() { + // Build fields + $fields = new FieldSet(); + $required = array(); + + if( !$this->SubmitButtonText ) + $this->SubmitButtonText = 'Submit'; + + foreach( $this->Fields() as $field ) { + $fields->push( $field->getFormField() ); + if( $field->Required ) + $required[] = $field->Name; + } + + if(!isset($_SERVER['HTTP_REFERER'])) { + $_SERVER['HTTP_REFERER'] = ""; + } + + $referer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : ''; + $fields->push( new HiddenField( "Referrer", "", $referer ) ); + + // Build actions + $actions = new FieldSet( + new FormAction( "process", $this->SubmitButtonText ) + ); + + // set the name of the form + $form = new Form( $this, "Form", $fields, $actions, new RequiredFields( $required ) ); + $form->loadDataFrom($this->failover); + return $form; + } + + function ReportFilterForm() { + return new SubmittedFormReportField_FilterForm( $this, 'ReportFilterForm' ); + } + + function process( $data, $form ) { + $submittedForm = new SubmittedForm(); + $submittedForm->SubmittedBy = Member::currentUser(); + $submittedForm->ParentID = $this->ID; + $submittedForm->Recipient = $this->EmailTo; + $submittedForm->write(); + + $values = array(); + $recipientAddresses = array(); + $sendCopy = false; + + $attachments = array(); + + $submittedFields = new DataObjectSet(); + foreach( $this->Fields() as $field ) { + $submittedField = new SubmittedFormField(); + $submittedField->ParentID = $submittedForm->ID; + $submittedField->Name = $field->Name; + $submittedField->Title = $field->Title; + + if( $field->hasMethod( 'getValueFromData' ) ) + $submittedField->Value = $field->getValueFromData( $data ); + else + if(isset($data[$field->Name])) $submittedField->Value = $data[$field->Name]; + + $submittedField->write(); + $submittedFields->push($submittedField); + + if(!empty( $data[$field->Name])){ + // execute the appropriate functionality based on the form field. + switch($field->ClassName){ + + case "EditableEmailField" : + + if($field->SendCopy){ + $recipientAddresses[] = $data[$field->Name]; + $sendCopy = true; + $values[$field->Title] = ''.$data[$field->Name].''; + } + + break; + + case "EditableFileField" : + + // Returns a file type which we attach to the email. + $submittedfile = $field->createSubmittedField($data[$field->Name], $submittedForm); + $file = $submittedfile->UploadedFile(); + + $filename = $file->getFilename(); + + // Attach the file if its less than 1MB, provide a link if its over. + if($file->getAbsoluteSize() < 1024*1024*1){ + $attachments[] = $file; + } + + // Always provide the link if present. + if($file->ID) { + $submittedField->Value = $values[$field->Title] = "Uploaded to: ". Director::absoluteBaseURL(). $filename . ""; + } else { + $submittedField->Value = $values[$field->Title] = ""; + } + + break; + } + + }elseif( $field->hasMethod( 'getValueFromData' ) ) { + $values[$field->Title] = Convert::linkIfMatch($field->getValueFromData( $data )); + + } else { + if(isset($data[$field->Name])) $values[$field->Title] = Convert::linkIfMatch($data[$field->Name]); + } + + } + + if( $this->EmailOnSubmit || $sendCopy ) { + $emailData = array( + "Recipient" => $this->EmailTo, + "Sender" => Member::currentUser(), + "Fields" => $submittedFields, + ); + + $email = new UserDefinedForm_SubmittedFormEmail($submittedFields); + $email->populateTemplate($emailData); + $email->setTo( $this->EmailTo ); + $email->setSubject( $this->Title ); + + // add attachments to email (<1MB) + if($attachments){ + foreach($attachments as $file){ + $email->attachFile($filename,$filename); + } + } + + $email->send(); + + // send to each of email fields + foreach( $recipientAddresses as $addr ) { + $email->setTo( $addr ); + $email->send(); + } + } + + $custom = $this->customise(array( + "Content" => $this->customise( array( 'Link' => $data['Referrer'] ) )->renderWith('ReceivedFormSubmission'), + "Form" => " ", + )); + + return $custom->renderWith('Page'); + } +} + +/** + * Email that gets sent when a submission is made. + * @package cms + * @subpackage pagetypes + */ +class UserDefinedForm_SubmittedFormEmail extends Email { + protected $ss_template = "SubmittedFormEmail"; + protected $from = '$Sender.Email'; + protected $to = '$Recipient.Email'; + protected $subject = 'Submission of form'; + protected $data; + + function __construct($values) { + $this->subject = _t('UserDefinedForm_SubmittedFormEmail.EMAILSUBJECT', 'Submission of form'); + parent::__construct(); + + $this->data = $values; + } + + function Data() { + return $this->data; + } +} + +?> diff --git a/code/editor/EditableButton.php b/code/editor/EditableButton.php new file mode 100755 index 0000000..b03d97d --- /dev/null +++ b/code/editor/EditableButton.php @@ -0,0 +1,14 @@ +'; + } +} +?> \ No newline at end of file diff --git a/code/editor/EditableCheckbox.php b/code/editor/EditableCheckbox.php new file mode 100755 index 0000000..27df716 --- /dev/null +++ b/code/editor/EditableCheckbox.php @@ -0,0 +1,45 @@ + "Boolean" + ); + + static $singular_name = 'Checkbox'; + static $plural_name = 'Checkboxes'; + + function CheckboxField() { + $checkbox = new CheckboxField("Fields[".$this->ID."][Default]", "Checked by default", $this->getField('Default')); + + if( $this->readonly ) + $checkbox = $checkbox->performReadonlyTransformation(); + + return $checkbox->FieldHolder(); + } + + function populateFromPostData( $data ) { + $this->setField('Checked', isset($data['Checked']) ? $data['Checked'] : null); + parent::populateFromPostData( $data ); + } + + function getFormField() { + return new CheckboxField( $this->Name, $this->Title, $this->getField('Default') ); + } + + function getFilterField() { + return new OptionsetField( $this->Name, + $this->Title, + array( '-1' => '('._t('EditableCheckbox.ANY', 'Any').')', + 'on' => _t('EditableCheckbox.SELECTED', 'Selected'), + '0' => _t('EditableCheckbox.NOTSELECTED', 'Not selected') ) + ); + } +} +?> \ No newline at end of file diff --git a/code/editor/EditableCheckboxGroupField.php b/code/editor/EditableCheckboxGroupField.php new file mode 100755 index 0000000..21bcb51 --- /dev/null +++ b/code/editor/EditableCheckboxGroupField.php @@ -0,0 +1,156 @@ +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'] ); + + + // store default, etc + foreach( $fieldSet as $option ) { + if( $deletedOptions && array_search( $option->ID, $deletedOptions ) !== false ) { + $option->delete(); + continue; + } + + if( $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; + + foreach( $this->Options() as $option ) { + if( $option->getField('Default') ) + return $defaultOption; + else + $defaultOption++; + } + + return -1; + } + + function getFormField() { + return $this->createField(); + } + + function getFilterField() { + return $this->createField( true ); + } + + function createField( $asFilter = false ) { + $optionSet = $this->Options(); + $options = array(); + + if( $asFilter ) + $options['-1'] = '(Any)'; + + $defaultOption = '-1'; + + /*foreach( $optionSet as $option ) { + $options[$option->Title] = $option->Title; + }*/ + + // return radiofields + $checkboxSet = new CheckboxSetField( $this->Name, $this->Title, $optionSet, $optionSet ); + + return $checkboxSet; + } + + function getValueFromData($data) { + if(empty($data[$this->Name])) { + return ""; + } + + $entries = $data[$this->Name]; + + if(!is_array($data[$this->Name])) { + $entries = array($data[$this->Name]); + } + + $selectedOptions = DataObject::get('EditableCheckboxOption', "ParentID={$this->ID} AND ID IN (" . implode(',', $entries) . ")"); + foreach($selectedOptions as $selected) { + if(!$result) { + $result = $selected->Title; + } else { + $result .= "," . $selected->Title; + } + } + + 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 new file mode 100755 index 0000000..0b5eda7 --- /dev/null +++ b/code/editor/EditableCheckboxOption.php @@ -0,0 +1,76 @@ + "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']; + $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\""; + + if( $this->getField('Default') ) + $default = " checked=\"checked\""; + + 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 new file mode 100755 index 0000000..ae21e3a --- /dev/null +++ b/code/editor/EditableDateField.php @@ -0,0 +1,49 @@ +ID}][Default]", "", $this->getField('Default') ); + + if( $this->readonly ) + $dmyField = $dmyField->performReadonlyTransformation(); + + return $dmyField; + } + + function populateFromPostData( $data ) { + /*if( !empty( $data['Default'] ) && !preg_match( '/^\d{4}-\d{2}-\d{2}$/', $data['Default'] ) ) { + if( empty( $data['Year'] ) || !is_numeric( $data['Year'] ) ) $data['Year'] = '2001'; + if( empty( $data['Month'] ) || !is_numeric( $data['Month'] ) ) $data['Month'] = '01'; + if( empty( $data['Day'] ) || !is_numeric( $data['Day'] ) ) $data['Day'] = '01'; + + // unset( $data['Default'] ); + $data['Default'] = $data['Year'] . '-' . $data['Month'] . '-' . $data['Day']; + }*/ + + /*echo "ERROR:"; + Debug::show( $data ); + die();*/ + + $fieldPrefix = 'Default-'; + + if( empty( $data['Default'] ) && !empty( $data[$fieldPrefix.'Year'] ) && !empty( $data[$fieldPrefix.'Month'] ) && !empty( $data[$fieldPrefix.'Day'] ) ) + $data['Default'] = $data['Year'] . '-' . $data['Month'] . '-' . $data['Day']; + + // Debug::show( $data ); + + parent::populateFromPostData( $data ); + } + + function getFormField() { + return new CalendarDateField( $this->Name, $this->Title, $this->getField('Default') ); + } +} +?> \ No newline at end of file diff --git a/code/editor/EditableDropdown.php b/code/editor/EditableDropdown.php new file mode 100755 index 0000000..c0d3f67 --- /dev/null +++ b/code/editor/EditableDropdown.php @@ -0,0 +1,127 @@ + "EditableDropdownOption" + ); + + 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($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 ) { + $optionSet = $this->Options(); + $options = array(); + + if( $asFilter ) + $options['-1'] = "(Any)"; + + $defaultOption = '-1'; + + foreach( $optionSet as $option ) { + $options[$option->Title] = $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 new file mode 100755 index 0000000..7d1bfd0 --- /dev/null +++ b/code/editor/EditableDropdownOption.php @@ -0,0 +1,77 @@ +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 new file mode 100755 index 0000000..737c622 --- /dev/null +++ b/code/editor/EditableEmailField.php @@ -0,0 +1,53 @@ + "Boolean" + ); + + static $singular_name = 'Email field'; + static $plural_name = 'Email fields'; + + function populateFromPostData( $data ) { + $this->SendCopy = !empty( $data['SendCopy'] ) ? "1" : "0"; + + parent::populateFromPostData( $data ); + } + + function ExtraOptions() { + $baseName = "Fields[$this->ID]"; + + $extraFields = new FieldSet( + new CheckboxField( $baseName . "[SendCopy]", _t('EditableEmailField.SENDCOPY', 'Send copy of submission to this address'), $this->SendCopy ) + ); + + foreach( parent::ExtraOptions() as $extraField ) + $extraFields->push( $extraField ); + + if( $this->readonly ) + $extraFields = $extraFields->makeReadonly(); + + return $extraFields; + } + + function getFormField() { + return new EmailField( $this->Name, $this->Title, $this->getField('Default') ); + } + + function getFilterField() { + return $this->createField( true ); + } + + function DefaultField() { + $disabled = ($this->readonly) ? " disabled=\"disabled\"" : ''; + + return ''; + } +} +?> \ No newline at end of file diff --git a/code/editor/EditableFileField.php b/code/editor/EditableFileField.php new file mode 100755 index 0000000..7283387 --- /dev/null +++ b/code/editor/EditableFileField.php @@ -0,0 +1,65 @@ + "File" + ); + + /** + * @see {Upload->allowedMaxFileSize} + * @var int + */ + public static $allowed_max_file_size; + + /** + * @see {Upload->allowedExtensions} + * @var array + */ + public static $allowed_extensions = array(); + + static $singular_name = 'File field'; + static $plural_names = 'File fields'; + + function getFormField() { + if($field = parent::getFormField()) + return $field; + return new FileField($this->Name, $this->Title, $this->getField('Default')); + // TODO We can't use the preview feature because FileIFrameField also shows the "From the file store" functionality + //return new FileIFrameField( $this->Name, $this->Title, $this->getField('Default') ); + } + + function getSimpleFormField(){ + return new FileField($this->Name, $this->Title, $this->getField('Default')); + } + + function createSubmittedField($data, $submittedForm, $fieldClass = "SubmittedFileField") { + if(!$_FILES[$this->Name]) + return null; + + $submittedField = new $fieldClass(); + $submittedField->Title = $this->Title; + $submittedField->Name = $this->Name; + $submittedField->ParentID = $submittedForm->ID; + + // create the file from post data + $upload = new Upload(); + $upload->setAllowedExtensions(self::$allowed_extensions); + $upload->setAllowedMaxFileSize(self::$allowed_max_file_size); + + // upload file + $upload->load($_FILES[$this->Name]); + + $uploadedFile = $upload->getFile(); + $submittedField->UploadedFileID = $uploadedFile->ID; + $submittedField->write(); + + return $submittedField; + } +} +?> \ No newline at end of file diff --git a/code/editor/EditableFormField.php b/code/editor/EditableFormField.php new file mode 100755 index 0000000..b23b682 --- /dev/null +++ b/code/editor/EditableFormField.php @@ -0,0 +1,213 @@ + "Varchar", + "Title" => "Varchar(255)", + "Default" => "Varchar", + "Sort" => "Int", + "Required" => "Boolean", + "CanDelete" => "Boolean", + "CustomParameter" => "Varchar" + ); + + static $defaults = array( + "CanDelete" => "1" + ); + + static $has_one = array( + "Parent" => "SiteTree", + ); + + protected $readonly; + + protected $editor; + + function setEditor( $editor ) { + $this->editor = $editor; + } + + function __construct( $record = null, $isSingleton = false ) { + $this->setField('Default', -1); + parent::__construct( $record, $isSingleton ); + } + + function EditSegment() { + return $this->renderWith('EditableFormField'); + } + + function isReadonly() { + return $this->readonly; + } + + function ClassName() { + return $this->class; + } + + function makeReadonly() { + $this->readonly = true; + return $this; + } + + function ReadonlyEditSegment() { + $this->readonly = true; + return $this->EditSegment(); + } + + 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 = ''; + } + + return "ID}][Title]\"$readOnlyAttr />"; + } + + function Name() { + return "Fields[".$this->ID."]"; + } + + /*function getName() { + return "field" . $this->ID; + }*/ + + function populateFromPostData( $data ) { + + $this->Title = $data['Title']; + if(isset($data['Default'])) { + $this->setField('Default', $data['Default']); + } + $this->Sort = isset($data['Sort']) ? $data['Sort'] : null; + $this->CustomParameter = $data['CustomParameter']; + $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(); + } + + function ExtraOptions() { + + $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( 'getExtraOptionsForField' ) ) { + $extraFields = $this->Parent()->getExtraOptionsForField( $this ); + + foreach( $extraFields as $extraField ) + $extraOptions->push( $extraField ); + } + + if( $this->readonly ) + $extraOptions = $extraOptions->makeReadonly(); + + return $extraOptions; + } + + /** + * Return a FormField to appear on the front end + */ + function getFormField() { + } + + function getFilterField() { + + } + + /** + * Return an evaluation appropriate for a filter clause + * @todo: escape the string + */ + function filterClause( $value ) { + // Not filtering on this field + + if( $value == '-1' ) + return ""; + else + return "`{$this->name}` = '$value'"; + } + + function showInReports() { + return true; + } + + function prepopulate( $value ) { + $this->prepopulateFromMap( $this->parsePrepopulateValue( $value ) ); + } + + protected function parsePrepopulateValue( $value ) { + $paramList = explode( ',', $value ); + + $paramMap = array(); + + foreach( $paramList as $param ) { + + if( preg_match( '/([^=]+)=(.+)/', $param, $match ) ) { + if( isset( $paramMap[$match[1]] ) && is_array( $paramMap[$match[1]] ) ) { + $paramMap[$match[1]][] = $match[2]; + } 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 ) ) { + $this->$field = $fieldValue; + // Debug::message( 'Set ' . $field . ':'. $fieldValue ); + } + } + + // exit(); + } + + function Type() { + return $this->class; + } + + function CustomParameter() { + return $this->CustomParameter; + } + /* + function saveInto( DataObject $record ) { + if( + } + */ +} +?> \ No newline at end of file diff --git a/code/editor/EditableFormHeading.php b/code/editor/EditableFormHeading.php new file mode 100755 index 0000000..053f8dd --- /dev/null +++ b/code/editor/EditableFormHeading.php @@ -0,0 +1,21 @@ +Title, 'FormHeading' ); + // return '

' . $this->Title . '

'; + } + + function showInReports() { + return false; + } +} +?> \ No newline at end of file diff --git a/code/editor/EditableMemberListField.php b/code/editor/EditableMemberListField.php new file mode 100644 index 0000000..eeb6f95 --- /dev/null +++ b/code/editor/EditableMemberListField.php @@ -0,0 +1,44 @@ + 'Group' + ); + + static $singular_name = 'Member list field'; + static $plural_name = 'Member list fields'; + + public function DefaultField() { + // return new TreeDropdownField( "Fields[{$this->ID}][GroupID]", 'Group' ); + + $groups = DataObject::get('Group'); + + foreach( $groups as $group ) + $groupArray[$group->ID] = $group->Title; + + return new DropdownField( "Fields[{$this->ID}][GroupID]", 'Group', $groupArray, $this->GroupID ); + } + + public function populateFromPostData( $data ) { + $this->GroupID = $data['GroupID']; + + parent::populateFromPostData( $data ); + } + + function getFormField() { + return new DropdownField( $this->Name, $this->Title, Member::mapInGroups( $this->GroupID ) ); + } + + function getValueFromData( $data ) { + $value = $data[$this->Name]; + + $member = DataObject::get_one('Member', "Member.ID = {$value}"); + return $member->getName(); + } +} +?> \ No newline at end of file diff --git a/code/editor/EditableRadioField.php b/code/editor/EditableRadioField.php new file mode 100755 index 0000000..820464c --- /dev/null +++ b/code/editor/EditableRadioField.php @@ -0,0 +1,187 @@ + "EditableRadioOption" + ); + + 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; + + foreach( $this->Options() as $option ) { + if( $option->getField('Default') ) + return $defaultOption; + else + $defaultOption++; + } + + return -1; + } + + function getFormField() { + return $this->createField(); + } + + function getFilterField() { + return $this->createField( true ); + } + + function createField( $asFilter = false ) { + $optionSet = $this->Options(); + $options = array(); + $defaultOption = ''; + + if( $asFilter ) + $options['-1'] = '(Any)'; + + // $defaultOption = '-1'; + + foreach( $optionSet as $option ) { + $options[$option->Title] = $option->Title; + if( $option->getField('Default') && !$asFilter ) $defaultOption = $option->Title; + } + + // 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 new file mode 100755 index 0000000..2c40464 --- /dev/null +++ b/code/editor/EditableRadioOption.php @@ -0,0 +1,84 @@ +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 new file mode 100755 index 0000000..2fa313f --- /dev/null +++ b/code/editor/EditableTextField.php @@ -0,0 +1,92 @@ + "Int", + "MinLength" => "Int", + "MaxLength" => "Int", + "Rows" => "Int" + ); + + static $singular_name = 'Text field'; + static $plural_name = 'Text fields'; + + function __construct( $record = null, $isSingleton = false ) { + $this->Size = 32; + $this->MinLength = 1; + $this->MaxLength = 32; + $this->Rows = 1; + parent::__construct( $record, $isSingleton ); + } + + function ExtraOptions() { + + // eventually replace hard-coded "Fields"? + $baseName = "Fields[$this->ID]"; + + $extraFields = new FieldSet( + new TextField($baseName . "[Size]", _t('EditableTextField.TEXTBOXLENGTH', 'Length of text box'), (string)$this->Size), + new FieldGroup(_t('EditableTextField.TEXTLENGTH', 'Text length'), + new TextField($baseName . "[MinLength]", "", (string)$this->MinLength), + new TextField($baseName . "[MaxLength]", " - ", (string)$this->MaxLength) + ), + new TextField($baseName . "[Rows]", _t('EditableTextField.NUMBERROWS', 'Number of rows'), (string)$this->Rows) + ); + + foreach( parent::ExtraOptions() as $extraField ) + $extraFields->push( $extraField ); + + if( $this->readonly ) + $extraFields = $extraFields->makeReadonly(); + + return $extraFields; + } + + function populateFromPostData( $data ) { + + $this->Size = !empty( $data['Size'] ) ? $data['Size'] : 32; + $this->MinLength = !empty( $data['MinLength'] ) ? $data['MinLength'] : 1; + $this->MaxLength = !empty( $data['MaxLength'] ) ? $data['MaxLength'] : 32; + $this->Rows = !empty( $data['Rows'] ) ? $data['Rows'] : 1; + parent::populateFromPostData( $data ); + } + + function getFormField() { + return $this->createField(); + } + + function getFilterField() { + return $this->createField( true ); + } + + function createField( $asFilter = false ) { + if( $this->Rows == 1 ) + return new TextField( $this->Name, $this->Title, ( $asFilter ) ? "" : $this->getField('Default'), ( $this->Size && $this->Size > 0 ) ? $this->Size : null ); + else + return new TextareaField( $this->Name, $this->Title, $this->Rows, $this->Size, ( $asFilter ) ? "" : $this->getField('Default') ); + } + + /** + * Populates the default fields. + */ + function DefaultField() { + $disabled = ''; + if( $this->readonly ){ + $disabled = " disabled=\"disabled\""; + } else { + $disabled = ''; + } + if( $this->Rows == 1 ){ + return '
'; + }else{ + return '
'; + } + } +} +?> \ No newline at end of file diff --git a/code/editor/FieldEditor.php b/code/editor/FieldEditor.php new file mode 100755 index 0000000..d837194 --- /dev/null +++ b/code/editor/FieldEditor.php @@ -0,0 +1,199 @@ +readonly; + } + + function performReadonlyTransformation() { + $this->readonly = true; + return $this; + } + + function makeReadonly() { + return $this->performReadonlyTransformation(); + } + + function FieldHolder() { + return $this->renderWith("FieldEditor"); + } + + function Fields() { + Requirements::css(SAPPHIRE_DIR . "/css/FieldEditor.css"); + Requirements::javascript(SAPPHIRE_DIR . "/javascript/FieldEditor.js"); + + $relationName = $this->name; + + $fields = $this->form->getRecord()->$relationName(); + + if( $this->readonly ) { + $readonlyFields = new DataObjectSet(); + + foreach( $fields as $field ) { + $field->setEditor( $this ); + $readonlyFields->push( $field->makeReadonly() ); + } + + $fields = $readonlyFields; + } + + return $fields; + } + + function saveInto(DataObject $record) { + + $name = $this->name; + $fieldSet = $record->$name(); + + $record->EmailTo = $_REQUEST[$name]['EmailTo']; + $record->EmailOnSubmit = isset( $_REQUEST[$name]['EmailOnSubmit'] ) ? "1" : "0"; + $record->SubmitButtonText = $_REQUEST[$name]['SubmitButtonText']; + + // 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 ){ + $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]; + + // `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(); + } + } + } + + // 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 addNewField( $newField ) { + $newField->ParentID = $this->ID; + $newField->write(); + return $this->renderWith("FieldEditor"); + }*/ + + 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*/ + $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); + } + } + + function adddropdownfield() { + return $this->addNewField( new EditableDropdown() ); + } + + function addcheckboxfield() { + return $this->addNewField( new EditableCheckbox() ); + } + + protected $haveFormOptions = true; + + function setHaveFormOptions($bool){ + $this->haveFormOptions = $bool; + } + + function getHaveFormOptions(){ + return $this->haveFormOptions; + } + + function FormOptions() { + if($this->haveFormOptions){ + $fields = new FieldSet( + new EmailField( "{$this->name}[EmailTo]", _t('FieldEditor.EMAILSUBMISSION', 'Email submission to:'), $this->form->getRecord()->EmailTo ), + new CheckboxField( "{$this->name}[EmailOnSubmit]", _t('FieldEditor.EMAILONSUBMIT', 'Email form on submit:'), $this->form->getRecord()->EmailOnSubmit ) + ); + + if( $this->form->getRecord()->hasMethod( 'customFormActions' ) ) { + $newFields = $this->form->getRecord()->customFormActions( $this->readonly ); + + foreach( $newFields as $newField ) { + $newField->setName( "{$this->name}[{$newField->Name()}]" ); + $fields->push( $newField ); + } + } + if( $this->readonly ) + $fields = $fields->makeReadonly(); + + return $fields; + } + + } +} +?> \ No newline at end of file diff --git a/code/editor/SubmittedFileField.php b/code/editor/SubmittedFileField.php new file mode 100755 index 0000000..90d09f5 --- /dev/null +++ b/code/editor/SubmittedFileField.php @@ -0,0 +1,13 @@ + "File" + ); + +} +?> \ No newline at end of file diff --git a/code/editor/SubmittedForm.php b/code/editor/SubmittedForm.php new file mode 100755 index 0000000..8f9e8ec --- /dev/null +++ b/code/editor/SubmittedForm.php @@ -0,0 +1,24 @@ + "Member", + "Parent" => "UserDefinedForm", + ); + + static $db = array( + "Recipient" => "Varchar" + ); + + static $has_many = array( + "FieldValues" => "SubmittedFormField" + ); + + function SubmitTime() { + return $this->Created; + } +} +?> \ No newline at end of file diff --git a/code/editor/SubmittedFormField.php b/code/editor/SubmittedFormField.php new file mode 100755 index 0000000..8b0cb57 --- /dev/null +++ b/code/editor/SubmittedFormField.php @@ -0,0 +1,18 @@ + "Varchar", + "Value" => "Text", + "Title" => "Varchar" + ); + + static $has_one = array( + "Parent" => "SubmittedForm" + ); +} +?> \ No newline at end of file diff --git a/code/editor/SubmittedFormReportField.php b/code/editor/SubmittedFormReportField.php new file mode 100755 index 0000000..9503c57 --- /dev/null +++ b/code/editor/SubmittedFormReportField.php @@ -0,0 +1,26 @@ +form->Form();*/ + } + + function Field() { + Requirements::css(SAPPHIRE_DIR . "/css/SubmittedFormReportField.css"); + + return $this->renderWith("SubmittedFormReportField"); + } + + function Submissions() { + return $this->form->getRecord()->Submissions(); + } +} +?> \ No newline at end of file diff --git a/css/FieldEditor.css b/css/FieldEditor.css new file mode 100755 index 0000000..98398ab --- /dev/null +++ b/css/FieldEditor.css @@ -0,0 +1,202 @@ +* { + font-family: Arial, Helvetica, sans-serif; + font-size: 10px; +} + +div.FieldEditor { + border: 1px solid #CCCCCC; + padding: 0px; +} + +div.FieldEditor ul.Menu { + margin: 0; + padding: 3px; + background-color: #eee; + position: relative; +} + + div.FieldEditor div.FormOptions div.CompositeField { + margin-left: 4em; + } + + div.FieldEditor div.FormOptions label.left { + position: relative; + } + + div.FieldEditor div.FormOptions label.right, + div.FieldEditor div.FormOptions label.CustomActionLabel { + margin-left: 0em; + float: none; + line-height: auto; + display: inline; + } + + div.FieldEditor input.checkbox { + width: auto !important; + } + + div.FieldEditor ul.Menu li { + background-color: transparent; + } + + div.FieldEditor div.FieldList { + background-color: white; + border-bottom: 1px solid #CCCCCC; + border-top: 1px solid #CCCCCC; + height: 75%; + /*overflow-y: scroll;*/ + } + + div.FieldEditor div.FormOptions { + background-color: #EEEEEE; + position: relative; + float: left; + width:100%; + } + + div.FieldEditor div.FormOptions div.field { + position: relative; + } + + div.FieldEditor div.FormOptions div.optionset li { + display: block; + } + + div.FieldEditor div.FormOptions div.optionset li label { + float: none !important; + } + +.DragEditable { + border: 0; +} + +div.FieldEditor ul li { + display: inline; +} + +div.FieldEditor ul a { + padding: 3px; +} + +div.FieldEditor ul a:hover { + background-color: #CCCCCC; +} + +/********************************************************************* + * EditableFormField - Default class for all editable form fields + ********************************************************************/ + +div.EditableFormField { + padding: 3px; + border-top: 1px dashed #CCCCCC; +} + +div.EditableFormField.mouseOver { + background-color: #f3ef9d; +} + + div.EditableFormField div.hidden { + display: none; + margin: 0px; + } + + div.EditableFormField div.FieldInfo { + margin: 0px; + padding-right: -3px; + } + + div.EditableFormField div.FieldInfo * { + display: inline; + margin-right: 3px; + margin-left: 0px; + vertical-align: middle; + } + + div.EditableFormField div.FieldInfo input, + div.EditableFormField div.FieldDefault input { + width: 250px; + margin-left: 0px; + } + + div.EditableFormField div.ExtraOptions { + display: none; + margin: 3px 0px 3px 57px; + background-color: #EEEEEE; + /* IE has background issues without this */ + /*position: relative;*/ + padding: 3px; + } + +/********************************************************************* + * EditableDateField + ********************************************************************/ + +div.EditableDateField div.FieldDefault input { + width: 2em; +} + + div.EditableDateField div.FieldDefault input.year { + width: 4em; + } + +/********************************************************************* + * EditableEmailField + ********************************************************************/ + +/********************************************************************* + * EditableDropdown + ********************************************************************/ + +#right #Form_EditForm div.EditableMultiOptionFormField div.FieldDefault ul.EditableDropdownOptions { + border: solid 1px #7F9DB9; + /* IE */ + margin: 0px; + /* display: none; */ + display: block; + position: relative; +} + +#right #Form_EditForm div.EditableDropdown select { + margin-bottom: 0px; +} + +#right #Form_EditForm div.EditableDropdown select, +#right #Form_EditForm div.EditableMultiOptionFormField ul.EditableDropdownOptions { + width: 250px; + padding: 3px; +} + + #right #Form_EditForm li.EditableFormFieldOption { + display: block; + padding: 2px 0px; + } + + #right #Form_EditForm li.EditableFormFieldOption * { + vertical-align: middle; + display: inline; + } + + #right #Form_EditForm li.EditableFormFieldOption a { + width: 16px; + } + + #right #Form_EditForm li.EditableFormFieldOption input.text { + width: 170px !important; + } + + #right #Form_EditForm li.EditableFormFieldOption input.radio, + #right #Form_EditForm li.EditableFormFieldOption input.checkbox { + width: 20px; + } + + #right #Form_EditForm li.AddDropdownOption { + border-top: dashed 1px #7F9DB9; + padding-left: 37px; + height: auto; + } + + /* Need first rule in order to overwrite */ + #right #Form_EditForm li.AddDropdownOption input.text { + width: 170px !important; + margin-left: 0px; + } \ No newline at end of file diff --git a/javascript/FieldEditor.js b/javascript/FieldEditor.js new file mode 100755 index 0000000..29cd0e0 --- /dev/null +++ b/javascript/FieldEditor.js @@ -0,0 +1,555 @@ +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++ ) { + 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 ) { + this.getElementsByTagName('div')[0].removeChild( optionToRemove ); + } +} + +FieldEditorField = Class.create(); + +FieldEditorField.prototype = { + initialize: function() { + var fieldInfoDiv = this.findDescendant( 'div', 'FieldInfo' ); + + this.titleField = this.findDescendant( 'input', 'text', element ); + + 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.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/lang/en_US.php b/lang/en_US.php new file mode 100644 index 0000000..15c5adc --- /dev/null +++ b/lang/en_US.php @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/templates/EditableCheckbox.ss b/templates/EditableCheckbox.ss new file mode 100755 index 0000000..1d1bc0a --- /dev/null +++ b/templates/EditableCheckbox.ss @@ -0,0 +1,26 @@ +
+
+ <% 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 new file mode 100755 index 0000000..82bec04 --- /dev/null +++ b/templates/EditableCheckboxGroupField.ss @@ -0,0 +1,51 @@ +
+
+ <% 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 new file mode 100755 index 0000000..70865a9 --- /dev/null +++ b/templates/EditableCheckboxOption.ss @@ -0,0 +1,10 @@ +
  • + <% _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 new file mode 100755 index 0000000..c4e6055 --- /dev/null +++ b/templates/EditableDateField.ss @@ -0,0 +1,20 @@ +
    +
    + <% _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 new file mode 100755 index 0000000..70b4f22 --- /dev/null +++ b/templates/EditableDropdown.ss @@ -0,0 +1,50 @@ +
    +
    + <% 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 new file mode 100755 index 0000000..2bdf47a --- /dev/null +++ b/templates/EditableDropdownOption.ss @@ -0,0 +1,10 @@ +
  • + <% _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 new file mode 100755 index 0000000..716c275 --- /dev/null +++ b/templates/EditableEmailField.ss @@ -0,0 +1,25 @@ +
    +
    + <% _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 new file mode 100755 index 0000000..aae08dc --- /dev/null +++ b/templates/EditableFileField.ss @@ -0,0 +1,20 @@ +
    +
    + <% _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 new file mode 100755 index 0000000..6837b6c --- /dev/null +++ b/templates/EditableFormField.ss @@ -0,0 +1,44 @@ +
    +
    + <% if isReadonly %> + <% _t('LOCKED', 'These fields cannot be modified') %> + <% else %> + <% _t('DRAG', 'Drag to rearrange order of fields') %> + <% end_if %> + $ClassName + $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 Options %> + + <% end_if %> +
    +
    + $DefaultField +
    + <% control ExtraOptions %> + $FieldHolder + <% end_control %> +
    + + + + +
    \ No newline at end of file diff --git a/templates/EditableFormFieldOption.ss b/templates/EditableFormFieldOption.ss new file mode 100755 index 0000000..4ec8971 --- /dev/null +++ b/templates/EditableFormFieldOption.ss @@ -0,0 +1,15 @@ +
  • + <% 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 new file mode 100755 index 0000000..62319df --- /dev/null +++ b/templates/EditableFormHeading.ss @@ -0,0 +1,17 @@ +
    +
    + <% _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/EditableRadioField.ss b/templates/EditableRadioField.ss new file mode 100755 index 0000000..26c5c55 --- /dev/null +++ b/templates/EditableRadioField.ss @@ -0,0 +1,51 @@ +
    +
    + <% 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 new file mode 100755 index 0000000..70865a9 --- /dev/null +++ b/templates/EditableRadioOption.ss @@ -0,0 +1,10 @@ +
  • + <% _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 new file mode 100755 index 0000000..26177db --- /dev/null +++ b/templates/EditableTextField.ss @@ -0,0 +1,20 @@ +
    +
    + <% _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/templates/FieldEditor.ss b/templates/FieldEditor.ss new file mode 100755 index 0000000..06b365f --- /dev/null +++ b/templates/FieldEditor.ss @@ -0,0 +1,81 @@ +
    + +
    + <% control Fields %> + <% if isReadonly %> + $ReadonlyEditSegment + <% else %> + $EditSegment + <% end_if %> + <% end_control %> +
    + +
    + <% control FormOptions %> + $FieldHolder + <% end_control %> +
    \ No newline at end of file diff --git a/templates/SubmittedFormReportField.ss b/templates/SubmittedFormReportField.ss new file mode 100644 index 0000000..9bcde60 --- /dev/null +++ b/templates/SubmittedFormReportField.ss @@ -0,0 +1,18 @@ +
    + $FilterForm +
    +
    +<% control Submissions %> +
    + + + <% control FieldValues %> + + + + + <% end_control %> +
    $Title$Value
    +
    +<% end_control %> +
    \ No newline at end of file diff --git a/templates/email/SubmittedFormEmail.ss b/templates/email/SubmittedFormEmail.ss new file mode 100755 index 0000000..40470c3 --- /dev/null +++ b/templates/email/SubmittedFormEmail.ss @@ -0,0 +1,21 @@ + + + + +

    $Subject

    +

    + <% _t('SUBMITTED','The following data was submitted to your website:') %> + +

    + $Body + + + <% control Fields %> + + + + + <% end_control %> +
    $Title$Value
    + +