diff --git a/_config/routes.yml b/_config/routes.yml index 5cca979..6525079 100644 --- a/_config/routes.yml +++ b/_config/routes.yml @@ -7,4 +7,3 @@ SilverStripe\Control\Director: rules: 'partialuserform//$Action/$ID/$OtherID': Firesphere\PartialUserForms\Controllers\PartialSubmissionController 'partial/$Key/$Token': Firesphere\PartialUserForms\Controllers\PartialUserFormController - 'verify': Firesphere\PartialUserForms\Controllers\PartialUserFormVerifyController diff --git a/client/dist/main.js b/client/dist/main.js index a7c794d..ab638ea 100644 --- a/client/dist/main.js +++ b/client/dist/main.js @@ -1 +1,257 @@ -!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="/",r(r.s=0)}([function(e,t,r){e.exports=r(1)},function(e,t,r){"use strict";function n(e){return(n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}r.r(t);var o=document.baseURI,u=document.body.querySelector("form.userform"),c=[],a=function(){var e=new FormData;Array.from(u.querySelectorAll("[aria-hidden=false] [name]:not([type=hidden]):not([type=submit])")).forEach(function(t){var r=t.getAttribute("name"),o=function(e,t){var r=e.value;if("select"===e.getAttribute("type"))return e[e.selectedIndex].value;if("radio"===e.getAttribute("type")){var n="[name=".concat(t,"]:checked"),o=document.body.querySelector(n);return null!==o?o.value:""}if("checkbox"===e.getAttribute("type")){var u='[name="'.concat(t,'"]:checked'),c=Array.from(document.body.querySelectorAll(u)),a=[];return c.length>0?(c.forEach(function(e){a.push(e.value)}),a):""}return"file"===e.getAttribute("type")&&e.files.length>0?e.files[0]:r}(t,r);e.has(r)||("object"===n(o)&&"file"===t.getAttribute("type")?e.append(r,o):"object"===n(o)?o.forEach(function(t){e.append(r,t)}):e.append(r,o))});var t=u.querySelector("[name=PartialID]");t&&e.append("PartialID",t.value);var r=new XMLHttpRequest;c.push(r),r.open("POST","".concat(o).concat("partialuserform/save"),!0),r.send(e)},i=function(e){e.addEventListener("click",a)};Array.from(u.querySelectorAll("ul li.step-button-wrapper button")).forEach(i),null!==u&&(u._submit=u.submit,u.submit=function(){confirm("Are you sure you want to submit this form?")&&(c.length&&c.forEach(function(e){e.abort()}),u._submit())})}]); \ No newline at end of file +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = "/"; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 0); +/******/ }) +/************************************************************************/ +/******/ ({ + +/***/ "./client/src/js/main.js": +/*!*******************************!*\ + !*** ./client/src/js/main.js ***! + \*******************************/ +/*! no exports provided */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var _partialuserforms_partialsubmission__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./partialuserforms/partialsubmission */ "./client/src/js/partialuserforms/partialsubmission.js"); +/* harmony import */ var _partialuserforms_partialstorage__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./partialuserforms/partialstorage */ "./client/src/js/partialuserforms/partialstorage.js"); + + +Object(_partialuserforms_partialsubmission__WEBPACK_IMPORTED_MODULE_0__["default"])(); +Object(_partialuserforms_partialstorage__WEBPACK_IMPORTED_MODULE_1__["default"])(); + +/***/ }), + +/***/ "./client/src/js/partialuserforms/partialstorage.js": +/*!**********************************************************!*\ + !*** ./client/src/js/partialuserforms/partialstorage.js ***! + \**********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony default export */ __webpack_exports__["default"] = (function () {// @todo, add the option to prefill +}); + +/***/ }), + +/***/ "./client/src/js/partialuserforms/partialsubmission.js": +/*!*************************************************************!*\ + !*** ./client/src/js/partialuserforms/partialsubmission.js ***! + \*************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } + +var baseDomain = document.baseURI; +var submitURL = 'partialuserform/save'; +var form = document.body.querySelector('form.userform'); + +var buttons = function buttons() { + return Array.from(form.querySelectorAll('ul li.step-button-wrapper button')); +}; // Just get the form elements from the current step + + +var formElements = function formElements() { + return Array.from(form.querySelectorAll('[aria-hidden=false] [name]:not([type=hidden]):not([type=submit])')); +}; + +var requests = []; + +var getElementValue = function getElementValue(element, fieldName) { + var value = element.value; + + if (element.getAttribute('type') === 'select') { + return element[element.selectedIndex].value; + } + + if (element.getAttribute('type') === 'radio') { + var name = "[name=".concat(fieldName, "]:checked"); + var checkedElement = document.body.querySelector(name); + return checkedElement !== null ? checkedElement.value : ""; + } + + if (element.getAttribute('type') === 'checkbox') { + var _name = "[name=\"".concat(fieldName, "\"]:checked"); + + var checkedElements = Array.from(document.body.querySelectorAll(_name)); + var valueArray = []; + + if (checkedElements.length > 0) { + checkedElements.forEach(function (element) { + valueArray.push(element.value); + }); + return valueArray; + } + + return ""; + } + + if (element.getAttribute('type') === 'file' && element.files.length > 0) { + return element.files[0]; + } + + return value; +}; + +var submitPartial = function submitPartial() { + var data = new FormData(); + formElements().forEach(function (element) { + var fieldName = element.getAttribute('name'); + var value = getElementValue(element, fieldName); + + if (!data.has(fieldName)) { + if (_typeof(value) === 'object' && element.getAttribute('type') === 'file') { + data.append(fieldName, value); + } else if (_typeof(value) === 'object') { + value.forEach(function (arrayValue) { + data.append(fieldName, arrayValue); + }); + } else { + data.append(fieldName, value); + } + } + }); // Pass partial params if available + + var partialID = form.querySelector('[name=PartialID]'); + + if (partialID) { + data.append('PartialID', partialID.value); + } + /** global: XMLHttpRequest */ + + + var httpRequest = new XMLHttpRequest(); + requests.push(httpRequest); + httpRequest.open('POST', "".concat(baseDomain).concat(submitURL), true); + httpRequest.send(data); +}; + +var attachSubmitPartial = function attachSubmitPartial(button) { + button.addEventListener('click', submitPartial); +}; + +var abortPendingSubmissions = function abortPendingSubmissions() { + // Clear all pending partial submissions on submit + if (form !== null) { + form._submit = form.submit; // Save reference + + form.submit = function () { + // Abort all requests + if (requests.length) { + requests.forEach(function (xhr) { + xhr.abort(); + }); + } + + form._submit(); + }; + } +}; + +/* harmony default export */ __webpack_exports__["default"] = (function () { + buttons().forEach(attachSubmitPartial); + abortPendingSubmissions(); +}); + +/***/ }), + +/***/ 0: +/*!*************************************!*\ + !*** multi ./client/src/js/main.js ***! + \*************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +module.exports = __webpack_require__(/*! /Users/scotthutchinson/sites/mbie/vendor/firesphere/partialuserforms/client/src/js/main.js */"./client/src/js/main.js"); + + +/***/ }) + +/******/ }); \ No newline at end of file diff --git a/client/src/js/partialuserforms/partialsubmission.js b/client/src/js/partialuserforms/partialsubmission.js index 3035075..eae7756 100644 --- a/client/src/js/partialuserforms/partialsubmission.js +++ b/client/src/js/partialuserforms/partialsubmission.js @@ -75,10 +75,6 @@ const abortPendingSubmissions = () => { if (form !== null) { form._submit = form.submit; // Save reference form.submit = () => { - if (!confirm("Are you sure you want to submit this form?")) { - return; - } - // Abort all requests if (requests.length) { requests.forEach(xhr => { diff --git a/src/controllers/PartialUserFormController.php b/src/controllers/PartialUserFormController.php index f55697a..27204c4 100644 --- a/src/controllers/PartialUserFormController.php +++ b/src/controllers/PartialUserFormController.php @@ -72,7 +72,7 @@ public function partial(HTTPRequest $request) if ($controller->PasswordProtected && $request->getSession()->get(PasswordForm::PASSWORD_SESSION_KEY) !== $partial->ID ) { - return $this->redirect('verify'); + return $this->redirect($record->link('verify')); } // Lock form diff --git a/src/controllers/PartialUserFormVerifyController.php b/src/controllers/PartialUserFormVerifyController.php index 4dcec25..f660470 100644 --- a/src/controllers/PartialUserFormVerifyController.php +++ b/src/controllers/PartialUserFormVerifyController.php @@ -3,14 +3,16 @@ namespace Firesphere\PartialUserforms\Controllers; -use Exception; -use Firesphere\PartialUserforms\Forms\PasswordForm; -use Firesphere\PartialUserforms\Models\PartialFormSubmission; use Page; +use Exception; use PageController; +use SilverStripe\Control\Director; +use SilverStripe\Control\HTTPRequest; use SilverStripe\Control\HTTPResponse; use SilverStripe\Control\HTTPResponse_Exception; use SilverStripe\UserForms\Model\UserDefinedForm; +use Firesphere\PartialUserforms\Forms\PasswordForm; +use Firesphere\PartialUserforms\Models\PartialFormSubmission; /** * Class \Firesphere\PartialUserforms\Controllers\PartialUserFormVerifyController @@ -54,9 +56,6 @@ public function init() $partial = PartialFormSubmission::get()->byID($sessionID); $this->setPartialFormSubmission($partial); - // Set data record and load the form - /** @var UserDefinedForm dataRecord */ - $this->dataRecord = Page::create(); } /** @@ -67,7 +66,6 @@ public function getForm() return PasswordForm::create($this, __FUNCTION__); } - /** * @param array $data * @param PasswordForm $form @@ -97,7 +95,7 @@ public function doValidate($data, $form) $request->getSession()->set(PasswordForm::PASSWORD_SESSION_KEY, $partial->ID); $request->getSession()->set(self::PASSWORD_KEY, $data['Password']); - return $this->redirect($partial->getPartialLink()); + return $this->redirect($partial->Parent()->Link('overview')); } /** diff --git a/src/extensions/UserDefinedFormControllerExtension.php b/src/extensions/UserDefinedFormControllerExtension.php index 16bf6f8..de01101 100644 --- a/src/extensions/UserDefinedFormControllerExtension.php +++ b/src/extensions/UserDefinedFormControllerExtension.php @@ -2,16 +2,26 @@ namespace Firesphere\PartialUserforms\Extensions; -use Firesphere\PartialUserforms\Controllers\PartialSubmissionController; -use Firesphere\PartialUserforms\Models\PartialFieldSubmission; -use Firesphere\PartialUserforms\Models\PartialFileFieldSubmission; -use Firesphere\PartialUserforms\Models\PartialFormSubmission; -use SilverStripe\Control\NullHTTPRequest; +use Page; +use SilverStripe\Forms\Form; use SilverStripe\Core\Extension; +use SilverStripe\Control\Session; +use SilverStripe\Forms\FieldList; +use SilverStripe\Forms\TextField; +use SilverStripe\Forms\FormAction; +use SilverStripe\View\Requirements; +use SilverStripe\Control\Controller; +use SilverStripe\Control\HTTPRequest; +use SilverStripe\Control\NullHTTPRequest; +use Firesphere\PartialUserforms\Models\PartialFormSubmission; use SilverStripe\UserForms\Control\UserDefinedFormController; -use SilverStripe\UserForms\Model\EditableFormField\EditableFileField; +use Firesphere\PartialUserforms\Models\PartialFieldSubmission; +use Firesphere\PartialUserforms\Models\PartialFileFieldSubmission; use SilverStripe\UserForms\Model\EditableFormField\EditableFormStep; -use SilverStripe\View\Requirements; +use SilverStripe\UserForms\Model\EditableFormField\EditableFileField; +use Firesphere\PartialUserforms\Controllers\PartialSubmissionController; +use Firesphere\PartialUserforms\Controllers\PartialUserFormVerifyController; +use Firesphere\PartialUserforms\Forms\PasswordForm; /** * Class UserDefinedFormControllerExtension @@ -21,6 +31,20 @@ */ class UserDefinedFormControllerExtension extends Extension { + private static $allowed_actions = [ + 'partialIndex', + 'start', + 'StartForm', + 'overview', + 'OverviewForm', + 'verify', + 'VerifyForm', + ]; + + private static $url_handlers = [ + '' => 'partialIndex', + ]; + /** * Add required javascripts */ @@ -29,33 +53,6 @@ public function onBeforeInit() Requirements::javascript('firesphere/partialuserforms:client/dist/main.js'); } - /** - * Start a clean session if the user visits the original form - */ - public function onAfterInit() - { - $request = $this->owner->getRequest(); - if ($request instanceof NullHTTPRequest) { - return; - } - - $params = $this->owner->getRequest()->params(); - // Pages without action e.g. /partial - if (!array_key_exists('Action', $params)) { - return; - } - - // This should only run on index - if ($params['Action'] === null || $params['Action'] === 'index') { - $session = $this->owner->getRequest()->getSession(); - if (!$session) { - return; - } - - $this->createPartialSubmission(); - } - } - /** * Creates a new partial submission and partial fields. * @@ -97,5 +94,154 @@ protected function createPartialSubmission() // Refresh session on start PartialSubmissionController::reloadSession($page->getRequest()->getSession(), $submissionID); + $page->getRequest()->getSession()->set(PasswordForm::PASSWORD_SESSION_KEY, $submissionID); + } + + /** + * Gets the PartialFormSubmission based on the current session + */ + protected function getPartialFormSubmission() + { + $session = Controller::curr()->getRequest()->getSession(); + $partialID = $session->get(PartialSubmissionController::SESSION_KEY); + + return PartialFormSubmission::get()->byID($partialID); + } + + /** + * Redirect user to form start if EnablePartialSubmissions is true + */ + public function partialIndex(HTTPRequest $request = null) + { + if (!$this->owner->EnablePartialSubmissions) { + return $this->owner->index($request); + } + + return $this->owner->redirect($this->owner->Link('start')); + } + + /** + * Verify route, for password entry + */ + public function verify() + { + return $this->owner->customise([ + 'Form' => $this->VerifyForm(), + ])->renderWith([UserDefinedFormController::class . '_start', Page::class]); + } + + /** + * Generate the PasswordForm + * @return Form + */ + public function VerifyForm() + { + $partial = $this->getPartialFormSubmission(); + $controller = PartialUserFormVerifyController::create(); + $controller->setPartialFormSubmission($partial); + + return $controller->getForm() + ->setFormAction($this->owner->link('VerifyForm')); + } + + /** + * Start route + */ + public function start(HTTPRequest $request = null) + { + return $this->owner->customise([ + 'Form' => $this->StartForm(), + ]); + } + + /** + * Generate the StartForm + * @return Form + */ + public function StartForm() + { + $actions = FieldList::create( + FormAction::create('goToOverview')->setTitle('Start') + ); + + $form = Form::create( + $this->owner, + 'StartForm', + FieldList::create(), + $actions + )->addExtraClass('userform'); + + return $form; + } + + /** + * Redirect to the overview page, creating a new PartialFormSubmission if necessary + */ + public function goToOverview($data, $form) + { + // If partial submission which matches this form already exists, redirect to overview + $submission = $this->getPartialFormSubmission(); + + if ($submission && $submission->ParentID === $this->owner->ID) { + return $this->owner->redirect($this->owner->Link('overview')); + } + + // Else create new partial submission before redirecting + $this->createPartialSubmission(); + return $this->owner->redirect($this->owner->Link('overview')); + } + + /** + * Overview route + */ + public function overview(HTTPRequest $request = null) + { + return $this->owner->customise([ + 'Form' => $this->OverviewForm($request), + ]); + } + + /** + * Creates the overview form to display the form link and password + * @return Form + */ + public function OverviewForm(HTTPRequest $request = null) + { + $partialID = $request->getSession()->get(PartialSubmissionController::SESSION_KEY); + $password = $request->getSession()->get(PartialUserFormVerifyController::PASSWORD_KEY); + + if (!$partialID) { + return $this->owner->redirect($this->owner->Link('start')); + } + + $submission = PartialFormSubmission::get()->byID($partialID); + + $fields = FieldList::create( + TextField::create('FormLink', 'FormLink', $submission->getPartialLink()) + ->setReadonly(true), + TextField::create('Password', 'Password', $password) + ->setReadonly(true) + ); + + $actions = FieldList::create( + FormAction::create('goToForm')->setTitle('Go to form') + ); + + return Form::create( + $this->owner, + 'OverviewForm', + $fields, + $actions + )->addExtraClass('userform'); + } + + /** + * Navigate to the partial form + */ + public function goToForm($data, $form) + { + $link = $data['FormLink']; + + return $this->owner->redirect($link); } } diff --git a/src/extensions/UserDefinedFormExtension.php b/src/extensions/UserDefinedFormExtension.php index 5baa210..2710ad6 100644 --- a/src/extensions/UserDefinedFormExtension.php +++ b/src/extensions/UserDefinedFormExtension.php @@ -2,16 +2,17 @@ namespace Firesphere\PartialUserforms\Extensions; -use Firesphere\PartialUserforms\Models\PartialFormSubmission; -use SilverStripe\Forms\CheckboxField; +use SilverStripe\Forms\Tab; +use SilverStripe\ORM\DataList; use SilverStripe\Forms\FieldList; +use SilverStripe\ORM\DataExtension; +use SilverStripe\Forms\CheckboxField; +use SilverStripe\Forms\TextareaField; use SilverStripe\Forms\GridField\GridField; +use SilverStripe\UserForms\Model\UserDefinedForm; use SilverStripe\Forms\GridField\GridFieldAddNewButton; +use Firesphere\PartialUserforms\Models\PartialFormSubmission; use SilverStripe\Forms\GridField\GridFieldConfig_RecordEditor; -use SilverStripe\Forms\Tab; -use SilverStripe\ORM\DataExtension; -use SilverStripe\ORM\DataList; -use SilverStripe\UserForms\Model\UserDefinedForm; /** * Class UserDefinedFormExtension @@ -28,8 +29,11 @@ class UserDefinedFormExtension extends DataExtension * @var array */ private static $db = [ + 'EnablePartialSubmissions' => 'Boolean(false)', 'ExportPartialSubmissions' => 'Boolean(true)', - 'PasswordProtected' => 'Boolean(false)' + 'PasswordProtected' => 'Boolean(false)', + 'FormIntroduction' => 'Text', + 'FormOverview' => 'Text', ]; /** @@ -70,30 +74,47 @@ public function updateCMSFields(FieldList $fields) ) ); - $fields->insertBefore( - 'DisableSaveSubmissions', - $pwdCheckbox = CheckboxField::create( - 'PasswordProtected', - _t(__CLASS__ . 'PasswordProtected', 'Password protect resuming partial submissions') - ) - ); - $pwdDescription = _t( + $enablePartialCheckbox = CheckboxField::create( + 'EnablePartialSubmissions', + _t(__CLASS__ . '.enablePartialSubmissionsCheckboxLabel', 'Enable partial submissions') + )->setDescription(_t( + __CLASS__ . '.enablePartialSubmissionsDescription', + 'If checked, this will allow this form to be shareable and filled out by multiple people' + )); + + $pwdCheckbox = CheckboxField::create( + 'PasswordProtected', + _t(__CLASS__ . 'PasswordProtected', 'Password protect resuming partial submissions') + )->setDescription(_t( __CLASS__ . '.PasswordProtectDescription', 'When resuming a partial submission, require the user to enter a password' - ); - $pwdCheckbox->setDescription($pwdDescription); + )); - $fields->insertAfter( - 'DisableSaveSubmissions', - $partialCheckbox = CheckboxField::create( - 'ExportPartialSubmissions', - _t(__CLASS__ . '.partialCheckboxLabel', 'Send partial submissions') - ) - ); - $description = _t( + $partialCheckbox = CheckboxField::create( + 'ExportPartialSubmissions', + _t(__CLASS__ . '.partialCheckboxLabel', 'Send partial submissions') + )->setDescription(_t( __CLASS__ . '.partialCheckboxDescription', 'The configuration and global export configuration can be set in the site Settings' + )); + + $introTextDescription = _t(__CLASS__ . '.introTextDescription', 'Text to display at the introduction page, before the user has started the form.'); + $overviewTextDescription = _t(__CLASS__ . '.overviewTextDescription', 'Text to display on the overview page of the form, alongside form credentials.'); + + $fields->addFieldToTab( + 'Root.FormOptions', + Tab::create('Partial', _t(__CLASS__ . '.partialTab', 'Partial')) ); - $partialCheckbox->setDescription($description); + $fields->addFieldsToTab('Root.FormOptions.Partial', [ + $enablePartialCheckbox, + $pwdCheckbox, + $partialCheckbox, + TextareaField::create('FormIntroduction', 'Form introduction text') + ->setDescription($introTextDescription) + ->setRows(3), + TextareaField::create('FormOverview', 'Form overview text') + ->setDescription($overviewTextDescription) + ->setRows(3), + ]); } } diff --git a/templates/SilverStripe/UserForms/Control/Layout/UserDefinedFormController_overview.ss b/templates/SilverStripe/UserForms/Control/Layout/UserDefinedFormController_overview.ss new file mode 100644 index 0000000..b2028e1 --- /dev/null +++ b/templates/SilverStripe/UserForms/Control/Layout/UserDefinedFormController_overview.ss @@ -0,0 +1,6 @@ +
+

$Title

+ +

$FormOverview

+ $Form +
diff --git a/templates/SilverStripe/UserForms/Control/Layout/UserDefinedFormController_start.ss b/templates/SilverStripe/UserForms/Control/Layout/UserDefinedFormController_start.ss new file mode 100644 index 0000000..730a095 --- /dev/null +++ b/templates/SilverStripe/UserForms/Control/Layout/UserDefinedFormController_start.ss @@ -0,0 +1,6 @@ +
+

$Title

+ +

$FormIntroduction

+ $Form +
diff --git a/tests/unit/PartialUserFormControllerTest.php b/tests/unit/PartialUserFormControllerTest.php index 8a10d30..df34655 100644 --- a/tests/unit/PartialUserFormControllerTest.php +++ b/tests/unit/PartialUserFormControllerTest.php @@ -92,7 +92,7 @@ public function testPasswordProtectedPartial() $key = $submission->generateKey($submission->Token); $result = $this->get("partial/{$key}/{$submission->Token}"); // Be redirected to the Password form - $formOpeningTag = '
'; + $formOpeningTag = ''; $this->assertContains($formOpeningTag, $result->getBody()); } diff --git a/tests/unit/PartialUserFormVerifyControllerTest.php b/tests/unit/PartialUserFormVerifyControllerTest.php index d0b0280..85514e8 100644 --- a/tests/unit/PartialUserFormVerifyControllerTest.php +++ b/tests/unit/PartialUserFormVerifyControllerTest.php @@ -69,7 +69,7 @@ public function testDoValidate() $result = $controller->doValidate(['Password' => '1234567890'], $form); $this->assertEquals($partialForm->ID, $session->get('PartialFormSession')); - $this->assertContains('/partial/', $result->getHeader('Location')); + $this->assertContains('/overview', $result->getHeader('Location')); $this->assertEquals('1234567890', $session->get(PartialUserFormVerifyController::PASSWORD_KEY)); $controller = new PartialUserFormVerifyController();