From 6af9246f26c128ca551897b9b80a6a647442765f Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Thu, 24 Mar 2022 16:02:50 -0700 Subject: [PATCH] feat: Allow disabling collection creation for members (#3270) --- .../Sidebar/components/SidebarAction.tsx | 4 ++++ app/models/Team.ts | 4 ++++ app/scenes/Settings/Security.tsx | 14 ++++++++++++++ server/commands/teamUpdater.ts | 4 ++++ ...20220319060408-collection-create-permission.js | 15 +++++++++++++++ server/models/Team.ts | 4 ++++ server/policies/collection.ts | 5 ++++- server/presenters/team.ts | 1 + server/routes/api/team.ts | 2 ++ shared/i18n/locales/en_US/translation.json | 2 ++ 10 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 server/migrations/20220319060408-collection-create-permission.js diff --git a/app/components/Sidebar/components/SidebarAction.tsx b/app/components/Sidebar/components/SidebarAction.tsx index 82ecd7c56..334d42e44 100644 --- a/app/components/Sidebar/components/SidebarAction.tsx +++ b/app/components/Sidebar/components/SidebarAction.tsx @@ -21,6 +21,10 @@ function SidebarAction({ action, ...rest }: Props) { const menuItem = actionToMenuItem(action, context); invariant(menuItem.type === "button", "passed action must be a button"); + if (!menuItem.visible) { + return null; + } + return ( + + + { sharing, guestSignin, documentEmbeds, + memberCollectionCreate, collaborativeEditing, defaultCollectionId, defaultUserRole, @@ -41,6 +42,9 @@ const teamUpdater = async ({ params, user, team, ip }: TeamUpdaterProps) => { if (avatarUrl !== undefined) { team.avatarUrl = avatarUrl; } + if (memberCollectionCreate !== undefined) { + team.memberCollectionCreate = memberCollectionCreate; + } if (defaultCollectionId !== undefined) { team.defaultCollectionId = defaultCollectionId; } diff --git a/server/migrations/20220319060408-collection-create-permission.js b/server/migrations/20220319060408-collection-create-permission.js new file mode 100644 index 000000000..797e477c1 --- /dev/null +++ b/server/migrations/20220319060408-collection-create-permission.js @@ -0,0 +1,15 @@ +'use strict'; + +module.exports = { + up: async (queryInterface, Sequelize) => { + await queryInterface.addColumn("teams", "memberCollectionCreate", { + type: Sequelize.BOOLEAN, + defaultValue: true, + allowNull: false, + }); + }, + + down: async (queryInterface, Sequelize) => { + await queryInterface.removeColumn("teams", "memberCollectionCreate"); + } +}; diff --git a/server/models/Team.ts b/server/models/Team.ts index 8ffead4e6..962e682b6 100644 --- a/server/models/Team.ts +++ b/server/models/Team.ts @@ -89,6 +89,10 @@ class Team extends ParanoidModel { @Column documentEmbeds: boolean; + @Default(true) + @Column + memberCollectionCreate: boolean; + @Default(false) @Column collaborativeEditing: boolean; diff --git a/server/policies/collection.ts b/server/policies/collection.ts index fe3695ffd..7d96c56e6 100644 --- a/server/policies/collection.ts +++ b/server/policies/collection.ts @@ -8,7 +8,10 @@ allow(User, "createCollection", Team, (user, team) => { if (!team || user.isViewer || user.teamId !== team.id) { return false; } - return true; + if (user.isAdmin || team.memberCollectionCreate) { + return true; + } + return false; }); allow(User, "importCollection", Team, (actor, team) => { diff --git a/server/presenters/team.ts b/server/presenters/team.ts index 7025a7dfe..60fab2cf4 100644 --- a/server/presenters/team.ts +++ b/server/presenters/team.ts @@ -6,6 +6,7 @@ export default function present(team: Team) { name: team.name, avatarUrl: team.logoUrl, sharing: team.sharing, + memberCollectionCreate: team.memberCollectionCreate, collaborativeEditing: team.collaborativeEditing, defaultCollectionId: team.defaultCollectionId, documentEmbeds: team.documentEmbeds, diff --git a/server/routes/api/team.ts b/server/routes/api/team.ts index 83cbd79f7..0890ad384 100644 --- a/server/routes/api/team.ts +++ b/server/routes/api/team.ts @@ -16,6 +16,7 @@ router.post("team.update", auth(), async (ctx) => { sharing, guestSignin, documentEmbeds, + memberCollectionCreate, collaborativeEditing, defaultCollectionId, defaultUserRole, @@ -37,6 +38,7 @@ router.post("team.update", auth(), async (ctx) => { sharing, guestSignin, documentEmbeds, + memberCollectionCreate, collaborativeEditing, defaultCollectionId, defaultUserRole, diff --git a/shared/i18n/locales/en_US/translation.json b/shared/i18n/locales/en_US/translation.json index cf3a49cc5..f78d40d43 100644 --- a/shared/i18n/locales/en_US/translation.json +++ b/shared/i18n/locales/en_US/translation.json @@ -616,6 +616,8 @@ "When enabled, documents can be shared publicly on the internet by any team member": "When enabled, documents can be shared publicly on the internet by any team member", "Rich service embeds": "Rich service embeds", "Links to supported services are shown as rich embeds within your documents": "Links to supported services are shown as rich embeds within your documents", + "Collection creation": "Collection creation", + "Allow members to create new collections within the knowledge base": "Allow members to create new collections within the knowledge base", "Default role": "Default role", "The default user role for new accounts. Changing this setting does not affect existing user accounts.": "The default user role for new accounts. Changing this setting does not affect existing user accounts.", "Sharing is currently disabled.": "Sharing is currently disabled.",