Browse Source

feat: add shared view type and show url accordingly

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/636/head
Pranav C 3 years ago
parent
commit
a39e9cbc37
  1. 4
      packages/nc-gui/components/project/spreadsheet/components/sharedViewsList.vue
  2. 5
      packages/nc-gui/components/project/spreadsheet/public/xcForm.vue
  3. 5
      packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSql.ts
  4. 7
      packages/nocodb/src/lib/noco/common/XcMigrationSource.ts
  5. 4
      packages/nocodb/src/lib/noco/meta/NcMetaMgr.ts
  6. 277
      packages/nocodb/src/lib/noco/meta/NcMetaMgrEE.ts
  7. 38
      packages/nocodb/src/lib/noco/migrations/nc_004_add_view_type_column.ts

4
packages/nc-gui/components/project/spreadsheet/components/sharedViewsList.vue

@ -18,8 +18,8 @@
<tbody> <tbody>
<tr v-for="link of viewsList" :key="link.id"> <tr v-for="link of viewsList" :key="link.id">
<td class="caption"> <td class="caption">
<nuxt-link :to="`/nc/view/${link.view_id}`"> <nuxt-link :to="`/nc/${link.view_type || 'view'}/${link.view_id}`">
{{ `${origin}/dashboard#/xc/view/${link.view_id}` }} {{ `${origin}/dashboard#/xc/${link.view_type || 'view'}/${link.view_id}` }}
</nuxt-link> </nuxt-link>
</td> </td>
<td class="caption"> <td class="caption">

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

@ -33,17 +33,18 @@
<!-- <v-chip small color="backgroundColorDefault caption grey--text"> <!-- <v-chip small color="backgroundColorDefault caption grey--text">
Add cover image Add cover image
</v-chip>--> </v-chip>-->
<v-img src="./icon.png" width="50" class="mx-4" /> <span class="display-1 font-weight-bold">NocoDB</span>
</div> </div>
</div> </div>
</div> </div>
<div class="mx-auto nc-form elevation-3 pa-2 mb-10"> <div class="mx-auto nc-form elevation-3 pa-2 mb-10">
<div class="nc-form-logo py-8"> <div class="nc-form-logo py-8" style="display: none">
<!-- <div v-ripple class="nc-form-add-logo text-center caption pointer" @click.stop>--> <!-- <div v-ripple class="nc-form-add-logo text-center caption pointer" @click.stop>-->
<!-- Add a logo--> <!-- Add a logo-->
<!-- </div>--> <!-- </div>-->
</div> </div>
<h2 <h2
class="display-1 font-weight-bold text-left mx-4 mb-3 px-1 text--text text--lighten-1" class="mt-4 display-1 font-weight-bold text-left mx-4 mb-3 px-1 text--text text--lighten-1"
> >
{{ localParams.name }} {{ localParams.name }}
</h2> </h2>

5
packages/nocodb/src/lib/dataMapper/lib/sql/BaseModelSql.ts

