feat: Allow viewers to be upgraded to editors on individual collections (#4023)
* Improve types * More types, fix default permission for viewers added to collection * fix change of default role for CollectionGroup * Restore policy * test * tests
This commit is contained in:
@@ -21,11 +21,12 @@ import {
|
||||
Length as SimpleLength,
|
||||
} from "sequelize-typescript";
|
||||
import isUUID from "validator/lib/isUUID";
|
||||
import { CollectionPermission } from "@shared/types";
|
||||
import { sortNavigationNodes } from "@shared/utils/collections";
|
||||
import { SLUG_URL_REGEX } from "@shared/utils/urlHelpers";
|
||||
import { CollectionValidation } from "@shared/validations";
|
||||
import slugify from "@server/utils/slugify";
|
||||
import { NavigationNode, CollectionSort } from "~/types";
|
||||
import type { NavigationNode, CollectionSort } from "~/types";
|
||||
import CollectionGroup from "./CollectionGroup";
|
||||
import CollectionUser from "./CollectionUser";
|
||||
import Document from "./Document";
|
||||
@@ -39,9 +40,6 @@ import IsHexColor from "./validators/IsHexColor";
|
||||
import Length from "./validators/Length";
|
||||
import NotContainsUrl from "./validators/NotContainsUrl";
|
||||
|
||||
// without this indirection, the app crashes on starup
|
||||
type Sort = CollectionSort;
|
||||
|
||||
@Scopes(() => ({
|
||||
withAllMemberships: {
|
||||
include: [
|
||||
@@ -174,9 +172,9 @@ class Collection extends ParanoidModel {
|
||||
@Column
|
||||
index: string | null;
|
||||
|
||||
@IsIn([["read", "read_write"]])
|
||||
@Column
|
||||
permission: "read" | "read_write" | null;
|
||||
@IsIn([Object.values(CollectionPermission)])
|
||||
@Column(DataType.STRING)
|
||||
permission: CollectionPermission | null;
|
||||
|
||||
@Default(false)
|
||||
@Column
|
||||
@@ -193,7 +191,7 @@ class Collection extends ParanoidModel {
|
||||
@Column({
|
||||
type: DataType.JSONB,
|
||||
validate: {
|
||||
isSort(value: Sort) {
|
||||
isSort(value: CollectionSort) {
|
||||
if (
|
||||
typeof value !== "object" ||
|
||||
!value.direction ||
|
||||
@@ -213,7 +211,7 @@ class Collection extends ParanoidModel {
|
||||
},
|
||||
},
|
||||
})
|
||||
sort: Sort;
|
||||
sort: CollectionSort;
|
||||
|
||||
// getters
|
||||
|
||||
@@ -255,14 +253,14 @@ class Collection extends ParanoidModel {
|
||||
model: Collection,
|
||||
options: { transaction: Transaction }
|
||||
) {
|
||||
if (model.permission !== "read_write") {
|
||||
if (model.permission !== CollectionPermission.ReadWrite) {
|
||||
return CollectionUser.findOrCreate({
|
||||
where: {
|
||||
collectionId: model.id,
|
||||
userId: model.createdById,
|
||||
},
|
||||
defaults: {
|
||||
permission: "read_write",
|
||||
permission: CollectionPermission.ReadWrite,
|
||||
createdById: model.createdById,
|
||||
},
|
||||
transaction: options.transaction,
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
DataType,
|
||||
Scopes,
|
||||
} from "sequelize-typescript";
|
||||
import { CollectionPermission } from "@shared/types";
|
||||
import Collection from "./Collection";
|
||||
import Group from "./Group";
|
||||
import User from "./User";
|
||||
@@ -33,10 +34,10 @@ import Fix from "./decorators/Fix";
|
||||
@Table({ tableName: "collection_groups", modelName: "collection_group" })
|
||||
@Fix
|
||||
class CollectionGroup extends BaseModel {
|
||||
@Default("read_write")
|
||||
@IsIn([["read", "read_write", "maintainer"]])
|
||||
@Column
|
||||
permission: string;
|
||||
@Default(CollectionPermission.ReadWrite)
|
||||
@IsIn([Object.values(CollectionPermission)])
|
||||
@Column(DataType.STRING)
|
||||
permission: CollectionPermission;
|
||||
|
||||
// associations
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
DataType,
|
||||
Scopes,
|
||||
} from "sequelize-typescript";
|
||||
import { CollectionPermission } from "@shared/types";
|
||||
import Collection from "./Collection";
|
||||
import User from "./User";
|
||||
import BaseModel from "./base/BaseModel";
|
||||
@@ -32,10 +33,10 @@ import Fix from "./decorators/Fix";
|
||||
@Table({ tableName: "collection_users", modelName: "collection_user" })
|
||||
@Fix
|
||||
class CollectionUser extends BaseModel {
|
||||
@Default("read_write")
|
||||
@IsIn([["read", "read_write", "maintainer"]])
|
||||
@Column
|
||||
permission: string;
|
||||
@Default(CollectionPermission.ReadWrite)
|
||||
@IsIn([Object.values(CollectionPermission)])
|
||||
@Column(DataType.STRING)
|
||||
permission: CollectionPermission;
|
||||
|
||||
// associations
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
IsUrl,
|
||||
AllowNull,
|
||||
} from "sequelize-typescript";
|
||||
import { CollectionPermission } from "@shared/types";
|
||||
import { getBaseDomain, RESERVED_SUBDOMAINS } from "@shared/utils/domains";
|
||||
import env from "@server/env";
|
||||
import { generateAvatarUrl } from "@server/utils/avatars";
|
||||
@@ -172,7 +173,7 @@ class Team extends ParanoidModel {
|
||||
teamId: this.id,
|
||||
createdById: userId,
|
||||
sort: Collection.DEFAULT_SORT,
|
||||
permission: "read_write",
|
||||
permission: CollectionPermission.ReadWrite,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { CollectionPermission } from "@shared/types";
|
||||
import { buildUser, buildTeam, buildCollection } from "@server/test/factories";
|
||||
import { getTestDatabase } from "@server/test/support";
|
||||
import CollectionUser from "./CollectionUser";
|
||||
@@ -39,7 +40,7 @@ describe("user model", () => {
|
||||
});
|
||||
const collection = await buildCollection({
|
||||
teamId: team.id,
|
||||
permission: "read_write",
|
||||
permission: CollectionPermission.ReadWrite,
|
||||
});
|
||||
const response = await user.collectionIds();
|
||||
expect(response.length).toEqual(1);
|
||||
@@ -52,7 +53,7 @@ describe("user model", () => {
|
||||
});
|
||||
const collection = await buildCollection({
|
||||
teamId: team.id,
|
||||
permission: "read",
|
||||
permission: CollectionPermission.Read,
|
||||
});
|
||||
const response = await user.collectionIds();
|
||||
expect(response.length).toEqual(1);
|
||||
@@ -83,7 +84,7 @@ describe("user model", () => {
|
||||
createdById: user.id,
|
||||
collectionId: collection.id,
|
||||
userId: user.id,
|
||||
permission: "read",
|
||||
permission: CollectionPermission.Read,
|
||||
});
|
||||
const response = await user.collectionIds();
|
||||
expect(response.length).toEqual(1);
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
AllowNull,
|
||||
} from "sequelize-typescript";
|
||||
import { languages } from "@shared/i18n";
|
||||
import { CollectionPermission } from "@shared/types";
|
||||
import { stringToColor } from "@shared/utils/color";
|
||||
import env from "@server/env";
|
||||
import { ValidationError } from "../errors";
|
||||
@@ -219,6 +220,12 @@ class User extends ParanoidModel {
|
||||
return stringToColor(this.id);
|
||||
}
|
||||
|
||||
get defaultCollectionPermission(): CollectionPermission {
|
||||
return this.isViewer
|
||||
? CollectionPermission.Read
|
||||
: CollectionPermission.ReadWrite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a code that can be used to delete this user account. The code will
|
||||
* be rotated when the user signs out.
|
||||
@@ -298,8 +305,8 @@ class User extends ParanoidModel {
|
||||
return collectionStubs
|
||||
.filter(
|
||||
(c) =>
|
||||
c.permission === "read" ||
|
||||
c.permission === "read_write" ||
|
||||
c.permission === CollectionPermission.Read ||
|
||||
c.permission === CollectionPermission.ReadWrite ||
|
||||
c.memberships.length > 0 ||
|
||||
c.collectionGroupMemberships.length > 0
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user