Fix file submissions preview

This commit is contained in:
Julien Nahum 2024-01-13 19:57:39 +01:00
parent bf98497711
commit 91432c4aed
12 changed files with 73 additions and 69 deletions

View File

@ -48,7 +48,7 @@ class Handler extends ExceptionHandler
{
return $request->expectsJson()
? response()->json(['message' => $exception->getMessage()], 401)
: redirect()->guest(url('/login'));
: redirect(front_url('login'));
}
public function report(Throwable $exception)

View File

@ -15,7 +15,8 @@ class FormSubmissionController extends Controller
{
public function __construct()
{
$this->middleware('auth');
$this->middleware('auth', ['except' => ['submissionFile']]);
$this->middleware('signed', ['only' => ['submissionFile']]);
}
public function submissions(string $id)
@ -51,9 +52,6 @@ class FormSubmissionController extends Controller
public function submissionFile($id, $fileName)
{
$form = Form::findOrFail((int) $id);
$this->authorize('view', $form);
$fileName = Str::of(PublicFormController::FILE_UPLOAD_PATH)->replace('?', $id).'/'
.urldecode($fileName);
@ -63,8 +61,12 @@ class FormSubmissionController extends Controller
], 404);
}
if (config('filesystems.default') !== 's3') {
return response()->file(Storage::path($fileName));
}
return redirect(
Storage::temporaryUrl($fileName, now()->addMinute())
Storage::temporaryUrl($fileName, now()->addMinute())
);
}
}

View File

@ -15,7 +15,7 @@ class Authenticate extends Middleware
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
return redirect('/login');
return redirect(front_url('login'));
}
}
}

View File

@ -50,7 +50,11 @@ class FormSubmissionResource extends JsonResource
return $file !== null && $file;
})->map(function ($file) {
return [
'file_url' => route('open.forms.submissions.file', [$this->form_id, $file]),
'file_url' => \URL::signedRoute(
'open.forms.submissions.file',
[$this->form_id, $file],
now()->addMinutes(10)
),
'file_name' => $file,
];
});

View File

