Insert Submission, new API
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
This commit is contained in:
parent
7462be7bfe
commit
e0d41aef0c
|
@ -32,7 +32,6 @@ return [
|
|||
['name' => 'page#getResult', 'url' => '/{hash}/results', 'verb' => 'GET'],
|
||||
|
||||
['name' => 'page#goto_form', 'url' => '/{hash}', 'verb' => 'GET'],
|
||||
['name' => 'page#insert_submission', 'url' => '/insert/submission', 'verb' => 'POST'],
|
||||
|
||||
// Forms
|
||||
['name' => 'api#getForms', 'url' => '/api/v1/forms', 'verb' => 'GET'],
|
||||
|
@ -53,6 +52,7 @@ return [
|
|||
['name' => 'api#deleteOption', 'url' => '/api/v1/option/{id}', 'verb' => 'DELETE'],
|
||||
|
||||
['name' => 'api#getSubmissions', 'url' => '/api/v1/submissions/{hash}', 'verb' => 'GET'],
|
||||
['name' => 'api#insertSubmission', 'url' => '/api/v1/submissions/insert', 'verb' => 'POST'],
|
||||
|
||||
['name' => 'system#get_site_users_and_groups', 'url' => '/get/siteusers', 'verb' => 'POST'],
|
||||
]
|
||||
|
|
|
@ -38,6 +38,7 @@ use OCP\AppFramework\Db\IMapperException;
|
|||
use OCP\ILogger;
|
||||
use OCP\IRequest;
|
||||
use OCP\IUser;
|
||||
use OCP\IUserSession;
|
||||
use OCP\Security\ISecureRandom;
|
||||
|
||||
use OCA\Forms\Db\Form;
|
||||
|
@ -62,12 +63,13 @@ class ApiController extends Controller {
|
|||
/** @var ILogger */
|
||||
private $logger;
|
||||
|
||||
/** @var string */
|
||||
private $userId;
|
||||
/** @var IUserSession */
|
||||
private $userSession;
|
||||
|
||||
public function __construct(
|
||||
IRequest $request,
|
||||
$userId,
|
||||
$userId, // TODO remove & replace with userSession below.
|
||||
IUserSession $userSession,
|
||||
FormMapper $formMapper,
|
||||
SubmissionMapper $submissionMapper,
|
||||
AnswerMapper $answerMapper,
|
||||
|
@ -77,6 +79,7 @@ class ApiController extends Controller {
|
|||
) {
|
||||
parent::__construct(Application::APP_ID, $request);
|
||||
$this->userId = $userId;
|
||||
$this->userSession = $userSession;
|
||||
$this->formMapper = $formMapper;
|
||||
$this->submissionMapper = $submissionMapper;
|
||||
$this->answerMapper = $answerMapper;
|
||||
|
@ -603,4 +606,84 @@ class ApiController extends Controller {
|
|||
|
||||
return new Http\JSONResponse($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
* @PublicPage
|
||||
*
|
||||
* Process a new submission
|
||||
* @param int $formId
|
||||
* @param array $answers [question_id => arrayOfString]
|
||||
*/
|
||||
public function insertSubmission(int $formId, array $answers) {
|
||||
$this->logger->debug('Inserting submission: formId: {formId}, answers: {answers}', [
|
||||
'formId' => $formId,
|
||||
'answers' => $answers,
|
||||
]);
|
||||
|
||||
try {
|
||||
$form = $this->formMapper->findById($formId);
|
||||
$questions = $this->getQuestions($formId);
|
||||
} catch (IMapperException $e) {
|
||||
$this->logger->debug('Could not find form');
|
||||
return new Http\JSONResponse(['message' => 'Could not find form'], Http::STATUS_BAD_REQUEST);
|
||||
}
|
||||
|
||||
$user = $this->userSession->getUser();
|
||||
// TODO check again hasUserAccess?!
|
||||
|
||||
// Create Submission
|
||||
$submission = new Submission();
|
||||
$submission->setFormId($formId);
|
||||
$submission->setTimestamp(time());
|
||||
|
||||
// If not logged in or anonymous use anonID
|
||||
if (!$user || $form->getIsAnonymous()){
|
||||
$anonID = "anon-user-". hash('md5', (time() + rand()));
|
||||
$submission->setUserId($anonID);
|
||||
} else {
|
||||
$submission->setUserId($user->getUID());
|
||||
}
|
||||
|
||||
// Insert new submission
|
||||
$this->submissionMapper->insert($submission);
|
||||
$submissionId = $submission->getId();
|
||||
|
||||
// Process Answers
|
||||
foreach($answers as $questionId => $answerArray) {
|
||||
// Search corresponding Question, skip processing if not found
|
||||
$questionIndex = array_search($questionId, array_column($questions, 'id'));
|
||||
if ($questionIndex === false) {
|
||||
continue;
|
||||
} else {
|
||||
$question = $questions[$questionIndex];
|
||||
}
|
||||
|
||||
foreach($answerArray as $answer) {
|
||||
if($question['type'] === 'multiple' || $question['type'] === 'multiple_unique') {
|
||||
// Search corresponding option, skip processing if not found
|
||||
$optionIndex = array_search($answer, array_column($question['options'], 'id'));
|
||||
if($optionIndex === false) {
|
||||
continue;
|
||||
} else {
|
||||
$option = $question['options'][$optionIndex];
|
||||
}
|
||||
|
||||
// Load option-text
|
||||
$answerText = $option['text'];
|
||||
|
||||
} else {
|
||||
$answerText = $answer; // Not a multiple-question, answerText is given answer
|
||||
}
|
||||
|
||||
$answerEntity = new Answer();
|
||||
$answerEntity->setSubmissionId($submissionId);
|
||||
$answerEntity->setQuestionId($question['id']);
|
||||
$answerEntity->setText($answerText);
|
||||
$this->answerMapper->insert($answerEntity);
|
||||
}
|
||||
}
|
||||
|
||||
return new Http\JSONResponse([]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,19 +29,16 @@
|
|||
|
||||
namespace OCA\Forms\Controller;
|
||||
|
||||
use Exception;
|
||||
use OCA\Forms\Db\Form;
|
||||
use OCA\Forms\Db\FormMapper;
|
||||
use OCA\Forms\Db\Submission;
|
||||
use OCA\Forms\Db\SubmissionMapper;
|
||||
use OCA\Forms\Db\Answer;
|
||||
use OCA\Forms\Db\AnswerMapper;
|
||||
|
||||
use OCA\Forms\Db\OptionMapper;
|
||||
use OCA\Forms\Db\Question;
|
||||
use OCA\Forms\Db\QuestionMapper;
|
||||
use OCA\Forms\Db\Option;
|
||||
use OCA\Forms\Db\OptionMapper;
|
||||
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Http\RedirectResponse;
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
use OCP\IGroupManager;
|
||||
use OCP\IRequest;
|
||||
|
@ -57,12 +54,6 @@ class PageController extends Controller {
|
|||
/** @var FormMapper */
|
||||
private $formMapper;
|
||||
|
||||
/** @var SubmissionMapper */
|
||||
private $submissionMapper;
|
||||
|
||||
/** @var AnswerMapper */
|
||||
private $answerMapper;
|
||||
|
||||
/** @var IURLGenerator */
|
||||
private $urlGenerator;
|
||||
|
||||
|
@ -82,8 +73,6 @@ class PageController extends Controller {
|
|||
FormMapper $formMapper,
|
||||
QuestionMapper $questionMapper,
|
||||
OptionMapper $optionMapper,
|
||||
SubmissionMapper $SubmissionMapper,
|
||||
AnswerMapper $AnswerMapper,
|
||||
IUserSession $userSession,
|
||||
IInitialStateService $initialStateService) {
|
||||
parent::__construct($appName, $request);
|
||||
|
@ -94,8 +83,6 @@ class PageController extends Controller {
|
|||
$this->formMapper = $formMapper;
|
||||
$this->questionMapper = $questionMapper;
|
||||
$this->optionMapper = $optionMapper;
|
||||
$this->submissionMapper = $SubmissionMapper;
|
||||
$this->answerMapper = $AnswerMapper;
|
||||
$this->userSession = $userSession;
|
||||
$this->initialStateService = $initialStateService;
|
||||
}
|
||||
|
@ -237,62 +224,6 @@ class PageController extends Controller {
|
|||
return new TemplateResponse($this->appName, 'main', [], $renderAs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
* @PublicPage
|
||||
*
|
||||
* Process a new submission
|
||||
* @param int $formId
|
||||
* @param string $userId
|
||||
* @param array $answers
|
||||
* @param array $questions
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
public function insertSubmission(int $id, string $userId, array $answers, array $questions) {
|
||||
|
||||
$form = $this->formMapper->findById($id);
|
||||
$anonID = "anon-user-". hash('md5', (time() + rand()));
|
||||
|
||||
// Insert Submission
|
||||
$submission = new Submission();
|
||||
$submission->setFormId($id);
|
||||
if ($form->getIsAnonymous()){
|
||||
$submission->setUserId($anonID);
|
||||
|
||||
} else {
|
||||
$submission->setUserId($userId);
|
||||
}
|
||||
$submission->setTimestamp(time());
|
||||
$this->submissionMapper->insert($submission);
|
||||
$submissionId = $submission->getId();
|
||||
|
||||
//Insert Answers
|
||||
foreach($questions as $question) {
|
||||
// If question is answered, the questionText exists as key in $answers. Does not exist, when a (non-mandatory) question was not answered.
|
||||
if (array_key_exists($question['text'], $answers)) {
|
||||
if($question['type'] === "checkbox"){
|
||||
foreach(($answers[$question['text']]) as $ansText) {
|
||||
$answer = new Answer();
|
||||
$answer->setSubmissionId($submissionId);
|
||||
$answer->setQuestionId($question['id']);
|
||||
$answer->setText($ansText);
|
||||
$this->answerMapper->insert($answer);
|
||||
}
|
||||
} else {
|
||||
$answer = new Answer();
|
||||
$answer->setSubmissionId($submissionId);
|
||||
$answer->setQuestionId($question['id']);
|
||||
$answer->setText($answers[$question['text']]);
|
||||
$this->answerMapper->insert($answer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$hash = $form->getHash();
|
||||
$url = $this->urlGenerator->linkToRoute('forms.page.goto_form', ['hash' => $hash]);
|
||||
return new RedirectResponse($url);
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
* Check if user has access to this form
|
||||
|
|
|
@ -43,7 +43,8 @@
|
|||
}"
|
||||
:name="`${id}-answer`"
|
||||
:required="true /* TODO: implement required option */"
|
||||
:type="isUnique ? 'radio' : 'checkbox'">
|
||||
:type="isUnique ? 'radio' : 'checkbox'"
|
||||
@change="onChange($event, answer.id)">
|
||||
<label v-if="!edit"
|
||||
ref="label"
|
||||
:for="`${id}-answer-${answer.id}`"
|
||||
|
@ -126,6 +127,26 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
onChange(event, answerId) {
|
||||
const isChecked = event.target.checked === true
|
||||
let values = this.values.slice()
|
||||
|
||||
// Radio
|
||||
if (this.isUnique) {
|
||||
this.$emit('update:values', [answerId])
|
||||
return
|
||||
}
|
||||
|
||||
// Checkbox
|
||||
if (isChecked) {
|
||||
values.push(answerId)
|
||||
} else {
|
||||
values = values.filter(id => id !== answerId)
|
||||
}
|
||||
|
||||
// Emit values and remove duplicates
|
||||
this.$emit('update:values', [...new Set(values)])
|
||||
},
|
||||
|
||||
/**
|
||||
* Is the provided index checked
|
||||
|
|
|
@ -392,7 +392,7 @@ export default {
|
|||
font-size: 2em;
|
||||
font-weight: bold;
|
||||
padding-left: 14px; // align with description (compensate font size diff)
|
||||
overflow: hidden;
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
|
|
@ -44,9 +44,14 @@
|
|||
:read-only="true"
|
||||
:model="answerTypes[question.type]"
|
||||
:index="index + 1"
|
||||
v-bind="question" />
|
||||
v-bind="question"
|
||||
:values.sync="answers[question.id]" />
|
||||
</ul>
|
||||
<input class="primary" type="submit" :value="t('forms', 'Submit')" :aria-label="t('forms', 'Submit form')">
|
||||
<input class="primary"
|
||||
type="submit"
|
||||
:value="t('forms', 'Submit')"
|
||||
:disabled="loading"
|
||||
:aria-label="t('forms', 'Submit form')">
|
||||
</form>
|
||||
</AppContent>
|
||||
</Content>
|
||||
|
@ -54,12 +59,14 @@
|
|||
|
||||
<script>
|
||||
import { loadState } from '@nextcloud/initial-state'
|
||||
|
||||
import answerTypes from '../models/AnswerTypes'
|
||||
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import { showError } from '@nextcloud/dialogs'
|
||||
import axios from '@nextcloud/axios'
|
||||
import AppContent from '@nextcloud/vue/dist/Components/AppContent'
|
||||
import Content from '@nextcloud/vue/dist/Components/Content'
|
||||
|
||||
import answerTypes from '../models/AnswerTypes'
|
||||
|
||||
import Question from '../components/Questions/Question'
|
||||
import QuestionLong from '../components/Questions/QuestionLong'
|
||||
import QuestionShort from '../components/Questions/QuestionShort'
|
||||
|
@ -81,6 +88,9 @@ export default {
|
|||
return {
|
||||
form: loadState('forms', 'form'),
|
||||
answerTypes,
|
||||
answers: {},
|
||||
loading: false,
|
||||
success: false,
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -102,8 +112,24 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
onSubmit(e) {
|
||||
console.info(e)
|
||||
/**
|
||||
* Submit the form after the browser validated it 🚀
|
||||
*/
|
||||
async onSubmit() {
|
||||
this.loading = true
|
||||
|
||||
try {
|
||||
await axios.post(generateUrl('/apps/forms/api/v1/submissions/insert'), {
|
||||
formId: this.form.id,
|
||||
answers: this.answers,
|
||||
})
|
||||
this.success = true
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
showError(t('forms', 'There was an error submitting the form'))
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
|
|
Loading…
Reference in a new issue