diff --git a/client/dist/main.js b/client/dist/main.js index d0543da..ed921c4 100644 --- a/client/dist/main.js +++ b/client/dist/main.js @@ -1 +1 @@ -!function(e){var t={};function r(o){if(t[o])return t[o].exports;var n=t[o]={i:o,l:!1,exports:{}};return e[o].call(n.exports,n,n.exports,r),n.l=!0,n.exports}r.m=e,r.c=t,r.d=function(e,t,o){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},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 o=Object.create(null);if(r.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)r.d(o,n,function(t){return e[t]}.bind(null,n));return o},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 o(e){return(o="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 n=document.baseURI,u=document.body.querySelector("form.userform"),c=[],f=function(){var e=new FormData;Array.from(document.body.querySelectorAll("form.userform [name]:not([type=hidden]):not([type=submit])")).forEach(function(t){var r=t.getAttribute("name"),n=function(e,t){var r=e.value;if("select"===e.getAttribute("type"))return e[e.selectedIndex].value;if("radio"===e.getAttribute("type")){var o="[name=".concat(t,"]:checked"),n=document.body.querySelector(o);return null!==n?n.value:""}if("checkbox"===e.getAttribute("type")){var u='[name="'.concat(t,'"]:checked'),c=Array.from(document.body.querySelectorAll(u)),f=[];return c.length>0?(c.forEach(function(e){f.push(e.value)}),f):""}return"file"===e.getAttribute("type")&&e.files.length>0?e.files[0]:r}(t,r);e.has(r)||("object"===o(n)&&"file"===t.getAttribute("type")?e.append(r,n):"object"===o(n)?n.forEach(function(t){e.append(r,t)}):e.append(r,n))});var t=new XMLHttpRequest;c.push(t),t.open("POST","".concat(n).concat("partialuserform/save"),!0),t.send(e)},i=function(e){e.addEventListener("click",f)};Array.from(document.body.querySelectorAll("form.userform 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(e){var t={};function r(o){if(t[o])return t[o].exports;var n=t[o]={i:o,l:!1,exports:{}};return e[o].call(n.exports,n,n.exports,r),n.l=!0,n.exports}r.m=e,r.c=t,r.d=function(e,t,o){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},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 o=Object.create(null);if(r.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)r.d(o,n,function(t){return e[t]}.bind(null,n));return o},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 o(e){return(o="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 n=document.baseURI,u=document.body.querySelector("form.userform"),c=[],f=function(){var e=new FormData;Array.from(document.body.querySelectorAll("form.userform [name]:not([type=hidden]):not([type=submit])")).forEach(function(t){var r=t.getAttribute("name"),n=function(e,t){var r=e.value;if("select"===e.getAttribute("type"))return e[e.selectedIndex].value;if("radio"===e.getAttribute("type")){var o="[name=".concat(t,"]:checked"),n=document.body.querySelector(o);return null!==n?n.value:""}if("checkbox"===e.getAttribute("type")){var u='[name="'.concat(t,'"]:checked'),c=Array.from(document.body.querySelectorAll(u)),f=[];return c.length>0?(c.forEach(function(e){f.push(e.value)}),f):""}return"file"===e.getAttribute("type")&&e.files.length>0?e.files[0]:r}(t,r);e.has(r)||("object"===o(n)&&"file"===t.getAttribute("type")?e.append(r,n):"object"===o(n)?n.forEach(function(t){e.append(r,t)}):e.append(r,n))});var t=document.body.querySelector("form.userform [name=PartialID]");t&&e.append("PartialID",t.value);var r=new XMLHttpRequest;c.push(r),r.open("POST","".concat(n).concat("partialuserform/save"),!0),r.send(e)},i=function(e){e.addEventListener("click",f)};Array.from(document.body.querySelectorAll("form.userform 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 diff --git a/client/src/js/partialuserforms/partialsubmission.js b/client/src/js/partialuserforms/partialsubmission.js index e4bd43a..ea54ac6 100644 --- a/client/src/js/partialuserforms/partialsubmission.js +++ b/client/src/js/partialuserforms/partialsubmission.js @@ -52,6 +52,12 @@ const submitPartial = () => { } }); + // Pass partial params if available + const partialID = document.body.querySelector('form.userform [name=PartialID]'); + if (partialID) { + data.append('PartialID', partialID.value); + } + /** global: XMLHttpRequest */ const httpRequest = new XMLHttpRequest(); requests.push(httpRequest); diff --git a/src/controllers/PartialSubmissionController.php b/src/controllers/PartialSubmissionController.php index a92d8e1..e46fe8e 100644 --- a/src/controllers/PartialSubmissionController.php +++ b/src/controllers/PartialSubmissionController.php @@ -59,19 +59,27 @@ public function savePartialSubmission(HTTPRequest $request) // We don't want SecurityID and/or the process Action stored as a thing unset($postVars['SecurityID'], $postVars['action_process']); - $submissionID = $request->getSession()->get(self::SESSION_KEY); + + // Check for partial params so the submission doesn't rely on session for partial page + if (!empty($postVars['PartialID'])) { + $submissionID = $postVars['PartialID']; + } else { + $submissionID = $request->getSession()->get(self::SESSION_KEY); + } /** @var PartialFormSubmission $partialSubmission */ $partialSubmission = PartialFormSubmission::get()->byID($submissionID); if (!$submissionID || !$partialSubmission) { - $partialSubmission = PartialFormSubmission::create(); - // TODO: Set the Parent ID and Parent Class before write, this issue will create new submissions - // every time the session expires when the user proceeds to the next step. - // Also, saving a new submission without a parent creates an - // "AccordionItems" as parent class (first DataObject found) - $submissionID = $partialSubmission->write(); + // A new session has already been created in {@link UserDefinedFormControllerExtension::onAfterInit()} + // so this shouldn't happen. Although, when there's a pending ajax save (e.g. user clicks next/prev too fast + // then hit submit), after submission, the submissionID has already been deleted while save is still in + // progress, thus resulting to 404 + // TODO: Find a solution dealing with race condition between ajax save and form submit + $this->httpError(404); } + + // Refresh session ID $request->getSession()->set(self::SESSION_KEY, $submissionID); foreach ($postVars as $field => $value) { /** @var EditableFormField $editableField */ @@ -166,29 +174,37 @@ protected function savePartialFile($formData, array $filter, EditableFormField\E $partialFileSubmission = PartialFileFieldSubmission::create($partialData); $partialFileSubmission->write(); } - // Don't overwrite existing uploads - if (!$partialFileSubmission->UploadedFileID && is_array($formData['Value'])) { - $file = $this->uploadFile($formData, $editableField); + + if (is_array($formData['Value'])) { + $file = $this->uploadFile($formData, $editableField, $partialFileSubmission); $partialFileSubmission->UploadedFileID = $file->ID ?? 0; + $partialFileSubmission->write(); } - $partialFileSubmission->write(); } /** * @param array $formData * @param EditableFormField\EditableFileField $field + * @param PartialFileFieldSubmission $partialFileSubmission * @return bool|File - * @throws \Exception + * @throws Exception */ - protected function uploadFile($formData, $field) + protected function uploadFile($formData, $field, $partialFileSubmission) { if (!empty($formData['Value']['name'])) { $foldername = $field->getFormField()->getFolderName(); - // create the file from post data + if (!$partialFileSubmission->UploadedFileID) { + $file = File::create([ + 'ShowInSearch' => 0 + ]); + } else { + // Allow overwrite existing uploads + $file = $partialFileSubmission->UploadedFile(); + } + + // Upload the file from post data $upload = Upload::create(); - $file = File::create(); - $file->ShowInSearch = 0; if ($upload->loadIntoFile($formData['Value'], $file, $foldername)) { return $file; } diff --git a/src/controllers/PartialUserFormController.php b/src/controllers/PartialUserFormController.php index 828ea32..f4e62bf 100644 --- a/src/controllers/PartialUserFormController.php +++ b/src/controllers/PartialUserFormController.php @@ -11,11 +11,11 @@ use SilverStripe\Control\HTTPResponse_Exception; use SilverStripe\Control\Middleware\HTTPCacheControlMiddleware; use SilverStripe\Forms\Form; +use SilverStripe\Forms\HiddenField; use SilverStripe\ORM\DataObject; use SilverStripe\ORM\FieldType\DBField; use SilverStripe\ORM\FieldType\DBHTMLText; use SilverStripe\UserForms\Control\UserDefinedFormController; -use SilverStripe\UserForms\Model\UserDefinedForm; /** * Class PartialUserFormController @@ -30,16 +30,13 @@ class PartialUserFormController extends UserDefinedFormController private static $url_handlers = [ '$Key/$Token' => 'partial', ]; + /** * @var array */ private static $allowed_actions = [ 'partial', ]; - /** - * @var PartialFormSubmission - */ - protected $partialFormSubmission; /** * Partial form @@ -54,20 +51,27 @@ public function partial(HTTPRequest $request) /** @var PartialFormSubmission $partial */ $partial = $this->validateToken($request); $record = DataObject::get_by_id($partial->UserDefinedFormClass, $partial->UserDefinedFormID); + /** @var self $controller */ - $controller = self::create($record); + $controller = parent::create($record); $controller->doInit(); - $controller->dataRecord = $record; - if ($controller->data()->PasswordProtected && + + // Set the session after init and check if the last session has expired + // or another submission has started + $sessionID = $request->getSession()->get(PartialSubmissionController::SESSION_KEY); + if (!$sessionID || $sessionID !== $partial->ID) { + $request->getSession()->set(PartialSubmissionController::SESSION_KEY, $partial->ID); + } + + if ($controller->PasswordProtected && $request->getSession()->get(PasswordForm::PASSWORD_SESSION_KEY) !== $partial->ID ) { return $this->redirect('verify'); } - /** @var Form $form */ $form = $controller->Form(); - $fields = $partial->PartialFields()->map('Name', 'Value')->toArray(); - $form->loadDataFrom($fields); + $form->loadDataFrom($partial->getFields()); + $this->populateData($form, $partial); // Copied from {@link UserDefinedFormController} if ($controller->Content && $form && !$controller->config()->disable_form_content_shortcode) { @@ -96,6 +100,44 @@ public function partial(HTTPRequest $request) ])->renderWith([static::class, Page::class]); } + /** + * Add partial submission and set the uploaded filenames as right title of the file fields + * + * @param Form $form + * @param PartialFormSubmission $partial + */ + protected function populateData($form, $partial) + { + $fields = $form->Fields(); + // Add partial submission ID + $fields->push( + HiddenField::create( + 'PartialID', + null, + $partial->ID + ) + ); + + // Populate files + $uploads = $partial->PartialUploads()->filter([ + 'UploadedFileID:not'=> 0 + ]); + + if (!$uploads->exists()) { + return; + } + + foreach ($uploads as $upload) { + $fields->dataFieldByName($upload->Name) + ->setRightTitle( + sprintf( + 'Uploaded: %s (Attach a new file to replace the uploaded file)', + $upload->UploadedFile()->Name + ) + ); + } + } + /** * A little abstraction to be more readable * @@ -115,37 +157,11 @@ public function validateToken($request) } /** @var PartialFormSubmission $partial */ - $partial = PartialFormSubmission::get()->find('Token', $token); - if (!$token || - !$partial || - !$partial->UserDefinedFormID || - !hash_equals($partial->generateKey($token), $key) - ) { + $partial = PartialFormSubmission::validateKeyToken($key, $token); + if ($partial === false) { return $this->httpError(404); } - $sessionKey = PartialSubmissionController::SESSION_KEY; - // Set the session if the last session has expired - if (!$request->getSession()->get($sessionKey)) { - $request->getSession()->set($sessionKey, $partial->ID); - } - return $partial; } - - /** - * @return PartialFormSubmission - */ - public function getPartialFormSubmission(): PartialFormSubmission - { - return $this->partialFormSubmission; - } - - /** - * @param PartialFormSubmission $partialFormSubmission - */ - public function setPartialFormSubmission(PartialFormSubmission $partialFormSubmission): void - { - $this->partialFormSubmission = $partialFormSubmission; - } } diff --git a/src/controllers/PartialUserFormVerifyController.php b/src/controllers/PartialUserFormVerifyController.php index 547a8e6..4dcec25 100644 --- a/src/controllers/PartialUserFormVerifyController.php +++ b/src/controllers/PartialUserFormVerifyController.php @@ -78,6 +78,7 @@ public function doValidate($data, $form) { /** @var PartialFormSubmission $partial */ $partial = $this->getPartialFormSubmission(); + $request = $this->getRequest(); $password = hash_pbkdf2('SHA256', $data['Password'], $partial->TokenSalt, 1000); if (!hash_equals($password, $partial->Password)) { @@ -87,11 +88,12 @@ public function doValidate($data, $form) 'Password incorrect, please check your password and try again' ) ); + $request->getSession()->clear(PasswordForm::PASSWORD_SESSION_KEY); + $request->getSession()->clear(self::PASSWORD_KEY); return $this->redirectBack(); } - $request = $this->getRequest(); $request->getSession()->set(PasswordForm::PASSWORD_SESSION_KEY, $partial->ID); $request->getSession()->set(self::PASSWORD_KEY, $data['Password']); diff --git a/src/extensions/SubmittedFormExtension.php b/src/extensions/SubmittedFormExtension.php index 7035a82..d7df770 100644 --- a/src/extensions/SubmittedFormExtension.php +++ b/src/extensions/SubmittedFormExtension.php @@ -3,6 +3,8 @@ namespace Firesphere\PartialUserforms\Extensions; use Firesphere\PartialUserforms\Controllers\PartialSubmissionController; +use Firesphere\PartialUserforms\Controllers\PartialUserFormVerifyController; +use Firesphere\PartialUserforms\Forms\PasswordForm; use Firesphere\PartialUserforms\Models\PartialFormSubmission; use SilverStripe\Control\Controller; use SilverStripe\ORM\DataExtension; @@ -22,7 +24,10 @@ class SubmittedFormExtension extends DataExtension public function updateAfterProcess() { // cleanup partial submissions - $partialID = Controller::curr()->getRequest()->getSession()->get(PartialSubmissionController::SESSION_KEY); + $request = Controller::curr()->getRequest(); + + $postID = $request->postVar('PartialID'); + $partialID = $postID ?? $request->getSession()->get(PartialSubmissionController::SESSION_KEY); if ($partialID === null) { return; } @@ -33,8 +38,21 @@ public function updateAfterProcess() return; } + // Link files to SubmittedForm + $uploads = $partialForm->PartialUploads()->filter([ + 'UploadedFileID:not'=> 0 + ]); + if ($uploads->exists()) { + foreach ($uploads as $upload) { + $upload->ParentID = $this->owner->ID; + $upload->write(); + } + } + $partialForm->delete(); $partialForm->destroy(); - Controller::curr()->getRequest()->getSession()->clear(PartialSubmissionController::SESSION_KEY); + $request->getSession()->clear(PartialSubmissionController::SESSION_KEY); + $request->getSession()->clear(PasswordForm::PASSWORD_SESSION_KEY); + $request->getSession()->clear(PartialUserFormVerifyController::PASSWORD_KEY); } } diff --git a/src/extensions/UserDefinedFormControllerExtension.php b/src/extensions/UserDefinedFormControllerExtension.php index 7168a9a..ae20c12 100644 --- a/src/extensions/UserDefinedFormControllerExtension.php +++ b/src/extensions/UserDefinedFormControllerExtension.php @@ -2,9 +2,15 @@ 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 SilverStripe\Core\Extension; use SilverStripe\UserForms\Control\UserDefinedFormController; +use SilverStripe\UserForms\Model\EditableFormField\EditableFileField; +use SilverStripe\UserForms\Model\EditableFormField\EditableFormStep; use SilverStripe\View\Requirements; /** @@ -22,4 +28,74 @@ 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. + * + * @throws \SilverStripe\ORM\ValidationException + */ + protected function createPartialSubmission() + { + $page = $this->owner; + // Create partial form + $partialSubmission = PartialFormSubmission::create([ + 'ParentID' => $page->ID, + 'ParentClass' => $page->ClassName, + 'UserDefinedFormID' => $page->ID, + 'UserDefinedFormClass' => $page->ClassName, + ]); + $submissionID = $partialSubmission->write(); + + // Create partial fields + foreach ($page->data()->Fields() as $field) { + // We don't need literal fields, headers, html, etc + if ($field::config()->literal === true || $field->ClassName == EditableFormStep::class) { + continue; + } + + $newData = [ + 'SubmittedFormID' => $submissionID, + 'Name' => $field->Name, + 'Title' => $field->getField('Title'), + ]; + + if (in_array(EditableFileField::class, $field->getClassAncestry())) { + $partialFile = PartialFileFieldSubmission::create($newData); + $partialSubmission->PartialUploads()->add($partialFile); + } else { + $partialField = PartialFieldSubmission::create($newData); + $partialSubmission->PartialFields()->add($partialField); + } + } + + // Refresh session on start + $page->getRequest()->getSession()->set(PartialSubmissionController::SESSION_KEY, $submissionID); + } } diff --git a/src/models/PartialFileFieldSubmission.php b/src/models/PartialFileFieldSubmission.php index 5958044..991b3a6 100644 --- a/src/models/PartialFileFieldSubmission.php +++ b/src/models/PartialFileFieldSubmission.php @@ -19,7 +19,6 @@ class PartialFileFieldSubmission extends SubmittedFileField 'SubmittedForm' => PartialFormSubmission::class, ]; - /** * @param Member $member * @param array $context diff --git a/src/models/PartialFormSubmission.php b/src/models/PartialFormSubmission.php index cf8e1d0..ea4d6c5 100644 --- a/src/models/PartialFormSubmission.php +++ b/src/models/PartialFormSubmission.php @@ -67,8 +67,12 @@ class PartialFormSubmission extends SubmittedForm 'PartialUploads' => PartialFileFieldSubmission::class ]; + /** + * @var array + */ private static $cascade_deletes = [ - 'PartialFields' + 'PartialFields', + 'PartialUploads', ]; /** @@ -119,7 +123,8 @@ public function getCMSFields() 'Token', 'UserDefinedFormID', 'Submitter', - 'PartialUploads' + 'PartialUploads', + 'Password' ]); $partialFields = $this->PartialFields(); @@ -169,13 +174,11 @@ public function getPartialLink() return '(none)'; } - $token = $this->Token; - return Controller::join_links( Director::absoluteBaseURL(), 'partial', - $this->generateKey($token), - $token + $this->generateKey($this->Token), + $this->Token ); } @@ -198,26 +201,15 @@ public function generateKey($token) public function onBeforeWrite() { parent::onBeforeWrite(); - $this->getPartialToken(); - if (!$this->Password) { - $this->Password = $this->generatePassword(); - } - } - /** - * Get the unique token for the share link - * - * @return bool|string|null - * @throws Exception - */ - protected function getPartialToken() - { if (!$this->TokenSalt) { $this->TokenSalt = $this->generateToken(); $this->Token = $this->generateToken(); } - return $this->Token; + if (!$this->Password) { + $this->Password = $this->generatePassword(); + } } /** @@ -276,7 +268,7 @@ public function canCreate($member = null, $context = []) */ public function canView($member = null) { - if ($this->UserDefinedForm()) { + if ($this->UserDefinedFormID) { return $this->UserDefinedForm()->canView($member); } @@ -291,7 +283,7 @@ public function canView($member = null) */ public function canEdit($member = null) { - if ($this->UserDefinedForm()) { + if ($this->UserDefinedFormID) { return $this->UserDefinedForm()->canEdit($member); } @@ -305,10 +297,44 @@ public function canEdit($member = null) */ public function canDelete($member = null) { - if ($this->UserDefinedForm()) { + if ($this->UserDefinedFormID) { return $this->UserDefinedForm()->canDelete($member); } return parent::canDelete($member); } + + /** + * Get all partial fields for loading data into the form + * + * @return array + */ + public function getFields() + { + $formFields = $this->PartialFields()->map('Name', 'Value')->toArray(); + $fileFields = $this->PartialUploads()->map('Name', 'FileName')->toArray(); + + return array_merge($formFields, $fileFields); + } + + /** + * Validate key/token combination + * + * @param string $key + * @param string $token + * @return bool|PartialFormSubmission + */ + public static function validateKeyToken($key, $token) + { + /** @var PartialFormSubmission $partial */ + $partial = PartialFormSubmission::get()->find('Token', $token); + if (!$partial || + !$partial->UserDefinedFormID || + !hash_equals($partial->generateKey($token), $key) + ) { + return false; + } + + return $partial; + } } diff --git a/tests/fixtures/partialformtest.yml b/tests/fixtures/partialformtest.yml index 3bc2218..79ccf3b 100644 --- a/tests/fixtures/partialformtest.yml +++ b/tests/fixtures/partialformtest.yml @@ -1,26 +1,39 @@ SilverStripe\UserForms\Model\UserDefinedForm: form1: Title: Form 1 +Firesphere\PartialUserforms\Models\PartialFormSubmission: + submission1: + UserDefinedForm: =>SilverStripe\UserForms\Model\UserDefinedForm.form1 + Parent: =>SilverStripe\UserForms\Model\UserDefinedForm.form1 + TokenSalt: aa11ss22dd33ff44 + Token: q1w2e3r4t5y6u7i8 Firesphere\PartialUserForms\Models\PartialFieldSubmission: field1: Name: 'fullname' Value: 'Test' Title: 'Full name' + SubmittedForm: =>Firesphere\PartialUserforms\Models\PartialFormSubmission.submission1 field2: Name: 'email' Value: 'test@test.com' Title: 'Email address' + SubmittedForm: =>Firesphere\PartialUserforms\Models\PartialFormSubmission.submission1 field3: Name: 'question' Value: 'I have a question, what''s the ultimate question of life, the universe, and everything' Title: 'Question' -Firesphere\PartialUserforms\Models\PartialFormSubmission: - submission1: - UserDefinedForm: =>SilverStripe\UserForms\Model\UserDefinedForm.form1 - Parent: =>SilverStripe\UserForms\Model\UserDefinedForm.form1 - PartialFields: =>Firesphere\PartialUserForms\Models\PartialFieldSubmission.field1,=>Firesphere\PartialUserForms\Models\PartialFieldSubmission.field2,=>Firesphere\PartialUserForms\Models\PartialFieldSubmission.field3 - TokenSalt: aa11ss22dd33ff44 - Token: q1w2e3r4t5y6u7i8 + SubmittedForm: =>Firesphere\PartialUserforms\Models\PartialFormSubmission.submission1 +SilverStripe\Assets\File: + file1: + Name: 'Hans.png' + Title: 'Hans Image' + Filename: partialuserforms/tests/fixtures/Hans-fullsize-sqr.png +Firesphere\PartialUserForms\Models\PartialFileFieldSubmission: + field1: + Name: 'File' + UploadedFile: =>SilverStripe\Assets\File.file1 + SubmittedForm: =>Firesphere\PartialUserforms\Models\PartialFormSubmission.submission1 + Parent: =>Firesphere\PartialUserforms\Models\PartialFormSubmission.submission1 SilverStripe\UserForms\Model\EditableFormField\EditableTextField: textfield1: Name: 'Field1' @@ -37,6 +50,11 @@ SilverStripe\UserForms\Model\EditableFormField\EditableTextField: Title: 'Field 3' ParentClass: SilverStripe\UserForms\Model\UserDefinedForm Parent: =>SilverStripe\UserForms\Model\UserDefinedForm.form1 + textfield4: + Name: 'question' + Title: 'Question' + ParentClass: SilverStripe\UserForms\Model\UserDefinedForm + Parent: =>SilverStripe\UserForms\Model\UserDefinedForm.form1 SilverStripe\UserForms\Model\EditableFormField\EditableFileField: filefield1: Name: 'File' diff --git a/tests/templates/Page.ss b/tests/templates/Page.ss new file mode 100644 index 0000000..cfe0764 --- /dev/null +++ b/tests/templates/Page.ss @@ -0,0 +1,8 @@ + + + + + $Content + $Form + + diff --git a/tests/unit/PartialFileFieldSubmissionTest.php b/tests/unit/PartialFileFieldSubmissionTest.php index f37c36b..3545be6 100644 --- a/tests/unit/PartialFileFieldSubmissionTest.php +++ b/tests/unit/PartialFileFieldSubmissionTest.php @@ -4,6 +4,7 @@ use Firesphere\PartialUserforms\Models\PartialFileFieldSubmission; use Firesphere\PartialUserforms\Models\PartialFormSubmission; +use SilverStripe\Assets\File; use SilverStripe\Dev\SapphireTest; use SilverStripe\Security\DefaultAdminService; use SilverStripe\Security\Security; @@ -64,11 +65,17 @@ public function testCanDelete() protected function setUp() { parent::setUp(); - $this->field = PartialFileFieldSubmission::create(); - $partialForm = PartialFormSubmission::create(); - $udf = UserDefinedForm::create(['Title' => 'Test'])->write(); - $partialForm->UserDefinedFormID = $udf; - $partialFormID = $partialForm->write(); - $this->field->SubmittedFormID = $partialFormID; + $udf = UserDefinedForm::create(['Title' => 'Test']); + $udf->write(); + $udf->publishRecursive(); + + $partialFormID = PartialFormSubmission::create([ + 'UserDefinedFormID' => $udf->ID, + 'UserDefinedFormClass' => $udf->ClassName, + ])->write(); + + $this->field = PartialFileFieldSubmission::create([ + 'SubmittedFormID' => $partialFormID, + ]); } } diff --git a/tests/unit/PartialFormSubmissionTest.php b/tests/unit/PartialFormSubmissionTest.php index 288e2f4..5d70640 100644 --- a/tests/unit/PartialFormSubmissionTest.php +++ b/tests/unit/PartialFormSubmissionTest.php @@ -105,18 +105,21 @@ public function testGetPartialLink() public function testGetPartialToken() { $partial = PartialFormSubmission::create(); + // Set a salt without saving $partial->TokenSalt = 'test-salt'; - $this->assertNull(TestHelper::invokeMethod($partial, 'getPartialToken')); + $this->assertNull($partial->Token); + // Set salt to null then check before and after write $partial->TokenSalt = null; + $this->assertNull($partial->Token); $partial->write(); - $this->assertNotNull(TestHelper::invokeMethod($partial, 'getPartialToken')); + $this->assertNotNull($partial->Token); $partial->Token = 'test-token'; $partial->TokenSalt = 'test-salt'; $partial->write(); - $this->assertEquals('test-token', TestHelper::invokeMethod($partial, 'getPartialToken')); + $this->assertEquals('test-token', $partial->Token); } public function testGenerateToken() diff --git a/tests/unit/PartialSubmissionControllerTest.php b/tests/unit/PartialSubmissionControllerTest.php index d00ac21..a711fcc 100644 --- a/tests/unit/PartialSubmissionControllerTest.php +++ b/tests/unit/PartialSubmissionControllerTest.php @@ -191,13 +191,8 @@ public function testArrayData() $this->assertEquals('Value1, Value2', $field3->Value); } - /** - * @todo Remove skip test after implementation - */ public function testSaveDataWithExpiredSession() { - $this->markTestSkipped('Remove skip test once implementation is complete'); - $values = [ 'Field1' => 'Value1', 'Field2' => 'Value2', @@ -207,14 +202,14 @@ public function testSaveDataWithExpiredSession() $id = $this->savePartial($values); $this->assertInternalType('int', $id); - $partial = PartialFormSubmission::get()->byID($id); - $this->assertNotNull($partial); + $sessionID = $this->session()->get(PartialSubmissionController::SESSION_KEY); + $this->assertEquals($id, $sessionID); - // Now clear session and save + // Now clear session and save, it should be 404 $this->session()->clear(PartialSubmissionController::SESSION_KEY); $values['Field1'] = 'NEW VALUE'; - $newId = $this->savePartial($values); - $this->assertEquals($id, $newId); + $response = $this->post('/partialuserform/save', $values); + $this->assertEquals(404, $response->getStatusCode()); } /** @@ -233,7 +228,17 @@ private function savePartial($values) public function setUp() { parent::setUp(); - $this->objFromFixture(UserDefinedForm::class, 'form1')->publishRecursive(); + $udf = $this->objFromFixture(UserDefinedForm::class, 'form1'); + $udf->publishRecursive(); $this->controller = Injector::inst()->get(PartialSubmissionController::class); + + // Since parent is now created in UDF / Partial controllers, let's just set the parent here + $partialID = PartialFormSubmission::create([ + 'UserDefinedFormID' => $udf->ID, + 'UserDefinedFormClass' => $udf->ClassName, + 'ParentClass' => $udf->ClassName, + 'ParentID' => $udf->ID, + ])->write(); + $this->session()->set(PartialSubmissionController::SESSION_KEY, $partialID); } } diff --git a/tests/unit/PartialUserFormControllerTest.php b/tests/unit/PartialUserFormControllerTest.php index 185e385..fac2ff5 100644 --- a/tests/unit/PartialUserFormControllerTest.php +++ b/tests/unit/PartialUserFormControllerTest.php @@ -2,7 +2,9 @@ namespace Firesphere\PartialUserforms\Tests; +use Firesphere\PartialUserforms\Forms\PasswordForm; use Firesphere\PartialUserforms\Models\PartialFormSubmission; +use SilverStripe\Assets\File; use SilverStripe\Dev\FunctionalTest; use SilverStripe\UserForms\Model\UserDefinedForm; @@ -23,13 +25,8 @@ public function testPartialPage() $this->assertEquals(404, $result->getStatusCode()); } - /** - * @todo - */ public function testPartialValidKeyToken() { - $this->markTestSkipped('Revisit and set up themes for testing'); - $token = 'q1w2e3r4t5y6u7i8'; // No Parent $key = singleton(PartialFormSubmission::class)->generateKey($token); @@ -67,9 +64,24 @@ public function testPartialInvalidKey() $this->assertEquals(404, $result->getStatusCode()); } + public function testDataPopulated() + { + $partial = $this->objFromFixture(PartialFormSubmission::class, 'submission1'); + $key = $partial->generateKey($partial->Token); + + $response = $this->get("/partial/{$key}/{$partial->Token}"); + $this->assertEquals(200, $response->getStatusCode()); + + $this->assertCount(1, $partial->PartialUploads()); + $this->assertCount(3, $partial->PartialFields()); + + $content = $response->getBody(); + $this->assertContains('I have a question', $content); + $this->assertContains('Hans-fullsize-sqr.png', $content); + } + public function testPasswordProtectedPartial() { - $token = 'q1w2e3r4t5y6u7i8'; // Partial with UserDefinedForm $submission = $this->objFromFixture(PartialFormSubmission::class, 'submission1'); /** @var UserDefinedForm $parent */ @@ -77,8 +89,9 @@ public function testPasswordProtectedPartial() $parent->PasswordProtected = true; $parent->write(); $parent->publishRecursive(); - $key = $submission->generateKey($token); - $result = $this->get("partial/{$key}/{$token}"); + + $key = $submission->generateKey($submission->Token); + $result = $this->get("partial/{$key}/{$submission->Token}"); // Be redirected to the Password form $formOpeningTag = '
'; $this->assertContains($formOpeningTag, $result->getBody()); @@ -88,5 +101,6 @@ public function setUp() { parent::setUp(); $this->objFromFixture(UserDefinedForm::class, 'form1')->publishRecursive(); + $this->objFromFixture(File::class, 'file1')->publishRecursive(); } }