From 03e9ff4a86556c39cd89a1f27b0e2f296acef35a Mon Sep 17 00:00:00 2001 From: Jonas Rittershofer Date: Mon, 30 Mar 2020 13:48:14 +0200 Subject: [PATCH] Alter Database Signed-off-by: Jonas Rittershofer --- appinfo/info.xml | 2 +- appinfo/routes.php | 10 +- css/{vote.scss => submit.scss} | 0 js/{vote.js => submit.js} | 12 +- lib/Controller/ApiController.php | 281 +++++++------- lib/Controller/PageController.php | 150 ++++---- lib/Db/Answer.php | 29 +- lib/Db/AnswerMapper.php | 83 +---- lib/Db/{Event.php => Form.php} | 61 ++- lib/Db/{EventMapper.php => FormMapper.php} | 46 +-- lib/Db/Option.php | 58 +++ lib/Db/OptionMapper.php | 84 +++++ lib/Db/Question.php | 31 +- lib/Db/QuestionMapper.php | 21 +- lib/Db/{Vote.php => Submission.php} | 34 +- .../{VoteMapper.php => SubmissionMapper.php} | 73 ++-- .../Version0009Date20190000000006.php | 210 ----------- .../Version010102Date20200323120846.php | 4 +- .../Version010200Date2020323141300.php | 352 ++++++++++++++++++ src/Forms.vue | 4 +- src/components/_base-CloudDiv.vue | 12 +- src/components/formListItem.vue | 35 +- src/components/quizFormItem.vue | 31 +- src/components/resultItem.vue | 14 +- src/utils/FormsUtils.js | 9 +- src/views/Create.vue | 134 +++---- src/views/List.vue | 21 +- src/views/Results.vue | 55 +-- src/views/Sidebar.vue | 40 +- templates/{vote.tmpl.php => submit.tmpl.php} | 6 +- 30 files changed, 1060 insertions(+), 842 deletions(-) rename css/{vote.scss => submit.scss} (100%) rename js/{vote.js => submit.js} (88%) rename lib/Db/{Event.php => Form.php} (73%) rename lib/Db/{EventMapper.php => FormMapper.php} (67%) create mode 100644 lib/Db/Option.php create mode 100644 lib/Db/OptionMapper.php rename lib/Db/{Vote.php => Submission.php} (55%) rename lib/Db/{VoteMapper.php => SubmissionMapper.php} (54%) delete mode 100644 lib/Migration/Version0009Date20190000000006.php create mode 100644 lib/Migration/Version010200Date2020323141300.php rename templates/{vote.tmpl.php => submit.tmpl.php} (92%) diff --git a/appinfo/info.xml b/appinfo/info.xml index 2bdd6a8..a6143de 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -5,7 +5,7 @@ Forms A forms app, similar to Google Forms. A forms app, similar to Google Forms with the possibility to restrict access (members, certain groups/users, and public). - 1.1.2 + 1.2.0 agpl Vinzenz Rosenkranz René Gieling diff --git a/appinfo/routes.php b/appinfo/routes.php index 8c77aed..5adbf4d 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -33,23 +33,23 @@ return [ ['name' => 'page#goto_form', 'url' => '/{hash}', 'verb' => 'GET'], - ['name' => 'page#insert_vote', 'url' => '/insert/vote', 'verb' => 'POST'], + ['name' => 'page#insert_submission', 'url' => '/insert/submission', 'verb' => 'POST'], ['name' => 'page#search', 'url' => '/search', 'verb' => 'POST'], ['name' => 'page#get_display_name', 'url' => '/get/displayname', 'verb' => 'POST'], ['name' => 'api#write_form', 'url' => '/write/form', 'verb' => 'POST'], - ['name' => 'api#get_form', 'url' => '/get/form/{formIdOrHash}', 'verb' => 'GET'], + ['name' => 'api#get_full_form', 'url' => '/get/fullform/{formIdOrHash}', 'verb' => 'GET'], ['name' => 'api#get_options', 'url' => '/get/options/{formId}', 'verb' => 'GET'], ['name' => 'api#get_shares', 'url' => '/get/shares/{formId}', 'verb' => 'GET'], - ['name' => 'api#get_event', 'url' => '/get/event/{formId}', 'verb' => 'GET'], + ['name' => 'api#get_form', 'url' => '/get/form/{formId}', 'verb' => 'GET'], ['name' => 'api#get_forms', 'url' => '/get/forms', 'verb' => 'GET'], ['name' => 'api#newForm', 'url' => 'api/v1/form', 'verb' => 'POST'], ['name' => 'api#deleteForm', 'url' => 'api/v1/form/{id}', 'verb' => 'DELETE'], ['name' => 'api#newQuestion', 'url' => 'api/v1/question/', 'verb' => 'POST'], ['name' => 'api#deleteQuestion', 'url' => 'api/v1/question/{id}', 'verb' => 'DELETE'], - ['name' => 'api#newAnswer', 'url' => 'api/v1/answer/', 'verb' => 'POST'], - ['name' => 'api#deleteAnswer', 'url' => 'api/v1/answer/{id}', 'verb' => 'DELETE'], + ['name' => 'api#newOption', 'url' => 'api/v1/option/', 'verb' => 'POST'], + ['name' => 'api#deleteOption', 'url' => 'api/v1/option/{id}', 'verb' => 'DELETE'], ['name' => 'api#getSubmissions', 'url' => 'api/v1/submissions/{hash}', 'verb' => 'GET'], ['name' => 'system#get_site_users_and_groups', 'url' => '/get/siteusers', 'verb' => 'POST'], diff --git a/css/vote.scss b/css/submit.scss similarity index 100% rename from css/vote.scss rename to css/submit.scss diff --git a/js/vote.js b/js/submit.js similarity index 88% rename from js/vote.js rename to js/submit.js index 96cf0b2..a86d81c 100644 --- a/js/vote.js +++ b/js/submit.js @@ -8,7 +8,7 @@ function sendDataToServer(survey) { form.userId = 'anon_' + Date.now() + '_' + Math.floor(Math.random() * 10000) } form.questions = questions; - $.post(OC.generateUrl('apps/forms/insert/vote'), form) + $.post(OC.generateUrl('apps/forms/insert/submission'), form) .then((response) => { }, (error) => { /* eslint-disable-next-line no-console */ @@ -34,7 +34,7 @@ function cssUpdate(survey, options){ $(document).ready(function () { var formJSON = $('#surveyContainer').attr('form') var questionJSON = $('#surveyContainer').attr('questions') - + form = JSON.parse(formJSON) questions = JSON.parse(questionJSON) @@ -45,11 +45,11 @@ $(document).ready(function () { }; questions.forEach(q => { - var ans = [] - q.answers.forEach(a => { - ans.push(a.text); + var qChoices = [] + q.options.forEach(o => { + qChoices.push(o.text); }); - surveyJSON.questions.push({type: q.type, name: q.text, choices: ans, isRequired: 'true'}); + surveyJSON.questions.push({type: q.type, name: q.text, choices: qChoices, isRequired: 'true'}); }); $('#surveyContainer').Survey({ diff --git a/lib/Controller/ApiController.php b/lib/Controller/ApiController.php index 1115987..20f4721 100644 --- a/lib/Controller/ApiController.php +++ b/lib/Controller/ApiController.php @@ -41,14 +41,17 @@ use OCP\IUser; use OCP\IUserManager; use OCP\Security\ISecureRandom; -use OCA\Forms\Db\Event; -use OCA\Forms\Db\EventMapper; -use OCA\Forms\Db\VoteMapper; +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\Question; use OCA\Forms\Db\QuestionMapper; -use OCA\Forms\Db\Answer; -use OCA\Forms\Db\AnswerMapper; +use OCA\Forms\Db\Option; +use OCA\Forms\Db\OptionMapper; use OCP\Util; @@ -56,10 +59,11 @@ class ApiController extends Controller { private $groupManager; private $userManager; - private $eventMapper; - private $voteMapper; - private $questionMapper; + private $formMapper; + private $submissionMapper; private $answerMapper; + private $questionMapper; + private $optionMapper; /** @var ILogger */ private $logger; @@ -74,10 +78,11 @@ class ApiController extends Controller { * @param IRequest $request * @param IUserManager $userManager * @param string $userId - * @param EventMapper $eventMapper - * @param VoteMapper $voteMapper - * @param QuestionMapper $questionMapper + * @param FormMapper $formMapper + * @param SubmissionMapper $submissionMapper * @param AnswerMapper $answerMapper + * @param QuestionMapper $questionMapper + * @param OptionMapper $optionMapper */ public function __construct( $appName, @@ -85,20 +90,22 @@ class ApiController extends Controller { IRequest $request, IUserManager $userManager, $userId, - EventMapper $eventMapper, - VoteMapper $voteMapper, - QuestionMapper $questionMapper, + FormMapper $formMapper, + SubmissionMapper $submissionMapper, AnswerMapper $answerMapper, + QuestionMapper $questionMapper, + OptionMapper $optionMapper, ILogger $logger ) { parent::__construct($appName, $request); $this->userId = $userId; $this->groupManager = $groupManager; $this->userManager = $userManager; - $this->eventMapper = $eventMapper; - $this->voteMapper = $voteMapper; - $this->questionMapper = $questionMapper; + $this->formMapper = $formMapper; + $this->submissionMapper = $submissionMapper; $this->answerMapper = $answerMapper; + $this->questionMapper = $questionMapper; + $this->optionMapper = $optionMapper; $this->logger = $logger; } @@ -108,8 +115,8 @@ class ApiController extends Controller { * @param string $item * @return Array */ - private function convertAccessList($item) { - $split = array(); + private function convertAccessList($item) : array { + $split = []; if (strpos($item, 'user_') === 0) { $user = $this->userManager->get(substr($item, 5)); $split = [ @@ -172,11 +179,11 @@ class ApiController extends Controller { /** * Set the access right of the current user for the form - * @param Array $event + * @param Array $form * @param Array $shares * @return String */ - private function grantAccessAs($event, $shares) { + private function grantAccessAs($form, $shares) { if (!\OC::$server->getUserSession()->getUser() instanceof IUser) { $currentUser = ''; } else { @@ -185,13 +192,13 @@ class ApiController extends Controller { $grantAccessAs = 'none'; - if ($event['owner'] === $currentUser) { + if ($form['ownerId'] === $currentUser) { $grantAccessAs = 'owner'; - } elseif ($event['access'] === 'public') { + } elseif ($form['access'] === 'public') { $grantAccessAs = 'public'; - } elseif ($event['access'] === 'registered' && \OC::$server->getUserSession()->getUser() instanceof IUser) { + } elseif ($form['access'] === 'registered' && \OC::$server->getUserSession()->getUser() instanceof IUser) { $grantAccessAs = 'registered'; - } elseif ($event['access'] === 'hidden' && ($event['owner'] === \OC::$server->getUserSession()->getUser())) { + } elseif ($form['access'] === 'hidden' && ($form['ownerId'] === \OC::$server->getUserSession()->getUser())) { $grantAccessAs = 'hidden'; } elseif ($this->checkUserAccess($shares)) { $grantAccessAs = 'userInvitation'; @@ -210,11 +217,11 @@ class ApiController extends Controller { * @param Integer $formId * @return Array */ - public function getEvent($formId) { + public function getForm($formId) { $data = array(); try { - $data = $this->eventMapper->find($formId)->read(); + $data = $this->formMapper->find($formId)->read(); } catch (DoesNotExistException $e) { // return silently } finally { @@ -234,7 +241,7 @@ class ApiController extends Controller { $accessList = array(); try { - $form = $this->eventMapper->find($formId); + $form = $this->formMapper->find($formId); if (!strpos('|public|hidden|registered', $form->getAccess())) { $accessList = explode(';', $form->getAccess()); $accessList = array_filter($accessList); @@ -248,14 +255,14 @@ class ApiController extends Controller { } - public function getQuestions($formId) { - $questionList = array(); + public function getQuestions($formId) : array { + $questionList = []; try{ - $questions = $this->questionMapper->findByForm($formId); - foreach ($questions as $questionElement) { - $temp = $questionElement->read(); - $temp['answers'] = $this->getAnswers($formId, $temp['id']); - $questionList[] = $temp; + $questionEntities = $this->questionMapper->findByForm($formId); + foreach ($questionEntities as $questionEntity) { + $question = $questionEntity->read(); + $question['options'] = $this->getOptions($question['id']); + $questionList[] = $question; } } catch (DoesNotExistException $e) { @@ -265,18 +272,18 @@ class ApiController extends Controller { } } - public function getAnswers($formId, $questionId) { - $answerList = array(); + public function getOptions($questionId) : array { + $optionList = []; try{ - $answers = $this->answerMapper->findByForm($formId, $questionId); - foreach ($answers as $answerElement) { - $answerList[] = $answerElement->read(); + $optionEntities = $this->optionMapper->findByQuestion($questionId); + foreach ($optionEntities as $optionEntity) { + $optionList[] = $optionEntity->read(); } } catch (DoesNotExistException $e) { //handle silently }finally{ - return $answerList; + return $optionList; } } @@ -286,7 +293,7 @@ class ApiController extends Controller { * @param String $formIdOrHash form id or hash * @return Array */ - public function getForm($formIdOrHash) { + public function getFullForm($formIdOrHash) { if (!\OC::$server->getUserSession()->getUser() instanceof IUser) { $currentUser = ''; @@ -299,32 +306,30 @@ class ApiController extends Controller { try { if (is_numeric($formIdOrHash)) { - $formId = $this->eventMapper->find(intval($formIdOrHash))->id; + $formId = $this->formMapper->find(intval($formIdOrHash))->id; $result = 'foundById'; } else { - $formId = $this->eventMapper->findByHash($formIdOrHash)->id; + $formId = $this->formMapper->findByHash($formIdOrHash)->id; $result = 'foundByHash'; } - $event = $this->getEvent($formId); - $shares = $this->getShares($event['id']); + $form = $this->getForm($formId); + $shares = $this->getShares($form['id']); - if ($event['owner'] !== $currentUser && !$this->groupManager->isAdmin($currentUser)) { + if ($form['ownerId'] !== $currentUser && !$this->groupManager->isAdmin($currentUser)) { $mode = 'create'; } else { $mode = 'edit'; } $data = [ - 'id' => $event['id'], + 'id' => $form['id'], 'result' => $result, - 'grantedAs' => $this->grantAccessAs($event, $shares), + 'grantedAs' => $this->grantAccessAs($form, $shares), 'mode' => $mode, - 'event' => $event, + 'form' => $form, 'shares' => $shares, - 'options' => [ - 'formQuizQuestions' => $this->getQuestions($event['id']) - ] + 'questions' => $this->getQuestions($form['id']), ]; } catch (DoesNotExistException $e) { $data['form'] = ['result' => 'notFound']; @@ -345,21 +350,20 @@ class ApiController extends Controller { } try { - $events = $this->eventMapper->findAll(); + $forms = $this->formMapper->findAll(); } catch (DoesNotExistException $e) { return new DataResponse($e, Http::STATUS_NOT_FOUND); } - $eventsList = array(); - - foreach ($events as $eventElement) { - $event = $this->getForm($eventElement->id); - //if ($event['grantedAs'] !== 'none') { - $eventsList[] = $event; + $formsList = array(); + foreach ($forms as $formElement) { + $form = $this->getFullForm($formElement->id); + //if ($form['grantedAs'] !== 'none') { + $formsList[] = $form; //} } - return new DataResponse($eventsList, Http::STATUS_OK); + return new DataResponse($formsList, Http::STATUS_OK); } /** @@ -370,17 +374,16 @@ class ApiController extends Controller { */ public function deleteForm(int $id) { try { - $formToDelete = $this->eventMapper->find($id); + $formToDelete = $this->formMapper->find($id); } catch (DoesNotExistException $e) { return new Http\JSONResponse([], Http::STATUS_NOT_FOUND); } - if ($this->userId !== $formToDelete->getOwner() && !$this->groupManager->isAdmin($this->userId)) { + if ($this->userId !== $formToDelete->getOwnerId() && !$this->groupManager->isAdmin($this->userId)) { return new DataResponse(null, Http::STATUS_UNAUTHORIZED); } - $this->voteMapper->deleteByForm($id); + $this->submissionMapper->deleteByForm($id); $this->questionMapper->deleteByForm($id); - $this->answerMapper->deleteByForm($id); - $this->eventMapper->delete($formToDelete); + $this->formMapper->delete($formToDelete); return new DataResponse(array( 'id' => $id, 'action' => 'deleted' @@ -391,30 +394,30 @@ class ApiController extends Controller { /** * Write form (create/update) * @NoAdminRequired - * @param Array $event + * @param Array $form * @param Array $options * @param Array $shares * @param String $mode * @return DataResponse */ - public function writeForm($event, $options, $shares, $mode) { + public function writeForm($form, $questions, $shares, $mode) { if (!\OC::$server->getUserSession()->getUser() instanceof IUser) { return new DataResponse(null, Http::STATUS_UNAUTHORIZED); } else { $currentUser = \OC::$server->getUserSession()->getUser()->getUID(); - $AdminAccess = $this->groupManager->isAdmin($currentUser); + $adminAccess = $this->groupManager->isAdmin($currentUser); } - $newEvent = new Event(); + $newForm = new Form(); // Set the configuration options entered by the user - $newEvent->setTitle($event['title']); - $newEvent->setDescription($event['description']); + $newForm->setTitle($form['title']); + $newForm->setDescription($form['description']); - $newEvent->setIsAnonymous($event['isAnonymous']); - $newEvent->setUnique($event['unique']); + $newForm->setIsAnonymous($form['isAnonymous']); + $newForm->setSubmitOnce($form['submitOnce']); - if ($event['access'] === 'select') { + if ($form['access'] === 'select') { $shareAccess = ''; foreach ($shares as $shareElement) { if ($shareElement['type'] === 'user') { @@ -423,50 +426,50 @@ class ApiController extends Controller { $shareAccess = $shareAccess . 'group_' . $shareElement['id'] . ';'; } } - $newEvent->setAccess(rtrim($shareAccess, ';')); + $newForm->setAccess(rtrim($shareAccess, ';')); } else { - $newEvent->setAccess($event['access']); + $newForm->setAccess($form['access']); } - if ($event['expiration']) { - $newEvent->setExpire(date('Y-m-d H:i:s', strtotime($event['expirationDate']))); + if ($form['expires']) { + $newForm->setExpirationDate(date('Y-m-d H:i:s', strtotime($form['expirationDate']))); } else { - $newEvent->setExpire(null); + $newForm->setExpirationDate(null); } if ($mode === 'edit') { // Edit existing form - $oldForm = $this->eventMapper->findByHash($event['hash']); + $oldForm = $this->formMapper->findByHash($form['hash']); // Check if current user is allowed to edit existing form - if ($oldForm->getOwner() !== $currentUser && !$AdminAccess) { + if ($oldForm->getOwnerId() !== $currentUser && !$adminAccess) { // If current user is not owner of existing form deny access return new DataResponse(null, Http::STATUS_UNAUTHORIZED); } // else take owner, hash and id of existing form - $newEvent->setOwner($oldForm->getOwner()); - $newEvent->setHash($oldForm->getHash()); - $newEvent->setId($oldForm->getId()); - $this->eventMapper->update($newEvent); + $newForm->setOwnerId($oldForm->getOwnerId()); + $newForm->setHash($oldForm->getHash()); + $newForm->setId($oldForm->getId()); + $this->formMapper->update($newForm); } elseif ($mode === 'create') { // Create new form // Define current user as owner, set new creation date and create a new hash - $newEvent->setOwner($currentUser); - $newEvent->setCreated(date('Y-m-d H:i:s')); - $newEvent->setHash(\OC::$server->getSecureRandom()->generate( + $newForm->setOwnerId($currentUser); + $newForm->setCreated(date('Y-m-d H:i:s')); + $newForm->setHash(\OC::$server->getSecureRandom()->generate( 16, ISecureRandom::CHAR_DIGITS . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER )); - $newEvent = $this->eventMapper->insert($newEvent); + $newForm = $this->formMapper->insert($newForm); } return new DataResponse(array( - 'id' => $newEvent->getId(), - 'hash' => $newEvent->getHash() + 'id' => $newForm->getId(), + 'hash' => $newForm->getHash() ), Http::STATUS_OK); } @@ -475,22 +478,22 @@ class ApiController extends Controller { * @NoAdminRequired */ public function newForm(): Http\JSONResponse { - $event = new Event(); + $form = new Form(); $currentUser = \OC::$server->getUserSession()->getUser()->getUID(); - $event->setOwner($currentUser); - $event->setCreated(date('Y-m-d H:i:s')); - $event->setHash(\OC::$server->getSecureRandom()->generate( + $form->setOwnerId($currentUser); + $form->setCreated(date('Y-m-d H:i:s')); + $form->setHash(\OC::$server->getSecureRandom()->generate( 16, ISecureRandom::CHAR_HUMAN_READABLE )); - $event->setTitle('New form'); - $event->setDescription(''); - $event->setAccess('public'); + $form->setTitle('New form'); + $form->setDescription(''); + $form->setAccess('public'); - $this->eventMapper->insert($event); + $this->formMapper->insert($form); - return new Http\JSONResponse($this->getForm($event->getHash())); + return new Http\JSONResponse($this->getFullForm($form->getHash())); } /** @@ -504,13 +507,13 @@ class ApiController extends Controller { ]); try { - $form = $this->eventMapper->find($formId); + $form = $this->formMapper->find($formId); } catch (IMapperException $e) { $this->logger->debug('Could not find form'); return new Http\JSONResponse([], Http::STATUS_BAD_REQUEST); } - if ($form->getOwner() !== $this->userId) { + if ($form->getOwnerId() !== $this->userId) { $this->logger->debug('This form is not owned by the current user'); return new Http\JSONResponse([], Http::STATUS_FORBIDDEN); } @@ -518,8 +521,8 @@ class ApiController extends Controller { $question = new Question(); $question->setFormId($formId); - $question->setFormQuestionType($type); - $question->setFormQuestionText($text); + $question->setType($type); + $question->setText($text); $question = $this->questionMapper->insert($question); @@ -536,18 +539,18 @@ class ApiController extends Controller { try { $question = $this->questionMapper->findById($id); - $form = $this->eventMapper->find($question->getFormId()); + $form = $this->formMapper->find($question->getFormId()); } catch (IMapperException $e) { - $this->logger->debug('Could not find form or question of this answer'); + $this->logger->debug('Could not find form or question'); return new Http\JSONResponse([], Http::STATUS_BAD_REQUEST); } - if ($form->getOwner() !== $this->userId) { + if ($form->getOwnerId() !== $this->userId) { $this->logger->debug('This form is not owned by the current user'); return new Http\JSONResponse([], Http::STATUS_FORBIDDEN); } - $this->answerMapper->deleteByQuestion($id); + $this->optionMapper->deleteByQuestion($id); $this->questionMapper->delete($question); return new Http\JSONResponse($id); @@ -556,64 +559,64 @@ class ApiController extends Controller { /** * @NoAdminRequired */ - public function newAnswer(int $formId, int $questionId, string $text): Http\JSONResponse { - $this->logger->debug('Adding new answer: formId: {formId}, questoinId: {questionId}, text: {text}', [ + public function newOption(int $formId, int $questionId, string $text): Http\JSONResponse { + $this->logger->debug('Adding new option: formId: {formId}, questionId: {questionId}, text: {text}', [ 'formId' => $formId, 'questionId' => $questionId, 'text' => $text, ]); try { - $form = $this->eventMapper->find($formId); + $form = $this->formMapper->find($formId); $question = $this->questionMapper->findById($questionId); } catch (IMapperException $e) { - $this->logger->debug('Could not find form or question so answer can\'t be added'); + $this->logger->debug('Could not find form or question so option can\'t be added'); return new Http\JSONResponse([], Http::STATUS_BAD_REQUEST); } - if ($form->getOwner() !== $this->userId) { + if ($form->getOwnerId() !== $this->userId) { $this->logger->debug('This form is not owned by the current user'); return new Http\JSONResponse([], Http::STATUS_FORBIDDEN); } if ($question->getFormId() !== $formId) { - $this->logger->debug('This question is not owned by the current user'); + $this->logger->debug('This question is not part of the current form'); return new Http\JSONResponse([], Http::STATUS_FORBIDDEN); } - $answer = new Answer(); + $option = new Option(); - $answer->setFormId($formId); - $answer->setQuestionId($questionId); - $answer->setText($text); + $option->setQuestionId($questionId); + $option->setText($text); - $answer = $this->answerMapper->insert($answer); + $option = $this->optionMapper->insert($option); - return new Http\JSONResponse($answer->getId()); + return new Http\JSONResponse($option->getId()); } /** * @NoAdminRequired */ - public function deleteAnswer(int $id): Http\JSONResponse { - $this->logger->debug('Deleting answer: {id}', [ + public function deleteOption(int $id): Http\JSONResponse { + $this->logger->debug('Deleting option: {id}', [ 'id' => $id ]); try { - $answer = $this->answerMapper->findById($id); - $form = $this->eventMapper->find($answer->getFormId()); + $option = $this->optionMapper->findById($id); + $question = $this->questionMapper->findById($option->getQuestionId()); + $form = $this->formMapper->find($question->getFormId()); } catch (IMapperException $e) { - $this->logger->debug('Could not find form or answer'); + $this->logger->debug('Could not find form or option'); return new Http\JSONResponse([], Http::STATUS_BAD_REQUEST); } - if ($form->getOwner() !== $this->userId) { + if ($form->getOwnerId() !== $this->userId) { $this->logger->debug('This form is not owned by the current user'); return new Http\JSONResponse([], Http::STATUS_FORBIDDEN); } - $this->answerMapper->delete($answer); + $this->optionMapper->delete($option); //TODO useful response return new Http\JSONResponse($id); @@ -624,20 +627,30 @@ class ApiController extends Controller { */ public function getSubmissions(string $hash): Http\JSONResponse { try { - $form = $this->eventMapper->findByHash($hash); + $form = $this->formMapper->findByHash($hash); } catch (IMapperException $e) { return new Http\JSONResponse([], Http::STATUS_BAD_REQUEST); } - if ($form->getOwner() !== $this->userId) { + if ($form->getOwnerId() !== $this->userId) { return new Http\JSONResponse([], Http::STATUS_FORBIDDEN); } - $votes = $this->voteMapper->findByForm($form->getId()); - $result = []; - foreach ($votes as $vote) { - $result[] = $vote->read(); + $submissionList = $this->submissionMapper->findByForm($form->getId()); + foreach ($submissionList as $submissionEntity) { + $answerList = $this->answerMapper->findBySubmission($submissionEntity->id); + foreach ($answerList as $answerEntity) { + $answer = $answerEntity->read(); + //Temporary Adapt Data to be usable by old Results-View + $answer['userId'] = $submissionEntity->getUserId(); + + $question = $this->questionMapper->findById($answer['questionId']); + $answer['questionText'] = $question->getText(); + $answer['questionType'] = $question->getType(); + + $result[] = $answer; + } } return new Http\JSONResponse($result); diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index 982ffb0..2994205 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -29,12 +29,14 @@ namespace OCA\Forms\Controller; use OCA\Forms\AppInfo\Application; -use OCA\Forms\Db\Event; -use OCA\Forms\Db\EventMapper; -use OCA\Forms\Db\Vote; -use OCA\Forms\Db\VoteMapper; - +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\QuestionMapper; use OCP\AppFramework\Controller; @@ -53,11 +55,12 @@ use OCP\Util; class PageController extends Controller { private $userId; - private $eventMapper; - private $voteMapper; + private $formMapper; + private $submissionMapper; + private $answerMapper; private $questionMapper; - private $answerMapper; + private $optionMapper; private $urlGenerator; private $userMgr; @@ -69,21 +72,24 @@ class PageController extends Controller { IGroupManager $groupManager, IURLGenerator $urlGenerator, $userId, - EventMapper $eventMapper, + FormMapper $formMapper, + QuestionMapper $questionMapper, - AnswerMapper $answerMapper, - VoteMapper $VoteMapper + OptionMapper $optionMapper, + SubmissionMapper $SubmissionMapper, + AnswerMapper $AnswerMapper ) { parent::__construct(Application::APP_ID, $request); $this->userMgr = $userMgr; $this->groupManager = $groupManager; $this->urlGenerator = $urlGenerator; $this->userId = $userId; - $this->eventMapper = $eventMapper; + $this->formMapper = $formMapper; $this->questionMapper = $questionMapper; - $this->answerMapper = $answerMapper; - $this->voteMapper = $VoteMapper; + $this->optionMapper = $optionMapper; + $this->submissionMapper = $SubmissionMapper; + $this->answerMapper = $AnswerMapper; } /** @@ -155,15 +161,15 @@ class PageController extends Controller { */ public function gotoForm($hash): ?TemplateResponse { try { - $form = $this->eventMapper->findByHash($hash); + $form = $this->formMapper->findByHash($hash); } catch (DoesNotExistException $e) { return new TemplateResponse('forms', 'no.acc.tmpl', []); } - if ($form->getExpire() === null) { + if ($form->getExpirationDate() === null) { $expired = false; } else { - $expired = time() > strtotime($form->getExpire()); + $expired = time() > strtotime($form->getExpirationDate()); } if ($expired) { @@ -172,7 +178,7 @@ class PageController extends Controller { if ($this->hasUserAccess($form)) { $renderAs = $this->userId !== null ? 'user' : 'public'; - $res = new TemplateResponse('forms', 'vote.tmpl', [ + $res = new TemplateResponse('forms', 'submit.tmpl', [ 'form' => $form, 'questions' => $this->getQuestions($form->getId()), ], $renderAs); @@ -192,11 +198,11 @@ class PageController extends Controller { public function getQuestions(int $formId): array { $questionList = []; try{ - $questions = $this->questionMapper->findByForm($formId); - foreach ($questions as $questionElement) { - $temp = $questionElement->read(); - $temp['answers'] = $this->getAnswers($formId, $temp['id']); - $questionList[] = $temp; + $questionEntities = $this->questionMapper->findByForm($formId); + foreach ($questionEntities as $questionEntity) { + $question = $questionEntity->read(); + $question['options'] = $this->getOptions($question['id']); + $questionList[] = $question; } } catch (DoesNotExistException $e) { //handle silently @@ -208,20 +214,20 @@ class PageController extends Controller { /** * @NoAdminRequired */ - public function getAnswers(int $formId, int $questionId): array { - $answerList = []; + public function getOptions(int $questionId): array { + $optionList = []; try{ - $answers = $this->answerMapper->findByForm($formId, $questionId); - foreach ($answers as $answerElement) { - $answerList[] = $answerElement->read(); + $optionEntities = $this->optionMapper->findByQuestion($questionId); + foreach ($optionEntities as $optionEntity) { + $optionList[] = $optionEntity->read(); } } catch (DoesNotExistException $e) { //handle silently } - return $answerList; + return $optionList; } /** @@ -230,14 +236,14 @@ class PageController extends Controller { * @return TemplateResponse|RedirectResponse */ public function deleteForm($formId) { - $formToDelete = $this->eventMapper->find($formId); - if ($this->userId !== $formToDelete->getOwner() && !$this->groupManager->isAdmin($this->userId)) { + $formToDelete = $this->formMapper->find($formId); + if ($this->userId !== $formToDelete->getOwnerId() && !$this->groupManager->isAdmin($this->userId)) { return new TemplateResponse('forms', 'no.delete.tmpl'); } - $form = new Event(); + $form = new Form(); $form->setId($formId); - $this->voteMapper->deleteByForm($formId); - $this->eventMapper->delete($form); + $this->submissionMapper->deleteByForm($formId); + $this->formMapper->delete($form); $url = $this->urlGenerator->linkToRoute('forms.page.index'); return new RedirectResponse($url); } @@ -247,51 +253,47 @@ class PageController extends Controller { * @PublicPage * @param int $formId * @param string $userId - * @param string $answers - * @param string $options question id - * @param bool $changed + * @param array $answers + * @param array $questions * @return RedirectResponse */ - public function insertVote($id, $userId, $answers, $questions) { + public function insertSubmission($id, $userId, $answers, $questions) { - $form = $this->eventMapper->find($id); - $count_answers = count($answers); - $count = 1; + $form = $this->formMapper->find($id); $anonID = "anon-user-". hash('md5', (time() + rand())); - for ($i = 0; $i < $count_answers; $i++) { - if($questions[$i]['type'] === "checkbox"){ - foreach (($answers[$questions[$i]['text']]) as $value) { - $vote = new Vote(); - $vote->setFormId($id); - if($form->getIsAnonymous()){ - $vote->setUserId($anonID); + //Insert Submission + $submission = new Submission(); + $submission->setFormId($id); + if($form->getIsAnonymous()){ + $submission->setUserId($anonID); - }else{ - $vote->setUserId($userId); - } - $vote->setVoteOptionText(htmlspecialchars($questions[$i]['text'])); - $vote->setVoteAnswer($value); - $vote->setVoteOptionId($count); - $vote->setVoteOptionType($questions[$i]['type']); - $this->voteMapper->insert($vote); + }else{ + $submission->setUserId($userId); + } + $submission->setTimestamp(date('Y-m-d H:i:s')); + $this->submissionMapper->insert($submission); + $submissionId = $submission->getId(); + + //Insert Answers + foreach($questions as $question) { + 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); } - $count++; } else { - $vote = new Vote(); - $vote->setFormId($id); - if($form->getIsAnonymous()){ - $vote->setUserId($anonID); - }else{ - $vote->setUserId($userId); - } - $vote->setVoteOptionText(htmlspecialchars($questions[$i]['text'])); - $vote->setVoteAnswer($answers[$questions[$i]['text']]); - $vote->setVoteOptionId($count++); - $vote->setVoteOptionType($questions[$i]['type']); - $this->voteMapper->insert($vote); + $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); @@ -379,12 +381,12 @@ class PageController extends Controller { /** * Check if user has access to this form * - * @param Event $form + * @param Form $form * @return bool */ private function hasUserAccess($form) { $access = $form->getAccess(); - $owner = $form->getOwner(); + $ownerId = $form->getOwnerId(); if ($access === 'public' || $access === 'hidden') { return true; } @@ -392,8 +394,8 @@ class PageController extends Controller { return false; } if ($access === 'registered') { - if ($form->getUnique()) { - $participants = $this->voteMapper->findParticipantsByForm($form->getId()); + if ($form->getSubmitOnce()) { + $participants = $this->submissionMapper->findParticipantsByForm($form->getId()); foreach($participants as $participant) { // Don't allow access if user has already taken part if ($participant->getUserId() === $this->userId) return false; @@ -401,7 +403,7 @@ class PageController extends Controller { } return true; } - if ($owner === $this->userId) { + if ($ownerId === $this->userId) { return true; } Util::writeLog('forms', $this->userId, Util::ERROR); diff --git a/lib/Db/Answer.php b/lib/Db/Answer.php index e340612..4b5f3b1 100644 --- a/lib/Db/Answer.php +++ b/lib/Db/Answer.php @@ -2,9 +2,9 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019 Inigo Jiron + * @copyright Copyright (c) 2020 Jonas Rittershofer * - * @author Inigo Jiron + * @author Jonas Rittershofer * * @license GNU AGPL version 3 or any later version * @@ -28,46 +28,33 @@ namespace OCA\Forms\Db; use OCP\AppFramework\Db\Entity; /** - * @method integer getFormId() - * @method void setFormId(integer $value) + * @method integer getSubmissionId() + * @method void setSubmissionId(integer $value) * @method integer getQuestionId() * @method void setQuestionId(integer $value) * @method string getText() * @method void setText(string $value) - * @method integer getTimestamp() - * @method void setTimestamp(integer $value) */ class Answer extends Entity { - - /** @var int */ - protected $formId; - - /** @var int */ + protected $submissionId; protected $questionId; - - /** @var string */ protected $text; - /** @var int */ - protected $timestamp; - /** * Answer constructor. */ public function __construct() { - $this->addType('id', 'integer'); - $this->addType('formId', 'integer'); + $this->addType('submissionId', 'integer'); $this->addType('questionId', 'integer'); - $this->addType('timestamp', 'integer'); } public function read(): array { return [ 'id' => $this->getId(), - 'formId' => $this->getFormId(), + 'submissionId' => $this->getSubmissionId(), 'questionId' => $this->getQuestionId(), 'text' => htmlspecialchars_decode($this->getText()), - 'timestamp' => $this->getTimestamp() ]; } + } diff --git a/lib/Db/AnswerMapper.php b/lib/Db/AnswerMapper.php index c9681e7..9330890 100644 --- a/lib/Db/AnswerMapper.php +++ b/lib/Db/AnswerMapper.php @@ -1,12 +1,9 @@ - * - * @author Inigo Jiron - * @author Natalie Gilbert + * @copyright Copyright (c) 2020 Jonas Rittershofer * + * @author Jonas Rittershofer +* * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify @@ -33,89 +30,43 @@ use OCP\AppFramework\Db\QBMapper; class AnswerMapper extends QBMapper { /** - * TextMapper constructor. + * AnswerMapper constructor. * @param IDBConnection $db */ public function __construct(IDBConnection $db) { - parent::__construct($db, 'forms_answers', Answer::class); + parent::__construct($db, 'forms_v2_answers', Answer::class); } - // TODO: Change below functions to search by form and question id - /** - * @param int $formId - * @param int $questionId + * @param int $submissionId * @throws \OCP\AppFramework\Db\DoesNotExistException if not found * @return Answer[] */ - public function findByForm(int $formId, int $questionId): array { - $qb = $this->db->getQueryBuilder(); - - $qb->select('*') - ->from($this->getTableName()) - ->where( - $qb->expr()->eq('form_id', $qb->createNamedParameter($formId, IQueryBuilder::PARAM_INT)) - ) - ->andWhere( - $qb->expr()->eq('question_id', $qb->createNamedParameter($questionId, IQueryBuilder::PARAM_INT)) - ); - - return $this->findEntities($qb); - } - - /** - * @param int $formId - * @param int $questionId - */ - public function deleteByFormAndQuestion(int $formId, int $questionId): void { - $qb = $this->db->getQueryBuilder(); - - $qb->delete($this->getTableName()) - ->where( - $qb->expr()->eq('form_id', $qb->createNamedParameter($formId, IQueryBuilder::PARAM_INT)) - ) - ->andWhere( - $qb->expr()->eq('question_id', $qb->createNamedParameter($questionId, IQueryBuilder::PARAM_INT)) - ); - - $qb->execute(); - } - - /** - * @param int $formId - */ - public function deleteByForm(int $formId): void { - $qb = $this->db->getQueryBuilder(); - - $qb->delete($this->getTableName()) - ->where( - $qb->expr()->eq('form_id', $qb->createNamedParameter($formId, IQueryBuilder::PARAM_INT)) - ); - - $qb->execute(); - } - - public function findById(int $answerId): Answer { + public function findBySubmission(int $submissionId): array { $qb = $this->db->getQueryBuilder(); $qb->select('*') ->from($this->getTableName()) ->where( - $qb->expr()->eq('id', $qb->createNamedParameter($answerId)) + $qb->expr()->eq('submission_id', $qb->createNamedParameter($submissionId, IQueryBuilder::PARAM_INT)) ); - return $this->findEntity($qb); + return $this->findEntities($qb); } - public function deleteByQuestion(int $questionId): void { + /** + * @param int $submissionId + */ + public function deleteBySubmission(int $submissionId): void { $qb = $this->db->getQueryBuilder(); $qb->delete($this->getTableName()) - ->where( - $qb->expr()->eq('question_id', $qb->createNamedParameter($questionId)) - ); + ->where( + $qb->expr()->eq('submission_id', $qb->createNamedParameter($submissionId, IQueryBuilder::PARAM_INT)) + ); $qb->execute(); } + } diff --git a/lib/Db/Event.php b/lib/Db/Form.php similarity index 73% rename from lib/Db/Event.php rename to lib/Db/Form.php index c4b360f..ae5163f 100644 --- a/lib/Db/Event.php +++ b/lib/Db/Form.php @@ -29,44 +29,43 @@ namespace OCA\Forms\Db; use OCP\AppFramework\Db\Entity; /** + * @method string getHash() + * @method void setHash(string $value) * @method string getTitle() * @method void setTitle(string $value) * @method string getDescription() * @method void setDescription(string $value) - * @method string getOwner() - * @method void setOwner(string $value) - * @method string getCreated() - * @method void setCreated(string $value) + * @method string getOwnerId() + * @method void setOwnerId(string $value) * @method string getAccess() * @method void setAccess(string $value) - * @method string getExpire() - * @method void setExpire(string $value) - * @method string getHash() - * @method void setHash(string $value) + * @method string getCreated() + * @method void setCreated(string $value) + * @method string getExpirationDate() + * @method void setExpirationDate(string $value) * @method integer getIsAnonymous() - * @method void setIsAnonymous(integer $value) - * @method integer getUnique() - * @method void setUnique(integer $value) + * @method void setIsAnonymous(bool $value) + * @method integer getSubmitOnce() + * @method void setSubmitOnce(bool $value) */ -class Event extends Entity { +class Form extends Entity { + + protected $hash; protected $title; protected $description; - protected $owner; - protected $created; + protected $ownerId; protected $access; - protected $expire; - protected $hash; + protected $created; + protected $expirationDate; protected $isAnonymous; - protected $fullAnonymous; - protected $allowMaybe; - protected $unique; + protected $submitOnce; /** - * Event constructor. + * Form constructor. */ public function __construct() { - $this->addType('isAnonymous', 'integer'); - $this->addType('unique', 'integer'); + $this->addType('isAnonymous', 'bool'); + $this->addType('submitOnce', 'bool'); } public function read() { @@ -74,12 +73,12 @@ class Event extends Entity { if (!strpos('|public|hidden|registered', $accessType)) { $accessType = 'select'; } - if ($this->getExpire() === null) { + if ($this->getExpirationDate() === null) { $expired = false; - $expiration = false; + $expires = false; } else { - $expired = time() > strtotime($this->getExpire()); - $expiration = true; + $expired = time() > strtotime($this->getExpirationDate()); + $expires = true; } return [ @@ -87,15 +86,15 @@ class Event extends Entity { 'hash' => $this->getHash(), 'title' => $this->getTitle(), 'description' => $this->getDescription(), - 'owner' => $this->getOwner(), - 'ownerDisplayName' => \OC_User::getDisplayName($this->getOwner()), + 'ownerId' => $this->getOwnerId(), + 'ownerDisplayName' => \OC_User::getDisplayName($this->getOwnerId()), 'created' => $this->getCreated(), 'access' => $accessType, - 'expiration' => $expiration, + 'expires' => $expires, 'expired' => $expired, - 'expirationDate' => $this->getExpire(), + 'expirationDate' => $this->getExpirationDate(), 'isAnonymous' => $this->getIsAnonymous(), - 'unique' => $this->getUnique() + 'submitOnce' => $this->getSubmitOnce() ]; } } diff --git a/lib/Db/EventMapper.php b/lib/Db/FormMapper.php similarity index 67% rename from lib/Db/EventMapper.php rename to lib/Db/FormMapper.php index e342d4e..0324952 100644 --- a/lib/Db/EventMapper.php +++ b/lib/Db/FormMapper.php @@ -28,63 +28,63 @@ use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use OCP\AppFramework\Db\QBMapper; -class EventMapper extends QBMapper { +class FormMapper extends QBMapper { /** - * EventMapper constructor. + * FormMapper constructor. * @param IDBConnection $db */ public function __construct(IDBConnection $db) { - parent::__construct($db, 'forms_events', Event::class); + parent::__construct($db, 'forms_v2_forms', Form::class); } /** * @param Integer $id * @throws \OCP\AppFramework\Db\DoesNotExistException if not found * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException if more than one result - * @return Event + * @return Form */ - public function find(int $id): Event { + public function find(int $id): Form { $qb = $this->db->getQueryBuilder(); - $qb->select('*') - ->from($this->getTableName()) - ->where( - $qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)) - ); + $qb->select('*') + ->from($this->tableName) + ->where( + $qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)) + ); - return $this->findEntity($qb); + return $this->findEntity($qb); } /** * @param String $hash * @throws \OCP\AppFramework\Db\DoesNotExistException if not found * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException if more than one result - * @return Event + * @return Form */ - public function findByHash(string $hash): Event { + public function findByHash(string $hash): Form { $qb = $this->db->getQueryBuilder(); - $qb->select('*') - ->from($this->getTableName()) - ->where( - $qb->expr()->eq('hash', $qb->createNamedParameter($hash, IQueryBuilder::PARAM_STR)) - ); + $qb->select('*') + ->from($this->tableName) + ->where( + $qb->expr()->eq('hash', $qb->createNamedParameter($hash, IQueryBuilder::PARAM_STR)) + ); - return $this->findEntity($qb); + return $this->findEntity($qb); } /** * @throws \OCP\AppFramework\Db\DoesNotExistException if not found - * @return Event[] + * @return Form[] */ public function findAll(): array { $qb = $this->db->getQueryBuilder(); - $qb->select('*') - ->from($this->getTableName()); + $qb->select('*') + ->from($this->tableName); - return $this->findEntities($qb); + return $this->findEntities($qb); } } diff --git a/lib/Db/Option.php b/lib/Db/Option.php new file mode 100644 index 0000000..69f6065 --- /dev/null +++ b/lib/Db/Option.php @@ -0,0 +1,58 @@ + + * + * @author Inigo Jiron + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Forms\Db; + +use OCP\AppFramework\Db\Entity; + +/** + * @method integer getQuestionId() + * @method void setQuestionId(integer $value) + * @method string getText() + * @method void setText(string $value) + */ +class Option extends Entity { + + /** @var int */ + protected $questionId; + /** @var string */ + protected $text; + + /** + * Option constructor. + */ + public function __construct() { + $this->addType('questionId', 'integer'); + $this->addType('text', 'string'); + } + + public function read(): array { + return [ + 'id' => $this->getId(), + 'questionId' => $this->getQuestionId(), + 'text' => htmlspecialchars_decode($this->getText()), + ]; + } +} diff --git a/lib/Db/OptionMapper.php b/lib/Db/OptionMapper.php new file mode 100644 index 0000000..218314b --- /dev/null +++ b/lib/Db/OptionMapper.php @@ -0,0 +1,84 @@ + + * + * @author Inigo Jiron + * @author Natalie Gilbert + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Forms\Db; + +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDBConnection; +use OCP\AppFramework\Db\QBMapper; + +class OptionMapper extends QBMapper { + + /** + * OptionMapper constructor. + * @param IDBConnection $db + */ + public function __construct(IDBConnection $db) { + parent::__construct($db, 'forms_v2_options', Option::class); + } + + /** + * @param int $questionId + * @throws \OCP\AppFramework\Db\DoesNotExistException if not found + * @return Option[] + */ + + public function findByQuestion(int $questionId): array { + $qb = $this->db->getQueryBuilder(); + + $qb->select('*') + ->from($this->getTableName()) + ->where( + $qb->expr()->eq('question_id', $qb->createNamedParameter($questionId)) + ); + + return $this->findEntities($qb); + } + + public function deleteByQuestion(int $questionId): void { + $qb = $this->db->getQueryBuilder(); + + $qb->delete($this->getTableName()) + ->where( + $qb->expr()->eq('question_id', $qb->createNamedParameter($questionId)) + ); + + $qb->execute(); + } + + public function findById(int $optionId): Option { + $qb = $this->db->getQueryBuilder(); + + $qb->select('*') + ->from($this->getTableName()) + ->where( + $qb->expr()->eq('id', $qb->createNamedParameter($optionId)) + ); + + return $this->findEntity($qb); + } + +} diff --git a/lib/Db/Question.php b/lib/Db/Question.php index 4062edd..58034de 100644 --- a/lib/Db/Question.php +++ b/lib/Db/Question.php @@ -29,39 +29,34 @@ use OCP\AppFramework\Db\Entity; /** * @method integer getFormId() * @method void setFormId(integer $value) - * @method string getFormQuestionType() - * @method void setFormQuestionType(string $value) - * @method string getFormQuestionText() - * @method void setFormQuestionText(string $value) - * @method integer getTimestamp() - * @method void setTimestamp(integer $value) + * @method string getType() + * @method void setType(string $value) + * @method string getText() + * @method void setText(string $value) */ class Question extends Entity { protected $formId; - protected $formQuestionType; - protected $formQuestionText; - protected $timestamp; + protected $type; + protected $mandatory; + protected $text; /** * Question constructor. */ public function __construct() { $this->addType('formId', 'integer'); - $this->addType('timestamp', 'integer'); + $this->addType('type', 'string'); + $this->addType('mandatory', 'bool'); + $this->addType('text', 'string'); } public function read(): array { return [ 'id' => $this->getId(), 'formId' => $this->getFormId(), - 'type' => htmlspecialchars_decode($this->getFormQuestionType()), - 'text' => htmlspecialchars_decode($this->getFormQuestionText()), - 'timestamp' => $this->getTimestamp() + 'type' => htmlspecialchars_decode($this->getType()), + 'mandatory' => $this->getMandatory(), + 'text' => htmlspecialchars_decode($this->getText()), ]; } - - - - - } diff --git a/lib/Db/QuestionMapper.php b/lib/Db/QuestionMapper.php index 1a4f28a..786c6fc 100644 --- a/lib/Db/QuestionMapper.php +++ b/lib/Db/QuestionMapper.php @@ -28,16 +28,22 @@ use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use OCP\AppFramework\Db\QBMapper; +use OCA\Forms\Db\OptionMapper; + class QuestionMapper extends QBMapper { - public function __construct(IDBConnection $db) { - parent::__construct($db, 'forms_questions', Question::class); + private $optionMapper; + + public function __construct(IDBConnection $db, OptionMapper $optionMapper) { + parent::__construct($db, 'forms_v2_questions', Question::class); + + $this->optionMapper = $optionMapper; } /** * @param int $formId * @throws \OCP\AppFramework\Db\DoesNotExistException if not found - * @return Option[] + * @return Question[] */ public function findByForm(int $formId): array { @@ -58,6 +64,13 @@ class QuestionMapper extends QBMapper { public function deleteByForm(int $formId): void { $qb = $this->db->getQueryBuilder(); + // First delete corresponding options. + $questionEntities = $this->findByForm($formId); + foreach ($questionEntities as $questionEntity) { + $this->optionMapper->deleteByQuestion($questionEntity->id); + } + + // Delete Questions $qb->delete($this->getTableName()) ->where( $qb->expr()->eq('form_id', $qb->createNamedParameter($formId, IQueryBuilder::PARAM_INT)) @@ -72,7 +85,7 @@ class QuestionMapper extends QBMapper { $qb->select('*') ->from($this->getTableName()) ->where( - $qb->expr()->eq('id', $qb->createNamedParameter($questionId)) + $qb->expr()->eq('id', $qb->createNamedParameter($questionId, IQueryBuilder::PARAM_INT)) ); return $this->findEntity($qb); diff --git a/lib/Db/Vote.php b/lib/Db/Submission.php similarity index 55% rename from lib/Db/Vote.php rename to lib/Db/Submission.php index 49560fd..73e1839 100644 --- a/lib/Db/Vote.php +++ b/lib/Db/Submission.php @@ -2,12 +2,10 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2017 Vinzenz Rosenkranz + * @copyright Copyright (c) 2020 Jonas Rittershofer + * + * @author Jonas Rittershofer * - * @author Vinzenz Rosenkranz - * @author Kai Schröer - * @author René Gieling -* * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify @@ -34,39 +32,27 @@ use OCP\AppFramework\Db\Entity; * @method void setFormId(integer $value) * @method string getUserId() * @method void setUserId(string $value) - * @method integer getVoteOptionId() - * @method void setVoteOptionId(integer $value) - * @method string getVoteOptionText() - * @method void setVoteOptionText(string $value) - * @method string getVoteAnswer() - * @method void setVoteAnswer(string $value) - * @method string getVoteOptionType() - * @method void setVoteOptionType(string $value) + * @method string getTimestamp() + * @method void setTimestamp(string $value) */ -class Vote extends Entity { +class Submission extends Entity { protected $formId; protected $userId; - protected $voteOptionId; - protected $voteOptionText; - protected $voteAnswer; - protected $voteOptionType; + protected $timestamp; /** - * Options constructor. + * Submission constructor. */ public function __construct() { $this->addType('formId', 'integer'); - $this->addType('voteOptionId', 'integer'); } public function read(): array { return [ 'id' => $this->getId(), + 'formId' => $this->getFormId(), 'userId' => $this->getUserId(), - 'voteOptionId' => $this->getVoteOptionId(), - 'voteOptionText' => htmlspecialchars_decode($this->getVoteOptionText()), - 'voteAnswer' => $this->getVoteAnswer(), - 'voteOptionType' => $this->getVoteOptionType() + 'timestamp' => $this->getTimestamp(), ]; } diff --git a/lib/Db/VoteMapper.php b/lib/Db/SubmissionMapper.php similarity index 54% rename from lib/Db/VoteMapper.php rename to lib/Db/SubmissionMapper.php index 57cbaa7..046adac 100644 --- a/lib/Db/VoteMapper.php +++ b/lib/Db/SubmissionMapper.php @@ -1,10 +1,9 @@ + * @copyright Copyright (c) 2020 Jonas Rittershofer + * + * @author Jonas Rittershofer * - * @author Vinzenz Rosenkranz - * @author René Gieling -* * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify @@ -28,31 +27,38 @@ use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use OCP\AppFramework\Db\QBMapper; -class VoteMapper extends QBMapper { +use OCA\Forms\Db\AnswerMapper; + +class SubmissionMapper extends QBMapper { + + private $answerMapper; /** - * VoteMapper constructor. + * SubmissionMapper constructor. * @param IDBConnection $db + * @param AnswerMapper $answerMapper */ - public function __construct(IDBConnection $db) { - parent::__construct($db, 'forms_votes', Vote::class); + public function __construct(IDBConnection $db, AnswerMapper $answerMapper) { + parent::__construct($db, 'forms_v2_submissions', Submission::class); + + $this->answerMapper = $answerMapper; } /** * @param int $formId * @throws \OCP\AppFramework\Db\DoesNotExistException if not found - * @return Vote[] + * @return Submission[] */ public function findByForm(int $formId): array { $qb = $this->db->getQueryBuilder(); - $qb->select('*') - ->from($this->getTableName()) - ->where( - $qb->expr()->eq('form_id', $qb->createNamedParameter($formId, IQueryBuilder::PARAM_INT)) - ); + $qb->select('*') + ->from($this->getTableName()) + ->where( + $qb->expr()->eq('form_id', $qb->createNamedParameter($formId, IQueryBuilder::PARAM_INT)) + ); - return $this->findEntities($qb); + return $this->findEntities($qb); } /** @@ -63,31 +69,13 @@ class VoteMapper extends QBMapper { public function findParticipantsByForm(int $formId, $limit = null, $offset = null): array { $qb = $this->db->getQueryBuilder(); - $qb->select('user_id') - ->from($this->getTableName()) - ->where( - $qb->expr()->eq('form_id', $qb->createNamedParameter($formId, IQueryBuilder::PARAM_INT)) - ); + $qb->select('user_id') + ->from($this->getTableName()) + ->where( + $qb->expr()->eq('form_id', $qb->createNamedParameter($formId, IQueryBuilder::PARAM_INT)) + ); - return $this->findEntities($qb); - } - - /** - * @param int $formId - * @param string $userId - */ - public function deleteByFormAndUser(int $formId, string $userId): void { - $qb = $this->db->getQueryBuilder(); - - $qb->delete($this->getTableName()) - ->where( - $qb->expr()->eq('form_id', $qb->createNamedParameter($formId, IQueryBuilder::PARAM_INT)) - ) - ->andWhere( - $qb->expr()->eq('user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)) - ); - - $qb->execute(); + return $this->findEntities($qb); } /** @@ -96,6 +84,13 @@ class VoteMapper extends QBMapper { public function deleteByForm(int $formId): void { $qb = $this->db->getQueryBuilder(); + // First delete corresponding answers. + $submissionEntities = $this->findByForm($formId); + foreach ($submissionEntities as $submissionEntity) { + $this->answerMapper->deleteBySubmission($submissionEntity->id); + } + + //Delete Submissions $qb->delete($this->getTableName()) ->where( $qb->expr()->eq('form_id', $qb->createNamedParameter($formId, IQueryBuilder::PARAM_INT)) diff --git a/lib/Migration/Version0009Date20190000000006.php b/lib/Migration/Version0009Date20190000000006.php deleted file mode 100644 index 3a1538b..0000000 --- a/lib/Migration/Version0009Date20190000000006.php +++ /dev/null @@ -1,210 +0,0 @@ - - * - * @author René Gieling - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -namespace OCA\Forms\Migration; - -use Doctrine\DBAL\Exception\TableNotFoundException; -use Doctrine\DBAL\Platforms\PostgreSqlPlatform; -use Doctrine\DBAL\Types\Type; -use OCP\DB\ISchemaWrapper; -use OCP\DB\QueryBuilder\IQueryBuilder; -use OCP\IConfig; -use OCP\IDBConnection; -use OCP\Migration\SimpleMigrationStep; -use OCP\Migration\IOutput; - -/** - * Installation class for the forms app. - * Initial db creation - */ -class Version0009Date20190000000006 extends SimpleMigrationStep { - - /** @var IDBConnection */ - protected $connection; - - /** @var IConfig */ - protected $config; - - /** - * @param IDBConnection $connection - * @param IConfig $config - */ - public function __construct(IDBConnection $connection, IConfig $config) { - $this->connection = $connection; - $this->config = $config; - } - - /** - * @param IOutput $output - * @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options - * @return null|ISchemaWrapper - * @since 13.0.0 - */ - public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) { - /** @var ISchemaWrapper $schema */ - $schema = $schemaClosure(); - - if (!$schema->hasTable('forms_events')) { - $table = $schema->createTable('forms_events'); - $table->addColumn('id', Type::INTEGER, [ - 'autoincrement' => true, - 'notnull' => true, - ]); - $table->addColumn('hash', Type::STRING, [ - 'notnull' => false, - 'length' => 64, - ]); - $table->addColumn('title', Type::STRING, [ - 'notnull' => true, - 'length' => 128, - ]); - $table->addColumn('description', Type::STRING, [ - 'notnull' => true, - 'length' => 1024, - ]); - $table->addColumn('owner', Type::STRING, [ - 'notnull' => true, - 'length' => 64, - ]); - $table->addColumn('created', Type::DATETIME, [ - 'notnull' => false, - ]); - $table->addColumn('access', Type::STRING, [ - 'notnull' => false, - 'length' => 1024, - ]); - $table->addColumn('expire', Type::DATETIME, [ - 'notnull' => false, - ]); - $table->addColumn('is_anonymous', Type::INTEGER, [ - 'notnull' => false, - 'default' => 0, - ]); - $table->addColumn('full_anonymous', Type::INTEGER, [ - 'notnull' => false, - 'default' => 0, - ]); - $table->setPrimaryKey(['id']); - } else { - } - - if (!$schema->hasTable('forms_questions')) { - $table = $schema->createTable('forms_questions'); - $table->addColumn('id', Type::INTEGER, [ - 'autoincrement' => true, - 'notnull' => true, - ]); - $table->addColumn('form_id', Type::INTEGER, [ - 'notnull' => false, - ]); - $table->addColumn('form_question_type', Type::STRING, [ - 'notnull' => false, // maybe true? - 'length' => 256, - ]); - $table->addColumn('form_question_text', Type::STRING, [ - 'notnull' => false, // maybe true? - 'length' => 4096, - ]); - $table->addColumn('timestamp', Type::INTEGER, [ - 'notnull' => false, - 'default' => 0 - ]); - $table->setPrimaryKey(['id']); - } - - if (!$schema->hasTable('forms_answers')) { - $table = $schema->createTable('forms_answers'); - $table->addColumn('id', Type::INTEGER, [ - 'autoincrement' => true, - 'notnull' => true, - ]); - $table->addColumn('form_id', Type::INTEGER, [ - 'notnull' => false, - ]); - $table->addColumn('question_id', Type::INTEGER, [ - 'notnull' => false, - ]); - $table->addColumn('text', Type::STRING, [ - 'notnull' => false, // maybe true? - 'length' => 4096, - ]); - $table->addColumn('timestamp', Type::INTEGER, [ - 'notnull' => false, - 'default' => 0 - ]); - $table->setPrimaryKey(['id']); - } - - if (!$schema->hasTable('forms_votes')) { - $table = $schema->createTable('forms_votes'); - $table->addColumn('id', Type::INTEGER, [ - 'autoincrement' => true, - 'notnull' => true, - ]); - $table->addColumn('form_id', Type::INTEGER, [ - 'notnull' => false, - ]); - $table->addColumn('user_id', Type::STRING, [ - 'notnull' => true, - 'length' => 64, - ]); - $table->addColumn('vote_option_id', Type::INTEGER, [ - 'notnull' => true, - 'default' => 0, - 'length' => 64, - ]); - $table->addColumn('vote_option_text', Type::STRING, [ - 'notnull' => false, // maybe true? - 'length' => 4096, - ]); - $table->addColumn('vote_answer', Type::STRING, [ - 'notnull' => false, - 'length' => 4096, - ]); - $table->addColumn('vote_option_type', Type::STRING, [ - 'notnull' => false, - 'length' => 256, - ]); - $table->setPrimaryKey(['id']); - } - - if (!$schema->hasTable('forms_notif')) { - $table = $schema->createTable('forms_notif'); - $table->addColumn('id', Type::INTEGER, [ - 'autoincrement' => true, - 'notnull' => true, - ]); - $table->addColumn('form_id', Type::INTEGER, [ - 'notnull' => false, - ]); - $table->addColumn('user_id', Type::STRING, [ - 'notnull' => true, - 'length' => 64, - ]); - $table->setPrimaryKey(['id']); - } - - return $schema; - } -} diff --git a/lib/Migration/Version010102Date20200323120846.php b/lib/Migration/Version010102Date20200323120846.php index 82c397e..8ed98b2 100644 --- a/lib/Migration/Version010102Date20200323120846.php +++ b/lib/Migration/Version010102Date20200323120846.php @@ -23,7 +23,9 @@ class Version010102Date20200323120846 extends SimpleMigrationStep { /** @var ISchemaWrapper $schema */ $schema = $schemaClosure(); - $schema->dropTable('forms_notif'); + if ($schema->hasTable('forms_notif')) { + $schema->dropTable('forms_notif'); + } return $schema; } diff --git a/lib/Migration/Version010200Date2020323141300.php b/lib/Migration/Version010200Date2020323141300.php new file mode 100644 index 0000000..0964628 --- /dev/null +++ b/lib/Migration/Version010200Date2020323141300.php @@ -0,0 +1,352 @@ + + * + * @author Jonas Rittershofer + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Forms\Migration; + +use Doctrine\DBAL\Exception\TableNotFoundException; +use Doctrine\DBAL\Platforms\PostgreSqlPlatform; +use Doctrine\DBAL\Types\Type; +use OCP\DB\ISchemaWrapper; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IConfig; +use OCP\IDBConnection; +use OCP\Migration\SimpleMigrationStep; +use OCP\Migration\IOutput; + +/** + * Installation class for the forms app. + * Initial db creation + */ +class Version010200Date2020323141300 extends SimpleMigrationStep { + + /** @var IDBConnection */ + protected $connection; + + /** @var IConfig */ + protected $config; + + /** + * @param IDBConnection $connection + * @param IConfig $config + */ + public function __construct(IDBConnection $connection, IConfig $config) { + $this->connection = $connection; + $this->config = $config; + } + + /** + * @param IOutput $output + * @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + * @return null|ISchemaWrapper + * @since 13.0.0 + */ + public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) { + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + + if (!$schema->hasTable('forms_v2_forms')) { + $table = $schema->createTable('forms_v2_forms'); + $table->addColumn('id', Type::INTEGER, [ + 'autoincrement' => true, + 'notnull' => true, + ]); + $table->addColumn('hash', Type::STRING, [ + 'notnull' => true, + 'length' => 64, + ]); + $table->addColumn('title', Type::STRING, [ + 'notnull' => true, + 'length' => 256, + ]); + $table->addColumn('description', Type::STRING, [ + 'notnull' => false, + 'length' => 2048, + ]); + $table->addColumn('owner_id', Type::STRING, [ + 'notnull' => true, + 'length' => 64, + ]); + $table->addColumn('access', Type::STRING, [ + 'notnull' => false, + 'length' => 1024, + ]); + $table->addColumn('created', Type::DATETIME, [ + 'notnull' => false, + ]); + $table->addColumn('expiration_date', Type::DATETIME, [ + 'notnull' => false, + ]); + $table->addColumn('is_anonymous', Type::BOOLEAN, [ + 'notnull' => true, + 'default' => 0, + ]); + $table->addColumn('submit_once', Type::BOOLEAN, [ + 'notnull' => true, + 'default' => 0, + ]); + $table->setPrimaryKey(['id']); + } + + if (!$schema->hasTable('forms_v2_questions')) { + $table = $schema->createTable('forms_v2_questions'); + $table->addColumn('id', Type::INTEGER, [ + 'autoincrement' => true, + 'notnull' => true, + ]); + $table->addColumn('form_id', Type::INTEGER, [ + 'notnull' => true, + ]); + $table->addColumn('type', Type::STRING, [ + 'notnull' => true, + 'length' => 256, + ]); + $table->addColumn('mandatory', Type::BOOLEAN, [ + 'notnull' => true, + 'default' => 1, + ]); + $table->addColumn('text', Type::STRING, [ + 'notnull' => true, + 'length' => 2048, + ]); + $table->setPrimaryKey(['id']); + } + + if (!$schema->hasTable('forms_v2_options')) { + $table = $schema->createTable('forms_v2_options'); + $table->addColumn('id', Type::INTEGER, [ + 'autoincrement' => true, + 'notnull' => true, + ]); + $table->addColumn('question_id', Type::INTEGER, [ + 'notnull' => true, + ]); + $table->addColumn('text', Type::STRING, [ + 'notnull' => true, + 'length' => 1024, + ]); + $table->setPrimaryKey(['id']); + } + + if (!$schema->hasTable('forms_v2_submissions')) { + $table = $schema->createTable('forms_v2_submissions'); + $table->addColumn('id', Type::INTEGER, [ + 'autoincrement' => true, + 'notnull' => true, + ]); + $table->addColumn('form_id', Type::INTEGER, [ + 'notnull' => true, + ]); + $table->addColumn('user_id', Type::STRING, [ + 'notnull' => true, + 'length' => 64, + ]); + $table->addColumn('timestamp', Type::DATETIME, [ + 'notnull' => false, + ]); + $table->setPrimaryKey(['id']); + } + + if (!$schema->hasTable('forms_v2_answers')) { + $table = $schema->createTable('forms_v2_answers'); + $table->addColumn('id', Type::INTEGER, [ + 'autoincrement' => true, + 'notnull' => true, + ]); + $table->addColumn('submission_id', Type::INTEGER, [ + 'notnull' => true, + ]); + $table->addColumn('question_id', Type::INTEGER, [ + 'notnull' => true, + ]); + $table->addColumn('text', Type::STRING, [ + 'notnull' => true, + 'length' => 2048, + ]); + $table->setPrimaryKey(['id']); + } + + return $schema; + } + + public function postSchemaChange(IOutput $output, \Closure $schemaClosure, array $options) { + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + + // if Database exists. + if( $schema->hasTable('forms_events') ){ + $id_mapping = []; + $id_mapping['events'] = []; // Maps oldevent-id => newevent-id + $id_mapping['questions'] = []; // Maps oldquestion-id => newquestion-id + + //Fetch & Restore Events + $qb_fetch = $this->connection->getQueryBuilder(); + $qb_restore = $this->connection->getQueryBuilder(); + + $qb_fetch->select('id', 'hash', 'title', 'description', 'owner', 'created', 'access', 'expire', 'is_anonymous', 'unique') + ->from('forms_events'); + $cursor = $qb_fetch->execute(); + while( $event = $cursor->fetch() ){ + $qb_restore->insert('forms_v2_forms') + ->values([ + 'hash' => $qb_restore->createNamedParameter($event['hash'], IQueryBuilder::PARAM_STR), + 'title' => $qb_restore->createNamedParameter($event['title'], IQueryBuilder::PARAM_STR), + 'description' => $qb_restore->createNamedParameter($event['description'], IQueryBuilder::PARAM_STR), + 'owner_id' => $qb_restore->createNamedParameter($event['owner'], IQueryBuilder::PARAM_STR), + 'access' => $qb_restore->createNamedParameter($event['access'], IQueryBuilder::PARAM_STR), + 'created' => $qb_restore->createNamedParameter($event['created'], IQueryBuilder::PARAM_STR), + 'expiration_date' => $qb_restore->createNamedParameter($event['expire'], IQueryBuilder::PARAM_STR), + 'is_anonymous' => $qb_restore->createNamedParameter($event['is_anonymous'], IQueryBuilder::PARAM_BOOL), + 'submit_once' => $qb_restore->createNamedParameter($event['unique'], IQueryBuilder::PARAM_BOOL) + ]); + $qb_restore->execute(); + $id_mapping['events'][$event['id']] = $qb_restore->getLastInsertId(); //Store new form-id to connect questions to new form. + } + $cursor->closeCursor(); + + //Fetch & restore Questions + $qb_fetch = $this->connection->getQueryBuilder(); + $qb_restore = $this->connection->getQueryBuilder(); + + $qb_fetch->select('id', 'form_id', 'form_question_type', 'form_question_text') + ->from('forms_questions'); + $cursor = $qb_fetch->execute(); + while( $question = $cursor->fetch() ){ + //In case the old Question would have been longer than current possible length, create a warning and shorten text to avoid Error on upgrade. + if(strlen($question['form_question_text']) > 2048) { + $output->warning("Question-text is too long for new Database: '" . $question['form_question_text'] . "'"); + $question['form_question_text'] = substr($question['form_question_text'], 0, 2048); + } + + $qb_restore->insert('forms_v2_questions') + ->values([ + 'form_id' => $qb_restore->createNamedParameter($id_mapping['events'][$question['form_id']], IQueryBuilder::PARAM_INT), + 'type' => $qb_restore->createNamedParameter($question['form_question_type'], IQueryBuilder::PARAM_STR), + 'text' => $qb_restore->createNamedParameter($question['form_question_text'], IQueryBuilder::PARAM_STR) + ]); + $qb_restore->execute(); + $id_mapping['questions'][$question['id']] = $qb_restore->getLastInsertId(); //Store new question-id to connect options to new question. + } + $cursor->closeCursor(); + + //Fetch all Answers and restore to Options + $qb_fetch = $this->connection->getQueryBuilder(); + $qb_restore = $this->connection->getQueryBuilder(); + + $qb_fetch->select('question_id', 'text') + ->from('forms_answers'); + $cursor = $qb_fetch->execute(); + while( $answer = $cursor->fetch() ){ + //In case the old Answer would have been longer than current possible length, create a warning and shorten text to avoid Error on upgrade. + if(strlen($answer['text']) > 1024) { + $output->warning("Option-text is too long for new Database: '" . $answer['text'] . "'"); + $answer['text'] = substr($answer['text'], 0, 1024); + } + + $qb_restore->insert('forms_v2_options') + ->values([ + 'question_id' => $qb_restore->createNamedParameter($id_mapping['questions'][$answer['question_id']], IQueryBuilder::PARAM_INT), + 'text' => $qb_restore->createNamedParameter($answer['text'], IQueryBuilder::PARAM_STR) + ]); + $qb_restore->execute(); + } + $cursor->closeCursor(); + + /* Fetch old id_structure of event-ids and question-ids + * This is necessary to restore the $oldQuestionId, as the vote_option_ids do not use the true question_ids + */ + $event_structure = []; + $qb_fetch = $this->connection->getQueryBuilder(); + $qb_fetch->select('id') + ->from('forms_events'); + $cursor = $qb_fetch->execute(); + while( $tmp = $cursor->fetch() ){ + $event_structure[$tmp['id']] = $tmp; + } + $cursor->closeCursor(); + + foreach ($event_structure as $eventkey => $event) { + $event_structure[$eventkey]['questions'] = []; + $qb_fetch = $this->connection->getQueryBuilder(); + $qb_fetch->select('id', 'form_question_text') + ->from('forms_questions') + ->where($qb_fetch->expr()->eq('form_id', $qb_fetch->createNamedParameter($event['id'], IQueryBuilder::PARAM_INT))); + $cursor = $qb_fetch->execute(); + while( $tmp = $cursor->fetch() ) { + $event_structure[$event['id']]['questions'][] = $tmp; + } + $cursor->closeCursor(); + } + + //Fetch Votes and restore to Submissions & Answers + $qb_fetch = $this->connection->getQueryBuilder(); + $qb_restore = $this->connection->getQueryBuilder(); + //initialize $last_vote + $last_vote = []; + $last_vote['form_id'] = 0; + $last_vote['user_id'] = ''; + $last_vote['vote_option_id'] = 0; + + $qb_fetch->select('id', 'form_id', 'user_id', 'vote_option_id', 'vote_option_text', 'vote_answer') + ->from('forms_votes'); + $cursor = $qb_fetch->execute(); + while( $vote = $cursor->fetch() ){ + //If the form changed, if the user changed or if vote_option_id became smaller than last one, then a new submission is interpreted. + if ( ($vote['form_id'] != $last_vote['form_id']) || ($vote['user_id'] != $last_vote['user_id']) || ($vote['vote_option_id'] < $last_vote['vote_option_id'])) { + $qb_restore->insert('forms_v2_submissions') + ->values([ + 'form_id' => $qb_restore->createNamedParameter($id_mapping['events'][$vote['form_id']], IQueryBuilder::PARAM_INT), + 'user_id' => $qb_restore->createNamedParameter($vote['user_id'], IQueryBuilder::PARAM_STR), + 'timestamp' => $qb_restore->createNamedParameter(date('Y-m-d H:i:s'), IQueryBuilder::PARAM_STR) //Information not available. Just using Migration-Timestamp. + ]); + $qb_restore->execute(); + $id_mapping['currentSubmission'] = $qb_restore->getLastInsertId(); //Store submission-id to connect answers to submission. + } + $last_vote = $vote; + + //In case the old Answer would have been longer than current possible length, create a warning and shorten text to avoid Error on upgrade. + if(strlen($vote['vote_answer']) > 2048) { + $output->warning("Answer-text is too long for new Database: '" . $vote['vote_answer'] . "'"); + $vote['vote_answer'] = substr($vote['vote_answer'], 0, 2048); + } + + /* Due to the unconventional storing fo vote_option_ids, the vote_option_id needs to get mapped onto old question-id and from there to new question-id. + * vote_option_ids count from 1 to x for the questions of a form. So the question at point [$vote[vote_option_id] - 1] within the id-structure is the corresponding question. + */ + $oldQuestionId = $event_structure[$vote['form_id']]['questions'][$vote['vote_option_id']-1]['id']; + //Just throw an Error, if aboves QuestionId-Mapping went wrong. Double-Checked by Question-Text. + if ($event_structure[$vote['form_id']]['questions'][$vote['vote_option_id']-1]['form_question_text'] != $vote['vote_option_text']) { + $output->warning("Some Question-Mapping went wrong within Submission-Mapping to new Database. On 'vote_id': " . $vote['id'] . " - 'vote_option_text': '" . $vote['vote_option_text'] . "'"); + } + + $qb_restore->insert('forms_v2_answers') + ->values([ + 'submission_id' => $qb_restore->createNamedParameter($id_mapping['currentSubmission'], IQueryBuilder::PARAM_INT), + 'question_id' => $qb_restore->createNamedParameter($id_mapping['questions'][$oldQuestionId], IQueryBuilder::PARAM_STR), + 'text' => $qb_restore->createNamedParameter($vote['vote_answer'], IQueryBuilder::PARAM_STR) + ]); + $qb_restore->execute(); + } + } + } +} diff --git a/src/Forms.vue b/src/Forms.vue index 976943b..0048f8b 100644 --- a/src/Forms.vue +++ b/src/Forms.vue @@ -107,7 +107,7 @@ export default { selectedForm() { // TODO: replace with form.hash - return this.forms.find(form => form.event.hash === this.hash) + return this.forms.find(form => form.form.hash === this.hash) }, }, @@ -141,7 +141,7 @@ export default { const response = await axios.post(generateUrl('/apps/forms/api/v1/form')) const newForm = response.data this.forms.push(newForm) - this.$router.push({ name: 'edit', params: { hash: newForm.event.hash } }) + this.$router.push({ name: 'edit', params: { hash: newForm.form.hash } }) } catch (error) { showError(t('forms', 'Unable to create a new form')) console.error(error) diff --git a/src/components/_base-CloudDiv.vue b/src/components/_base-CloudDiv.vue index 785f92b..103903f 100644 --- a/src/components/_base-CloudDiv.vue +++ b/src/components/_base-CloudDiv.vue @@ -26,8 +26,8 @@ {{ t('forms', 'Expired') }} - - {{ t('forms', 'Expires %n', 1, expirationdate) }} + + {{ t('forms', 'Expires %n', 1, expirationDate) }} {{ t('forms', 'Expires never') }} @@ -39,12 +39,6 @@ {{ t('forms', 'Anonymous form') }} - - {{ t('forms', 'Usernames hidden to Owner') }} - - - {{ t('forms', 'Usernames visible to Owner') }} - @@ -61,7 +55,7 @@ export default { }, computed: { - expirationdate() { + expirationDate() { const date = moment(this.options.expirationDate, moment.localeData().longDateFormat('L')).fromNow() return date }, diff --git a/src/components/formListItem.vue b/src/components/formListItem.vue index c41b87b..bf38855 100644 --- a/src/components/formListItem.vue +++ b/src/components/formListItem.vue @@ -72,12 +72,12 @@ @@ -102,18 +102,18 @@
-
+
{{ accessType }}
- +
{{ timeSpanCreated }}
-
+
{{ timeSpanExpiration }}
@@ -150,13 +150,13 @@ export default { computed: { accessType() { - if (this.form.event.access === 'public') { + if (this.form.form.access === 'public') { return t('forms', 'Public access') - } else if (this.form.event.access === 'select') { + } else if (this.form.form.access === 'select') { return t('forms', 'Only shared') - } else if (this.form.event.access === 'registered') { + } else if (this.form.form.access === 'registered') { return t('forms', 'Registered users only') - } else if (this.form.event.access === 'hidden') { + } else if (this.form.form.access === 'hidden') { return t('forms', 'Hidden form') } else { return '' @@ -164,12 +164,12 @@ export default { }, timeSpanCreated() { - return moment(this.form.event.created, 'YYYY-MM-DD HH:mm') + return moment(this.form.form.created, 'YYYY-MM-DD HH:mm') }, timeSpanExpiration() { - if (this.form.event.expiration) { - return moment(this.form.event.expirationDate) + if (this.form.form.expires) { + return moment(this.form.form.expirationDate) } else { return t('forms', 'never') } @@ -179,8 +179,8 @@ export default { return this.form.shares.length }, - voteUrl() { - return OC.generateUrl('apps/forms/form/') + this.form.event.hash + submitUrl() { + return OC.generateUrl('apps/forms/form/') + this.form.form.hash }, }, @@ -196,7 +196,7 @@ export default { copyLink() { // this.$emit('copyLink') - this.$copyText(window.location.origin + this.voteUrl).then( + this.$copyText(window.location.origin + this.submitUrl).then( function(e) { OC.Notification.showTemporary(t('forms', 'Link copied to clipboard')) }, @@ -217,6 +217,7 @@ export default { }, } +