diff --git a/packages/nc-gui/components/project/spreadsheet/components/SharedViewsList.vue b/packages/nc-gui/components/project/spreadsheet/components/SharedViewsList.vue
index 8e5bf21dc1..5cc237f718 100644
--- a/packages/nc-gui/components/project/spreadsheet/components/SharedViewsList.vue
+++ b/packages/nc-gui/components/project/spreadsheet/components/SharedViewsList.vue
@@ -18,6 +18,10 @@
{{ $t('labels.password') }}
+
+
+ Download Allowed
+ |
{{ $t('labels.actions') }}
@@ -46,6 +50,11 @@
+ |
+
+ {{ renderAllowCSVDownload(currentView) }}
+
+ |
mdi-content-copy
mdi-delete-outline
@@ -80,6 +89,11 @@
|
+
+
+ {{ renderAllowCSVDownload(link) }}
+
+ |
mdi-content-copy
mdi-delete-outline
@@ -173,6 +187,14 @@ export default {
}
return `/nc/${viewType}/${view.uuid}`;
},
+ renderAllowCSVDownload(view) {
+ if (view.type === ViewTypes.GRID) {
+ view.meta = view.meta && typeof view.meta === 'string' ? JSON.parse(view.meta) : view.meta;
+ return view.meta.allowCSVDownload ? '✔️' : '❌';
+ } else {
+ return 'N/A';
+ }
+ },
},
};
diff --git a/packages/nc-gui/components/project/spreadsheet/components/SpreadsheetNavDrawer.vue b/packages/nc-gui/components/project/spreadsheet/components/SpreadsheetNavDrawer.vue
index 0386c6cc18..7074c77e29 100644
--- a/packages/nc-gui/components/project/spreadsheet/components/SpreadsheetNavDrawer.vue
+++ b/packages/nc-gui/components/project/spreadsheet/components/SpreadsheetNavDrawer.vue
@@ -316,44 +316,63 @@
mdi-content-copy
-
-
-
-
- {{ $t('msg.info.beforeEnablePwd') }}
-
-
-
- {{ $t('msg.info.afterEnablePwd') }}
-
-
-
-
-
-
-
-
- {{ showShareLinkPassword ? 'visibility_off' : 'visibility' }}
-
-
-
-
-
- {{ $t('placeholder.password.save') }}
-
-
+
+
+
+
+ More Options
+
+ mdi-chevron-{{ advanceOptionsPanel === 0 ? 'up' : 'down' }}
+
+
+
+
+
+
+
+
+ {{ showShareLinkPassword ? 'visibility_off' : 'visibility' }}
+
+
+
+
+
+ {{ $t('placeholder.password.save') }}
+
+
+
+
+
+
@@ -410,6 +429,7 @@ export default {
queryParams: Object,
},
data: () => ({
+ advanceOptionsPanel: false,
webhookSliderModal: false,
codeSnippetModal: false,
drag: false,
@@ -425,6 +445,7 @@ export default {
searchQueryVal: '',
showShareLinkPassword: false,
passwordProtect: false,
+ allowCSVDownload: true,
sharedViewPassword: '',
overAdvShieldIcon: false,
overShieldIcon: false,
@@ -611,6 +632,9 @@ export default {
this.saveShareLinkPassword();
}
},
+ onAllowCSVDownloadChange() {
+ this.saveAllowCSVDownload();
+ },
async saveShareLinkPassword() {
try {
await this.$api.dbViewShare.update(this.shareLink.id, {
@@ -632,6 +656,27 @@ export default {
this.$e('a:view:share:enable-pwd');
},
+ async saveAllowCSVDownload() {
+ try {
+ const meta =
+ this.shareLink.meta && typeof this.shareLink.meta === 'string'
+ ? JSON.parse(this.shareLink.meta)
+ : this.shareLink.meta;
+
+ meta.allowCSVDownload = this.allowCSVDownload;
+ await this.$api.dbViewShare.update(this.shareLink.id, {
+ meta,
+ });
+ this.$toast.success('Successfully updated').goAway(3000);
+ } catch (e) {
+ this.$toast.error(await this._extractSdkResponseErrorMsg(e)).goAway(3000);
+ }
+ if (this.allowCSVDownload) {
+ this.$e('a:view:share:enable-csv-download');
+ } else {
+ this.$e('a:view:share:disable-csv-download');
+ }
+ },
async loadViews() {
// this.viewsList = await this.sqlOp(
// {
@@ -725,34 +770,12 @@ export default {
this.$e('a:view:delete', { view: view.type });
},
async genShareLink() {
- // const sharedViewUrl = await this.$store.dispatch('sqlMgr/ActSqlOp', [
- // { dbAlias: this.nodes.dbAlias },
- // 'createSharedViewLink',
- // {
- // model_name: this.table,
- // // meta: this.meta,
- // query_params: {
- // where: this.concatenatedXWhere,
- // sort: this.sort,
- // fields: Object.keys(this.showFields)
- // .filter(f => this.showFields[f])
- // .join(','),
- // showFields: this.showFields,
- // fieldsOrder: this.fieldsOrder,
- // extraViewParams: this.extraViewParams,
- // selectedViewId: this.selectedViewId,
- // columnsWidth: this.columnsWidth
- // },
- // view_name: this.selectedView.title,
- // type: this.selectedView.type,
- // show_as: this.selectedView.show_as,
- // password: this.sharedViewPassword
- // }
- // ])
const shared = await this.$api.dbViewShare.create(this.selectedViewId);
-
+ shared.meta = shared.meta && typeof shared.meta === 'string' ? JSON.parse(shared.meta) : shared.meta;
// todo: url
this.shareLink = shared;
+ this.passwordProtect = shared.password !== null;
+ this.allowCSVDownload = shared.meta.allowCSVDownload;
this.showShareModel = true;
},
copyView(view, i) {
@@ -903,4 +926,7 @@ export default {
opacity: 0.5;
background: grey;
}
+.mx-auto .v-expansion-panel {
+ background: var(--v-backgroundColor-base);
+}
diff --git a/packages/nc-gui/components/project/spreadsheet/public/XcTable.vue b/packages/nc-gui/components/project/spreadsheet/public/XcTable.vue
index 2af4fad519..23cff36657 100644
--- a/packages/nc-gui/components/project/spreadsheet/public/XcTable.vue
+++ b/packages/nc-gui/components/project/spreadsheet/public/XcTable.vue
@@ -46,7 +46,8 @@
@input="loadTableData"
/>
- , res) {
- Tele.emit('evt', { evt_type: 'sharedView:password-updated' });
- res.json(await View.passwordUpdate(req.params.viewId, req.body));
+async function shareViewUpdate(req: Request, res) {
+ Tele.emit('evt', { evt_type: 'sharedView:updated' });
+ res.json(await View.update(req.params.viewId, req.body));
}
async function shareViewDelete(req: Request, res) {
@@ -140,7 +140,7 @@ router.post(
router.patch(
'/api/v1/db/meta/views/:viewId/share',
metaApiMetrics,
- ncMetaAclMw(shareViewPasswordUpdate, 'shareViewPasswordUpdate')
+ ncMetaAclMw(shareViewUpdate, 'shareViewUpdate')
);
router.delete(
'/api/v1/db/meta/views/:viewId/share',
diff --git a/packages/nocodb/src/lib/migrations/XcMigrationSourcev2.ts b/packages/nocodb/src/lib/migrations/XcMigrationSourcev2.ts
index eb5b9a9c2c..0bd7404e8d 100644
--- a/packages/nocodb/src/lib/migrations/XcMigrationSourcev2.ts
+++ b/packages/nocodb/src/lib/migrations/XcMigrationSourcev2.ts
@@ -5,6 +5,7 @@ import * as nc_014_alter_column_data_types from './v2/nc_014_alter_column_data_t
import * as nc_015_add_meta_col_in_column_table from './v2/nc_015_add_meta_col_in_column_table';
import * as nc_016_alter_hooklog_payload_types from './v2/nc_016_alter_hooklog_payload_types';
import * as nc_017_add_user_token_version_column from './v2/nc_017_add_user_token_version_column';
+import * as nc_018_add_meta_in_view from './v2/nc_018_add_meta_in_view';
// Create a custom migration source class
export default class XcMigrationSourcev2 {
@@ -21,6 +22,7 @@ export default class XcMigrationSourcev2 {
'nc_015_add_meta_col_in_column_table',
'nc_016_alter_hooklog_payload_types',
'nc_017_add_user_token_version_column',
+ 'nc_018_add_meta_in_view',
]);
}
@@ -44,6 +46,8 @@ export default class XcMigrationSourcev2 {
return nc_016_alter_hooklog_payload_types;
case 'nc_017_add_user_token_version_column':
return nc_017_add_user_token_version_column;
+ case 'nc_018_add_meta_in_view':
+ return nc_018_add_meta_in_view;
}
}
}
diff --git a/packages/nocodb/src/lib/migrations/v2/nc_018_add_meta_in_view.ts b/packages/nocodb/src/lib/migrations/v2/nc_018_add_meta_in_view.ts
new file mode 100644
index 0000000000..80ec206059
--- /dev/null
+++ b/packages/nocodb/src/lib/migrations/v2/nc_018_add_meta_in_view.ts
@@ -0,0 +1,38 @@
+import Knex from 'knex';
+import { MetaTable } from '../../utils/globals';
+
+const up = async (knex: Knex) => {
+ await knex.schema.alterTable(MetaTable.VIEWS, (table) => {
+ table.text('meta');
+ });
+};
+
+const down = async (knex) => {
+ await knex.schema.alterTable(MetaTable.VIEWS, (table) => {
+ table.dropColumns('meta');
+ });
+};
+
+export { up, down };
+
+/**
+ * @copyright Copyright (c) 2021, Xgene Cloud Ltd
+ *
+ * @author Wing-Kam Wong
+ *
+ * @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 .
+ *
+ */
diff --git a/packages/nocodb/src/lib/models/View.ts b/packages/nocodb/src/lib/models/View.ts
index f5eb88d7f9..a3dc3b657e 100644
--- a/packages/nocodb/src/lib/models/View.ts
+++ b/packages/nocodb/src/lib/models/View.ts
@@ -42,6 +42,7 @@ export default class View implements ViewType {
project_id?: string;
base_id?: string;
show_system_fields?: boolean;
+ meta?: any;
constructor(data: View) {
Object.assign(this, data);
@@ -614,7 +615,31 @@ export default class View implements ViewType {
viewId
);
}
-
+ if (!view.meta) {
+ const defaultMeta = {
+ allowCSVDownload: true,
+ };
+ // get existing cache
+ const key = `${CacheScope.VIEW}:${view.id}`;
+ const o = await NocoCache.get(key, CacheGetType.TYPE_OBJECT);
+ if (o) {
+ // update data
+ o.meta = JSON.stringify(defaultMeta);
+ // set cache
+ await NocoCache.set(key, o);
+ }
+ // set meta
+ await ncMeta.metaUpdate(
+ null,
+ null,
+ MetaTable.VIEWS,
+ {
+ meta: JSON.stringify(defaultMeta),
+ },
+ viewId
+ );
+ view.meta = defaultMeta;
+ }
return view;
}
@@ -675,6 +700,7 @@ export default class View implements ViewType {
lock_type?: string;
password?: string;
uuid?: string;
+ meta?: any;
},
ncMeta = Noco.ncMeta
) {
@@ -684,14 +710,19 @@ export default class View implements ViewType {
'show_system_fields',
'lock_type',
'password',
+ 'meta',
'uuid',
]);
+ updateObj.meta = JSON.stringify(updateObj.meta);
// get existing cache
const key = `${CacheScope.VIEW}:${viewId}`;
let o = await NocoCache.get(key, CacheGetType.TYPE_OBJECT);
if (o) {
// update data
- o = { ...o, ...updateObj };
+ o = {
+ ...o,
+ ...updateObj,
+ };
if (o.is_default) {
await NocoCache.set(`${CacheScope.VIEW}:${o.fk_model_id}:default`, o);
}
diff --git a/scripts/cypress/integration/common/4b_table_view_share.js b/scripts/cypress/integration/common/4b_table_view_share.js
index e8c77af5b1..4a283973e9 100644
--- a/scripts/cypress/integration/common/4b_table_view_share.js
+++ b/scripts/cypress/integration/common/4b_table_view_share.js
@@ -5,26 +5,24 @@ let storedURL = "";
let linkText = "";
const generateLinkWithPwd = () => {
- // cy.get(".v-navigation-drawer__content > .container")
- // .find(".v-list > .v-list-item")
- // .contains("Share View")
- // .click();
- mainPage.shareView().click();
+ mainPage.shareView().click({ force: true });
+
+ cy.wait(3000);
cy.snipActiveModal("Modal_ShareView");
// enable checkbox & feed pwd, save
- cy.getActiveModal()
- .find('[role="switch"][type="checkbox"]')
- .click({ force: true });
- cy.getActiveModal().find('input[type="password"]').type("1");
-
- cy.snipActiveModal("Modal_ShareView_Password");
-
- cy.getActiveModal().find('button:contains("Save password")').click();
-
- cy.toastWait("Successfully updated");
-
+ cy.getActiveModal().find('button:contains("More Options")').click({ force: true });
+ cy.getActiveModal().find('[role="checkbox"][type="checkbox"]').first().then(($el) => {
+ if (!$el.prop("checked")) {
+ cy.wrap($el).click({ force: true });
+ cy.getActiveModal().find('input[type="password"]').type("1");
+ cy.snipActiveModal("Modal_ShareView_Password");
+ cy.getActiveModal().find('button:contains("Save password")').click();
+ cy.toastWait("Successfully updated");
+ }
+ });
+
// copy link text, visit URL
cy.getActiveModal()
.find(".share-link-box")
@@ -93,6 +91,11 @@ export const genTest = (apiType, dbType) => {
cy.get("body")
.find(".v-dialog.v-dialog--active")
.should("not.exist");
+
+ // Verify Download as CSV is here
+ cy.get(".nc-actions-menu-btn").click();
+ cy.snipActiveMenu("Menu_ActionsMenu");
+ cy.get(`.menuable__content__active .v-list-item span:contains("Download as CSV")`).should("exist");
});
it("Delete view", () => {
diff --git a/scripts/cypress/integration/common/4e_form_view_share.js b/scripts/cypress/integration/common/4e_form_view_share.js
index c661775c3b..dfc5a67202 100644
--- a/scripts/cypress/integration/common/4e_form_view_share.js
+++ b/scripts/cypress/integration/common/4e_form_view_share.js
@@ -97,7 +97,9 @@ export const genTest = (apiType, dbType) => {
// .find(".v-list > .v-list-item")
// .contains("Share View")
// .click();
- mainPage.shareView().click();
+ mainPage.shareView().click({ force: true });
+
+ cy.wait(5000);
cy.snipActiveModal("Modal_ShareView");
diff --git a/scripts/cypress/integration/common/4f_grid_view_share.js b/scripts/cypress/integration/common/4f_grid_view_share.js
index c625951b58..6d06ebc773 100644
--- a/scripts/cypress/integration/common/4f_grid_view_share.js
+++ b/scripts/cypress/integration/common/4f_grid_view_share.js
@@ -20,7 +20,9 @@ export const genTest = (apiType, dbType) => {
// .find(".v-list > .v-list-item")
// .contains("Share View")
// .click();
- mainPage.shareView().click();
+ mainPage.shareView().click({ force: true });
+
+ cy.wait(5000);
// wait, as URL initially will be /undefined
cy.getActiveModal()
diff --git a/scripts/cypress/integration/common/4f_pg_grid_view_share.js b/scripts/cypress/integration/common/4f_pg_grid_view_share.js
index e049477f05..7e74114db1 100644
--- a/scripts/cypress/integration/common/4f_pg_grid_view_share.js
+++ b/scripts/cypress/integration/common/4f_pg_grid_view_share.js
@@ -20,8 +20,10 @@ export const genTest = (apiType, dbType) => {
// .find(".v-list > .v-list-item")
// .contains("Share View")
// .click();
- mainPage.shareView().click();
-
+ mainPage.shareView().click({ force: true });
+
+ cy.wait(5000);
+
// wait, as URL initially will be /undefined
cy.getActiveModal()
.find(".share-link-box")
diff --git a/scripts/cypress/integration/common/6f_attachments.js b/scripts/cypress/integration/common/6f_attachments.js
index b7aab2dc0d..9f692524a2 100644
--- a/scripts/cypress/integration/common/6f_attachments.js
+++ b/scripts/cypress/integration/common/6f_attachments.js
@@ -66,7 +66,9 @@ export const genTest = (apiType, dbType) => {
// .find(".v-list > .v-list-item")
// .contains("Share View")
// .click();
- mainPage.shareView().click();
+ mainPage.shareView().click({ force: true });
+
+ cy.wait(5000);
// copy link text, visit URL
cy.getActiveModal()
diff --git a/scripts/cypress/support/page_objects/mainPage.js b/scripts/cypress/support/page_objects/mainPage.js
index 307452ed61..b8d321561b 100644
--- a/scripts/cypress/support/page_objects/mainPage.js
+++ b/scripts/cypress/support/page_objects/mainPage.js
@@ -245,6 +245,7 @@ export class _mainPage {
};
shareView = () => {
+ cy.wait(3000);
return cy.get(".nc-btn-share-view");
};
|