Browse Source

fix: enable attachment upload in shared view

re #687

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/743/head
Pranav C 3 years ago
parent
commit
afcb955071
  1. 2
      packages/nc-gui/components/project/appStore/inputs/attachment.vue
  2. 2
      packages/nc-gui/components/project/settings/xcMeta.vue
  3. 2
      packages/nc-gui/components/project/spreadsheet/components/editableCell.vue
  4. 93
      packages/nc-gui/components/project/spreadsheet/components/editableCell/editableAttachmentCell.vue
  5. 24
      packages/nc-gui/components/project/spreadsheet/components/editableCell/editableUrlCell.vue
  6. 2
      packages/nc-gui/components/project/spreadsheet/helpers/imageExt.js
  7. 37
      packages/nc-gui/components/project/spreadsheet/public/xcForm.vue
  8. 2
      packages/nc-gui/pages/projects/index.vue
  9. 31
      packages/nc-gui/store/sqlMgr.js
  10. 1
      packages/nocodb/src/interface/IStorageAdapter.ts
  11. 153
      packages/nocodb/src/lib/noco/meta/NcMetaMgr.ts

2
packages/nc-gui/components/project/appStore/inputs/attachment.vue

@ -246,7 +246,7 @@ export default {
} }
this.uploading = true this.uploading = true
for (const file of this.$refs.file.files) { for (const file of this.$refs.file.files) {
const item = await this.$store.dispatch('sqlMgr/ActUpload', [{ const item = await this.$store.dispatch('sqlMgr/ActUploadOld', [{
dbAlias: this.dbAlias dbAlias: this.dbAlias
}, 'xcAttachmentUpload', { public: true }, file]) }, 'xcAttachmentUpload', { public: true }, file])
this.localState.push(item) this.localState.push(item)

2
packages/nc-gui/components/project/settings/xcMeta.vue

@ -288,7 +288,7 @@ export default {
this.loading = 'import-zip' this.loading = 'import-zip'
try { try {
this.$refs.importFile.value = '' this.$refs.importFile.value = ''
await this.$store.dispatch('sqlMgr/ActUpload', [ await this.$store.dispatch('sqlMgr/ActUploadOld', [
{ {
env: '_noco' env: '_noco'
}, },

2
packages/nc-gui/components/project/spreadsheet/components/editableCell.vue

@ -13,8 +13,10 @@
:active="active" :active="active"
:db-alias="dbAlias" :db-alias="dbAlias"
:meta="meta" :meta="meta"
:is-form="isForm"
:column="column" :column="column"
:is-public-grid="isPublic && !isForm" :is-public-grid="isPublic && !isForm"
:is-public-form="isPublic && isForm"
v-on="$listeners" v-on="$listeners"
/> />

93
packages/nc-gui/components/project/spreadsheet/components/editableCell/editableAttachmentCell.vue

@ -19,8 +19,8 @@
<div class="d-flex align-center img-container"> <div class="d-flex align-center img-container">
<div <div
v-for="(item,i) in localState" v-for="(item,i) in (isPublicForm ? localFilesState : localState)"
:key="item.url" :key="item.url || item.title"
class="thumbnail align-center justify-center d-flex" class="thumbnail align-center justify-center d-flex"
> >
<v-tooltip bottom> <v-tooltip bottom>
@ -32,9 +32,9 @@
alt="#" alt="#"
max-height="33px" max-height="33px"
contain contain
:src="item.url" :src="item.url || item.data"
v-on="on" v-on="on"
@click="selectImage(item.url,i)" @click="selectImage(item.url || item.data,i)"
> >
<template #placeholder> <template #placeholder>
<v-skeleton-loader <v-skeleton-loader
@ -44,12 +44,17 @@
/> />
</template> </template>
</v-img> </v-img>
<v-icon v-else-if="item.icon" :size="active ? 33 : 22" v-on="on" @click="openUrl(item.url,'_blank')"> <v-icon
v-else-if="item.icon"
:size="active ? 33 : 22"
v-on="on"
@click="openUrl(item.url || item.data,'_blank')"
>
{{ {{
item.icon item.icon
}} }}
</v-icon> </v-icon>
<v-icon v-else :size="active ? 33 : 22" v-on="on" @click="openUrl(item.url,'_blank')"> <v-icon v-else :size="active ? 33 : 22" v-on="on" @click="openUrl(item.url|| item.data,'_blank')">
mdi-file mdi-file
</v-icon> </v-icon>
</template> </template>
@ -82,7 +87,13 @@
<v-card class="h-100 images-modal"> <v-card class="h-100 images-modal">
<v-card-text class="h-100 backgroundColor"> <v-card-text class="h-100 backgroundColor">
<div class="d-flex mx-2"> <div class="d-flex mx-2">
<v-btn v-if="_isUIAllowed('tableAttachment') && !isPublicGrid" small class="my-4 " :loading="uploading" @click="addFile"> <v-btn
v-if="_isUIAllowed('tableAttachment') && !isPublicGrid"
small
class="my-4 "
:loading="uploading"
@click="addFile"
>
<v-icon small class="mr-2"> <v-icon small class="mr-2">
mdi-link-variant mdi-link-variant
</v-icon> </v-icon>
@ -97,13 +108,18 @@
class="row" class="row"
@update="onOrderUpdate" @update="onOrderUpdate"
> >
<v-col v-for="(item,i) in localState" :key="i" cols="4"> <v-col v-for="(item,i) in (isPublicForm ? localFilesState : localState)" :key="i" cols="4">
<v-card <v-card
class="modal-thumbnail-card align-center justify-center d-flex" class="modal-thumbnail-card align-center justify-center d-flex"
height="200px" height="200px"
style="position: relative" style="position: relative"
> >
<v-icon v-if="_isUIAllowed('tableAttachment') && !isPublicGrid" small class="remove-icon" @click="removeItem(i)"> <v-icon
v-if="_isUIAllowed('tableAttachment') && !isPublicGrid"
small
class="remove-icon"
@click="removeItem(i)"
>
mdi-close-circle mdi-close-circle
</v-icon> </v-icon>
<v-icon color="grey" class="download-icon" @click.stop="downloadItem(item,i)"> <v-icon color="grey" class="download-icon" @click.stop="downloadItem(item,i)">
@ -114,16 +130,16 @@
v-if="isImage(item.title)" v-if="isImage(item.title)"
style="max-height: 100%;max-width: 100%" style="max-height: 100%;max-width: 100%"
alt="#" alt="#"
:src="item.url" :src="item.url || item.data"
@click="selectImage(item.url,i)" @click="selectImage(item.url,i)"
> >
<v-icon v-else-if="item.icon" size="33" @click="openUrl(item.url,'_blank')"> <v-icon v-else-if="item.icon" size="33" @click="openUrl(item.url || item.data,'_blank')">
{{ {{
item.icon item.icon
}} }}
</v-icon> </v-icon>
<v-icon v-else size="33" @click="openUrl(item.url,'_blank')"> <v-icon v-else size="33" @click="openUrl(item.url || item.data,'_blank')">
mdi-file mdi-file
</v-icon> </v-icon>
</div> </div>
@ -144,7 +160,7 @@
<template v-if="showImage && selectedImage"> <template v-if="showImage && selectedImage">
<v-carousel v-model="carousel" height="calc(100vh - 100px)" hide-delimiters> <v-carousel v-model="carousel" height="calc(100vh - 100px)" hide-delimiters>
<v-carousel-item <v-carousel-item
v-for="(item,i) in localState" v-for="(item,i) in (isPublicForm ? localFilesState : localState)"
:key="i" :key="i"
> >
<div class="mx-auto d-flex flex-column justify-center align-center" style="min-height:100px"> <div class="mx-auto d-flex flex-column justify-center align-center" style="min-height:100px">
@ -158,7 +174,7 @@
<img <img
v-if="isImage(item.title)" v-if="isImage(item.title)"
style="max-width:90vh;max-height:calc(100vh - 100px)" style="max-width:90vh;max-height:calc(100vh - 100px)"
:src="item.url" :src="item.url || item.data"
> >
<v-icon v-else-if="item.icon" size="55"> <v-icon v-else-if="item.icon" size="55">
{{ item.icon }} {{ item.icon }}
@ -183,7 +199,7 @@
show-arrows show-arrows
> >
<v-slide-item <v-slide-item
v-for="(item,i) in localState" v-for="(item,i) in (isPublicForm ? localFilesState : localState)"
:key="i" :key="i"
> >
<!-- <div class="d-flex justify-center" style="height:80px">--> <!-- <div class="d-flex justify-center" style="height:80px">-->
@ -198,7 +214,7 @@
<img <img
v-if="isImage(item.title)" v-if="isImage(item.title)"
style="max-width:100%;max-height:100%" style="max-width:100%;max-height:100%"
:src="item.url" :src="item.url || item.data"
> >
<v-icon v-else-if="item.icon" size="48"> <v-icon v-else-if="item.icon" size="48">
{{ item.icon }} {{ item.icon }}
@ -229,7 +245,7 @@ import { isImage } from '@/components/project/spreadsheet/helpers/imageExt'
export default { export default {
name: 'EditableAttachmentCell', name: 'EditableAttachmentCell',
components: { draggable }, components: { draggable },
props: ['dbAlias', 'value', 'active', 'isLocked', 'meta', 'column', 'isPublicGrid'], props: ['dbAlias', 'value', 'active', 'isLocked', 'meta', 'column', 'isPublicGrid', 'isForm', 'isPublicForm'],
data: () => ({ data: () => ({
carousel: null, carousel: null,
uploading: false, uploading: false,
@ -237,7 +253,8 @@ export default {
dialog: false, dialog: false,
showImage: false, showImage: false,
selectedImage: null, selectedImage: null,
dragOver: false dragOver: false,
localFilesState: []
}), }),
watch: { watch: {
value(val, prev) { value(val, prev) {
@ -287,14 +304,34 @@ export default {
} }
}, },
async onFileSelection() { async onFileSelection() {
if (this.isPublicGrid) { return } if (this.isPublicGrid) {
return
}
if (!this.$refs.file.files || !this.$refs.file.files.length) { if (!this.$refs.file.files || !this.$refs.file.files.length) {
return return
} }
if (this.isPublicForm) {
this.localFilesState.push(...Array.from(this.$refs.file.files).map((file) => {
const res = { file, title: file.name }
if (isImage(file.name)) {
const reader = new FileReader()
reader.onload = (e) => {
this.$set(res, 'data', e.target.result)
}
reader.readAsDataURL(file)
}
return res
}))
this.$emit('input', this.localFilesState.map(f => f.file))
return
}
this.uploading = true this.uploading = true
for (const file of this.$refs.file.files) { for (const file of this.$refs.file.files) {
try { try {
const item = await this.$store.dispatch('sqlMgr/ActUpload', [{ const item = await this.$store.dispatch('sqlMgr/ActUploadOld', [{
dbAlias: this.dbAlias dbAlias: this.dbAlias
}, 'xcAttachmentUpload', { }, 'xcAttachmentUpload', {
appendPath: [this.meta.tn], appendPath: [this.meta.tn],
@ -317,12 +354,17 @@ export default {
this.$emit('update') this.$emit('update')
}, },
removeItem(i) { removeItem(i) {
this.localState.splice(i, 1) if (this.isPublicForm) {
this.$emit('input', JSON.stringify(this.localState)) this.localFilesState.splice(i, 1)
this.$emit('input', this.localFilesState.map(f => f.file))
} else {
this.localState.splice(i, 1)
this.$emit('input', JSON.stringify(this.localState))
}
this.$emit('update') this.$emit('update')
}, },
downloadItem(item) { downloadItem(item) {
FileSaver.saveAs(item.url, item.title) FileSaver.saveAs(item.url || item.data, item.title)
}, },
onArrowDown(e) { onArrowDown(e) {
if (!this.showImage) { if (!this.showImage) {
@ -512,4 +554,7 @@ export default {
* GNU Affero General Public License for more details. * GNU Affero General Public License for more details.
* *
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this p * along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
-->

24
packages/nc-gui/components/project/spreadsheet/components/editableCell/editableUrlCell.vue

@ -50,3 +50,27 @@ input, textarea {
color: var(--v-textColor-base); color: var(--v-textColor-base);
} }
</style> </style>
<!--
/**
* @copyright Copyright (c) 2021, Xgene Cloud Ltd
*
* @author Naveen MR <oof1lab@gmail.com>
* @author Pranav C Balan <pranavxc@gmail.com>
*
* @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 <http://www.gnu.org/licenses/>.
*
*/
-->

2
packages/nc-gui/components/project/spreadsheet/helpers/imageExt.js

@ -1,4 +1,4 @@
const imageExt = ['jpeg', 'gif', 'png', 'apng', 'svg', 'bmp', 'ico', 'jpg'] const imageExt = ['jpeg', 'gif', 'png', 'png', 'svg', 'bmp', 'ico', 'jpg']
export default imageExt export default imageExt

37
packages/nc-gui/components/project/spreadsheet/public/xcForm.vue

@ -171,6 +171,7 @@
:meta="meta" :meta="meta"
:sql-ui="sqlUiLoc" :sql-ui="sqlUiLoc"
is-form is-form
is-public
:hint="localParams.fields[col.alias].description" :hint="localParams.fields[col.alias].description"
@focus="active = col._cn" @focus="active = col._cn"
@blur="active = ''" @blur="active = ''"
@ -306,7 +307,9 @@ export default {
const showFields = this.query_params.showFields || {} const showFields = this.query_params.showFields || {}
let fields = this.query_params.fieldsOrder || [] let fields = this.query_params.fieldsOrder || []
if (!fields.length) { fields = Object.keys(showFields) } if (!fields.length) {
fields = Object.keys(showFields)
}
// eslint-disable-next-line camelcase // eslint-disable-next-line camelcase
let columns = this.meta.columns let columns = this.meta.columns
@ -371,12 +374,29 @@ export default {
// if (this.isNew) { // if (this.isNew) {
await this.$store.dispatch('sqlMgr/ActSqlOp', [null, 'sharedViewInsert', { const formData = new FormData()
view_id: this.$route.params.id, const data = { ...this.localState }
password: this.password,
data: this.localState, for (const col of this.meta.columns) {
nested: this.virtual if (col.uidt === 'Attachment') {
}]) const files = data[col._cn]
delete data[col._cn]
for (const file of (files || [])) {
formData.append(`${col._cn}`, file)
}
}
}
await this.$store.dispatch('sqlMgr/ActUpload', {
op: 'sharedViewInsert',
opArgs: {
view_id: this.$route.params.id,
password: this.password,
data,
nested: this.virtual
},
formData
})
// //
// data = { ...this.localState, ...data } // data = { ...this.localState, ...data }
@ -399,7 +419,8 @@ export default {
this.$toast.success(this.localParams.submit.message || 'Saved successfully.', { this.$toast.success(this.localParams.submit.message || 'Saved successfully.', {
position: 'bottom-right' position: 'bottom-right'
}).goAway(3000) }).goAway(3000)
} catch (e) { } catch
(e) {
console.log(e) console.log(e)
this.$toast.error(`Failed to update row : ${e.message}`).goAway(3000) this.$toast.error(`Failed to update row : ${e.message}`).goAway(3000)
} }

2
packages/nc-gui/pages/projects/index.vue

@ -1113,7 +1113,7 @@ export default {
this.loading = 'import-zip' this.loading = 'import-zip'
try { try {
this.$refs.importFile.value = '' this.$refs.importFile.value = ''
await this.$store.dispatch('sqlMgr/ActUpload', [ await this.$store.dispatch('sqlMgr/ActUploadOld', [
{ {
// dbAlias: 'db', // dbAlias: 'db',
project_id: projectId, project_id: projectId,

31
packages/nc-gui/store/sqlMgr.js

@ -354,7 +354,12 @@ export const actions = {
} }
}, },
async ActSqlOp({ commit, state, rootState, dispatch }, [args, op, opArgs, cusHeaders, cusAxiosOptions, queryParams, returnResponse]) { async ActSqlOp({
commit,
state,
rootState,
dispatch
}, [args, op, opArgs, cusHeaders, cusAxiosOptions, queryParams, returnResponse]) {
const params = {} const params = {}
if (this.$router.currentRoute && this.$router.currentRoute.params && this.$router.currentRoute.params.project_id) { if (this.$router.currentRoute && this.$router.currentRoute.params && this.$router.currentRoute.params.project_id) {
params.project_id = this.$router.currentRoute.params.project_id params.project_id = this.$router.currentRoute.params.project_id
@ -415,7 +420,24 @@ export const actions = {
} }
}, },
async ActUpload({ commit, state, rootState }, [args, op, opArgs, file, cusHeaders, cusAxiosOptions]) { async ActUploadOld({
commit,
state,
rootState,
dispatch
}, [args, op, opArgs, file, cusHeaders, cusAxiosOptions, formData]) {
return await dispatch('ActUpload', { args, op, opArgs, file, cusHeaders, cusAxiosOptions, formData })
},
async ActUpload({ commit, state, rootState }, {
args = {},
op,
opArgs,
file,
cusHeaders = {},
cusAxiosOptions = {},
formData = new FormData()
}) {
try { try {
const params = {} const params = {}
if (this.$router.currentRoute && this.$router.currentRoute.params && this.$router.currentRoute.params.project_id) { if (this.$router.currentRoute && this.$router.currentRoute.params && this.$router.currentRoute.params.project_id) {
@ -435,9 +457,10 @@ export const actions = {
Object.assign(headers, cusHeaders) Object.assign(headers, cusHeaders)
} }
const formData = new FormData() if (file) {
formData.append('file', file)
}
formData.append('file', file)
formData.append('json', JSON.stringify({ api: op, ...params, ...args, args: opArgs })) formData.append('json', JSON.stringify({ api: op, ...params, ...args, args: opArgs }))
// formData.append('project_id', params.project_id); // formData.append('project_id', params.project_id);

1
packages/nocodb/src/interface/IStorageAdapter.ts

@ -11,6 +11,7 @@ interface XcFile {
path: string; path: string;
mimetype: string; mimetype: string;
size: number | string; size: number | string;
buffer?: any;
} }
export { XcFile }; export { XcFile };

153
packages/nocodb/src/lib/noco/meta/NcMetaMgr.ts

@ -38,6 +38,7 @@ import { packageVersion } from 'nc-help';
import NcMetaIO, { META_TABLES } from './NcMetaIO'; import NcMetaIO, { META_TABLES } from './NcMetaIO';
import { promisify } from 'util'; import { promisify } from 'util';
import NcTemplateParser from '../../templateParser/NcTemplateParser'; import NcTemplateParser from '../../templateParser/NcTemplateParser';
import UITypes from '../../sqlUi/UITypes';
const XC_PLUGIN_DET = 'XC_PLUGIN_DET'; const XC_PLUGIN_DET = 'XC_PLUGIN_DET';
@ -128,19 +129,24 @@ export default class NcMetaMgr {
}) })
); );
// todo: add multer middleware only for certain api calls
if (!process.env.NC_SERVERLESS_TYPE && !this.config.try) { if (!process.env.NC_SERVERLESS_TYPE && !this.config.try) {
const upload = multer({ const upload = multer({
dest: path.join(this.config.toolDir, 'uploads') storage: multer.diskStorage({
// dest: path.join(this.config.toolDir, 'uploads')
})
}); });
router.post(this.config.dashboardPath, upload.single('file')); // router.post(this.config.dashboardPath, upload.single('file'));
router.post(this.config.dashboardPath, upload.any());
} }
router.post(this.config.dashboardPath, (req, res, next) => router.post(this.config.dashboardPath, (req, res, next) =>
this.handlePublicRequest(req, res, next) this.handlePublicRequest(req, res, next)
); );
// @ts-ignore // @ts-ignore
router.post(this.config.dashboardPath, async (req: any, res, next) => { router.post(this.config.dashboardPath, async (req: any, res, next) => {
if (req.file && req.body.json) { if (req.files && req.body.json) {
req.body = JSON.parse(req.body.json); req.body = JSON.parse(req.body.json);
} }
if (req?.session?.passport?.user?.isAuthorized) { if (req?.session?.passport?.user?.isAuthorized) {
@ -209,7 +215,7 @@ export default class NcMetaMgr {
} }
} }
if (req.file) { if (req.files) {
await this.handleRequestWithFile(req, res, next); await this.handleRequestWithFile(req, res, next);
} else { } else {
await this.handleRequest(req, res, next); await this.handleRequest(req, res, next);
@ -219,11 +225,11 @@ export default class NcMetaMgr {
async (req: any, res) => { async (req: any, res) => {
try { try {
let output; let output;
if (req.file) { if (req.files && req.body.json) {
req.body = JSON.parse(req.body.json); req.body = JSON.parse(req.body.json);
output = await this.projectMgr output = await this.projectMgr
.getSqlMgr({ id: req.body.project_id }) .getSqlMgr({ id: req.body.project_id })
.handleRequestWithFile(req.body.api, req.body, req.file); .handleRequestWithFile(req.body.api, req.body, req.files);
} else { } else {
output = await this.projectMgr output = await this.projectMgr
.getSqlMgr({ id: req.body.project_id }) .getSqlMgr({ id: req.body.project_id })
@ -336,7 +342,7 @@ export default class NcMetaMgr {
} }
public async handleRequestWithFile(req, res, next) { public async handleRequestWithFile(req, res, next) {
const [operation, args, file] = [req.body.api, req.body, req.file]; const [operation, args, file] = [req.body.api, req.body, req.files?.[0]];
let result; let result;
try { try {
switch (operation) { switch (operation) {
@ -1230,43 +1236,15 @@ export default class NcMetaMgr {
const prependName = args.args.prependName?.length const prependName = args.args.prependName?.length
? args.args.prependName.join('_') + '_' ? args.args.prependName.join('_') + '_'
: ''; : '';
const fileName = `${prependName}${nanoid(6)}_${file.originalname}`; return await this._uploadFile({
let destPath; prependName,
if (args?.args?.public) { file,
destPath = path.join('nc', 'public', 'files', 'uploads', ...appendPath); storeInPublicFolder: args?.args?.public,
} else { appendPath,
destPath = path.join( req,
'nc', dbAlias: this.getDbAlias(args),
this.getProjectId(args), projectId: this.getProjectId(args)
this.getDbAlias(args), });
'uploads',
...appendPath
);
}
let url = await this.storageAdapter.fileCreate(
slash(path.join(destPath, fileName)),
file
);
if (!url) {
if (args?.args?.public) {
url = `${req.ncSiteUrl}/dl/public/files/${
appendPath?.length ? appendPath.join('/') + '/' : ''
}${fileName}`;
} else {
url = `${req.ncSiteUrl}/dl/${this.getProjectId(
args
)}/${this.getDbAlias(args)}/${
appendPath?.length ? appendPath.join('/') + '/' : ''
}${fileName}`;
}
}
return {
url,
title: file.originalname,
mimetype: file.mimetype,
size: file.size,
icon: mimeIcons[path.extname(file.originalname).slice(1)] || undefined
};
} catch (e) { } catch (e) {
throw e; throw e;
} finally { } finally {
@ -1274,6 +1252,54 @@ export default class NcMetaMgr {
} }
} }
private async _uploadFile({
prependName = '',
file,
storeInPublicFolder = false,
appendPath = [],
req,
projectId,
dbAlias
}: {
prependName?: string;
file: any;
storeInPublicFolder: boolean;
appendPath?: string[];
req: express.Request & any;
projectId?: string;
dbAlias?: string;
}) {
const fileName = `${prependName}${nanoid(6)}_${file.originalname}`;
let destPath;
if (storeInPublicFolder) {
destPath = path.join('nc', 'public', 'files', 'uploads', ...appendPath);
} else {
destPath = path.join('nc', projectId, dbAlias, 'uploads', ...appendPath);
}
let url = await this.storageAdapter.fileCreate(
slash(path.join(destPath, fileName)),
file
);
if (!url) {
if (storeInPublicFolder) {
url = `${req.ncSiteUrl}/dl/public/files/${
appendPath?.length ? appendPath.join('/') + '/' : ''
}${fileName}`;
} else {
url = `${req.ncSiteUrl}/dl/${projectId}/${dbAlias}/${
appendPath?.length ? appendPath.join('/') + '/' : ''
}${fileName}`;
}
}
return {
url,
title: file.originalname,
mimetype: file.mimetype,
size: file.size,
icon: mimeIcons[path.extname(file.originalname).slice(1)] || undefined
};
}
protected async initTwilio(overwrite = false): Promise<void> { protected async initTwilio(overwrite = false): Promise<void> {
const activeStorage = await this.xcMeta.metaGet(null, null, 'nc_plugins', { const activeStorage = await this.xcMeta.metaGet(null, null, 'nc_plugins', {
active: true, active: true,
@ -1294,7 +1320,11 @@ export default class NcMetaMgr {
} }
protected async handlePublicRequest(req, res, next) { protected async handlePublicRequest(req, res, next) {
const args = req.body; let args = req.body;
try {
if (req.body.json) args = JSON.parse(req.body.json);
} catch {}
let result; let result;
try { try {
switch (args.api) { switch (args.api) {
@ -1304,7 +1334,6 @@ export default class NcMetaMgr {
case 'getSharedViewData': case 'getSharedViewData':
result = await this.getSharedViewData(req, args); result = await this.getSharedViewData(req, args);
break; break;
case 'sharedViewGet': case 'sharedViewGet':
result = await this.sharedViewGet(req, args); result = await this.sharedViewGet(req, args);
break; break;
@ -3723,12 +3752,18 @@ export default class NcMetaMgr {
const queryParams = JSON.parse(viewMeta.query_params); const queryParams = JSON.parse(viewMeta.query_params);
// const meta = JSON.parse(viewMeta.meta); // const meta = JSON.parse(viewMeta.meta);
const fields: string[] = Object.keys(queryParams.showFields); const fields: string[] = Object.keys(queryParams.showFields).filter(
k => queryParams.showFields[k]
);
const apiBuilder = this.app?.projectBuilders const apiBuilder = this.app?.projectBuilders
?.find(pb => pb.id === sharedViewMeta.project_id) ?.find(pb => pb.id === sharedViewMeta.project_id)
?.apiBuilders?.find(ab => ab.dbAlias === sharedViewMeta.db_alias); ?.apiBuilders?.find(ab => ab.dbAlias === sharedViewMeta.db_alias);
const tableMeta = (viewMeta.meta = apiBuilder?.getMeta(
sharedViewMeta.model_name
));
const insertObject = Object.entries(args.args.data).reduce( const insertObject = Object.entries(args.args.data).reduce(
(obj, [key, val]) => { (obj, [key, val]) => {
if (fields.includes(key)) { if (fields.includes(key)) {
@ -3745,7 +3780,31 @@ export default class NcMetaMgr {
} }
} }
const attachments = {};
for (const file of req.files || []) {
if (
fields.includes(file?.fieldname) &&
tableMeta.columns.find(
c => c._cn === file?.fieldname && c.uidt === UITypes.Attachment
)
) {
attachments[file.fieldname] = attachments[file.fieldname] || [];
attachments[file.fieldname].push(
await this._uploadFile({
file,
storeInPublicFolder: true,
req
})
);
}
}
for (const [column, data] of Object.entries(attachments)) {
insertObject[column] = JSON.stringify(data);
}
const model = apiBuilder?.xcModels?.[sharedViewMeta.model_name]; const model = apiBuilder?.xcModels?.[sharedViewMeta.model_name];
if (model) { if (model) {
req.query.form = viewMeta.view_name; req.query.form = viewMeta.view_name;
await model.nestedInsert(insertObject, null, req); await model.nestedInsert(insertObject, null, req);

Loading…
Cancel
Save