import { PostKind, PostVisibilityLevel, Post, PostPageKind, PostFile, PostLink, PostAccessLevel } from "../entities/Post"; import { FriendsHelper } from "./FriendsHelper"; import { DatabaseHelper } from "./DatabaseHelper"; import { UserHelper } from "./UserHelper"; import { GroupsHelper } from "./GroupsHelper"; import { GroupMembershipLevels } from "../entities/GroupMember"; /** * Posts helper * * @author Pierre HUBERT */ /** * Table name */ const TABLE_NAME = "texte"; /** * Database mapping */ const PostDBTypes : Record = { "texte": PostKind.POST_KIND_TEXT, "image": PostKind.POST_KIND_IMAGE, "webpage_link": PostKind.POST_KIND_WEBLINK, "pdf": PostKind.POST_KIND_PDF, "video": PostKind.POST_KIND_MOVIE, "count_down": PostKind.POST_KIND_COUNTDOWN, "sondage": PostKind.POST_KIND_SURVEY, "youtube": PostKind.POST_KIND_YOUTUBE } /** * Posts helper */ export class PostsHelper { /** * Get the posts of a user * * @param userID The ID of the user making the request * @param targetID The ID of the target user * @param startFrom Start point (0 = none) * @param limit Maximum number of messages to fetch */ public static async GetUserPosts(userID: number, targetID: number, startFrom: number = 0, limit: number = 10) : Promise> { if(limit < 1) throw Error("Limit of post query must be greater or equal to one!"); // Determine max user visibility let level : PostVisibilityLevel = PostVisibilityLevel.VISIBILITY_PUBLIC; if(userID == targetID) level = PostVisibilityLevel.VISIBILITY_USER; else if(userID > 0 && await FriendsHelper.AreFriend(userID, targetID)) level = PostVisibilityLevel.VISIBILITY_FRIENDS; // Preprocess conditions /// ============= PERMISSION CONDITIONS ================= // Visibility level let customWhere = "((niveau_visibilite <= ?) "; let customWhereArgs = [level.toString()]; // Add user post (if user signed in) if(userID > 0) { customWhere += " OR (ID_amis = ?) "; customWhereArgs.push(userID.toString()); } customWhere += ")" /// ============= /PERMISSION CONDITIONS ================ // ============== START POINT CONDITION ================= if(startFrom != 0) { customWhere += " AND ID <= ?"; customWhereArgs.push(startFrom.toString()); } // ============== /START POINT CONDITION ================ // Perform the request const entries = await DatabaseHelper.Query({ table: TABLE_NAME, // Base conditions where: { ID_personne: targetID, group_id: 0 }, customWhere: customWhere, customWhereArgs: customWhereArgs, order: "ID DESC", limit: limit, }); return entries.map((r) => this.DBToPost(r)); } /** * Get the posts of a group * * @param userID The ID of the user making the request * @param groupID The ID of the target group * @param startFrom Start point. 0 for none */ public static async GetGroupPosts(userID: number, groupID: number, startFrom: number = 0, limit: number = 10) : Promise> { if(limit < 1) throw new Error("Limit (" + limit +") is invalid!"); // Check membership of the user const membership = await GroupsHelper.GetMembershipLevel(groupID, userID); const canSeeAllPosts = membership <= GroupMembershipLevels.MEMBER; const visibilityLevel = canSeeAllPosts ? PostVisibilityLevel.VISIBILITY_GROUP_MEMBERS : PostVisibilityLevel.VISIBILITY_PUBLIC; // Prepare request // === VISIBILITY RESTRICTION === let customWhere = "(niveau_visibilite <= ?)"; let customWhereArgs = [visibilityLevel.toString()]; // == /VISIBILITY RESTRICTION === // === START POINT === if(startFrom != 0) { customWhere += " AND ID <= ?"; customWhereArgs.push(startFrom.toString()); } // == /START POINT === const results = await DatabaseHelper.Query({ table: TABLE_NAME, where: { group_id: groupID }, customWhere: customWhere, customWhereArgs: customWhereArgs, order: "ID DESC", limit: limit }); return results.map((r) => this.DBToPost(r)); } /** * Get the latest posts of a user * * @param userID The ID of the user making the request * @param startFrom Startpoint for the research * @param limit Limit of the research (default: 10) * @param includeGroups Specify whether groups can be selected too or not */ public static async GetLatest(userID: number, startFrom: number = 10, limit: number = 10, includeGroups: boolean = false) { if(limit < 1) throw new Error("Limit of the query must be greater than 0!"); const visibilityLevel = PostVisibilityLevel.VISIBILITY_FRIENDS; // Get the list of friends of the user const friendsList = await FriendsHelper.GetList(userID, true); // Prepare the request on the database // === Membership condition === let customWhere = "("; // === FRIENDS POSTS === customWhere += "(group_id = 0 AND niveau_visibilite <= ? AND (ID_personne = ?"; let customWhereArgs = [visibilityLevel.toString(), userID.toString()]; friendsList.forEach((f) => { customWhere += " OR ID_personne = ?"; customWhereArgs.push(f.friendID.toString()); }); customWhere += "))" // === FRIENDS POSTS === // === GROUPS POSTS === if(includeGroups) { const groups = await GroupsHelper.GetListUser(userID, true); groups.forEach((groupID) => { customWhere += " OR group_id = ? "; customWhereArgs.push(groupID.toString()); }) } // == /GROUPS POSTS === customWhere += ")"; // === /Membership condition === // === START POINT === if(startFrom > 0) { customWhere += "AND ID <= ?"; customWhereArgs.push(startFrom.toString()); } // == /START POINT === const results = await DatabaseHelper.Query({ table: TABLE_NAME, customWhere: customWhere, customWhereArgs: customWhereArgs, order: "ID DESC", limit: limit }); return results.map((r) => this.DBToPost(r)); } /** * Get information about a single post * * @param postID Target post ID */ public static async GetSingle(postID: number) : Promise { const row = await DatabaseHelper.QueryRow({ table: TABLE_NAME, where: { ID: postID } }); if(row == null) throw new Error("Post " + postID + " not found!"); return this.DBToPost(row); } /** * Get the access level of a user over a post * * This is a convenience function * * @param userID Target user ID * @param postID Target post ID */ public static async GetAccessLevelFromPostID(userID: number, postID: number) : Promise { return await this.GetAccessLevel(userID, await this.GetSingle(postID)); } /** * Get the access level of a user over a post * * @param userID Target user ID * @param post Target post */ public static async GetAccessLevel(userID: number, post: Post) : Promise { // User is the owner of the post if(userID == post.userID) return PostAccessLevel.FULL_ACCESS; // User page if(post.kindPage == PostPageKind.PAGE_KIND_USER) { // Post made on user page if(post.pageID == userID) return PostAccessLevel.INTERMEDIATE_ACCESS; // Check if the post is private if(post.visibilityLevel == PostVisibilityLevel.VISIBILITY_USER) return PostAccessLevel.NO_ACCESS; // In case the post is only for friends else if(post.visibilityLevel == PostVisibilityLevel.VISIBILITY_FRIENDS) { if(userID < 1 /* user not signed in */ || !await FriendsHelper.AreFriend(userID, post.pageID) /* not a friend */) return PostAccessLevel.NO_ACCESS; else return PostAccessLevel.BASIC_ACCESS; } // In case of public post else if(post.visibilityLevel == PostVisibilityLevel.VISIBILITY_PUBLIC) { // Check if the user can see the page if(await UserHelper.CanSeeUserPage(userID, post.userPageID)) return PostAccessLevel.BASIC_ACCESS; // Else no access to the user return PostAccessLevel.NO_ACCESS; } } // Group page else if(post.kindPage == PostPageKind.PAGE_KIND_GROUP) { // TODO : check this code const accessLevel = await GroupsHelper.GetMembershipLevel(post.groupID, userID); // Moderator & admin = intermediate access if(accessLevel < GroupMembershipLevels.MEMBER) return PostAccessLevel.INTERMEDIATE_ACCESS; // Members can see all the posts of a group if(accessLevel == GroupMembershipLevels.MEMBER) return PostAccessLevel.BASIC_ACCESS; // Now we check if the post is public & the group is open if(post.visibilityLevel != PostVisibilityLevel.VISIBILITY_PUBLIC || !await GroupsHelper.IsOpen(post.groupID)) return PostAccessLevel.NO_ACCESS; // Public post + open group = basic access return PostAccessLevel.BASIC_ACCESS; } throw Error("GetAccessLevel reached an unimplemented status!"); } /** * Check out whether comments are allowed on a post or not * * @param post The post to check */ public static async AllowCommentsOnPost(post: Post) : Promise { return !post.isUserPage || await UserHelper.AllowComments(post.userPageID); } /** * Check out whether a post exists or not * * @param postID The id of the post to check */ public static async Exists(postID: number) : Promise { return await DatabaseHelper.Count({ table: TABLE_NAME, where: { ID: postID } }) > 0; } /** * Turn a database entry into a row object * * @param row The database entry */ private static DBToPost(row: any) : Post { const postPageKind = row.group_id == 0 ? PostPageKind.PAGE_KIND_USER : PostPageKind.PAGE_KIND_GROUP; const postType = PostDBTypes[row.type]; return new Post({ // General info id: row.ID, userID: row.ID_amis == 0 ? row.ID_personne : row.ID_amis, // Kind of target of the page and its ID kindPage: postPageKind, pageID: postPageKind == PostPageKind.PAGE_KIND_USER ? row.ID_personne : row.group_id, // Basic info timeCreate: row.time_insert == null ? (new Date(row.date_envoi).getTime() / 1000) : row.time_insert, content: row.texte, visibilityLevel: row.niveau_visibilite, kind: postType, // Files specific file: row.path == null ? undefined : new PostFile({ size: Number(row.size), type: row.file_type, path: row.path }), // Movies specific movieID: row.idvideo == null ? undefined : row.idvideo, // Coutdown timer specific timeEnd: (row.time_end && row.time_end) > 0 ? row.time_end // Coutdown legacy support : (row.annee_fin && row.annee_fin > 2010 ? new Date(row.annee_fin + "/" + row.mois_fin + "/" + row.jour_fin).getTime() / 1000 : undefined), // Weblink - specific link: row.url_page == null ? undefined : new PostLink({ url: row.url_page, title: row.titre_page, description: row.description_page, image: row.image_page }), }); } }