Skip to content

Commit

Permalink
Merge pull request #1 from torque-foxes/feature/link-file-submission
Browse files Browse the repository at this point in the history
Allow overwrite file uploads and link file to submitted form
  • Loading branch information
scott1702 committed Sep 8, 2019
2 parents 5d79cef + 510c3fb commit 5982f65
Show file tree
Hide file tree
Showing 15 changed files with 335 additions and 118 deletions.
2 changes: 1 addition & 1 deletion client/dist/main.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions client/src/js/partialuserforms/partialsubmission.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
48 changes: 32 additions & 16 deletions src/controllers/PartialSubmissionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
}
Expand Down
97 changes: 58 additions & 39 deletions src/controllers/PartialUserFormController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@
use Page;
use SilverStripe\Control\HTTPRequest;
use Firesphere\PartialUserforms\Forms\PasswordForm;
use SilverStripe\Control\Director;
use SilverStripe\Control\HTTPResponse;
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
Expand All @@ -30,16 +31,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
Expand All @@ -54,20 +52,29 @@ 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 &&

Director::set_current_page($controller->data());

// 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) {
Expand Down Expand Up @@ -96,6 +103,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
*
Expand All @@ -115,37 +160,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;
}
}
4 changes: 3 additions & 1 deletion src/controllers/PartialUserFormVerifyController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand All @@ -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']);

Expand Down
22 changes: 20 additions & 2 deletions src/extensions/SubmittedFormExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}
Expand All @@ -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);
}
}
Loading

0 comments on commit 5982f65

Please sign in to comment.