Fix file submissions preview
This commit is contained in:
parent
bf98497711
commit
91432c4aed
|
@ -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)
|
||||
|
|
|
@ -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,6 +61,10 @@ class FormSubmissionController extends Controller
|
|||
], 404);
|
||||
}
|
||||
|
||||
if (config('filesystems.default') !== 's3') {
|
||||
return response()->file(Storage::path($fileName));
|
||||
}
|
||||
|
||||
return redirect(
|
||||
Storage::temporaryUrl($fileName, now()->addMinute())
|
||||
);
|
||||
|
|
|
@ -15,7 +15,7 @@ class Authenticate extends Middleware
|
|||
protected function redirectTo($request)
|
||||
{
|
||||
if (! $request->expectsJson()) {
|
||||
return redirect('/login');
|
||||
return redirect(front_url('login'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
];
|
||||
});
|
||||
|
|
|
@ -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
|
||||
})
|
||||
|
||||
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({
|
||||
type: 'form-submitted',
|
||||
form: {
|
||||
slug: this.form.slug,
|
||||
id: this.form.id
|
||||
},
|
||||
submission_data: form.data()
|
||||
}, '*')
|
||||
window.parent.postMessage(payload, '*')
|
||||
}
|
||||
window.postMessage({
|
||||
type: 'form-submitted',
|
||||
form: {
|
||||
slug: this.form.slug,
|
||||
id: this.form.id
|
||||
},
|
||||
submission_data: form.data()
|
||||
}, '*')
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
<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>
|
||||
<a :href="file.file_url" target="_blank" download>{{ file.displayed_file_name }}</a>
|
||||
</span>
|
||||
</a>
|
||||
</span>
|
||||
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
import opnformConfig from "~/opnform.config.js";
|
||||
|
||||
export const storeFile = async (file, options = {}) => {
|
||||
if(!opnformConfig.s3_enabled) { // If not s3 then upload to local temp
|
||||
async function storeLocalFile(file, options={}) {
|
||||
let formData = new FormData()
|
||||
formData.append('file', file)
|
||||
const response = await useOpnApi('/upload-file', {
|
||||
const response = await opnFetch('/upload-file', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
response.data.extension = file.name.split('.').pop()
|
||||
return response.data
|
||||
response.extension = file.name.split('.').pop()
|
||||
return response
|
||||
}
|
||||
|
||||
const response = await useOpnApi(options.signedStorageUrl ? options.signedStorageUrl : '/vapor/signed-storage-url', {
|
||||
export const storeFile = async (file, options = {}) => {
|
||||
if(!useRuntimeConfig().public.s3Enabled) return storeLocalFile(file, options)
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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' })
|
||||
|
||||
|
|
|
@ -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');
|
||||
|
||||
|
|
Loading…
Reference in New Issue