@ -1697,12 +1697,11 @@ class BaseModelSql extends BaseModel {
response = response[0]; response = response[0];
} }
rowId = this._extractPksValues(response); if (response) rowId = this._extractPksValues(response);
await Promise.all(postInsertOps.map(f => f())); await Promise.all(postInsertOps.map(f => f()));
if (rowId) {
if(rowId){
response = await this.nestedRead( response = await this.nestedRead(
rowId, rowId,
this.defaultNestedBtQueryParams, this.defaultNestedBtQueryParams,

7
packages/nocodb/src/lib/noco/common/XcMigrationSource.ts

@ -1,15 +1,16 @@
import * as project from '../migrations/nc_001_init'; import * as project from '../migrations/nc_001_init';
import * as m2m from '../migrations/nc_002_add_m2m'; import * as m2m from '../migrations/nc_002_add_m2m';
import * as fkn from '../migrations/nc_003_add_fkn_column'; import * as fkn from '../migrations/nc_003_add_fkn_column';
import * as viewType from '../migrations/nc_004_add_view_type_column';
// Create a custom migration source class // Create a custom migration source class
export default class XcMigrationSource{ export default class XcMigrationSource {
// Must return a Promise containing a list of migrations. // Must return a Promise containing a list of migrations.
// Migrations can be whatever you want, they will be passed as // Migrations can be whatever you want, they will be passed as
// arguments to getMigrationName and getMigration // arguments to getMigrationName and getMigration
public getMigrations(): Promise<any> { public getMigrations(): Promise<any> {
// In this example we are just returning migration names // In this example we are just returning migration names
return Promise.resolve(['project','m2m', 'fkn']) return Promise.resolve(['project', 'm2m', 'fkn', 'viewType']);
} }
public getMigrationName(migration): string { public getMigrationName(migration): string {
@ -24,6 +25,8 @@ export default class XcMigrationSource{
return m2m; return m2m;
case 'fkn': case 'fkn':
return fkn; return fkn;
case 'viewType':
return viewType;
} }
} }
} }

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

@ -3380,7 +3380,7 @@ export default class NcMetaMgr {
condition: { condition: {
model_name: args.args.model_name model_name: args.args.model_name
}, },
fields: ['id', 'view_id', 'password', 'model_name'] fields: ['id', 'view_id', 'password', 'model_name', 'view_type']
} }
); );
} }
@ -3474,7 +3474,7 @@ export default class NcMetaMgr {
limit: args.args.limit, limit: args.args.limit,
offset: args.args.offset offset: args.args.offset
}), }),
count: await model?.countByPk({}) count: (await model?.countByPk({}))?.count
}; };
} catch (e) { } catch (e) {
console.log(e); console.log(e);

277
packages/nocodb/src/lib/noco/meta/NcMetaMgrEE.ts

@ -1,10 +1,9 @@
import {Tele} from 'nc-help'; import { Tele } from 'nc-help';
import {v4 as uuidv4} from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import NcMetaMgr from "./NcMetaMgr"; import NcMetaMgr from './NcMetaMgr';
export default class NcMetaMgrEE extends NcMetaMgr { export default class NcMetaMgrEE extends NcMetaMgr {
/* protected async handlePublicRequest(req, res, next) { /* protected async handlePublicRequest(req, res, next) {
const args = req.body; const args = req.body;
// let result; // let result;
@ -63,68 +62,77 @@ export default class NcMetaMgrEE extends NcMetaMgr {
} }
}*/ }*/
protected async xcTableList(req, args): Promise<any> { protected async xcTableList(req, args): Promise<any> {
const roles = req.session?.passport?.user?.roles; const roles = req.session?.passport?.user?.roles;
let tables = (await this.xcVisibilityMetaGet({...args, args: {type: 'table', ...args.args}})); let tables = await this.xcVisibilityMetaGet({
...args,
args: { type: 'table', ...args.args }
});
tables = tables.filter((table: any) => { tables = tables.filter((table: any) => {
return Object.keys(roles).some(role => roles[role] && !table.disabled[role]) return Object.keys(roles).some(
role => roles[role] && !table.disabled[role]
);
}); });
return { data: { list: tables } };
return {data: {list: tables}};
} }
// NOTE: updated // NOTE: updated
protected async xcAclSave(args, req): Promise<any> { protected async xcAclSave(args, req): Promise<any> {
try { try {
const dbAlias = await this.getDbAlias(args); const dbAlias = await this.getDbAlias(args);
const projectId = await this.getProjectId(args); const projectId = await this.getProjectId(args);
const res = await this.xcMeta.metaUpdate(projectId, dbAlias, 'nc_acl', { const res = await this.xcMeta.metaUpdate(
projectId,
dbAlias,
'nc_acl',
{
acl: JSON.stringify(args.args.acl) acl: JSON.stringify(args.args.acl)
}, { },
{
tn: args.args.tn || args.args.name tn: args.args.tn || args.args.name
}); }
);
this.app.ncMeta.audit(projectId, dbAlias, 'nc_audit', { this.app.ncMeta.audit(projectId, dbAlias, 'nc_audit', {
op_type: 'TABLE_ACL', op_type: 'TABLE_ACL',
op_sub_type: 'UPDATED', op_sub_type: 'UPDATED',
user: req.user.email, user: req.user.email,
description: `updated table ${args.args.tn || args.args.name} acl `, ip: req.clientIp description: `updated table ${args.args.tn || args.args.name} acl `,
ip: req.clientIp
}); });
Tele.emit('evt', { evt_type: 'acl:updated' });
Tele.emit('evt', {evt_type: 'acl:updated'})
return res; return res;
} catch (e) { } catch (e) {
throw(e); throw e;
} }
} }
protected async getSharedViewData(req, args: any): Promise<any> { protected async getSharedViewData(req, args: any): Promise<any> {
try { try {
console.log(args) console.log(args);
const viewMeta = await this.xcMeta.knex('nc_shared_views').where({ const viewMeta = await this.xcMeta
.knex('nc_shared_views')
.where({
view_id: args.args.view_id view_id: args.args.view_id
}).first(); })
.first();
if (viewMeta && viewMeta.password && viewMeta.password !== args.args.password) { if (
throw new Error('Invalid password') viewMeta &&
viewMeta.password &&
viewMeta.password !== args.args.password
) {
throw new Error('Invalid password');
} }
const apiBuilder = this.app?.projectBuilders
const apiBuilder = this.app
?.projectBuilders
?.find(pb => pb.id === viewMeta.project_id) ?.find(pb => pb.id === viewMeta.project_id)
?.apiBuilders ?.apiBuilders?.find(ab => ab.dbAlias === viewMeta.db_alias);
?.find(ab => ab.dbAlias === viewMeta.db_alias);
const model = apiBuilder?.xcModels?.[viewMeta.model_name]; const model = apiBuilder?.xcModels?.[viewMeta.model_name];
if (model) { if (model) {
@ -149,20 +157,17 @@ export default class NcMetaMgrEE extends NcMetaMgr {
where, where,
fields fields
}), }),
...await model.countByPk({ ...(await model.countByPk({
...req.query, ...req.query,
where, where,
fields fields
}), })),
client: apiBuilder?.client client: apiBuilder?.client
};
} }
}
} catch (e) { } catch (e) {
throw e; throw e;
} }
} }
protected async createSharedViewLink(req, args: any): Promise<any> { protected async createSharedViewLink(req, args: any): Promise<any> {
@ -170,10 +175,11 @@ export default class NcMetaMgrEE extends NcMetaMgr {
// todo: keep belongs to column if belongs to virtual column present // todo: keep belongs to column if belongs to virtual column present
if (args.args.query_params?.fields && args.args.show_as !== 'form') { if (args.args.query_params?.fields && args.args.show_as !== 'form') {
const fields = args.args.query_params?.fields.split(','); const fields = args.args.query_params?.fields.split(',');
args.args.meta.columns = args.args.meta.columns.filter(c => fields.includes(c._cn)) args.args.meta.columns = args.args.meta.columns.filter(c =>
fields.includes(c._cn)
);
} }
const insertData = { const insertData = {
project_id: args.project_id, project_id: args.project_id,
db_alias: this.getDbAlias(args), db_alias: this.getDbAlias(args),
@ -181,11 +187,23 @@ export default class NcMetaMgrEE extends NcMetaMgr {
meta: JSON.stringify(args.args.meta), meta: JSON.stringify(args.args.meta),
query_params: JSON.stringify(args.args.query_params), query_params: JSON.stringify(args.args.query_params),
view_id: uuidv4(), view_id: uuidv4(),
password: args.args.password password: args.args.password,
} view_type: args.args.show_as
};
await this.xcMeta.metaInsert(args.project_id, this.getDbAlias(args), 'nc_shared_views', insertData);
const res = await this.xcMeta.metaGet(this.getProjectId(args), this.getDbAlias(args), 'nc_shared_views', insertData, ['id', 'view_id']); await this.xcMeta.metaInsert(
args.project_id,
this.getDbAlias(args),
'nc_shared_views',
insertData
);
const res = await this.xcMeta.metaGet(
this.getProjectId(args),
this.getDbAlias(args),
'nc_shared_views',
insertData,
['id', 'view_id']
);
if (args.args.show_as === 'form') { if (args.args.show_as === 'form') {
res.url = `${req.ncSiteUrl}${this.config.dashboardPath}#/nc/form/${res.view_id}`; res.url = `${req.ncSiteUrl}${this.config.dashboardPath}#/nc/form/${res.view_id}`;
} else if (args.args.show_as === 'gallery') { } else if (args.args.show_as === 'gallery') {
@ -193,28 +211,31 @@ export default class NcMetaMgrEE extends NcMetaMgr {
} else { } else {
res.url = `${req.ncSiteUrl}${this.config.dashboardPath}#/nc/view/${res.view_id}`; res.url = `${req.ncSiteUrl}${this.config.dashboardPath}#/nc/view/${res.view_id}`;
} }
Tele.emit('evt', {evt_type: 'sharedView:generated-link'}) Tele.emit('evt', { evt_type: 'sharedView:generated-link' });
return res; return res;
} catch (e) { } catch (e) {
console.log(e) console.log(e);
} }
} }
protected async updateSharedViewLinkPassword(args: any): Promise<any> { protected async updateSharedViewLinkPassword(args: any): Promise<any> {
try { try {
await this.xcMeta.metaUpdate(
await this.xcMeta.metaUpdate(this.getProjectId(args), this.getDbAlias(args), 'nc_shared_views', { this.getProjectId(args),
this.getDbAlias(args),
'nc_shared_views',
{
password: args.args?.password password: args.args?.password
}, args.args.id); },
Tele.emit('evt', {evt_type: 'sharedView:password-updated'}) args.args.id
return {msg: 'Success'}; );
Tele.emit('evt', { evt_type: 'sharedView:password-updated' });
return { msg: 'Success' };
} catch (e) { } catch (e) {
console.log(e) console.log(e);
} }
} }
protected async xcVisibilityMetaSet(args) { protected async xcVisibilityMetaSet(args) {
try { try {
let field = ''; let field = '';
@ -245,91 +266,127 @@ export default class NcMetaMgrEE extends NcMetaMgr {
cn: d.cn, cn: d.cn,
rcn: d.rcn, rcn: d.rcn,
relation_type: d.relationType relation_type: d.relationType
}) });
} }
for (const role of Object.keys(d.disabled)) { for (const role of Object.keys(d.disabled)) {
const dataInDb = await this.xcMeta.metaGet(this.getProjectId(args), this.getDbAlias(args), 'nc_disabled_models_for_role', { const dataInDb = await this.xcMeta.metaGet(
this.getProjectId(args),
this.getDbAlias(args),
'nc_disabled_models_for_role',
{
type: args.args.type, type: args.args.type,
title: d[field], title: d[field],
role, role,
...props ...props
}); }
);
if (dataInDb) { if (dataInDb) {
if (d.disabled[role]) { if (d.disabled[role]) {
if (!dataInDb.disabled) { if (!dataInDb.disabled) {
await this.xcMeta.metaUpdate(this.getProjectId(args), this.getDbAlias(args), 'nc_disabled_models_for_role', { await this.xcMeta.metaUpdate(
this.getProjectId(args),
this.getDbAlias(args),
'nc_disabled_models_for_role',
{
disabled: d.disabled[role] disabled: d.disabled[role]
}, { },
{
type: args.args.type, type: args.args.type,
title: d[field], title: d[field],
role, ...props role,
}) ...props
}
);
} }
} else { } else {
await this.xcMeta.metaDelete(
await this.xcMeta.metaDelete(this.getProjectId(args), this.getDbAlias(args), 'nc_disabled_models_for_role', { this.getProjectId(args),
this.getDbAlias(args),
'nc_disabled_models_for_role',
{
type: args.args.type, type: args.args.type,
title: d[field], title: d[field],
role, ...props role,
}) ...props
}
);
} }
} else if (d.disabled[role]) { } else if (d.disabled[role]) {
await this.xcMeta.metaInsert(this.getProjectId(args), this.getDbAlias(args), 'nc_disabled_models_for_role', { await this.xcMeta.metaInsert(
this.getProjectId(args),
this.getDbAlias(args),
'nc_disabled_models_for_role',
{
disabled: d.disabled[role], disabled: d.disabled[role],
type: args.args.type, type: args.args.type,
title: d[field], title: d[field],
role, ...props role,
}) ...props
}
);
} }
} }
} }
} catch (e) { } catch (e) {
throw e; throw e;
} }
} }
protected async xcAuditList(args): Promise<any> { protected async xcAuditList(args): Promise<any> {
return this.xcMeta.metaPaginatedList(this.getProjectId(args), null, 'nc_audit', { return this.xcMeta.metaPaginatedList(
this.getProjectId(args),
null,
'nc_audit',
{
limit: args.args.limit, limit: args.args.limit,
offset: args.args.offset, offset: args.args.offset,
sort: { sort: {
field: 'created_at', field: 'created_at',
desc: true desc: true
} }
}); }
);
} }
protected async xcTableModelsEnable(args): Promise<any> { protected async xcTableModelsEnable(args): Promise<any> {
const dbAlias = this.getDbAlias(args); const dbAlias = this.getDbAlias(args);
await this.xcMeta.metaUpdate(args.project_id, dbAlias, 'nc_models', { await this.xcMeta.metaUpdate(
args.project_id,
dbAlias,
'nc_models',
{
enabled: true enabled: true
}, null, { },
'title': { null,
{
title: {
in: args.args in: args.args
}, },
type: { type: {
eq: 'table' eq: 'table'
} }
}); }
);
await this.xcMeta.metaUpdate(args.project_id, dbAlias, 'nc_models', { await this.xcMeta.metaUpdate(
args.project_id,
dbAlias,
'nc_models',
{
enabled: false enabled: false
}, null, { },
'title': { null,
nin: args.args, {
title: {
nin: args.args
}, },
type: { type: {
eq: 'table' eq: 'table'
} }
});
} }
);
}
// NOTE: updated // NOTE: updated
protected async xcRelationsSet(args): Promise<any> { protected async xcRelationsSet(args): Promise<any> {
@ -337,56 +394,73 @@ export default class NcMetaMgrEE extends NcMetaMgr {
const dbAlias = await this.getDbAlias(args); const dbAlias = await this.getDbAlias(args);
// filter out model names which toggled // filter out model names which toggled
const metaTableNames = [...new Set(args.args.map(rel => { const metaTableNames = [
return rel.relationType === 'hm' ? rel.rtn : rel.tn ...new Set(
}))] args.args.map(rel => {
return rel.relationType === 'hm' ? rel.rtn : rel.tn;
})
)
];
// get current meta from db // get current meta from db
// const metas = await client.knex('nc_models').select('meta', 'id', 'title').whereIn('title', metaTableNames); // const metas = await client.knex('nc_models').select('meta', 'id', 'title').whereIn('title', metaTableNames);
const metas = await this.xcMeta.metaList(args.project_id, dbAlias, 'nc_models', { const metas = await this.xcMeta.metaList(
args.project_id,
dbAlias,
'nc_models',
{
xcCondition: { xcCondition: {
'title': { title: {
in: metaTableNames in: metaTableNames
} }
} }
}); }
);
const metaMap: { const metaMap: {
[key: string]: any [key: string]: any;
} = {}; } = {};
for (const {meta, id, title} of metas) { for (const { meta, id, title } of metas) {
metaMap[title] = { metaMap[title] = {
id, id,
meta: JSON.parse(meta) meta: JSON.parse(meta)
} };
} }
// todo: handle if there is multiple relations between same tables(by comparing column names) // todo: handle if there is multiple relations between same tables(by comparing column names)
for (const rel of args.args) { for (const rel of args.args) {
if (rel.relationType === 'hm') { if (rel.relationType === 'hm') {
const relation = metaMap[rel.rtn].meta.hasMany.find(hmRel => hmRel.tn === rel.tn); const relation = metaMap[rel.rtn].meta.hasMany.find(
hmRel => hmRel.tn === rel.tn
);
relation.enabled = rel.enabled; relation.enabled = rel.enabled;
} else { } else {
const relation = metaMap[rel.tn].meta.belongsTo.find(btRel => btRel.rtn === rel.rtn); const relation = metaMap[rel.tn].meta.belongsTo.find(
btRel => btRel.rtn === rel.rtn
);
relation.enabled = rel.enabled; relation.enabled = rel.enabled;
} }
} }
try { try {
await this.xcMeta.startTransaction(); await this.xcMeta.startTransaction();
for (const {id, meta} of Object.values(metaMap)) { for (const { id, meta } of Object.values(metaMap)) {
await this.xcMeta.metaUpdate(args.project_id, dbAlias, 'nc_models', { await this.xcMeta.metaUpdate(
args.project_id,
dbAlias,
'nc_models',
{
meta: JSON.stringify(meta) meta: JSON.stringify(meta)
}, id) },
id
);
} }
await this.xcMeta.commit(); await this.xcMeta.commit();
} catch (e) { } catch (e) {
this.xcMeta.rollback(e) this.xcMeta.rollback(e);
throw e; throw e;
} }
} }
} }
@ -412,4 +486,3 @@ export default class NcMetaMgrEE extends NcMetaMgr {
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */

38
packages/nocodb/src/lib/noco/migrations/nc_004_add_view_type_column.ts

@ -0,0 +1,38 @@
import Knex from 'knex';
const up = async (knex: Knex) => {
await knex.schema.alterTable('nc_shared_views', table => {
table.string('view_type');
});
};
const down = async knex => {
await knex.schema.alterTable('nc_shared_views', table => {
table.dropColumns('view_type');
});
};
export { up, down };
/**
* @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/>.
*
*/
Loading…
Cancel
Save