import { RequestHandler } from "../entities/RequestHandler"; import { GroupsHelper, PATH_GROUPS_LOGOS } from "../helpers/GroupsHelper"; import { GroupsAccessLevel, GroupInfo, GroupVisibilityLevel, GroupPostsCreationLevel, GroupRegistrationLevel } from "../entities/Group"; import { GroupMembershipLevels, GroupMember } from "../entities/GroupMember"; import { time } from "../utils/DateUtils"; import { LikesHelper, LikesType } from "../helpers/LikesHelper"; import { GroupSettings } from "../entities/GroupSettings"; import { removeHTMLNodes, checkURL } from "../utils/StringUtils"; import { findKey } from "../utils/ArrayUtils"; import { checkVirtualDirectoryAvailability, VirtualDirType } from "../utils/VirtualDirsUtils"; /** * Groups API controller * * @author Pierre HUBERT */ /** * API groups registration levels */ const GROUPS_REGISTRATION_LEVELS = {}; GROUPS_REGISTRATION_LEVELS[GroupRegistrationLevel.OPEN_REGISTRATION] = "open"; GROUPS_REGISTRATION_LEVELS[GroupRegistrationLevel.MODERATED_REGISTRATION] = "moderated"; GROUPS_REGISTRATION_LEVELS[GroupRegistrationLevel.CLOSED_REGISTRATION] = "closed"; /** * API groups membership levels */ const GROUPS_MEMBERSHIP_LEVELS = {}; GROUPS_MEMBERSHIP_LEVELS[GroupMembershipLevels.ADMINISTRATOR] = "administrator"; GROUPS_MEMBERSHIP_LEVELS[GroupMembershipLevels.MODERATOR] = "moderator"; GROUPS_MEMBERSHIP_LEVELS[GroupMembershipLevels.MEMBER] = "member"; GROUPS_MEMBERSHIP_LEVELS[GroupMembershipLevels.INVITED] = "invited"; GROUPS_MEMBERSHIP_LEVELS[GroupMembershipLevels.PENDING] = "pending"; GROUPS_MEMBERSHIP_LEVELS[GroupMembershipLevels.VISITOR] = "visitor"; /** * API groups visibility levels */ const GROUPS_VISIBILITY_LEVELS = {}; GROUPS_VISIBILITY_LEVELS[GroupVisibilityLevel.OPEN_GROUP] = "open"; GROUPS_VISIBILITY_LEVELS[GroupVisibilityLevel.PRIVATE_GROUP] = "private"; GROUPS_VISIBILITY_LEVELS[GroupVisibilityLevel.SECRETE_GROUP] = "secrete"; /** * API posts creation levels */ const GROUPS_POSTS_LEVELS = []; GROUPS_POSTS_LEVELS[GroupPostsCreationLevel.POSTS_LEVEL_MODERATORS] = "moderators"; GROUPS_POSTS_LEVELS[GroupPostsCreationLevel.POSTS_LEVEL_ALL_MEMBERS] = "members"; export class GroupsController { /** * Create a new group * * @param h Request handler */ public static async Create(h: RequestHandler) { const name = h.postString("name", 3); const groupID = await GroupsHelper.Create({ name: name, userID: h.getUserId(), timeCreate: time() }); h.send({ success: "The group has been successfully created!", id: groupID }); } /** * Get the list of groups of the user * * @param h Request handler */ public static async GetListUser(h: RequestHandler) { h.send(await GroupsHelper.GetListUser(h.getUserId())); } /** * Get information about a single group * * @param h Request handler */ public static async GetInfoSingle(h: RequestHandler) { const groupID = await h.postGroupIDWithAccess("id", GroupsAccessLevel.LIMITED_ACCESS); const groupInfo = await GroupsHelper.GetInfo(groupID); h.send(await this.GroupInfoToAPI(groupInfo, h)); } /** * Get information about multiple users * * @param h Request handler */ public static async GetInfoMultiple(h: RequestHandler) { const ids = h.postNumbersList("list"); const result = {}; for (const id of ids) { // Check group existence & user authorization if(!await GroupsHelper.Exists(id) || await GroupsHelper.GetAccessLevel(id, h.getUserId()) < GroupsAccessLevel.LIMITED_ACCESS) h.error(404, "Group " + id + " not found"); const group = await GroupsHelper.GetInfo(id); result[id] = await this.GroupInfoToAPI(group, h); } h.send(result); } /** * Get advanced information about a group * * @param h Request handler */ public static async GetAdvancedInfo(h: RequestHandler) { const groupID = await h.postGroupIDWithAccess("id", GroupsAccessLevel.VIEW_ACCESS); const group = await GroupsHelper.GetInfo(groupID); h.send(await this.GroupInfoToAPI(group, h, true)); } /** * Get group settings * * @param h Request handler */ public static async GetSettings(h: RequestHandler) { const groupID = await h.postGroupIDWithAccess("id", GroupsAccessLevel.ADMIN_ACCESS); // For now, this method is the same as the get advanced info methods, // but this might change in the future... const group = await GroupsHelper.GetInfo(groupID); h.send(await this.GroupInfoToAPI(group, h, true)); } /** * Set (update) group settings * * @param h Request handler */ public static async SetSettings(h: RequestHandler) { const groupID = await h.postGroupIDWithAccess("id", GroupsAccessLevel.ADMIN_ACCESS); // Check group visibility const visibilityKey = findKey(GROUPS_VISIBILITY_LEVELS, h.postString("visibility", 3)); if(visibilityKey == null) h.error(400, "Group visibility level not recognized!"); // Check group registration level const registrationKey = findKey(GROUPS_REGISTRATION_LEVELS, h.postString("registration_level", 3)); if(registrationKey == null) h.error(400, "Group registration level not recognized!"); // Check post creation level const postLevelKey = findKey(GROUPS_POSTS_LEVELS, h.postString("posts_level", 3)); if(postLevelKey == null) h.error(400, "Group post creation level not recognized!"); // Check URL const url = h.postString("url", 0); if(url.length > 0 && ! checkURL(url)) h.error(401, "Invalid group URL!"); // Check virtual directory let virtualDirectory = ""; if(h.hasPostString("virtual_directory", 1)) { virtualDirectory = h.postVirtualDirectory("virtual_directory"); // Check out whether virtual directory is available or not if(!await checkVirtualDirectoryAvailability(virtualDirectory, groupID, VirtualDirType.GROUP)) h.error(401, "Requested virtual directory is not available!"); } const settings = new GroupSettings({ // Basic information id: groupID, name: removeHTMLNodes(h.postString("name", 3)), visiblity: Number(visibilityKey), registrationLevel: Number(registrationKey), postsCreationLevel: Number(postLevelKey), // Useless info membersCount: -1, timeCreate: -1, // Optionnal description: removeHTMLNodes(h.postString("description", 0)), // Optionnal url: url, // Optionnal virtualDirectory: virtualDirectory, }); await GroupsHelper.SetSettings(settings); h.success("Group settings have been successfully updated!"); } /** * Check the availability of a virtual directory for a given group * * @param h Request handler */ public static async CheckVirtualDirectory(h: RequestHandler) { const groupID = await h.postGroupIDWithAccess("groupID", GroupsAccessLevel.ADMIN_ACCESS); const virtualDirectory = h.postVirtualDirectory("directory"); if(!await checkVirtualDirectoryAvailability(virtualDirectory, groupID, VirtualDirType.GROUP)) h.error(401, "The requested virtual directory seems not to be available!"); h.success("Requested virtual directory seems to be available!"); } /** * Upload a new group logo * * @param h Request handler */ public static async UploadLogo(h: RequestHandler) { const groupID = await h.postGroupIDWithAccess("id", GroupsAccessLevel.ADMIN_ACCESS); if(!h.hasFile("logo")) h.error(400, "An error occured while receiving logo !"); // Delete current logo (if any) await GroupsHelper.DeleteLogo(groupID); // Save the new group logo const targetFilePath = await h.savePostImage("logo", PATH_GROUPS_LOGOS, 500, 500); // Update the settings of the group const settings = await GroupsHelper.GetInfo(groupID); settings.logo = targetFilePath; await GroupsHelper.SetSettings(settings); h.send({ success: "Group logo has been successfully updated!", url: settings.logoURL }); } /** * Delete the current logo of a group * * @param h Request handler */ public static async DeleteLogo(h: RequestHandler) { const groupID = await h.postGroupIDWithAccess("id", GroupsAccessLevel.ADMIN_ACCESS); await GroupsHelper.DeleteLogo(groupID); h.send({ success: "Group logo has been successfully deleted!", url: (await GroupsHelper.GetInfo(groupID)).logoURL }) } /** * Get the entire list of members of the group * * @param h Request handler */ public static async GetMembers(h: RequestHandler) { const groupID = await h.postGroupIDWithAccess("id", GroupsAccessLevel.MODERATOR_ACCESS); const members = await GroupsHelper.GetListMembers(groupID); // Parse the list of members h.send(members.map((m) => this.GroupMemberToAPI(m))); } /** * Invite a user to join the network * * @param h Request handler */ public static async InviteUser(h: RequestHandler) { const groupID = await h.postGroupIDWithAccess("group_id", GroupsAccessLevel.MODERATOR_ACCESS); const userID = await h.postUserId("userID"); if(!await GroupsHelper.GetMembershipLevel(groupID, userID)) h.error(401, "The user is not a visitor of the group!"); await GroupsHelper.SendInvitation(groupID, userID); // TODO : Create a notification h.success("The user has been successfully invited to join the group!"); } /** * Turn a GroupInfo object into a valid API object * * @param info Information about the group * @param h Request handler * @param advanced Specify whether advanced information should be returned * in the request or not * @returns Generated object */ private static async GroupInfoToAPI(info: GroupInfo, h: RequestHandler, advanced: boolean = false) : Promise { const membership = await GroupsHelper.GetMembershipInfo(info.id, h.optionnalUserID) const data = { id: info.id, name: info.name, icon_url: info.logoURL, number_members: info.membersCount, visibility: GROUPS_VISIBILITY_LEVELS[info.visiblity], registration_level: GROUPS_REGISTRATION_LEVELS[info.registrationLevel], posts_level: GROUPS_POSTS_LEVELS[info.postsCreationLevel], virtual_directory: info.virtualDirectory ? info.virtualDirectory : "null", membership: GROUPS_MEMBERSHIP_LEVELS[membership ? membership.level : GroupMembershipLevels.VISITOR], following: membership ? membership.following : false } if(advanced) { data["time_create"] = info.timeCreate; data["description"] = info.hasDescription ? info.description : "null"; data["url"] = info.url ? info.hasURL : "null"; data["number_likes"] = await LikesHelper.Count(info.id, LikesType.GROUP); data["is_liking"] = h.signedIn ? await LikesHelper.IsLiking(h.getUserId(), info.id, LikesType.GROUP) : false; } return data; } /** * Convert a {GroupMember} object into an API entry * * @param m Group Member to transform */ private static GroupMemberToAPI(m: GroupMember) : Object { return { user_id: m.userID, group_id: m.groupID, time_create: m.timeCreate, level: GROUPS_MEMBERSHIP_LEVELS[m.level] }; } }