Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding moderator WIP #566

Merged
merged 30 commits into from
Sep 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
2198c6f
Adding moderator
antoinejaussoin Jul 19, 2023
ce4bec1
Create board section
antoinejaussoin Jul 22, 2023
a8797ed
Simplify editor
antoinejaussoin Jul 22, 2023
473576b
Simplify updates (WIP)
antoinejaussoin Jul 25, 2023
e4720c2
Simplify updates (WIP)
antoinejaussoin Jul 25, 2023
bf6e561
Simplify session editing
antoinejaussoin Aug 3, 2023
b4bfba5
Make session settings partial
antoinejaussoin Aug 3, 2023
77243dc
Add related users endpoint
antoinejaussoin Aug 6, 2023
63078e9
Selector
antoinejaussoin Aug 6, 2023
81bed8b
api
antoinejaussoin Aug 6, 2023
d6019c8
User selector
antoinejaussoin Sep 6, 2023
8c22ead
log
antoinejaussoin Sep 6, 2023
71d2ebb
console
antoinejaussoin Sep 6, 2023
b5725e5
Remove dead code 1
antoinejaussoin Sep 6, 2023
19ba7c2
Remove dead code 2
antoinejaussoin Sep 6, 2023
4d95d46
Remove dead code 3
antoinejaussoin Sep 6, 2023
a22a66e
Postgres update
antoinejaussoin Sep 23, 2023
06b2329
Clean up
antoinejaussoin Sep 23, 2023
e62de56
English
antoinejaussoin Sep 23, 2023
d289b9b
translations
antoinejaussoin Sep 23, 2023
596fe9b
PR
antoinejaussoin Sep 23, 2023
0320e4a
Allow moderator to modify UI
antoinejaussoin Sep 23, 2023
e875598
Handle change of settings
antoinejaussoin Sep 23, 2023
4aed1a5
Remove old handlers
antoinejaussoin Sep 23, 2023
8280cc8
Wording
antoinejaussoin Sep 24, 2023
ef85e73
Rename options to moderator
antoinejaussoin Sep 24, 2023
5dc98f7
model
antoinejaussoin Sep 24, 2023
3a2cd50
test
antoinejaussoin Sep 24, 2023
b942538
wide
antoinejaussoin Sep 24, 2023
0f4e2ca
Responsibe
antoinejaussoin Sep 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions backend/src/common/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,9 @@ const actions = {
ADD_POST_GROUP_SUCCESS: 'retrospected/group/add/success',
DELETE_POST_GROUP: 'retrospected/group/delete',
EDIT_POST_GROUP: 'retrospected/group/edit',
RENAME_SESSION: 'retrospected/session/rename',
JOIN_SESSION: 'retrospected/session/join',
LEAVE_SESSION: 'retrospected/session/leave',
EDIT_OPTIONS: 'retrospected/session/options/edit',
EDIT_COLUMNS: 'retrospected/session/columns/edit',
SAVE_TEMPLATE: 'retrospected/session/template/save',
SAVE_SESSION_SETTINGS: 'retrospected/session/settings/save',
LOCK_SESSION: 'retrospected/session/lock',
REQUEST_BOARD: 'retrospected/session/request',
USER_READY: 'retrospected/user-ready',
Expand All @@ -32,10 +29,8 @@ const actions = {
RECEIVE_DELETE_POST_GROUP: 'retrospected/group/receive/delete',
RECEIVE_EDIT_POST_GROUP: 'retrospected/group/receive/edit',
RECEIVE_BOARD: 'retrospected/posts/receive-all',
RECEIVE_OPTIONS: 'retrospected/session/options/receive',
RECEIVE_COLUMNS: 'retrospected/session/columns/receive',
RECEIVE_SESSION_SETTINGS: 'retrospected/session/settings/receive',
RECEIVE_CLIENT_LIST: 'retrospected/session/receive/client-list',
RECEIVE_SESSION_NAME: 'retrospected/session/receive/rename',
RECEIVE_LOCK_SESSION: 'retrospected/session/receive/lock',
RECEIVE_UNAUTHORIZED: 'retrospected/session/receive/unauthorized',
RECEIVE_RATE_LIMITED: 'retrospected/rate-limited-error',
Expand Down
8 changes: 4 additions & 4 deletions backend/src/common/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ export const defaultOptions: SessionOptions = {
allowTimer: true,
timerDuration: 15 * 60,
readonlyOnTimerEnd: true,
restrictTitleEditToOwner: false,
restrictReorderingToOwner: false,
restrictGroupingToOwner: false,
restrictTitleEditToModerator: false,
restrictReorderingToModerator: false,
restrictGroupingToModerator: false,
};

export const defaultSession: Omit<Session, 'createdBy'> = {
export const defaultSession: Omit<Session, 'createdBy' | 'moderator'> = {
id: '',
columns: [
{ id: '', index: 0, label: '', type: 'well', color: '', icon: null },
Expand Down
23 changes: 15 additions & 8 deletions backend/src/common/types.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
export interface Session extends PostContainer, Entity {
export type SessionSettings = Partial<AllSessionSettings>;

export interface AllSessionSettings {
name: string | null;
moderator: User;
options: SessionOptions;
columns: ColumnDefinition[];
locked: boolean;
timer: Date | null;
}

export interface Session extends AllSessionSettings, PostContainer, Entity {
posts: Post[];
groups: PostGroup[];
columns: ColumnDefinition[];
messages: Message[];
options: SessionOptions;
encrypted: string | null;
locked: boolean;
createdBy: User;
ready: string[];
timer: Date | null;
demo: boolean;
}

Expand Down Expand Up @@ -60,9 +66,9 @@ export interface SessionOptions {
allowGrouping: boolean;
allowReordering: boolean;
allowCancelVote: boolean;
restrictTitleEditToOwner: boolean;
restrictReorderingToOwner: boolean;
restrictGroupingToOwner: boolean;
restrictTitleEditToModerator: boolean;
restrictReorderingToModerator: boolean;
restrictGroupingToModerator: boolean;
blurCards: boolean;
newPostsFirst: boolean;
allowTimer: boolean;
Expand Down Expand Up @@ -211,6 +217,7 @@ export type TrackingEvent =
| 'home/load-previous'
| 'game/session/edit-options'
| 'game/session/edit-columns'
| 'game/session/save-options'
| 'game/session/reset'
| 'game/session/disconnect'
| 'game/session/unexpected-disconnection'
Expand Down
16 changes: 6 additions & 10 deletions backend/src/common/ws.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import {
ColumnDefinition,
Post,
PostGroup,
SessionOptions,
SessionSettings,
User,
VoteExtract,
VoteType,
Expand All @@ -18,10 +17,6 @@ export interface WsUserData {
user: User;
}

export interface WsNameData {
name: string;
}

export interface WsPostUpdatePayload {
post: Omit<Omit<Omit<Post, 'votes'>, 'user'>, 'group'>;
groupId: string | null;
Expand Down Expand Up @@ -58,9 +53,9 @@ export interface WsDeleteGroupPayload {
groupId: string;
}

export interface WsSaveTemplatePayload {
columns: ColumnDefinition[];
options: SessionOptions;
export interface WsSaveSessionSettingsPayload {
settings: SessionSettings;
saveAsTemplate: boolean;
}

export interface WsUserReadyPayload {
Expand All @@ -84,7 +79,8 @@ export type WsErrorType =
| 'cannot_record_chat_message'
| 'cannot_cancel_votes'
| 'unknown_error'
| 'action_unauthorised';
| 'action_unauthorised'
| 'cannot_save_session_settings';

export interface WsErrorPayload {
type: WsErrorType;
Expand Down
26 changes: 26 additions & 0 deletions backend/src/db/actions/sessions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,32 @@ export async function updateName(
});
}

export async function updateModerator(
sessionId: string,
moderatorId: string
): Promise<boolean> {
return await transaction(async (manager) => {
try {
const sessionRepository = manager.withRepository(SessionRepository);
const session = await sessionRepository.findOne({
where: { id: sessionId },
});
if (session) {
await sessionRepository.save({
...session,
moderator: {
id: moderatorId,
},
});
return true;
}
return false;
} catch {
return false;
}
});
}

export async function getSessionWithVisitors(
sessionId: string
): Promise<SessionEntity | null> {
Expand Down
11 changes: 10 additions & 1 deletion backend/src/db/actions/users.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { UserEntity, UserView } from '../entities/index.js';
import { EntityManager, Not } from 'typeorm';
import { EntityManager, In, Not } from 'typeorm';
import {
UserIdentityRepository,
UserRepository,
Expand Down Expand Up @@ -88,6 +88,15 @@ export async function getUserViewInner(
return user || null;
}

export async function getRelatedUsers(userId: string): Promise<UserView[]> {
return await transaction(async (manager) => {
const userRepository = manager.withRepository(UserRepository);
const ids = await userRepository.getRelatedUsersIds(userId);
const userViewRepository = manager.getRepository(UserView);
return userViewRepository.findBy({ id: In(ids) });
});
}

export async function getPasswordIdentity(
username: string
): Promise<UserIdentityEntity | null> {
Expand Down
5 changes: 5 additions & 0 deletions backend/src/db/entities/Session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ export default class SessionEntity {
@ManyToOne(() => UserEntity, { eager: true, cascade: true, nullable: false })
@Index()
public createdBy: UserEntity;
@ManyToOne(() => UserEntity, { eager: true, cascade: true, nullable: false })
@Index()
public moderator: UserEntity;
@OneToMany(() => PostEntity, (post) => post.session, {
cascade: true,
nullable: false,
Expand Down Expand Up @@ -80,6 +83,7 @@ export default class SessionEntity {
columns:
this.columns === undefined ? [] : this.columns.map((c) => c.toJson()),
createdBy: this.createdBy.toJson(),
moderator: this.moderator.toJson(),
groups:
this.groups === undefined ? [] : this.groups.map((g) => g.toJson()),
id: this.id,
Expand All @@ -105,6 +109,7 @@ export default class SessionEntity {
this.id = id;
this.name = name;
this.createdBy = createdBy;
this.moderator = createdBy;
this.options = new SessionOptionsEntity(options);
this.encrypted = null;
this.locked = false;
Expand Down
16 changes: 9 additions & 7 deletions backend/src/db/entities/SessionOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ export default class SessionOptionsEntity {
@Column({ default: true })
public allowTimer: boolean;
@Column({ default: false })
public restrictTitleEditToOwner: boolean;
public restrictTitleEditToModerator: boolean;
@Column({ default: false })
public restrictReorderingToOwner: boolean;
public restrictReorderingToModerator: boolean;
@Column({ default: false })
public restrictGroupingToOwner: boolean;
public restrictGroupingToModerator: boolean;
@Column({ type: 'numeric', default: 15 * 60 })
public timerDuration: number;
@Column({ default: true })
Expand Down Expand Up @@ -69,10 +69,12 @@ export default class SessionOptionsEntity {
this.allowTimer = optionsWithDefault.allowTimer;
this.timerDuration = optionsWithDefault.timerDuration;
this.readonlyOnTimerEnd = optionsWithDefault.readonlyOnTimerEnd;
this.restrictTitleEditToOwner = optionsWithDefault.restrictTitleEditToOwner;
this.restrictReorderingToOwner =
optionsWithDefault.restrictReorderingToOwner;
this.restrictGroupingToOwner = optionsWithDefault.restrictGroupingToOwner;
this.restrictTitleEditToModerator =
optionsWithDefault.restrictTitleEditToModerator;
this.restrictReorderingToModerator =
optionsWithDefault.restrictReorderingToModerator;
this.restrictGroupingToModerator =
optionsWithDefault.restrictGroupingToModerator;
}
}

Expand Down
19 changes: 19 additions & 0 deletions backend/src/db/migrations/1689787853666-Moderator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { MigrationInterface, QueryRunner } from "typeorm";

export class Moderator1689787853666 implements MigrationInterface {
name = 'Moderator1689787853666'

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "sessions" ADD "moderator_id" character varying`);
await queryRunner.query(`CREATE INDEX "IDX_7127e358f4f0bf8686d77af14c" ON "sessions" ("moderator_id") `);
await queryRunner.query(`ALTER TABLE "sessions" ADD CONSTRAINT "FK_7127e358f4f0bf8686d77af14cd" FOREIGN KEY ("moderator_id") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`UPDATE "sessions" SET "moderator_id" = "created_by_id"`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "sessions" DROP CONSTRAINT "FK_7127e358f4f0bf8686d77af14cd"`);
await queryRunner.query(`DROP INDEX "public"."IDX_7127e358f4f0bf8686d77af14c"`);
await queryRunner.query(`ALTER TABLE "sessions" DROP COLUMN "moderator_id"`);
}

}
18 changes: 18 additions & 0 deletions backend/src/db/migrations/1689787951867-ModeratorNonNull.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { MigrationInterface, QueryRunner } from "typeorm";

export class ModeratorNonNull1689787951867 implements MigrationInterface {
name = 'ModeratorNonNull1689787951867'

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "sessions" DROP CONSTRAINT "FK_1ccf045da14e5350b26ee882592"`);
await queryRunner.query(`ALTER TABLE "sessions" ALTER COLUMN "created_by_id" DROP NOT NULL`);
await queryRunner.query(`ALTER TABLE "sessions" ADD CONSTRAINT "FK_1ccf045da14e5350b26ee882592" FOREIGN KEY ("created_by_id") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "sessions" DROP CONSTRAINT "FK_1ccf045da14e5350b26ee882592"`);
await queryRunner.query(`ALTER TABLE "sessions" ALTER COLUMN "created_by_id" SET NOT NULL`);
await queryRunner.query(`ALTER TABLE "sessions" ADD CONSTRAINT "FK_1ccf045da14e5350b26ee882592" FOREIGN KEY ("created_by_id") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
}

}
66 changes: 66 additions & 0 deletions backend/src/db/migrations/1695539888278-RenameModerator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { MigrationInterface, QueryRunner } from "typeorm";

export class RenameModerator1695539888278 implements MigrationInterface {
name = 'RenameModerator1695539888278'

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "templates" ADD "options_restrict_title_edit_to_moderator" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "templates" ADD "options_restrict_reordering_to_moderator" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "templates" ADD "options_restrict_grouping_to_moderator" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "sessions" ADD "options_restrict_title_edit_to_moderator" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "sessions" ADD "options_restrict_reordering_to_moderator" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "sessions" ADD "options_restrict_grouping_to_moderator" boolean NOT NULL DEFAULT false`);

await queryRunner.query(`UPDATE "templates" SET
options_restrict_title_edit_to_moderator = options_restrict_title_edit_to_owner,
options_restrict_reordering_to_moderator = options_restrict_reordering_to_owner,
options_restrict_grouping_to_moderator = options_restrict_grouping_to_owner
`);
await queryRunner.query(`UPDATE "sessions" SET
options_restrict_title_edit_to_moderator = options_restrict_title_edit_to_owner,
options_restrict_reordering_to_moderator = options_restrict_reordering_to_owner,
options_restrict_grouping_to_moderator = options_restrict_grouping_to_owner
`);
await queryRunner.query(`ALTER TABLE "templates" DROP COLUMN "options_restrict_title_edit_to_owner"`);
await queryRunner.query(`ALTER TABLE "templates" DROP COLUMN "options_restrict_reordering_to_owner"`);
await queryRunner.query(`ALTER TABLE "templates" DROP COLUMN "options_restrict_grouping_to_owner"`);
await queryRunner.query(`ALTER TABLE "sessions" DROP COLUMN "options_restrict_title_edit_to_owner"`);
await queryRunner.query(`ALTER TABLE "sessions" DROP COLUMN "options_restrict_reordering_to_owner"`);
await queryRunner.query(`ALTER TABLE "sessions" DROP COLUMN "options_restrict_grouping_to_owner"`);
await queryRunner.query(`ALTER TABLE "sessions" DROP CONSTRAINT "FK_1ccf045da14e5350b26ee882592"`);
await queryRunner.query(`ALTER TABLE "sessions" ALTER COLUMN "created_by_id" SET NOT NULL`);
await queryRunner.query(`ALTER TABLE "sessions" ADD CONSTRAINT "FK_1ccf045da14e5350b26ee882592" FOREIGN KEY ("created_by_id") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "sessions" DROP CONSTRAINT "FK_1ccf045da14e5350b26ee882592"`);
await queryRunner.query(`ALTER TABLE "sessions" ALTER COLUMN "created_by_id" DROP NOT NULL`);
await queryRunner.query(`ALTER TABLE "sessions" ADD CONSTRAINT "FK_1ccf045da14e5350b26ee882592" FOREIGN KEY ("created_by_id") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "sessions" ADD "options_restrict_grouping_to_owner" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "sessions" ADD "options_restrict_reordering_to_owner" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "sessions" ADD "options_restrict_title_edit_to_owner" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "templates" ADD "options_restrict_grouping_to_owner" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "templates" ADD "options_restrict_reordering_to_owner" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "templates" ADD "options_restrict_title_edit_to_owner" boolean NOT NULL DEFAULT false`);

await queryRunner.query(`UPDATE "templates" SET
options_restrict_title_edit_to_owner = options_restrict_title_edit_to_moderator,
options_restrict_reordering_to_owner = options_restrict_reordering_to_moderator,
options_restrict_grouping_to_owner = options_restrict_grouping_to_moderator
`);
await queryRunner.query(`UPDATE "sessions" SET
options_restrict_title_edit_to_owner = options_restrict_title_edit_to_moderator,
options_restrict_reordering_to_owner = options_restrict_reordering_to_moderator,
options_restrict_grouping_to_owner = options_restrict_grouping_to_moderator
`);

await queryRunner.query(`ALTER TABLE "sessions" DROP COLUMN "options_restrict_grouping_to_moderator"`);
await queryRunner.query(`ALTER TABLE "sessions" DROP COLUMN "options_restrict_reordering_to_moderator"`);
await queryRunner.query(`ALTER TABLE "sessions" DROP COLUMN "options_restrict_title_edit_to_moderator"`);
await queryRunner.query(`ALTER TABLE "templates" DROP COLUMN "options_restrict_grouping_to_moderator"`);
await queryRunner.query(`ALTER TABLE "templates" DROP COLUMN "options_restrict_reordering_to_moderator"`);
await queryRunner.query(`ALTER TABLE "templates" DROP COLUMN "options_restrict_title_edit_to_moderator"`);

}

}
18 changes: 18 additions & 0 deletions backend/src/db/migrations/1695540515122-ModeratorNonNull.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { MigrationInterface, QueryRunner } from "typeorm";

export class ModeratorNonNull1695540515122 implements MigrationInterface {
name = 'ModeratorNonNull1695540515122'

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "sessions" DROP CONSTRAINT "FK_7127e358f4f0bf8686d77af14cd"`);
await queryRunner.query(`ALTER TABLE "sessions" ALTER COLUMN "moderator_id" SET NOT NULL`);
await queryRunner.query(`ALTER TABLE "sessions" ADD CONSTRAINT "FK_7127e358f4f0bf8686d77af14cd" FOREIGN KEY ("moderator_id") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "sessions" DROP CONSTRAINT "FK_7127e358f4f0bf8686d77af14cd"`);
await queryRunner.query(`ALTER TABLE "sessions" ALTER COLUMN "moderator_id" DROP NOT NULL`);
await queryRunner.query(`ALTER TABLE "sessions" ADD CONSTRAINT "FK_7127e358f4f0bf8686d77af14cd" FOREIGN KEY ("moderator_id") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
}

}
3 changes: 2 additions & 1 deletion backend/src/db/repositories/SessionRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,15 @@ export default getBaseRepository(SessionEntity).extend({
}
},
async saveFromJson(
session: Omit<JsonSession, 'createdBy'>,
session: Omit<JsonSession, 'createdBy' | 'moderator'>,
authorId: string
): Promise<JsonSession> {
const sessionWithoutPosts = {
...session,
posts: undefined,
columns: undefined,
createdBy: { id: authorId },
moderator: { id: authorId },
};
delete sessionWithoutPosts.posts;
delete sessionWithoutPosts.columns;
Expand Down
Loading
Loading