@ -117,6 +117,7 @@ import VButton from '~/components/global/VButton.vue'
import FormCleanings from '../../pages/forms/show/FormCleanings.vue'
import VTransition from '~/components/global/transitions/VTransition.vue'
import {pendingSubmission} from "~/composables/forms/pendingSubmission.js";
import clonedeep from "clone-deep";
export default {
components: { VTransition, VButton, OpenFormButton, OpenForm, FormCleanings },
@ -176,29 +177,24 @@ export default {
this.loading = true
// this.closeAlert()
form.post('/forms/' + this.form.slug + '/answer').then((data) => {
this.$logEvent('form_submission', {
useAmplitude().logEvent('form_submission', {
workspace_id: this.form.workspace_id,
form_id: this.form.id
})
if (this.isIframe) {
window.parent.postMessage({
type: 'form-submitted',
form: {
slug: this.form.slug,
id: this.form.id
},
submission_data: form.data()
}, '*')
}
window.postMessage({
const payload = clonedeep({
type: 'form-submitted',
form: {
slug: this.form.slug,
id: this.form.id
},
submission_data: form.data()
}, '*')
})
if (this.isIframe) {
window.parent.postMessage(payload, '*')
}
window.postMessage(payload, '*')
try {
this.pendingSubmission.remove()
@ -221,7 +217,7 @@ export default {
this.confetti.play()
}
}).catch((error) => {
console.log('here')
console.error(error)
if (error.response && error.data && error.data.message) {
useAlert().error(error.data.message)
}

View File

@ -1,20 +1,20 @@
<template>
<p class="text-xs">
<span v-for="file in value" :key="file.file_url"
<span v-for="file in parsedFiles" :key="file.file_url"
class="whitespace-nowrap rounded-md transition-colors hover:decoration-none"
:class="{'open-file text-gray-700 dark:text-gray-300 truncate':!isImage(file.file_url), 'open-file-img':isImage(file.file_url)}"
:class="{'open-file text-gray-700 dark:text-gray-300 truncate':!file.is_image, 'open-file-img':file.is_image}"
>
<a class="text-gray-700 dark:text-gray-300" :href="file.file_url" target="_blank"
rel="nofollow"
>
<div v-if="isImage(file.file_url)" class="w-8 h-8">
<img class="object-cover h-full w-full rounded" :src="file.file_url"/>
<div v-if="file.is_image" class="w-8 h-8">
<img class="object-cover h-full w-full rounded" :src="file.file_url" @error="failedImages.push(file.file_url)"/>
</div>
<span v-else
class="py-1 px-2"
>
<a :href="file.file_url" target="_blank" download>{{ displayedFileName(file.file_name) }}</a>
</span>
<a :href="file.file_url" target="_blank" download>{{ file.displayed_file_name }}</a>
</span>
</a>
</span>
</p>
@ -31,25 +31,36 @@ export default {
},
data() {
return {}
return {
failedImages: []
}
},
computed: {},
mounted() {
computed: {
parsedFiles() {
return this.value.map((file) => {
return {
file_name: file.file_name,
file_url: file.file_url,
displayed_file_name: this.displayedFileName(file.file_name),
is_image: !this.failedImages.includes(file.file_url) && this.isImage(file.file_name)
}
})
}
},
methods: {
isImage(url) {
isImage(fileName) {
return ['png', 'gif', 'jpg', 'jpeg', 'tif'].some((suffix) => {
return url && url.endsWith(suffix)
return fileName && fileName.endsWith(suffix)
})
},
displayedFileName(fileName) {
const extension = fileName.substr(fileName.lastIndexOf(".") + 1)
const filename = fileName.substr(0, fileName.lastIndexOf("."))
if (filename.length > 12) {
return filename.substr(0, 12) + '(...).' + extension
if (filename.length > 10) {
return filename.substr(0, 10) + '[...].' + extension
}
return filename + '.' + extension
}

View File

@ -47,8 +47,9 @@ export function getOpnRequestsOptions(request, opts) {
addPasswordToFormRequest(request, opts)
addCustomDomainHeader(request, opts)
if (!opts.baseURL) opts.baseURL = config.public.apiBase
return {
baseURL: config.public.apiBase,
async onResponseError({response}) {
const authStore = useAuthStore()

View File

@ -1,18 +1,19 @@
import opnformConfig from "~/opnform.config.js";
async function storeLocalFile(file, options={}) {
let formData = new FormData()
formData.append('file', file)
const response = await opnFetch('/upload-file', {
method: 'POST',
body: formData
})
response.extension = file.name.split('.').pop()
return response
}
export const storeFile = async (file, options = {}) => {
if(!opnformConfig.s3_enabled) { // If not s3 then upload to local temp
let formData = new FormData()
formData.append('file', file)
const response = await useOpnApi('/upload-file', {
method: 'POST',
body: formData
})
response.data.extension = file.name.split('.').pop()
return response.data
}
if(!useRuntimeConfig().public.s3Enabled) return storeLocalFile(file, options)
const response = await useOpnApi(options.signedStorageUrl ? options.signedStorageUrl : '/vapor/signed-storage-url', {
const response = await opnFetch(options.signedStorageUrl || 'vapor/signed-storage-url', {
method: 'POST',
body: options.data,
bucket: options.bucket || '',
@ -24,26 +25,13 @@ export const storeFile = async (file, options = {}) => {
...options.options
})
console.log(response)
const headers = response.data.headers
if ('Host' in headers) {
delete headers.Host
}
if (typeof options.progress === 'undefined') {
options.progress = () => {}
}
// Upload to S3
await useFetch(response.data.url,{
await useFetch(response.url,{
method: 'PUT',
body: file,
headers: headers,
})
response.data.extension = file.name.split('.').pop()
response.extension = file.name.split('.').pop()
return response.data
return response
}

View File

@ -16,6 +16,5 @@ export default {
"zapier_integration": "https://zapier.com/developer/public-invite/146950/58db583730cc46b821614468d94c35de/",
"book_onboarding": "https://zcal.co/i/YQVGEULQ",
"feature_requests": "https://opnform.canny.io/feature-requests"
},
"production": false
}
}

View File

@ -138,7 +138,7 @@ useOpnSeoMeta({
})
useHead({
titleTemplate: (titleChunk) => {
if (form && form.value.is_pro && form.value.seo_meta.page_title) {
if (form && form.value?.is_pro && form.value?.seo_meta.page_title) {
// Disable template if custom SEO title
return titleChunk
}

View File

@ -57,7 +57,7 @@ export default {
},
redirectIfSubscribed () {
if (this.user.is_subscribed) {
this.$logEvent('subscribed', { plan: this.user.has_enterprise_subscription ? 'enterprise' : 'pro' })
useAmplitude().logEvent('subscribed', { plan: this.user.has_enterprise_subscription ? 'enterprise' : 'pro' })
this.$crisp.push(['set', 'session:event', [[['subscribed', { plan: this.user.has_enterprise_subscription ? 'enterprise' : 'pro' }, 'blue']]]])
this.$router.push({ name: 'home' })

View File

@ -86,7 +86,10 @@ Route::group(['middleware' => 'auth:api'], function () {
Route::get('/{id}/submissions', [FormSubmissionController::class, 'submissions'])->name('submissions');
Route::get('/{id}/submissions/export', [FormSubmissionController::class, 'export'])->name('submissions.export');
Route::get('/{id}/submissions/file/{filename}', [FormSubmissionController::class, 'submissionFile'])->name('submissions.file');
Route::get('/{id}/submissions/file/{filename}', [FormSubmissionController::class, 'submissionFile'])
->middleware('signed')
->withoutMiddleware(['auth:api'])
->name('submissions.file');
Route::delete('/{id}/records/{recordid}/delete', [RecordController::class, 'delete'])->name('records.delete');