diff --git a/.travis.yml b/.travis.yml index 640a63d..9acf8e4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,9 @@ # See https://github.com/silverstripe-labs/silverstripe-travis-support for setup details -language: php +language: php + +sudo: false + php: - 5.4 diff --git a/_config.php b/_config.php index f6092e9..10cdf25 100644 --- a/_config.php +++ b/_config.php @@ -3,3 +3,5 @@ if(!defined('USERFORMS_DIR')) { define('USERFORMS_DIR', basename(__DIR__)); } + +Deprecation::notification_version('3.0', 'userforms'); diff --git a/_config/userforms.yml b/_config/userforms.yml deleted file mode 100644 index 7445bea..0000000 --- a/_config/userforms.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: userforms ---- -LeftAndMain: - extra_requirements_javascript: - - userforms/javascript/UserForm.js - - extra_requirements_css: - - userforms/css/FieldEditor.css diff --git a/code/extensions/UserFormFieldEditorExtension.php b/code/extensions/UserFormFieldEditorExtension.php new file mode 100644 index 0000000..08efb2a --- /dev/null +++ b/code/extensions/UserFormFieldEditorExtension.php @@ -0,0 +1,186 @@ + 'EditableFormField' + ); + + /** + * Adds the field editor to the page. + * + * @return FieldList + */ + public function updateCMSFields(FieldList $fields) { + $fieldEditor = $this->getFieldEditorGrid(); + + $fields->insertAfter(new Tab('FormFields', _t('UserFormFieldEditorExtension.FORMFIELDS', 'Form Fields')), 'Main'); + $fields->addFieldToTab('Root.FormFields', $fieldEditor); + + return $fields; + } + + /** + * Gets the field editor, for adding and removing EditableFormFields. + * + * @return GridField + */ + public function getFieldEditorGrid() { + $fields = $this->owner->Fields(); + + $editableColumns = new GridFieldEditableColumns(); + $editableColumns->setDisplayFields(array( + 'ClassName' => function($record, $column, $grid) { + return DropdownField::create($column, '', $this->getEditableFieldClasses()); + }, + 'Title' => function($record, $column, $grid) { + return TextField::create($column, ' ') + ->setAttribute('placeholder', _t('UserDefinedForm.TITLE', 'Title')); + } + )); + + $fieldEditor = GridField::create( + 'Fields', + _t('UserDefinedForm.FIELDS', 'Fields'), + $fields, + GridFieldConfig::create() + ->addComponents( + $editableColumns, + new GridFieldButtonRow(), + new GridFieldAddNewInlineButton(), + new GridFieldEditButton(), + new GridFieldDeleteAction(), + new GridFieldToolbarHeader(), + new GridFieldOrderableRows('Sort'), + new GridState_Component(), + new GridFieldDetailForm() + ) + ); + + return $fieldEditor; + } + + /** + * @return array + */ + public function getEditableFieldClasses() { + $classes = ClassInfo::getValidSubClasses('EditableFormField'); + + // Remove classes we don't want to display in the dropdown. + $classes = array_diff($classes, array( + 'EditableFormField', + 'EditableMultipleOptionField' + )); + + $editableFieldClasses = array(); + + foreach ($classes as $key => $className) { + $singleton = singleton($className); + + if(!$singleton->canCreate()) { + continue; + } + + $editableFieldClasses[$className] = $singleton->i18n_singular_name(); + } + + return $editableFieldClasses; + } + + /** + * @see SiteTree::doPublish + * @param Page $original + * + * @return void + */ + public function onAfterPublish($original) { + // Remove fields on the live table which could have been orphaned. + $live = Versioned::get_by_stage("EditableFormField", "Live") + ->filter('ParentID', $original->ID); + + if($live) { + foreach($live as $field) { + $field->doDeleteFromStage('Live'); + } + } + + foreach($this->owner->Fields() as $field) { + $field->doPublish('Stage', 'Live'); + } + } + + /** + * @see SiteTree::doUnpublish + * @param Page $page + * + * @return void + */ + public function onAfterUnpublish($page) { + foreach($page->Fields() as $field) { + $field->doDeleteFromStage('Live'); + } + } + + /** + * @see SiteTree::duplicate + * @param DataObject $newPage + * + * @return DataObject + */ + public function onAfterDuplicate($newPage) { + foreach($this->owner->Fields() as $field) { + $newField = $field->duplicate(false); + $newField->ParentID = $newPage->ID; + $newField->ParentClass = $newPage->ClassName; + $newField->Version = 0; + $newField->write(); + + foreach ($field->DisplayRules() as $customRule) { + $newRule = $customRule->duplicate(false); + $newRule->ParentID = $newField->ID; + $newRule->Version = 0; + $newRule->write(); + } + } + + return $newPage; + } + + /** + * @see SiteTree::getIsModifiedOnStage + * @param boolean $isModified + * + * @return boolean + */ + public function getIsModifiedOnStage($isModified) { + if(!$isModified) { + foreach($this->owner->Fields() as $field) { + if($field->getIsModifiedOnStage()) { + $isModified = true; + break; + } + } + } + + return $isModified; + } + + /** + * @see SiteTree::doRevertToLive + * @param Page $page + * + * @return void + */ + public function onAfterRevertToLive($page) { + foreach($page->Fields() as $field) { + $field->publish('Live', 'Stage', false); + $field->writeWithoutVersion(); + } + } +} diff --git a/code/formfields/FieldEditor.php b/code/formfields/FieldEditor.php deleted file mode 100755 index d0dbefb..0000000 --- a/code/formfields/FieldEditor.php +++ /dev/null @@ -1,338 +0,0 @@ - '$Action' - ); - - private static $allowed_actions = array( - 'addfield', - 'addoptionfield', - 'handleField' - ); - - /** - * @param array $properties - * - * @return HTML - */ - public function FieldHolder($properties = array()) { - $add = Controller::join_links($this->Link('addfield')); - - $this->setAttribute('data-add-url', '\''. $add.'\''); - - return $this->renderWith("FieldEditor"); - } - - /** - * Returns whether a user can edit the form. - * - * @param Member $member - * - * @return boolean - */ - public function canEdit($member = null) { - if($this->readonly) { - return false; - } - - return $this->form->getRecord()->canEdit(); - } - - /** - * Returns whether a user delete a field in the form. The - * {@link EditableFormField} instances check if they can delete themselves - * but this counts as an {@link self::canEdit()} function rather than a - * delete. - * - * @param Member $member - * @return boolean - */ - public function canDelete($member = null) { - if($this->readonly) return false; - - return $this->form->getRecord()->canEdit(); - } - - /** - * Transform this form field to a readonly version. - * - * @return ViewableData_Customised - */ - public function performReadonlyTransformation() { - $clone = clone $this; - $clone->readonly = true; - $fields = $clone->Fields(); - - if($fields) { - foreach($fields as $field) { - $field->setReadonly(); - } - } - - return $clone->customise(array( - 'Fields' => $fields - )); - } - - /** - * Return the fields. - * - * @return RelationList - */ - public function Fields() { - if($this->form && $this->form->getRecord() && $this->name) { - $relationName = $this->name; - $fields = $this->form->getRecord()->getComponents($relationName); - - if($fields) { - foreach($fields as $field) { - if(!$this->canEdit() && is_a($field, 'FormField')) { - $fields->remove($field); - $fields->push($field->performReadonlyTransformation()); - } - } - } - - return $fields; - } - } - - /** - * Return a {@link ArrayList} of all the addable fields to populate the add - * field menu. - * - * @return ArrayList - */ - public function CreatableFields() { - $fields = ClassInfo::subclassesFor('EditableFormField'); - - if($fields) { - array_shift($fields); // get rid of subclass 0 - asort($fields); // get in order - - $output = new ArrayList(); - - foreach($fields as $field => $title) { - // get the nice title and strip out field - $niceTitle = _t( - $field.'.SINGULARNAME', - $title - ); - - if($niceTitle && $field != "EditableMultipleOptionField") { - $output->push(new ArrayData(array( - 'ClassName' => $field, - 'Title' => "$niceTitle" - ))); - } - } - - return $output; - } - - return false; - } - - /** - * Handles saving the page. Needs to keep an eye on fields and options which - * have been removed / added - * - * @param DataObject $record - */ - public function saveInto(DataObjectInterface $record) { - $name = $this->name; - $fieldSet = $record->$name(); - - // 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; - } - - if(isset($_REQUEST[$name]) && is_array($_REQUEST[$name])) { - foreach($_REQUEST[$name] as $newEditableID => $newEditableData) { - if(!is_numeric($newEditableID)) continue; - - // get it from the db - $editable = DataObject::get_by_id('EditableFormField', $newEditableID); - - // if it exists in the db update it - if($editable) { - - // remove it from the removed fields list - if(isset($missingFields[$editable->ID]) && isset($newEditableData) && is_array($newEditableData)) { - unset($missingFields[$editable->ID]); - } - - // set form id - if($editable->ParentID == 0) { - $editable->ParentID = $record->ID; - } - - // save data - $editable->populateFromPostData($newEditableData); - } - } - } - - // remove the fields not saved - if($this->canEdit()) { - foreach($missingFields as $removedField) { - if(is_numeric($removedField->ID)) { - // check we can edit this - $removedField->delete(); - } - } - } - } - - /** - * Add a field to the field editor. Called via a ajax get. - * - * @return bool|html - */ - public function addfield() { - if(!SecurityToken::inst()->checkRequest($this->request)) { - return $this->httpError(400); - } - - // get the last field in this form editor - $parentID = $this->form->getRecord()->ID; - - if($parentID) { - $parentID = (int)$parentID; - - $sqlQuery = new SQLQuery(); - $sqlQuery = $sqlQuery - ->setSelect("MAX(\"Sort\")") - ->setFrom("\"EditableFormField\"") - ->setWhere("\"ParentID\" = $parentID"); - - $sort = $sqlQuery->execute()->value() + 1; - - $className = (isset($_REQUEST['Type'])) ? $_REQUEST['Type'] : ''; - - if(!$className) { - // A possible reason for the classname being blank is because we have execeded - // the number of input requests - // http://www.php.net/manual/en/info.configuration.php#ini.max-input-vars - $maxRequests = ini_get('max_input_vars'); - $numRequests = count($_REQUEST, COUNT_RECURSIVE); - if ($numRequests > $maxRequests) { - $error = sprintf('You have exceded the maximum number %s of input requests', - "[$maxRequests]"); - user_error($error, E_USER_WARNING); - } - user_error('Please select a field type to created', E_USER_WARNING); - } - - if(is_subclass_of($className, "EditableFormField")) { - $field = new $className(); - $field->ParentID = $this->form->getRecord()->ID; - $field->Name = $field->class . $field->ID; - $field->Sort = $sort; - $field->write(); - - return $field->EditSegment(); - } - } - - return false; - } - - /** - * Return the html for a field option such as a - * dropdown field or a radio check box field - * - * @return bool|html - */ - public function addoptionfield() { - if(!SecurityToken::inst()->checkRequest($this->request)) { - return $this->httpError(400); - } - - // passed via the ajax - $parent = (isset($_REQUEST['Parent'])) ? $_REQUEST['Parent'] : false; - - // work out the sort by getting the sort of the last field in the form +1 - if($parent) { - $sql_parent = (int)$parent; - - $parentObj = EditableFormField::get()->byID($parent); - $optionClass = ($parentObj && $parentObj->exists()) ? $parentObj->getRelationClass('Options') : 'EditableOption'; - - $sqlQuery = new SQLQuery(); - $sqlQuery = $sqlQuery - ->setSelect("MAX(\"Sort\")") - ->setFrom("\"EditableOption\"") - ->setWhere("\"ParentID\" = $sql_parent"); - - $sort = $sqlQuery->execute()->value() + 1; - - if($parent) { - $object = Injector::inst()->create($optionClass); - $object->write(); - $object->ParentID = $parent; - $object->Sort = $sort; - $object->Name = 'option' . $object->ID; - $object->write(); - - return $object->EditSegment(); - } - } - - return false; - } - - /** - * Pass sub {@link FormField} requests through the editor. For example, - * option fields need to be able to call themselves. - * - * @param SS_HTTPRequest - */ - public function handleField(SS_HTTPRequest $request) { - if(!SecurityToken::inst()->checkRequest($this->request)) { - return $this->httpError(400); - } - - $fields = $this->Fields(); - - // extract the ID and option field name - preg_match( - '/Fields\[(?P\d+)\]\[CustomSettings\]\[(?P"); - $("#Fields_fields").sortable('refresh'); - }, - error: function(e) { - alert(ss.i18n._t('GRIDFIELD.ERRORINTRANSACTION')); - } - }); - } - }); - - /** - * Delete a field from the user defined form - */ - $(".EditableFormField .delete").entwine({ - onclick: function(e) { - e.preventDefault(); - - var text = $(this).parents("li").find(".fieldInfo .text").val(); - - // Remove all the rules with relate to this field - $("#Fields_fields .customRules select.fieldOption option").each(function(i, element) { - if($(element).text() === text) { - // check to see if this is selected. If it is then just remove the whole rule - if($(element).parent('select.customRuleField').val() === $(element).val()) { - $(element).parents('li.customRule').remove(); - } else { - // otherwise remove the option - $(element).remove(); - } - } - }); - $(this).parents(".EditableFormField").slideUp(function(){$(this).remove()}) - } - }); - - /** - * Upon renaming a field we should go through and rename all the - * fields in the select fields to use this new field title. We can - * just worry about the title text - don't mess around with the keys - */ - $('.EditableFormField .fieldInfo .text').entwine({ - onchange: function(e) { - var value = $(this).val(); - var name = $(this).parents("li").attr("id").split(' '); - $("#Fields_fields select.fieldOption option").each(function(i, domElement) { - if($(domElement).val() === name[2]) { - $(domElement).text(value); - } - }); - } - }); - - /** - * Show the more options popdown. Or hide it if we currently have it open - */ - $(".EditableFormField .moreOptions").entwine({ - onclick: function(e) { - e.preventDefault(); - - var parent = $(this).parents(".EditableFormField"); - if(!parent) { - return; - } - - var extraOptions = parent.children(".extraOptions"); - if(!extraOptions) { - return; - } - - if(extraOptions.hasClass('hidden')) { - $(this).addClass("showing"); - $(this).html('Hide options'); - extraOptions.removeClass('hidden'); - } else { - $(this).removeClass("showing"); - $(this).html('Show options'); - extraOptions.addClass('hidden'); - } - } - }); - - /** - * Add a suboption to a radio field or to a dropdown box for example - */ - $(".EditableFormField .addableOption").entwine({ - onclick: function(e) { - e.preventDefault(); - - // Give the user some feedback - statusMessage(userforms.message('ADDING_OPTION')); - - // variables - var options = $(this).parent("li"); - var action = userforms.appendToURL($("#Form_EditForm").attr("action"), '/field/Fields/addoptionfield'); - var parent = $(this).attr("rel"); - var securityID = ($("input[name=SecurityID]").length > 0) ? $("input[name=SecurityID]").first().attr("value") : ''; - - // send ajax request to the page - $.ajax({ - type: "GET", - url: action, - data: 'Parent='+ parent +'&SecurityID='+securityID, - // create a new field - success: function(msg){ - options.before(msg); - statusMessage(userforms.message('ADDED_OPTION')); - }, - - // error creating new field - error: function(request, text, error) { - statusMessage(userforms.message('ERROR_CREATING_OPTION')); - } - }); - - } - }); - - /** - * Delete a suboption such as an dropdown option or a - * checkbox field - */ - $(".EditableFormField .deleteOption").entwine({ - onclick: function(e) { - e.preventDefault(); - - // pass the deleted status onto the element - $(this).parents("li:first").find("[type=text]:first").attr("value", "field-node-deleted"); - $(this).parents("li:first").hide(); - - // Give the user some feedback - statusMessage(userforms.message('REMOVED_OPTION')); - } - }); - - /** - * Custom Rules Interface - * - * Hides the input text field if the conditionOption is 'IsBlank' or 'IsNotBlank' - */ - $("select.conditionOption").entwine({ - onchange: function() { - var valueInput = $(this).siblings(".ruleValue"); - if($(this).val() && $(this).val() !== "IsBlank" && $(this).val() !== "IsNotBlank") { - valueInput.removeClass("hidden"); - } else { - valueInput.addClass("hidden"); - } - } - }); - - /** - * Delete a custom rule - */ - $(".customRules .deleteCondition").entwine({ - onclick: function(e) { - e.preventDefault(); - $(this).parent("li").fadeOut().remove(); - } - }); - - /** - * Adding a custom rule to a given form - */ - $(".customRules .addCondition").entwine({ - onclick: function(e) { - e.preventDefault(); - - // Give the user some feedback - statusMessage(userforms.message('ADDING_RULE')); - - // get the fields li which to duplicate - var currentRules = $(this).parent("li").parent("ul"); - var defaultRule = currentRules.children("li.hidden:first"); - var newRule = defaultRule.clone(); - - newRule.children(".customRuleField").each(function(i, domElement) { - var currentName = domElement.name.split("]["); - currentName[3] = currentName[2]; - currentName[2] = currentRules.children().size() + 1; - domElement.name = currentName.join("]["); - }); - - // remove hidden tag - newRule.removeClass("hidden"); - - // update the fields dropdown - var optionChildren = newRule.children("select.fieldOption"); - optionChildren.empty(); - - $("#Fields_fields li.EditableFormField").each(function () { - var name = $(this).attr("id").split(' '); - var option = $("") - .attr('value', name[2]) - .text($(this).find(".text").val()); - optionChildren - .append(option); - }); - - // append to the list - currentRules.append(newRule); - } - }); - }); - }); -})(jQuery); diff --git a/templates/EditableFormField.ss b/templates/EditableFormField.ss deleted file mode 100755 index 6d21357..0000000 --- a/templates/EditableFormField.ss +++ /dev/null @@ -1,134 +0,0 @@ - -
  • -
    - <% if canEdit %> - <% _t('EditableFormField.DRAG', 'Drag to rearrange order of fields') %> - <% else %> - <% _t('EditableFormField.LOCKED', 'These fields cannot be modified') %> - <% end_if %> - - $ClassName - - $TitleField -
    - -
    - <% if showExtraOptions %> - <% _t('EditableFormField.SHOWOPTIONS','Show Options') %> - <% end_if %> - - <% if canDelete %> - <% _t('EditableFormField.DELETE', 'Delete') %> - <% end_if %> -
    - - <% if showExtraOptions %> - - <% end_if %> - - - - -
  • diff --git a/templates/EditableOption.ss b/templates/EditableOption.ss deleted file mode 100644 index b4881b4..0000000 --- a/templates/EditableOption.ss +++ /dev/null @@ -1,11 +0,0 @@ -
  • - <% _t('EditableOption.DRAG', 'Drag to rearrange order of options') %> - - - - <% if canEdit %> - <% _t('EditableOption.DELETE', 'Remove this option') %> - <% else %> - <% _t('EditableOption.LOCKED', 'These fields cannot be modified') %> - <% end_if %> -
  • \ No newline at end of file diff --git a/templates/FieldEditor.ss b/templates/FieldEditor.ss deleted file mode 100755 index 6805d91..0000000 --- a/templates/FieldEditor.ss +++ /dev/null @@ -1,31 +0,0 @@ -<% require css(userforms/css/FieldEditor.css) %> -<% require javascript(userforms/javascript/UserForm.js) %> - -
    - -
    -
      - <% loop Fields %> - $EditSegment - <% end_loop %> -
    -
    - - <% if canEdit %> - - <% end_if %> - -
    diff --git a/templates/Includes/CustomRule.ss b/templates/Includes/CustomRule.ss deleted file mode 100644 index f5352a7..0000000 --- a/templates/Includes/CustomRule.ss +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - -<% _t('CustomRule.DELETE', 'Delete') %> diff --git a/tests/EditableFormFieldTest.php b/tests/EditableFormFieldTest.php index 17490fe..fe320c2 100644 --- a/tests/EditableFormFieldTest.php +++ b/tests/EditableFormFieldTest.php @@ -7,11 +7,6 @@ class EditableFormFieldTest extends FunctionalTest { static $fixture_file = 'userforms/tests/EditableFormFieldTest.yml'; - - protected $extraDataObjects = array( - 'ExtendedEditableFormFieldTestOnly', - 'EditableFormFieldExtensionTestOnly' - ); function testFormFieldPermissions() { $text = $this->objFromFixture('EditableTextField', 'basic-text'); @@ -41,100 +36,6 @@ class EditableFormFieldTest extends FunctionalTest { $this->assertFalse($text->canDelete()); } - function testGettingAndSettingSettings() { - $text = $this->objFromFixture('EditableTextField', 'basic-text'); - - $this->logInWithPermission('ADMIN'); - - $this->assertEquals($text->getSettings(), array()); - $text->setSetting('Test', 'Value'); - $text->write(); - - $this->assertEquals($text->getSetting('Test'), 'Value'); - $this->assertEquals($text->getSettings(), array('Test' => 'Value')); - - $text->setSetting('Foo', 'Bar'); - $text->write(); - - $this->assertEquals($text->getSetting('Foo'), 'Bar'); - $this->assertEquals($text->getSettings(), array('Test' => 'Value', 'Foo' => 'Bar')); - - // test overridding an existing setting - $text->setSetting('Foo', 'Baz'); - $text->write(); - - $this->assertEquals($text->getSetting('Foo'), 'Baz'); - $this->assertEquals($text->getSettings(), array('Test' => 'Value', 'Foo' => 'Baz')); - } - - function testShowOnLoad() { - $text = $this->objFromFixture('EditableTextField', 'basic-text'); - - $this->logInWithPermission('ADMIN'); - $this->assertTrue($text->getShowOnLoad()); - - $text->setSetting('ShowOnLoad', 'Show'); - $this->assertTrue($text->getShowOnLoad()); - - $text->setSetting('ShowOnLoad', 'Hide'); - $this->assertFalse($text->getShowOnLoad()); - - $text->setSetting('ShowOnLoad', ''); - $this->assertTrue($text->getShowOnLoad()); - } - - - function testPopulateFromPostData() { - $this->logInWithPermission('ADMIN'); - $set = new ArrayList(); - - $field = new EditableFormField(); - - $data = array( - 'Title' => 'Field Title', - 'Default' => 'Default Value', - 'Sort' => '2', - 'Required' => 0, - 'CustomErrorMessage' => 'Custom' - ); - - $field->populateFromPostData($data); - $set->push($field); - $this->assertDOSEquals(array($data), $set); - - // test the custom settings - $data['CustomSettings'] = array( - 'Foo' => 'Bar' - ); - - $checkbox = new EditableCheckbox(); - $checkbox->write(); - - $checkbox->populateFromPostData(array('Title' => 'Checkbox')); - - $field->populateFromPostData($data); - - $this->assertEquals($field->getSettings(), array('Foo' => 'Bar')); - - $rule = array( - 'Display' => 'Hide', - 'ConditionField' => $checkbox->Name, - 'ConditionOption' => 'HasValue', - 'Value' => 6 - ); - - // test the custom rules - $data['CustomRules'] = array( - 'Rule1' => $rule - ); - - $field->populateFromPostData($data); - - $rules = unserialize($field->CustomRules); - - $this->assertEquals($rules[0], $rule); - } - function testCustomRules() { $this->logInWithPermission('ADMIN'); $form = $this->objFromFixture('UserDefinedForm', 'custom-rules-form'); @@ -142,58 +43,18 @@ class EditableFormFieldTest extends FunctionalTest { $checkbox = $form->Fields()->find('ClassName', 'EditableCheckbox'); $field = $form->Fields()->find('ClassName', 'EditableTextField'); - $rule = array( - 'Display' => 'Hide', - 'ConditionField' => $checkbox->Name, - 'ConditionOption' => 'HasValue', - 'Value' => 6 - ); + $rules = $checkbox->DisplayRules(); - $data['CustomRules'] = array( - 'Rule1' => $rule - ); - - $field->populateFromPostData($data); - - $rules = $field->CustomRules(); - // form has 2 fields - a checkbox and a text field // it has 1 rule - when ticked the checkbox hides the text field $this->assertEquals($rules->Count(), 1); - // rules are ArrayDatas not dataobjects - // $this->assertDOSEquals(array($rule), $rules); - $checkboxRule = $rules->First(); + $checkboxRule->ConditionFieldID = $field->ID; + $this->assertEquals($checkboxRule->Display, 'Hide'); - $this->assertEquals($checkboxRule->ConditionField, $checkbox->Name); $this->assertEquals($checkboxRule->ConditionOption, 'HasValue'); - $this->assertEquals($checkboxRule->Value, '6'); - - foreach($checkboxRule->Fields as $condition) { - if($checkbox->Name == $condition->Name) { - $this->assertTrue($condition->isSelected); - } - else { - $this->assertFalse($condition->isSelected); - } - } - - $data['CustomRules'] = array( - 'Rule2' => array( - 'Display' => 'Hide', - 'ConditionField' => $checkbox->Name, - 'ConditionOption' => 'Blank' - ) - ); - - $field->populateFromPostData($data); - - $rules = $field->CustomRules(); - - // test that saving additional rules deletes the old one - $this->assertEquals($rules->Count(), 1); - + $this->assertEquals($checkboxRule->FieldValue, '6'); } function testEditableDropdownField() { @@ -240,15 +101,6 @@ class EditableFormFieldTest extends FunctionalTest { $this->assertEquals($title->Value(), "Basic Text Field"); } - function testGettingFieldAndSettingNames() { - $text = $this->objFromFixture('EditableTextField', 'basic-text'); - - $this->assertEquals($text->getFieldName(), "Fields[". $text->ID ."]"); - $this->assertEquals($text->getFieldName('Setting'), "Fields[". $text->ID ."][Setting]"); - - $this->assertEquals($text->getSettingName('Foo'), "Fields[". $text->ID ."][CustomSettings][Foo]"); - } - function testMultipleOptionDuplication() { $dropdown = $this->objFromFixture('EditableDropdown','basic-dropdown'); @@ -262,70 +114,6 @@ class EditableFormFieldTest extends FunctionalTest { $this->assertEquals($orginal->Sort, $option->Sort); } } - - function testMultipleOptionPopulateFromPostData() { - $dropdown = $this->objFromFixture('EditableDropdown','basic-dropdown'); - - $data = array(); - - foreach($dropdown->Options() as $option) { - $orginal[$option->ID] = array( - 'Title' => $option->Title, - 'Sort' => $option->Sort - ); - - $data[$option->ID] = array( - 'Title' => 'New - '. $option->Title, - 'Sort' => $option->Sort + 1 - ); - } - - $dropdown->populateFromPostData($data); - - $count = $dropdown->Options()->Count(); - - foreach($dropdown->Options() as $option) { - $this->assertEquals($option->Title, 'New - '. $orginal[$option->ID]['Title']); - $this->assertEquals($option->Sort, $orginal[$option->ID]['Sort'] + 1); - } - - // remove the first one. can't assume by ID - foreach($data as $key => $value) { - unset($data[$key]); - break; - } - - $dropdown->populateFromPostData($data); - - $this->assertEquals($dropdown->Options()->Count(), $count-1); - } - - function testEditableTextFieldConfiguration() { -// $text = $this->objFromFixture('EditableTextField', 'basic-text'); - -// $configuration = $text->getFieldConfiguration(); - - } - - function testExtendedEditableFormField() { - /** @var ExtendedEditableFormField $field */ - $field = $this->objFromFixture('ExtendedEditableFormFieldTestOnly', 'extended-field'); - - // Check db fields - $dbFields = $field->stat('db'); - $this->assertTrue(array_key_exists('TestExtraField', $dbFields)); - $this->assertTrue(array_key_exists('TestValidationField', $dbFields)); - - // Check Field Configuration - $fieldConfiguration = $field->getFieldConfiguration(); - $extraField = $fieldConfiguration->dataFieldByName($field->getSettingName('TestExtraField')); - $this->assertNotNull($extraField); - - // Check Validation Fields - $fieldValidation = $field->getFieldValidationOptions(); - $validationField = $fieldValidation->dataFieldByName($field->getSettingName('TestValidationField')); - $this->assertNotNull($validationField); - } public function testFileField() { $fileField = $this->objFromFixture('EditableFileField', 'file-field'); @@ -336,48 +124,3 @@ class EditableFormFieldTest extends FunctionalTest { } } - -/** - * Class ExtendedEditableFormField - * A base EditableFormFieldClass that will be extended with {@link EditableFormFieldExtension} - * @mixin EditableFormFieldExtension - */ -class ExtendedEditableFormFieldTestOnly extends EditableFormField implements TestOnly -{ - private static $extensions = array( - 'EditableFormFieldExtensionTestOnly' - ); -} - -/** - * Class EditableFormFieldExtension - * Used for testing extensions to EditableFormField and the extended Fields methods - * @property EditableFormField owner - */ -class EditableFormFieldExtensionTestOnly extends DataExtension implements TestOnly -{ - private static $db = array( - 'TestExtraField' => 'Varchar', - 'TestValidationField' => 'Boolean' - ); - - public function updateFieldConfiguration(FieldList $fields) - { - $extraField = 'TestExtraField'; - $fields->push(TextField::create( - $this->owner->getSettingName($extraField), - 'Test extra field', - $this->owner->getSetting($extraField) - )); - } - - public function updateFieldValidationOptions(FieldList $fields) - { - $extraField = 'TestValidationField'; - $fields->push(CheckboxField::create( - $this->owner->getSettingName($extraField), - 'Test validation field', - $this->owner->getSetting($extraField) - )); - } -} diff --git a/tests/EditableFormFieldTest.yml b/tests/EditableFormFieldTest.yml index f211d45..1f6df12 100644 --- a/tests/EditableFormFieldTest.yml +++ b/tests/EditableFormFieldTest.yml @@ -1,8 +1,14 @@ +EditableCustomRule: + rule-1: + Display: Hide + ConditionOption: HasValue + FieldValue: 6 + EditableOption: option-1: Name: Option1 Title: Option 1 - + option-2: Name: Option2 Title: Option 2 @@ -30,110 +36,104 @@ EditableOption: option-6: Name: Option6 Title: Option 6 - + UserDefinedForm_EmailRecipient: recipient-1: EmailAddress: test@example.com EmailSubject: Email Subject EmailFrom: no-reply@example.com - + no-html: EmailAddress: nohtml@example.com EmailSubject: Email Subject EmailFrom: no-reply@example.com SendPlain: true - + no-data: EmailAddress: nodata@example.com EmailSubject: Email Subject EmailFrom: no-reply@example.com HideFormData: true - + EditableTextField: basic-text: Name: basic-text-name Title: Basic Text Field - + basic-text-2: Name: basic-text-name Title: Basic Text Field - + required-text: Name: required-text-field Title: Required Text Field CustomErrorMessage: Custom Error Message Required: true - + EditableDropdown: basic-dropdown: Name: basic-dropdown Title: Basic Dropdown Field Options: =>EditableOption.option-1, =>EditableOption.option-2 - + department-dropdown: Name: department Title: Department Options: =>EditableOption.department-1, =>EditableOption.department-2 - + EditableCheckbox: checkbox-1: Name: checkbox-1 Title: Checkbox 1 - + checkbox-2: Name: checkbox-1 Title: Checkbox 1 - + + checkbox-with-rule: + Name: checkbox-with-rule + Title: Checkbox with rule + DisplayRules: =>EditableCustomRule.rule-1 + EditableCheckboxGroupField: checkbox-group: Name: check-box-group Title: Check box group Options: =>EditableOption.option-3, =>EditableOption.option-4 - + EditableEmailField: email-field: Name: email-field Title: Email - EditableRadioField: radio-field: Name: radio-option Title: Radio Option Options: =>EditableOption.option-5, =>EditableOption.option-6 - - + EditableFileField: file-field: Name: file-uploader Title: Set file -ExtendedEditableFormFieldTestOnly: - extended-field: - Name: extended-field - Title: Extended Field - TestExtraField: Extra Field - TestValidationField: Extra Validation Field - UserDefinedForm: basic-form-page: Title: User Defined Form Fields: =>EditableTextField.basic-text EmailRecipients: =>UserDefinedForm_EmailRecipient.recipient-1, =>UserDefinedForm_EmailRecipient.no-html, =>UserDefinedForm_EmailRecipient.no-data - + form-with-reset-and-custom-action: Title: Form with Reset Action SubmitButtonText: Custom Button ShowClearButton: true - + validation-form: Title: Validation Form Fields: =>EditableTextField.required-text - + custom-rules-form: Title: Custom Rules Form - Fields: =>EditableCheckbox.checkbox-2, =>EditableTextField.basic-text-2 + Fields: =>EditableCheckbox.checkbox-with-rule, =>EditableTextField.basic-text-2 empty-form: Title: Empty Form - - diff --git a/tests/FieldEditorTest.php b/tests/FieldEditorTest.php deleted file mode 100644 index d0efda7..0000000 --- a/tests/FieldEditorTest.php +++ /dev/null @@ -1,46 +0,0 @@ -objFromFixture('UserDefinedForm', 'basic-form-page'); - - $controller = new FieldEditorTest_Controller($form); - - $fields = $controller->Form()->Fields(); - - $this->editor = $fields->fieldByName('Fields'); - } - - function testSaveInto() { - $this->logInWithPermission('ADMIN'); - - // @todo - } - - function testAddField() { - $this->logInWithPermission('ADMIN'); - - // Debug::show($this->editor->addfield()); - } -} - -class FieldEditorTest_Controller extends Controller { - - public function Form() { - return new Form($this, 'Form', new FieldList(new FieldEditor('Fields')), new FieldList()); - } -} \ No newline at end of file diff --git a/tests/UserDefinedFormControllerTest.php b/tests/UserDefinedFormControllerTest.php index 6b9f6d0..a56385e 100644 --- a/tests/UserDefinedFormControllerTest.php +++ b/tests/UserDefinedFormControllerTest.php @@ -18,7 +18,7 @@ class UserDefinedFormControllerTest extends FunctionalTest { // load the form $this->get($form->URLSegment); - $response = $this->submitForm('Form_Form', null, array('basic-text-name' => 'Basic Value')); + $response = $this->submitForm('UserForm_Form', null, array('basic-text-name' => 'Basic Value')); // should have a submitted form field now $submitted = DataObject::get('SubmittedFormField', "\"Name\" = 'basic-text-name'"); @@ -106,7 +106,7 @@ class UserDefinedFormControllerTest extends FunctionalTest { $controller = new UserDefinedFormControllerTest_Controller($form); - $fields = $controller->getFormFields(); + $fields = $controller->Form()->getFormFields(); $this->assertEquals($fields->Count(), 1); @@ -116,26 +116,26 @@ class UserDefinedFormControllerTest extends FunctionalTest { UserDefinedForm::config()->required_identifier = "*"; - $fields = $controller->getFormFields(); + $fields = $controller->Form()->getFormFields(); $this->assertEquals($fields->First()->getCustomValidationMessage()->getValue(), 'Custom Error Message'); $this->assertEquals($fields->First()->Title(), 'Required Text Field *'); // test custom right title $field = $form->Fields()->First(); - $field->setSetting('RightTitle', 'Right Title'); + $field->RightTitle = 'Right Title'; $field->write(); $controller = new UserDefinedFormControllerTest_Controller($form); - $fields = $controller->getFormFields(); + $fields = $controller->Form()->getFormFields(); $this->assertEquals($fields->First()->RightTitle(), "Right Title"); // test empty form $emptyForm = $this->objFromFixture('UserDefinedForm', 'empty-form'); $controller = new UserDefinedFormControllerTest_Controller($emptyForm); - - $this->assertFalse($controller->Form()); + + $this->assertFalse($controller->Form()->getFormFields()->exists()); } function testGetFormActions() { @@ -143,7 +143,7 @@ class UserDefinedFormControllerTest extends FunctionalTest { $form = $this->objFromFixture('UserDefinedForm', 'basic-form-page'); $controller = new UserDefinedFormControllerTest_Controller($form); - $actions = $controller->getFormActions(); + $actions = $controller->Form()->getFormActions(); // by default will have 1 submit button which links to process $expected = new FieldList(new FormAction('process', 'Submit')); @@ -153,23 +153,14 @@ class UserDefinedFormControllerTest extends FunctionalTest { // the custom popup should have a reset button and a custom text $custom = $this->objFromFixture('UserDefinedForm', 'form-with-reset-and-custom-action'); $controller = new UserDefinedFormControllerTest_Controller($custom); - - $actions = $controller->getFormActions(); + $actions = $controller->Form()->getFormActions(); $expected = new FieldList(new FormAction('process', 'Custom Button')); $expected->push(new ResetFormAction("clearForm", "Clear")); - + $this->assertEquals($actions, $expected); } - function testArrayToJson() { - $array = array('1' => 'one', '2' => 'two'); - $string = "{\n1:\"one\", 2:\"two\"\n}\n"; - $form = new UserDefinedFormControllerTest_Controller(); - $this->assertEquals($form->array2json($array), $string); - } - - function testRenderingIntoFormTemplate() { $form = $this->setupFormFrontend(); @@ -210,7 +201,7 @@ class UserDefinedFormControllerTest extends FunctionalTest { } function checkTemplateIsCorrect($parser) { - $this->assertArrayHasKey(0, $parser->getBySelector('form#Form_Form')); + $this->assertArrayHasKey(0, $parser->getBySelector('form#UserForm_Form')); // check for the input $this->assertArrayHasKey(0, $parser->getBySelector('input.text')); diff --git a/tests/UserDefinedFormTest.php b/tests/UserDefinedFormTest.php index 082f681..fb7c8fc 100644 --- a/tests/UserDefinedFormTest.php +++ b/tests/UserDefinedFormTest.php @@ -6,7 +6,7 @@ class UserDefinedFormTest extends FunctionalTest { - static $fixture_file = 'userforms/tests/UserDefinedFormTest.yml'; + static $fixture_file = 'UserDefinedFormTest.yml'; function testRollbackToVersion() { diff --git a/tests/UserDefinedFormTest.yml b/tests/UserDefinedFormTest.yml index ef657e0..07d47c9 100644 --- a/tests/UserDefinedFormTest.yml +++ b/tests/UserDefinedFormTest.yml @@ -121,27 +121,22 @@ UserDefinedForm_EmailRecipientCondition: blank-rule: ConditionOption: IsBlank ConditionField: =>EditableTextField.your-name-field - not-blank-rule: ConditionOption: IsNotBlank ConditionField: =>EditableTextField.address-field - equals-rule: ConditionOption: Equals ConditionField: =>EditableTextField.street-field ConditionValue: 'Matches Equals' - not-equals-rule: ConditionOption: NotEquals ConditionField: =>EditableTextField.city-field ConditionValue: 'Matches Not Equals' - # filtered recipient 2 group-equals-rule: ConditionOption: Equals ConditionField: =>EditableCheckboxGroupField.colour-checkbox-group ConditionValue: Red - group-not-equals-rule: ConditionOption: NotEquals ConditionField: =>EditableCheckboxGroupField.colour-checkbox-group diff --git a/tests/UserFormsUpgradeServiceTest.php b/tests/UserFormsUpgradeServiceTest.php new file mode 100644 index 0000000..b95dd4f --- /dev/null +++ b/tests/UserFormsUpgradeServiceTest.php @@ -0,0 +1,216 @@ +update('UserDefinedForm', 'upgrade_on_build', false); + parent::setUp(); + + // Assign rules programatically + $field1 = $this->objFromFixture('EditableTextField', 'text1'); + $field2 = $this->objFromFixture('EditableTextField', 'text2'); + $field3 = $this->objFromFixture('EditableTextField', 'text3'); + + $field3->CustomRules = serialize(array( + array( + 'Display' => 'Show', + 'ConditionField' => $field1->Name, + 'ConditionOption' => 'IsBlank', + 'Value' => '' + ), + array( + 'Display' => 'Hide', + 'ConditionField' => $field2->Name, + 'ConditionOption' => 'HasValue', + 'Value' => 'bob' + ) + )); + $field3->write(); + + // Assign settings programatically + $field4 = $this->objFromFixture('EditableTextField', 'text4'); + $field4->CustomSettings = serialize(array( + 'MinLength' => 20, + 'MaxLength' => 100, + 'Rows' => 4, + 'ExtraClass' => 'special class', + 'RightTitle' => 'My Field', + 'ShowOnLoad' => '', + 'Default' => 'Enter your text here' + )); + $field4->write(); + + $numeric1 = $this->objFromFixture('EditableNumericField', 'numeric1'); + $numeric1->CustomSettings = serialize(array( + 'RightTitle' => 'Number of %', + 'Default' => 1, + 'MinValue' => 1, + 'MaxValue' => 100, + 'ShowOnLoad' => 'Show' + )); + $numeric1->write(); + + $group1 = $this->objFromFixture('Group', 'group1'); + $members1 = $this->objFromFixture('EditableMemberListField', 'members1'); + $members1->CustomSettings = serialize(array( + 'RightTitle' => 'Select group', + 'GroupID' => $group1->ID, + 'ShowOnLoad' => 'Hide' + )); + $members1->write(); + + $literal1 = $this->objFromFixture('EditableLiteralField', 'literal1'); + $literal1->CustomSettings = serialize(array( + 'HideFromReports' => 1, + 'RightTitle' => 'Literal', + 'Content' => '

    Content

    ', + 'ShowOnLoad' => true + )); + $literal1->write(); + + $heading1 = $this->objFromFixture('EditableFormHeading', 'heading1'); + $heading1->CustomSettings = serialize(array( + 'RightTitle' => 'Right', + 'Level' => 3, + 'HideFromReports' => true, + 'ShowOnLoad' => false + )); + $heading1->write(); + + $folder = $this->objFromFixture('Folder', 'folder1'); + $file1 = $this->objFromFixture('EditableFileField', 'file1'); + $file1->CustomSettings = serialize(array( + 'RightTitle' => 'File field', + 'Folder' => $folder->ID + )); + $file1->write(); + + $date1 = $this->objFromFixture('EditableDateField', 'date1'); + $date1->CustomSettings = serialize(array( + 'RightTitle' => 'Date field', + 'DefaultToToday' => '1' + )); + $date1->write(); + + $checkbox1 = $this->objFromFixture('EditableCheckbox', 'checkbox1'); + $checkbox1->CustomSettings = serialize(array( + 'Default' => true, + 'RightTitle' => 'Check this' + )); + $checkbox1->write(); + + } + + /** + * @return UserFormsUpgradeService; + */ + protected function getService() { + return singleton('UserFormsUpgradeService'); + } + + /** + * Tests migration of custom rules + */ + public function testCustomRulesMigration() { + $service = $this->getService(); + $service->setQuiet(true); + $service->run(); + + $field1 = $this->objFromFixture('EditableTextField', 'text1'); + $field2 = $this->objFromFixture('EditableTextField', 'text2'); + $field3 = $this->objFromFixture('EditableTextField', 'text3'); + + $this->assertDOSEquals(array( + array( + 'Display' => 'Show', + 'ConditionFieldID' => $field1->ID, + 'ConditionOption' => 'IsBlank' + ), + array( + 'Display' => 'Hide', + 'ConditionFieldID' => $field2->ID, + 'ConditionOption' => 'HasValue', + 'FieldValue' => 'bob' + ) + ), $field3->DisplayRules()); + } + + /** + * Tests migration of all custom settings + */ + public function testCustomSettingsMigration() { + $service = $this->getService(); + $service->setQuiet(true); + $service->run(); + + $group1 = $this->objFromFixture('Group', 'group1'); + $form = $this->objFromFixture('UserDefinedForm', 'form-with-settings'); + $folder = $this->objFromFixture('Folder', 'folder1'); + + $this->assertDOSEquals(array( + array( + 'ClassName' => 'EditableTextField', + 'Title' => 'Text with rule', + 'MinLength' => 20, + 'MaxLength' => 100, + 'Rows' => 4, + 'ExtraClass' => 'special class', + 'RightTitle' => 'My Field', + 'ShowOnLoad' => true, + 'Default' => 'Enter your text here', + ), + array( + 'ClassName' => 'EditableNumericField', + 'Title' => 'Numeric 1', + 'RightTitle' => 'Number of %', + 'Default' => 1, + 'MinValue' => 1, + 'MaxValue' => 100, + 'ShowOnLoad' => true, + ), + array( + 'ClassName' => 'EditableMemberListField', + 'Title' => 'Members 1', + 'RightTitle' => 'Select group', + 'GroupID' => $group1->ID, + 'ShowOnLoad' => false, + ), + array( + 'ClassName' => 'EditableLiteralField', + 'Title' => 'Literal 1', + 'HideFromReports' => true, + 'RightTitle' => 'Literal', + 'Content' => '

    Content

    ', + 'ShowOnLoad' => true, + ), + array( + 'ClassName' => 'EditableFormHeading', + 'Title' => 'Heading 1', + 'RightTitle' => 'Right', + 'Level' => 3, + 'HideFromReports' => true, + 'ShowOnLoad' => false, + ), + array( + 'ClassName' => 'EditableFileField', + 'Title' => 'File 1', + 'RightTitle' => 'File field', + 'FolderID' => $folder->ID, + ), + array( + 'ClassName' => 'EditableDateField', + 'Title' => 'Date 1', + 'RightTitle' => 'Date field', + 'DefaultToToday' => true, + ), + array( + 'ClassName' => 'EditableCheckbox', + 'Title' => 'Checkbox 1', + 'CheckedDefault' => true, + 'RightTitle' => 'Check this', + ), + ), $form->Fields()); + } +} diff --git a/tests/UserFormsUpgradeServiceTest.yml b/tests/UserFormsUpgradeServiceTest.yml new file mode 100644 index 0000000..c993d3d --- /dev/null +++ b/tests/UserFormsUpgradeServiceTest.yml @@ -0,0 +1,57 @@ +Group: + group1: + Title: 'Awesome Group' + +Folder: + folder1: + Title: 'Folder 1' + +EditableTextField: + text1: + Name: text1 + Title: 'First field' + text2: + Name: text2 + Title: 'Second field' + text3: + Name: text3 + Title: 'Third field' + text4: + Name: textwithrule + Title: 'Text with rule' +EditableNumericField: + numeric1: + Name: numeric1 + Title: 'Numeric 1' +EditableMemberListField: + members1: + Name: members1 + Title: 'Members 1' +EditableLiteralField: + literal1: + Name: literal1 + Title: 'Literal 1' +EditableFormHeading: + heading1: + Name: heading1 + Title: 'Heading 1' +EditableFileField: + file1: + Name: file1 + Title: 'File 1' +EditableDateField: + date1: + Name: date1 + Title: 'Date 1' +EditableCheckbox: + checkbox1: + Name: checkbox1 + Title: 'Checkbox 1' + +UserDefinedForm: + form-with-rules: + Title: 'User Defined Form' + Fields: =>EditableTextField.text1,=>EditableTextField.text2,=>EditableTextField.text3 + form-with-settings: + Title: 'Form with custom settings' + Fields: =>EditableTextField.text4,=>EditableNumericField.numeric1,=>EditableMemberListField.members1,=>EditableLiteralField.literal1,=>EditableFormHeading.heading1,=>EditableFileField.file1,=>EditableDateField.date1,=>EditableCheckbox.checkbox1