From 4f0f38d953fde3bb96db67d98dfdcea9819d6b72 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Tue, 21 Nov 2023 14:16:43 +0530 Subject: [PATCH 01/17] feat: remove signup link from signup page if invite only signup enabled --- .../nc-gui/composables/useGlobal/state.ts | 1 + .../nc-gui/composables/useGlobal/types.ts | 1 + packages/nc-gui/pages/signin.vue | 2 +- packages/nocodb/src/services/utils.service.ts | 26 ++++++++++++------- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/packages/nc-gui/composables/useGlobal/state.ts b/packages/nc-gui/composables/useGlobal/state.ts index 4fab0a5c72..318dea2a43 100644 --- a/packages/nc-gui/composables/useGlobal/state.ts +++ b/packages/nc-gui/composables/useGlobal/state.ts @@ -109,6 +109,7 @@ export function useGlobalState(storageKey = 'nocodb-gui-v2'): State { automationLogLevel: 'OFF', disableEmailAuth: false, dashboardPath: '/dashboard', + inviteOnlySignup: false, }) /** reactive token payload */ diff --git a/packages/nc-gui/composables/useGlobal/types.ts b/packages/nc-gui/composables/useGlobal/types.ts index f7e640cd7c..9efbfacb45 100644 --- a/packages/nc-gui/composables/useGlobal/types.ts +++ b/packages/nc-gui/composables/useGlobal/types.ts @@ -34,6 +34,7 @@ export interface AppInfo { disableEmailAuth: boolean mainSubDomain?: string dashboardPath: string + inviteOnlySignup: boolean } export interface StoredState { diff --git a/packages/nc-gui/pages/signin.vue b/packages/nc-gui/pages/signin.vue index 31cc73eb4e..63d3b35d77 100644 --- a/packages/nc-gui/pages/signin.vue +++ b/packages/nc-gui/pages/signin.vue @@ -184,7 +184,7 @@ function navigateForgotPassword() { -
+
{{ $t('msg.info.signUp.dontHaveAccount') }} {{ $t('general.signUp') }}
diff --git a/packages/nocodb/src/services/utils.service.ts b/packages/nocodb/src/services/utils.service.ts index 9cd676aef3..3abeeb31e1 100644 --- a/packages/nocodb/src/services/utils.service.ts +++ b/packages/nocodb/src/services/utils.service.ts @@ -5,10 +5,10 @@ import { ViewTypes } from 'nocodb-sdk'; import { ConfigService } from '@nestjs/config'; import { useAgent } from 'request-filtering-agent'; import type { AppConfig } from '~/interface/config'; -import { NC_ATTACHMENT_FIELD_SIZE } from '~/constants'; +import {NC_APP_SETTINGS, NC_ATTACHMENT_FIELD_SIZE} from '~/constants'; import SqlMgrv2 from '~/db/sql-mgr/v2/SqlMgrv2'; import { NcError } from '~/helpers/catchError'; -import { Base, User } from '~/models'; +import {Base, Store, User} from '~/models'; import Noco from '~/Noco'; import NcConnectionMgrv2 from '~/utils/common/NcConnectionMgrv2'; import { MetaTable } from '~/utils/globals'; @@ -365,6 +365,13 @@ export class UtilsService { async appInfo(param: { req: { ncSiteUrl: string } }) { const baseHasAdmin = !(await User.isFirst()); + + let settings: { invite_only_signup?: boolean } = {}; + try { + settings = JSON.parse((await Store.get(NC_APP_SETTINGS))?.value); + } catch {} + + const oidcAuthEnabled = !!( process.env.NC_OIDC_ISSUER && process.env.NC_OIDC_AUTHORIZATION_URL && @@ -384,10 +391,10 @@ export class UtilsService { type: 'rest', env: process.env.NODE_ENV, googleAuthEnabled: !!( - process.env.NC_GOOGLE_CLIENT_ID && process.env.NC_GOOGLE_CLIENT_SECRET + process.env.NC_GOOGLE_CLIENT_ID && process.env.NC_GOOGLE_CLIENT_SECRET ), githubAuthEnabled: !!( - process.env.NC_GITHUB_CLIENT_ID && process.env.NC_GITHUB_CLIENT_SECRET + process.env.NC_GITHUB_CLIENT_ID && process.env.NC_GITHUB_CLIENT_SECRET ), oidcAuthEnabled, oidcProviderName, @@ -395,8 +402,8 @@ export class UtilsService { connectToExternalDB: !process.env.NC_CONNECT_TO_EXTERNAL_DB_DISABLED, version: packageVersion, defaultLimit: Math.max( - Math.min(defaultLimitConfig.limitDefault, defaultLimitConfig.limitMax), - defaultLimitConfig.limitMin, + Math.min(defaultLimitConfig.limitDefault, defaultLimitConfig.limitMax), + defaultLimitConfig.limitMin, ), defaultGroupByLimit: defaultGroupByLimitConfig, timezone: defaultConnectionConfig.timezone, @@ -413,9 +420,10 @@ export class UtilsService { disableEmailAuth: this.configService.get('auth.disableEmailAuth', { infer: true, }), - mainSubDomain: this.configService.get('mainSubDomain', { infer: true }), - dashboardPath: this.configService.get('dashboardPath', { infer: true }), - }; + mainSubDomain: this.configService.get('mainSubDomain', {infer: true}), + dashboardPath: this.configService.get('dashboardPath', {infer: true}), + inviteOnlySignup: settings.invite_only_signup, + } return result; } From 9b5c9867179b05204efbcd98fabf226c4ff9e214 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 23 Nov 2023 16:26:04 +0530 Subject: [PATCH 02/17] fix: validate select options before insert --- packages/nocodb/src/db/BaseModelSqlv2.ts | 31 ++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/packages/nocodb/src/db/BaseModelSqlv2.ts b/packages/nocodb/src/db/BaseModelSqlv2.ts index 96f0bd7f41..2adf7b7a77 100644 --- a/packages/nocodb/src/db/BaseModelSqlv2.ts +++ b/packages/nocodb/src/db/BaseModelSqlv2.ts @@ -3763,6 +3763,37 @@ class BaseModelSqlv2 { // let cols = Object.keys(this.columns); for (let i = 0; i < this.model.columns.length; ++i) { const column = this.model.columns[i]; + + // if SingleSelect or MultiSelect, then validate the options + if ( + column.uidt === UITypes.SingleSelect || + column.uidt === UITypes.MultiSelect + ) { + const options = await column + .getColOptions<{ options: SelectOption[] }>() + .then(({ options }) => options.map((opt) => opt.title)); + const columnTitle = column.title; + const columnName = column.column_name; + const columnValue = columns?.[columnTitle] ?? columns?.[columnName]; + if (columnValue) { + // if multi select, then split the values + const columnValueArr = + column.uidt === UITypes.MultiSelect + ? columnValue.split(',') + : [columnValue]; + for (let j = 0; j < columnValueArr.length; ++j) { + const val = columnValueArr[j]; + if (!options.includes(val)) { + NcError.badRequest( + `Invalid option "${val}" provided for column "${columnTitle}". Valid options are "${options.join( + ', ', + )}"`, + ); + } + } + } + } + // skip validation if `validate` is undefined or false if (!column?.meta?.validate || !column?.validate) continue; From 0cd3b727688e5b10315aed3cc0f53ecb8d310c7d Mon Sep 17 00:00:00 2001 From: Pranav C Date: Thu, 23 Nov 2023 17:24:41 +0530 Subject: [PATCH 03/17] fix: cache store value --- packages/nocodb/src/models/Store.ts | 32 ++++++++++++++++--- packages/nocodb/src/services/utils.service.ts | 21 ++++++------ packages/nocodb/src/utils/globals.ts | 1 + 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/packages/nocodb/src/models/Store.ts b/packages/nocodb/src/models/Store.ts index a12018fe23..789a87bd41 100644 --- a/packages/nocodb/src/models/Store.ts +++ b/packages/nocodb/src/models/Store.ts @@ -2,7 +2,8 @@ import type { SortType } from 'nocodb-sdk'; import { NcError } from '~/helpers/catchError'; import { extractProps } from '~/helpers/extractProps'; import Noco from '~/Noco'; -import { MetaTable } from '~/utils/globals'; +import { CacheGetType, CacheScope, MetaTable } from '~/utils/globals'; +import NocoCache from '~/cache/NocoCache'; // Store is used for storing key value pairs export default class Store { @@ -18,8 +19,30 @@ export default class Store { Object.assign(this, data); } - public static get(key: string, ncMeta = Noco.ncMeta): Promise { - return ncMeta.metaGet(null, null, MetaTable.STORE, { key }); + public static async get( + key: string, + lookInCache = false, + ncMeta = Noco.ncMeta, + ): Promise { + // get from cache if lookInCache is true + if (lookInCache) { + const storeData = + key && + (await NocoCache.get( + `${CacheScope.STORE}:${key}`, + CacheGetType.TYPE_OBJECT, + )); + if (storeData) return storeData; + } + + const storeData = await ncMeta.metaGet(null, null, MetaTable.STORE, { + key, + }); + + if (lookInCache) + await NocoCache.set(`${CacheScope.STORE}:${key}`, storeData); + + return storeData; } static async saveOrUpdate(store: Store, ncMeta = Noco.ncMeta) { @@ -35,7 +58,7 @@ export default class Store { 'tag', ]); - const existing = await Store.get(store.key, ncMeta); + const existing = await Store.get(store.key,false, ncMeta); if (existing) { await ncMeta.metaUpdate(null, null, MetaTable.STORE, insertObj, { key: store.key, @@ -43,5 +66,6 @@ export default class Store { } else { await ncMeta.metaInsert(null, null, MetaTable.STORE, insertObj); } + if (store.key) await NocoCache.del(`${CacheScope.STORE}:${store.key}`); } } diff --git a/packages/nocodb/src/services/utils.service.ts b/packages/nocodb/src/services/utils.service.ts index 3abeeb31e1..2786ca4783 100644 --- a/packages/nocodb/src/services/utils.service.ts +++ b/packages/nocodb/src/services/utils.service.ts @@ -5,10 +5,10 @@ import { ViewTypes } from 'nocodb-sdk'; import { ConfigService } from '@nestjs/config'; import { useAgent } from 'request-filtering-agent'; import type { AppConfig } from '~/interface/config'; -import {NC_APP_SETTINGS, NC_ATTACHMENT_FIELD_SIZE} from '~/constants'; +import { NC_APP_SETTINGS, NC_ATTACHMENT_FIELD_SIZE } from '~/constants'; import SqlMgrv2 from '~/db/sql-mgr/v2/SqlMgrv2'; import { NcError } from '~/helpers/catchError'; -import {Base, Store, User} from '~/models'; +import { Base, Store, User } from '~/models'; import Noco from '~/Noco'; import NcConnectionMgrv2 from '~/utils/common/NcConnectionMgrv2'; import { MetaTable } from '~/utils/globals'; @@ -368,10 +368,9 @@ export class UtilsService { let settings: { invite_only_signup?: boolean } = {}; try { - settings = JSON.parse((await Store.get(NC_APP_SETTINGS))?.value); + settings = JSON.parse((await Store.get(NC_APP_SETTINGS, true))?.value); } catch {} - const oidcAuthEnabled = !!( process.env.NC_OIDC_ISSUER && process.env.NC_OIDC_AUTHORIZATION_URL && @@ -391,10 +390,10 @@ export class UtilsService { type: 'rest', env: process.env.NODE_ENV, googleAuthEnabled: !!( - process.env.NC_GOOGLE_CLIENT_ID && process.env.NC_GOOGLE_CLIENT_SECRET + process.env.NC_GOOGLE_CLIENT_ID && process.env.NC_GOOGLE_CLIENT_SECRET ), githubAuthEnabled: !!( - process.env.NC_GITHUB_CLIENT_ID && process.env.NC_GITHUB_CLIENT_SECRET + process.env.NC_GITHUB_CLIENT_ID && process.env.NC_GITHUB_CLIENT_SECRET ), oidcAuthEnabled, oidcProviderName, @@ -402,8 +401,8 @@ export class UtilsService { connectToExternalDB: !process.env.NC_CONNECT_TO_EXTERNAL_DB_DISABLED, version: packageVersion, defaultLimit: Math.max( - Math.min(defaultLimitConfig.limitDefault, defaultLimitConfig.limitMax), - defaultLimitConfig.limitMin, + Math.min(defaultLimitConfig.limitDefault, defaultLimitConfig.limitMax), + defaultLimitConfig.limitMin, ), defaultGroupByLimit: defaultGroupByLimitConfig, timezone: defaultConnectionConfig.timezone, @@ -420,10 +419,10 @@ export class UtilsService { disableEmailAuth: this.configService.get('auth.disableEmailAuth', { infer: true, }), - mainSubDomain: this.configService.get('mainSubDomain', {infer: true}), - dashboardPath: this.configService.get('dashboardPath', {infer: true}), + mainSubDomain: this.configService.get('mainSubDomain', { infer: true }), + dashboardPath: this.configService.get('dashboardPath', { infer: true }), inviteOnlySignup: settings.invite_only_signup, - } + }; return result; } diff --git a/packages/nocodb/src/utils/globals.ts b/packages/nocodb/src/utils/globals.ts index 0a31046558..07e7096526 100644 --- a/packages/nocodb/src/utils/globals.ts +++ b/packages/nocodb/src/utils/globals.ts @@ -158,6 +158,7 @@ export enum CacheScope { SINGLE_QUERY = 'singleQuery', JOBS = 'nc_jobs', PRESIGNED_URL = 'presignedUrl', + STORE = 'presignedUrl', } export enum CacheGetType { From 33b409cbf261ef79ea4063e3aa3dfa975f501629 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Fri, 24 Nov 2023 14:34:51 +0530 Subject: [PATCH 04/17] fix: add pre-insert option validation for bulk insert --- packages/nocodb/src/db/BaseModelSqlv2.ts | 76 ++++++++++++++---------- 1 file changed, 46 insertions(+), 30 deletions(-) diff --git a/packages/nocodb/src/db/BaseModelSqlv2.ts b/packages/nocodb/src/db/BaseModelSqlv2.ts index 2adf7b7a77..0e951b3ee5 100644 --- a/packages/nocodb/src/db/BaseModelSqlv2.ts +++ b/packages/nocodb/src/db/BaseModelSqlv2.ts @@ -2977,6 +2977,8 @@ class BaseModelSqlv2 { } } + await this.validateOptions(col, insertObj); + // validate data if (col?.meta?.validate && col?.validate) { const validate = col.getValidators(); @@ -3763,36 +3765,7 @@ class BaseModelSqlv2 { // let cols = Object.keys(this.columns); for (let i = 0; i < this.model.columns.length; ++i) { const column = this.model.columns[i]; - - // if SingleSelect or MultiSelect, then validate the options - if ( - column.uidt === UITypes.SingleSelect || - column.uidt === UITypes.MultiSelect - ) { - const options = await column - .getColOptions<{ options: SelectOption[] }>() - .then(({ options }) => options.map((opt) => opt.title)); - const columnTitle = column.title; - const columnName = column.column_name; - const columnValue = columns?.[columnTitle] ?? columns?.[columnName]; - if (columnValue) { - // if multi select, then split the values - const columnValueArr = - column.uidt === UITypes.MultiSelect - ? columnValue.split(',') - : [columnValue]; - for (let j = 0; j < columnValueArr.length; ++j) { - const val = columnValueArr[j]; - if (!options.includes(val)) { - NcError.badRequest( - `Invalid option "${val}" provided for column "${columnTitle}". Valid options are "${options.join( - ', ', - )}"`, - ); - } - } - } - } + await this.validateOptions(column, columns); // skip validation if `validate` is undefined or false if (!column?.meta?.validate || !column?.validate) continue; @@ -3828,6 +3801,49 @@ class BaseModelSqlv2 { return true; } + // method for validating otpions if column is single/multi select + private async validateOptions( + column: Column, + insertOrUpdateObject: Record, + ) { + // if SingleSelect or MultiSelect, then validate the options + if ( + !( + column.uidt === UITypes.SingleSelect || + column.uidt === UITypes.MultiSelect + ) + ) { + return; + } + + const options = await column + .getColOptions<{ options: SelectOption[] }>() + .then(({ options }) => options.map((opt) => opt.title)); + const columnTitle = column.title; + const columnName = column.column_name; + const columnValue = + insertOrUpdateObject?.[columnTitle] ?? insertOrUpdateObject?.[columnName]; + if (!columnValue) { + return; + } + + // if multi select, then split the values + const columnValueArr = + column.uidt === UITypes.MultiSelect + ? columnValue.split(',') + : [columnValue]; + for (let j = 0; j < columnValueArr.length; ++j) { + const val = columnValueArr[j]; + if (!options.includes(val)) { + NcError.badRequest( + `Invalid option "${val}" provided for column "${columnTitle}". Valid options are "${options.join( + ', ', + )}"`, + ); + } + } + } + async addChild({ colId, rowId, From 41d6ebdc6bc4da73b3e4ba37bbf06abfb93720e1 Mon Sep 17 00:00:00 2001 From: mertmit Date: Fri, 24 Nov 2023 10:05:38 +0000 Subject: [PATCH 05/17] fix: use getWithRoles for auth token strategy --- .../authtoken.strategy/authtoken.strategy.ts | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/packages/nocodb/src/strategies/authtoken.strategy/authtoken.strategy.ts b/packages/nocodb/src/strategies/authtoken.strategy/authtoken.strategy.ts index 26327d8887..f666ac1b10 100644 --- a/packages/nocodb/src/strategies/authtoken.strategy/authtoken.strategy.ts +++ b/packages/nocodb/src/strategies/authtoken.strategy/authtoken.strategy.ts @@ -3,7 +3,7 @@ import { PassportStrategy } from '@nestjs/passport'; import { extractRolesObj, ProjectRoles } from 'nocodb-sdk'; import { Strategy } from 'passport-custom'; import type { Request } from 'express'; -import { ApiToken, BaseUser, User } from '~/models'; +import { ApiToken, User } from '~/models'; import { sanitiseUserObj } from '~/utils'; @Injectable() @@ -22,12 +22,21 @@ export class AuthTokenStrategy extends PassportStrategy(Strategy, 'authtoken') { is_api_token: true, }; + // old auth tokens will not have fk_user_id, so we return editor role if (!apiToken.fk_user_id) { user.base_roles = extractRolesObj(ProjectRoles.EDITOR); return callback(null, user); } - const dbUser: Record = await User.get(apiToken.fk_user_id); + const dbUser: Record = await User.getWithRoles( + apiToken.fk_user_id, + { + baseId: req['ncBaseId'], + ...(req['ncWorkspaceId'] + ? { workspaceId: req['ncWorkspaceId'] } + : {}), + }, + ); if (!dbUser) { return callback({ msg: 'User not found' }); } @@ -35,16 +44,11 @@ export class AuthTokenStrategy extends PassportStrategy(Strategy, 'authtoken') { Object.assign(user, { id: dbUser.id, roles: extractRolesObj(dbUser.roles), + base_roles: extractRolesObj(dbUser.base_roles), + ...(dbUser.workspace_roles + ? { workspace_roles: extractRolesObj(dbUser.workspace_roles) } + : {}), }); - - if (req['ncProjectId']) { - const baseUser = await BaseUser.get(req['ncProjectId'], dbUser.id); - user.base_roles = extractRolesObj(baseUser?.roles); - if (user.base_roles.owner) { - user.base_roles.creator = true; - } - return callback(null, sanitiseUserObj(user)); - } } return callback(null, sanitiseUserObj(user)); } catch (error) { From 2220b493e2d5a6afe32af7c68a9cff63024a3329 Mon Sep 17 00:00:00 2001 From: Raju Udava <86527202+dstala@users.noreply.github.com> Date: Fri, 24 Nov 2023 19:52:22 +0530 Subject: [PATCH 06/17] test: remove trailing space when defining options Signed-off-by: Raju Udava <86527202+dstala@users.noreply.github.com> --- packages/nocodb/tests/unit/rest/tests/filter.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nocodb/tests/unit/rest/tests/filter.test.ts b/packages/nocodb/tests/unit/rest/tests/filter.test.ts index e9144aee4f..318aab464a 100644 --- a/packages/nocodb/tests/unit/rest/tests/filter.test.ts +++ b/packages/nocodb/tests/unit/rest/tests/filter.test.ts @@ -519,13 +519,13 @@ function filterSelectBased() { column_name: 'SingleSelect', title: 'SingleSelect', uidt: UITypes.SingleSelect, - dtxp: "'jan','feb','mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'", + dtxp: "'jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec'", }, { column_name: 'MultiSelect', title: 'MultiSelect', uidt: UITypes.MultiSelect, - dtxp: "'jan','feb','mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'", + dtxp: "'jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec'", }, ], }); From 22339915cc67a39faee89232e1e3ea8d4f34f005 Mon Sep 17 00:00:00 2001 From: Raju Udava <86527202+dstala@users.noreply.github.com> Date: Fri, 24 Nov 2023 21:11:03 +0530 Subject: [PATCH 07/17] test: remove trailing space during select field creation Signed-off-by: Raju Udava <86527202+dstala@users.noreply.github.com> --- packages/nocodb/tests/unit/factory/column.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nocodb/tests/unit/factory/column.ts b/packages/nocodb/tests/unit/factory/column.ts index a65fa3fc69..21740b1bc9 100644 --- a/packages/nocodb/tests/unit/factory/column.ts +++ b/packages/nocodb/tests/unit/factory/column.ts @@ -148,13 +148,13 @@ const customColumns = function (type: string, options: any = {}) { column_name: 'SingleSelect', title: 'SingleSelect', uidt: UITypes.SingleSelect, - dtxp: "'jan','feb','mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'", + dtxp: "'jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec'", }, { column_name: 'MultiSelect', title: 'MultiSelect', uidt: UITypes.MultiSelect, - dtxp: "'jan','feb','mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'", + dtxp: "'jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec'", }, ]; case 'custom': From b0d0a6fbea5163369269e1f50263d40464d75ea9 Mon Sep 17 00:00:00 2001 From: Pranav C Date: Fri, 24 Nov 2023 22:47:26 +0530 Subject: [PATCH 08/17] fix: skip validation and hooks when updating column options --- packages/nocodb/src/db/BaseModelSqlv2.ts | 14 ++++++++++---- packages/nocodb/src/services/columns.service.ts | 5 ++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/nocodb/src/db/BaseModelSqlv2.ts b/packages/nocodb/src/db/BaseModelSqlv2.ts index 0e951b3ee5..8b9cd35335 100644 --- a/packages/nocodb/src/db/BaseModelSqlv2.ts +++ b/packages/nocodb/src/db/BaseModelSqlv2.ts @@ -3210,7 +3210,12 @@ class BaseModelSqlv2 { } async bulkUpdateAll( - args: { where?: string; filterArr?: Filter[]; viewId?: string } = {}, + args: { + where?: string; + filterArr?: Filter[]; + viewId?: string; + skipValidationAndHooks?: boolean; + } = {}, data, { cookie }: { cookie?: any } = {}, ) { @@ -3221,7 +3226,7 @@ class BaseModelSqlv2 { this.clientMeta, this.dbDriver, ); - await this.validate(updateData); + if (!args.skipValidationAndHooks) await this.validate(updateData); const pkValues = await this._extractPksValues(updateData); if (pkValues) { // pk is specified - by pass @@ -3255,7 +3260,7 @@ class BaseModelSqlv2 { ); } - await conditionV2(this, conditionObj, qb); + await conditionV2(this, conditionObj, qb, undefined, true); count = ( await this.execAndParse( @@ -3273,7 +3278,8 @@ class BaseModelSqlv2 { await this.execAndParse(qb, null, { raw: true }); } - await this.afterBulkUpdate(null, count, this.dbDriver, cookie, true); + if (!args.skipValidationAndHooks) + await this.afterBulkUpdate(null, count, this.dbDriver, cookie, true); return count; } catch (e) { diff --git a/packages/nocodb/src/services/columns.service.ts b/packages/nocodb/src/services/columns.service.ts index 569433d55a..732c7f6657 100644 --- a/packages/nocodb/src/services/columns.service.ts +++ b/packages/nocodb/src/services/columns.service.ts @@ -546,7 +546,10 @@ export class ColumnsService { ]); } else { await baseModel.bulkUpdateAll( - { where: `(${column.title},eq,${option.title})` }, + { + where: `(${column.title},eq,${option.title})`, + skipValidationAndHooks: true, + }, { [column.column_name]: null }, { cookie }, ); From 0fa44775174eb2df90f98e2c361382e7da5a09ca Mon Sep 17 00:00:00 2001 From: Sergio Josue Maldonado <33098182+Scalcaneo@users.noreply.github.com> Date: Sat, 25 Nov 2023 00:52:34 +0000 Subject: [PATCH 09/17] Subtle update to spanish.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - At the end of "Por qué estamos construyendo esto?" and "Nuestra misión" extra quotation marks, removed. - There is a miss-translation between spanish billions and english billions, in spanish "billions" refers to: 1,000,000,000,000, while in english billions is 1,000,000,000, if we want to refer to the english quantity the correct term is "miles de millones" (thousands of millions). --- markdown/readme/languages/spanish.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/markdown/readme/languages/spanish.md b/markdown/readme/languages/spanish.md index 12692193c2..935bc07e08 100644 --- a/markdown/readme/languages/spanish.md +++ b/markdown/readme/languages/spanish.md @@ -194,8 +194,8 @@ Por favor diríjase a [Contribution Guide](https://github.com/nocodb/nocodb/blob # Por qué estamos construyendo esto? -La mayoría de las empresas de Internet emplean una hoja de cálculo o una base de datos para resolver sus necesidades comerciales. Las hojas de cálculo son utilizadas por billones de personas o más de manera colaborativa todos los días. Sin embargo, estamos lejos de trabajar a velocidades similares en bases de datos, ya que son herramientas computacionalmente más poderosas. Los intentos de resolver esto con soluciones SaaS han significado horribles controles de acceso, dependencia de un proveedor, dependencia de datos, cambios abruptos de precios y lo que es más importante, un techo de cristal sobre lo que es posible en el futuro." +La mayoría de las empresas de Internet emplean una hoja de cálculo o una base de datos para resolver sus necesidades comerciales. Las hojas de cálculo son utilizadas por billones de personas o más de manera colaborativa todos los días. Sin embargo, estamos lejos de trabajar a velocidades similares en bases de datos, ya que son herramientas computacionalmente más poderosas. Los intentos de resolver esto con soluciones SaaS han significado horribles controles de acceso, dependencia de un proveedor, dependencia de datos, cambios abruptos de precios y lo que es más importante, un techo de cristal sobre lo que es posible en el futuro. # Nuestra misión -Nuestra misión es proporcionar la interfaz sin-código más potente para bases de datos, la cual es open-source para negocios de Internet en el mundo. Esto no solo democratizaría el acceso a una poderosa herramienta de computación, sino que también producirá a billones de personas o más con habilidades radicales de perfección y construcción en Internet." +Nuestra misión es proporcionar la interfaz sin-código más potente para bases de datos, la cual es open-source para negocios de Internet en el mundo. Esto no solo democratizaría el acceso a una poderosa herramienta de computación, sino que también producirá a miles de millones de personas o más con habilidades radicales de perfección y construcción en Internet. From 2e00a0fa2a2f20a5c3187d509fd7be1ae9bcc010 Mon Sep 17 00:00:00 2001 From: Muhammed Mustafa Date: Sat, 25 Nov 2023 04:19:43 +0000 Subject: [PATCH 10/17] fix: Hide export btn on toolbar when on viewer permision --- .../nc-gui/components/smartsheet/Toolbar.vue | 2 +- .../smartsheet/toolbar/ViewActionMenu.vue | 70 ++++++++++--------- 2 files changed, 38 insertions(+), 34 deletions(-) diff --git a/packages/nc-gui/components/smartsheet/Toolbar.vue b/packages/nc-gui/components/smartsheet/Toolbar.vue index 7587db85f8..fd1347b27e 100644 --- a/packages/nc-gui/components/smartsheet/Toolbar.vue +++ b/packages/nc-gui/components/smartsheet/Toolbar.vue @@ -39,7 +39,7 @@ const { allowCSVDownload } = useSharedView() - +
diff --git a/packages/nc-gui/components/smartsheet/toolbar/ViewActionMenu.vue b/packages/nc-gui/components/smartsheet/toolbar/ViewActionMenu.vue index 7616aaf08e..82795110c7 100644 --- a/packages/nc-gui/components/smartsheet/toolbar/ViewActionMenu.vue +++ b/packages/nc-gui/components/smartsheet/toolbar/ViewActionMenu.vue @@ -239,44 +239,48 @@ const onDelete = async () => { - - - - - - -