mirror of
https://gitlab.com/comunic/comunicapiv2
synced 2024-11-26 07:19:23 +00:00
620 lines
16 KiB
TypeScript
620 lines
16 KiB
TypeScript
import { DatabaseHelper } from "./DatabaseHelper";
|
|
import { GroupsAccessLevel, GroupVisibilityLevel, GroupInfo, GroupPostsCreationLevel } from "../entities/Group";
|
|
import { GroupMembershipLevels, GroupMember } from "../entities/GroupMember";
|
|
import { NewGroup } from "../entities/NewGroup";
|
|
import { time } from "../utils/DateUtils";
|
|
import { GroupSettings } from "../entities/GroupSettings";
|
|
import { existsSync, unlinkSync } from "fs";
|
|
|
|
/**
|
|
* Groups helper
|
|
*
|
|
* @author Pierre HUBERT
|
|
*/
|
|
|
|
const GROUPS_LIST_TABLE = "comunic_groups";
|
|
const GROUPS_MEMBERS_TABLE = "comunic_groups_members";
|
|
|
|
export const PATH_GROUPS_LOGOS = "groups_logo";
|
|
|
|
export class GroupsHelper {
|
|
|
|
|
|
/**
|
|
* Create a new group
|
|
*
|
|
* @param info Information about the new group to create
|
|
* @throws {Error} In case of error
|
|
*/
|
|
public static async Create(info: NewGroup) : Promise<number> {
|
|
|
|
// Insert the group into the database
|
|
const groupID = await DatabaseHelper.InsertRow(GROUPS_LIST_TABLE, {
|
|
time_create: info.timeCreate,
|
|
userid_create: info.userID,
|
|
name: info.name
|
|
});
|
|
|
|
if(groupID <= 0)
|
|
throw Error("Could not create the group!");
|
|
|
|
await this.InsertMember({
|
|
id: 0,
|
|
groupID: groupID,
|
|
userID: info.userID,
|
|
timeCreate: time(),
|
|
level: GroupMembershipLevels.ADMINISTRATOR,
|
|
following: true
|
|
});
|
|
|
|
return groupID;
|
|
}
|
|
|
|
/**
|
|
* Check out whether a group exists or not
|
|
*
|
|
* @param groupID Target group ID
|
|
*/
|
|
public static async Exists(groupID: number) : Promise<boolean> {
|
|
return await DatabaseHelper.Count({
|
|
table: GROUPS_LIST_TABLE,
|
|
where: {
|
|
id: groupID
|
|
}
|
|
}) > 0;
|
|
}
|
|
|
|
/**
|
|
* Get the list of groups of a user
|
|
*
|
|
* @param userID Target user ID
|
|
* @param onlyFollowed Include only the groups the user follow
|
|
*/
|
|
public static async GetListUser(userID: number, onlyFollowed: boolean = false) : Promise<Array<number>> {
|
|
const list = await DatabaseHelper.Query({
|
|
table: GROUPS_MEMBERS_TABLE,
|
|
where: {
|
|
user_id: userID,
|
|
},
|
|
customWhere: onlyFollowed ? "following = 1" : "",
|
|
fields: ["groups_id"]
|
|
});
|
|
|
|
return list.map(e => e.groups_id);
|
|
}
|
|
|
|
/**
|
|
* Get the list of gruops of a user where the users can create
|
|
* posts
|
|
*
|
|
* @param userID The ID of the target user
|
|
*/
|
|
public static async GetListUserWhereCanCreatePosts(userID: number) : Promise<Array<number>> {
|
|
const list = await DatabaseHelper.Query({
|
|
// Members table
|
|
table: GROUPS_MEMBERS_TABLE,
|
|
tableAlias: "m",
|
|
|
|
// Groups liist table
|
|
joins: [
|
|
{
|
|
table: GROUPS_LIST_TABLE,
|
|
tableAlias: "g",
|
|
condition: "m.groups_id = g.id"
|
|
}
|
|
],
|
|
|
|
where: {
|
|
user_id: userID
|
|
},
|
|
|
|
customWhere: "level = ? OR level = ? OR (level = ? AND posts_level = ?)",
|
|
customWhereArgs: [
|
|
GroupMembershipLevels.ADMINISTRATOR.toString(),
|
|
GroupMembershipLevels.MODERATOR.toString(),
|
|
|
|
GroupMembershipLevels.MEMBER.toString(),
|
|
GroupPostsCreationLevel.POSTS_LEVEL_ALL_MEMBERS.toString()
|
|
],
|
|
|
|
fields: ["g.id"]
|
|
});
|
|
|
|
return list.map((e) => e.id);
|
|
}
|
|
|
|
/**
|
|
* Get information about a group
|
|
*
|
|
* @param groupID Target group ID
|
|
* @returns Information about the group
|
|
* @throws {Error} if the group was not found
|
|
*/
|
|
public static async GetInfo(groupID: number) : Promise<GroupInfo> {
|
|
const row = await DatabaseHelper.QueryRow({
|
|
table: GROUPS_LIST_TABLE,
|
|
where: {
|
|
id: groupID.toString()
|
|
}
|
|
});
|
|
|
|
if(row == null || !row)
|
|
throw new Error("Could not get information about the group!");
|
|
|
|
return this.DbToGroupInfo(row);
|
|
}
|
|
|
|
/**
|
|
* Update (set) group settings
|
|
*
|
|
* @param settings Group settings
|
|
*/
|
|
public static async SetSettings(settings: GroupSettings) {
|
|
const dbEntry = this.GroupSettingsToDB(settings);
|
|
|
|
await DatabaseHelper.UpdateRows({
|
|
table: GROUPS_LIST_TABLE,
|
|
where: {
|
|
id: settings.id
|
|
},
|
|
set: dbEntry
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Delete the logo of a group
|
|
*
|
|
* @param groupID Target group ID
|
|
*/
|
|
public static async DeleteLogo(groupID: number) {
|
|
const currSettings = await this.GetInfo(groupID);
|
|
|
|
if(!currSettings.hasLogo)
|
|
return;
|
|
|
|
if(existsSync(currSettings.logoSysPath))
|
|
unlinkSync(currSettings.logoSysPath);
|
|
|
|
currSettings.logo = "";
|
|
|
|
await this.SetSettings(currSettings);
|
|
}
|
|
|
|
/**
|
|
* Get the visibility level of a group
|
|
*
|
|
* @param groupID Target group ID
|
|
*/
|
|
public static async GetVisibility(groupID: number) : Promise<GroupVisibilityLevel> {
|
|
const result = await DatabaseHelper.QueryRow({
|
|
table: GROUPS_LIST_TABLE,
|
|
where: {
|
|
id: groupID
|
|
},
|
|
fields: ["visibility"]
|
|
});
|
|
|
|
if(result == null)
|
|
throw Error("Group " + groupID + " does not exists!");
|
|
|
|
return result.visibility;
|
|
}
|
|
|
|
/**
|
|
* Convenience function to check whether a group is open or not
|
|
*
|
|
* @param groupID Target group ID
|
|
*/
|
|
public static async IsOpen(groupID: number) : Promise<boolean> {
|
|
return (await this.GetVisibility(groupID)) == GroupVisibilityLevel.OPEN_GROUP;
|
|
}
|
|
|
|
/**
|
|
* Invite a user to join a group
|
|
*
|
|
* @param groupID The ID of the target group
|
|
* @param userID The ID of the target user
|
|
*/
|
|
public static async SendInvitation(groupID: number, userID: number) {
|
|
await this.InsertMember(new GroupMember({
|
|
id: -1,
|
|
userID: userID,
|
|
groupID: groupID,
|
|
timeCreate: time(),
|
|
following: true,
|
|
level: GroupMembershipLevels.INVITED
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* Check if a user received an invitation to join a group or not
|
|
*
|
|
* @param groupID Target group ID
|
|
* @param userID Target user ID
|
|
*/
|
|
public static async ReceivedInvitation(groupID: number, userID: number) : Promise<boolean> {
|
|
return await DatabaseHelper.Count({
|
|
table: GROUPS_MEMBERS_TABLE,
|
|
where: {
|
|
groups_id: groupID,
|
|
user_ID: userID,
|
|
level: GroupMembershipLevels.INVITED
|
|
}
|
|
}) > 0;
|
|
}
|
|
|
|
/**
|
|
* Respond to a membership invitation
|
|
*
|
|
* @param groupID Target group ID
|
|
* @param userID Target user ID
|
|
* @param accept true to accept invitation / FALSE else
|
|
*/
|
|
public static async RespondInvitation(groupID: number, userID: number, accept: boolean) {
|
|
if(!accept)
|
|
await this.DeleteMember(groupID, userID);
|
|
else
|
|
await this.UpdateMembershipLevel(groupID, userID, GroupMembershipLevels.MEMBER);
|
|
}
|
|
|
|
/**
|
|
* Respond to a group membership request
|
|
*
|
|
* @param groupID Target groupID
|
|
* @param userID Target user ID
|
|
* @param accept TRUE to accept / FALSE else
|
|
*/
|
|
public static async RespondRequest(groupID: number, userID: number, accept: boolean) {
|
|
if(!accept)
|
|
await this.DeleteMember(groupID, userID);
|
|
else
|
|
await this.UpdateMembershipLevel(groupID, userID, GroupMembershipLevels.MEMBER);
|
|
}
|
|
|
|
/**
|
|
* Insert a new group member
|
|
*
|
|
* @param member Information about the group member to add
|
|
*/
|
|
public static async InsertMember(member: GroupMember) {
|
|
await DatabaseHelper.InsertRow(GROUPS_MEMBERS_TABLE, {
|
|
groups_id: member.groupID,
|
|
user_id: member.userID,
|
|
time_create: member.timeCreate,
|
|
level: member.level
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Update a user membership level
|
|
*
|
|
* @param groupID Target group ID
|
|
* @param userID Target user ID
|
|
* @param level New membership level
|
|
*/
|
|
public static async UpdateMembershipLevel(groupID: number, userID: number, level: GroupMembershipLevels) {
|
|
await DatabaseHelper.UpdateRows({
|
|
table: GROUPS_MEMBERS_TABLE,
|
|
where: {
|
|
user_id: userID,
|
|
groups_id: groupID
|
|
},
|
|
set: {
|
|
level: level
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Update following status of a user
|
|
*
|
|
* @param groupID Target group ID
|
|
* @param userID Target user ID
|
|
* @param follow true to follow / false else
|
|
*/
|
|
public static async SetFollowing(groupID: number, userID: number, follow: boolean) {
|
|
await DatabaseHelper.UpdateRows({
|
|
table: GROUPS_MEMBERS_TABLE,
|
|
where: {
|
|
groups_id: groupID,
|
|
user_id: userID
|
|
},
|
|
set: {
|
|
following: follow ? 1 : 0
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Delete completely a user membership
|
|
*
|
|
* @param groupID Target group ID
|
|
* @param userID Target user ID
|
|
*/
|
|
public static async DeleteMember(groupID: number, userID: number) {
|
|
await DatabaseHelper.DeleteRows(GROUPS_MEMBERS_TABLE, {
|
|
groups_id: groupID,
|
|
user_id: userID
|
|
});
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the membership level of a user for a group
|
|
*
|
|
* @param groupID The ID of target group
|
|
* @param userID The ID of target user
|
|
*/
|
|
public static async GetMembershipLevel(groupID: number, userID: number) : Promise<GroupMembershipLevels> {
|
|
|
|
// If the user is not signed in
|
|
if(userID < 1)
|
|
return GroupMembershipLevels.VISITOR;
|
|
|
|
const result = await DatabaseHelper.Query({
|
|
table: GROUPS_MEMBERS_TABLE,
|
|
where: {
|
|
groups_id: groupID,
|
|
user_id: userID
|
|
},
|
|
fields: ["level"]
|
|
});
|
|
|
|
// If user has no membership
|
|
if(result.length == 0)
|
|
return GroupMembershipLevels.VISITOR;
|
|
|
|
return result[0].level;
|
|
|
|
}
|
|
|
|
/**
|
|
* Get the current access of a user to a group
|
|
*
|
|
* @param groupID The ID of the target group
|
|
* @param userID The ID of the target user
|
|
*/
|
|
public static async GetAccessLevel(groupID: number, userID: number) : Promise<GroupsAccessLevel> {
|
|
|
|
const membershipLevel =
|
|
userID > 0 ? await this.GetMembershipLevel(groupID, userID) : GroupMembershipLevels.VISITOR;
|
|
|
|
//Check if the user is a confirmed member of group
|
|
if(membershipLevel == GroupMembershipLevels.ADMINISTRATOR)
|
|
return GroupsAccessLevel.ADMIN_ACCESS;
|
|
if(membershipLevel == GroupMembershipLevels.MODERATOR)
|
|
return GroupsAccessLevel.MODERATOR_ACCESS;
|
|
if(membershipLevel == GroupMembershipLevels.MEMBER)
|
|
return GroupsAccessLevel.MEMBER_ACCESS;
|
|
|
|
const groupVisibilityLevel = await this.GetVisibility(groupID);
|
|
|
|
//If the group is open, everyone has view access
|
|
if(groupVisibilityLevel == GroupVisibilityLevel.OPEN_GROUP)
|
|
return GroupsAccessLevel.VIEW_ACCESS;
|
|
|
|
//Else, all pending and invited membership get limited access
|
|
if(membershipLevel == GroupMembershipLevels.PENDING ||
|
|
membershipLevel == GroupMembershipLevels.INVITED)
|
|
return GroupsAccessLevel.LIMITED_ACCESS;
|
|
|
|
//Private groups gives limited access
|
|
if(groupVisibilityLevel == GroupVisibilityLevel.PRIVATE_GROUP)
|
|
return GroupsAccessLevel.LIMITED_ACCESS;
|
|
|
|
// Else the user can not see the group
|
|
// Especially in the case of secret groupe
|
|
return GroupsAccessLevel.NO_ACCESS;
|
|
}
|
|
|
|
/**
|
|
* Get a user membership information
|
|
*
|
|
* @param groupID Target group ID
|
|
* @param userID Target user ID
|
|
* @returns Membership info / null if none found
|
|
*/
|
|
public static async GetMembershipInfo(groupID: number, userID: number): Promise<GroupMember> {
|
|
|
|
if(userID == 0)
|
|
return null;
|
|
|
|
const data = await DatabaseHelper.QueryRow({
|
|
table: GROUPS_MEMBERS_TABLE,
|
|
where: {
|
|
groups_id: groupID,
|
|
user_id: userID
|
|
}
|
|
});
|
|
|
|
if(data == null)
|
|
return null;
|
|
|
|
return this.DbToGroupMember(data);
|
|
}
|
|
|
|
/**
|
|
* Find a group by its virtual directory
|
|
*
|
|
* @param dir Target directory
|
|
* @returns The ID of the target directory / -1 if none found
|
|
*/
|
|
public static async FindByVirtualDirectory(dir: string) : Promise<number> {
|
|
const result = await DatabaseHelper.QueryRow({
|
|
table: GROUPS_LIST_TABLE,
|
|
where: {
|
|
virtual_directory: dir
|
|
},
|
|
fields: ["id"]
|
|
});
|
|
|
|
return result == null ? -1 : result.id;
|
|
}
|
|
|
|
/**
|
|
* Check out whether a virtual directory is available or not
|
|
*
|
|
* @param dir The directory to check
|
|
* @param groupID The ID of the group making the request
|
|
*/
|
|
public static async CheckDirectoryAvailability(dir: string, groupID: number = -1) : Promise<boolean> {
|
|
const currID = await this.FindByVirtualDirectory(dir);
|
|
|
|
return currID < 1 || currID == groupID;
|
|
}
|
|
|
|
/**
|
|
* Get the entire list of members of a group
|
|
*
|
|
* @param groupID Target Group ID
|
|
*/
|
|
public static async GetListMembers(groupID: number) : Promise<Array<GroupMember>> {
|
|
const members = await DatabaseHelper.Query({
|
|
table: GROUPS_MEMBERS_TABLE,
|
|
where: {
|
|
groups_id: groupID
|
|
}
|
|
});
|
|
|
|
return members.map((row) => this.DbToGroupMember(row));
|
|
}
|
|
|
|
/**
|
|
* Count the number of members of a group
|
|
*
|
|
* @param groupID Target group ID
|
|
*/
|
|
private static async CountMembers(groupID: number) : Promise<number> {
|
|
return await DatabaseHelper.Count({
|
|
table: GROUPS_MEMBERS_TABLE,
|
|
where: {
|
|
groups_id: groupID
|
|
},
|
|
customWhere: "level <= ?",
|
|
customWhereArgs: [GroupMembershipLevels.MEMBER.toString()]
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Count the number of members of a group at a specific member
|
|
* @param groupID Target group ID
|
|
* @param level The level to check
|
|
*/
|
|
public static async CountMembersAtLevel(groupID: number, level: GroupMembershipLevels) : Promise<number> {
|
|
return await DatabaseHelper.Count({
|
|
table: GROUPS_MEMBERS_TABLE,
|
|
where: {
|
|
groups_id: groupID,
|
|
level: level
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get the last activity time of a group
|
|
*
|
|
* @param groupID Target group ID
|
|
*/
|
|
public static async GetLastActivity(groupID: number) : Promise<number> {
|
|
// TODO URGENT : implement
|
|
return groupID;
|
|
}
|
|
|
|
/**
|
|
* Check out whether a user can create posts on a group or not
|
|
*
|
|
* @param groupID Target group ID
|
|
* @param userID Target user ID
|
|
*/
|
|
public static async CanUserCreatePosts(groupID: number, userID: number) : Promise<boolean> {
|
|
const membershipLevel = await this.GetMembershipLevel(groupID, userID);
|
|
|
|
// Moderators + administrators => can always create posts
|
|
if(membershipLevel == GroupMembershipLevels.MODERATOR ||
|
|
membershipLevel == GroupMembershipLevels.ADMINISTRATOR)
|
|
return true;
|
|
|
|
// Simple members => check authorization
|
|
if(membershipLevel == GroupMembershipLevels.MEMBER) {
|
|
return (await this.GetInfo(groupID)).postsCreationLevel
|
|
== GroupPostsCreationLevel.POSTS_LEVEL_ALL_MEMBERS;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Turn a database row into a {GroupInfo} object
|
|
*
|
|
* @param row Database entry
|
|
* @returns Generated object
|
|
*/
|
|
private static async DbToGroupInfo(row: any) : Promise<GroupInfo> {
|
|
return new GroupInfo({
|
|
id: row.id,
|
|
name: row.name,
|
|
membersCount: await this.CountMembers(row.id),
|
|
visiblity: row.visibility,
|
|
registrationLevel: row.registration_level,
|
|
postsCreationLevel: row.posts_level,
|
|
logo: (row.path_logo != null && row.path_logo && row.path_logo != "null" ? row.path_logo : undefined),
|
|
virtualDirectory: (row.virtual_directory != null && row.virtual_directory && row.virtual_directory != "null" ? row.virtual_directory : undefined),
|
|
timeCreate: row.time_create,
|
|
description: (row.description != null && row.description && row.description != "null" ? row.description : undefined),
|
|
url: (row.url != null && row.url && row.url != "null" ? row.url : undefined)
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Turn a database entry into a group membership information object
|
|
*
|
|
* @param row Database entry
|
|
* @returns Generated object
|
|
*/
|
|
private static DbToGroupMember(row: any) : GroupMember {
|
|
return new GroupMember({
|
|
id: row.id,
|
|
groupID: row.groups_id,
|
|
userID: Number(row.user_id),
|
|
timeCreate: Number(row.time_create),
|
|
level: row.level,
|
|
following: row.following == 1
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Turn a GroupSettings object into a database entry object
|
|
*
|
|
* @param settings Group settings object to transform
|
|
* @return Generated database entry
|
|
*/
|
|
private static GroupSettingsToDB(settings: GroupSettings) : Object {
|
|
let data = {};
|
|
|
|
if(settings.name != null)
|
|
data['name'] = settings.name;
|
|
|
|
if(settings.logo != null)
|
|
data["path_logo"] = settings.logo;
|
|
|
|
if(settings.visiblity != null)
|
|
data["visibility"] = settings.visiblity;
|
|
|
|
if(settings.registrationLevel != null)
|
|
data["registration_level"] = settings.registrationLevel;
|
|
|
|
if(settings.postsCreationLevel != null)
|
|
data["posts_level"] = settings.postsCreationLevel;
|
|
|
|
if(settings.virtualDirectory != null)
|
|
data["virtual_directory"] = settings.virtualDirectory;
|
|
|
|
if(settings.description != null)
|
|
data["description"] = settings.description;
|
|
|
|
if(settings.url != null)
|
|
data["url"] = settings.url;
|
|
|
|
return data;
|
|
}
|
|
} |