1
0
mirror of https://gitlab.com/comunic/comunicapiv2 synced 2024-11-26 07:19:23 +00:00
comunicapiv2/src/helpers/PostsHelper.ts

553 lines
14 KiB
TypeScript
Raw Normal View History

2020-01-03 15:38:44 +00:00
import { PostKind, PostVisibilityLevel, Post, PostPageKind, PostFile, PostLink, PostAccessLevel } from "../entities/Post";
2020-01-03 09:17:34 +00:00
import { FriendsHelper } from "./FriendsHelper";
import { DatabaseHelper } from "./DatabaseHelper";
2020-01-03 15:38:44 +00:00
import { UserHelper } from "./UserHelper";
2020-01-03 15:49:11 +00:00
import { GroupsHelper } from "./GroupsHelper";
import { GroupMembershipLevels } from "../entities/GroupMember";
2020-01-04 16:06:43 +00:00
import { mysql_date } from "../utils/DateUtils";
2020-03-20 17:59:35 +00:00
import { LikesHelper, LikesType } from "./LikesHelper";
2020-03-20 18:07:19 +00:00
import { CommentsHelper } from "./CommentsHelper";
2020-01-03 09:17:34 +00:00
/**
* Posts helper
*
* @author Pierre HUBERT
*/
/**
* Table name
*/
const TABLE_NAME = "texte";
/**
* Database mapping
*/
const PostDBTypes : Record<string, PostKind> = {
2020-01-03 16:38:30 +00:00
"texte": PostKind.POST_KIND_TEXT,
2020-01-03 09:17:34 +00:00
"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 {
2020-01-04 16:06:43 +00:00
/**
* Create a new post
*
* @param p Information about the post
* @returns The ID of the created post
* @throws In case of failure
*/
public static async Create(p: Post) : Promise<number> {
// Extract the kind of post
let kindDb : string = "";
for (const key in PostDBTypes) {
if (PostDBTypes.hasOwnProperty(key) && PostDBTypes[key] == p.kind)
kindDb = key;
}
if(kindDb == "")
throw new Error("Unknown post kind: " + kindDb);
// Prepare post target
let userID: number, friendID: number, groupID: number;
// Post on user page
if(p.isUserPage) {
userID = p.userPageID;
friendID = p.userID;
groupID = 0;
}
// Post on group page
else {
userID = p.userID;
friendID = 0;
groupID = p.groupID;
}
// Generate database entry
let data = {
// Post meta-data & basic info
ID_personne: userID,
ID_amis: friendID,
group_id: groupID,
date_envoi: mysql_date(),
time_insert: p.timeCreate,
niveau_visibilite: p.visibilityLevel,
type: kindDb,
texte: p.hasContent ? p.content : "",
// Generic file infos
size: !p.hasFile ? null : p.file.size,
file_type: !p.hasFile ? null : p.file.type,
path: !p.hasFile ? null : p.file.path,
// Movies post
idvideo: p.hasMovie ? p.movieID : null,
// Countdown timer (TODO : implement)
jour_fin: null,
mois_fin: null,
annee_fin: null,
time_end: p.hasTimeEnd ? p.timeEnd : null,
// Weblink
url_page: !p.hasLink ? null : p.link.url,
titre_page: !p.hasLink ? null : p.link.title,
description_page: !p.hasLink ? null : p.link.description,
image_page: !p.hasLink ? null : p.link.image
}
// Insert the post
const postID = await DatabaseHelper.InsertRow(TABLE_NAME, data);
return postID;
}
2020-01-03 09:17:34 +00:00
/**
* 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<Array<Post>> {
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));
}
2020-01-04 09:27:54 +00:00
/**
* 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<Array<Post>> {
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,
2020-01-04 10:30:33 +00:00
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,
2020-01-04 09:27:54 +00:00
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<Post> {
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<PostAccessLevel> {
return await this.GetAccessLevel(userID, await this.GetSingle(postID));
}
2020-01-03 15:38:44 +00:00
/**
* 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<PostAccessLevel> {
// 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) {
2020-01-03 15:49:11 +00:00
2020-01-04 09:29:42 +00:00
const accessLevel = await GroupsHelper.GetMembershipLevel(post.groupID, userID);
2020-01-03 15:38:44 +00:00
2020-01-03 15:49:11 +00:00
// 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;
2020-01-03 15:38:44 +00:00
}
throw Error("GetAccessLevel reached an unimplemented status!");
}
2020-01-03 16:01:09 +00:00
/**
* Check out whether comments are allowed on a post or not
*
* @param post The post to check
*/
public static async AllowCommentsOnPost(post: Post) : Promise<boolean> {
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<boolean> {
return await DatabaseHelper.Count({
table: TABLE_NAME,
where: {
ID: postID
}
}) > 0;
}
2020-03-20 17:34:28 +00:00
/**
* Set new visibility level to the post
*
* @param postID Target post ID
* @param level Target access level
*/
public static async SetLevel(postID: number, level: PostVisibilityLevel) {
await DatabaseHelper.UpdateRows({
table: TABLE_NAME,
where: {
ID: postID
},
set: {
niveau_visibilite: level
}
})
}
2020-03-20 17:41:32 +00:00
/**
* Set new content to the post
*
* @param postID Target post ID
* @param content Target content
*/
public static async SetContent(postID: number, content: string) {
await DatabaseHelper.UpdateRows({
table: TABLE_NAME,
where: {
ID: postID
},
set: {
texte: content
}
})
}
2020-03-20 17:47:28 +00:00
/**
* Delete a post
*
* @param postID The ID of the post to delete
*/
public static async Delete(postID: number) {
2020-03-20 17:59:35 +00:00
const post = await this.GetSingle(postID);
2020-03-20 17:47:28 +00:00
2020-03-20 17:59:35 +00:00
// Delete all the likes associated with the post
await LikesHelper.DeleteAll(postID, LikesType.POST);
2020-03-20 18:07:19 +00:00
// Delete all the comments associated to the post
await CommentsHelper.DeleteAll(postID);
// TODO : continue deletion
2020-03-20 17:47:28 +00:00
}
2020-01-03 09:17:34 +00:00
/**
* 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
}),
});
}
}