Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow overwrite file uploads and link file to submitted form #32

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
94 changes: 55 additions & 39 deletions src/controllers/PartialUserFormController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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) {
Expand Down Expand Up @@ -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
*
Expand All @@ -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;
}
}
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