Skip to content

Commit

Permalink
Download a single submission
Browse files Browse the repository at this point in the history
Signed-off-by: hamza221 <[email protected]>
  • Loading branch information
hamza221 committed Dec 7, 2022
1 parent 4c1b378 commit a7d6184
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 1 deletion.
8 changes: 8 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,14 @@
'apiVersion' => 'v2'
]
],
[
'name' => 'api#exportSubmission',
'url' => '/api/{apiVersion}/submissions/exportSubmission/{submissionId}',
'verb' => 'GET',
'requirements' => [
'apiVersion' => 'v2'
]
],
[
'name' => 'api#exportSubmissionsToCloud',
'url' => '/api/{apiVersion}/submissions/export',
Expand Down
32 changes: 32 additions & 0 deletions lib/Controller/ApiController.php
Original file line number Diff line number Diff line change
Expand Up @@ -1129,7 +1129,39 @@ public function exportQuestion(int $questionId): DataDownloadResponse {
$csv = $this->submissionService->getQuestionCsv($formId, $questionId);
return new DataDownloadResponse($csv['data'], $csv['fileName'], 'text/csv');
}
/**
* @NoAdminRequired
* @NoCSRFRequired
*
* Export a single submission
*
* @param int $submissionId of the submission
* @return DataDownloadResponse
* @throws OCSBadRequestException
* @throws OCSForbiddenException
*/
public function exportSubmission(int $submissionId): DataDownloadResponse {
$this->logger->debug('Export submission: {submissionId}', [
'submissionId' => $submissionId,
]);

try {
$submission = $this->submissionMapper->findById($submissionId);
$formId = $submission->getFormId();
$form = $this->formMapper->findById($formId);
} catch (IMapperException $e) {
$this->logger->debug('Could not find submission');
throw new OCSBadRequestException();
}

if ($form->getOwnerId() !== $this->currentUser->getUID()) {
$this->logger->debug('This form is not owned by the current user');
throw new OCSForbiddenException();
}

$csv = $this->submissionService->getSubmissionCsv($formId, $submissionId);
return new DataDownloadResponse($csv['data'], $csv['fileName'], 'text/csv');
}
/**
* @NoAdminRequired
*
Expand Down
84 changes: 84 additions & 0 deletions lib/Service/SubmissionService.php
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,90 @@ public function getQuestionCsv(int $formId, int $questionId): array {
];
}

/**
* Create a submission Csv
* @param string $formId id of the form
* @param string $questionId id of the question
* @return array{fileName:string,data:string} Array with 'fileName' and 'data'
*/

public function getSubmissionCsv(int $formId ,int $submissionId): array {

try {
$submission = $this->submissionMapper->findById($submissionId);
$form = $this->formMapper->findById($formId);

} catch (DoesNotExistException $e) {
// Just ignore, if no Data. Returns empty Submissions-Array
}

$questions = $this->questionMapper->findByForm($form->getId());
$defaultTimeZone = date_default_timezone_get();
$userTimezone = $this->config->getUserValue($this->currentUser->getUID(), 'core', 'timezone', $defaultTimeZone);

// Process initial header
$header = [];
$header[] = $this->l10n->t('User ID');
$header[] = $this->l10n->t('User display name');
$header[] = $this->l10n->t('Timestamp');
foreach ($questions as $question) {
$header[] = $question->getText();
}

// Init dataset
$data = [];

// Process each answers

$row = [];

// User
$user = $this->userManager->get($submission->getUserId());
if ($user === null) {
// Give empty userId
$row[] = '';
// TRANSLATORS Shown on export if no Display-Name is available.
$row[] = $this->l10n->t('Anonymous user');
} else {
$row[] = $user->getUID();
$row[] = $user->getDisplayName();
}

// Date
$row[] = $this->dateTimeFormatter->formatDateTime($submission->getTimestamp(), 'full', 'full', new DateTimeZone($userTimezone), $this->l10n);

// Answers, make sure we keep the question order
$answers = array_reduce($this->answerMapper->findBySubmission($submission->getId()), function (array $carry, Answer $answer) {
$questionId = $answer->getQuestionId();

// If key exists, insert separator
if (key_exists($questionId, $carry)) {
$carry[$questionId] .= '; ' . $answer->getText();
} else {
$carry[$questionId] = $answer->getText();
}

return $carry;
}, []);

foreach ($questions as $question) {
$row[] = key_exists($question->getId(), $answers)
? $answers[$question->getId()]
: null;
}

$data[] = $row;


// TRANSLATORS Appendix for CSV-Export: 'Form Title (responses).csv'
$fileName = $form->getTitle() . ' (' . $this->l10n->t('responses') . ').csv';

return [
'fileName' => $fileName,
'data' => $this->array2csv($header, $data),
];
}

/**
* Convert an array to a csv string
* @param array $array
Expand Down
17 changes: 16 additions & 1 deletion src/components/Results/Submission.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@
<div class="submission-head">
<h3>{{ submission.userDisplayName }}</h3>
<NcActions class="submission-menu" :force-menu="true">
<NcActionLink :href="responseDownload">
<template #icon>
<IconDownload :size="20" />
</template>
{{ t('forms', 'Download this response') }}
</NcActionLink>
<NcActionButton @click="onDelete">
<template #icon>
<IconDelete :size="20" />
Expand All @@ -47,8 +53,11 @@
<script>
import NcActions from '@nextcloud/vue/dist/Components/NcActions.js'
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
import NcActionLink from '@nextcloud/vue/dist/Components/NcActionLink.js'
import moment from '@nextcloud/moment'
import IconDelete from 'vue-material-design-icons/Delete.vue'
import IconDownload from 'vue-material-design-icons/Download.vue'
import { generateOcsUrl } from '@nextcloud/router'
import Answer from './Answer.vue'
Expand All @@ -58,8 +67,10 @@ export default {
components: {
Answer,
IconDelete,
IconDownload,
NcActions,
NcActionButton,
NcActionLink,
},
props: {
Expand All @@ -74,6 +85,10 @@ export default {
},
computed: {
responseDownload() {
return generateOcsUrl('apps/forms/api/v2/submissions/exportSubmission/{submissionId}', { submissionId: this.submission.id })
},
// Format submission-timestamp to DateTime
submissionDateTime() {
return moment(this.submission.timestamp, 'X').format('LLLL')
Expand Down Expand Up @@ -107,7 +122,7 @@ export default {
methods: {
onDelete() {
this.$emit('delete')
xthis.$emit('delete')
},
},
}
Expand Down

0 comments on commit a7d6184

Please sign in to comment.